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