DirectoryWatcherTest.cpp 14 KB

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