DirectoryWatcherTest.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  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. #include "clang/DirectoryWatcher/DirectoryWatcher.h"
  9. #include "llvm/Support/FileSystem.h"
  10. #include "llvm/Support/Path.h"
  11. #include "llvm/Support/raw_ostream.h"
  12. #include "gtest/gtest.h"
  13. #include <condition_variable>
  14. #include <future>
  15. #include <mutex>
  16. #include <thread>
  17. using namespace llvm;
  18. using namespace llvm::sys;
  19. using namespace llvm::sys::fs;
  20. using namespace clang;
  21. namespace clang {
  22. static bool operator==(const DirectoryWatcher::Event &lhs,
  23. const DirectoryWatcher::Event &rhs) {
  24. return lhs.Filename == rhs.Filename &&
  25. static_cast<int>(lhs.Kind) == static_cast<int>(rhs.Kind);
  26. }
  27. } // namespace clang
  28. namespace {
  29. typedef DirectoryWatcher::Event::EventKind EventKind;
  30. struct DirectoryWatcherTestFixture {
  31. std::string TestRootDir;
  32. std::string TestWatchedDir;
  33. DirectoryWatcherTestFixture() {
  34. SmallString<128> pathBuf;
  35. #ifndef NDEBUG
  36. std::error_code UniqDirRes =
  37. #endif
  38. createUniqueDirectory("dirwatcher", pathBuf);
  39. assert(!UniqDirRes);
  40. TestRootDir = pathBuf.str();
  41. path::append(pathBuf, "watch");
  42. TestWatchedDir = pathBuf.str();
  43. #ifndef NDEBUG
  44. std::error_code CreateDirRes =
  45. #endif
  46. create_directory(TestWatchedDir, false);
  47. assert(!CreateDirRes);
  48. }
  49. ~DirectoryWatcherTestFixture() { remove_directories(TestRootDir); }
  50. SmallString<128> getPathInWatched(const std::string &testFile) {
  51. SmallString<128> pathBuf;
  52. pathBuf = TestWatchedDir;
  53. path::append(pathBuf, testFile);
  54. return pathBuf;
  55. }
  56. void addFile(const std::string &testFile) {
  57. Expected<file_t> ft = openNativeFileForWrite(getPathInWatched(testFile),
  58. CD_CreateNew, OF_None);
  59. if (ft) {
  60. closeFile(*ft);
  61. } else {
  62. llvm::errs() << llvm::toString(ft.takeError()) << "\n";
  63. llvm::errs() << getPathInWatched(testFile) << "\n";
  64. llvm_unreachable("Couldn't create test file.");
  65. }
  66. }
  67. void deleteFile(const std::string &testFile) {
  68. std::error_code EC =
  69. remove(getPathInWatched(testFile), /*IgnoreNonExisting=*/false);
  70. ASSERT_FALSE(EC);
  71. }
  72. };
  73. std::string eventKindToString(const EventKind K) {
  74. switch (K) {
  75. case EventKind::Removed:
  76. return "Removed";
  77. case EventKind::Modified:
  78. return "Modified";
  79. case EventKind::WatchedDirRemoved:
  80. return "WatchedDirRemoved";
  81. case EventKind::WatcherGotInvalidated:
  82. return "WatcherGotInvalidated";
  83. }
  84. llvm_unreachable("unknown event kind");
  85. }
  86. struct VerifyingConsumer {
  87. std::vector<DirectoryWatcher::Event> ExpectedInitial;
  88. const std::vector<DirectoryWatcher::Event> ExpectedInitialCopy;
  89. std::vector<DirectoryWatcher::Event> ExpectedNonInitial;
  90. const std::vector<DirectoryWatcher::Event> ExpectedNonInitialCopy;
  91. std::vector<DirectoryWatcher::Event> OptionalNonInitial;
  92. std::vector<DirectoryWatcher::Event> UnexpectedInitial;
  93. std::vector<DirectoryWatcher::Event> UnexpectedNonInitial;
  94. std::mutex Mtx;
  95. std::condition_variable ResultIsReady;
  96. VerifyingConsumer(
  97. const std::vector<DirectoryWatcher::Event> &ExpectedInitial,
  98. const std::vector<DirectoryWatcher::Event> &ExpectedNonInitial,
  99. const std::vector<DirectoryWatcher::Event> &OptionalNonInitial = {})
  100. : ExpectedInitial(ExpectedInitial), ExpectedInitialCopy(ExpectedInitial),
  101. ExpectedNonInitial(ExpectedNonInitial), ExpectedNonInitialCopy(ExpectedNonInitial),
  102. OptionalNonInitial(OptionalNonInitial) {}
  103. // This method is used by DirectoryWatcher.
  104. void consume(DirectoryWatcher::Event E, bool IsInitial) {
  105. if (IsInitial)
  106. consumeInitial(E);
  107. else
  108. consumeNonInitial(E);
  109. }
  110. void consumeInitial(DirectoryWatcher::Event E) {
  111. std::unique_lock<std::mutex> L(Mtx);
  112. auto It = std::find(ExpectedInitial.begin(), ExpectedInitial.end(), E);
  113. if (It == ExpectedInitial.end()) {
  114. UnexpectedInitial.push_back(E);
  115. } else {
  116. ExpectedInitial.erase(It);
  117. }
  118. if (result()) {
  119. L.unlock();
  120. ResultIsReady.notify_one();
  121. }
  122. }
  123. void consumeNonInitial(DirectoryWatcher::Event E) {
  124. std::unique_lock<std::mutex> L(Mtx);
  125. auto It =
  126. std::find(ExpectedNonInitial.begin(), ExpectedNonInitial.end(), E);
  127. if (It == ExpectedNonInitial.end()) {
  128. auto OptIt =
  129. std::find(OptionalNonInitial.begin(), OptionalNonInitial.end(), E);
  130. if (OptIt != OptionalNonInitial.end()) {
  131. OptionalNonInitial.erase(OptIt);
  132. } else {
  133. UnexpectedNonInitial.push_back(E);
  134. }
  135. } else {
  136. ExpectedNonInitial.erase(It);
  137. }
  138. if (result()) {
  139. L.unlock();
  140. ResultIsReady.notify_one();
  141. }
  142. }
  143. // This method is used by DirectoryWatcher.
  144. void consume(llvm::ArrayRef<DirectoryWatcher::Event> Es, bool IsInitial) {
  145. for (const auto &E : Es)
  146. consume(E, IsInitial);
  147. }
  148. // Not locking - caller has to lock Mtx.
  149. llvm::Optional<bool> result() const {
  150. if (ExpectedInitial.empty() && ExpectedNonInitial.empty() &&
  151. UnexpectedInitial.empty() && UnexpectedNonInitial.empty())
  152. return true;
  153. if (!UnexpectedInitial.empty() || !UnexpectedNonInitial.empty())
  154. return false;
  155. return llvm::None;
  156. }
  157. // This method is used by tests.
  158. // \returns true on success
  159. bool blockUntilResult() {
  160. std::unique_lock<std::mutex> L(Mtx);
  161. while (true) {
  162. if (result())
  163. return *result();
  164. ResultIsReady.wait(L, [this]() { return result().hasValue(); });
  165. }
  166. return false; // Just to make compiler happy.
  167. }
  168. void printUnmetExpectations(llvm::raw_ostream &OS) {
  169. // If there was any issue, print the expected state
  170. if (
  171. !ExpectedInitial.empty()
  172. ||
  173. !ExpectedNonInitial.empty()
  174. ||
  175. !UnexpectedInitial.empty()
  176. ||
  177. !UnexpectedNonInitial.empty()
  178. ) {
  179. OS << "Expected initial events: \n";
  180. for (const auto &E : ExpectedInitialCopy) {
  181. OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
  182. }
  183. OS << "Expected non-initial events: \n";
  184. for (const auto &E : ExpectedNonInitialCopy) {
  185. OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
  186. }
  187. }
  188. if (!ExpectedInitial.empty()) {
  189. OS << "Expected but not seen initial events: \n";
  190. for (const auto &E : ExpectedInitial) {
  191. OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
  192. }
  193. }
  194. if (!ExpectedNonInitial.empty()) {
  195. OS << "Expected but not seen non-initial events: \n";
  196. for (const auto &E : ExpectedNonInitial) {
  197. OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
  198. }
  199. }
  200. if (!UnexpectedInitial.empty()) {
  201. OS << "Unexpected initial events seen: \n";
  202. for (const auto &E : UnexpectedInitial) {
  203. OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
  204. }
  205. }
  206. if (!UnexpectedNonInitial.empty()) {
  207. OS << "Unexpected non-initial events seen: \n";
  208. for (const auto &E : UnexpectedNonInitial) {
  209. OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
  210. }
  211. }
  212. }
  213. };
  214. void checkEventualResultWithTimeout(VerifyingConsumer &TestConsumer) {
  215. std::packaged_task<int(void)> task(
  216. [&TestConsumer]() { return TestConsumer.blockUntilResult(); });
  217. std::future<int> WaitForExpectedStateResult = task.get_future();
  218. std::thread worker(std::move(task));
  219. worker.detach();
  220. EXPECT_TRUE(WaitForExpectedStateResult.wait_for(std::chrono::seconds(3)) ==
  221. std::future_status::ready)
  222. << "The expected result state wasn't reached before the time-out.";
  223. std::unique_lock<std::mutex> L(TestConsumer.Mtx);
  224. EXPECT_TRUE(TestConsumer.result().hasValue());
  225. if (TestConsumer.result().hasValue()) {
  226. EXPECT_TRUE(*TestConsumer.result());
  227. }
  228. if ((TestConsumer.result().hasValue() && !TestConsumer.result().getValue()) ||
  229. !TestConsumer.result().hasValue())
  230. TestConsumer.printUnmetExpectations(llvm::outs());
  231. }
  232. } // namespace
  233. TEST(DirectoryWatcherTest, InitialScanSync) {
  234. DirectoryWatcherTestFixture fixture;
  235. fixture.addFile("a");
  236. fixture.addFile("b");
  237. fixture.addFile("c");
  238. VerifyingConsumer TestConsumer{
  239. {{EventKind::Modified, "a"},
  240. {EventKind::Modified, "b"},
  241. {EventKind::Modified, "c"}},
  242. {},
  243. // We have to ignore these as it's a race between the test process
  244. // which is scanning the directory and kernel which is sending
  245. // notification.
  246. {{EventKind::Modified, "a"},
  247. {EventKind::Modified, "b"},
  248. {EventKind::Modified, "c"}}
  249. };
  250. llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
  251. DirectoryWatcher::create(
  252. fixture.TestWatchedDir,
  253. [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
  254. bool IsInitial) {
  255. TestConsumer.consume(Events, IsInitial);
  256. },
  257. /*waitForInitialSync=*/true);
  258. if (!DW) {
  259. logAllUnhandledErrors(
  260. DW.takeError(), llvm::errs(),
  261. "DirectoryWatcherTest Failure on DirectoryWatcher::create(): ");
  262. exit(EXIT_FAILURE);
  263. }
  264. checkEventualResultWithTimeout(TestConsumer);
  265. }
  266. TEST(DirectoryWatcherTest, InitialScanAsync) {
  267. DirectoryWatcherTestFixture fixture;
  268. fixture.addFile("a");
  269. fixture.addFile("b");
  270. fixture.addFile("c");
  271. VerifyingConsumer TestConsumer{
  272. {{EventKind::Modified, "a"},
  273. {EventKind::Modified, "b"},
  274. {EventKind::Modified, "c"}},
  275. {},
  276. // We have to ignore these as it's a race between the test process
  277. // which is scanning the directory and kernel which is sending
  278. // notification.
  279. {{EventKind::Modified, "a"},
  280. {EventKind::Modified, "b"},
  281. {EventKind::Modified, "c"}}
  282. };
  283. llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
  284. DirectoryWatcher::create(
  285. fixture.TestWatchedDir,
  286. [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
  287. bool IsInitial) {
  288. TestConsumer.consume(Events, IsInitial);
  289. },
  290. /*waitForInitialSync=*/false);
  291. if (!DW) {
  292. logAllUnhandledErrors(
  293. DW.takeError(), llvm::errs(),
  294. "DirectoryWatcherTest Failure on DirectoryWatcher::create(): ");
  295. exit(EXIT_FAILURE);
  296. }
  297. checkEventualResultWithTimeout(TestConsumer);
  298. }
  299. TEST(DirectoryWatcherTest, AddFiles) {
  300. DirectoryWatcherTestFixture fixture;
  301. VerifyingConsumer TestConsumer{
  302. {},
  303. {{EventKind::Modified, "a"},
  304. {EventKind::Modified, "b"},
  305. {EventKind::Modified, "c"}}};
  306. llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
  307. DirectoryWatcher::create(
  308. fixture.TestWatchedDir,
  309. [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
  310. bool IsInitial) {
  311. TestConsumer.consume(Events, IsInitial);
  312. },
  313. /*waitForInitialSync=*/true);
  314. if (!DW) {
  315. logAllUnhandledErrors(
  316. DW.takeError(), llvm::errs(),
  317. "DirectoryWatcherTest Failure on DirectoryWatcher::create(): ");
  318. exit(EXIT_FAILURE);
  319. }
  320. fixture.addFile("a");
  321. fixture.addFile("b");
  322. fixture.addFile("c");
  323. checkEventualResultWithTimeout(TestConsumer);
  324. }
  325. TEST(DirectoryWatcherTest, ModifyFile) {
  326. DirectoryWatcherTestFixture fixture;
  327. fixture.addFile("a");
  328. VerifyingConsumer TestConsumer{
  329. {{EventKind::Modified, "a"}},
  330. {{EventKind::Modified, "a"}},
  331. {{EventKind::Modified, "a"}}};
  332. llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
  333. DirectoryWatcher::create(
  334. fixture.TestWatchedDir,
  335. [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
  336. bool IsInitial) {
  337. TestConsumer.consume(Events, IsInitial);
  338. },
  339. /*waitForInitialSync=*/true);
  340. if (!DW) {
  341. logAllUnhandledErrors(
  342. DW.takeError(), llvm::errs(),
  343. "DirectoryWatcherTest Failure on DirectoryWatcher::create(): ");
  344. exit(EXIT_FAILURE);
  345. }
  346. // modify the file
  347. {
  348. std::error_code error;
  349. llvm::raw_fd_ostream bStream(fixture.getPathInWatched("a"), error,
  350. CD_OpenExisting);
  351. assert(!error);
  352. bStream << "foo";
  353. }
  354. checkEventualResultWithTimeout(TestConsumer);
  355. }
  356. TEST(DirectoryWatcherTest, DeleteFile) {
  357. DirectoryWatcherTestFixture fixture;
  358. fixture.addFile("a");
  359. VerifyingConsumer TestConsumer{
  360. {{EventKind::Modified, "a"}},
  361. {{EventKind::Removed, "a"}},
  362. {{EventKind::Modified, "a"}, {EventKind::Removed, "a"}}};
  363. llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
  364. DirectoryWatcher::create(
  365. fixture.TestWatchedDir,
  366. [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
  367. bool IsInitial) {
  368. TestConsumer.consume(Events, IsInitial);
  369. },
  370. /*waitForInitialSync=*/true);
  371. if (!DW) {
  372. logAllUnhandledErrors(
  373. DW.takeError(), llvm::errs(),
  374. "DirectoryWatcherTest Failure on DirectoryWatcher::create(): ");
  375. exit(EXIT_FAILURE);
  376. }
  377. fixture.deleteFile("a");
  378. checkEventualResultWithTimeout(TestConsumer);
  379. }
  380. TEST(DirectoryWatcherTest, DeleteWatchedDir) {
  381. DirectoryWatcherTestFixture fixture;
  382. VerifyingConsumer TestConsumer{
  383. {},
  384. {{EventKind::WatchedDirRemoved, ""},
  385. {EventKind::WatcherGotInvalidated, ""}}};
  386. llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
  387. DirectoryWatcher::create(
  388. fixture.TestWatchedDir,
  389. [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
  390. bool IsInitial) {
  391. TestConsumer.consume(Events, IsInitial);
  392. },
  393. /*waitForInitialSync=*/true);
  394. if (!DW) {
  395. logAllUnhandledErrors(
  396. DW.takeError(), llvm::errs(),
  397. "DirectoryWatcherTest Failure on DirectoryWatcher::create(): ");
  398. exit(EXIT_FAILURE);
  399. }
  400. remove_directories(fixture.TestWatchedDir);
  401. checkEventualResultWithTimeout(TestConsumer);
  402. }
  403. TEST(DirectoryWatcherTest, InvalidatedWatcher) {
  404. DirectoryWatcherTestFixture fixture;
  405. VerifyingConsumer TestConsumer{
  406. {}, {{EventKind::WatcherGotInvalidated, ""}}};
  407. {
  408. llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
  409. DirectoryWatcher::create(
  410. fixture.TestWatchedDir,
  411. [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
  412. bool IsInitial) {
  413. TestConsumer.consume(Events, IsInitial);
  414. },
  415. /*waitForInitialSync=*/true);
  416. if (!DW) {
  417. logAllUnhandledErrors(
  418. DW.takeError(), llvm::errs(),
  419. "DirectoryWatcherTest Failure on DirectoryWatcher::create(): ");
  420. exit(EXIT_FAILURE);
  421. }
  422. } // DW is destructed here.
  423. checkEventualResultWithTimeout(TestConsumer);
  424. }