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