177dd8a79SJan Korous //===- unittests/DirectoryWatcher/DirectoryWatcherTest.cpp ----------------===//
277dd8a79SJan Korous //
377dd8a79SJan Korous // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
477dd8a79SJan Korous // See https://llvm.org/LICENSE.txt for license information.
577dd8a79SJan Korous // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
677dd8a79SJan Korous //
777dd8a79SJan Korous //===----------------------------------------------------------------------===//
877dd8a79SJan Korous
977dd8a79SJan Korous #include "clang/DirectoryWatcher/DirectoryWatcher.h"
1077dd8a79SJan Korous #include "llvm/Support/FileSystem.h"
1177dd8a79SJan Korous #include "llvm/Support/Path.h"
1277dd8a79SJan Korous #include "llvm/Support/raw_ostream.h"
13e1873363SDmitri Gribenko #include "llvm/Testing/Support/Error.h"
1477dd8a79SJan Korous #include "gtest/gtest.h"
1577dd8a79SJan Korous #include <condition_variable>
1677dd8a79SJan Korous #include <future>
1777dd8a79SJan Korous #include <mutex>
18*9cf4419eSKazu Hirata #include <optional>
1977dd8a79SJan Korous #include <thread>
2077dd8a79SJan Korous
2177dd8a79SJan Korous using namespace llvm;
2277dd8a79SJan Korous using namespace llvm::sys;
2377dd8a79SJan Korous using namespace llvm::sys::fs;
2477dd8a79SJan Korous using namespace clang;
2577dd8a79SJan Korous
2677dd8a79SJan Korous namespace clang {
operator ==(const DirectoryWatcher::Event & lhs,const DirectoryWatcher::Event & rhs)2777dd8a79SJan Korous static bool operator==(const DirectoryWatcher::Event &lhs,
2877dd8a79SJan Korous const DirectoryWatcher::Event &rhs) {
2977dd8a79SJan Korous return lhs.Filename == rhs.Filename &&
3077dd8a79SJan Korous static_cast<int>(lhs.Kind) == static_cast<int>(rhs.Kind);
3177dd8a79SJan Korous }
3277dd8a79SJan Korous } // namespace clang
3377dd8a79SJan Korous
3477dd8a79SJan Korous namespace {
3577dd8a79SJan Korous
365076038bSJan Korous typedef DirectoryWatcher::Event::EventKind EventKind;
375076038bSJan Korous
389a2a167bSShoaib Meenai // We've observed this test being significantly flaky when running on a heavily
399a2a167bSShoaib Meenai // loaded machine (e.g. when it's being run as part of the full check-clang
409a2a167bSShoaib Meenai // suite). Set a high timeout value to avoid this flakiness. The 60s timeout
419a2a167bSShoaib Meenai // value was determined empirically. It's a timeout value, not a sleep value,
429a2a167bSShoaib Meenai // and the test should require much less time in practice the vast majority of
439a2a167bSShoaib Meenai // instances. The cases where we do come close to (or still end up hitting) the
449a2a167bSShoaib Meenai // longer timeout are most likely to occur when other tests are also running at
459a2a167bSShoaib Meenai // the same time (e.g. as part of the full check-clang suite), in which case the
469a2a167bSShoaib Meenai // latency of the timeout will be masked by the latency of the other tests.
479a2a167bSShoaib Meenai constexpr std::chrono::seconds EventualResultTimeout(60);
489a2a167bSShoaib Meenai
4977dd8a79SJan Korous struct DirectoryWatcherTestFixture {
5077dd8a79SJan Korous std::string TestRootDir;
5177dd8a79SJan Korous std::string TestWatchedDir;
5277dd8a79SJan Korous
DirectoryWatcherTestFixture__anon421a5f3e0111::DirectoryWatcherTestFixture5377dd8a79SJan Korous DirectoryWatcherTestFixture() {
5477dd8a79SJan Korous SmallString<128> pathBuf;
55000ba715SJan Korous #ifndef NDEBUG
56000ba715SJan Korous std::error_code UniqDirRes =
57000ba715SJan Korous #endif
58000ba715SJan Korous createUniqueDirectory("dirwatcher", pathBuf);
5977dd8a79SJan Korous assert(!UniqDirRes);
60adcd0268SBenjamin Kramer TestRootDir = std::string(pathBuf.str());
6177dd8a79SJan Korous path::append(pathBuf, "watch");
62adcd0268SBenjamin Kramer TestWatchedDir = std::string(pathBuf.str());
63000ba715SJan Korous #ifndef NDEBUG
64000ba715SJan Korous std::error_code CreateDirRes =
65000ba715SJan Korous #endif
66000ba715SJan Korous create_directory(TestWatchedDir, false);
6777dd8a79SJan Korous assert(!CreateDirRes);
6877dd8a79SJan Korous }
6977dd8a79SJan Korous
~DirectoryWatcherTestFixture__anon421a5f3e0111::DirectoryWatcherTestFixture7077dd8a79SJan Korous ~DirectoryWatcherTestFixture() { remove_directories(TestRootDir); }
7177dd8a79SJan Korous
getPathInWatched__anon421a5f3e0111::DirectoryWatcherTestFixture7277dd8a79SJan Korous SmallString<128> getPathInWatched(const std::string &testFile) {
7377dd8a79SJan Korous SmallString<128> pathBuf;
7477dd8a79SJan Korous pathBuf = TestWatchedDir;
7577dd8a79SJan Korous path::append(pathBuf, testFile);
7677dd8a79SJan Korous return pathBuf;
7777dd8a79SJan Korous }
7877dd8a79SJan Korous
addFile__anon421a5f3e0111::DirectoryWatcherTestFixture7977dd8a79SJan Korous void addFile(const std::string &testFile) {
8077dd8a79SJan Korous Expected<file_t> ft = openNativeFileForWrite(getPathInWatched(testFile),
8177dd8a79SJan Korous CD_CreateNew, OF_None);
8277dd8a79SJan Korous if (ft) {
8377dd8a79SJan Korous closeFile(*ft);
8477dd8a79SJan Korous } else {
8577dd8a79SJan Korous llvm::errs() << llvm::toString(ft.takeError()) << "\n";
8677dd8a79SJan Korous llvm::errs() << getPathInWatched(testFile) << "\n";
8777dd8a79SJan Korous llvm_unreachable("Couldn't create test file.");
8877dd8a79SJan Korous }
8977dd8a79SJan Korous }
9077dd8a79SJan Korous
deleteFile__anon421a5f3e0111::DirectoryWatcherTestFixture9177dd8a79SJan Korous void deleteFile(const std::string &testFile) {
9277dd8a79SJan Korous std::error_code EC =
9377dd8a79SJan Korous remove(getPathInWatched(testFile), /*IgnoreNonExisting=*/false);
9477dd8a79SJan Korous ASSERT_FALSE(EC);
9577dd8a79SJan Korous }
9677dd8a79SJan Korous };
9777dd8a79SJan Korous
eventKindToString(const EventKind K)985076038bSJan Korous std::string eventKindToString(const EventKind K) {
9977dd8a79SJan Korous switch (K) {
1005076038bSJan Korous case EventKind::Removed:
10177dd8a79SJan Korous return "Removed";
1025076038bSJan Korous case EventKind::Modified:
10377dd8a79SJan Korous return "Modified";
1045076038bSJan Korous case EventKind::WatchedDirRemoved:
10577dd8a79SJan Korous return "WatchedDirRemoved";
1065076038bSJan Korous case EventKind::WatcherGotInvalidated:
10777dd8a79SJan Korous return "WatcherGotInvalidated";
10877dd8a79SJan Korous }
10977dd8a79SJan Korous llvm_unreachable("unknown event kind");
11077dd8a79SJan Korous }
11177dd8a79SJan Korous
11277dd8a79SJan Korous struct VerifyingConsumer {
11377dd8a79SJan Korous std::vector<DirectoryWatcher::Event> ExpectedInitial;
1144765aa14SJan Korous const std::vector<DirectoryWatcher::Event> ExpectedInitialCopy;
11577dd8a79SJan Korous std::vector<DirectoryWatcher::Event> ExpectedNonInitial;
1164765aa14SJan Korous const std::vector<DirectoryWatcher::Event> ExpectedNonInitialCopy;
11777dd8a79SJan Korous std::vector<DirectoryWatcher::Event> OptionalNonInitial;
11877dd8a79SJan Korous std::vector<DirectoryWatcher::Event> UnexpectedInitial;
11977dd8a79SJan Korous std::vector<DirectoryWatcher::Event> UnexpectedNonInitial;
12077dd8a79SJan Korous std::mutex Mtx;
12177dd8a79SJan Korous std::condition_variable ResultIsReady;
12277dd8a79SJan Korous
VerifyingConsumer__anon421a5f3e0111::VerifyingConsumer12377dd8a79SJan Korous VerifyingConsumer(
12477dd8a79SJan Korous const std::vector<DirectoryWatcher::Event> &ExpectedInitial,
12577dd8a79SJan Korous const std::vector<DirectoryWatcher::Event> &ExpectedNonInitial,
12677dd8a79SJan Korous const std::vector<DirectoryWatcher::Event> &OptionalNonInitial = {})
1274765aa14SJan Korous : ExpectedInitial(ExpectedInitial), ExpectedInitialCopy(ExpectedInitial),
1284765aa14SJan Korous ExpectedNonInitial(ExpectedNonInitial), ExpectedNonInitialCopy(ExpectedNonInitial),
12977dd8a79SJan Korous OptionalNonInitial(OptionalNonInitial) {}
13077dd8a79SJan Korous
13177dd8a79SJan Korous // This method is used by DirectoryWatcher.
consume__anon421a5f3e0111::VerifyingConsumer13277dd8a79SJan Korous void consume(DirectoryWatcher::Event E, bool IsInitial) {
13377dd8a79SJan Korous if (IsInitial)
13477dd8a79SJan Korous consumeInitial(E);
13577dd8a79SJan Korous else
13677dd8a79SJan Korous consumeNonInitial(E);
13777dd8a79SJan Korous }
13877dd8a79SJan Korous
consumeInitial__anon421a5f3e0111::VerifyingConsumer13977dd8a79SJan Korous void consumeInitial(DirectoryWatcher::Event E) {
14077dd8a79SJan Korous std::unique_lock<std::mutex> L(Mtx);
14177dd8a79SJan Korous auto It = std::find(ExpectedInitial.begin(), ExpectedInitial.end(), E);
14277dd8a79SJan Korous if (It == ExpectedInitial.end()) {
14377dd8a79SJan Korous UnexpectedInitial.push_back(E);
14477dd8a79SJan Korous } else {
14577dd8a79SJan Korous ExpectedInitial.erase(It);
14677dd8a79SJan Korous }
147fa086d70SPuyan Lotfi if (result()) {
148fa086d70SPuyan Lotfi L.unlock();
14977dd8a79SJan Korous ResultIsReady.notify_one();
15077dd8a79SJan Korous }
151fa086d70SPuyan Lotfi }
15277dd8a79SJan Korous
consumeNonInitial__anon421a5f3e0111::VerifyingConsumer15377dd8a79SJan Korous void consumeNonInitial(DirectoryWatcher::Event E) {
15477dd8a79SJan Korous std::unique_lock<std::mutex> L(Mtx);
15577dd8a79SJan Korous auto It =
15677dd8a79SJan Korous std::find(ExpectedNonInitial.begin(), ExpectedNonInitial.end(), E);
15777dd8a79SJan Korous if (It == ExpectedNonInitial.end()) {
15877dd8a79SJan Korous auto OptIt =
15977dd8a79SJan Korous std::find(OptionalNonInitial.begin(), OptionalNonInitial.end(), E);
16077dd8a79SJan Korous if (OptIt != OptionalNonInitial.end()) {
16177dd8a79SJan Korous OptionalNonInitial.erase(OptIt);
16277dd8a79SJan Korous } else {
16377dd8a79SJan Korous UnexpectedNonInitial.push_back(E);
16477dd8a79SJan Korous }
16577dd8a79SJan Korous } else {
16677dd8a79SJan Korous ExpectedNonInitial.erase(It);
16777dd8a79SJan Korous }
168fa086d70SPuyan Lotfi if (result()) {
169fa086d70SPuyan Lotfi L.unlock();
17077dd8a79SJan Korous ResultIsReady.notify_one();
17177dd8a79SJan Korous }
172fa086d70SPuyan Lotfi }
17377dd8a79SJan Korous
17477dd8a79SJan Korous // This method is used by DirectoryWatcher.
consume__anon421a5f3e0111::VerifyingConsumer17577dd8a79SJan Korous void consume(llvm::ArrayRef<DirectoryWatcher::Event> Es, bool IsInitial) {
17677dd8a79SJan Korous for (const auto &E : Es)
17777dd8a79SJan Korous consume(E, IsInitial);
17877dd8a79SJan Korous }
17977dd8a79SJan Korous
18077dd8a79SJan Korous // Not locking - caller has to lock Mtx.
result__anon421a5f3e0111::VerifyingConsumer181*9cf4419eSKazu Hirata std::optional<bool> result() const {
18277dd8a79SJan Korous if (ExpectedInitial.empty() && ExpectedNonInitial.empty() &&
18377dd8a79SJan Korous UnexpectedInitial.empty() && UnexpectedNonInitial.empty())
18477dd8a79SJan Korous return true;
18577dd8a79SJan Korous if (!UnexpectedInitial.empty() || !UnexpectedNonInitial.empty())
18677dd8a79SJan Korous return false;
187a41fbb1fSKazu Hirata return std::nullopt;
18877dd8a79SJan Korous }
18977dd8a79SJan Korous
19077dd8a79SJan Korous // This method is used by tests.
19177dd8a79SJan Korous // \returns true on success
blockUntilResult__anon421a5f3e0111::VerifyingConsumer19277dd8a79SJan Korous bool blockUntilResult() {
19377dd8a79SJan Korous std::unique_lock<std::mutex> L(Mtx);
19477dd8a79SJan Korous while (true) {
19577dd8a79SJan Korous if (result())
19677dd8a79SJan Korous return *result();
19777dd8a79SJan Korous
198b8df4093SKazu Hirata ResultIsReady.wait(L, [this]() { return result().has_value(); });
19977dd8a79SJan Korous }
20077dd8a79SJan Korous return false; // Just to make compiler happy.
20177dd8a79SJan Korous }
20277dd8a79SJan Korous
printUnmetExpectations__anon421a5f3e0111::VerifyingConsumer20377dd8a79SJan Korous void printUnmetExpectations(llvm::raw_ostream &OS) {
2044765aa14SJan Korous // If there was any issue, print the expected state
2054765aa14SJan Korous if (
2064765aa14SJan Korous !ExpectedInitial.empty()
2074765aa14SJan Korous ||
2084765aa14SJan Korous !ExpectedNonInitial.empty()
2094765aa14SJan Korous ||
2104765aa14SJan Korous !UnexpectedInitial.empty()
2114765aa14SJan Korous ||
2124765aa14SJan Korous !UnexpectedNonInitial.empty()
2134765aa14SJan Korous ) {
2144765aa14SJan Korous OS << "Expected initial events: \n";
2154765aa14SJan Korous for (const auto &E : ExpectedInitialCopy) {
2164765aa14SJan Korous OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
2174765aa14SJan Korous }
2184765aa14SJan Korous OS << "Expected non-initial events: \n";
2194765aa14SJan Korous for (const auto &E : ExpectedNonInitialCopy) {
2204765aa14SJan Korous OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
2214765aa14SJan Korous }
2224765aa14SJan Korous }
2234765aa14SJan Korous
22477dd8a79SJan Korous if (!ExpectedInitial.empty()) {
22577dd8a79SJan Korous OS << "Expected but not seen initial events: \n";
22677dd8a79SJan Korous for (const auto &E : ExpectedInitial) {
22777dd8a79SJan Korous OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
22877dd8a79SJan Korous }
22977dd8a79SJan Korous }
23077dd8a79SJan Korous if (!ExpectedNonInitial.empty()) {
23177dd8a79SJan Korous OS << "Expected but not seen non-initial events: \n";
23277dd8a79SJan Korous for (const auto &E : ExpectedNonInitial) {
23377dd8a79SJan Korous OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
23477dd8a79SJan Korous }
23577dd8a79SJan Korous }
23677dd8a79SJan Korous if (!UnexpectedInitial.empty()) {
23777dd8a79SJan Korous OS << "Unexpected initial events seen: \n";
23877dd8a79SJan Korous for (const auto &E : UnexpectedInitial) {
23977dd8a79SJan Korous OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
24077dd8a79SJan Korous }
24177dd8a79SJan Korous }
24277dd8a79SJan Korous if (!UnexpectedNonInitial.empty()) {
24377dd8a79SJan Korous OS << "Unexpected non-initial events seen: \n";
24477dd8a79SJan Korous for (const auto &E : UnexpectedNonInitial) {
24577dd8a79SJan Korous OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";
24677dd8a79SJan Korous }
24777dd8a79SJan Korous }
24877dd8a79SJan Korous }
24977dd8a79SJan Korous };
25077dd8a79SJan Korous
checkEventualResultWithTimeout(VerifyingConsumer & TestConsumer)25177dd8a79SJan Korous void checkEventualResultWithTimeout(VerifyingConsumer &TestConsumer) {
25277dd8a79SJan Korous std::packaged_task<int(void)> task(
25377dd8a79SJan Korous [&TestConsumer]() { return TestConsumer.blockUntilResult(); });
25477dd8a79SJan Korous std::future<int> WaitForExpectedStateResult = task.get_future();
25577dd8a79SJan Korous std::thread worker(std::move(task));
25677dd8a79SJan Korous worker.detach();
25777dd8a79SJan Korous
2589a2a167bSShoaib Meenai EXPECT_TRUE(WaitForExpectedStateResult.wait_for(EventualResultTimeout) ==
25977dd8a79SJan Korous std::future_status::ready)
26077dd8a79SJan Korous << "The expected result state wasn't reached before the time-out.";
2614765aa14SJan Korous std::unique_lock<std::mutex> L(TestConsumer.Mtx);
26253daa177SKazu Hirata EXPECT_TRUE(TestConsumer.result().has_value());
26397afce08SKazu Hirata if (TestConsumer.result()) {
26477dd8a79SJan Korous EXPECT_TRUE(*TestConsumer.result());
26577dd8a79SJan Korous }
266d1f47534SFangrui Song if ((TestConsumer.result() && !*TestConsumer.result()) ||
26797afce08SKazu Hirata !TestConsumer.result())
26877dd8a79SJan Korous TestConsumer.printUnmetExpectations(llvm::outs());
26977dd8a79SJan Korous }
27077dd8a79SJan Korous } // namespace
27177dd8a79SJan Korous
TEST(DirectoryWatcherTest,InitialScanSync)27277dd8a79SJan Korous TEST(DirectoryWatcherTest, InitialScanSync) {
27377dd8a79SJan Korous DirectoryWatcherTestFixture fixture;
27477dd8a79SJan Korous
27577dd8a79SJan Korous fixture.addFile("a");
27677dd8a79SJan Korous fixture.addFile("b");
27777dd8a79SJan Korous fixture.addFile("c");
27877dd8a79SJan Korous
27977dd8a79SJan Korous VerifyingConsumer TestConsumer{
2805076038bSJan Korous {{EventKind::Modified, "a"},
2815076038bSJan Korous {EventKind::Modified, "b"},
2825076038bSJan Korous {EventKind::Modified, "c"}},
283c5e7a3d7SJan Korous {},
284c5e7a3d7SJan Korous // We have to ignore these as it's a race between the test process
285c5e7a3d7SJan Korous // which is scanning the directory and kernel which is sending
286c5e7a3d7SJan Korous // notification.
287c5e7a3d7SJan Korous {{EventKind::Modified, "a"},
288c5e7a3d7SJan Korous {EventKind::Modified, "b"},
289c5e7a3d7SJan Korous {EventKind::Modified, "c"}}
290c5e7a3d7SJan Korous };
29177dd8a79SJan Korous
2921dcf216fSPuyan Lotfi llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
2931dcf216fSPuyan Lotfi DirectoryWatcher::create(
29477dd8a79SJan Korous fixture.TestWatchedDir,
29577dd8a79SJan Korous [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
29677dd8a79SJan Korous bool IsInitial) {
29777dd8a79SJan Korous TestConsumer.consume(Events, IsInitial);
29877dd8a79SJan Korous },
29977dd8a79SJan Korous /*waitForInitialSync=*/true);
300e1873363SDmitri Gribenko ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
30177dd8a79SJan Korous
30277dd8a79SJan Korous checkEventualResultWithTimeout(TestConsumer);
30377dd8a79SJan Korous }
30477dd8a79SJan Korous
TEST(DirectoryWatcherTest,InitialScanAsync)30577dd8a79SJan Korous TEST(DirectoryWatcherTest, InitialScanAsync) {
30677dd8a79SJan Korous DirectoryWatcherTestFixture fixture;
30777dd8a79SJan Korous
30877dd8a79SJan Korous fixture.addFile("a");
30977dd8a79SJan Korous fixture.addFile("b");
31077dd8a79SJan Korous fixture.addFile("c");
31177dd8a79SJan Korous
31277dd8a79SJan Korous VerifyingConsumer TestConsumer{
3135076038bSJan Korous {{EventKind::Modified, "a"},
3145076038bSJan Korous {EventKind::Modified, "b"},
3155076038bSJan Korous {EventKind::Modified, "c"}},
316c5e7a3d7SJan Korous {},
317c5e7a3d7SJan Korous // We have to ignore these as it's a race between the test process
318c5e7a3d7SJan Korous // which is scanning the directory and kernel which is sending
319c5e7a3d7SJan Korous // notification.
320c5e7a3d7SJan Korous {{EventKind::Modified, "a"},
321c5e7a3d7SJan Korous {EventKind::Modified, "b"},
322c5e7a3d7SJan Korous {EventKind::Modified, "c"}}
323c5e7a3d7SJan Korous };
32477dd8a79SJan Korous
3251dcf216fSPuyan Lotfi llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
3261dcf216fSPuyan Lotfi DirectoryWatcher::create(
32777dd8a79SJan Korous fixture.TestWatchedDir,
32877dd8a79SJan Korous [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
32977dd8a79SJan Korous bool IsInitial) {
33077dd8a79SJan Korous TestConsumer.consume(Events, IsInitial);
33177dd8a79SJan Korous },
33277dd8a79SJan Korous /*waitForInitialSync=*/false);
333e1873363SDmitri Gribenko ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
33477dd8a79SJan Korous
33577dd8a79SJan Korous checkEventualResultWithTimeout(TestConsumer);
33677dd8a79SJan Korous }
33777dd8a79SJan Korous
TEST(DirectoryWatcherTest,AddFiles)33877dd8a79SJan Korous TEST(DirectoryWatcherTest, AddFiles) {
33977dd8a79SJan Korous DirectoryWatcherTestFixture fixture;
34077dd8a79SJan Korous
34177dd8a79SJan Korous VerifyingConsumer TestConsumer{
34277dd8a79SJan Korous {},
3435076038bSJan Korous {{EventKind::Modified, "a"},
3445076038bSJan Korous {EventKind::Modified, "b"},
3455076038bSJan Korous {EventKind::Modified, "c"}}};
34677dd8a79SJan Korous
3471dcf216fSPuyan Lotfi llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
3481dcf216fSPuyan Lotfi DirectoryWatcher::create(
34977dd8a79SJan Korous fixture.TestWatchedDir,
35077dd8a79SJan Korous [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
35177dd8a79SJan Korous bool IsInitial) {
35277dd8a79SJan Korous TestConsumer.consume(Events, IsInitial);
35377dd8a79SJan Korous },
35477dd8a79SJan Korous /*waitForInitialSync=*/true);
355e1873363SDmitri Gribenko ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
35677dd8a79SJan Korous
35777dd8a79SJan Korous fixture.addFile("a");
35877dd8a79SJan Korous fixture.addFile("b");
35977dd8a79SJan Korous fixture.addFile("c");
36077dd8a79SJan Korous
36177dd8a79SJan Korous checkEventualResultWithTimeout(TestConsumer);
36277dd8a79SJan Korous }
36377dd8a79SJan Korous
TEST(DirectoryWatcherTest,ModifyFile)36477dd8a79SJan Korous TEST(DirectoryWatcherTest, ModifyFile) {
36577dd8a79SJan Korous DirectoryWatcherTestFixture fixture;
36677dd8a79SJan Korous
36777dd8a79SJan Korous fixture.addFile("a");
36877dd8a79SJan Korous
36977dd8a79SJan Korous VerifyingConsumer TestConsumer{
3705076038bSJan Korous {{EventKind::Modified, "a"}},
371c5e7a3d7SJan Korous {{EventKind::Modified, "a"}},
3725076038bSJan Korous {{EventKind::Modified, "a"}}};
37377dd8a79SJan Korous
3741dcf216fSPuyan Lotfi llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
3751dcf216fSPuyan Lotfi DirectoryWatcher::create(
37677dd8a79SJan Korous fixture.TestWatchedDir,
37777dd8a79SJan Korous [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
37877dd8a79SJan Korous bool IsInitial) {
37977dd8a79SJan Korous TestConsumer.consume(Events, IsInitial);
38077dd8a79SJan Korous },
38177dd8a79SJan Korous /*waitForInitialSync=*/true);
382e1873363SDmitri Gribenko ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
38377dd8a79SJan Korous
38477dd8a79SJan Korous // modify the file
38577dd8a79SJan Korous {
38677dd8a79SJan Korous std::error_code error;
38777dd8a79SJan Korous llvm::raw_fd_ostream bStream(fixture.getPathInWatched("a"), error,
38877dd8a79SJan Korous CD_OpenExisting);
38977dd8a79SJan Korous assert(!error);
39077dd8a79SJan Korous bStream << "foo";
39177dd8a79SJan Korous }
39277dd8a79SJan Korous
39377dd8a79SJan Korous checkEventualResultWithTimeout(TestConsumer);
39477dd8a79SJan Korous }
39577dd8a79SJan Korous
TEST(DirectoryWatcherTest,DeleteFile)39677dd8a79SJan Korous TEST(DirectoryWatcherTest, DeleteFile) {
39777dd8a79SJan Korous DirectoryWatcherTestFixture fixture;
39877dd8a79SJan Korous
39977dd8a79SJan Korous fixture.addFile("a");
40077dd8a79SJan Korous
40177dd8a79SJan Korous VerifyingConsumer TestConsumer{
4025076038bSJan Korous {{EventKind::Modified, "a"}},
403c5e7a3d7SJan Korous {{EventKind::Removed, "a"}},
4049debb024SJan Korous {{EventKind::Modified, "a"}, {EventKind::Removed, "a"}}};
40577dd8a79SJan Korous
4061dcf216fSPuyan Lotfi llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
4071dcf216fSPuyan Lotfi DirectoryWatcher::create(
40877dd8a79SJan Korous fixture.TestWatchedDir,
40977dd8a79SJan Korous [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
41077dd8a79SJan Korous bool IsInitial) {
41177dd8a79SJan Korous TestConsumer.consume(Events, IsInitial);
41277dd8a79SJan Korous },
41377dd8a79SJan Korous /*waitForInitialSync=*/true);
414e1873363SDmitri Gribenko ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
41577dd8a79SJan Korous
41677dd8a79SJan Korous fixture.deleteFile("a");
41777dd8a79SJan Korous
41877dd8a79SJan Korous checkEventualResultWithTimeout(TestConsumer);
41977dd8a79SJan Korous }
42077dd8a79SJan Korous
TEST(DirectoryWatcherTest,DeleteWatchedDir)42177dd8a79SJan Korous TEST(DirectoryWatcherTest, DeleteWatchedDir) {
42277dd8a79SJan Korous DirectoryWatcherTestFixture fixture;
42377dd8a79SJan Korous
42477dd8a79SJan Korous VerifyingConsumer TestConsumer{
42577dd8a79SJan Korous {},
4265076038bSJan Korous {{EventKind::WatchedDirRemoved, ""},
4275076038bSJan Korous {EventKind::WatcherGotInvalidated, ""}}};
42877dd8a79SJan Korous
4291dcf216fSPuyan Lotfi llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
4301dcf216fSPuyan Lotfi DirectoryWatcher::create(
43177dd8a79SJan Korous fixture.TestWatchedDir,
43277dd8a79SJan Korous [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
43377dd8a79SJan Korous bool IsInitial) {
43477dd8a79SJan Korous TestConsumer.consume(Events, IsInitial);
43577dd8a79SJan Korous },
43677dd8a79SJan Korous /*waitForInitialSync=*/true);
437e1873363SDmitri Gribenko ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
43877dd8a79SJan Korous
43977dd8a79SJan Korous remove_directories(fixture.TestWatchedDir);
44077dd8a79SJan Korous
44177dd8a79SJan Korous checkEventualResultWithTimeout(TestConsumer);
44277dd8a79SJan Korous }
44377dd8a79SJan Korous
TEST(DirectoryWatcherTest,InvalidatedWatcher)44477dd8a79SJan Korous TEST(DirectoryWatcherTest, InvalidatedWatcher) {
44577dd8a79SJan Korous DirectoryWatcherTestFixture fixture;
44677dd8a79SJan Korous
44777dd8a79SJan Korous VerifyingConsumer TestConsumer{
4485076038bSJan Korous {}, {{EventKind::WatcherGotInvalidated, ""}}};
44977dd8a79SJan Korous
45077dd8a79SJan Korous {
4511dcf216fSPuyan Lotfi llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
4521dcf216fSPuyan Lotfi DirectoryWatcher::create(
45377dd8a79SJan Korous fixture.TestWatchedDir,
45477dd8a79SJan Korous [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,
45577dd8a79SJan Korous bool IsInitial) {
45677dd8a79SJan Korous TestConsumer.consume(Events, IsInitial);
45777dd8a79SJan Korous },
45877dd8a79SJan Korous /*waitForInitialSync=*/true);
459e1873363SDmitri Gribenko ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
46077dd8a79SJan Korous } // DW is destructed here.
46177dd8a79SJan Korous
46277dd8a79SJan Korous checkEventualResultWithTimeout(TestConsumer);
46377dd8a79SJan Korous }
4642ac0c4b4SBen Langmuir
TEST(DirectoryWatcherTest,InvalidatedWatcherAsync)4652ac0c4b4SBen Langmuir TEST(DirectoryWatcherTest, InvalidatedWatcherAsync) {
4662ac0c4b4SBen Langmuir DirectoryWatcherTestFixture fixture;
4672ac0c4b4SBen Langmuir fixture.addFile("a");
4682ac0c4b4SBen Langmuir
4692ac0c4b4SBen Langmuir // This test is checking that we get the initial notification for 'a' before
4702ac0c4b4SBen Langmuir // the final 'invalidated'. Strictly speaking, we do not care whether 'a' is
4712ac0c4b4SBen Langmuir // processed or not, only that it is neither racing with, nor after
4722ac0c4b4SBen Langmuir // 'invalidated'. In practice, it is always processed in our implementations.
4732ac0c4b4SBen Langmuir VerifyingConsumer TestConsumer{
4742ac0c4b4SBen Langmuir {{EventKind::Modified, "a"}},
4752ac0c4b4SBen Langmuir {{EventKind::WatcherGotInvalidated, ""}},
4762ac0c4b4SBen Langmuir // We have to ignore these as it's a race between the test process
4772ac0c4b4SBen Langmuir // which is scanning the directory and kernel which is sending
4782ac0c4b4SBen Langmuir // notification.
4792ac0c4b4SBen Langmuir {{EventKind::Modified, "a"}},
4802ac0c4b4SBen Langmuir };
4812ac0c4b4SBen Langmuir
4822ac0c4b4SBen Langmuir // A counter that can help detect data races on the event receiver,
4832ac0c4b4SBen Langmuir // particularly if used with TSan. Expected count will be 2 or 3 depending on
4842ac0c4b4SBen Langmuir // whether we get the kernel event or not (see comment above).
4852ac0c4b4SBen Langmuir unsigned Count = 0;
4862ac0c4b4SBen Langmuir {
4872ac0c4b4SBen Langmuir llvm::Expected<std::unique_ptr<DirectoryWatcher>> DW =
4882ac0c4b4SBen Langmuir DirectoryWatcher::create(
4892ac0c4b4SBen Langmuir fixture.TestWatchedDir,
4902ac0c4b4SBen Langmuir [&TestConsumer,
4912ac0c4b4SBen Langmuir &Count](llvm::ArrayRef<DirectoryWatcher::Event> Events,
4922ac0c4b4SBen Langmuir bool IsInitial) {
4932ac0c4b4SBen Langmuir Count += 1;
4942ac0c4b4SBen Langmuir TestConsumer.consume(Events, IsInitial);
4952ac0c4b4SBen Langmuir },
4962ac0c4b4SBen Langmuir /*waitForInitialSync=*/false);
4972ac0c4b4SBen Langmuir ASSERT_THAT_ERROR(DW.takeError(), Succeeded());
4982ac0c4b4SBen Langmuir } // DW is destructed here.
4992ac0c4b4SBen Langmuir
5002ac0c4b4SBen Langmuir checkEventualResultWithTimeout(TestConsumer);
5012ac0c4b4SBen Langmuir ASSERT_TRUE(Count == 2u || Count == 3u);
5022ac0c4b4SBen Langmuir }
503