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 { 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 52 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 69 ~DirectoryWatcherTestFixture() { remove_directories(TestRootDir); } 70 71 SmallString<128> getPathInWatched(const std::string &testFile) { 72 SmallString<128> pathBuf; 73 pathBuf = TestWatchedDir; 74 path::append(pathBuf, testFile); 75 return pathBuf; 76 } 77 78 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 90 void deleteFile(const std::string &testFile) { 91 std::error_code EC = 92 remove(getPathInWatched(testFile), /*IgnoreNonExisting=*/false); 93 ASSERT_FALSE(EC); 94 } 95 }; 96 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 122 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. 131 void consume(DirectoryWatcher::Event E, bool IsInitial) { 132 if (IsInitial) 133 consumeInitial(E); 134 else 135 consumeNonInitial(E); 136 } 137 138 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 152 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. 174 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. 180 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 191 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().hasValue(); }); 198 } 199 return false; // Just to make compiler happy. 200 } 201 202 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 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().hasValue()); 262 if (TestConsumer.result().hasValue()) { 263 EXPECT_TRUE(*TestConsumer.result()); 264 } 265 if ((TestConsumer.result().hasValue() && !TestConsumer.result().getValue()) || 266 !TestConsumer.result().hasValue()) 267 TestConsumer.printUnmetExpectations(llvm::outs()); 268 } 269 } // namespace 270 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 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 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 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 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 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 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 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