1 //===- unittests/DirectoryWatcher/DirectoryWatcherTest.cpp ----------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "clang/DirectoryWatcher/DirectoryWatcher.h"
10 #include "llvm/Support/FileSystem.h"
11 #include "llvm/Support/Path.h"
12 #include "llvm/Support/raw_ostream.h"
13 #include "llvm/Testing/Support/Error.h"
14 #include "gtest/gtest.h"
15 #include <condition_variable>
16 #include <future>
17 #include <mutex>
18 #include <thread>
19
20 using namespace llvm;
21 using namespace llvm::sys;
22 using namespace llvm::sys::fs;
23 using namespace clang;
24
25 namespace clang {
operator ==(const DirectoryWatcher::Event & lhs,const DirectoryWatcher::Event & rhs)26 static bool operator==(const DirectoryWatcher::Event &lhs,
27 const DirectoryWatcher::Event &rhs) {
28 return lhs.Filename == rhs.Filename &&
29 static_cast<int>(lhs.Kind) == static_cast<int>(rhs.Kind);
30 }
31 } // namespace clang
32
33 namespace {
34
35 typedef DirectoryWatcher::Event::EventKind EventKind;
36
37 // We've observed this test being significantly flaky when running on a heavily
38 // loaded machine (e.g. when it's being run as part of the full check-clang
39 // suite). Set a high timeout value to avoid this flakiness. The 60s timeout
40 // value was determined empirically. It's a timeout value, not a sleep value,
41 // and the test should require much less time in practice the vast majority of
42 // instances. The cases where we do come close to (or still end up hitting) the
43 // longer timeout are most likely to occur when other tests are also running at
44 // the same time (e.g. as part of the full check-clang suite), in which case the
45 // latency of the timeout will be masked by the latency of the other tests.
46 constexpr std::chrono::seconds EventualResultTimeout(60);
47
48 struct DirectoryWatcherTestFixture {
49 std::string TestRootDir;
50 std::string TestWatchedDir;
51
DirectoryWatcherTestFixture__anon676715bb0111::DirectoryWatcherTestFixture52 DirectoryWatcherTestFixture() {
53 SmallString<128> pathBuf;
54 #ifndef NDEBUG
55 std::error_code UniqDirRes =
56 #endif
57 createUniqueDirectory("dirwatcher", pathBuf);
58 assert(!UniqDirRes);
59 TestRootDir = std::string(pathBuf.str());
60 path::append(pathBuf, "watch");
61 TestWatchedDir = std::string(pathBuf.str());
62 #ifndef NDEBUG
63 std::error_code CreateDirRes =
64 #endif
65 create_directory(TestWatchedDir, false);
66 assert(!CreateDirRes);
67 }
68
~DirectoryWatcherTestFixture__anon676715bb0111::DirectoryWatcherTestFixture69 ~DirectoryWatcherTestFixture() { remove_directories(TestRootDir); }
70
getPathInWatched__anon676715bb0111::DirectoryWatcherTestFixture71 SmallString<128> getPathInWatched(const std::string &testFile) {
72 SmallString<128> pathBuf;
73 pathBuf = TestWatchedDir;
74 path::append(pathBuf, testFile);
75 return pathBuf;
76 }
77
addFile__anon676715bb0111::DirectoryWatcherTestFixture78 void addFile(const std::string &testFile) {
79 Expected<file_t> ft = openNativeFileForWrite(getPathInWatched(testFile),
80 CD_CreateNew, OF_None);
81 if (ft) {
82 closeFile(*ft);
83 } else {
84 llvm::errs() << llvm::toString(ft.takeError()) << "\n";
85 llvm::errs() << getPathInWatched(testFile) << "\n";
86 llvm_unreachable("Couldn't create test file.");
87 }
88 }
89
deleteFile__anon676715bb0111::DirectoryWatcherTestFixture90 void deleteFile(const std::string &testFile) {
91 std::error_code EC =
92 remove(getPathInWatched(testFile), /*IgnoreNonExisting=*/false);
93 ASSERT_FALSE(EC);
94 }
95 };
96
eventKindToString(const EventKind K)97 std::string eventKindToString(const EventKind K) {
98 switch (K) {
99 case EventKind::Removed:
100 return "Removed";
101 case EventKind::Modified:
102 return "Modified";
103 case EventKind::WatchedDirRemoved:
104 return "WatchedDirRemoved";
105 case EventKind::WatcherGotInvalidated:
106 return "WatcherGotInvalidated";
107 }
108 llvm_unreachable("unknown event kind");
109 }
110
111 struct VerifyingConsumer {
112 std::vector<DirectoryWatcher::Event> ExpectedInitial;
113 const std::vector<DirectoryWatcher::Event> ExpectedInitialCopy;
114 std::vector<DirectoryWatcher::Event> ExpectedNonInitial;
115 const std::vector<DirectoryWatcher::Event> ExpectedNonInitialCopy;
116 std::vector<DirectoryWatcher::Event> OptionalNonInitial;
117 std::vector<DirectoryWatcher::Event> UnexpectedInitial;
118 std::vector<DirectoryWatcher::Event> UnexpectedNonInitial;
119 std::mutex Mtx;
120 std::condition_variable ResultIsReady;
121
VerifyingConsumer__anon676715bb0111::VerifyingConsumer122 VerifyingConsumer(
123 const std::vector<DirectoryWatcher::Event> &ExpectedInitial,
124 const std::vector<DirectoryWatcher::Event> &ExpectedNonInitial,
125 const std::vector<DirectoryWatcher::Event> &OptionalNonInitial = {})
126 : ExpectedInitial(ExpectedInitial), ExpectedInitialCopy(ExpectedInitial),
127 ExpectedNonInitial(ExpectedNonInitial), ExpectedNonInitialCopy(ExpectedNonInitial),
128 OptionalNonInitial(OptionalNonInitial) {}
129
130 // This method is used by DirectoryWatcher.
consume__anon676715bb0111::VerifyingConsumer131 void consume(DirectoryWatcher::Event E, bool IsInitial) {
132 if (IsInitial)
133 consumeInitial(E);
134 else
135 consumeNonInitial(E);
136 }
137
consumeInitial__anon676715bb0111::VerifyingConsumer138 void consumeInitial(DirectoryWatcher::Event E) {
139 std::unique_lock<std::mutex> L(Mtx);
140 auto It = std::find(ExpectedInitial.begin(), ExpectedInitial.end(), E);
141 if (It == ExpectedInitial.end()) {
142 UnexpectedInitial.push_back(E);
143 } else {
144 ExpectedInitial.erase(It);
145 }
146 if (result()) {
147 L.unlock();
148 ResultIsReady.notify_one();
149 }
150 }
151
consumeNonInitial__anon676715bb0111::VerifyingConsumer152 void consumeNonInitial(DirectoryWatcher::Event E) {
153 std::unique_lock<std::mutex> L(Mtx);
154 auto It =
155 std::find(ExpectedNonInitial.begin(), ExpectedNonInitial.end(), E);
156 if (It == ExpectedNonInitial.end()) {
157 auto OptIt =
158 std::find(OptionalNonInitial.begin(), OptionalNonInitial.end(), E);
159 if (OptIt != OptionalNonInitial.end()) {
160 OptionalNonInitial.erase(OptIt);
161 } else {
162 UnexpectedNonInitial.push_back(E);
163 }
164 } else {
165 ExpectedNonInitial.erase(It);
166 }
167 if (result()) {
168 L.unlock();
169 ResultIsReady.notify_one();
170 }
171 }
172
173 // This method is used by DirectoryWatcher.
consume__anon676715bb0111::VerifyingConsumer174 void consume(llvm::ArrayRef<DirectoryWatcher::Event> Es, bool IsInitial) {
175 for (const auto &E : Es)
176 consume(E, IsInitial);
177 }
178
179 // Not locking - caller has to lock Mtx.
result__anon676715bb0111::VerifyingConsumer180 llvm::Optional<bool> result() const {
181 if (ExpectedInitial.empty() && ExpectedNonInitial.empty() &&
182 UnexpectedInitial.empty() && UnexpectedNonInitial.empty())
183 return true;
184 if (!UnexpectedInitial.empty() || !UnexpectedNonInitial.empty())
185 return false;
186 return llvm::None;
187 }
188
189 // This method is used by tests.
190 // \returns true on success
blockUntilResult__anon676715bb0111::VerifyingConsumer191 bool blockUntilResult() {
192 std::unique_lock<std::mutex> L(Mtx);
193 while (true) {
194 if (result())
195 return *result();
196
197 ResultIsReady.wait(L, [this]() { return result().has_value(); });
198 }
199 return false; // Just to make compiler happy.
200 }
201
printUnmetExpectations__anon676715bb0111::VerifyingConsumer202 void printUnmetExpectations(llvm::raw_ostream &OS) {
203 // If there was any issue, print the expected state
204 if (
205 !ExpectedInitial.empty()
206 ||
207 !ExpectedNonInitial.empty()
208 ||
209 !UnexpectedInitial.empty()
210 ||
211 !UnexpectedNonInitial.empty()
212 ) {
213 OS << "Expected initial events: \n";
214 for (const auto &E : ExpectedInitialCopy) {
215 OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
216 }
217 OS << "Expected non-initial events: \n";
218 for (const auto &E : ExpectedNonInitialCopy) {
219 OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
220 }
221 }
222
223 if (!ExpectedInitial.empty()) {
224 OS << "Expected but not seen initial events: \n";
225 for (const auto &E : ExpectedInitial) {
226 OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
227 }
228 }
229 if (!ExpectedNonInitial.empty()) {
230 OS << "Expected but not seen non-initial events: \n";
231 for (const auto &E : ExpectedNonInitial) {
232 OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
233 }
234 }
235 if (!UnexpectedInitial.empty()) {
236 OS << "Unexpected initial events seen: \n";
237 for (const auto &E : UnexpectedInitial) {
238 OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
239 }
240 }
241 if (!UnexpectedNonInitial.empty()) {
242 OS << "Unexpected non-initial events seen: \n";
243 for (const auto &E : UnexpectedNonInitial) {
244 OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
245 }
246 }
247 }
248 };
249
checkEventualResultWithTimeout(VerifyingConsumer & TestConsumer)250 void checkEventualResultWithTimeout(VerifyingConsumer &TestConsumer) {
251 std::packaged_task<int(void)> task(
252 [&TestConsumer]() { return TestConsumer.blockUntilResult(); });
253 std::future<int> WaitForExpectedStateResult = task.get_future();
254 std::thread worker(std::move(task));
255 worker.detach();
256
257 EXPECT_TRUE(WaitForExpectedStateResult.wait_for(EventualResultTimeout) ==
258 std::future_status::ready)
259 << "The expected result state wasn't reached before the time-out.";
260 std::unique_lock<std::mutex> L(TestConsumer.Mtx);
261 EXPECT_TRUE(TestConsumer.result().has_value());
262 if (TestConsumer.result()) {
263 EXPECT_TRUE(*TestConsumer.result());
264 }
265 if ((TestConsumer.result() && !TestConsumer.result().value()) ||
266 !TestConsumer.result())
267 TestConsumer.printUnmetExpectations(llvm::outs());
268 }
269 } // namespace
270
TEST(DirectoryWatcherTest,InitialScanSync)271 TEST(DirectoryWatcherTest, InitialScanSync) {
272 DirectoryWatcherTestFixture fixture;
273
274 fixture.addFile("a");
275 fixture.addFile("b");
276 fixture.addFile("c");
277
278 VerifyingConsumer TestConsumer{
279 {{EventKind::Modified, "a"},
280 {EventKind::Modified, "b"},
281 {EventKind::Modified, "c"}},
282 {},
283 // We have to ignore these as it's a race between the test process
284 // which is scanning the directory and kernel which is sending
285 // notification.
286 {{EventKind::Modified, "a"},
287 {EventKind::Modified, "b"},
288 {EventKind::Modified, "c"}}
289 };
290
291 llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
292 DirectoryWatcher::create(
293 fixture.TestWatchedDir,
294 [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
295 bool IsInitial) {
296 TestConsumer.consume(Events, IsInitial);
297 },
298 /*waitForInitialSync=*/true);
299 ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
300
301 checkEventualResultWithTimeout(TestConsumer);
302 }
303
TEST(DirectoryWatcherTest,InitialScanAsync)304 TEST(DirectoryWatcherTest, InitialScanAsync) {
305 DirectoryWatcherTestFixture fixture;
306
307 fixture.addFile("a");
308 fixture.addFile("b");
309 fixture.addFile("c");
310
311 VerifyingConsumer TestConsumer{
312 {{EventKind::Modified, "a"},
313 {EventKind::Modified, "b"},
314 {EventKind::Modified, "c"}},
315 {},
316 // We have to ignore these as it's a race between the test process
317 // which is scanning the directory and kernel which is sending
318 // notification.
319 {{EventKind::Modified, "a"},
320 {EventKind::Modified, "b"},
321 {EventKind::Modified, "c"}}
322 };
323
324 llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
325 DirectoryWatcher::create(
326 fixture.TestWatchedDir,
327 [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
328 bool IsInitial) {
329 TestConsumer.consume(Events, IsInitial);
330 },
331 /*waitForInitialSync=*/false);
332 ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
333
334 checkEventualResultWithTimeout(TestConsumer);
335 }
336
TEST(DirectoryWatcherTest,AddFiles)337 TEST(DirectoryWatcherTest, AddFiles) {
338 DirectoryWatcherTestFixture fixture;
339
340 VerifyingConsumer TestConsumer{
341 {},
342 {{EventKind::Modified, "a"},
343 {EventKind::Modified, "b"},
344 {EventKind::Modified, "c"}}};
345
346 llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
347 DirectoryWatcher::create(
348 fixture.TestWatchedDir,
349 [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
350 bool IsInitial) {
351 TestConsumer.consume(Events, IsInitial);
352 },
353 /*waitForInitialSync=*/true);
354 ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
355
356 fixture.addFile("a");
357 fixture.addFile("b");
358 fixture.addFile("c");
359
360 checkEventualResultWithTimeout(TestConsumer);
361 }
362
TEST(DirectoryWatcherTest,ModifyFile)363 TEST(DirectoryWatcherTest, ModifyFile) {
364 DirectoryWatcherTestFixture fixture;
365
366 fixture.addFile("a");
367
368 VerifyingConsumer TestConsumer{
369 {{EventKind::Modified, "a"}},
370 {{EventKind::Modified, "a"}},
371 {{EventKind::Modified, "a"}}};
372
373 llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
374 DirectoryWatcher::create(
375 fixture.TestWatchedDir,
376 [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
377 bool IsInitial) {
378 TestConsumer.consume(Events, IsInitial);
379 },
380 /*waitForInitialSync=*/true);
381 ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
382
383 // modify the file
384 {
385 std::error_code error;
386 llvm::raw_fd_ostream bStream(fixture.getPathInWatched("a"), error,
387 CD_OpenExisting);
388 assert(!error);
389 bStream << "foo";
390 }
391
392 checkEventualResultWithTimeout(TestConsumer);
393 }
394
TEST(DirectoryWatcherTest,DeleteFile)395 TEST(DirectoryWatcherTest, DeleteFile) {
396 DirectoryWatcherTestFixture fixture;
397
398 fixture.addFile("a");
399
400 VerifyingConsumer TestConsumer{
401 {{EventKind::Modified, "a"}},
402 {{EventKind::Removed, "a"}},
403 {{EventKind::Modified, "a"}, {EventKind::Removed, "a"}}};
404
405 llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
406 DirectoryWatcher::create(
407 fixture.TestWatchedDir,
408 [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
409 bool IsInitial) {
410 TestConsumer.consume(Events, IsInitial);
411 },
412 /*waitForInitialSync=*/true);
413 ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
414
415 fixture.deleteFile("a");
416
417 checkEventualResultWithTimeout(TestConsumer);
418 }
419
TEST(DirectoryWatcherTest,DeleteWatchedDir)420 TEST(DirectoryWatcherTest, DeleteWatchedDir) {
421 DirectoryWatcherTestFixture fixture;
422
423 VerifyingConsumer TestConsumer{
424 {},
425 {{EventKind::WatchedDirRemoved, ""},
426 {EventKind::WatcherGotInvalidated, ""}}};
427
428 llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
429 DirectoryWatcher::create(
430 fixture.TestWatchedDir,
431 [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
432 bool IsInitial) {
433 TestConsumer.consume(Events, IsInitial);
434 },
435 /*waitForInitialSync=*/true);
436 ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
437
438 remove_directories(fixture.TestWatchedDir);
439
440 checkEventualResultWithTimeout(TestConsumer);
441 }
442
TEST(DirectoryWatcherTest,InvalidatedWatcher)443 TEST(DirectoryWatcherTest, InvalidatedWatcher) {
444 DirectoryWatcherTestFixture fixture;
445
446 VerifyingConsumer TestConsumer{
447 {}, {{EventKind::WatcherGotInvalidated, ""}}};
448
449 {
450 llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
451 DirectoryWatcher::create(
452 fixture.TestWatchedDir,
453 [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
454 bool IsInitial) {
455 TestConsumer.consume(Events, IsInitial);
456 },
457 /*waitForInitialSync=*/true);
458 ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
459 } // DW is destructed here.
460
461 checkEventualResultWithTimeout(TestConsumer);
462 }
463
TEST(DirectoryWatcherTest,InvalidatedWatcherAsync)464 TEST(DirectoryWatcherTest, InvalidatedWatcherAsync) {
465 DirectoryWatcherTestFixture fixture;
466 fixture.addFile("a");
467
468 // This test is checking that we get the initial notification for 'a' before
469 // the final 'invalidated'. Strictly speaking, we do not care whether 'a' is
470 // processed or not, only that it is neither racing with, nor after
471 // 'invalidated'. In practice, it is always processed in our implementations.
472 VerifyingConsumer TestConsumer{
473 {{EventKind::Modified, "a"}},
474 {{EventKind::WatcherGotInvalidated, ""}},
475 // We have to ignore these as it's a race between the test process
476 // which is scanning the directory and kernel which is sending
477 // notification.
478 {{EventKind::Modified, "a"}},
479 };
480
481 // A counter that can help detect data races on the event receiver,
482 // particularly if used with TSan. Expected count will be 2 or 3 depending on
483 // whether we get the kernel event or not (see comment above).
484 unsigned Count = 0;
485 {
486 llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
487 DirectoryWatcher::create(
488 fixture.TestWatchedDir,
489 [&TestConsumer,
490 &Count](llvm::ArrayRef<DirectoryWatcher::Event> Events,
491 bool IsInitial) {
492 Count += 1;
493 TestConsumer.consume(Events, IsInitial);
494 },
495 /*waitForInitialSync=*/false);
496 ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
497 } // DW is destructed here.
498
499 checkEventualResultWithTimeout(TestConsumer);
500 ASSERT_TRUE(Count == 2u || Count == 3u);
501 }
502