1 //===- unittests/DirectoryWatcher/DirectoryWatcherTest.cpp ----------------===//
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
7 //===----------------------------------------------------------------------===//
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>
22 using namespace llvm::sys
;
23 using namespace llvm::sys::fs
;
24 using namespace clang
;
27 static bool operator==(const DirectoryWatcher::Event
&lhs
,
28 const DirectoryWatcher::Event
&rhs
) {
29 return lhs
.Filename
== rhs
.Filename
&&
30 static_cast<int>(lhs
.Kind
) == static_cast<int>(rhs
.Kind
);
36 typedef DirectoryWatcher::Event::EventKind EventKind
;
38 // We've observed this test being significantly flaky when running on a heavily
39 // loaded machine (e.g. when it's being run as part of the full check-clang
40 // suite). Set a high timeout value to avoid this flakiness. The 60s timeout
41 // value was determined empirically. It's a timeout value, not a sleep value,
42 // and the test should require much less time in practice the vast majority of
43 // instances. The cases where we do come close to (or still end up hitting) the
44 // longer timeout are most likely to occur when other tests are also running at
45 // the same time (e.g. as part of the full check-clang suite), in which case the
46 // latency of the timeout will be masked by the latency of the other tests.
47 constexpr std::chrono::seconds
EventualResultTimeout(60);
49 struct DirectoryWatcherTestFixture
{
50 std::string TestRootDir
;
51 std::string TestWatchedDir
;
53 DirectoryWatcherTestFixture() {
54 SmallString
<128> pathBuf
;
56 std::error_code UniqDirRes
=
58 createUniqueDirectory("dirwatcher", pathBuf
);
60 TestRootDir
= std::string(pathBuf
.str());
61 path::append(pathBuf
, "watch");
62 TestWatchedDir
= std::string(pathBuf
.str());
64 std::error_code CreateDirRes
=
66 create_directory(TestWatchedDir
, false);
67 assert(!CreateDirRes
);
70 ~DirectoryWatcherTestFixture() { remove_directories(TestRootDir
); }
72 SmallString
<128> getPathInWatched(const std::string
&testFile
) {
73 SmallString
<128> pathBuf
;
74 pathBuf
= TestWatchedDir
;
75 path::append(pathBuf
, testFile
);
79 void addFile(const std::string
&testFile
) {
80 Expected
<file_t
> ft
= openNativeFileForWrite(getPathInWatched(testFile
),
81 CD_CreateNew
, OF_None
);
85 llvm::errs() << llvm::toString(ft
.takeError()) << "\n";
86 llvm::errs() << getPathInWatched(testFile
) << "\n";
87 llvm_unreachable("Couldn't create test file.");
91 void deleteFile(const std::string
&testFile
) {
93 remove(getPathInWatched(testFile
), /*IgnoreNonExisting=*/false);
98 std::string
eventKindToString(const EventKind K
) {
100 case EventKind::Removed
:
102 case EventKind::Modified
:
104 case EventKind::WatchedDirRemoved
:
105 return "WatchedDirRemoved";
106 case EventKind::WatcherGotInvalidated
:
107 return "WatcherGotInvalidated";
109 llvm_unreachable("unknown event kind");
112 struct VerifyingConsumer
{
113 std::vector
<DirectoryWatcher::Event
> ExpectedInitial
;
114 const std::vector
<DirectoryWatcher::Event
> ExpectedInitialCopy
;
115 std::vector
<DirectoryWatcher::Event
> ExpectedNonInitial
;
116 const std::vector
<DirectoryWatcher::Event
> ExpectedNonInitialCopy
;
117 std::vector
<DirectoryWatcher::Event
> OptionalNonInitial
;
118 std::vector
<DirectoryWatcher::Event
> UnexpectedInitial
;
119 std::vector
<DirectoryWatcher::Event
> UnexpectedNonInitial
;
121 std::condition_variable ResultIsReady
;
124 const std::vector
<DirectoryWatcher::Event
> &ExpectedInitial
,
125 const std::vector
<DirectoryWatcher::Event
> &ExpectedNonInitial
,
126 const std::vector
<DirectoryWatcher::Event
> &OptionalNonInitial
= {})
127 : ExpectedInitial(ExpectedInitial
), ExpectedInitialCopy(ExpectedInitial
),
128 ExpectedNonInitial(ExpectedNonInitial
), ExpectedNonInitialCopy(ExpectedNonInitial
),
129 OptionalNonInitial(OptionalNonInitial
) {}
131 // This method is used by DirectoryWatcher.
132 void consume(DirectoryWatcher::Event E
, bool IsInitial
) {
136 consumeNonInitial(E
);
139 void consumeInitial(DirectoryWatcher::Event E
) {
140 std::unique_lock
<std::mutex
> L(Mtx
);
141 auto It
= std::find(ExpectedInitial
.begin(), ExpectedInitial
.end(), E
);
142 if (It
== ExpectedInitial
.end()) {
143 UnexpectedInitial
.push_back(E
);
145 ExpectedInitial
.erase(It
);
149 ResultIsReady
.notify_one();
153 void consumeNonInitial(DirectoryWatcher::Event E
) {
154 std::unique_lock
<std::mutex
> L(Mtx
);
156 std::find(ExpectedNonInitial
.begin(), ExpectedNonInitial
.end(), E
);
157 if (It
== ExpectedNonInitial
.end()) {
159 std::find(OptionalNonInitial
.begin(), OptionalNonInitial
.end(), E
);
160 if (OptIt
!= OptionalNonInitial
.end()) {
161 OptionalNonInitial
.erase(OptIt
);
163 UnexpectedNonInitial
.push_back(E
);
166 ExpectedNonInitial
.erase(It
);
170 ResultIsReady
.notify_one();
174 // This method is used by DirectoryWatcher.
175 void consume(llvm::ArrayRef
<DirectoryWatcher::Event
> Es
, bool IsInitial
) {
176 for (const auto &E
: Es
)
177 consume(E
, IsInitial
);
180 // Not locking - caller has to lock Mtx.
181 std::optional
<bool> result() const {
182 if (ExpectedInitial
.empty() && ExpectedNonInitial
.empty() &&
183 UnexpectedInitial
.empty() && UnexpectedNonInitial
.empty())
185 if (!UnexpectedInitial
.empty() || !UnexpectedNonInitial
.empty())
190 // This method is used by tests.
191 // \returns true on success
192 bool blockUntilResult() {
193 std::unique_lock
<std::mutex
> L(Mtx
);
198 ResultIsReady
.wait(L
, [this]() { return result().has_value(); });
200 return false; // Just to make compiler happy.
203 void printUnmetExpectations(llvm::raw_ostream
&OS
) {
204 // If there was any issue, print the expected state
206 !ExpectedInitial
.empty()
208 !ExpectedNonInitial
.empty()
210 !UnexpectedInitial
.empty()
212 !UnexpectedNonInitial
.empty()
214 OS
<< "Expected initial events: \n";
215 for (const auto &E
: ExpectedInitialCopy
) {
216 OS
<< eventKindToString(E
.Kind
) << " " << E
.Filename
<< "\n";
218 OS
<< "Expected non-initial events: \n";
219 for (const auto &E
: ExpectedNonInitialCopy
) {
220 OS
<< eventKindToString(E
.Kind
) << " " << E
.Filename
<< "\n";
224 if (!ExpectedInitial
.empty()) {
225 OS
<< "Expected but not seen initial events: \n";
226 for (const auto &E
: ExpectedInitial
) {
227 OS
<< eventKindToString(E
.Kind
) << " " << E
.Filename
<< "\n";
230 if (!ExpectedNonInitial
.empty()) {
231 OS
<< "Expected but not seen non-initial events: \n";
232 for (const auto &E
: ExpectedNonInitial
) {
233 OS
<< eventKindToString(E
.Kind
) << " " << E
.Filename
<< "\n";
236 if (!UnexpectedInitial
.empty()) {
237 OS
<< "Unexpected initial events seen: \n";
238 for (const auto &E
: UnexpectedInitial
) {
239 OS
<< eventKindToString(E
.Kind
) << " " << E
.Filename
<< "\n";
242 if (!UnexpectedNonInitial
.empty()) {
243 OS
<< "Unexpected non-initial events seen: \n";
244 for (const auto &E
: UnexpectedNonInitial
) {
245 OS
<< eventKindToString(E
.Kind
) << " " << E
.Filename
<< "\n";
251 void checkEventualResultWithTimeout(VerifyingConsumer
&TestConsumer
) {
252 std::packaged_task
<int(void)> task(
253 [&TestConsumer
]() { return TestConsumer
.blockUntilResult(); });
254 std::future
<int> WaitForExpectedStateResult
= task
.get_future();
255 std::thread
worker(std::move(task
));
258 EXPECT_TRUE(WaitForExpectedStateResult
.wait_for(EventualResultTimeout
) ==
259 std::future_status::ready
)
260 << "The expected result state wasn't reached before the time-out.";
261 std::unique_lock
<std::mutex
> L(TestConsumer
.Mtx
);
262 EXPECT_TRUE(TestConsumer
.result().has_value());
263 if (TestConsumer
.result()) {
264 EXPECT_TRUE(*TestConsumer
.result());
266 if ((TestConsumer
.result() && !*TestConsumer
.result()) ||
267 !TestConsumer
.result())
268 TestConsumer
.printUnmetExpectations(llvm::outs());
272 TEST(DirectoryWatcherTest
, InitialScanSync
) {
273 DirectoryWatcherTestFixture fixture
;
275 fixture
.addFile("a");
276 fixture
.addFile("b");
277 fixture
.addFile("c");
279 VerifyingConsumer TestConsumer
{
280 {{EventKind::Modified
, "a"},
281 {EventKind::Modified
, "b"},
282 {EventKind::Modified
, "c"}},
284 // We have to ignore these as it's a race between the test process
285 // which is scanning the directory and kernel which is sending
287 {{EventKind::Modified
, "a"},
288 {EventKind::Modified
, "b"},
289 {EventKind::Modified
, "c"}}
292 llvm::Expected
<std::unique_ptr
<DirectoryWatcher
>> DW
=
293 DirectoryWatcher::create(
294 fixture
.TestWatchedDir
,
295 [&TestConsumer
](llvm::ArrayRef
<DirectoryWatcher::Event
> Events
,
297 TestConsumer
.consume(Events
, IsInitial
);
299 /*waitForInitialSync=*/true);
300 ASSERT_THAT_ERROR(DW
.takeError(), Succeeded());
302 checkEventualResultWithTimeout(TestConsumer
);
305 TEST(DirectoryWatcherTest
, InitialScanAsync
) {
306 DirectoryWatcherTestFixture fixture
;
308 fixture
.addFile("a");
309 fixture
.addFile("b");
310 fixture
.addFile("c");
312 VerifyingConsumer TestConsumer
{
313 {{EventKind::Modified
, "a"},
314 {EventKind::Modified
, "b"},
315 {EventKind::Modified
, "c"}},
317 // We have to ignore these as it's a race between the test process
318 // which is scanning the directory and kernel which is sending
320 {{EventKind::Modified
, "a"},
321 {EventKind::Modified
, "b"},
322 {EventKind::Modified
, "c"}}
325 llvm::Expected
<std::unique_ptr
<DirectoryWatcher
>> DW
=
326 DirectoryWatcher::create(
327 fixture
.TestWatchedDir
,
328 [&TestConsumer
](llvm::ArrayRef
<DirectoryWatcher::Event
> Events
,
330 TestConsumer
.consume(Events
, IsInitial
);
332 /*waitForInitialSync=*/false);
333 ASSERT_THAT_ERROR(DW
.takeError(), Succeeded());
335 checkEventualResultWithTimeout(TestConsumer
);
338 TEST(DirectoryWatcherTest
, AddFiles
) {
339 DirectoryWatcherTestFixture fixture
;
341 VerifyingConsumer TestConsumer
{
343 {{EventKind::Modified
, "a"},
344 {EventKind::Modified
, "b"},
345 {EventKind::Modified
, "c"}}};
347 llvm::Expected
<std::unique_ptr
<DirectoryWatcher
>> DW
=
348 DirectoryWatcher::create(
349 fixture
.TestWatchedDir
,
350 [&TestConsumer
](llvm::ArrayRef
<DirectoryWatcher::Event
> Events
,
352 TestConsumer
.consume(Events
, IsInitial
);
354 /*waitForInitialSync=*/true);
355 ASSERT_THAT_ERROR(DW
.takeError(), Succeeded());
357 fixture
.addFile("a");
358 fixture
.addFile("b");
359 fixture
.addFile("c");
361 checkEventualResultWithTimeout(TestConsumer
);
364 TEST(DirectoryWatcherTest
, ModifyFile
) {
365 DirectoryWatcherTestFixture fixture
;
367 fixture
.addFile("a");
369 VerifyingConsumer TestConsumer
{
370 {{EventKind::Modified
, "a"}},
371 {{EventKind::Modified
, "a"}},
372 {{EventKind::Modified
, "a"}}};
374 llvm::Expected
<std::unique_ptr
<DirectoryWatcher
>> DW
=
375 DirectoryWatcher::create(
376 fixture
.TestWatchedDir
,
377 [&TestConsumer
](llvm::ArrayRef
<DirectoryWatcher::Event
> Events
,
379 TestConsumer
.consume(Events
, IsInitial
);
381 /*waitForInitialSync=*/true);
382 ASSERT_THAT_ERROR(DW
.takeError(), Succeeded());
386 std::error_code error
;
387 llvm::raw_fd_ostream
bStream(fixture
.getPathInWatched("a"), error
,
393 checkEventualResultWithTimeout(TestConsumer
);
396 TEST(DirectoryWatcherTest
, DeleteFile
) {
397 DirectoryWatcherTestFixture fixture
;
399 fixture
.addFile("a");
401 VerifyingConsumer TestConsumer
{
402 {{EventKind::Modified
, "a"}},
403 {{EventKind::Removed
, "a"}},
404 {{EventKind::Modified
, "a"}, {EventKind::Removed
, "a"}}};
406 llvm::Expected
<std::unique_ptr
<DirectoryWatcher
>> DW
=
407 DirectoryWatcher::create(
408 fixture
.TestWatchedDir
,
409 [&TestConsumer
](llvm::ArrayRef
<DirectoryWatcher::Event
> Events
,
411 TestConsumer
.consume(Events
, IsInitial
);
413 /*waitForInitialSync=*/true);
414 ASSERT_THAT_ERROR(DW
.takeError(), Succeeded());
416 fixture
.deleteFile("a");
418 checkEventualResultWithTimeout(TestConsumer
);
421 TEST(DirectoryWatcherTest
, DeleteWatchedDir
) {
422 DirectoryWatcherTestFixture fixture
;
424 VerifyingConsumer TestConsumer
{
426 {{EventKind::WatchedDirRemoved
, ""},
427 {EventKind::WatcherGotInvalidated
, ""}}};
429 llvm::Expected
<std::unique_ptr
<DirectoryWatcher
>> DW
=
430 DirectoryWatcher::create(
431 fixture
.TestWatchedDir
,
432 [&TestConsumer
](llvm::ArrayRef
<DirectoryWatcher::Event
> Events
,
434 TestConsumer
.consume(Events
, IsInitial
);
436 /*waitForInitialSync=*/true);
437 ASSERT_THAT_ERROR(DW
.takeError(), Succeeded());
439 remove_directories(fixture
.TestWatchedDir
);
441 checkEventualResultWithTimeout(TestConsumer
);
444 TEST(DirectoryWatcherTest
, InvalidatedWatcher
) {
445 DirectoryWatcherTestFixture fixture
;
447 VerifyingConsumer TestConsumer
{
448 {}, {{EventKind::WatcherGotInvalidated
, ""}}};
451 llvm::Expected
<std::unique_ptr
<DirectoryWatcher
>> DW
=
452 DirectoryWatcher::create(
453 fixture
.TestWatchedDir
,
454 [&TestConsumer
](llvm::ArrayRef
<DirectoryWatcher::Event
> Events
,
456 TestConsumer
.consume(Events
, IsInitial
);
458 /*waitForInitialSync=*/true);
459 ASSERT_THAT_ERROR(DW
.takeError(), Succeeded());
460 } // DW is destructed here.
462 checkEventualResultWithTimeout(TestConsumer
);
465 TEST(DirectoryWatcherTest
, InvalidatedWatcherAsync
) {
466 DirectoryWatcherTestFixture fixture
;
467 fixture
.addFile("a");
469 // This test is checking that we get the initial notification for 'a' before
470 // the final 'invalidated'. Strictly speaking, we do not care whether 'a' is
471 // processed or not, only that it is neither racing with, nor after
472 // 'invalidated'. In practice, it is always processed in our implementations.
473 VerifyingConsumer TestConsumer
{
474 {{EventKind::Modified
, "a"}},
475 {{EventKind::WatcherGotInvalidated
, ""}},
476 // We have to ignore these as it's a race between the test process
477 // which is scanning the directory and kernel which is sending
479 {{EventKind::Modified
, "a"}},
482 // A counter that can help detect data races on the event receiver,
483 // particularly if used with TSan. Expected count will be 2 or 3 depending on
484 // whether we get the kernel event or not (see comment above).
487 llvm::Expected
<std::unique_ptr
<DirectoryWatcher
>> DW
=
488 DirectoryWatcher::create(
489 fixture
.TestWatchedDir
,
491 &Count
](llvm::ArrayRef
<DirectoryWatcher::Event
> Events
,
494 TestConsumer
.consume(Events
, IsInitial
);
496 /*waitForInitialSync=*/false);
497 ASSERT_THAT_ERROR(DW
.takeError(), Succeeded());
498 } // DW is destructed here.
500 checkEventualResultWithTimeout(TestConsumer
);
501 ASSERT_TRUE(Count
== 2u || Count
== 3u);