DirectoryWatcherTest.cpp 13 KB

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