xref: /aosp_15_r20/external/cronet/base/files/file_path_watcher_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/files/file_path_watcher.h"
6 
7 #include <list>
8 #include <memory>
9 #include <string>
10 #include <vector>
11 
12 #include "base/atomic_sequence_num.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/functional/bind.h"
17 #include "base/location.h"
18 #include "base/logging.h"
19 #include "base/run_loop.h"
20 #include "base/sequence_checker.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/task/bind_post_task.h"
23 #include "base/task/sequenced_task_runner.h"
24 #include "base/task/single_thread_task_runner.h"
25 #include "base/test/bind.h"
26 #include "base/test/run_until.h"
27 #include "base/test/task_environment.h"
28 #include "base/test/test_file_util.h"
29 #include "base/test/test_timeouts.h"
30 #include "base/thread_annotations.h"
31 #include "base/threading/thread.h"
32 #include "build/build_config.h"
33 #include "testing/gmock/include/gmock/gmock.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 
36 #if BUILDFLAG(IS_WIN)
37 #include <windows.h>
38 
39 #include <aclapi.h>
40 #elif BUILDFLAG(IS_POSIX)
41 #include <sys/stat.h>
42 #endif
43 
44 #if BUILDFLAG(IS_ANDROID)
45 #include "base/android/path_utils.h"
46 #endif  // BUILDFLAG(IS_ANDROID)
47 
48 #if BUILDFLAG(IS_POSIX)
49 #include "base/files/file_descriptor_watcher_posix.h"
50 #endif  // BUILDFLAG(IS_POSIX)
51 
52 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
53 #include "base/files/file_path_watcher_inotify.h"
54 #include "base/format_macros.h"
55 #endif
56 
57 namespace base {
58 
59 namespace {
60 
61 AtomicSequenceNumber g_next_delegate_id;
62 
63 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
64 // inotify fires two events - one for each file creation + modification.
65 constexpr size_t kExpectedEventsForNewFileWrite = 2;
66 #else
67 constexpr size_t kExpectedEventsForNewFileWrite = 1;
68 #endif
69 
70 enum class ExpectedEventsSinceLastWait { kNone, kSome };
71 
72 struct Event {
73   bool error;
74   FilePath path;
75   FilePathWatcher::ChangeInfo change_info;
76 
operator ==base::__anon20d88b390111::Event77   bool operator==(const Event& other) const {
78     return error == other.error && path == other.path &&
79            change_info.file_path_type == other.change_info.file_path_type &&
80            change_info.change_type == other.change_info.change_type &&
81            // Don't compare the values of the cookies.
82            change_info.cookie.has_value() ==
83                other.change_info.cookie.has_value();
84   }
85 };
86 using EventListMatcher = testing::Matcher<std::list<Event>>;
87 
ToEvent(const FilePathWatcher::ChangeInfo & change_info,const FilePath & path,bool error)88 Event ToEvent(const FilePathWatcher::ChangeInfo& change_info,
89               const FilePath& path,
90               bool error) {
91   return Event{.error = error, .path = path, .change_info = change_info};
92 }
93 
operator <<(std::ostream & os,const FilePathWatcher::ChangeType & change_type)94 std::ostream& operator<<(std::ostream& os,
95                          const FilePathWatcher::ChangeType& change_type) {
96   switch (change_type) {
97     case FilePathWatcher::ChangeType::kUnsupported:
98       return os << "unsupported";
99     case FilePathWatcher::ChangeType::kCreated:
100       return os << "created";
101     case FilePathWatcher::ChangeType::kDeleted:
102       return os << "deleted";
103     case FilePathWatcher::ChangeType::kModified:
104       return os << "modified";
105     case FilePathWatcher::ChangeType::kMoved:
106       return os << "moved";
107   }
108 }
109 
operator <<(std::ostream & os,const FilePathWatcher::FilePathType & file_path_type)110 std::ostream& operator<<(std::ostream& os,
111                          const FilePathWatcher::FilePathType& file_path_type) {
112   switch (file_path_type) {
113     case FilePathWatcher::FilePathType::kUnknown:
114       return os << "Unknown";
115     case FilePathWatcher::FilePathType::kFile:
116       return os << "File";
117     case FilePathWatcher::FilePathType::kDirectory:
118       return os << "Directory";
119   }
120 }
121 
operator <<(std::ostream & os,const FilePathWatcher::ChangeInfo & change_info)122 std::ostream& operator<<(std::ostream& os,
123                          const FilePathWatcher::ChangeInfo& change_info) {
124   return os << "ChangeInfo{ file_path_type: " << change_info.file_path_type
125             << ", change_type: " << change_info.change_type
126             << ", cookie: " << change_info.cookie.has_value() << " }";
127 }
128 
operator <<(std::ostream & os,const Event & event)129 std::ostream& operator<<(std::ostream& os, const Event& event) {
130   if (event.error) {
131     return os << "Event{ ERROR }";
132   }
133 
134   return os << "Event{ path: " << event.path
135             << ", change_info: " << event.change_info << " }";
136 }
137 
SpinEventLoopForABit()138 void SpinEventLoopForABit() {
139   base::RunLoop loop;
140   SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
141       FROM_HERE, loop.QuitClosure(), TestTimeouts::tiny_timeout());
142   loop.Run();
143 }
144 
145 // Returns the reason why `value` matches, or doesn't match, `matcher`.
146 template <typename MatcherType, typename Value>
Explain(const MatcherType & matcher,const Value & value)147 std::string Explain(const MatcherType& matcher, const Value& value) {
148   testing::StringMatchResultListener listener;
149   testing::ExplainMatchResult(matcher, value, &listener);
150   return listener.str();
151 }
152 
__anon20d88b390202(const FilePath& path) 153 inline constexpr auto HasPath = [](const FilePath& path) {
154   return testing::Field(&Event::path, path);
155 };
__anon20d88b390302() 156 inline constexpr auto HasErrored = []() {
157   return testing::Field(&Event::error, testing::IsTrue());
158 };
__anon20d88b390402() 159 inline constexpr auto HasCookie = []() {
160   return testing::Field(
161       &Event::change_info,
162       testing::Field(&FilePathWatcher::ChangeInfo::cookie, testing::IsTrue()));
163 };
164 inline constexpr auto IsType =
__anon20d88b390502(const FilePathWatcher::ChangeType& change_type) 165     [](const FilePathWatcher::ChangeType& change_type) {
166       return testing::Field(
167           &Event::change_info,
168           testing::Field(&FilePathWatcher::ChangeInfo::change_type,
169                          change_type));
170     };
171 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
__anon20d88b390602() 172 inline constexpr auto IsFile = []() {
173   return testing::Field(
174       &Event::change_info,
175       testing::Field(&FilePathWatcher::ChangeInfo::file_path_type,
176                      FilePathWatcher::FilePathType::kFile));
177 };
__anon20d88b390702() 178 inline constexpr auto IsDirectory = []() {
179   return testing::Field(
180       &Event::change_info,
181       testing::Field(&FilePathWatcher::ChangeInfo::file_path_type,
182                      FilePathWatcher::FilePathType::kDirectory));
183 };
184 #else
__anon20d88b390802() 185 inline constexpr auto IsUnknownPathType = []() {
186   return testing::Field(
187       &Event::change_info,
188       testing::Field(&FilePathWatcher::ChangeInfo::file_path_type,
189                      FilePathWatcher::FilePathType::kUnknown));
190 };
191 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
192         // BUILDFLAG(IS_ANDROID)
193 
194 // Enables an accumulative, add-as-you-go pattern for expecting events:
195 //   - Do something that should fire `event1` on `delegate`
196 //   - Add `event1` to an `accumulated_event_expecter`
197 //   - Wait until `delegate` matches { `event1` }
198 //   - Do something that should fire `event2` on `delegate`
199 //   - Add `event2` to an `accumulated_event_expecter`
200 //   - Wait until `delegate` matches { `event1`, `event2` }
201 //   - ...
202 //
203 // These tests use an accumulative pattern due to the potential for
204 // false-positives, given that all we know is the number of changes at a given
205 // path (which is often fixed) and whether or not an error occurred (which is
206 // rare).
207 //
208 // TODO(https://crbug.com/1425601): This is not a common pattern. Generally,
209 // expectations are specified all-in-one at the start of a test, like so:
210 //   - Expect events { `event1`, `event2` }
211 //   - Do something that should fire `event1` on `delegate`
212 //   - Do something that should fire `event2` on `delegate`
213 //   - Wait until `delegate` matches { `event1`, `event2` }
214 //
215 // The potential for false-positives is much less if event types are known. We
216 // should consider moving towards the latter pattern
217 // (see `FilePathWatcherWithChangeInfoTest`) once that is supported.
218 class AccumulatingEventExpecter {
219  public:
GetMatcher()220   EventListMatcher GetMatcher() {
221     return testing::ContainerEq(expected_events_);
222   }
223 
GetAndResetExpectedEventsSinceLastWait()224   ExpectedEventsSinceLastWait GetAndResetExpectedEventsSinceLastWait() {
225     auto temp = expected_events_since_last_wait_;
226     expected_events_since_last_wait_ = ExpectedEventsSinceLastWait::kNone;
227     return temp;
228   }
229 
AddExpectedEventForPath(const FilePath & path,bool error=false)230   void AddExpectedEventForPath(const FilePath& path, bool error = false) {
231     expected_events_.emplace_back(ToEvent({}, path, error));
232     expected_events_since_last_wait_ = ExpectedEventsSinceLastWait::kSome;
233   }
234 
235  private:
236   std::list<Event> expected_events_;
237   ExpectedEventsSinceLastWait expected_events_since_last_wait_ =
238       ExpectedEventsSinceLastWait::kNone;
239 };
240 
241 class TestDelegateBase {
242  public:
243   TestDelegateBase() = default;
244   TestDelegateBase(const TestDelegateBase&) = delete;
245   TestDelegateBase& operator=(const TestDelegateBase&) = delete;
246   virtual ~TestDelegateBase() = default;
247 
248   virtual void OnFileChanged(const FilePath& path, bool error) = 0;
249   virtual void OnFileChangedWithInfo(
250       const FilePathWatcher::ChangeInfo& change_info,
251       const FilePath& path,
252       bool error) = 0;
253   virtual base::WeakPtr<TestDelegateBase> AsWeakPtr() = 0;
254 };
255 
256 // Receives and accumulates notifications from a specific `FilePathWatcher`.
257 // This class is not thread safe. All methods must be called from the sequence
258 // the instance is constructed on.
259 class TestDelegate final : public TestDelegateBase {
260  public:
TestDelegate()261   TestDelegate() : id_(g_next_delegate_id.GetNext()) {}
262   TestDelegate(const TestDelegate&) = delete;
263   TestDelegate& operator=(const TestDelegate&) = delete;
264   ~TestDelegate() override = default;
265 
266   // TestDelegateBase:
OnFileChanged(const FilePath & path,bool error)267   void OnFileChanged(const FilePath& path, bool error) override {
268     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
269     Event event = ToEvent({}, path, error);
270     received_events_.emplace_back(std::move(event));
271   }
OnFileChangedWithInfo(const FilePathWatcher::ChangeInfo & change_info,const FilePath & path,bool error)272   void OnFileChangedWithInfo(const FilePathWatcher::ChangeInfo& change_info,
273                              const FilePath& path,
274                              bool error) override {
275     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
276     Event event = ToEvent(change_info, path, error);
277     received_events_.emplace_back(std::move(event));
278   }
279 
AsWeakPtr()280   base::WeakPtr<TestDelegateBase> AsWeakPtr() override {
281     return weak_ptr_factory_.GetWeakPtr();
282   }
283 
284   // Gives all in-flight events a chance to arrive, then forgets all events that
285   // have been received by this delegate. This method may be a useful reset
286   // after performing a file system operation that may result in a variable
287   // sequence of events.
SpinAndDiscardAllReceivedEvents()288   void SpinAndDiscardAllReceivedEvents() {
289     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
290     SpinEventLoopForABit();
291     received_events_.clear();
292   }
293 
294   // Spin the event loop until `received_events_` match `matcher`, or we time
295   // out.
RunUntilEventsMatch(const EventListMatcher & matcher,ExpectedEventsSinceLastWait expected_events_since_last_wait,const Location & location=FROM_HERE)296   void RunUntilEventsMatch(
297       const EventListMatcher& matcher,
298       ExpectedEventsSinceLastWait expected_events_since_last_wait,
299       const Location& location = FROM_HERE) {
300     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
301 
302     if (expected_events_since_last_wait == ExpectedEventsSinceLastWait::kNone) {
303       // Give unexpected events a chance to arrive.
304       SpinEventLoopForABit();
305     }
306 
307     EXPECT_TRUE(test::RunUntil([&]() {
308       DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
309       return testing::Matches(matcher)(received_events_);
310     })) << "Timed out attemping to match events at "
311         << location.file_name() << ":" << location.line_number() << std::endl
312         << Explain(matcher, received_events_);
313   }
314   // Convenience method for above.
RunUntilEventsMatch(const EventListMatcher & matcher,const Location & location=FROM_HERE)315   void RunUntilEventsMatch(const EventListMatcher& matcher,
316                            const Location& location = FROM_HERE) {
317     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
318     return RunUntilEventsMatch(matcher, ExpectedEventsSinceLastWait::kSome,
319                                location);
320   }
321   // Convenience method for above.
RunUntilEventsMatch(AccumulatingEventExpecter & event_expecter,const Location & location=FROM_HERE)322   void RunUntilEventsMatch(AccumulatingEventExpecter& event_expecter,
323                            const Location& location = FROM_HERE) {
324     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
325 
326     return RunUntilEventsMatch(
327         event_expecter.GetMatcher(),
328         event_expecter.GetAndResetExpectedEventsSinceLastWait(), location);
329   }
330   // Convenience method for above when no events are expected.
SpinAndExpectNoEvents(const Location & location=FROM_HERE)331   void SpinAndExpectNoEvents(const Location& location = FROM_HERE) {
332     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
333 
334     return RunUntilEventsMatch(testing::IsEmpty(),
335                                ExpectedEventsSinceLastWait::kNone, location);
336   }
337 
events() const338   const std::list<Event>& events() const {
339     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
340     return received_events_;
341   }
342 
343  private:
344   SEQUENCE_CHECKER(sequence_checker_);
345 
346   // Uniquely generated ID used to tie events to this delegate.
347   const size_t id_;
348 
349   std::list<Event> received_events_ GUARDED_BY_CONTEXT(sequence_checker_);
350   base::WeakPtrFactory<TestDelegateBase> weak_ptr_factory_{this};
351 };
352 
353 }  // namespace
354 
355 #if BUILDFLAG(IS_FUCHSIA)
356 // FilePatchWatcherImpl is not implemented (see crbug.com/851641).
357 // Disable all tests.
358 #define FilePathWatcherTest DISABLED_FilePathWatcherTest
359 #endif
360 
361 class FilePathWatcherTest : public testing::Test {
362  public:
FilePathWatcherTest()363   FilePathWatcherTest()
364 #if BUILDFLAG(IS_POSIX)
365       : task_environment_(test::TaskEnvironment::MainThreadType::IO)
366 #endif
367   {
368   }
369 
370   FilePathWatcherTest(const FilePathWatcherTest&) = delete;
371   FilePathWatcherTest& operator=(const FilePathWatcherTest&) = delete;
372   ~FilePathWatcherTest() override = default;
373 
374  protected:
SetUp()375   void SetUp() override {
376 #if BUILDFLAG(IS_ANDROID)
377     // Watching files is only permitted when all parent directories are
378     // accessible, which is not the case for the default temp directory
379     // on Android which is under /data/data.  Use /sdcard instead.
380     // TODO(pauljensen): Remove this when crbug.com/475568 is fixed.
381     FilePath parent_dir;
382     ASSERT_TRUE(android::GetExternalStorageDirectory(&parent_dir));
383     ASSERT_TRUE(temp_dir_.CreateUniqueTempDirUnderPath(parent_dir));
384 #else   // BUILDFLAG(IS_ANDROID)
385     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
386 #endif  // BUILDFLAG(IS_ANDROID)
387   }
388 
TearDown()389   void TearDown() override { RunLoop().RunUntilIdle(); }
390 
test_file()391   FilePath test_file() {
392     return temp_dir_.GetPath().AppendASCII("FilePathWatcherTest");
393   }
394 
test_link()395   FilePath test_link() {
396     return temp_dir_.GetPath().AppendASCII("FilePathWatcherTest.lnk");
397   }
398 
399   bool SetupWatch(const FilePath& target,
400                   FilePathWatcher* watcher,
401                   TestDelegateBase* delegate,
402                   FilePathWatcher::Type watch_type);
403 
404   bool SetupWatchWithOptions(const FilePath& target,
405                              FilePathWatcher* watcher,
406                              TestDelegateBase* delegate,
407                              FilePathWatcher::WatchOptions watch_options);
408 
409   bool SetupWatchWithChangeInfo(const FilePath& target,
410                                 FilePathWatcher* watcher,
411                                 TestDelegateBase* delegate,
412                                 FilePathWatcher::WatchOptions watch_options);
413 
414   test::TaskEnvironment task_environment_;
415 
416   ScopedTempDir temp_dir_;
417 };
418 
SetupWatch(const FilePath & target,FilePathWatcher * watcher,TestDelegateBase * delegate,FilePathWatcher::Type watch_type)419 bool FilePathWatcherTest::SetupWatch(const FilePath& target,
420                                      FilePathWatcher* watcher,
421                                      TestDelegateBase* delegate,
422                                      FilePathWatcher::Type watch_type) {
423   return watcher->Watch(
424       target, watch_type,
425       BindRepeating(&TestDelegateBase::OnFileChanged, delegate->AsWeakPtr()));
426 }
427 
SetupWatchWithOptions(const FilePath & target,FilePathWatcher * watcher,TestDelegateBase * delegate,FilePathWatcher::WatchOptions watch_options)428 bool FilePathWatcherTest::SetupWatchWithOptions(
429     const FilePath& target,
430     FilePathWatcher* watcher,
431     TestDelegateBase* delegate,
432     FilePathWatcher::WatchOptions watch_options) {
433   return watcher->WatchWithOptions(
434       target, watch_options,
435       BindRepeating(&TestDelegateBase::OnFileChanged, delegate->AsWeakPtr()));
436 }
437 
SetupWatchWithChangeInfo(const FilePath & target,FilePathWatcher * watcher,TestDelegateBase * delegate,FilePathWatcher::WatchOptions watch_options)438 bool FilePathWatcherTest::SetupWatchWithChangeInfo(
439     const FilePath& target,
440     FilePathWatcher* watcher,
441     TestDelegateBase* delegate,
442     FilePathWatcher::WatchOptions watch_options) {
443   return watcher->WatchWithChangeInfo(
444       target, watch_options,
445       BindPostTaskToCurrentDefault(BindRepeating(
446           &TestDelegateBase::OnFileChangedWithInfo, delegate->AsWeakPtr())));
447 }
448 
449 // Basic test: Create the file and verify that we notice.
TEST_F(FilePathWatcherTest,NewFile)450 TEST_F(FilePathWatcherTest, NewFile) {
451   FilePathWatcher watcher;
452   TestDelegate delegate;
453   AccumulatingEventExpecter event_expecter;
454   ASSERT_TRUE(SetupWatch(test_file(), &watcher, &delegate,
455                          FilePathWatcher::Type::kNonRecursive));
456 
457   ASSERT_TRUE(WriteFile(test_file(), "content"));
458   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
459     event_expecter.AddExpectedEventForPath(test_file());
460   }
461   delegate.RunUntilEventsMatch(event_expecter);
462 }
463 
464 // Basic test: Create the directory and verify that we notice.
TEST_F(FilePathWatcherTest,NewDirectory)465 TEST_F(FilePathWatcherTest, NewDirectory) {
466   FilePathWatcher watcher;
467   TestDelegate delegate;
468   AccumulatingEventExpecter event_expecter;
469   ASSERT_TRUE(SetupWatch(test_file(), &watcher, &delegate,
470                          FilePathWatcher::Type::kNonRecursive));
471 
472   ASSERT_TRUE(CreateDirectory(test_file()));
473   event_expecter.AddExpectedEventForPath(test_file());
474   delegate.RunUntilEventsMatch(event_expecter);
475 }
476 
477 // Basic test: Create the directory and verify that we notice.
TEST_F(FilePathWatcherTest,NewDirectoryRecursiveWatch)478 TEST_F(FilePathWatcherTest, NewDirectoryRecursiveWatch) {
479   if (!FilePathWatcher::RecursiveWatchAvailable()) {
480     return;
481   }
482 
483   FilePathWatcher watcher;
484   TestDelegate delegate;
485   AccumulatingEventExpecter event_expecter;
486   ASSERT_TRUE(SetupWatch(test_file(), &watcher, &delegate,
487                          FilePathWatcher::Type::kRecursive));
488 
489   ASSERT_TRUE(CreateDirectory(test_file()));
490   event_expecter.AddExpectedEventForPath(test_file());
491   delegate.RunUntilEventsMatch(event_expecter);
492 }
493 
494 // Verify that modifying the file is caught.
TEST_F(FilePathWatcherTest,ModifiedFile)495 TEST_F(FilePathWatcherTest, ModifiedFile) {
496   ASSERT_TRUE(WriteFile(test_file(), "content"));
497 
498   FilePathWatcher watcher;
499   TestDelegate delegate;
500   AccumulatingEventExpecter event_expecter;
501   ASSERT_TRUE(SetupWatch(test_file(), &watcher, &delegate,
502                          FilePathWatcher::Type::kNonRecursive));
503 
504   // Now make sure we get notified if the file is modified.
505   ASSERT_TRUE(WriteFile(test_file(), "new content"));
506   event_expecter.AddExpectedEventForPath(test_file());
507   delegate.RunUntilEventsMatch(event_expecter);
508 }
509 
510 // Verify that creating the parent directory of the watched file is not caught.
TEST_F(FilePathWatcherTest,CreateParentDirectory)511 TEST_F(FilePathWatcherTest, CreateParentDirectory) {
512   FilePathWatcher watcher;
513   TestDelegate delegate;
514   FilePath parent(temp_dir_.GetPath().AppendASCII("parent"));
515   FilePath child(parent.AppendASCII("child"));
516 
517   ASSERT_TRUE(SetupWatch(child, &watcher, &delegate,
518                          FilePathWatcher::Type::kNonRecursive));
519 
520   // Now make sure we do not get notified when the parent is created.
521   ASSERT_TRUE(CreateDirectory(parent));
522   delegate.SpinAndExpectNoEvents();
523 }
524 
525 // Verify that changes to the sibling of the watched file are not caught.
TEST_F(FilePathWatcherTest,CreateSiblingFile)526 TEST_F(FilePathWatcherTest, CreateSiblingFile) {
527   FilePathWatcher watcher;
528   TestDelegate delegate;
529   ASSERT_TRUE(SetupWatch(test_file(), &watcher, &delegate,
530                          FilePathWatcher::Type::kNonRecursive));
531 
532   // Now make sure we do not get notified if a sibling of the watched file is
533   // created or modified.
534   ASSERT_TRUE(WriteFile(test_file().AddExtensionASCII(".swap"), "content"));
535   ASSERT_TRUE(WriteFile(test_file().AddExtensionASCII(".swap"), "new content"));
536   delegate.SpinAndExpectNoEvents();
537 }
538 
539 // Verify that changes to the sibling of the parent of the watched file are not
540 // caught.
TEST_F(FilePathWatcherTest,CreateParentSiblingFile)541 TEST_F(FilePathWatcherTest, CreateParentSiblingFile) {
542   FilePathWatcher watcher;
543   TestDelegate delegate;
544   FilePath parent(temp_dir_.GetPath().AppendASCII("parent"));
545   FilePath parent_sibling(temp_dir_.GetPath().AppendASCII("parent_sibling"));
546   FilePath child(parent.AppendASCII("child"));
547   ASSERT_TRUE(SetupWatch(child, &watcher, &delegate,
548                          FilePathWatcher::Type::kNonRecursive));
549 
550   // Don't notice changes to a sibling directory of `parent` while `parent` does
551   // not exist.
552   ASSERT_TRUE(CreateDirectory(parent_sibling));
553   ASSERT_TRUE(DeletePathRecursively(parent_sibling));
554 
555   // Don't notice changes to a sibling file of `parent` while `parent` does
556   // not exist.
557   ASSERT_TRUE(WriteFile(parent_sibling, "do not notice this"));
558   ASSERT_TRUE(DeleteFile(parent_sibling));
559 
560   // Don't notice the creation of `parent`.
561   ASSERT_TRUE(CreateDirectory(parent));
562 
563   // Don't notice changes to a sibling directory of `parent` while `parent`
564   // exists.
565   ASSERT_TRUE(CreateDirectory(parent_sibling));
566   ASSERT_TRUE(DeletePathRecursively(parent_sibling));
567 
568   // Don't notice changes to a sibling file of `parent` while `parent` exists.
569   ASSERT_TRUE(WriteFile(parent_sibling, "do not notice this"));
570   ASSERT_TRUE(DeleteFile(parent_sibling));
571 
572   delegate.SpinAndExpectNoEvents();
573 }
574 
575 // Verify that moving an unwatched file to a watched path is caught.
TEST_F(FilePathWatcherTest,MovedToFile)576 TEST_F(FilePathWatcherTest, MovedToFile) {
577   FilePath source_file(temp_dir_.GetPath().AppendASCII("source"));
578   ASSERT_TRUE(WriteFile(source_file, "content"));
579 
580   FilePathWatcher watcher;
581   TestDelegate delegate;
582   AccumulatingEventExpecter event_expecter;
583   ASSERT_TRUE(SetupWatch(test_file(), &watcher, &delegate,
584                          FilePathWatcher::Type::kNonRecursive));
585 
586   // Now make sure we get notified if the file is moved.
587   ASSERT_TRUE(Move(source_file, test_file()));
588   event_expecter.AddExpectedEventForPath(test_file());
589   delegate.RunUntilEventsMatch(event_expecter);
590 }
591 
592 // Verify that moving the watched file to an unwatched path is caught.
TEST_F(FilePathWatcherTest,MovedFromFile)593 TEST_F(FilePathWatcherTest, MovedFromFile) {
594   ASSERT_TRUE(WriteFile(test_file(), "content"));
595 
596   FilePathWatcher watcher;
597   TestDelegate delegate;
598   AccumulatingEventExpecter event_expecter;
599   ASSERT_TRUE(SetupWatch(test_file(), &watcher, &delegate,
600                          FilePathWatcher::Type::kNonRecursive));
601 
602   // Now make sure we get notified if the file is modified.
603   ASSERT_TRUE(Move(test_file(), temp_dir_.GetPath().AppendASCII("dest")));
604   event_expecter.AddExpectedEventForPath(test_file());
605   delegate.RunUntilEventsMatch(event_expecter);
606 }
607 
TEST_F(FilePathWatcherTest,DeletedFile)608 TEST_F(FilePathWatcherTest, DeletedFile) {
609   ASSERT_TRUE(WriteFile(test_file(), "content"));
610 
611   FilePathWatcher watcher;
612   TestDelegate delegate;
613   AccumulatingEventExpecter event_expecter;
614   ASSERT_TRUE(SetupWatch(test_file(), &watcher, &delegate,
615                          FilePathWatcher::Type::kNonRecursive));
616 
617   // Now make sure we get notified if the file is deleted.
618   DeleteFile(test_file());
619   event_expecter.AddExpectedEventForPath(test_file());
620   delegate.RunUntilEventsMatch(event_expecter);
621 }
622 
623 namespace {
624 
625 // Used by the DeleteDuringNotify test below.
626 // Deletes the FilePathWatcher when it's notified.
627 class Deleter final : public TestDelegateBase {
628  public:
Deleter(OnceClosure done_closure)629   explicit Deleter(OnceClosure done_closure)
630       : watcher_(std::make_unique<FilePathWatcher>()),
631         done_closure_(std::move(done_closure)) {}
632   Deleter(const Deleter&) = delete;
633   Deleter& operator=(const Deleter&) = delete;
634   ~Deleter() override = default;
635 
OnFileChanged(const FilePath &,bool)636   void OnFileChanged(const FilePath& /*path*/, bool /*error*/) override {
637     watcher_.reset();
638     std::move(done_closure_).Run();
639   }
OnFileChangedWithInfo(const FilePathWatcher::ChangeInfo &,const FilePath &,bool)640   void OnFileChangedWithInfo(const FilePathWatcher::ChangeInfo& /*change_info*/,
641                              const FilePath& /*path*/,
642                              bool /*error*/) override {
643     watcher_.reset();
644     std::move(done_closure_).Run();
645   }
646 
AsWeakPtr()647   base::WeakPtr<TestDelegateBase> AsWeakPtr() override {
648     return weak_ptr_factory_.GetWeakPtr();
649   }
650 
watcher() const651   FilePathWatcher* watcher() const { return watcher_.get(); }
652 
653  private:
654   std::unique_ptr<FilePathWatcher> watcher_;
655   OnceClosure done_closure_;
656   base::WeakPtrFactory<Deleter> weak_ptr_factory_{this};
657 };
658 
659 }  // namespace
660 
661 // Verify that deleting a watcher during the callback doesn't crash.
TEST_F(FilePathWatcherTest,DeleteDuringNotify)662 TEST_F(FilePathWatcherTest, DeleteDuringNotify) {
663   RunLoop run_loop;
664   Deleter deleter(run_loop.QuitClosure());
665   ASSERT_TRUE(SetupWatch(test_file(), deleter.watcher(), &deleter,
666                          FilePathWatcher::Type::kNonRecursive));
667 
668   ASSERT_TRUE(WriteFile(test_file(), "content"));
669   run_loop.Run();
670 
671   // We win if we haven't crashed yet.
672   // Might as well double-check it got deleted, too.
673   ASSERT_TRUE(deleter.watcher() == nullptr);
674 }
675 
676 // Verify that deleting the watcher works even if there is a pending
677 // notification.
TEST_F(FilePathWatcherTest,DestroyWithPendingNotification)678 TEST_F(FilePathWatcherTest, DestroyWithPendingNotification) {
679   TestDelegate delegate;
680   FilePathWatcher watcher;
681   ASSERT_TRUE(SetupWatch(test_file(), &watcher, &delegate,
682                          FilePathWatcher::Type::kNonRecursive));
683   ASSERT_TRUE(WriteFile(test_file(), "content"));
684 }
685 
TEST_F(FilePathWatcherTest,MultipleWatchersSingleFile)686 TEST_F(FilePathWatcherTest, MultipleWatchersSingleFile) {
687   FilePathWatcher watcher1, watcher2;
688   TestDelegate delegate1, delegate2;
689   AccumulatingEventExpecter event_expecter1, event_expecter2;
690   ASSERT_TRUE(SetupWatch(test_file(), &watcher1, &delegate1,
691                          FilePathWatcher::Type::kNonRecursive));
692   ASSERT_TRUE(SetupWatch(test_file(), &watcher2, &delegate2,
693                          FilePathWatcher::Type::kNonRecursive));
694 
695   // Expect to be notified for writing to a new file for each delegate.
696   ASSERT_TRUE(WriteFile(test_file(), "content"));
697   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
698     event_expecter1.AddExpectedEventForPath(test_file());
699     event_expecter2.AddExpectedEventForPath(test_file());
700   }
701   delegate1.RunUntilEventsMatch(event_expecter1);
702   delegate2.RunUntilEventsMatch(event_expecter2);
703 }
704 
705 // Verify that watching a file whose parent directory doesn't exist yet works if
706 // the directory and file are created eventually.
TEST_F(FilePathWatcherTest,NonExistentDirectory)707 TEST_F(FilePathWatcherTest, NonExistentDirectory) {
708   FilePathWatcher watcher;
709   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
710   FilePath file(dir.AppendASCII("file"));
711   TestDelegate delegate;
712   ASSERT_TRUE(SetupWatch(file, &watcher, &delegate,
713                          FilePathWatcher::Type::kNonRecursive));
714 
715   // The delegate is only watching the file. Parent directory creation should
716   // not trigger an event.
717   ASSERT_TRUE(CreateDirectory(dir));
718   // TODO(https://crbug.com/1432064): Expect that no events are fired.
719 
720   // It may take some time for `watcher` to re-construct its watch list, so it's
721   // possible an event is missed. _At least_ one event should be fired, though.
722   ASSERT_TRUE(WriteFile(file, "content"));
723   VLOG(1) << "Waiting for file creation";
724   delegate.RunUntilEventsMatch(testing::Not(testing::IsEmpty()),
725                                ExpectedEventsSinceLastWait::kSome);
726 
727   delegate.SpinAndDiscardAllReceivedEvents();
728   AccumulatingEventExpecter event_expecter;
729 
730   ASSERT_TRUE(WriteFile(file, "content v2"));
731   VLOG(1) << "Waiting for file change";
732   event_expecter.AddExpectedEventForPath(file);
733   delegate.RunUntilEventsMatch(event_expecter);
734 
735   ASSERT_TRUE(DeleteFile(file));
736   VLOG(1) << "Waiting for file deletion";
737   event_expecter.AddExpectedEventForPath(file);
738   delegate.RunUntilEventsMatch(event_expecter);
739 }
740 
741 // Exercises watch reconfiguration for the case that directories on the path
742 // are rapidly created.
TEST_F(FilePathWatcherTest,DirectoryChain)743 TEST_F(FilePathWatcherTest, DirectoryChain) {
744   FilePath path(temp_dir_.GetPath());
745   std::vector<std::string> dir_names;
746   for (int i = 0; i < 20; i++) {
747     std::string dir(StringPrintf("d%d", i));
748     dir_names.push_back(dir);
749     path = path.AppendASCII(dir);
750   }
751 
752   FilePathWatcher watcher;
753   FilePath file(path.AppendASCII("file"));
754   TestDelegate delegate;
755   ASSERT_TRUE(SetupWatch(file, &watcher, &delegate,
756                          FilePathWatcher::Type::kNonRecursive));
757 
758   FilePath sub_path(temp_dir_.GetPath());
759   for (const auto& dir_name : dir_names) {
760     sub_path = sub_path.AppendASCII(dir_name);
761     ASSERT_TRUE(CreateDirectory(sub_path));
762     // TODO(https://crbug.com/1432064): Expect that no events are fired.
763   }
764 
765   // It may take some time for `watcher` to re-construct its watch list, so it's
766   // possible an event is missed. _At least_ one event should be fired, though.
767   VLOG(1) << "Create File";
768   ASSERT_TRUE(WriteFile(file, "content"));
769   VLOG(1) << "Waiting for file creation + modification";
770   delegate.RunUntilEventsMatch(testing::Not(testing::IsEmpty()),
771                                ExpectedEventsSinceLastWait::kSome);
772 
773   delegate.SpinAndDiscardAllReceivedEvents();
774   AccumulatingEventExpecter event_expecter;
775 
776   ASSERT_TRUE(WriteFile(file, "content v2"));
777   VLOG(1) << "Waiting for file modification";
778   event_expecter.AddExpectedEventForPath(file);
779   delegate.RunUntilEventsMatch(event_expecter);
780 }
781 
TEST_F(FilePathWatcherTest,DisappearingDirectory)782 TEST_F(FilePathWatcherTest, DisappearingDirectory) {
783   FilePathWatcher watcher;
784   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
785   FilePath file(dir.AppendASCII("file"));
786   ASSERT_TRUE(CreateDirectory(dir));
787   ASSERT_TRUE(WriteFile(file, "content"));
788   TestDelegate delegate;
789   AccumulatingEventExpecter event_expecter;
790   ASSERT_TRUE(SetupWatch(file, &watcher, &delegate,
791                          FilePathWatcher::Type::kNonRecursive));
792 
793   ASSERT_TRUE(DeletePathRecursively(dir));
794   event_expecter.AddExpectedEventForPath(file);
795 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
796   // TODO(https://crbug.com/1432044): Figure out why this may fire two events on
797   // inotify. Only the file is being watched, so presumably there should only be
798   // one deletion event.
799   event_expecter.AddExpectedEventForPath(file);
800 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
801         // BUILDFLAG(IS_ANDROID)
802   delegate.RunUntilEventsMatch(event_expecter);
803 }
804 
805 // Tests that a file that is deleted and reappears is tracked correctly.
TEST_F(FilePathWatcherTest,DeleteAndRecreate)806 TEST_F(FilePathWatcherTest, DeleteAndRecreate) {
807   ASSERT_TRUE(WriteFile(test_file(), "content"));
808   FilePathWatcher watcher;
809   TestDelegate delegate;
810   AccumulatingEventExpecter event_expecter;
811   ASSERT_TRUE(SetupWatch(test_file(), &watcher, &delegate,
812                          FilePathWatcher::Type::kNonRecursive));
813 
814   ASSERT_TRUE(DeleteFile(test_file()));
815   VLOG(1) << "Waiting for file deletion";
816   event_expecter.AddExpectedEventForPath(test_file());
817   delegate.RunUntilEventsMatch(event_expecter);
818 
819   ASSERT_TRUE(WriteFile(test_file(), "content"));
820   VLOG(1) << "Waiting for file creation + modification";
821   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
822     event_expecter.AddExpectedEventForPath(test_file());
823   }
824   delegate.RunUntilEventsMatch(event_expecter);
825 }
826 
827 // TODO(https://crbug.com/1432064): Split into smaller tests.
TEST_F(FilePathWatcherTest,WatchDirectory)828 TEST_F(FilePathWatcherTest, WatchDirectory) {
829   FilePathWatcher watcher;
830   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
831   FilePath file1(dir.AppendASCII("file1"));
832   FilePath file2(dir.AppendASCII("file2"));
833   TestDelegate delegate;
834   AccumulatingEventExpecter event_expecter;
835   ASSERT_TRUE(SetupWatch(dir, &watcher, &delegate,
836                          FilePathWatcher::Type::kNonRecursive));
837 
838   ASSERT_TRUE(CreateDirectory(dir));
839   VLOG(1) << "Waiting for directory creation";
840   event_expecter.AddExpectedEventForPath(dir);
841   delegate.RunUntilEventsMatch(event_expecter);
842 
843   ASSERT_TRUE(WriteFile(file1, "content"));
844   VLOG(1) << "Waiting for file1 creation + modification";
845   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
846     event_expecter.AddExpectedEventForPath(dir);
847   }
848   delegate.RunUntilEventsMatch(event_expecter);
849 
850 #if !BUILDFLAG(IS_APPLE)
851   ASSERT_TRUE(WriteFile(file1, "content v2"));
852   // Mac implementation does not detect files modified in a directory.
853   // TODO(https://crbug.com/1432064): Expect that no events are fired on Mac.
854   // TODO(https://crbug.com/1019297): Consider using FSEvents to support
855   // watching a directory and its immediate children, as Type::kNonRecursive
856   // does on other platforms.
857   VLOG(1) << "Waiting for file1 modification";
858   event_expecter.AddExpectedEventForPath(dir);
859   delegate.RunUntilEventsMatch(event_expecter);
860 #endif  // !BUILDFLAG(IS_APPLE)
861 
862   ASSERT_TRUE(DeleteFile(file1));
863   VLOG(1) << "Waiting for file1 deletion";
864   event_expecter.AddExpectedEventForPath(dir);
865   delegate.RunUntilEventsMatch(event_expecter);
866 
867   ASSERT_TRUE(WriteFile(file2, "content"));
868   VLOG(1) << "Waiting for file2 creation + modification";
869   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
870     event_expecter.AddExpectedEventForPath(dir);
871   }
872   delegate.RunUntilEventsMatch(event_expecter);
873 }
874 
TEST_F(FilePathWatcherTest,MoveParent)875 TEST_F(FilePathWatcherTest, MoveParent) {
876   FilePathWatcher file_watcher, subdir_watcher;
877   TestDelegate file_delegate, subdir_delegate;
878   AccumulatingEventExpecter file_event_expecter, subdir_event_expecter;
879   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
880   FilePath dest(temp_dir_.GetPath().AppendASCII("dest"));
881   FilePath subdir(dir.AppendASCII("subdir"));
882   FilePath file(subdir.AppendASCII("file"));
883   ASSERT_TRUE(SetupWatch(file, &file_watcher, &file_delegate,
884                          FilePathWatcher::Type::kNonRecursive));
885   ASSERT_TRUE(SetupWatch(subdir, &subdir_watcher, &subdir_delegate,
886                          FilePathWatcher::Type::kNonRecursive));
887 
888   // Setup a directory hierarchy.
889   // We should only get notified on `subdir_delegate` of its creation.
890   ASSERT_TRUE(CreateDirectory(subdir));
891   subdir_event_expecter.AddExpectedEventForPath(subdir);
892   // TODO(https://crbug.com/1432064): Expect that no events are fired on the
893   // file delegate.
894   subdir_delegate.RunUntilEventsMatch(subdir_event_expecter);
895 
896   ASSERT_TRUE(WriteFile(file, "content"));
897   VLOG(1) << "Waiting for file creation + modification";
898   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
899     file_event_expecter.AddExpectedEventForPath(file);
900     subdir_event_expecter.AddExpectedEventForPath(subdir);
901   }
902   file_delegate.RunUntilEventsMatch(file_event_expecter);
903   subdir_delegate.RunUntilEventsMatch(subdir_event_expecter);
904 
905   Move(dir, dest);
906   VLOG(1) << "Waiting for directory move";
907   file_event_expecter.AddExpectedEventForPath(file);
908   subdir_event_expecter.AddExpectedEventForPath(subdir);
909   file_delegate.RunUntilEventsMatch(file_event_expecter);
910   subdir_delegate.RunUntilEventsMatch(subdir_event_expecter);
911 }
912 
TEST_F(FilePathWatcherTest,RecursiveWatch)913 TEST_F(FilePathWatcherTest, RecursiveWatch) {
914   FilePathWatcher watcher;
915   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
916   TestDelegate delegate;
917   AccumulatingEventExpecter event_expecter;
918   bool setup_result =
919       SetupWatch(dir, &watcher, &delegate, FilePathWatcher::Type::kRecursive);
920   if (!FilePathWatcher::RecursiveWatchAvailable()) {
921     ASSERT_FALSE(setup_result);
922     return;
923   }
924   ASSERT_TRUE(setup_result);
925 
926   // TODO(https://crbug.com/1432064): Create a version of this test which also
927   // verifies that the events occur on the correct file path if the watcher is
928   // set up to record the target of the event.
929 
930   // Main directory("dir") creation.
931   ASSERT_TRUE(CreateDirectory(dir));
932   event_expecter.AddExpectedEventForPath(dir);
933   delegate.RunUntilEventsMatch(event_expecter);
934 
935   // Create "$dir/file1".
936   FilePath file1(dir.AppendASCII("file1"));
937   ASSERT_TRUE(WriteFile(file1, "content"));
938   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
939     event_expecter.AddExpectedEventForPath(dir);
940   }
941   delegate.RunUntilEventsMatch(event_expecter);
942 
943   // Create "$dir/subdir".
944   FilePath subdir(dir.AppendASCII("subdir"));
945   ASSERT_TRUE(CreateDirectory(subdir));
946   event_expecter.AddExpectedEventForPath(dir);
947   delegate.RunUntilEventsMatch(event_expecter);
948 
949   // Create "$dir/subdir/subdir2".
950   FilePath subdir2(subdir.AppendASCII("subdir2"));
951   ASSERT_TRUE(CreateDirectory(subdir2));
952   event_expecter.AddExpectedEventForPath(dir);
953   delegate.RunUntilEventsMatch(event_expecter);
954 
955   // Rename "$dir/subdir/subdir2" to "$dir/subdir/subdir2b".
956   FilePath subdir2b(subdir.AppendASCII("subdir2b"));
957   Move(subdir2, subdir2b);
958   event_expecter.AddExpectedEventForPath(dir);
959 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
960   // inotify generates separate IN_MOVED_TO and IN_MOVED_FROM events for a
961   // rename. Since both the source and destination are within the scope of this
962   // watch, both events should be received.
963   event_expecter.AddExpectedEventForPath(dir);
964 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
965         // BUILDFLAG(IS_ANDROID)
966   delegate.RunUntilEventsMatch(event_expecter);
967 
968 // Mac and Win don't generate events for Touch.
969 // TODO(https://crbug.com/1432064): Add explicit expectations for Mac and Win.
970 // Android TouchFile returns false.
971 #if !(BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID))
972   // Touch "$dir".
973   Time access_time;
974   ASSERT_TRUE(Time::FromString("Wed, 16 Nov 1994, 00:00:00", &access_time));
975   ASSERT_TRUE(TouchFile(dir, access_time, access_time));
976   // TODO(https://crbug.com/1432044): Investigate why we're getting two events
977   // here from inotify.
978   event_expecter.AddExpectedEventForPath(dir);
979   event_expecter.AddExpectedEventForPath(dir);
980   delegate.RunUntilEventsMatch(event_expecter);
981   // TODO(https://crbug.com/1432064): Add a test touching `subdir`.
982 #endif  // !(BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID))
983 
984   // Create "$dir/subdir/subdir_file1".
985   FilePath subdir_file1(subdir.AppendASCII("subdir_file1"));
986   ASSERT_TRUE(WriteFile(subdir_file1, "content"));
987   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
988     event_expecter.AddExpectedEventForPath(dir);
989   }
990   delegate.RunUntilEventsMatch(event_expecter);
991 
992   // Create "$dir/subdir/subdir_child_dir".
993   FilePath subdir_child_dir(subdir.AppendASCII("subdir_child_dir"));
994   ASSERT_TRUE(CreateDirectory(subdir_child_dir));
995   event_expecter.AddExpectedEventForPath(dir);
996   delegate.RunUntilEventsMatch(event_expecter);
997 
998   // Create "$dir/subdir/subdir_child_dir/child_dir_file1".
999   FilePath child_dir_file1(subdir_child_dir.AppendASCII("child_dir_file1"));
1000   ASSERT_TRUE(WriteFile(child_dir_file1, "content v2"));
1001   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
1002     event_expecter.AddExpectedEventForPath(dir);
1003   }
1004   delegate.RunUntilEventsMatch(event_expecter);
1005 
1006   // Write into "$dir/subdir/subdir_child_dir/child_dir_file1".
1007   ASSERT_TRUE(WriteFile(child_dir_file1, "content"));
1008   event_expecter.AddExpectedEventForPath(dir);
1009   delegate.RunUntilEventsMatch(event_expecter);
1010 
1011 // Apps cannot change file attributes on Android in /sdcard as /sdcard uses the
1012 // "fuse" file system, while /data uses "ext4".  Running these tests in /data
1013 // would be preferable and allow testing file attributes and symlinks.
1014 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
1015 // the |temp_dir_| in /data.
1016 #if !BUILDFLAG(IS_ANDROID)
1017   // Modify "$dir/subdir/subdir_child_dir/child_dir_file1" attributes.
1018   ASSERT_TRUE(MakeFileUnreadable(child_dir_file1));
1019   event_expecter.AddExpectedEventForPath(dir);
1020   delegate.RunUntilEventsMatch(event_expecter);
1021 #endif  // !BUILDFLAG(IS_ANDROID))
1022 
1023   // Delete "$dir/subdir/subdir_file1".
1024   ASSERT_TRUE(DeleteFile(subdir_file1));
1025   event_expecter.AddExpectedEventForPath(dir);
1026   delegate.RunUntilEventsMatch(event_expecter);
1027 
1028   // Delete "$dir/subdir/subdir_child_dir/child_dir_file1".
1029   ASSERT_TRUE(DeleteFile(child_dir_file1));
1030   event_expecter.AddExpectedEventForPath(dir);
1031   delegate.RunUntilEventsMatch(event_expecter);
1032 }
1033 
1034 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
1035 // Apps cannot create symlinks on Android in /sdcard as /sdcard uses the
1036 // "fuse" file system, while /data uses "ext4".  Running these tests in /data
1037 // would be preferable and allow testing file attributes and symlinks.
1038 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
1039 // the |temp_dir_| in /data.
1040 //
1041 // This test is disabled on Fuchsia since it doesn't support symlinking.
TEST_F(FilePathWatcherTest,RecursiveWithSymLink)1042 TEST_F(FilePathWatcherTest, RecursiveWithSymLink) {
1043   if (!FilePathWatcher::RecursiveWatchAvailable()) {
1044     return;
1045   }
1046 
1047   FilePathWatcher watcher;
1048   FilePath test_dir(temp_dir_.GetPath().AppendASCII("test_dir"));
1049   ASSERT_TRUE(CreateDirectory(test_dir));
1050   FilePath symlink(test_dir.AppendASCII("symlink"));
1051   TestDelegate delegate;
1052   AccumulatingEventExpecter event_expecter;
1053   ASSERT_TRUE(SetupWatch(symlink, &watcher, &delegate,
1054                          FilePathWatcher::Type::kRecursive));
1055 
1056   // TODO(https://crbug.com/1432064): Figure out what the intended behavior here
1057   // is. Many symlink operations don't seem to be supported on Mac, while in
1058   // other cases Mac fires more events than expected.
1059 
1060   // Link creation.
1061   FilePath target1(temp_dir_.GetPath().AppendASCII("target1"));
1062   ASSERT_TRUE(CreateSymbolicLink(target1, symlink));
1063   event_expecter.AddExpectedEventForPath(symlink);
1064   delegate.RunUntilEventsMatch(event_expecter);
1065 
1066   // Target1 creation.
1067   ASSERT_TRUE(CreateDirectory(target1));
1068   event_expecter.AddExpectedEventForPath(symlink);
1069   delegate.RunUntilEventsMatch(event_expecter);
1070 
1071   // Create a file in target1.
1072   FilePath target1_file(target1.AppendASCII("file"));
1073   ASSERT_TRUE(WriteFile(target1_file, "content"));
1074   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
1075     event_expecter.AddExpectedEventForPath(symlink);
1076   }
1077   delegate.RunUntilEventsMatch(event_expecter);
1078 
1079   // Link change.
1080   FilePath target2(temp_dir_.GetPath().AppendASCII("target2"));
1081   ASSERT_TRUE(CreateDirectory(target2));
1082   // TODO(https://crbug.com/1432064): Expect that no events are fired.
1083 
1084   ASSERT_TRUE(DeleteFile(symlink));
1085   event_expecter.AddExpectedEventForPath(symlink);
1086   delegate.RunUntilEventsMatch(event_expecter);
1087 
1088   ASSERT_TRUE(CreateSymbolicLink(target2, symlink));
1089   event_expecter.AddExpectedEventForPath(symlink);
1090   delegate.RunUntilEventsMatch(event_expecter);
1091 
1092   // Create a file in target2.
1093   FilePath target2_file(target2.AppendASCII("file"));
1094   ASSERT_TRUE(WriteFile(target2_file, "content"));
1095   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
1096     event_expecter.AddExpectedEventForPath(symlink);
1097   }
1098   delegate.RunUntilEventsMatch(event_expecter);
1099 }
1100 #endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
1101 
TEST_F(FilePathWatcherTest,MoveChild)1102 TEST_F(FilePathWatcherTest, MoveChild) {
1103   FilePathWatcher file_watcher, subdir_watcher;
1104   TestDelegate file_delegate, subdir_delegate;
1105   AccumulatingEventExpecter file_event_expecter, subdir_event_expecter;
1106   FilePath source_dir(temp_dir_.GetPath().AppendASCII("source"));
1107   FilePath source_subdir(source_dir.AppendASCII("subdir"));
1108   FilePath source_file(source_subdir.AppendASCII("file"));
1109   FilePath dest_dir(temp_dir_.GetPath().AppendASCII("dest"));
1110   FilePath dest_subdir(dest_dir.AppendASCII("subdir"));
1111   FilePath dest_file(dest_subdir.AppendASCII("file"));
1112 
1113   // Setup a directory hierarchy.
1114   ASSERT_TRUE(CreateDirectory(source_subdir));
1115   ASSERT_TRUE(WriteFile(source_file, "content"));
1116 
1117   ASSERT_TRUE(SetupWatch(dest_file, &file_watcher, &file_delegate,
1118                          FilePathWatcher::Type::kNonRecursive));
1119   ASSERT_TRUE(SetupWatch(dest_subdir, &subdir_watcher, &subdir_delegate,
1120                          FilePathWatcher::Type::kNonRecursive));
1121 
1122   // Move the directory into place, s.t. the watched file appears.
1123   ASSERT_TRUE(Move(source_dir, dest_dir));
1124   file_event_expecter.AddExpectedEventForPath(dest_file);
1125   subdir_event_expecter.AddExpectedEventForPath(dest_subdir);
1126   file_delegate.RunUntilEventsMatch(file_event_expecter);
1127   subdir_delegate.RunUntilEventsMatch(subdir_event_expecter);
1128 }
1129 
1130 // Verify that changing attributes on a file is caught
1131 #if BUILDFLAG(IS_ANDROID)
1132 // Apps cannot change file attributes on Android in /sdcard as /sdcard uses the
1133 // "fuse" file system, while /data uses "ext4".  Running these tests in /data
1134 // would be preferable and allow testing file attributes and symlinks.
1135 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
1136 // the |temp_dir_| in /data.
1137 #define FileAttributesChanged DISABLED_FileAttributesChanged
1138 #endif  // BUILDFLAG(IS_ANDROID)
TEST_F(FilePathWatcherTest,FileAttributesChanged)1139 TEST_F(FilePathWatcherTest, FileAttributesChanged) {
1140   ASSERT_TRUE(WriteFile(test_file(), "content"));
1141   FilePathWatcher watcher;
1142   TestDelegate delegate;
1143   AccumulatingEventExpecter event_expecter;
1144   ASSERT_TRUE(SetupWatch(test_file(), &watcher, &delegate,
1145                          FilePathWatcher::Type::kNonRecursive));
1146 
1147   // Now make sure we get notified if the file is modified.
1148   ASSERT_TRUE(MakeFileUnreadable(test_file()));
1149   event_expecter.AddExpectedEventForPath(test_file());
1150   delegate.RunUntilEventsMatch(event_expecter);
1151 }
1152 
1153 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
1154 
1155 // Verify that creating a symlink is caught.
TEST_F(FilePathWatcherTest,CreateLink)1156 TEST_F(FilePathWatcherTest, CreateLink) {
1157   FilePathWatcher watcher;
1158   TestDelegate delegate;
1159   AccumulatingEventExpecter event_expecter;
1160   // Note that we are watching the symlink.
1161   ASSERT_TRUE(SetupWatch(test_link(), &watcher, &delegate,
1162                          FilePathWatcher::Type::kNonRecursive));
1163 
1164   // Now make sure we get notified if the link is created.
1165   // Note that test_file() doesn't have to exist.
1166   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
1167   event_expecter.AddExpectedEventForPath(test_link());
1168   delegate.RunUntilEventsMatch(event_expecter);
1169 }
1170 
1171 // Verify that deleting a symlink is caught.
TEST_F(FilePathWatcherTest,DeleteLink)1172 TEST_F(FilePathWatcherTest, DeleteLink) {
1173   // Unfortunately this test case only works if the link target exists.
1174   // TODO(craig) fix this as part of crbug.com/91561.
1175   ASSERT_TRUE(WriteFile(test_file(), "content"));
1176   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
1177   FilePathWatcher watcher;
1178   TestDelegate delegate;
1179   AccumulatingEventExpecter event_expecter;
1180   ASSERT_TRUE(SetupWatch(test_link(), &watcher, &delegate,
1181                          FilePathWatcher::Type::kNonRecursive));
1182 
1183   // Now make sure we get notified if the link is deleted.
1184   ASSERT_TRUE(DeleteFile(test_link()));
1185   event_expecter.AddExpectedEventForPath(test_link());
1186   delegate.RunUntilEventsMatch(event_expecter);
1187 }
1188 
1189 // Verify that modifying a target file that a link is pointing to
1190 // when we are watching the link is caught.
TEST_F(FilePathWatcherTest,ModifiedLinkedFile)1191 TEST_F(FilePathWatcherTest, ModifiedLinkedFile) {
1192   ASSERT_TRUE(WriteFile(test_file(), "content"));
1193   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
1194   FilePathWatcher watcher;
1195   TestDelegate delegate;
1196   AccumulatingEventExpecter event_expecter;
1197   // Note that we are watching the symlink.
1198   ASSERT_TRUE(SetupWatch(test_link(), &watcher, &delegate,
1199                          FilePathWatcher::Type::kNonRecursive));
1200 
1201   // Now make sure we get notified if the file is modified.
1202   ASSERT_TRUE(WriteFile(test_file(), "new content"));
1203   event_expecter.AddExpectedEventForPath(test_link());
1204   delegate.RunUntilEventsMatch(event_expecter);
1205 }
1206 
1207 // Verify that creating a target file that a link is pointing to
1208 // when we are watching the link is caught.
TEST_F(FilePathWatcherTest,CreateTargetLinkedFile)1209 TEST_F(FilePathWatcherTest, CreateTargetLinkedFile) {
1210   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
1211   FilePathWatcher watcher;
1212   TestDelegate delegate;
1213   AccumulatingEventExpecter event_expecter;
1214   // Note that we are watching the symlink.
1215   ASSERT_TRUE(SetupWatch(test_link(), &watcher, &delegate,
1216                          FilePathWatcher::Type::kNonRecursive));
1217 
1218   // Now make sure we get notified if the target file is created.
1219   ASSERT_TRUE(WriteFile(test_file(), "content"));
1220   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
1221     event_expecter.AddExpectedEventForPath(test_link());
1222   }
1223   delegate.RunUntilEventsMatch(event_expecter);
1224 }
1225 
1226 // Verify that deleting a target file that a link is pointing to
1227 // when we are watching the link is caught.
TEST_F(FilePathWatcherTest,DeleteTargetLinkedFile)1228 TEST_F(FilePathWatcherTest, DeleteTargetLinkedFile) {
1229   ASSERT_TRUE(WriteFile(test_file(), "content"));
1230   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
1231   FilePathWatcher watcher;
1232   TestDelegate delegate;
1233   AccumulatingEventExpecter event_expecter;
1234   // Note that we are watching the symlink.
1235   ASSERT_TRUE(SetupWatch(test_link(), &watcher, &delegate,
1236                          FilePathWatcher::Type::kNonRecursive));
1237 
1238   // Now make sure we get notified if the target file is deleted.
1239   ASSERT_TRUE(DeleteFile(test_file()));
1240   event_expecter.AddExpectedEventForPath(test_link());
1241   delegate.RunUntilEventsMatch(event_expecter);
1242 }
1243 
1244 // Verify that watching a file whose parent directory is a link that
1245 // doesn't exist yet works if the symlink is created eventually.
TEST_F(FilePathWatcherTest,LinkedDirectoryPart1)1246 TEST_F(FilePathWatcherTest, LinkedDirectoryPart1) {
1247   FilePathWatcher watcher;
1248   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
1249   FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
1250   FilePath file(dir.AppendASCII("file"));
1251   FilePath linkfile(link_dir.AppendASCII("file"));
1252   TestDelegate delegate;
1253   AccumulatingEventExpecter event_expecter;
1254   // dir/file should exist.
1255   ASSERT_TRUE(CreateDirectory(dir));
1256   ASSERT_TRUE(WriteFile(file, "content"));
1257   // Note that we are watching dir.lnk/file which doesn't exist yet.
1258   ASSERT_TRUE(SetupWatch(linkfile, &watcher, &delegate,
1259                          FilePathWatcher::Type::kNonRecursive));
1260 
1261   ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
1262   VLOG(1) << "Waiting for link creation";
1263   event_expecter.AddExpectedEventForPath(linkfile);
1264   delegate.RunUntilEventsMatch(event_expecter);
1265 
1266   ASSERT_TRUE(WriteFile(file, "content v2"));
1267   VLOG(1) << "Waiting for file creation + modification";
1268   // TODO(https://crbug.com/1432064): Should this fire two events on inotify?
1269   event_expecter.AddExpectedEventForPath(linkfile);
1270   delegate.RunUntilEventsMatch(event_expecter);
1271 
1272   ASSERT_TRUE(WriteFile(file, "content v2"));
1273   VLOG(1) << "Waiting for file change";
1274   event_expecter.AddExpectedEventForPath(linkfile);
1275   delegate.RunUntilEventsMatch(event_expecter);
1276 
1277   ASSERT_TRUE(DeleteFile(file));
1278   VLOG(1) << "Waiting for file deletion";
1279   event_expecter.AddExpectedEventForPath(linkfile);
1280   delegate.RunUntilEventsMatch(event_expecter);
1281 }
1282 
1283 // Verify that watching a file whose parent directory is a
1284 // dangling symlink works if the directory is created eventually.
1285 // TODO(https://crbug.com/1432064): Add test coverage for symlinked file
1286 // creation independent of a corresponding write.
TEST_F(FilePathWatcherTest,LinkedDirectoryPart2)1287 TEST_F(FilePathWatcherTest, LinkedDirectoryPart2) {
1288   FilePathWatcher watcher;
1289   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
1290   FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
1291   FilePath file(dir.AppendASCII("file"));
1292   FilePath linkfile(link_dir.AppendASCII("file"));
1293   TestDelegate delegate;
1294 
1295   // Now create the link from dir.lnk pointing to dir but
1296   // neither dir nor dir/file exist yet.
1297   ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
1298   // Note that we are watching dir.lnk/file.
1299   ASSERT_TRUE(SetupWatch(linkfile, &watcher, &delegate,
1300                          FilePathWatcher::Type::kNonRecursive));
1301 
1302   ASSERT_TRUE(CreateDirectory(dir));
1303   // TODO(https://crbug.com/1432064): Expect that no events are fired.
1304 
1305   // It may take some time for `watcher` to re-construct its watch list, so it's
1306   // possible an event is missed. _At least_ one event should be fired, though.
1307   ASSERT_TRUE(WriteFile(file, "content"));
1308   VLOG(1) << "Waiting for file creation";
1309   delegate.RunUntilEventsMatch(testing::Not(testing::IsEmpty()),
1310                                ExpectedEventsSinceLastWait::kSome);
1311 
1312   delegate.SpinAndDiscardAllReceivedEvents();
1313   AccumulatingEventExpecter event_expecter;
1314 
1315   ASSERT_TRUE(WriteFile(file, "content v2"));
1316   VLOG(1) << "Waiting for file change";
1317   event_expecter.AddExpectedEventForPath(linkfile);
1318   delegate.RunUntilEventsMatch(event_expecter);
1319 
1320   ASSERT_TRUE(DeleteFile(file));
1321   VLOG(1) << "Waiting for file deletion";
1322   event_expecter.AddExpectedEventForPath(linkfile);
1323   delegate.RunUntilEventsMatch(event_expecter);
1324 }
1325 
1326 // Verify that watching a file with a symlink on the path
1327 // to the file works.
TEST_F(FilePathWatcherTest,LinkedDirectoryPart3)1328 TEST_F(FilePathWatcherTest, LinkedDirectoryPart3) {
1329   FilePathWatcher watcher;
1330   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
1331   FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
1332   FilePath file(dir.AppendASCII("file"));
1333   FilePath linkfile(link_dir.AppendASCII("file"));
1334   TestDelegate delegate;
1335   AccumulatingEventExpecter event_expecter;
1336   ASSERT_TRUE(CreateDirectory(dir));
1337   ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
1338   // Note that we are watching dir.lnk/file but the file doesn't exist yet.
1339   ASSERT_TRUE(SetupWatch(linkfile, &watcher, &delegate,
1340                          FilePathWatcher::Type::kNonRecursive));
1341 
1342   ASSERT_TRUE(WriteFile(file, "content"));
1343   VLOG(1) << "Waiting for file creation";
1344   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
1345     event_expecter.AddExpectedEventForPath(linkfile);
1346   }
1347   delegate.RunUntilEventsMatch(event_expecter);
1348 
1349   ASSERT_TRUE(WriteFile(file, "content v2"));
1350   VLOG(1) << "Waiting for file change";
1351   event_expecter.AddExpectedEventForPath(linkfile);
1352   delegate.RunUntilEventsMatch(event_expecter);
1353 
1354   ASSERT_TRUE(DeleteFile(file));
1355   VLOG(1) << "Waiting for file deletion";
1356   event_expecter.AddExpectedEventForPath(linkfile);
1357   delegate.RunUntilEventsMatch(event_expecter);
1358 }
1359 
1360 // Regression tests that FilePathWatcherImpl does not leave its reference in
1361 // `g_inotify_reader` due to a race in recursive watch.
1362 // See https://crbug.com/990004.
TEST_F(FilePathWatcherTest,RacyRecursiveWatch)1363 TEST_F(FilePathWatcherTest, RacyRecursiveWatch) {
1364   if (!FilePathWatcher::RecursiveWatchAvailable()) {
1365     GTEST_SKIP();
1366   }
1367 
1368   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
1369 
1370   // Create and delete many subdirs. 20 is an arbitrary number big enough
1371   // to have more chances to make FilePathWatcherImpl leak watchers.
1372   std::vector<FilePath> subdirs;
1373   for (int i = 0; i < 20; ++i) {
1374     subdirs.emplace_back(dir.AppendASCII(StringPrintf("subdir_%d", i)));
1375   }
1376 
1377   Thread subdir_updater("SubDir Updater");
1378   ASSERT_TRUE(subdir_updater.Start());
1379 
1380   auto subdir_update_task = BindLambdaForTesting([&]() {
1381     for (const auto& subdir : subdirs) {
1382       // First update event to trigger watch callback.
1383       ASSERT_TRUE(CreateDirectory(subdir));
1384 
1385       // Second update event. The notification sent for this event will race
1386       // with the upcoming deletion of the directory below. This test is about
1387       // verifying that the impl handles this.
1388       FilePath subdir_file(subdir.AppendASCII("subdir_file"));
1389       ASSERT_TRUE(WriteFile(subdir_file, "content"));
1390 
1391       // Racy subdir delete to trigger watcher leak.
1392       ASSERT_TRUE(DeletePathRecursively(subdir));
1393     }
1394   });
1395 
1396   // Try the racy subdir update 100 times.
1397   for (int i = 0; i < 100; ++i) {
1398     RunLoop run_loop;
1399     auto watcher = std::make_unique<FilePathWatcher>();
1400 
1401     // Keep watch callback in `watcher_callback` so that "watcher.reset()"
1402     // inside does not release the callback and the lambda capture with it.
1403     // Otherwise, accessing `run_loop` as part of the lamda capture would be
1404     // use-after-free under asan.
1405     auto watcher_callback =
1406         BindLambdaForTesting([&](const FilePath& path, bool error) {
1407           // Release watchers in callback so that the leaked watchers of
1408           // the subdir stays. Otherwise, when the subdir is deleted,
1409           // its delete event would clean up leaked watchers in
1410           // `g_inotify_reader`.
1411           watcher.reset();
1412 
1413           run_loop.Quit();
1414         });
1415 
1416     bool setup_result = watcher->Watch(dir, FilePathWatcher::Type::kRecursive,
1417                                        watcher_callback);
1418     ASSERT_TRUE(setup_result);
1419 
1420     subdir_updater.task_runner()->PostTask(FROM_HERE, subdir_update_task);
1421 
1422     // Wait for the watch callback.
1423     run_loop.Run();
1424 
1425     // `watcher` should have been released.
1426     ASSERT_FALSE(watcher);
1427 
1428     // There should be no outstanding watchers.
1429     ASSERT_FALSE(FilePathWatcher::HasWatchesForTest());
1430   }
1431 }
1432 
1433 // Verify that "Watch()" returns false and callback is not invoked when limit is
1434 // hit during setup.
TEST_F(FilePathWatcherTest,InotifyLimitInWatch)1435 TEST_F(FilePathWatcherTest, InotifyLimitInWatch) {
1436   auto watcher = std::make_unique<FilePathWatcher>();
1437 
1438   // "test_file()" is like "/tmp/__unique_path__/FilePathWatcherTest" and has 4
1439   // dir components ("/" + 3 named parts). "Watch()" creates inotify watches
1440   // for each dir component of the given dir. It would fail with limit set to 1.
1441   ScopedMaxNumberOfInotifyWatchesOverrideForTest max_inotify_watches(1);
1442   ASSERT_FALSE(
1443       watcher->Watch(test_file(), FilePathWatcher::Type::kNonRecursive,
1444                      BindLambdaForTesting([&](const FilePath& path,
1445                                               bool error) { ADD_FAILURE(); })));
1446 
1447   // Triggers update but callback should not be invoked.
1448   ASSERT_TRUE(WriteFile(test_file(), "content"));
1449 
1450   // Ensures that the callback did not happen.
1451   RunLoop().RunUntilIdle();
1452 }
1453 
1454 // Verify that "error=true" callback happens when limit is hit during update.
TEST_F(FilePathWatcherTest,InotifyLimitInUpdate)1455 TEST_F(FilePathWatcherTest, InotifyLimitInUpdate) {
1456   enum kTestType {
1457     // Destroy watcher in "error=true" callback.
1458     // No crash/deadlock when releasing watcher in the callback.
1459     kDestroyWatcher,
1460 
1461     // Do not destroy watcher in "error=true" callback.
1462     kDoNothing,
1463   };
1464 
1465   for (auto callback_type : {kDestroyWatcher, kDoNothing}) {
1466     SCOPED_TRACE(testing::Message() << "type=" << callback_type);
1467 
1468     RunLoop run_loop;
1469     auto watcher = std::make_unique<FilePathWatcher>();
1470 
1471     bool error_callback_called = false;
1472     auto watcher_callback =
1473         BindLambdaForTesting([&](const FilePath& path, bool error) {
1474           // No callback should happen after "error=true" one.
1475           ASSERT_FALSE(error_callback_called);
1476 
1477           if (!error) {
1478             return;
1479           }
1480 
1481           error_callback_called = true;
1482 
1483           if (callback_type == kDestroyWatcher) {
1484             watcher.reset();
1485           }
1486 
1487           run_loop.Quit();
1488         });
1489     ASSERT_TRUE(watcher->Watch(
1490         test_file(), FilePathWatcher::Type::kNonRecursive, watcher_callback));
1491 
1492     ScopedMaxNumberOfInotifyWatchesOverrideForTest max_inotify_watches(1);
1493 
1494     // Triggers update and over limit.
1495     ASSERT_TRUE(WriteFile(test_file(), "content"));
1496 
1497     run_loop.Run();
1498 
1499     // More update but no more callback should happen.
1500     ASSERT_TRUE(DeleteFile(test_file()));
1501     RunLoop().RunUntilIdle();
1502   }
1503 }
1504 
1505 // Similar to InotifyLimitInUpdate but test a recursive watcher.
TEST_F(FilePathWatcherTest,InotifyLimitInUpdateRecursive)1506 TEST_F(FilePathWatcherTest, InotifyLimitInUpdateRecursive) {
1507   enum kTestType {
1508     // Destroy watcher in "error=true" callback.
1509     // No crash/deadlock when releasing watcher in the callback.
1510     kDestroyWatcher,
1511 
1512     // Do not destroy watcher in "error=true" callback.
1513     kDoNothing,
1514   };
1515 
1516   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
1517 
1518   for (auto callback_type : {kDestroyWatcher, kDoNothing}) {
1519     SCOPED_TRACE(testing::Message() << "type=" << callback_type);
1520 
1521     RunLoop run_loop;
1522     auto watcher = std::make_unique<FilePathWatcher>();
1523 
1524     bool error_callback_called = false;
1525     auto watcher_callback =
1526         BindLambdaForTesting([&](const FilePath& path, bool error) {
1527           // No callback should happen after "error=true" one.
1528           ASSERT_FALSE(error_callback_called);
1529 
1530           if (!error) {
1531             return;
1532           }
1533 
1534           error_callback_called = true;
1535 
1536           if (callback_type == kDestroyWatcher) {
1537             watcher.reset();
1538           }
1539 
1540           run_loop.Quit();
1541         });
1542     ASSERT_TRUE(watcher->Watch(dir, FilePathWatcher::Type::kRecursive,
1543                                watcher_callback));
1544 
1545     constexpr size_t kMaxLimit = 10u;
1546     ScopedMaxNumberOfInotifyWatchesOverrideForTest max_inotify_watches(
1547         kMaxLimit);
1548 
1549     // Triggers updates and over limit.
1550     for (size_t i = 0; i < kMaxLimit; ++i) {
1551       FilePath subdir = dir.AppendASCII(StringPrintf("subdir_%" PRIuS, i));
1552       ASSERT_TRUE(CreateDirectory(subdir));
1553     }
1554 
1555     run_loop.Run();
1556 
1557     // More update but no more callback should happen.
1558     for (size_t i = 0; i < kMaxLimit; ++i) {
1559       FilePath subdir = dir.AppendASCII(StringPrintf("subdir_%" PRIuS, i));
1560       ASSERT_TRUE(DeleteFile(subdir));
1561     }
1562     RunLoop().RunUntilIdle();
1563   }
1564 }
1565 
1566 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
1567 
1568 // TODO(fxbug.dev/60109): enable BUILDFLAG(IS_FUCHSIA) when implemented.
1569 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
1570 
TEST_F(FilePathWatcherTest,ReturnFullPath_RecursiveInRootFolder)1571 TEST_F(FilePathWatcherTest, ReturnFullPath_RecursiveInRootFolder) {
1572   FilePathWatcher directory_watcher;
1573   FilePath watched_folder(temp_dir_.GetPath().AppendASCII("watched_folder"));
1574   FilePath file(watched_folder.AppendASCII("file"));
1575 
1576   ASSERT_TRUE(CreateDirectory(watched_folder));
1577 
1578   TestDelegate delegate;
1579   AccumulatingEventExpecter event_expecter;
1580   ASSERT_TRUE(SetupWatchWithOptions(watched_folder, &directory_watcher,
1581                                     &delegate,
1582                                     {.type = FilePathWatcher::Type::kRecursive,
1583                                      .report_modified_path = true}));
1584 
1585   // Triggers two events:
1586   // create on /watched_folder/file.
1587   // modify on /watched_folder/file.
1588   ASSERT_TRUE(WriteFile(file, "test"));
1589   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
1590     event_expecter.AddExpectedEventForPath(file);
1591   }
1592   delegate.RunUntilEventsMatch(event_expecter);
1593 
1594   // Expects modify on /watched_folder/file.
1595   ASSERT_TRUE(WriteFile(file, "test123"));
1596   event_expecter.AddExpectedEventForPath(file);
1597   delegate.RunUntilEventsMatch(event_expecter);
1598 
1599   // Expects delete on /watched_folder/file.
1600   ASSERT_TRUE(DeleteFile(file));
1601   event_expecter.AddExpectedEventForPath(file);
1602   delegate.RunUntilEventsMatch(event_expecter);
1603 }
1604 
TEST_F(FilePathWatcherTest,ReturnFullPath_RecursiveInNestedFolder)1605 TEST_F(FilePathWatcherTest, ReturnFullPath_RecursiveInNestedFolder) {
1606   FilePathWatcher directory_watcher;
1607   FilePath watched_folder(temp_dir_.GetPath().AppendASCII("watched_folder"));
1608   FilePath subfolder(watched_folder.AppendASCII("subfolder"));
1609   FilePath file(subfolder.AppendASCII("file"));
1610 
1611   ASSERT_TRUE(CreateDirectory(watched_folder));
1612 
1613   TestDelegate delegate;
1614   AccumulatingEventExpecter event_expecter;
1615   ASSERT_TRUE(SetupWatchWithOptions(watched_folder, &directory_watcher,
1616                                     &delegate,
1617                                     {.type = FilePathWatcher::Type::kRecursive,
1618                                      .report_modified_path = true}));
1619 
1620   // Expects create on /watched_folder/subfolder.
1621   ASSERT_TRUE(CreateDirectory(subfolder));
1622   event_expecter.AddExpectedEventForPath(subfolder);
1623   delegate.RunUntilEventsMatch(event_expecter);
1624 
1625   // Triggers two events:
1626   // create on /watched_folder/subfolder/file.
1627   // modify on /watched_folder/subfolder/file.
1628   ASSERT_TRUE(WriteFile(file, "test"));
1629   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
1630     event_expecter.AddExpectedEventForPath(file);
1631   }
1632   delegate.RunUntilEventsMatch(event_expecter);
1633 
1634   // Expects modify on /watched_folder/subfolder/file.
1635   ASSERT_TRUE(WriteFile(file, "test123"));
1636   event_expecter.AddExpectedEventForPath(file);
1637   delegate.RunUntilEventsMatch(event_expecter);
1638 
1639   // Expects delete on /watched_folder/subfolder/file.
1640   ASSERT_TRUE(DeleteFile(file));
1641   event_expecter.AddExpectedEventForPath(file);
1642   delegate.RunUntilEventsMatch(event_expecter);
1643 
1644   // Expects delete on /watched_folder/subfolder.
1645   ASSERT_TRUE(DeleteFile(subfolder));
1646   event_expecter.AddExpectedEventForPath(subfolder);
1647   delegate.RunUntilEventsMatch(event_expecter);
1648 }
1649 
TEST_F(FilePathWatcherTest,ReturnFullPath_NonRecursiveInRootFolder)1650 TEST_F(FilePathWatcherTest, ReturnFullPath_NonRecursiveInRootFolder) {
1651   FilePathWatcher directory_watcher;
1652   FilePath watched_folder(temp_dir_.GetPath().AppendASCII("watched_folder"));
1653   FilePath file(watched_folder.AppendASCII("file"));
1654 
1655   ASSERT_TRUE(CreateDirectory(watched_folder));
1656 
1657   TestDelegate delegate;
1658   AccumulatingEventExpecter event_expecter;
1659   ASSERT_TRUE(
1660       SetupWatchWithOptions(watched_folder, &directory_watcher, &delegate,
1661                             {.type = FilePathWatcher::Type::kNonRecursive,
1662                              .report_modified_path = true}));
1663 
1664   // Triggers two events:
1665   // create on /watched_folder/file.
1666   // modify on /watched_folder/file.
1667   ASSERT_TRUE(WriteFile(file, "test"));
1668   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
1669     event_expecter.AddExpectedEventForPath(file);
1670   }
1671   delegate.RunUntilEventsMatch(event_expecter);
1672 
1673   // Expects modify on /watched_folder/file.
1674   ASSERT_TRUE(WriteFile(file, "test123"));
1675   event_expecter.AddExpectedEventForPath(file);
1676   delegate.RunUntilEventsMatch(event_expecter);
1677 
1678   // Expects delete on /watched_folder/file.
1679   ASSERT_TRUE(DeleteFile(file));
1680   event_expecter.AddExpectedEventForPath(file);
1681   delegate.RunUntilEventsMatch(event_expecter);
1682 }
1683 
TEST_F(FilePathWatcherTest,ReturnFullPath_NonRecursiveRemoveEnclosingFolder)1684 TEST_F(FilePathWatcherTest, ReturnFullPath_NonRecursiveRemoveEnclosingFolder) {
1685   FilePathWatcher directory_watcher;
1686   FilePath root_folder(temp_dir_.GetPath().AppendASCII("root_folder"));
1687   FilePath folder(root_folder.AppendASCII("folder"));
1688   FilePath watched_folder(folder.AppendASCII("watched_folder"));
1689   FilePath file(watched_folder.AppendASCII("file"));
1690 
1691   ASSERT_TRUE(CreateDirectory(watched_folder));
1692   ASSERT_TRUE(WriteFile(file, "test"));
1693 
1694   TestDelegate delegate;
1695   AccumulatingEventExpecter event_expecter;
1696   ASSERT_TRUE(
1697       SetupWatchWithOptions(watched_folder, &directory_watcher, &delegate,
1698                             {.type = FilePathWatcher::Type::kNonRecursive,
1699                              .report_modified_path = true}));
1700 
1701   // Triggers three events:
1702   // delete on /watched_folder/file.
1703   // delete on /watched_folder twice.
1704   // TODO(https://crbug.com/1432044): Figure out why duplicate events are fired
1705   // on `watched_folder`.
1706   ASSERT_TRUE(DeletePathRecursively(folder));
1707   event_expecter.AddExpectedEventForPath(file);
1708   event_expecter.AddExpectedEventForPath(watched_folder);
1709   event_expecter.AddExpectedEventForPath(watched_folder);
1710   delegate.RunUntilEventsMatch(event_expecter);
1711 }
1712 
TEST_F(FilePathWatcherTest,ReturnWatchedPath_RecursiveInRootFolder)1713 TEST_F(FilePathWatcherTest, ReturnWatchedPath_RecursiveInRootFolder) {
1714   FilePathWatcher directory_watcher;
1715   FilePath watched_folder(temp_dir_.GetPath().AppendASCII("watched_folder"));
1716   FilePath file(watched_folder.AppendASCII("file"));
1717 
1718   ASSERT_TRUE(CreateDirectory(watched_folder));
1719 
1720   TestDelegate delegate;
1721   AccumulatingEventExpecter event_expecter;
1722   ASSERT_TRUE(
1723       SetupWatchWithOptions(watched_folder, &directory_watcher, &delegate,
1724                             {.type = FilePathWatcher::Type::kRecursive}));
1725 
1726   // Triggers two events:
1727   // create on /watched_folder.
1728   // modify on /watched_folder.
1729   ASSERT_TRUE(WriteFile(file, "test"));
1730   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
1731     event_expecter.AddExpectedEventForPath(watched_folder);
1732   }
1733   delegate.RunUntilEventsMatch(event_expecter);
1734 
1735   // Expects modify on /watched_folder.
1736   ASSERT_TRUE(WriteFile(file, "test123"));
1737   event_expecter.AddExpectedEventForPath(watched_folder);
1738   delegate.RunUntilEventsMatch(event_expecter);
1739 
1740   // Expects delete on /watched_folder.
1741   ASSERT_TRUE(DeleteFile(file));
1742   event_expecter.AddExpectedEventForPath(watched_folder);
1743   delegate.RunUntilEventsMatch(event_expecter);
1744 }
1745 
TEST_F(FilePathWatcherTest,ReturnWatchedPath_NonRecursiveInRootFolder)1746 TEST_F(FilePathWatcherTest, ReturnWatchedPath_NonRecursiveInRootFolder) {
1747   FilePathWatcher directory_watcher;
1748   FilePath watched_folder(temp_dir_.GetPath().AppendASCII("watched_folder"));
1749   FilePath file(watched_folder.AppendASCII("file"));
1750 
1751   ASSERT_TRUE(CreateDirectory(watched_folder));
1752 
1753   TestDelegate delegate;
1754   AccumulatingEventExpecter event_expecter;
1755   ASSERT_TRUE(
1756       SetupWatchWithOptions(watched_folder, &directory_watcher, &delegate,
1757                             {.type = FilePathWatcher::Type::kNonRecursive}));
1758 
1759   // Triggers two events:
1760   // Expects create /watched_folder.
1761   // Expects modify /watched_folder.
1762   ASSERT_TRUE(WriteFile(file, "test"));
1763   for (size_t i = 0; i < kExpectedEventsForNewFileWrite; ++i) {
1764     event_expecter.AddExpectedEventForPath(watched_folder);
1765   }
1766   delegate.RunUntilEventsMatch(event_expecter);
1767 
1768   // Expects modify on /watched_folder.
1769   ASSERT_TRUE(WriteFile(file, "test123"));
1770   event_expecter.AddExpectedEventForPath(watched_folder);
1771   delegate.RunUntilEventsMatch(event_expecter);
1772 
1773   // Expects delete on /watched_folder.
1774   ASSERT_TRUE(DeleteFile(file));
1775   event_expecter.AddExpectedEventForPath(watched_folder);
1776   delegate.RunUntilEventsMatch(event_expecter);
1777 }
1778 
1779 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
1780         // BUILDFLAG(IS_ANDROID)
1781 
1782 namespace {
1783 
1784 enum Permission { Read, Write, Execute };
1785 
1786 #if BUILDFLAG(IS_APPLE)
ChangeFilePermissions(const FilePath & path,Permission perm,bool allow)1787 bool ChangeFilePermissions(const FilePath& path, Permission perm, bool allow) {
1788   struct stat stat_buf;
1789 
1790   if (stat(path.value().c_str(), &stat_buf) != 0) {
1791     return false;
1792   }
1793 
1794   mode_t mode = 0;
1795   switch (perm) {
1796     case Read:
1797       mode = S_IRUSR | S_IRGRP | S_IROTH;
1798       break;
1799     case Write:
1800       mode = S_IWUSR | S_IWGRP | S_IWOTH;
1801       break;
1802     case Execute:
1803       mode = S_IXUSR | S_IXGRP | S_IXOTH;
1804       break;
1805     default:
1806       ADD_FAILURE() << "unknown perm " << perm;
1807       return false;
1808   }
1809   if (allow) {
1810     stat_buf.st_mode |= mode;
1811   } else {
1812     stat_buf.st_mode &= ~mode;
1813   }
1814   return chmod(path.value().c_str(), stat_buf.st_mode) == 0;
1815 }
1816 #endif  // BUILDFLAG(IS_APPLE)
1817 
1818 }  // namespace
1819 
1820 #if BUILDFLAG(IS_APPLE)
1821 // Linux implementation of FilePathWatcher doesn't catch attribute changes.
1822 // http://crbug.com/78043
1823 // Windows implementation of FilePathWatcher catches attribute changes that
1824 // don't affect the path being watched.
1825 // http://crbug.com/78045
1826 
1827 // Verify that changing attributes on a directory works.
TEST_F(FilePathWatcherTest,DirAttributesChanged)1828 TEST_F(FilePathWatcherTest, DirAttributesChanged) {
1829   FilePath test_dir1(
1830       temp_dir_.GetPath().AppendASCII("DirAttributesChangedDir1"));
1831   FilePath test_dir2(test_dir1.AppendASCII("DirAttributesChangedDir2"));
1832   FilePath test_file(test_dir2.AppendASCII("DirAttributesChangedFile"));
1833   // Setup a directory hierarchy.
1834   ASSERT_TRUE(CreateDirectory(test_dir1));
1835   ASSERT_TRUE(CreateDirectory(test_dir2));
1836   ASSERT_TRUE(WriteFile(test_file, "content"));
1837 
1838   FilePathWatcher watcher;
1839   TestDelegate delegate;
1840   AccumulatingEventExpecter event_expecter;
1841   ASSERT_TRUE(SetupWatch(test_file, &watcher, &delegate,
1842                          FilePathWatcher::Type::kNonRecursive));
1843 
1844   // We should not get notified in this case as it hasn't affected our ability
1845   // to access the file.
1846   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, false));
1847   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, true));
1848   // TODO(https://crbug.com/1432064): Expect that no events are fired.
1849 
1850   // We should get notified in this case because filepathwatcher can no
1851   // longer access the file.
1852   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, false));
1853   event_expecter.AddExpectedEventForPath(test_file);
1854   delegate.RunUntilEventsMatch(event_expecter);
1855 
1856   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, true));
1857   // TODO(https://crbug.com/1432064): Expect that no events are fired.
1858 }
1859 
1860 #endif  // BUILDFLAG(IS_APPLE)
1861 
1862 #if BUILDFLAG(IS_APPLE)
1863 
1864 // Fail fast if trying to trivially watch a non-existent item.
TEST_F(FilePathWatcherTest,TrivialNoDir)1865 TEST_F(FilePathWatcherTest, TrivialNoDir) {
1866   const FilePath tmp_dir = temp_dir_.GetPath();
1867   const FilePath non_existent = tmp_dir.Append(FILE_PATH_LITERAL("nope"));
1868 
1869   FilePathWatcher watcher;
1870   TestDelegate delegate;
1871   ASSERT_FALSE(SetupWatch(non_existent, &watcher, &delegate,
1872                           FilePathWatcher::Type::kTrivial));
1873 }
1874 
1875 // Succeed starting a watch on a directory.
TEST_F(FilePathWatcherTest,TrivialDirStart)1876 TEST_F(FilePathWatcherTest, TrivialDirStart) {
1877   const FilePath tmp_dir = temp_dir_.GetPath();
1878 
1879   FilePathWatcher watcher;
1880   TestDelegate delegate;
1881   ASSERT_TRUE(SetupWatch(tmp_dir, &watcher, &delegate,
1882                          FilePathWatcher::Type::kTrivial));
1883 }
1884 
1885 // Observe a change on a directory
TEST_F(FilePathWatcherTest,TrivialDirChange)1886 TEST_F(FilePathWatcherTest, TrivialDirChange) {
1887   const FilePath tmp_dir = temp_dir_.GetPath();
1888 
1889   FilePathWatcher watcher;
1890   TestDelegate delegate;
1891   AccumulatingEventExpecter event_expecter;
1892   ASSERT_TRUE(SetupWatch(tmp_dir, &watcher, &delegate,
1893                          FilePathWatcher::Type::kTrivial));
1894 
1895   ASSERT_TRUE(TouchFile(tmp_dir, Time::Now(), Time::Now()));
1896   event_expecter.AddExpectedEventForPath(tmp_dir);
1897   delegate.RunUntilEventsMatch(event_expecter);
1898 }
1899 
1900 // Observe no change when a parent is modified.
TEST_F(FilePathWatcherTest,TrivialParentDirChange)1901 TEST_F(FilePathWatcherTest, TrivialParentDirChange) {
1902   const FilePath tmp_dir = temp_dir_.GetPath();
1903   const FilePath sub_dir1 = tmp_dir.Append(FILE_PATH_LITERAL("subdir"));
1904   const FilePath sub_dir2 = sub_dir1.Append(FILE_PATH_LITERAL("subdir_redux"));
1905 
1906   ASSERT_TRUE(CreateDirectory(sub_dir1));
1907   ASSERT_TRUE(CreateDirectory(sub_dir2));
1908 
1909   FilePathWatcher watcher;
1910   TestDelegate delegate;
1911   AccumulatingEventExpecter event_expecter;
1912   ASSERT_TRUE(SetupWatch(sub_dir2, &watcher, &delegate,
1913                          FilePathWatcher::Type::kTrivial));
1914 
1915   // There should be no notification for a change to |sub_dir2|'s parent.
1916   ASSERT_TRUE(Move(sub_dir1, tmp_dir.Append(FILE_PATH_LITERAL("over_here"))));
1917   delegate.RunUntilEventsMatch(event_expecter);
1918 }
1919 
1920 // Do not crash when a directory is moved; https://crbug.com/1156603.
TEST_F(FilePathWatcherTest,TrivialDirMove)1921 TEST_F(FilePathWatcherTest, TrivialDirMove) {
1922   const FilePath tmp_dir = temp_dir_.GetPath();
1923   const FilePath sub_dir = tmp_dir.Append(FILE_PATH_LITERAL("subdir"));
1924 
1925   ASSERT_TRUE(CreateDirectory(sub_dir));
1926 
1927   FilePathWatcher watcher;
1928   TestDelegate delegate;
1929   AccumulatingEventExpecter event_expecter;
1930   ASSERT_TRUE(SetupWatch(sub_dir, &watcher, &delegate,
1931                          FilePathWatcher::Type::kTrivial));
1932 
1933   ASSERT_TRUE(Move(sub_dir, tmp_dir.Append(FILE_PATH_LITERAL("over_here"))));
1934   event_expecter.AddExpectedEventForPath(sub_dir, /**error=*/true);
1935   delegate.RunUntilEventsMatch(event_expecter);
1936 }
1937 
1938 #endif  // BUILDFLAG(IS_APPLE)
1939 
1940 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
1941 // TODO(https://crbug.com/1432064): Ideally most all of the tests above would be
1942 // parameterized in this way.
1943 // TODO(https://crbug.com/1425601): ChangeInfo is currently only supported by
1944 // the inotify based implementation.
1945 class FilePathWatcherWithChangeInfoTest
1946     : public FilePathWatcherTest,
1947       public testing::WithParamInterface<
1948           std::tuple<FilePathWatcher::Type, bool>> {
1949  public:
SetUp()1950   void SetUp() override { FilePathWatcherTest::SetUp(); }
1951 
1952  protected:
type() const1953   FilePathWatcher::Type type() const { return std::get<0>(GetParam()); }
report_modified_path() const1954   bool report_modified_path() const { return std::get<1>(GetParam()); }
1955 
GetWatchOptions() const1956   FilePathWatcher::WatchOptions GetWatchOptions() const {
1957     return FilePathWatcher::WatchOptions{
1958         .type = type(), .report_modified_path = report_modified_path()};
1959   }
1960 };
1961 
TEST_P(FilePathWatcherWithChangeInfoTest,NewFile)1962 TEST_P(FilePathWatcherWithChangeInfoTest, NewFile) {
1963   // Each change should have these attributes.
1964   const auto each_event_matcher = testing::Each(
1965       testing::AllOf(HasPath(test_file()), testing::Not(HasErrored()), IsFile(),
1966                      testing::Not(HasCookie())));
1967   // Match the expected change types, in this order.
1968   // TODO(https://crbug.com/1425601): Update this when change types are
1969   // supported on more platforms.
1970   static_assert(kExpectedEventsForNewFileWrite == 2);
1971   const auto sequence_matcher =
1972       testing::ElementsAre(IsType(FilePathWatcher::ChangeType::kCreated),
1973                            IsType(FilePathWatcher::ChangeType::kModified));
1974   // Put it all together.
1975   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
1976 
1977   FilePathWatcher watcher;
1978   TestDelegate delegate;
1979   ASSERT_TRUE(SetupWatchWithChangeInfo(test_file(), &watcher, &delegate,
1980                                        GetWatchOptions()));
1981 
1982   ASSERT_TRUE(WriteFile(test_file(), "content"));
1983   delegate.RunUntilEventsMatch(matcher);
1984 }
1985 
TEST_P(FilePathWatcherWithChangeInfoTest,NewDirectory)1986 TEST_P(FilePathWatcherWithChangeInfoTest, NewDirectory) {
1987   const auto matcher = testing::ElementsAre(testing::AllOf(
1988       HasPath(test_file()), testing::Not(HasErrored()), IsDirectory(),
1989       IsType(FilePathWatcher::ChangeType::kCreated),
1990       testing::Not(HasCookie())));
1991 
1992   FilePathWatcher watcher;
1993   TestDelegate delegate;
1994   ASSERT_TRUE(SetupWatchWithChangeInfo(test_file(), &watcher, &delegate,
1995                                        GetWatchOptions()));
1996 
1997   ASSERT_TRUE(CreateDirectory(test_file()));
1998   delegate.RunUntilEventsMatch(matcher);
1999 }
2000 
TEST_P(FilePathWatcherWithChangeInfoTest,ModifiedFile)2001 TEST_P(FilePathWatcherWithChangeInfoTest, ModifiedFile) {
2002   // TODO(https://crbug.com/1425601): Some platforms will not support
2003   // `ChangeType::kContentsModified`. Update this matcher once support for those
2004   // platforms is added.
2005   const auto matcher = testing::ElementsAre(
2006       testing::AllOf(HasPath(test_file()), testing::Not(HasErrored()), IsFile(),
2007                      IsType(FilePathWatcher::ChangeType::kModified),
2008                      testing::Not(HasCookie())));
2009 
2010   ASSERT_TRUE(WriteFile(test_file(), "content"));
2011 #if BUILDFLAG(IS_ANDROID)
2012   // TODO(https://crbug.com/1496350): There appears to be a race condition
2013   // between setting up the inotify watch and the processing of the file system
2014   // notifications created while setting up the file system for this test. Spin
2015   // the event loop to ensure that the events have been processed by the time
2016   // the inotify watch has been set up.
2017   SpinEventLoopForABit();
2018 #endif  // BUILDFLAG(IS_ANDROID)
2019 
2020   FilePathWatcher watcher;
2021   TestDelegate delegate;
2022   ASSERT_TRUE(SetupWatchWithChangeInfo(test_file(), &watcher, &delegate,
2023                                        GetWatchOptions()));
2024 
2025   ASSERT_TRUE(WriteFile(test_file(), "new content"));
2026   delegate.RunUntilEventsMatch(matcher);
2027 }
2028 
TEST_P(FilePathWatcherWithChangeInfoTest,MovedFile)2029 TEST_P(FilePathWatcherWithChangeInfoTest, MovedFile) {
2030   // TODO(https://crbug.com/1425601): Some platforms will not provide separate
2031   // events for "moved from" and "moved to". Update this matcher once support
2032   // for those platforms is added.
2033   const auto matcher = testing::ElementsAre(
2034       testing::AllOf(HasPath(test_file()), testing::Not(HasErrored()), IsFile(),
2035                      IsType(FilePathWatcher::ChangeType::kMoved), HasCookie()));
2036 
2037   FilePath source_file(temp_dir_.GetPath().AppendASCII("source"));
2038   ASSERT_TRUE(WriteFile(source_file, "content"));
2039 
2040   FilePathWatcher watcher;
2041   TestDelegate delegate;
2042   ASSERT_TRUE(SetupWatchWithChangeInfo(test_file(), &watcher, &delegate,
2043                                        GetWatchOptions()));
2044 
2045   ASSERT_TRUE(Move(source_file, test_file()));
2046   delegate.RunUntilEventsMatch(matcher);
2047 }
2048 
TEST_P(FilePathWatcherWithChangeInfoTest,MatchCookies)2049 TEST_P(FilePathWatcherWithChangeInfoTest, MatchCookies) {
2050   FilePath source_file(test_file().AppendASCII("source"));
2051   FilePath dest_file(test_file().AppendASCII("dest"));
2052 
2053   const auto each_event_matcher = testing::Each(
2054       testing::AllOf(testing::Not(HasErrored()), IsFile(),
2055                      IsType(FilePathWatcher::ChangeType::kMoved), HasCookie()));
2056   // TODO(https://crbug.com/1425601): Some platforms will not provide separate
2057   // events for "moved from" and "moved to". Update this matcher once support
2058   // for those platforms is added.
2059   const auto sequence_matcher = testing::UnorderedElementsAre(
2060       testing::AllOf(
2061           HasPath(report_modified_path() ? source_file : test_file()),
2062           IsType(FilePathWatcher::ChangeType::kMoved)),
2063       testing::AllOf(HasPath(report_modified_path() ? dest_file : test_file()),
2064                      IsType(FilePathWatcher::ChangeType::kMoved)));
2065   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2066 
2067   ASSERT_TRUE(CreateDirectory(test_file()));
2068   ASSERT_TRUE(WriteFile(source_file, "content"));
2069 #if BUILDFLAG(IS_ANDROID)
2070   // TODO(https://crbug.com/1496350): There appears to be a race condition
2071   // between setting up the inotify watch and the processing of the file system
2072   // notifications created while setting up the file system for this test. Spin
2073   // the event loop to ensure that the events have been processed by the time
2074   // the inotify watch has been set up.
2075   SpinEventLoopForABit();
2076 #endif  // BUILDFLAG(IS_ANDROID)
2077 
2078   FilePathWatcher watcher;
2079   TestDelegate delegate;
2080   ASSERT_TRUE(SetupWatchWithChangeInfo(test_file(), &watcher, &delegate,
2081                                        GetWatchOptions()));
2082 
2083   ASSERT_TRUE(Move(source_file, dest_file));
2084   delegate.RunUntilEventsMatch(matcher);
2085 
2086   const auto& events = delegate.events();
2087   ASSERT_THAT(events, testing::SizeIs(2));
2088 
2089   EXPECT_TRUE(events.front().change_info.cookie.has_value());
2090   EXPECT_EQ(events.front().change_info.cookie,
2091             events.back().change_info.cookie);
2092 }
2093 
TEST_P(FilePathWatcherWithChangeInfoTest,DeletedFile)2094 TEST_P(FilePathWatcherWithChangeInfoTest, DeletedFile) {
2095   const auto matcher = testing::ElementsAre(
2096       testing::AllOf(HasPath(test_file()), testing::Not(HasErrored()), IsFile(),
2097                      IsType(FilePathWatcher::ChangeType::kDeleted),
2098                      testing::Not(HasCookie())));
2099 
2100   ASSERT_TRUE(WriteFile(test_file(), "content"));
2101 #if BUILDFLAG(IS_ANDROID)
2102   // TODO(https://crbug.com/1496350): There appears to be a race condition
2103   // between setting up the inotify watch and the processing of the file system
2104   // notifications created while setting up the file system for this test. Spin
2105   // the event loop to ensure that the events have been processed by the time
2106   // the inotify watch has been set up.
2107   SpinEventLoopForABit();
2108 #endif  // BUILDFLAG(IS_ANDROID)
2109 
2110   FilePathWatcher watcher;
2111   TestDelegate delegate;
2112   ASSERT_TRUE(SetupWatchWithChangeInfo(test_file(), &watcher, &delegate,
2113                                        GetWatchOptions()));
2114 
2115   ASSERT_TRUE(DeleteFile(test_file()));
2116   delegate.RunUntilEventsMatch(matcher);
2117 }
2118 
TEST_P(FilePathWatcherWithChangeInfoTest,DeletedDirectory)2119 TEST_P(FilePathWatcherWithChangeInfoTest, DeletedDirectory) {
2120   const auto matcher = testing::ElementsAre(testing::AllOf(
2121       HasPath(test_file()), testing::Not(HasErrored()), IsDirectory(),
2122       IsType(FilePathWatcher::ChangeType::kDeleted),
2123       testing::Not(HasCookie())));
2124 
2125   ASSERT_TRUE(CreateDirectory(test_file()));
2126 #if BUILDFLAG(IS_ANDROID)
2127   // TODO(https://crbug.com/1496350): There appears to be a race condition
2128   // between setting up the inotify watch and the processing of the file system
2129   // notifications created while setting up the file system for this test. Spin
2130   // the event loop to ensure that the events have been processed by the time
2131   // the inotify watch has been set up.
2132   SpinEventLoopForABit();
2133 #endif  // BUILDFLAG(IS_ANDROID)
2134 
2135   FilePathWatcher watcher;
2136   TestDelegate delegate;
2137   ASSERT_TRUE(SetupWatchWithChangeInfo(test_file(), &watcher, &delegate,
2138                                        GetWatchOptions()));
2139 
2140   ASSERT_TRUE(DeletePathRecursively(test_file()));
2141   delegate.RunUntilEventsMatch(matcher);
2142 }
2143 
TEST_P(FilePathWatcherWithChangeInfoTest,MultipleWatchersSingleFile)2144 TEST_P(FilePathWatcherWithChangeInfoTest, MultipleWatchersSingleFile) {
2145   const auto each_event_matcher = testing::Each(
2146       testing::AllOf(HasPath(test_file()), testing::Not(HasErrored()), IsFile(),
2147                      testing::Not(HasCookie())));
2148   // TODO(https://crbug.com/1425601): Update this when change types are
2149   // supported on more platforms.
2150   static_assert(kExpectedEventsForNewFileWrite == 2);
2151   const auto sequence_matcher =
2152       testing::ElementsAre(IsType(FilePathWatcher::ChangeType::kCreated),
2153                            IsType(FilePathWatcher::ChangeType::kModified));
2154   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2155 
2156   FilePathWatcher watcher1, watcher2;
2157   TestDelegate delegate1, delegate2;
2158   ASSERT_TRUE(SetupWatchWithChangeInfo(test_file(), &watcher1, &delegate1,
2159                                        GetWatchOptions()));
2160   ASSERT_TRUE(SetupWatchWithChangeInfo(test_file(), &watcher2, &delegate2,
2161                                        GetWatchOptions()));
2162 
2163   // Expect each delegate to get notified of all changes.
2164   ASSERT_TRUE(WriteFile(test_file(), "content"));
2165 
2166   delegate1.RunUntilEventsMatch(matcher);
2167   delegate2.RunUntilEventsMatch(matcher);
2168 }
2169 
TEST_P(FilePathWatcherWithChangeInfoTest,NonExistentDirectory)2170 TEST_P(FilePathWatcherWithChangeInfoTest, NonExistentDirectory) {
2171   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
2172   FilePath file(dir.AppendASCII("file"));
2173   const auto each_event_matcher =
2174       testing::Each(testing::AllOf(HasPath(file), testing::Not(HasErrored()),
2175                                    IsFile(), testing::Not(HasCookie())));
2176   const auto sequence_matcher =
2177       testing::IsSupersetOf({IsType(FilePathWatcher::ChangeType::kCreated),
2178                              IsType(FilePathWatcher::ChangeType::kModified),
2179                              IsType(FilePathWatcher::ChangeType::kDeleted)});
2180   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2181 
2182   FilePathWatcher watcher;
2183   TestDelegate delegate;
2184   ASSERT_TRUE(
2185       SetupWatchWithChangeInfo(file, &watcher, &delegate, GetWatchOptions()));
2186 
2187   // The delegate is only watching the file. Parent directory creation should
2188   // not trigger an event.
2189   ASSERT_TRUE(CreateDirectory(dir));
2190   // It may take some time for `watcher` to re-construct its watch list, so spin
2191   // for a bit while we ensure that creating the parent directory does not
2192   // trigger an event.
2193   delegate.RunUntilEventsMatch(testing::IsEmpty(),
2194                                ExpectedEventsSinceLastWait::kNone);
2195 
2196   ASSERT_TRUE(WriteFile(file, "content"));
2197   ASSERT_TRUE(WriteFile(file, "content v2"));
2198   ASSERT_TRUE(DeleteFile(file));
2199 
2200   delegate.RunUntilEventsMatch(matcher);
2201 }
2202 
TEST_P(FilePathWatcherWithChangeInfoTest,DirectoryChain)2203 TEST_P(FilePathWatcherWithChangeInfoTest, DirectoryChain) {
2204   FilePath path(temp_dir_.GetPath());
2205   std::vector<std::string> dir_names;
2206   for (int i = 0; i < 20; i++) {
2207     std::string dir(StringPrintf("d%d", i));
2208     dir_names.push_back(dir);
2209     path = path.AppendASCII(dir);
2210   }
2211   FilePath file(path.AppendASCII("file"));
2212 
2213   const auto each_event_matcher =
2214       testing::Each(testing::AllOf(HasPath(file), testing::Not(HasErrored()),
2215                                    IsFile(), testing::Not(HasCookie())));
2216   const auto sequence_matcher =
2217       testing::IsSupersetOf({IsType(FilePathWatcher::ChangeType::kCreated),
2218                              IsType(FilePathWatcher::ChangeType::kModified)});
2219   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2220 
2221   FilePathWatcher watcher;
2222   TestDelegate delegate;
2223   ASSERT_TRUE(
2224       SetupWatchWithChangeInfo(file, &watcher, &delegate, GetWatchOptions()));
2225 
2226   FilePath sub_path(temp_dir_.GetPath());
2227   for (const auto& dir_name : dir_names) {
2228     sub_path = sub_path.AppendASCII(dir_name);
2229     ASSERT_TRUE(CreateDirectory(sub_path));
2230   }
2231   // Allow the watcher to reconstruct its watch list.
2232   SpinEventLoopForABit();
2233 
2234   ASSERT_TRUE(WriteFile(file, "content"));
2235   ASSERT_TRUE(WriteFile(file, "content v2"));
2236 
2237   delegate.RunUntilEventsMatch(matcher);
2238 }
2239 
TEST_P(FilePathWatcherWithChangeInfoTest,DisappearingDirectory)2240 TEST_P(FilePathWatcherWithChangeInfoTest, DisappearingDirectory) {
2241   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
2242   FilePath file(dir.AppendASCII("file"));
2243 
2244   const auto each_event_matcher = testing::Each(
2245       testing::AllOf(HasPath(file), testing::Not(HasErrored()),
2246                      IsType(FilePathWatcher::ChangeType::kDeleted),
2247                      testing::Not(HasCookie())));
2248   // TODO(https://crbug.com/1432044): inotify incorrectly reports an additional
2249   // deletion event for the parent directory (though while confusingly reporting
2250   // the path as `file`). Once fixed, update this matcher to assert that only
2251   // one event is received.
2252   const auto sequence_matcher = testing::Contains(IsFile());
2253   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2254 
2255   ASSERT_TRUE(CreateDirectory(dir));
2256   ASSERT_TRUE(WriteFile(file, "content"));
2257 #if BUILDFLAG(IS_ANDROID)
2258   // TODO(https://crbug.com/1496350): There appears to be a race condition
2259   // between setting up the inotify watch and the processing of the file system
2260   // notifications created while setting up the file system for this test. Spin
2261   // the event loop to ensure that the events have been processed by the time
2262   // the inotify watch has been set up.
2263   SpinEventLoopForABit();
2264 #endif  // BUILDFLAG(IS_ANDROID)
2265 
2266   FilePathWatcher watcher;
2267   TestDelegate delegate;
2268   ASSERT_TRUE(
2269       SetupWatchWithChangeInfo(file, &watcher, &delegate, GetWatchOptions()));
2270 
2271   ASSERT_TRUE(DeletePathRecursively(dir));
2272   delegate.RunUntilEventsMatch(matcher);
2273 }
2274 
TEST_P(FilePathWatcherWithChangeInfoTest,DeleteAndRecreate)2275 TEST_P(FilePathWatcherWithChangeInfoTest, DeleteAndRecreate) {
2276   const auto each_event_matcher = testing::Each(
2277       testing::AllOf(HasPath(test_file()), testing::Not(HasErrored()), IsFile(),
2278                      testing::Not(HasCookie())));
2279   // TODO(https://crbug.com/1425601): Update this when change types are
2280   // supported on on more platforms.
2281   static_assert(kExpectedEventsForNewFileWrite == 2);
2282   const auto sequence_matcher =
2283       testing::ElementsAre(IsType(FilePathWatcher::ChangeType::kDeleted),
2284                            IsType(FilePathWatcher::ChangeType::kCreated),
2285                            IsType(FilePathWatcher::ChangeType::kModified));
2286   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2287 
2288   ASSERT_TRUE(WriteFile(test_file(), "content"));
2289 #if BUILDFLAG(IS_ANDROID)
2290   // TODO(https://crbug.com/1496350): There appears to be a race condition
2291   // between setting up the inotify watch and the processing of the file system
2292   // notifications created while setting up the file system for this test. Spin
2293   // the event loop to ensure that the events have been processed by the time
2294   // the inotify watch has been set up.
2295   SpinEventLoopForABit();
2296 #endif  // BUILDFLAG(IS_ANDROID)
2297 
2298   FilePathWatcher watcher;
2299   TestDelegate delegate;
2300   ASSERT_TRUE(SetupWatchWithChangeInfo(test_file(), &watcher, &delegate,
2301                                        GetWatchOptions()));
2302 
2303   ASSERT_TRUE(DeleteFile(test_file()));
2304   ASSERT_TRUE(WriteFile(test_file(), "content"));
2305 
2306   delegate.RunUntilEventsMatch(matcher);
2307 }
2308 
TEST_P(FilePathWatcherWithChangeInfoTest,WatchDirectory)2309 TEST_P(FilePathWatcherWithChangeInfoTest, WatchDirectory) {
2310   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
2311   FilePath file1(dir.AppendASCII("file1"));
2312   FilePath file2(dir.AppendASCII("file2"));
2313 
2314   const auto each_event_matcher = testing::Each(
2315       testing::AllOf(testing::Not(HasErrored()), testing::Not(HasCookie())));
2316   const auto sequence_matcher = testing::IsSupersetOf(
2317       {testing::AllOf(HasPath(report_modified_path() ? file1 : dir), IsFile(),
2318                       IsType(FilePathWatcher::ChangeType::kCreated)),
2319        testing::AllOf(HasPath(report_modified_path() ? file1 : dir), IsFile(),
2320                       IsType(FilePathWatcher::ChangeType::kModified)),
2321        testing::AllOf(HasPath(report_modified_path() ? file1 : dir), IsFile(),
2322                       IsType(FilePathWatcher::ChangeType::kDeleted)),
2323        testing::AllOf(HasPath(report_modified_path() ? file2 : dir), IsFile(),
2324                       IsType(FilePathWatcher::ChangeType::kCreated))});
2325   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2326 
2327   ASSERT_TRUE(CreateDirectory(dir));
2328 #if BUILDFLAG(IS_ANDROID)
2329   // TODO(https://crbug.com/1496350): There appears to be a race condition
2330   // between setting up the inotify watch and the processing of the file system
2331   // notifications created while setting up the file system for this test. Spin
2332   // the event loop to ensure that the events have been processed by the time
2333   // the inotify watch has been set up.
2334   SpinEventLoopForABit();
2335 #endif  // BUILDFLAG(IS_ANDROID)
2336 
2337   FilePathWatcher watcher;
2338   TestDelegate delegate;
2339   ASSERT_TRUE(
2340       SetupWatchWithChangeInfo(dir, &watcher, &delegate, GetWatchOptions()));
2341 
2342   ASSERT_TRUE(WriteFile(file1, "content"));
2343   ASSERT_TRUE(WriteFile(file1, "content v2"));
2344   ASSERT_TRUE(DeleteFile(file1));
2345   ASSERT_TRUE(WriteFile(file2, "content"));
2346   delegate.RunUntilEventsMatch(matcher);
2347 }
2348 
TEST_P(FilePathWatcherWithChangeInfoTest,MoveParent)2349 TEST_P(FilePathWatcherWithChangeInfoTest, MoveParent) {
2350   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
2351   FilePath dest(temp_dir_.GetPath().AppendASCII("dest"));
2352   FilePath subdir(dir.AppendASCII("subdir"));
2353   FilePath file(subdir.AppendASCII("file"));
2354 
2355   const auto each_event_matcher = testing::Each(testing::Not(HasErrored()));
2356   // TODO(https://crbug.com/1432044): inotify incorrectly sometimes reports
2357   // the first event as a directory creation... why?
2358   const auto file_delegate_sequence_matcher = testing::IsSupersetOf(
2359       {testing::AllOf(HasPath(file), IsFile(),
2360                       IsType(FilePathWatcher::ChangeType::kCreated)),
2361        testing::AllOf(HasPath(file), IsDirectory(),
2362                       IsType(FilePathWatcher::ChangeType::kMoved))});
2363   const auto subdir_delegate_sequence_matcher = testing::IsSupersetOf(
2364       {testing::AllOf(HasPath(subdir), IsDirectory(),
2365                       IsType(FilePathWatcher::ChangeType::kCreated)),
2366        testing::AllOf(HasPath(report_modified_path() ? file : subdir), IsFile(),
2367                       IsType(FilePathWatcher::ChangeType::kCreated)),
2368        testing::AllOf(HasPath(subdir), IsDirectory(),
2369                       IsType(FilePathWatcher::ChangeType::kMoved))});
2370   const auto file_delegate_matcher =
2371       testing::AllOf(each_event_matcher, file_delegate_sequence_matcher);
2372   const auto subdir_delegate_matcher =
2373       testing::AllOf(each_event_matcher, subdir_delegate_sequence_matcher);
2374 
2375   FilePathWatcher file_watcher, subdir_watcher;
2376   TestDelegate file_delegate, subdir_delegate;
2377   ASSERT_TRUE(SetupWatchWithChangeInfo(file, &file_watcher, &file_delegate,
2378                                        GetWatchOptions()));
2379   ASSERT_TRUE(SetupWatchWithChangeInfo(subdir, &subdir_watcher,
2380                                        &subdir_delegate, GetWatchOptions()));
2381 
2382   // Setup a directory hierarchy.
2383   // We should only get notified on `subdir_delegate` of its creation.
2384   ASSERT_TRUE(CreateDirectory(subdir));
2385   // Allow the watchers to reconstruct their watch lists.
2386   SpinEventLoopForABit();
2387 
2388   ASSERT_TRUE(WriteFile(file, "content"));
2389   // Allow the file watcher to reconstruct its watch list.
2390   SpinEventLoopForABit();
2391 
2392   Move(dir, dest);
2393   file_delegate.RunUntilEventsMatch(file_delegate_matcher);
2394   subdir_delegate.RunUntilEventsMatch(subdir_delegate_matcher);
2395 }
2396 
TEST_P(FilePathWatcherWithChangeInfoTest,MoveChild)2397 TEST_P(FilePathWatcherWithChangeInfoTest, MoveChild) {
2398   FilePath source_dir(temp_dir_.GetPath().AppendASCII("source"));
2399   FilePath source_subdir(source_dir.AppendASCII("subdir"));
2400   FilePath source_file(source_subdir.AppendASCII("file"));
2401   FilePath dest_dir(temp_dir_.GetPath().AppendASCII("dest"));
2402   FilePath dest_subdir(dest_dir.AppendASCII("subdir"));
2403   FilePath dest_file(dest_subdir.AppendASCII("file"));
2404 
2405   const auto each_event_matcher = testing::Each(
2406       testing::AllOf(testing::Not(HasErrored()), IsDirectory(),
2407                      IsType(FilePathWatcher::ChangeType::kMoved), HasCookie()));
2408   const auto file_delegate_sequence_matcher =
2409       testing::ElementsAre(HasPath(dest_file));
2410   const auto subdir_delegate_sequence_matcher =
2411       testing::ElementsAre(HasPath(dest_subdir));
2412   const auto file_delegate_matcher =
2413       testing::AllOf(each_event_matcher, file_delegate_sequence_matcher);
2414   const auto subdir_delegate_matcher =
2415       testing::AllOf(each_event_matcher, subdir_delegate_sequence_matcher);
2416 
2417   // Setup a directory hierarchy.
2418   ASSERT_TRUE(CreateDirectory(source_subdir));
2419   ASSERT_TRUE(WriteFile(source_file, "content"));
2420 
2421   FilePathWatcher file_watcher, subdir_watcher;
2422   TestDelegate file_delegate, subdir_delegate;
2423   ASSERT_TRUE(SetupWatchWithChangeInfo(dest_file, &file_watcher, &file_delegate,
2424                                        GetWatchOptions()));
2425   ASSERT_TRUE(SetupWatchWithChangeInfo(dest_subdir, &subdir_watcher,
2426                                        &subdir_delegate, GetWatchOptions()));
2427 
2428   // Move the directory into place, s.t. the watched file appears.
2429   ASSERT_TRUE(Move(source_dir, dest_dir));
2430   file_delegate.RunUntilEventsMatch(file_delegate_matcher);
2431   subdir_delegate.RunUntilEventsMatch(subdir_delegate_matcher);
2432 }
2433 
2434 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
2435 // the |temp_dir_| in /data.
2436 #if !BUILDFLAG(IS_ANDROID)
TEST_P(FilePathWatcherWithChangeInfoTest,FileAttributesChanged)2437 TEST_P(FilePathWatcherWithChangeInfoTest, FileAttributesChanged) {
2438   const auto matcher = testing::ElementsAre(
2439       testing::AllOf(HasPath(test_file()), testing::Not(HasErrored()), IsFile(),
2440                      IsType(FilePathWatcher::ChangeType::kModified),
2441                      testing::Not(HasCookie())));
2442 
2443   ASSERT_TRUE(WriteFile(test_file(), "content"));
2444 
2445   FilePathWatcher watcher;
2446   TestDelegate delegate;
2447   ASSERT_TRUE(SetupWatchWithChangeInfo(test_file(), &watcher, &delegate,
2448                                        GetWatchOptions()));
2449 
2450   // Now make sure we get notified if the file is modified.
2451   ASSERT_TRUE(MakeFileUnreadable(test_file()));
2452   delegate.RunUntilEventsMatch(matcher);
2453 }
2454 
TEST_P(FilePathWatcherWithChangeInfoTest,CreateLink)2455 TEST_P(FilePathWatcherWithChangeInfoTest, CreateLink) {
2456   // TODO(https://crbug.com/1425601): Check for symlink-ness on platforms which
2457   // support it.
2458   const auto matcher = testing::ElementsAre(
2459       testing::AllOf(HasPath(test_link()), testing::Not(HasErrored()), IsFile(),
2460                      IsType(FilePathWatcher::ChangeType::kCreated),
2461                      testing::Not(HasCookie())));
2462 
2463   FilePathWatcher watcher;
2464   TestDelegate delegate;
2465   AccumulatingEventExpecter event_expecter;
2466   ASSERT_TRUE(SetupWatchWithChangeInfo(test_link(), &watcher, &delegate,
2467                                        GetWatchOptions()));
2468 
2469   // Now make sure we get notified if the link is created.
2470   // Note that test_file() doesn't have to exist.
2471   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
2472   delegate.RunUntilEventsMatch(matcher);
2473 }
2474 
2475 // Unfortunately this test case only works if the link target exists.
2476 // TODO(craig) fix this as part of crbug.com/91561.
TEST_P(FilePathWatcherWithChangeInfoTest,DeleteLink)2477 TEST_P(FilePathWatcherWithChangeInfoTest, DeleteLink) {
2478   // TODO(https://crbug.com/1425601): Check for symlink-ness on platforms which
2479   // support it.
2480   const auto matcher = testing::ElementsAre(
2481       testing::AllOf(HasPath(test_link()), testing::Not(HasErrored()), IsFile(),
2482                      IsType(FilePathWatcher::ChangeType::kDeleted),
2483                      testing::Not(HasCookie())));
2484 
2485   ASSERT_TRUE(WriteFile(test_file(), "content"));
2486   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
2487 
2488   FilePathWatcher watcher;
2489   TestDelegate delegate;
2490   ASSERT_TRUE(SetupWatchWithChangeInfo(test_link(), &watcher, &delegate,
2491                                        GetWatchOptions()));
2492 
2493   // Now make sure we get notified if the link is deleted.
2494   ASSERT_TRUE(DeleteFile(test_link()));
2495   delegate.RunUntilEventsMatch(matcher);
2496 }
2497 
TEST_P(FilePathWatcherWithChangeInfoTest,ModifiedLinkedFile)2498 TEST_P(FilePathWatcherWithChangeInfoTest, ModifiedLinkedFile) {
2499   // TODO(https://crbug.com/1425601): Check for symlink-ness on platforms which
2500   // support it.
2501   const auto matcher = testing::ElementsAre(
2502       testing::AllOf(HasPath(test_link()), testing::Not(HasErrored()), IsFile(),
2503                      IsType(FilePathWatcher::ChangeType::kModified),
2504                      testing::Not(HasCookie())));
2505 
2506   ASSERT_TRUE(WriteFile(test_file(), "content"));
2507   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
2508 
2509   FilePathWatcher watcher;
2510   TestDelegate delegate;
2511   ASSERT_TRUE(SetupWatchWithChangeInfo(test_link(), &watcher, &delegate,
2512                                        GetWatchOptions()));
2513 
2514   // Now make sure we get notified if the file is modified.
2515   ASSERT_TRUE(WriteFile(test_file(), "new content"));
2516   delegate.RunUntilEventsMatch(matcher);
2517 }
2518 
TEST_P(FilePathWatcherWithChangeInfoTest,CreateTargetLinkedFile)2519 TEST_P(FilePathWatcherWithChangeInfoTest, CreateTargetLinkedFile) {
2520   // TODO(https://crbug.com/1425601): Check for symlink-ness on platforms which
2521   // support it.
2522   const auto each_event_matcher = testing::Each(
2523       testing::AllOf(HasPath(test_link()), testing::Not(HasErrored()), IsFile(),
2524                      testing::Not(HasCookie())));
2525   // TODO(https://crbug.com/1425601): Update this when change types are
2526   // supported on on more platforms.
2527   static_assert(kExpectedEventsForNewFileWrite == 2);
2528   const auto sequence_matcher =
2529       testing::ElementsAre(IsType(FilePathWatcher::ChangeType::kCreated),
2530                            IsType(FilePathWatcher::ChangeType::kModified));
2531   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2532 
2533   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
2534 
2535   FilePathWatcher watcher;
2536   TestDelegate delegate;
2537   ASSERT_TRUE(SetupWatchWithChangeInfo(test_link(), &watcher, &delegate,
2538                                        GetWatchOptions()));
2539 
2540   // Now make sure we get notified if the target file is created.
2541   ASSERT_TRUE(WriteFile(test_file(), "content"));
2542   delegate.RunUntilEventsMatch(matcher);
2543 }
2544 
TEST_P(FilePathWatcherWithChangeInfoTest,DeleteTargetLinkedFile)2545 TEST_P(FilePathWatcherWithChangeInfoTest, DeleteTargetLinkedFile) {
2546   // TODO(https://crbug.com/1425601): Check for symlink-ness on platforms which
2547   // support it.
2548   const auto matcher = testing::ElementsAre(
2549       testing::AllOf(HasPath(test_link()), testing::Not(HasErrored()), IsFile(),
2550                      IsType(FilePathWatcher::ChangeType::kDeleted),
2551                      testing::Not(HasCookie())));
2552 
2553   ASSERT_TRUE(WriteFile(test_file(), "content"));
2554   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
2555 
2556   FilePathWatcher watcher;
2557   TestDelegate delegate;
2558   ASSERT_TRUE(SetupWatchWithChangeInfo(test_link(), &watcher, &delegate,
2559                                        GetWatchOptions()));
2560 
2561   // Now make sure we get notified if the target file is deleted.
2562   ASSERT_TRUE(DeleteFile(test_file()));
2563   delegate.RunUntilEventsMatch(matcher);
2564 }
2565 
TEST_P(FilePathWatcherWithChangeInfoTest,LinkedDirectoryPart1)2566 TEST_P(FilePathWatcherWithChangeInfoTest, LinkedDirectoryPart1) {
2567   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
2568   FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
2569   FilePath file(dir.AppendASCII("file"));
2570   FilePath linkfile(link_dir.AppendASCII("file"));
2571 
2572   // TODO(https://crbug.com/1425601): Check for symlink-ness on platforms which
2573   // support it.
2574   const auto each_event_matcher = testing::Each(
2575       testing::AllOf(HasPath(linkfile), testing::Not(HasErrored()), IsFile(),
2576                      testing::Not(HasCookie())));
2577   const auto sequence_matcher =
2578       testing::IsSupersetOf({IsType(FilePathWatcher::ChangeType::kCreated),
2579                              IsType(FilePathWatcher::ChangeType::kModified),
2580                              IsType(FilePathWatcher::ChangeType::kDeleted)});
2581   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2582 
2583   // dir/file should exist.
2584   ASSERT_TRUE(CreateDirectory(dir));
2585   ASSERT_TRUE(WriteFile(file, "content"));
2586 
2587   FilePathWatcher watcher;
2588   TestDelegate delegate;
2589   // Note that we are watching dir.lnk/file which doesn't exist yet.
2590   ASSERT_TRUE(SetupWatchWithChangeInfo(linkfile, &watcher, &delegate,
2591                                        GetWatchOptions()));
2592 
2593   ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
2594   // Allow the watcher to reconstruct its watch list.
2595   SpinEventLoopForABit();
2596 
2597   ASSERT_TRUE(WriteFile(file, "content v2"));
2598   ASSERT_TRUE(WriteFile(file, "content v2"));
2599   ASSERT_TRUE(DeleteFile(file));
2600   delegate.RunUntilEventsMatch(matcher);
2601 }
2602 
TEST_P(FilePathWatcherWithChangeInfoTest,LinkedDirectoryPart2)2603 TEST_P(FilePathWatcherWithChangeInfoTest, LinkedDirectoryPart2) {
2604   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
2605   FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
2606   FilePath file(dir.AppendASCII("file"));
2607   FilePath linkfile(link_dir.AppendASCII("file"));
2608 
2609   // TODO(https://crbug.com/1425601): Check for symlink-ness on platforms which
2610   // support it.
2611   const auto each_event_matcher = testing::Each(
2612       testing::AllOf(HasPath(linkfile), testing::Not(HasErrored()), IsFile(),
2613                      testing::Not(HasCookie())));
2614   const auto sequence_matcher =
2615       testing::IsSupersetOf({IsType(FilePathWatcher::ChangeType::kCreated),
2616                              IsType(FilePathWatcher::ChangeType::kModified),
2617                              IsType(FilePathWatcher::ChangeType::kDeleted)});
2618   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2619 
2620   // Now create the link from dir.lnk pointing to dir but
2621   // neither dir nor dir/file exist yet.
2622   ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
2623 
2624   FilePathWatcher watcher;
2625   TestDelegate delegate;
2626   // Note that we are watching dir.lnk/file.
2627   ASSERT_TRUE(SetupWatchWithChangeInfo(linkfile, &watcher, &delegate,
2628                                        GetWatchOptions()));
2629 
2630   ASSERT_TRUE(CreateDirectory(dir));
2631   // Allow the watcher to reconstruct its watch list.
2632   SpinEventLoopForABit();
2633 
2634   ASSERT_TRUE(WriteFile(file, "content"));
2635   ASSERT_TRUE(WriteFile(file, "content v2"));
2636   ASSERT_TRUE(DeleteFile(file));
2637   delegate.RunUntilEventsMatch(matcher);
2638 }
2639 
TEST_P(FilePathWatcherWithChangeInfoTest,LinkedDirectoryPart3)2640 TEST_P(FilePathWatcherWithChangeInfoTest, LinkedDirectoryPart3) {
2641   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
2642   FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
2643   FilePath file(dir.AppendASCII("file"));
2644   FilePath linkfile(link_dir.AppendASCII("file"));
2645 
2646   // TODO(https://crbug.com/1425601): Check for symlink-ness on platforms which
2647   // support it.
2648   const auto each_event_matcher = testing::Each(
2649       testing::AllOf(HasPath(linkfile), testing::Not(HasErrored()), IsFile(),
2650                      testing::Not(HasCookie())));
2651   const auto sequence_matcher =
2652       testing::IsSupersetOf({IsType(FilePathWatcher::ChangeType::kCreated),
2653                              IsType(FilePathWatcher::ChangeType::kModified),
2654                              IsType(FilePathWatcher::ChangeType::kDeleted)});
2655   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2656 
2657   ASSERT_TRUE(CreateDirectory(dir));
2658   ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
2659 
2660   FilePathWatcher watcher;
2661   TestDelegate delegate;
2662   // Note that we are watching dir.lnk/file but the file doesn't exist yet.
2663   ASSERT_TRUE(SetupWatchWithChangeInfo(linkfile, &watcher, &delegate,
2664                                        GetWatchOptions()));
2665 
2666   ASSERT_TRUE(WriteFile(file, "content"));
2667   ASSERT_TRUE(WriteFile(file, "content v2"));
2668   ASSERT_TRUE(DeleteFile(file));
2669   delegate.RunUntilEventsMatch(matcher);
2670 }
2671 #endif  // !BUILDFLAG(IS_ANDROID)
2672 
TEST_P(FilePathWatcherWithChangeInfoTest,CreatedFileInDirectory)2673 TEST_P(FilePathWatcherWithChangeInfoTest, CreatedFileInDirectory) {
2674   // Expect the change to be reported as a file creation, not as a
2675   // directory modification.
2676   FilePath parent(temp_dir_.GetPath().AppendASCII("parent"));
2677   FilePath child(parent.AppendASCII("child"));
2678 
2679   const auto matcher = testing::IsSupersetOf(
2680       {testing::AllOf(HasPath(report_modified_path() ? child : parent),
2681                       IsFile(), IsType(FilePathWatcher::ChangeType::kCreated),
2682                       testing::Not(HasErrored()), testing::Not(HasCookie()))});
2683 
2684   ASSERT_TRUE(CreateDirectory(parent));
2685 
2686   FilePathWatcher watcher;
2687   TestDelegate delegate;
2688   ASSERT_TRUE(
2689       SetupWatchWithChangeInfo(parent, &watcher, &delegate, GetWatchOptions()));
2690 
2691   ASSERT_TRUE(WriteFile(child, "contents"));
2692   delegate.RunUntilEventsMatch(matcher);
2693 }
2694 
TEST_P(FilePathWatcherWithChangeInfoTest,ModifiedFileInDirectory)2695 TEST_P(FilePathWatcherWithChangeInfoTest, ModifiedFileInDirectory) {
2696   // Expect the change to be reported as a file modification, not as a
2697   // directory modification.
2698   FilePath parent(temp_dir_.GetPath().AppendASCII("parent"));
2699   FilePath child(parent.AppendASCII("child"));
2700 
2701   const auto matcher = testing::ElementsAre(
2702       testing::AllOf(HasPath(report_modified_path() ? child : parent), IsFile(),
2703                      IsType(FilePathWatcher::ChangeType::kModified),
2704                      testing::Not(HasErrored()), testing::Not(HasCookie())));
2705 
2706   ASSERT_TRUE(CreateDirectory(parent));
2707   ASSERT_TRUE(WriteFile(child, "contents"));
2708 #if BUILDFLAG(IS_ANDROID)
2709   // TODO(https://crbug.com/1496350): There appears to be a race condition
2710   // between setting up the inotify watch and the processing of the file system
2711   // notifications created while setting up the file system for this test. Spin
2712   // the event loop to ensure that the events have been processed by the time
2713   // the inotify watch has been set up.
2714 #if BUILDFLAG(IS_ANDROID)
2715   // TODO(https://crbug.com/1496350): There appears to be a race condition
2716   // between setting up the inotify watch and the processing of the file system
2717   // notifications created while setting up the file system for this test. Spin
2718   // the event loop to ensure that the events have been processed by the time
2719   // the inotify watch has been set up.
2720   SpinEventLoopForABit();
2721 #endif  // BUILDFLAG(IS_ANDROID)
2722 #endif  // BUILDFLAG(IS_ANDROID)
2723 
2724   FilePathWatcher watcher;
2725   TestDelegate delegate;
2726   ASSERT_TRUE(
2727       SetupWatchWithChangeInfo(parent, &watcher, &delegate, GetWatchOptions()));
2728 
2729   ASSERT_TRUE(WriteFile(child, "contents v2"));
2730   delegate.RunUntilEventsMatch(matcher);
2731 }
2732 
TEST_P(FilePathWatcherWithChangeInfoTest,DeletedFileInDirectory)2733 TEST_P(FilePathWatcherWithChangeInfoTest, DeletedFileInDirectory) {
2734   // Expect the change to be reported as a file deletion, not as a
2735   // directory modification.
2736   FilePath parent(temp_dir_.GetPath().AppendASCII("parent"));
2737   FilePath child(parent.AppendASCII("child"));
2738 
2739   const auto matcher = testing::ElementsAre(
2740       testing::AllOf(HasPath(report_modified_path() ? child : parent), IsFile(),
2741                      IsType(FilePathWatcher::ChangeType::kDeleted),
2742                      testing::Not(HasErrored()), testing::Not(HasCookie())));
2743 
2744   ASSERT_TRUE(CreateDirectory(parent));
2745   ASSERT_TRUE(WriteFile(child, "contents"));
2746 
2747   FilePathWatcher watcher;
2748   TestDelegate delegate;
2749   ASSERT_TRUE(
2750       SetupWatchWithChangeInfo(parent, &watcher, &delegate, GetWatchOptions()));
2751 
2752   ASSERT_TRUE(DeleteFile(child));
2753   delegate.RunUntilEventsMatch(matcher);
2754 }
2755 
TEST_P(FilePathWatcherWithChangeInfoTest,FileInDirectory)2756 TEST_P(FilePathWatcherWithChangeInfoTest, FileInDirectory) {
2757   // Expect the changes to be reported as events on the file, not as
2758   // modifications to the directory.
2759   FilePath parent(temp_dir_.GetPath().AppendASCII("parent"));
2760   FilePath child(parent.AppendASCII("child"));
2761 
2762   const auto each_event_matcher = testing::Each(testing::AllOf(
2763       HasPath(report_modified_path() ? child : parent),
2764       testing::Not(HasErrored()), IsFile(), testing::Not(HasCookie())));
2765   const auto sequence_matcher =
2766       testing::IsSupersetOf({IsType(FilePathWatcher::ChangeType::kCreated),
2767                              IsType(FilePathWatcher::ChangeType::kModified),
2768                              IsType(FilePathWatcher::ChangeType::kDeleted)});
2769   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2770 
2771   ASSERT_TRUE(CreateDirectory(parent));
2772 
2773   FilePathWatcher watcher;
2774   TestDelegate delegate;
2775   ASSERT_TRUE(
2776       SetupWatchWithChangeInfo(parent, &watcher, &delegate, GetWatchOptions()));
2777 
2778   ASSERT_TRUE(WriteFile(child, "contents"));
2779   ASSERT_TRUE(WriteFile(child, "contents v2"));
2780   ASSERT_TRUE(DeleteFile(child));
2781   delegate.RunUntilEventsMatch(matcher);
2782 }
2783 
TEST_P(FilePathWatcherWithChangeInfoTest,DirectoryInDirectory)2784 TEST_P(FilePathWatcherWithChangeInfoTest, DirectoryInDirectory) {
2785   // Expect the changes to be reported as events on the child directory, not as
2786   // modifications to the parent directory.
2787   FilePath parent(temp_dir_.GetPath().AppendASCII("parent"));
2788   FilePath child(parent.AppendASCII("child"));
2789 
2790   const auto each_event_matcher = testing::Each(testing::AllOf(
2791       HasPath(report_modified_path() ? child : parent),
2792       testing::Not(HasErrored()), IsDirectory(), testing::Not(HasCookie())));
2793   const auto sequence_matcher =
2794       testing::ElementsAre(IsType(FilePathWatcher::ChangeType::kCreated),
2795                            IsType(FilePathWatcher::ChangeType::kDeleted));
2796   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2797 
2798   ASSERT_TRUE(CreateDirectory(parent));
2799 
2800   FilePathWatcher watcher;
2801   TestDelegate delegate;
2802   ASSERT_TRUE(
2803       SetupWatchWithChangeInfo(parent, &watcher, &delegate, GetWatchOptions()));
2804 
2805   ASSERT_TRUE(CreateDirectory(child));
2806   ASSERT_TRUE(DeletePathRecursively(child));
2807   delegate.RunUntilEventsMatch(matcher);
2808 }
2809 
TEST_P(FilePathWatcherWithChangeInfoTest,NestedDirectoryInDirectory)2810 TEST_P(FilePathWatcherWithChangeInfoTest, NestedDirectoryInDirectory) {
2811   FilePath parent(temp_dir_.GetPath().AppendASCII("parent"));
2812   FilePath child(parent.AppendASCII("child"));
2813   FilePath grandchild(child.AppendASCII("grandchild"));
2814 
2815   const auto each_event_matcher = testing::Each(
2816       testing::AllOf(testing::Not(HasErrored()), testing::Not(HasCookie())));
2817 
2818   EventListMatcher sequence_matcher;
2819   if (type() == FilePathWatcher::Type::kRecursive) {
2820     sequence_matcher = testing::IsSupersetOf(
2821         {testing::AllOf(HasPath(report_modified_path() ? child : parent),
2822                         IsDirectory(),
2823                         IsType(FilePathWatcher::ChangeType::kCreated)),
2824          testing::AllOf(HasPath(report_modified_path() ? grandchild : parent),
2825                         IsFile(),
2826                         IsType(FilePathWatcher::ChangeType::kCreated)),
2827          testing::AllOf(HasPath(report_modified_path() ? grandchild : parent),
2828                         IsFile(),
2829                         IsType(FilePathWatcher::ChangeType::kModified)),
2830          testing::AllOf(HasPath(report_modified_path() ? grandchild : parent),
2831                         IsFile(),
2832                         IsType(FilePathWatcher::ChangeType::kDeleted)),
2833          testing::AllOf(HasPath(report_modified_path() ? child : parent),
2834                         IsDirectory(),
2835                         IsType(FilePathWatcher::ChangeType::kDeleted))});
2836   } else {
2837     // Do not expect changes to `grandchild` when watching `parent`
2838     // non-recursively.
2839     sequence_matcher = testing::ElementsAre(
2840         testing::AllOf(HasPath(report_modified_path() ? child : parent),
2841                        IsDirectory(),
2842                        IsType(FilePathWatcher::ChangeType::kCreated)),
2843         testing::AllOf(HasPath(report_modified_path() ? child : parent),
2844                        IsDirectory(),
2845                        IsType(FilePathWatcher::ChangeType::kDeleted)));
2846   }
2847   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2848 
2849   ASSERT_TRUE(CreateDirectory(parent));
2850 
2851   FilePathWatcher watcher;
2852   TestDelegate delegate;
2853   ASSERT_TRUE(
2854       SetupWatchWithChangeInfo(parent, &watcher, &delegate, GetWatchOptions()));
2855 
2856   ASSERT_TRUE(CreateDirectory(child));
2857   // Allow the watcher to reconstruct its watch list.
2858   SpinEventLoopForABit();
2859 
2860   ASSERT_TRUE(WriteFile(grandchild, "contents"));
2861   ASSERT_TRUE(WriteFile(grandchild, "contents v2"));
2862   ASSERT_TRUE(DeleteFile(grandchild));
2863   ASSERT_TRUE(DeletePathRecursively(child));
2864   delegate.RunUntilEventsMatch(matcher);
2865 }
2866 
TEST_P(FilePathWatcherWithChangeInfoTest,DeleteDirectoryRecursively)2867 TEST_P(FilePathWatcherWithChangeInfoTest, DeleteDirectoryRecursively) {
2868   FilePath grandparent(temp_dir_.GetPath());
2869   FilePath parent(grandparent.AppendASCII("parent"));
2870   FilePath child(parent.AppendASCII("child"));
2871   FilePath grandchild(child.AppendASCII("grandchild"));
2872 
2873   const auto each_event_matcher = testing::Each(testing::AllOf(
2874       testing::Not(HasErrored()), IsType(FilePathWatcher::ChangeType::kDeleted),
2875       testing::Not(HasCookie())));
2876 
2877   // TODO(https://crbug.com/1432044): inotify incorrectly reports an additional
2878   // deletion event. Once fixed, update this matcher to assert that only one
2879   // event per removed file/dir is received.
2880   EventListMatcher sequence_matcher;
2881   if (type() == FilePathWatcher::Type::kRecursive) {
2882     sequence_matcher = testing::IsSupersetOf(
2883         {testing::AllOf(HasPath(parent), IsDirectory()),
2884          testing::AllOf(HasPath(report_modified_path() ? child : parent),
2885                         IsDirectory()),
2886          // TODO(https://crbug.com/1432044): inotify incorrectly reports this
2887          // deletion on the path of just "grandchild" rather than on
2888          // "/absolute/path/blah/blah/parent/child/grantchild".
2889          testing::AllOf(
2890              HasPath(report_modified_path() ? grandchild.BaseName() : parent),
2891              IsFile())});
2892   } else {
2893     // Do not expect changes to `grandchild` when watching `parent`
2894     // non-recursively.
2895     sequence_matcher = testing::IsSupersetOf(
2896         {testing::AllOf(HasPath(parent), IsDirectory()),
2897          testing::AllOf(HasPath(report_modified_path() ? child : parent),
2898                         IsDirectory())});
2899   }
2900   const auto matcher = testing::AllOf(each_event_matcher, sequence_matcher);
2901 
2902   ASSERT_TRUE(CreateDirectory(parent));
2903   ASSERT_TRUE(CreateDirectory(child));
2904   ASSERT_TRUE(WriteFile(grandchild, "contents"));
2905 
2906   FilePathWatcher watcher;
2907   TestDelegate delegate;
2908   ASSERT_TRUE(
2909       SetupWatchWithChangeInfo(parent, &watcher, &delegate, GetWatchOptions()));
2910 
2911   ASSERT_TRUE(DeletePathRecursively(grandparent));
2912   delegate.RunUntilEventsMatch(matcher);
2913 }
2914 
2915 INSTANTIATE_TEST_SUITE_P(
2916     /* no prefix */,
2917     FilePathWatcherWithChangeInfoTest,
2918     ::testing::Combine(::testing::Values(FilePathWatcher::Type::kNonRecursive,
2919                                          FilePathWatcher::Type::kRecursive),
2920                        // Is WatchOptions.report_modified_path enabled?
2921                        ::testing::Bool()));
2922 
2923 #else
2924 
TEST_F(FilePathWatcherTest,UseDummyChangeInfoIfNotSupported)2925 TEST_F(FilePathWatcherTest, UseDummyChangeInfoIfNotSupported) {
2926   const auto matcher = testing::ElementsAre(testing::AllOf(
2927       HasPath(test_file()), testing::Not(HasErrored()), IsUnknownPathType(),
2928       IsType(FilePathWatcher::ChangeType::kUnsupported),
2929       testing::Not(HasCookie())));
2930 
2931   FilePathWatcher watcher;
2932   TestDelegate delegate;
2933   ASSERT_TRUE(
2934       SetupWatchWithChangeInfo(test_file(), &watcher, &delegate,
2935                                {.type = FilePathWatcher::Type::kNonRecursive}));
2936 
2937   ASSERT_TRUE(CreateDirectory(test_file()));
2938   delegate.RunUntilEventsMatch(matcher);
2939 }
2940 
2941 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
2942         // BUILDFLAG(IS_ANDROID)
2943 
2944 }  // namespace base
2945