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