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