• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 struct DirectoryWatcherTestFixture {
38   std::string TestRootDir;
39   std::string TestWatchedDir;
40 
DirectoryWatcherTestFixture__anonff1223820111::DirectoryWatcherTestFixture41   DirectoryWatcherTestFixture() {
42     SmallString<128> pathBuf;
43 #ifndef NDEBUG
44     std::error_code UniqDirRes =
45 #endif
46     createUniqueDirectory("dirwatcher", pathBuf);
47     assert(!UniqDirRes);
48     TestRootDir = std::string(pathBuf.str());
49     path::append(pathBuf, "watch");
50     TestWatchedDir = std::string(pathBuf.str());
51 #ifndef NDEBUG
52     std::error_code CreateDirRes =
53 #endif
54     create_directory(TestWatchedDir, false);
55     assert(!CreateDirRes);
56   }
57 
~DirectoryWatcherTestFixture__anonff1223820111::DirectoryWatcherTestFixture58   ~DirectoryWatcherTestFixture() { remove_directories(TestRootDir); }
59 
getPathInWatched__anonff1223820111::DirectoryWatcherTestFixture60   SmallString<128> getPathInWatched(const std::string &testFile) {
61     SmallString<128> pathBuf;
62     pathBuf = TestWatchedDir;
63     path::append(pathBuf, testFile);
64     return pathBuf;
65   }
66 
addFile__anonff1223820111::DirectoryWatcherTestFixture67   void addFile(const std::string &testFile) {
68     Expected<file_t> ft = openNativeFileForWrite(getPathInWatched(testFile),
69                                                  CD_CreateNew, OF_None);
70     if (ft) {
71       closeFile(*ft);
72     } else {
73       llvm::errs() << llvm::toString(ft.takeError()) << "\n";
74       llvm::errs() << getPathInWatched(testFile) << "\n";
75       llvm_unreachable("Couldn't create test file.");
76     }
77   }
78 
deleteFile__anonff1223820111::DirectoryWatcherTestFixture79   void deleteFile(const std::string &testFile) {
80     std::error_code EC =
81         remove(getPathInWatched(testFile), /*IgnoreNonExisting=*/false);
82     ASSERT_FALSE(EC);
83   }
84 };
85 
eventKindToString(const EventKind K)86 std::string eventKindToString(const EventKind K) {
87   switch (K) {
88   case EventKind::Removed:
89     return "Removed";
90   case EventKind::Modified:
91     return "Modified";
92   case EventKind::WatchedDirRemoved:
93     return "WatchedDirRemoved";
94   case EventKind::WatcherGotInvalidated:
95     return "WatcherGotInvalidated";
96   }
97   llvm_unreachable("unknown event kind");
98 }
99 
100 struct VerifyingConsumer {
101   std::vector<DirectoryWatcher::Event> ExpectedInitial;
102   const std::vector<DirectoryWatcher::Event> ExpectedInitialCopy;
103   std::vector<DirectoryWatcher::Event> ExpectedNonInitial;
104   const std::vector<DirectoryWatcher::Event> ExpectedNonInitialCopy;
105   std::vector<DirectoryWatcher::Event> OptionalNonInitial;
106   std::vector<DirectoryWatcher::Event> UnexpectedInitial;
107   std::vector<DirectoryWatcher::Event> UnexpectedNonInitial;
108   std::mutex Mtx;
109   std::condition_variable ResultIsReady;
110 
VerifyingConsumer__anonff1223820111::VerifyingConsumer111   VerifyingConsumer(
112       const std::vector<DirectoryWatcher::Event> &ExpectedInitial,
113       const std::vector<DirectoryWatcher::Event> &ExpectedNonInitial,
114       const std::vector<DirectoryWatcher::Event> &OptionalNonInitial = {})
115       : ExpectedInitial(ExpectedInitial), ExpectedInitialCopy(ExpectedInitial),
116         ExpectedNonInitial(ExpectedNonInitial), ExpectedNonInitialCopy(ExpectedNonInitial),
117         OptionalNonInitial(OptionalNonInitial) {}
118 
119   // This method is used by DirectoryWatcher.
consume__anonff1223820111::VerifyingConsumer120   void consume(DirectoryWatcher::Event E, bool IsInitial) {
121     if (IsInitial)
122       consumeInitial(E);
123     else
124       consumeNonInitial(E);
125   }
126 
consumeInitial__anonff1223820111::VerifyingConsumer127   void consumeInitial(DirectoryWatcher::Event E) {
128     std::unique_lock<std::mutex> L(Mtx);
129     auto It = std::find(ExpectedInitial.begin(), ExpectedInitial.end(), E);
130     if (It == ExpectedInitial.end()) {
131       UnexpectedInitial.push_back(E);
132     } else {
133       ExpectedInitial.erase(It);
134     }
135     if (result()) {
136       L.unlock();
137       ResultIsReady.notify_one();
138     }
139   }
140 
consumeNonInitial__anonff1223820111::VerifyingConsumer141   void consumeNonInitial(DirectoryWatcher::Event E) {
142     std::unique_lock<std::mutex> L(Mtx);
143     auto It =
144         std::find(ExpectedNonInitial.begin(), ExpectedNonInitial.end(), E);
145     if (It == ExpectedNonInitial.end()) {
146       auto OptIt =
147           std::find(OptionalNonInitial.begin(), OptionalNonInitial.end(), E);
148       if (OptIt != OptionalNonInitial.end()) {
149         OptionalNonInitial.erase(OptIt);
150       } else {
151         UnexpectedNonInitial.push_back(E);
152       }
153     } else {
154       ExpectedNonInitial.erase(It);
155     }
156     if (result()) {
157       L.unlock();
158       ResultIsReady.notify_one();
159     }
160   }
161 
162   // This method is used by DirectoryWatcher.
consume__anonff1223820111::VerifyingConsumer163   void consume(llvm::ArrayRef<DirectoryWatcher::Event> Es, bool IsInitial) {
164     for (const auto &E : Es)
165       consume(E, IsInitial);
166   }
167 
168   // Not locking - caller has to lock Mtx.
result__anonff1223820111::VerifyingConsumer169   llvm::Optional<bool> result() const {
170     if (ExpectedInitial.empty() && ExpectedNonInitial.empty() &&
171         UnexpectedInitial.empty() && UnexpectedNonInitial.empty())
172       return true;
173     if (!UnexpectedInitial.empty() || !UnexpectedNonInitial.empty())
174       return false;
175     return llvm::None;
176   }
177 
178   // This method is used by tests.
179   // \returns true on success
blockUntilResult__anonff1223820111::VerifyingConsumer180   bool blockUntilResult() {
181     std::unique_lock<std::mutex> L(Mtx);
182     while (true) {
183       if (result())
184         return *result();
185 
186       ResultIsReady.wait(L, [this]() { return result().hasValue(); });
187     }
188     return false; // Just to make compiler happy.
189   }
190 
printUnmetExpectations__anonff1223820111::VerifyingConsumer191   void printUnmetExpectations(llvm::raw_ostream &OS) {
192     // If there was any issue, print the expected state
193     if (
194       !ExpectedInitial.empty()
195       ||
196       !ExpectedNonInitial.empty()
197       ||
198       !UnexpectedInitial.empty()
199       ||
200       !UnexpectedNonInitial.empty()
201     ) {
202       OS << "Expected initial events: \n";
203       for (const auto &E : ExpectedInitialCopy) {
204         OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
205       }
206       OS << "Expected non-initial events: \n";
207       for (const auto &E : ExpectedNonInitialCopy) {
208         OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
209       }
210     }
211 
212     if (!ExpectedInitial.empty()) {
213       OS << "Expected but not seen initial events: \n";
214       for (const auto &E : ExpectedInitial) {
215         OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
216       }
217     }
218     if (!ExpectedNonInitial.empty()) {
219       OS << "Expected but not seen non-initial events: \n";
220       for (const auto &E : ExpectedNonInitial) {
221         OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
222       }
223     }
224     if (!UnexpectedInitial.empty()) {
225       OS << "Unexpected initial events seen: \n";
226       for (const auto &E : UnexpectedInitial) {
227         OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
228       }
229     }
230     if (!UnexpectedNonInitial.empty()) {
231       OS << "Unexpected non-initial events seen: \n";
232       for (const auto &E : UnexpectedNonInitial) {
233         OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
234       }
235     }
236   }
237 };
238 
checkEventualResultWithTimeout(VerifyingConsumer & TestConsumer)239 void checkEventualResultWithTimeout(VerifyingConsumer &TestConsumer) {
240   std::packaged_task<int(void)> task(
241       [&TestConsumer]() { return TestConsumer.blockUntilResult(); });
242   std::future<int> WaitForExpectedStateResult = task.get_future();
243   std::thread worker(std::move(task));
244   worker.detach();
245 
246   EXPECT_TRUE(WaitForExpectedStateResult.wait_for(std::chrono::seconds(3)) ==
247               std::future_status::ready)
248       << "The expected result state wasn't reached before the time-out.";
249   std::unique_lock<std::mutex> L(TestConsumer.Mtx);
250   EXPECT_TRUE(TestConsumer.result().hasValue());
251   if (TestConsumer.result().hasValue()) {
252     EXPECT_TRUE(*TestConsumer.result());
253   }
254   if ((TestConsumer.result().hasValue() && !TestConsumer.result().getValue()) ||
255       !TestConsumer.result().hasValue())
256     TestConsumer.printUnmetExpectations(llvm::outs());
257 }
258 } // namespace
259 
TEST(DirectoryWatcherTest,InitialScanSync)260 TEST(DirectoryWatcherTest, InitialScanSync) {
261   DirectoryWatcherTestFixture fixture;
262 
263   fixture.addFile("a");
264   fixture.addFile("b");
265   fixture.addFile("c");
266 
267   VerifyingConsumer TestConsumer{
268       {{EventKind::Modified, "a"},
269        {EventKind::Modified, "b"},
270        {EventKind::Modified, "c"}},
271       {},
272       // We have to ignore these as it's a race between the test process
273       // which is scanning the directory and kernel which is sending
274       // notification.
275       {{EventKind::Modified, "a"},
276        {EventKind::Modified, "b"},
277        {EventKind::Modified, "c"}}
278       };
279 
280   llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
281       DirectoryWatcher::create(
282           fixture.TestWatchedDir,
283           [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
284                           bool IsInitial) {
285             TestConsumer.consume(Events, IsInitial);
286           },
287           /*waitForInitialSync=*/true);
288   ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
289 
290   checkEventualResultWithTimeout(TestConsumer);
291 }
292 
TEST(DirectoryWatcherTest,InitialScanAsync)293 TEST(DirectoryWatcherTest, InitialScanAsync) {
294   DirectoryWatcherTestFixture fixture;
295 
296   fixture.addFile("a");
297   fixture.addFile("b");
298   fixture.addFile("c");
299 
300   VerifyingConsumer TestConsumer{
301       {{EventKind::Modified, "a"},
302        {EventKind::Modified, "b"},
303        {EventKind::Modified, "c"}},
304       {},
305       // We have to ignore these as it's a race between the test process
306       // which is scanning the directory and kernel which is sending
307       // notification.
308       {{EventKind::Modified, "a"},
309        {EventKind::Modified, "b"},
310        {EventKind::Modified, "c"}}
311        };
312 
313   llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
314       DirectoryWatcher::create(
315           fixture.TestWatchedDir,
316           [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
317                           bool IsInitial) {
318             TestConsumer.consume(Events, IsInitial);
319           },
320           /*waitForInitialSync=*/false);
321   ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
322 
323   checkEventualResultWithTimeout(TestConsumer);
324 }
325 
TEST(DirectoryWatcherTest,AddFiles)326 TEST(DirectoryWatcherTest, AddFiles) {
327   DirectoryWatcherTestFixture fixture;
328 
329   VerifyingConsumer TestConsumer{
330       {},
331       {{EventKind::Modified, "a"},
332        {EventKind::Modified, "b"},
333        {EventKind::Modified, "c"}}};
334 
335   llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
336       DirectoryWatcher::create(
337           fixture.TestWatchedDir,
338           [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
339                           bool IsInitial) {
340             TestConsumer.consume(Events, IsInitial);
341           },
342           /*waitForInitialSync=*/true);
343   ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
344 
345   fixture.addFile("a");
346   fixture.addFile("b");
347   fixture.addFile("c");
348 
349   checkEventualResultWithTimeout(TestConsumer);
350 }
351 
TEST(DirectoryWatcherTest,ModifyFile)352 TEST(DirectoryWatcherTest, ModifyFile) {
353   DirectoryWatcherTestFixture fixture;
354 
355   fixture.addFile("a");
356 
357   VerifyingConsumer TestConsumer{
358       {{EventKind::Modified, "a"}},
359       {{EventKind::Modified, "a"}},
360       {{EventKind::Modified, "a"}}};
361 
362   llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
363       DirectoryWatcher::create(
364           fixture.TestWatchedDir,
365           [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
366                           bool IsInitial) {
367             TestConsumer.consume(Events, IsInitial);
368           },
369           /*waitForInitialSync=*/true);
370   ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
371 
372   // modify the file
373   {
374     std::error_code error;
375     llvm::raw_fd_ostream bStream(fixture.getPathInWatched("a"), error,
376                                  CD_OpenExisting);
377     assert(!error);
378     bStream << "foo";
379   }
380 
381   checkEventualResultWithTimeout(TestConsumer);
382 }
383 
TEST(DirectoryWatcherTest,DeleteFile)384 TEST(DirectoryWatcherTest, DeleteFile) {
385   DirectoryWatcherTestFixture fixture;
386 
387   fixture.addFile("a");
388 
389   VerifyingConsumer TestConsumer{
390       {{EventKind::Modified, "a"}},
391       {{EventKind::Removed, "a"}},
392       {{EventKind::Modified, "a"}, {EventKind::Removed, "a"}}};
393 
394   llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
395       DirectoryWatcher::create(
396           fixture.TestWatchedDir,
397           [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
398                           bool IsInitial) {
399             TestConsumer.consume(Events, IsInitial);
400           },
401           /*waitForInitialSync=*/true);
402   ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
403 
404   fixture.deleteFile("a");
405 
406   checkEventualResultWithTimeout(TestConsumer);
407 }
408 
TEST(DirectoryWatcherTest,DeleteWatchedDir)409 TEST(DirectoryWatcherTest, DeleteWatchedDir) {
410   DirectoryWatcherTestFixture fixture;
411 
412   VerifyingConsumer TestConsumer{
413       {},
414       {{EventKind::WatchedDirRemoved, ""},
415        {EventKind::WatcherGotInvalidated, ""}}};
416 
417   llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
418       DirectoryWatcher::create(
419           fixture.TestWatchedDir,
420           [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
421                           bool IsInitial) {
422             TestConsumer.consume(Events, IsInitial);
423           },
424           /*waitForInitialSync=*/true);
425   ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
426 
427   remove_directories(fixture.TestWatchedDir);
428 
429   checkEventualResultWithTimeout(TestConsumer);
430 }
431 
TEST(DirectoryWatcherTest,InvalidatedWatcher)432 TEST(DirectoryWatcherTest, InvalidatedWatcher) {
433   DirectoryWatcherTestFixture fixture;
434 
435   VerifyingConsumer TestConsumer{
436       {}, {{EventKind::WatcherGotInvalidated, ""}}};
437 
438   {
439     llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
440         DirectoryWatcher::create(
441             fixture.TestWatchedDir,
442             [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
443                             bool IsInitial) {
444               TestConsumer.consume(Events, IsInitial);
445             },
446             /*waitForInitialSync=*/true);
447     ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
448   } // DW is destructed here.
449 
450   checkEventualResultWithTimeout(TestConsumer);
451 }
452 
TEST(DirectoryWatcherTest,InvalidatedWatcherAsync)453 TEST(DirectoryWatcherTest, InvalidatedWatcherAsync) {
454   DirectoryWatcherTestFixture fixture;
455   fixture.addFile("a");
456 
457   // This test is checking that we get the initial notification for 'a' before
458   // the final 'invalidated'. Strictly speaking, we do not care whether 'a' is
459   // processed or not, only that it is neither racing with, nor after
460   // 'invalidated'. In practice, it is always processed in our implementations.
461   VerifyingConsumer TestConsumer{
462       {{EventKind::Modified, "a"}},
463       {{EventKind::WatcherGotInvalidated, ""}},
464       // We have to ignore these as it's a race between the test process
465       // which is scanning the directory and kernel which is sending
466       // notification.
467       {{EventKind::Modified, "a"}},
468   };
469 
470   // A counter that can help detect data races on the event receiver,
471   // particularly if used with TSan. Expected count will be 2 or 3 depending on
472   // whether we get the kernel event or not (see comment above).
473   unsigned Count = 0;
474   {
475     llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
476         DirectoryWatcher::create(
477             fixture.TestWatchedDir,
478             [&TestConsumer,
479              &Count](llvm::ArrayRef<DirectoryWatcher::Event> Events,
480                      bool IsInitial) {
481               Count += 1;
482               TestConsumer.consume(Events, IsInitial);
483             },
484             /*waitForInitialSync=*/false);
485     ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
486   } // DW is destructed here.
487 
488   checkEventualResultWithTimeout(TestConsumer);
489   ASSERT_TRUE(Count == 2u || Count == 3u);
490 }
491