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 <memory>
8 #include <set>
9 #include <string>
10 #include <vector>
11 
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/functional/bind.h"
16 #include "base/functional/callback_helpers.h"
17 #include "base/location.h"
18 #include "base/logging.h"
19 #include "base/run_loop.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/synchronization/waitable_event.h"
22 #include "base/task/single_thread_task_runner.h"
23 #include "base/test/bind.h"
24 #include "base/test/task_environment.h"
25 #include "base/test/test_file_util.h"
26 #include "base/test/test_timeouts.h"
27 #include "base/threading/thread.h"
28 #include "build/build_config.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 
31 #if BUILDFLAG(IS_WIN)
32 #include <windows.h>
33 
34 #include <aclapi.h>
35 #elif BUILDFLAG(IS_POSIX)
36 #include <sys/stat.h>
37 #endif
38 
39 #if BUILDFLAG(IS_ANDROID)
40 #include "base/android/path_utils.h"
41 #endif  // BUILDFLAG(IS_ANDROID)
42 
43 #if BUILDFLAG(IS_POSIX)
44 #include "base/files/file_descriptor_watcher_posix.h"
45 #endif  // BUILDFLAG(IS_POSIX)
46 
47 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
48 #include "base/files/file_path_watcher_inotify.h"
49 #include "base/format_macros.h"
50 #endif
51 
52 namespace base {
53 
54 namespace {
55 
56 class TestDelegate;
57 
58 // Aggregates notifications from the test delegates and breaks the run loop
59 // the test thread is waiting on once they all came in.
60 class NotificationCollector
61     : public base::RefCountedThreadSafe<NotificationCollector> {
62  public:
NotificationCollector()63   NotificationCollector()
64       : task_runner_(SingleThreadTaskRunner::GetCurrentDefault()) {}
65 
66   // Called from the file thread by the delegates.
OnChange(TestDelegate * delegate)67   void OnChange(TestDelegate* delegate) {
68     task_runner_->PostTask(
69         FROM_HERE, base::BindOnce(&NotificationCollector::RecordChange, this,
70                                   base::Unretained(delegate)));
71   }
72 
Register(TestDelegate * delegate)73   void Register(TestDelegate* delegate) {
74     delegates_.insert(delegate);
75   }
76 
Reset(base::OnceClosure signal_closure)77   void Reset(base::OnceClosure signal_closure) {
78     signal_closure_ = std::move(signal_closure);
79     signaled_.clear();
80   }
81 
Success()82   bool Success() {
83     return signaled_ == delegates_;
84   }
85 
86  private:
87   friend class base::RefCountedThreadSafe<NotificationCollector>;
88   ~NotificationCollector() = default;
89 
RecordChange(TestDelegate * delegate)90   void RecordChange(TestDelegate* delegate) {
91     // Warning: |delegate| is Unretained. Do not dereference.
92     ASSERT_TRUE(task_runner_->BelongsToCurrentThread());
93     ASSERT_TRUE(delegates_.count(delegate));
94     signaled_.insert(delegate);
95 
96     // Check whether all delegates have been signaled.
97     if (signal_closure_ && signaled_ == delegates_)
98       std::move(signal_closure_).Run();
99   }
100 
101   // Set of registered delegates.
102   std::set<TestDelegate*> delegates_;
103 
104   // Set of signaled delegates.
105   std::set<TestDelegate*> signaled_;
106 
107   // The loop we should break after all delegates signaled.
108   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
109 
110   // Closure to run when all delegates have signaled.
111   base::OnceClosure signal_closure_;
112 };
113 
114 class TestDelegateBase : public SupportsWeakPtr<TestDelegateBase> {
115  public:
116   TestDelegateBase() = default;
117   TestDelegateBase(const TestDelegateBase&) = delete;
118   TestDelegateBase& operator=(const TestDelegateBase&) = delete;
119   virtual ~TestDelegateBase() = default;
120 
121   virtual void OnFileChanged(const FilePath& path, bool error) = 0;
122 };
123 
124 // A mock class for testing. Gmock is not appropriate because it is not
125 // thread-safe for setting expectations. Thus the test code cannot safely
126 // reset expectations while the file watcher is running.
127 // Instead, TestDelegate gets the notifications from FilePathWatcher and uses
128 // NotificationCollector to aggregate the results.
129 class TestDelegate : public TestDelegateBase {
130  public:
TestDelegate(NotificationCollector * collector)131   explicit TestDelegate(NotificationCollector* collector)
132       : collector_(collector) {
133     collector_->Register(this);
134   }
135   TestDelegate(const TestDelegate&) = delete;
136   TestDelegate& operator=(const TestDelegate&) = delete;
137   ~TestDelegate() override = default;
138 
139   // Configure this delegate so that it expects an error.
set_expect_error()140   void set_expect_error() { expect_error_ = true; }
141   // Returns observed paths for each invocation of OnFileChanged.
get_observed_paths() const142   std::vector<FilePath> get_observed_paths() const { return observed_paths_; }
143 
144   // TestDelegateBase:
OnFileChanged(const FilePath & path,bool error)145   void OnFileChanged(const FilePath& path, bool error) override {
146     observed_paths_.push_back(path);
147 
148     if (error != expect_error_) {
149       ADD_FAILURE() << "Unexpected change for \"" << path
150                     << "\" with |error| = " << (error ? "true" : "false");
151     } else {
152       collector_->OnChange(this);
153     }
154   }
155 
156  private:
157   scoped_refptr<NotificationCollector> collector_;
158   bool expect_error_ = false;
159   std::vector<FilePath> observed_paths_;
160 };
161 
162 class FilePathWatcherTest : public testing::Test {
163  public:
FilePathWatcherTest()164   FilePathWatcherTest()
165 #if BUILDFLAG(IS_POSIX)
166       : task_environment_(test::TaskEnvironment::MainThreadType::IO)
167 #endif
168   {
169   }
170 
171   FilePathWatcherTest(const FilePathWatcherTest&) = delete;
172   FilePathWatcherTest& operator=(const FilePathWatcherTest&) = delete;
173   ~FilePathWatcherTest() override = default;
174 
175  protected:
SetUp()176   void SetUp() override {
177 #if BUILDFLAG(IS_ANDROID)
178     // Watching files is only permitted when all parent directories are
179     // accessible, which is not the case for the default temp directory
180     // on Android which is under /data/data.  Use /sdcard instead.
181     // TODO(pauljensen): Remove this when crbug.com/475568 is fixed.
182     FilePath parent_dir;
183     ASSERT_TRUE(android::GetExternalStorageDirectory(&parent_dir));
184     ASSERT_TRUE(temp_dir_.CreateUniqueTempDirUnderPath(parent_dir));
185 #else   // BUILDFLAG(IS_ANDROID)
186     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
187 #endif  // BUILDFLAG(IS_ANDROID)
188     collector_ = new NotificationCollector();
189   }
190 
TearDown()191   void TearDown() override { RunLoop().RunUntilIdle(); }
192 
test_file()193   FilePath test_file() {
194     return temp_dir_.GetPath().AppendASCII("FilePathWatcherTest");
195   }
196 
test_link()197   FilePath test_link() {
198     return temp_dir_.GetPath().AppendASCII("FilePathWatcherTest.lnk");
199   }
200 
201   [[nodiscard]] bool SetupWatch(const FilePath& target,
202                                 FilePathWatcher* watcher,
203                                 TestDelegateBase* delegate,
204                                 FilePathWatcher::Type watch_type);
205 
206   [[nodiscard]] bool SetupWatchWithOptions(
207       const FilePath& target,
208       FilePathWatcher* watcher,
209       TestDelegateBase* delegate,
210       FilePathWatcher::WatchOptions watch_options);
211 
WaitForEvent()212   [[nodiscard]] bool WaitForEvent() {
213     return WaitForEventWithTimeout(TestTimeouts::action_timeout());
214   }
215 
WaitForEventWithTimeout(TimeDelta timeout)216   [[nodiscard]] bool WaitForEventWithTimeout(TimeDelta timeout) {
217     RunLoop run_loop;
218     collector_->Reset(run_loop.QuitClosure());
219 
220     // Make sure we timeout if we don't get notified.
221     SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
222         FROM_HERE, run_loop.QuitClosure(), timeout);
223     run_loop.Run();
224     return collector_->Success();
225   }
226 
collector()227   NotificationCollector* collector() { return collector_.get(); }
228 
229   test::TaskEnvironment task_environment_;
230 
231   ScopedTempDir temp_dir_;
232   scoped_refptr<NotificationCollector> collector_;
233 };
234 
SetupWatch(const FilePath & target,FilePathWatcher * watcher,TestDelegateBase * delegate,FilePathWatcher::Type watch_type)235 bool FilePathWatcherTest::SetupWatch(const FilePath& target,
236                                      FilePathWatcher* watcher,
237                                      TestDelegateBase* delegate,
238                                      FilePathWatcher::Type watch_type) {
239   return watcher->Watch(target, watch_type,
240                         base::BindRepeating(&TestDelegateBase::OnFileChanged,
241                                             delegate->AsWeakPtr()));
242 }
243 
SetupWatchWithOptions(const FilePath & target,FilePathWatcher * watcher,TestDelegateBase * delegate,FilePathWatcher::WatchOptions watch_options)244 bool FilePathWatcherTest::SetupWatchWithOptions(
245     const FilePath& target,
246     FilePathWatcher* watcher,
247     TestDelegateBase* delegate,
248     FilePathWatcher::WatchOptions watch_options) {
249   return watcher->WatchWithOptions(
250       target, watch_options,
251       base::BindRepeating(&TestDelegateBase::OnFileChanged,
252                           delegate->AsWeakPtr()));
253 }
254 
255 // Basic test: Create the file and verify that we notice.
TEST_F(FilePathWatcherTest,NewFile)256 TEST_F(FilePathWatcherTest, NewFile) {
257   FilePathWatcher watcher;
258   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
259   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(),
260                          FilePathWatcher::Type::kNonRecursive));
261 
262   ASSERT_TRUE(WriteFile(test_file(), "content"));
263   ASSERT_TRUE(WaitForEvent());
264 }
265 
266 // Verify that modifying the file is caught.
TEST_F(FilePathWatcherTest,ModifiedFile)267 TEST_F(FilePathWatcherTest, ModifiedFile) {
268   ASSERT_TRUE(WriteFile(test_file(), "content"));
269 
270   FilePathWatcher watcher;
271   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
272   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(),
273                          FilePathWatcher::Type::kNonRecursive));
274 
275   // Now make sure we get notified if the file is modified.
276   ASSERT_TRUE(WriteFile(test_file(), "new content"));
277   ASSERT_TRUE(WaitForEvent());
278 }
279 
280 // Verify that moving the file into place is caught.
TEST_F(FilePathWatcherTest,MovedFile)281 TEST_F(FilePathWatcherTest, MovedFile) {
282   FilePath source_file(temp_dir_.GetPath().AppendASCII("source"));
283   ASSERT_TRUE(WriteFile(source_file, "content"));
284 
285   FilePathWatcher watcher;
286   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
287   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(),
288                          FilePathWatcher::Type::kNonRecursive));
289 
290   // Now make sure we get notified if the file is modified.
291   ASSERT_TRUE(base::Move(source_file, test_file()));
292   ASSERT_TRUE(WaitForEvent());
293 }
294 
TEST_F(FilePathWatcherTest,DeletedFile)295 TEST_F(FilePathWatcherTest, DeletedFile) {
296   ASSERT_TRUE(WriteFile(test_file(), "content"));
297 
298   FilePathWatcher watcher;
299   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
300   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(),
301                          FilePathWatcher::Type::kNonRecursive));
302 
303   // Now make sure we get notified if the file is deleted.
304   base::DeleteFile(test_file());
305   ASSERT_TRUE(WaitForEvent());
306 }
307 
308 // Used by the DeleteDuringNotify test below.
309 // Deletes the FilePathWatcher when it's notified.
310 class Deleter : public TestDelegateBase {
311  public:
Deleter(base::OnceClosure done_closure)312   explicit Deleter(base::OnceClosure done_closure)
313       : watcher_(std::make_unique<FilePathWatcher>()),
314         done_closure_(std::move(done_closure)) {}
315   Deleter(const Deleter&) = delete;
316   Deleter& operator=(const Deleter&) = delete;
317   ~Deleter() override = default;
318 
OnFileChanged(const FilePath &,bool)319   void OnFileChanged(const FilePath&, bool) override {
320     watcher_.reset();
321     std::move(done_closure_).Run();
322   }
323 
watcher() const324   FilePathWatcher* watcher() const { return watcher_.get(); }
325 
326  private:
327   std::unique_ptr<FilePathWatcher> watcher_;
328   base::OnceClosure done_closure_;
329 };
330 
331 // Verify that deleting a watcher during the callback doesn't crash.
TEST_F(FilePathWatcherTest,DeleteDuringNotify)332 TEST_F(FilePathWatcherTest, DeleteDuringNotify) {
333   base::RunLoop run_loop;
334   Deleter deleter(run_loop.QuitClosure());
335   ASSERT_TRUE(SetupWatch(test_file(), deleter.watcher(), &deleter,
336                          FilePathWatcher::Type::kNonRecursive));
337 
338   ASSERT_TRUE(WriteFile(test_file(), "content"));
339   run_loop.Run();
340 
341   // We win if we haven't crashed yet.
342   // Might as well double-check it got deleted, too.
343   ASSERT_TRUE(deleter.watcher() == nullptr);
344 }
345 
346 // Verify that deleting the watcher works even if there is a pending
347 // notification.
TEST_F(FilePathWatcherTest,DestroyWithPendingNotification)348 TEST_F(FilePathWatcherTest, DestroyWithPendingNotification) {
349   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
350   FilePathWatcher watcher;
351   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(),
352                          FilePathWatcher::Type::kNonRecursive));
353   ASSERT_TRUE(WriteFile(test_file(), "content"));
354 }
355 
TEST_F(FilePathWatcherTest,MultipleWatchersSingleFile)356 TEST_F(FilePathWatcherTest, MultipleWatchersSingleFile) {
357   FilePathWatcher watcher1, watcher2;
358   std::unique_ptr<TestDelegate> delegate1(new TestDelegate(collector()));
359   std::unique_ptr<TestDelegate> delegate2(new TestDelegate(collector()));
360   ASSERT_TRUE(SetupWatch(test_file(), &watcher1, delegate1.get(),
361                          FilePathWatcher::Type::kNonRecursive));
362   ASSERT_TRUE(SetupWatch(test_file(), &watcher2, delegate2.get(),
363                          FilePathWatcher::Type::kNonRecursive));
364 
365   ASSERT_TRUE(WriteFile(test_file(), "content"));
366   ASSERT_TRUE(WaitForEvent());
367 }
368 
369 // Verify that watching a file whose parent directory doesn't exist yet works if
370 // the directory and file are created eventually.
TEST_F(FilePathWatcherTest,NonExistentDirectory)371 TEST_F(FilePathWatcherTest, NonExistentDirectory) {
372   FilePathWatcher watcher;
373   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
374   FilePath file(dir.AppendASCII("file"));
375   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
376   ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(),
377                          FilePathWatcher::Type::kNonRecursive));
378 
379   ASSERT_TRUE(base::CreateDirectory(dir));
380 
381   ASSERT_TRUE(WriteFile(file, "content"));
382 
383   VLOG(1) << "Waiting for file creation";
384   ASSERT_TRUE(WaitForEvent());
385 
386   ASSERT_TRUE(WriteFile(file, "content v2"));
387   VLOG(1) << "Waiting for file change";
388   ASSERT_TRUE(WaitForEvent());
389 
390   ASSERT_TRUE(base::DeleteFile(file));
391   VLOG(1) << "Waiting for file deletion";
392   ASSERT_TRUE(WaitForEvent());
393 }
394 
395 // Exercises watch reconfiguration for the case that directories on the path
396 // are rapidly created.
TEST_F(FilePathWatcherTest,DirectoryChain)397 TEST_F(FilePathWatcherTest, DirectoryChain) {
398   FilePath path(temp_dir_.GetPath());
399   std::vector<std::string> dir_names;
400   for (int i = 0; i < 20; i++) {
401     std::string dir(base::StringPrintf("d%d", i));
402     dir_names.push_back(dir);
403     path = path.AppendASCII(dir);
404   }
405 
406   FilePathWatcher watcher;
407   FilePath file(path.AppendASCII("file"));
408   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
409   ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(),
410                          FilePathWatcher::Type::kNonRecursive));
411 
412   FilePath sub_path(temp_dir_.GetPath());
413   for (std::vector<std::string>::const_iterator d(dir_names.begin());
414        d != dir_names.end(); ++d) {
415     sub_path = sub_path.AppendASCII(*d);
416     ASSERT_TRUE(base::CreateDirectory(sub_path));
417   }
418   VLOG(1) << "Create File";
419   ASSERT_TRUE(WriteFile(file, "content"));
420   VLOG(1) << "Waiting for file creation";
421   ASSERT_TRUE(WaitForEvent());
422 
423   ASSERT_TRUE(WriteFile(file, "content v2"));
424   VLOG(1) << "Waiting for file modification";
425   ASSERT_TRUE(WaitForEvent());
426 }
427 
TEST_F(FilePathWatcherTest,DisappearingDirectory)428 TEST_F(FilePathWatcherTest, DisappearingDirectory) {
429   FilePathWatcher watcher;
430   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
431   FilePath file(dir.AppendASCII("file"));
432   ASSERT_TRUE(base::CreateDirectory(dir));
433   ASSERT_TRUE(WriteFile(file, "content"));
434   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
435   ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(),
436                          FilePathWatcher::Type::kNonRecursive));
437 
438   ASSERT_TRUE(base::DeletePathRecursively(dir));
439   ASSERT_TRUE(WaitForEvent());
440 }
441 
442 // Tests that a file that is deleted and reappears is tracked correctly.
TEST_F(FilePathWatcherTest,DeleteAndRecreate)443 TEST_F(FilePathWatcherTest, DeleteAndRecreate) {
444   ASSERT_TRUE(WriteFile(test_file(), "content"));
445   FilePathWatcher watcher;
446   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
447   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(),
448                          FilePathWatcher::Type::kNonRecursive));
449 
450   ASSERT_TRUE(base::DeleteFile(test_file()));
451   VLOG(1) << "Waiting for file deletion";
452   ASSERT_TRUE(WaitForEvent());
453 
454   ASSERT_TRUE(WriteFile(test_file(), "content"));
455   VLOG(1) << "Waiting for file creation";
456   ASSERT_TRUE(WaitForEvent());
457 }
458 
TEST_F(FilePathWatcherTest,WatchDirectory)459 TEST_F(FilePathWatcherTest, WatchDirectory) {
460   FilePathWatcher watcher;
461   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
462   FilePath file1(dir.AppendASCII("file1"));
463   FilePath file2(dir.AppendASCII("file2"));
464   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
465   ASSERT_TRUE(SetupWatch(dir, &watcher, delegate.get(),
466                          FilePathWatcher::Type::kNonRecursive));
467 
468   ASSERT_TRUE(base::CreateDirectory(dir));
469   VLOG(1) << "Waiting for directory creation";
470   ASSERT_TRUE(WaitForEvent());
471 
472   ASSERT_TRUE(WriteFile(file1, "content"));
473   VLOG(1) << "Waiting for file1 creation";
474   ASSERT_TRUE(WaitForEvent());
475 
476 #if !BUILDFLAG(IS_APPLE)
477   // Mac implementation does not detect files modified in a directory.
478   ASSERT_TRUE(WriteFile(file1, "content v2"));
479   VLOG(1) << "Waiting for file1 modification";
480   ASSERT_TRUE(WaitForEvent());
481 #endif  // !BUILDFLAG(IS_APPLE)
482 
483   ASSERT_TRUE(base::DeleteFile(file1));
484   VLOG(1) << "Waiting for file1 deletion";
485   ASSERT_TRUE(WaitForEvent());
486 
487   ASSERT_TRUE(WriteFile(file2, "content"));
488   VLOG(1) << "Waiting for file2 creation";
489   ASSERT_TRUE(WaitForEvent());
490 }
491 
TEST_F(FilePathWatcherTest,MoveParent)492 TEST_F(FilePathWatcherTest, MoveParent) {
493   FilePathWatcher file_watcher;
494   FilePathWatcher subdir_watcher;
495   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
496   FilePath dest(temp_dir_.GetPath().AppendASCII("dest"));
497   FilePath subdir(dir.AppendASCII("subdir"));
498   FilePath file(subdir.AppendASCII("file"));
499   std::unique_ptr<TestDelegate> file_delegate(new TestDelegate(collector()));
500   ASSERT_TRUE(SetupWatch(file, &file_watcher, file_delegate.get(),
501                          FilePathWatcher::Type::kNonRecursive));
502   std::unique_ptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
503   ASSERT_TRUE(SetupWatch(subdir, &subdir_watcher, subdir_delegate.get(),
504                          FilePathWatcher::Type::kNonRecursive));
505 
506   // Setup a directory hierarchy.
507   ASSERT_TRUE(base::CreateDirectory(subdir));
508   ASSERT_TRUE(WriteFile(file, "content"));
509   VLOG(1) << "Waiting for file creation";
510   ASSERT_TRUE(WaitForEvent());
511 
512   // Move the parent directory.
513   base::Move(dir, dest);
514   VLOG(1) << "Waiting for directory move";
515   ASSERT_TRUE(WaitForEvent());
516 }
517 
TEST_F(FilePathWatcherTest,RecursiveWatch)518 TEST_F(FilePathWatcherTest, RecursiveWatch) {
519   FilePathWatcher watcher;
520   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
521   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
522   bool setup_result = SetupWatch(dir, &watcher, delegate.get(),
523                                  FilePathWatcher::Type::kRecursive);
524   if (!FilePathWatcher::RecursiveWatchAvailable()) {
525     ASSERT_FALSE(setup_result);
526     return;
527   }
528   ASSERT_TRUE(setup_result);
529 
530   // Main directory("dir") creation.
531   ASSERT_TRUE(base::CreateDirectory(dir));
532   ASSERT_TRUE(WaitForEvent());
533 
534   // Create "$dir/file1".
535   FilePath file1(dir.AppendASCII("file1"));
536   ASSERT_TRUE(WriteFile(file1, "content"));
537   ASSERT_TRUE(WaitForEvent());
538 
539   // Create "$dir/subdir".
540   FilePath subdir(dir.AppendASCII("subdir"));
541   ASSERT_TRUE(base::CreateDirectory(subdir));
542   ASSERT_TRUE(WaitForEvent());
543 
544   // Create "$dir/subdir/subdir2".
545   FilePath subdir2(subdir.AppendASCII("subdir2"));
546   ASSERT_TRUE(base::CreateDirectory(subdir2));
547   ASSERT_TRUE(WaitForEvent());
548 
549   // Rename "$dir/subdir/subdir2" to "$dir/subdir/subdir2b".
550   FilePath subdir2b(subdir.AppendASCII("subdir2b"));
551   base::Move(subdir2, subdir2b);
552   ASSERT_TRUE(WaitForEvent());
553 
554 // Mac and Win don't generate events for Touch.
555 // Android TouchFile returns false.
556 #if !(BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID))
557   // Touch "$dir".
558   Time access_time;
559   ASSERT_TRUE(Time::FromString("Wed, 16 Nov 1994, 00:00:00", &access_time));
560   ASSERT_TRUE(base::TouchFile(dir, access_time, access_time));
561   ASSERT_TRUE(WaitForEvent());
562 #endif  // !(BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID))
563 
564   // Create "$dir/subdir/subdir_file1".
565   FilePath subdir_file1(subdir.AppendASCII("subdir_file1"));
566   ASSERT_TRUE(WriteFile(subdir_file1, "content"));
567   ASSERT_TRUE(WaitForEvent());
568 
569   // Create "$dir/subdir/subdir_child_dir".
570   FilePath subdir_child_dir(subdir.AppendASCII("subdir_child_dir"));
571   ASSERT_TRUE(base::CreateDirectory(subdir_child_dir));
572   ASSERT_TRUE(WaitForEvent());
573 
574   // Create "$dir/subdir/subdir_child_dir/child_dir_file1".
575   FilePath child_dir_file1(subdir_child_dir.AppendASCII("child_dir_file1"));
576   ASSERT_TRUE(WriteFile(child_dir_file1, "content v2"));
577   ASSERT_TRUE(WaitForEvent());
578 
579   // Write into "$dir/subdir/subdir_child_dir/child_dir_file1".
580   ASSERT_TRUE(WriteFile(child_dir_file1, "content"));
581   ASSERT_TRUE(WaitForEvent());
582 
583 // Apps cannot change file attributes on Android in /sdcard as /sdcard uses the
584 // "fuse" file system, while /data uses "ext4".  Running these tests in /data
585 // would be preferable and allow testing file attributes and symlinks.
586 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
587 // the |temp_dir_| in /data.
588 #if !BUILDFLAG(IS_ANDROID)
589   // Modify "$dir/subdir/subdir_child_dir/child_dir_file1" attributes.
590   ASSERT_TRUE(base::MakeFileUnreadable(child_dir_file1));
591   ASSERT_TRUE(WaitForEvent());
592 #endif
593 
594   // Delete "$dir/subdir/subdir_file1".
595   ASSERT_TRUE(base::DeleteFile(subdir_file1));
596   ASSERT_TRUE(WaitForEvent());
597 
598   // Delete "$dir/subdir/subdir_child_dir/child_dir_file1".
599   ASSERT_TRUE(base::DeleteFile(child_dir_file1));
600   ASSERT_TRUE(WaitForEvent());
601 }
602 
603 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
604 // Apps cannot create symlinks on Android in /sdcard as /sdcard uses the
605 // "fuse" file system, while /data uses "ext4".  Running these tests in /data
606 // would be preferable and allow testing file attributes and symlinks.
607 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
608 // the |temp_dir_| in /data.
609 //
610 // This test is disabled on Fuchsia since it doesn't support symlinking.
TEST_F(FilePathWatcherTest,RecursiveWithSymLink)611 TEST_F(FilePathWatcherTest, RecursiveWithSymLink) {
612   if (!FilePathWatcher::RecursiveWatchAvailable())
613     return;
614 
615   FilePathWatcher watcher;
616   FilePath test_dir(temp_dir_.GetPath().AppendASCII("test_dir"));
617   ASSERT_TRUE(base::CreateDirectory(test_dir));
618   FilePath symlink(test_dir.AppendASCII("symlink"));
619   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
620   ASSERT_TRUE(SetupWatch(symlink, &watcher, delegate.get(),
621                          FilePathWatcher::Type::kRecursive));
622 
623   // Link creation.
624   FilePath target1(temp_dir_.GetPath().AppendASCII("target1"));
625   ASSERT_TRUE(base::CreateSymbolicLink(target1, symlink));
626   ASSERT_TRUE(WaitForEvent());
627 
628   // Target1 creation.
629   ASSERT_TRUE(base::CreateDirectory(target1));
630   ASSERT_TRUE(WaitForEvent());
631 
632   // Create a file in target1.
633   FilePath target1_file(target1.AppendASCII("file"));
634   ASSERT_TRUE(WriteFile(target1_file, "content"));
635   ASSERT_TRUE(WaitForEvent());
636 
637   // Link change.
638   FilePath target2(temp_dir_.GetPath().AppendASCII("target2"));
639   ASSERT_TRUE(base::CreateDirectory(target2));
640   ASSERT_TRUE(base::DeleteFile(symlink));
641   ASSERT_TRUE(base::CreateSymbolicLink(target2, symlink));
642   ASSERT_TRUE(WaitForEvent());
643 
644   // Create a file in target2.
645   FilePath target2_file(target2.AppendASCII("file"));
646   ASSERT_TRUE(WriteFile(target2_file, "content"));
647   ASSERT_TRUE(WaitForEvent());
648 }
649 #endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
650 
TEST_F(FilePathWatcherTest,MoveChild)651 TEST_F(FilePathWatcherTest, MoveChild) {
652   FilePathWatcher file_watcher;
653   FilePathWatcher subdir_watcher;
654   FilePath source_dir(temp_dir_.GetPath().AppendASCII("source"));
655   FilePath source_subdir(source_dir.AppendASCII("subdir"));
656   FilePath source_file(source_subdir.AppendASCII("file"));
657   FilePath dest_dir(temp_dir_.GetPath().AppendASCII("dest"));
658   FilePath dest_subdir(dest_dir.AppendASCII("subdir"));
659   FilePath dest_file(dest_subdir.AppendASCII("file"));
660 
661   // Setup a directory hierarchy.
662   ASSERT_TRUE(base::CreateDirectory(source_subdir));
663   ASSERT_TRUE(WriteFile(source_file, "content"));
664 
665   std::unique_ptr<TestDelegate> file_delegate(new TestDelegate(collector()));
666   ASSERT_TRUE(SetupWatch(dest_file, &file_watcher, file_delegate.get(),
667                          FilePathWatcher::Type::kNonRecursive));
668   std::unique_ptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
669   ASSERT_TRUE(SetupWatch(dest_subdir, &subdir_watcher, subdir_delegate.get(),
670                          FilePathWatcher::Type::kNonRecursive));
671 
672   // Move the directory into place, s.t. the watched file appears.
673   ASSERT_TRUE(base::Move(source_dir, dest_dir));
674   ASSERT_TRUE(WaitForEvent());
675 }
676 
677 // Verify that changing attributes on a file is caught
678 #if BUILDFLAG(IS_ANDROID)
679 // Apps cannot change file attributes on Android in /sdcard as /sdcard uses the
680 // "fuse" file system, while /data uses "ext4".  Running these tests in /data
681 // would be preferable and allow testing file attributes and symlinks.
682 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
683 // the |temp_dir_| in /data.
684 #define FileAttributesChanged DISABLED_FileAttributesChanged
685 #endif  // BUILDFLAG(IS_ANDROID)
TEST_F(FilePathWatcherTest,FileAttributesChanged)686 TEST_F(FilePathWatcherTest, FileAttributesChanged) {
687   ASSERT_TRUE(WriteFile(test_file(), "content"));
688   FilePathWatcher watcher;
689   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
690   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(),
691                          FilePathWatcher::Type::kNonRecursive));
692 
693   // Now make sure we get notified if the file is modified.
694   ASSERT_TRUE(base::MakeFileUnreadable(test_file()));
695   ASSERT_TRUE(WaitForEvent());
696 }
697 
698 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
699 
700 // Verify that creating a symlink is caught.
TEST_F(FilePathWatcherTest,CreateLink)701 TEST_F(FilePathWatcherTest, CreateLink) {
702   FilePathWatcher watcher;
703   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
704   // Note that we are watching the symlink
705   ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(),
706                          FilePathWatcher::Type::kNonRecursive));
707 
708   // Now make sure we get notified if the link is created.
709   // Note that test_file() doesn't have to exist.
710   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
711   ASSERT_TRUE(WaitForEvent());
712 }
713 
714 // Verify that deleting a symlink is caught.
TEST_F(FilePathWatcherTest,DeleteLink)715 TEST_F(FilePathWatcherTest, DeleteLink) {
716   // Unfortunately this test case only works if the link target exists.
717   // TODO(craig) fix this as part of crbug.com/91561.
718   ASSERT_TRUE(WriteFile(test_file(), "content"));
719   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
720   FilePathWatcher watcher;
721   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
722   ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(),
723                          FilePathWatcher::Type::kNonRecursive));
724 
725   // Now make sure we get notified if the link is deleted.
726   ASSERT_TRUE(base::DeleteFile(test_link()));
727   ASSERT_TRUE(WaitForEvent());
728 }
729 
730 // Verify that modifying a target file that a link is pointing to
731 // when we are watching the link is caught.
TEST_F(FilePathWatcherTest,ModifiedLinkedFile)732 TEST_F(FilePathWatcherTest, ModifiedLinkedFile) {
733   ASSERT_TRUE(WriteFile(test_file(), "content"));
734   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
735   FilePathWatcher watcher;
736   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
737   // Note that we are watching the symlink.
738   ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(),
739                          FilePathWatcher::Type::kNonRecursive));
740 
741   // Now make sure we get notified if the file is modified.
742   ASSERT_TRUE(WriteFile(test_file(), "new content"));
743   ASSERT_TRUE(WaitForEvent());
744 }
745 
746 // Verify that creating a target file that a link is pointing to
747 // when we are watching the link is caught.
TEST_F(FilePathWatcherTest,CreateTargetLinkedFile)748 TEST_F(FilePathWatcherTest, CreateTargetLinkedFile) {
749   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
750   FilePathWatcher watcher;
751   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
752   // Note that we are watching the symlink.
753   ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(),
754                          FilePathWatcher::Type::kNonRecursive));
755 
756   // Now make sure we get notified if the target file is created.
757   ASSERT_TRUE(WriteFile(test_file(), "content"));
758   ASSERT_TRUE(WaitForEvent());
759 }
760 
761 // Verify that deleting a target file that a link is pointing to
762 // when we are watching the link is caught.
TEST_F(FilePathWatcherTest,DeleteTargetLinkedFile)763 TEST_F(FilePathWatcherTest, DeleteTargetLinkedFile) {
764   ASSERT_TRUE(WriteFile(test_file(), "content"));
765   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
766   FilePathWatcher watcher;
767   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
768   // Note that we are watching the symlink.
769   ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(),
770                          FilePathWatcher::Type::kNonRecursive));
771 
772   // Now make sure we get notified if the target file is deleted.
773   ASSERT_TRUE(base::DeleteFile(test_file()));
774   ASSERT_TRUE(WaitForEvent());
775 }
776 
777 // Verify that watching a file whose parent directory is a link that
778 // doesn't exist yet works if the symlink is created eventually.
TEST_F(FilePathWatcherTest,LinkedDirectoryPart1)779 TEST_F(FilePathWatcherTest, LinkedDirectoryPart1) {
780   FilePathWatcher watcher;
781   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
782   FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
783   FilePath file(dir.AppendASCII("file"));
784   FilePath linkfile(link_dir.AppendASCII("file"));
785   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
786   // dir/file should exist.
787   ASSERT_TRUE(base::CreateDirectory(dir));
788   ASSERT_TRUE(WriteFile(file, "content"));
789   // Note that we are watching dir.lnk/file which doesn't exist yet.
790   ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(),
791                          FilePathWatcher::Type::kNonRecursive));
792 
793   ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
794   VLOG(1) << "Waiting for link creation";
795   ASSERT_TRUE(WaitForEvent());
796 
797   ASSERT_TRUE(WriteFile(file, "content v2"));
798   VLOG(1) << "Waiting for file change";
799   ASSERT_TRUE(WaitForEvent());
800 
801   ASSERT_TRUE(base::DeleteFile(file));
802   VLOG(1) << "Waiting for file deletion";
803   ASSERT_TRUE(WaitForEvent());
804 }
805 
806 // Verify that watching a file whose parent directory is a
807 // dangling symlink works if the directory is created eventually.
TEST_F(FilePathWatcherTest,LinkedDirectoryPart2)808 TEST_F(FilePathWatcherTest, LinkedDirectoryPart2) {
809   FilePathWatcher watcher;
810   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
811   FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
812   FilePath file(dir.AppendASCII("file"));
813   FilePath linkfile(link_dir.AppendASCII("file"));
814   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
815   // Now create the link from dir.lnk pointing to dir but
816   // neither dir nor dir/file exist yet.
817   ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
818   // Note that we are watching dir.lnk/file.
819   ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(),
820                          FilePathWatcher::Type::kNonRecursive));
821 
822   ASSERT_TRUE(base::CreateDirectory(dir));
823   ASSERT_TRUE(WriteFile(file, "content"));
824   VLOG(1) << "Waiting for dir/file creation";
825   ASSERT_TRUE(WaitForEvent());
826 
827   ASSERT_TRUE(WriteFile(file, "content v2"));
828   VLOG(1) << "Waiting for file change";
829   ASSERT_TRUE(WaitForEvent());
830 
831   ASSERT_TRUE(base::DeleteFile(file));
832   VLOG(1) << "Waiting for file deletion";
833   ASSERT_TRUE(WaitForEvent());
834 }
835 
836 // Verify that watching a file with a symlink on the path
837 // to the file works.
TEST_F(FilePathWatcherTest,LinkedDirectoryPart3)838 TEST_F(FilePathWatcherTest, LinkedDirectoryPart3) {
839   FilePathWatcher watcher;
840   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
841   FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
842   FilePath file(dir.AppendASCII("file"));
843   FilePath linkfile(link_dir.AppendASCII("file"));
844   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
845   ASSERT_TRUE(base::CreateDirectory(dir));
846   ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
847   // Note that we are watching dir.lnk/file but the file doesn't exist yet.
848   ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(),
849                          FilePathWatcher::Type::kNonRecursive));
850 
851   ASSERT_TRUE(WriteFile(file, "content"));
852   VLOG(1) << "Waiting for file creation";
853   ASSERT_TRUE(WaitForEvent());
854 
855   ASSERT_TRUE(WriteFile(file, "content v2"));
856   VLOG(1) << "Waiting for file change";
857   ASSERT_TRUE(WaitForEvent());
858 
859   ASSERT_TRUE(base::DeleteFile(file));
860   VLOG(1) << "Waiting for file deletion";
861   ASSERT_TRUE(WaitForEvent());
862 }
863 
864 // Regression tests that FilePathWatcherImpl does not leave its reference in
865 // `g_inotify_reader` due to a race in recursive watch.
866 // See https://crbug.com/990004.
TEST_F(FilePathWatcherTest,RacyRecursiveWatch)867 TEST_F(FilePathWatcherTest, RacyRecursiveWatch) {
868   if (!FilePathWatcher::RecursiveWatchAvailable())
869     GTEST_SKIP();
870 
871   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
872 
873   // Create and delete many subdirs. 20 is an arbitrary number big enough
874   // to have more chances to make FilePathWatcherImpl leak watchers.
875   std::vector<FilePath> subdirs;
876   for (int i = 0; i < 20; ++i)
877     subdirs.emplace_back(dir.AppendASCII(base::StringPrintf("subdir_%d", i)));
878 
879   Thread subdir_updater("SubDir Updater");
880   ASSERT_TRUE(subdir_updater.Start());
881 
882   auto subdir_update_task = base::BindLambdaForTesting([&]() {
883     for (const auto& subdir : subdirs) {
884       // First update event to trigger watch callback.
885       ASSERT_TRUE(CreateDirectory(subdir));
886 
887       // Second update event. The notification sent for this event will race
888       // with the upcoming deletion of the directory below. This test is about
889       // verifying that the impl handles this.
890       FilePath subdir_file(subdir.AppendASCII("subdir_file"));
891       ASSERT_TRUE(WriteFile(subdir_file, "content"));
892 
893       // Racy subdir delete to trigger watcher leak.
894       ASSERT_TRUE(DeletePathRecursively(subdir));
895     }
896   });
897 
898   // Try the racy subdir update 100 times.
899   for (int i = 0; i < 100; ++i) {
900     RunLoop run_loop;
901     auto watcher = std::make_unique<FilePathWatcher>();
902 
903     // Keep watch callback in `watcher_callback` so that "watcher.reset()"
904     // inside does not release the callback and the lambda capture with it.
905     // Otherwise, accessing `run_loop` as part of the lamda capture would be
906     // use-after-free under asan.
907     auto watcher_callback =
908         base::BindLambdaForTesting([&](const FilePath& path, bool error) {
909           // Release watchers in callback so that the leaked watchers of
910           // the subdir stays. Otherwise, when the subdir is deleted,
911           // its delete event would clean up leaked watchers in
912           // `g_inotify_reader`.
913           watcher.reset();
914 
915           run_loop.Quit();
916         });
917 
918     bool setup_result = watcher->Watch(dir, FilePathWatcher::Type::kRecursive,
919                                        watcher_callback);
920     ASSERT_TRUE(setup_result);
921 
922     subdir_updater.task_runner()->PostTask(FROM_HERE, subdir_update_task);
923 
924     // Wait for the watch callback.
925     run_loop.Run();
926 
927     // `watcher` should have been released.
928     ASSERT_FALSE(watcher);
929 
930     // There should be no outstanding watchers.
931     ASSERT_FALSE(FilePathWatcher::HasWatchesForTest());
932   }
933 }
934 
935 // Verify that "Watch()" returns false and callback is not invoked when limit is
936 // hit during setup.
TEST_F(FilePathWatcherTest,InotifyLimitInWatch)937 TEST_F(FilePathWatcherTest, InotifyLimitInWatch) {
938   auto watcher = std::make_unique<FilePathWatcher>();
939 
940   // "test_file()" is like "/tmp/__unique_path__/FilePathWatcherTest" and has 4
941   // dir components ("/" + 3 named parts). "Watch()" creates inotify watches
942   // for each dir component of the given dir. It would fail with limit set to 1.
943   ScopedMaxNumberOfInotifyWatchesOverrideForTest max_inotify_watches(1);
944   ASSERT_FALSE(watcher->Watch(
945       test_file(), FilePathWatcher::Type::kNonRecursive,
946       base::BindLambdaForTesting(
947           [&](const FilePath& path, bool error) { ADD_FAILURE(); })));
948 
949   // Triggers update but callback should not be invoked.
950   ASSERT_TRUE(WriteFile(test_file(), "content"));
951 
952   // Ensures that the callback did not happen.
953   base::RunLoop().RunUntilIdle();
954 }
955 
956 // Verify that "error=true" callback happens when limit is hit during update.
TEST_F(FilePathWatcherTest,InotifyLimitInUpdate)957 TEST_F(FilePathWatcherTest, InotifyLimitInUpdate) {
958   enum kTestType {
959     // Destroy watcher in "error=true" callback.
960     // No crash/deadlock when releasing watcher in the callback.
961     kDestroyWatcher,
962 
963     // Do not destroy watcher in "error=true" callback.
964     kDoNothing,
965   };
966 
967   for (auto callback_type : {kDestroyWatcher, kDoNothing}) {
968     SCOPED_TRACE(testing::Message() << "type=" << callback_type);
969 
970     base::RunLoop run_loop;
971     auto watcher = std::make_unique<FilePathWatcher>();
972 
973     bool error_callback_called = false;
974     auto watcher_callback =
975         base::BindLambdaForTesting([&](const FilePath& path, bool error) {
976           // No callback should happen after "error=true" one.
977           ASSERT_FALSE(error_callback_called);
978 
979           if (!error)
980             return;
981 
982           error_callback_called = true;
983 
984           if (callback_type == kDestroyWatcher)
985             watcher.reset();
986 
987           run_loop.Quit();
988         });
989     ASSERT_TRUE(watcher->Watch(
990         test_file(), FilePathWatcher::Type::kNonRecursive, watcher_callback));
991 
992     ScopedMaxNumberOfInotifyWatchesOverrideForTest max_inotify_watches(1);
993 
994     // Triggers update and over limit.
995     ASSERT_TRUE(WriteFile(test_file(), "content"));
996 
997     run_loop.Run();
998 
999     // More update but no more callback should happen.
1000     ASSERT_TRUE(DeleteFile(test_file()));
1001     base::RunLoop().RunUntilIdle();
1002   }
1003 }
1004 
1005 // Similar to InotifyLimitInUpdate but test a recursive watcher.
TEST_F(FilePathWatcherTest,InotifyLimitInUpdateRecursive)1006 TEST_F(FilePathWatcherTest, InotifyLimitInUpdateRecursive) {
1007   enum kTestType {
1008     // Destroy watcher in "error=true" callback.
1009     // No crash/deadlock when releasing watcher in the callback.
1010     kDestroyWatcher,
1011 
1012     // Do not destroy watcher in "error=true" callback.
1013     kDoNothing,
1014   };
1015 
1016   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
1017 
1018   for (auto callback_type : {kDestroyWatcher, kDoNothing}) {
1019     SCOPED_TRACE(testing::Message() << "type=" << callback_type);
1020 
1021     base::RunLoop run_loop;
1022     auto watcher = std::make_unique<FilePathWatcher>();
1023 
1024     bool error_callback_called = false;
1025     auto watcher_callback =
1026         base::BindLambdaForTesting([&](const FilePath& path, bool error) {
1027           // No callback should happen after "error=true" one.
1028           ASSERT_FALSE(error_callback_called);
1029 
1030           if (!error)
1031             return;
1032 
1033           error_callback_called = true;
1034 
1035           if (callback_type == kDestroyWatcher)
1036             watcher.reset();
1037 
1038           run_loop.Quit();
1039         });
1040     ASSERT_TRUE(watcher->Watch(dir, FilePathWatcher::Type::kRecursive,
1041                                watcher_callback));
1042 
1043     constexpr size_t kMaxLimit = 10u;
1044     ScopedMaxNumberOfInotifyWatchesOverrideForTest max_inotify_watches(
1045         kMaxLimit);
1046 
1047     // Triggers updates and over limit.
1048     for (size_t i = 0; i < kMaxLimit; ++i) {
1049       base::FilePath subdir =
1050           dir.AppendASCII(base::StringPrintf("subdir_%" PRIuS, i));
1051       ASSERT_TRUE(CreateDirectory(subdir));
1052     }
1053 
1054     run_loop.Run();
1055 
1056     // More update but no more callback should happen.
1057     for (size_t i = 0; i < kMaxLimit; ++i) {
1058       base::FilePath subdir =
1059           dir.AppendASCII(base::StringPrintf("subdir_%" PRIuS, i));
1060       ASSERT_TRUE(DeleteFile(subdir));
1061     }
1062     base::RunLoop().RunUntilIdle();
1063   }
1064 }
1065 
1066 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
1067 
1068 // TODO(fxbug.dev/60109): enable BUILDFLAG(IS_FUCHSIA) when implemented.
1069 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
1070 
TEST_F(FilePathWatcherTest,ReturnFullPath_RecursiveInRootFolder)1071 TEST_F(FilePathWatcherTest, ReturnFullPath_RecursiveInRootFolder) {
1072   FilePathWatcher directory_watcher;
1073   FilePath watched_folder(temp_dir_.GetPath().AppendASCII("watched_folder"));
1074   FilePath file(watched_folder.AppendASCII("file"));
1075 
1076   ASSERT_TRUE(CreateDirectory(watched_folder));
1077 
1078   auto delegate = std::make_unique<TestDelegate>(collector());
1079   ASSERT_TRUE(
1080       SetupWatchWithOptions(watched_folder, &directory_watcher, delegate.get(),
1081                             {.type = base::FilePathWatcher::Type::kRecursive,
1082                              .report_modified_path = true}));
1083 
1084   // Triggers three events:
1085   // create on /watched_folder/file
1086   // modify on /watched_folder/file.
1087   ASSERT_TRUE(WriteFile(file, "test"));
1088   ASSERT_TRUE(WaitForEvent());
1089   ASSERT_TRUE(WaitForEvent());
1090 
1091   // Expects modify on /watched_folder/file.
1092   ASSERT_TRUE(WriteFile(file, "test123"));
1093   ASSERT_TRUE(WaitForEvent());
1094 
1095   // Expects delete on /watched_folder/file.
1096   ASSERT_TRUE(DeleteFile(file));
1097   ASSERT_TRUE(WaitForEvent());
1098 
1099   std::vector<base::FilePath> expected_paths{file, file, file, file};
1100   EXPECT_EQ(delegate->get_observed_paths(), expected_paths);
1101 }
1102 
TEST_F(FilePathWatcherTest,ReturnFullPath_RecursiveInNestedFolder)1103 TEST_F(FilePathWatcherTest, ReturnFullPath_RecursiveInNestedFolder) {
1104   FilePathWatcher directory_watcher;
1105   FilePath watched_folder(temp_dir_.GetPath().AppendASCII("watched_folder"));
1106   FilePath subfolder(watched_folder.AppendASCII("subfolder"));
1107   FilePath file(subfolder.AppendASCII("file"));
1108 
1109   ASSERT_TRUE(CreateDirectory(watched_folder));
1110 
1111   auto delegate = std::make_unique<TestDelegate>(collector());
1112   ASSERT_TRUE(
1113       SetupWatchWithOptions(watched_folder, &directory_watcher, delegate.get(),
1114                             {.type = base::FilePathWatcher::Type::kRecursive,
1115                              .report_modified_path = true}));
1116 
1117   // Expects create on /watched_folder/subfolder.
1118   ASSERT_TRUE(CreateDirectory(subfolder));
1119   ASSERT_TRUE(WaitForEvent());
1120 
1121   // Triggers two events:
1122   // create on /watched_folder/subfolder/file.
1123   // modify on /watched_folder/subfolder/file.
1124   ASSERT_TRUE(WriteFile(file, "test"));
1125   ASSERT_TRUE(WaitForEvent());
1126   ASSERT_TRUE(WaitForEvent());
1127 
1128   // Expects modify on /watched_folder/subfolder/file.
1129   ASSERT_TRUE(WriteFile(file, "test123"));
1130   ASSERT_TRUE(WaitForEvent());
1131 
1132   // Expects delete on /watched_folder/subfolder/file.
1133   ASSERT_TRUE(DeleteFile(file));
1134   ASSERT_TRUE(WaitForEvent());
1135 
1136   // Expects delete on /watched_folder/subfolder.
1137   ASSERT_TRUE(DeleteFile(subfolder));
1138   ASSERT_TRUE(WaitForEvent());
1139 
1140   std::vector<base::FilePath> expected_paths{subfolder, file, file,
1141                                              file,      file, subfolder};
1142   EXPECT_EQ(delegate->get_observed_paths(), expected_paths);
1143 }
1144 
TEST_F(FilePathWatcherTest,ReturnFullPath_NonRecursiveInRootFolder)1145 TEST_F(FilePathWatcherTest, ReturnFullPath_NonRecursiveInRootFolder) {
1146   FilePathWatcher directory_watcher;
1147   FilePath watched_folder(temp_dir_.GetPath().AppendASCII("watched_folder"));
1148   FilePath file(watched_folder.AppendASCII("file"));
1149 
1150   ASSERT_TRUE(base::CreateDirectory(watched_folder));
1151 
1152   auto delegate = std::make_unique<TestDelegate>(collector());
1153   ASSERT_TRUE(
1154       SetupWatchWithOptions(watched_folder, &directory_watcher, delegate.get(),
1155                             {.type = base::FilePathWatcher::Type::kNonRecursive,
1156                              .report_modified_path = true}));
1157 
1158   // Triggers three events:
1159   // create on /watched_folder/file.
1160   // modify on /watched_folder/file.
1161   ASSERT_TRUE(WriteFile(file, "test"));
1162   ASSERT_TRUE(WaitForEvent());
1163   ASSERT_TRUE(WaitForEvent());
1164 
1165   // Expects modify on /watched_folder/file.
1166   ASSERT_TRUE(WriteFile(file, "test123"));
1167   ASSERT_TRUE(WaitForEvent());
1168 
1169   // Expects delete on /watched_folder/file.
1170   ASSERT_TRUE(DeleteFile(file));
1171   ASSERT_TRUE(WaitForEvent());
1172 
1173   std::vector<base::FilePath> expected_paths{file, file, file, file};
1174   EXPECT_EQ(delegate->get_observed_paths(), expected_paths);
1175 }
1176 
TEST_F(FilePathWatcherTest,ReturnFullPath_NonRecursiveRemoveEnclosingFolder)1177 TEST_F(FilePathWatcherTest, ReturnFullPath_NonRecursiveRemoveEnclosingFolder) {
1178   FilePathWatcher directory_watcher;
1179   FilePath root_folder(temp_dir_.GetPath().AppendASCII("root_folder"));
1180   FilePath folder(root_folder.AppendASCII("folder"));
1181   FilePath watched_folder(folder.AppendASCII("watched_folder"));
1182   FilePath file(watched_folder.AppendASCII("file"));
1183 
1184   ASSERT_TRUE(base::CreateDirectory(watched_folder));
1185   ASSERT_TRUE(WriteFile(file, "test"));
1186 
1187   auto delegate = std::make_unique<TestDelegate>(collector());
1188   ASSERT_TRUE(
1189       SetupWatchWithOptions(watched_folder, &directory_watcher, delegate.get(),
1190                             {.type = base::FilePathWatcher::Type::kNonRecursive,
1191                              .report_modified_path = true}));
1192 
1193   // Triggers four events:
1194   // delete on /watched_folder/file.
1195   // delete on /watched_folder twice.
1196   ASSERT_TRUE(DeletePathRecursively(folder));
1197   ASSERT_TRUE(WaitForEvent());
1198   ASSERT_TRUE(WaitForEvent());
1199   ASSERT_TRUE(WaitForEvent());
1200 
1201   std::vector<base::FilePath> expected_paths{file, watched_folder,
1202                                              watched_folder};
1203   EXPECT_EQ(delegate->get_observed_paths(), expected_paths);
1204 }
1205 
TEST_F(FilePathWatcherTest,ReturnWatchedPath_RecursiveInRootFolder)1206 TEST_F(FilePathWatcherTest, ReturnWatchedPath_RecursiveInRootFolder) {
1207   FilePathWatcher directory_watcher;
1208   FilePath watched_folder(temp_dir_.GetPath().AppendASCII("watched_folder"));
1209   FilePath file(watched_folder.AppendASCII("file"));
1210 
1211   ASSERT_TRUE(base::CreateDirectory(watched_folder));
1212 
1213   auto delegate = std::make_unique<TestDelegate>(collector());
1214   ASSERT_TRUE(
1215       SetupWatchWithOptions(watched_folder, &directory_watcher, delegate.get(),
1216                             {.type = base::FilePathWatcher::Type::kRecursive}));
1217 
1218   // Triggers three events:
1219   // create on /watched_folder.
1220   // modify on /watched_folder.
1221   ASSERT_TRUE(WriteFile(file, "test"));
1222   ASSERT_TRUE(WaitForEvent());
1223   ASSERT_TRUE(WaitForEvent());
1224 
1225   // Expects modify on /watched_folder.
1226   ASSERT_TRUE(WriteFile(file, "test123"));
1227   ASSERT_TRUE(WaitForEvent());
1228 
1229   // Expects delete on /watched_folder.
1230   ASSERT_TRUE(DeleteFile(file));
1231   ASSERT_TRUE(WaitForEvent());
1232 
1233   std::vector<base::FilePath> expected_paths{watched_folder, watched_folder,
1234                                              watched_folder, watched_folder};
1235   EXPECT_EQ(delegate->get_observed_paths(), expected_paths);
1236 }
1237 
TEST_F(FilePathWatcherTest,ReturnWatchedPath_NonRecursiveInRootFolder)1238 TEST_F(FilePathWatcherTest, ReturnWatchedPath_NonRecursiveInRootFolder) {
1239   FilePathWatcher directory_watcher;
1240   FilePath watched_folder(temp_dir_.GetPath().AppendASCII("watched_folder"));
1241   FilePath file(watched_folder.AppendASCII("file"));
1242 
1243   ASSERT_TRUE(base::CreateDirectory(watched_folder));
1244 
1245   auto delegate = std::make_unique<TestDelegate>(collector());
1246   ASSERT_TRUE(SetupWatchWithOptions(
1247       watched_folder, &directory_watcher, delegate.get(),
1248       {.type = base::FilePathWatcher::Type::kNonRecursive}));
1249 
1250   // Triggers three events:
1251   // Expects create /watched_folder.
1252   // Expects modify /watched_folder.
1253   ASSERT_TRUE(WriteFile(file, "test"));
1254   ASSERT_TRUE(WaitForEvent());
1255   ASSERT_TRUE(WaitForEvent());
1256 
1257   // Expects modify on /watched_folder.
1258   ASSERT_TRUE(WriteFile(file, "test123"));
1259   ASSERT_TRUE(WaitForEvent());
1260 
1261   // Expects delete on /watched_folder.
1262   ASSERT_TRUE(DeleteFile(file));
1263   ASSERT_TRUE(WaitForEvent());
1264 
1265   std::vector<base::FilePath> expected_paths {watched_folder, watched_folder,
1266                               watched_folder, watched_folder};
1267   EXPECT_EQ(delegate->get_observed_paths(), expected_paths);
1268 }
1269 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
1270         // BUILDFLAG(IS_ANDROID)
1271 
1272 enum Permission {
1273   Read,
1274   Write,
1275   Execute
1276 };
1277 
1278 #if BUILDFLAG(IS_APPLE)
ChangeFilePermissions(const FilePath & path,Permission perm,bool allow)1279 bool ChangeFilePermissions(const FilePath& path, Permission perm, bool allow) {
1280   struct stat stat_buf;
1281 
1282   if (stat(path.value().c_str(), &stat_buf) != 0)
1283     return false;
1284 
1285   mode_t mode = 0;
1286   switch (perm) {
1287     case Read:
1288       mode = S_IRUSR | S_IRGRP | S_IROTH;
1289       break;
1290     case Write:
1291       mode = S_IWUSR | S_IWGRP | S_IWOTH;
1292       break;
1293     case Execute:
1294       mode = S_IXUSR | S_IXGRP | S_IXOTH;
1295       break;
1296     default:
1297       ADD_FAILURE() << "unknown perm " << perm;
1298       return false;
1299   }
1300   if (allow) {
1301     stat_buf.st_mode |= mode;
1302   } else {
1303     stat_buf.st_mode &= ~mode;
1304   }
1305   return chmod(path.value().c_str(), stat_buf.st_mode) == 0;
1306 }
1307 #endif  // BUILDFLAG(IS_APPLE)
1308 
1309 #if BUILDFLAG(IS_APPLE)
1310 // Linux implementation of FilePathWatcher doesn't catch attribute changes.
1311 // http://crbug.com/78043
1312 // Windows implementation of FilePathWatcher catches attribute changes that
1313 // don't affect the path being watched.
1314 // http://crbug.com/78045
1315 
1316 // Verify that changing attributes on a directory works.
TEST_F(FilePathWatcherTest,DirAttributesChanged)1317 TEST_F(FilePathWatcherTest, DirAttributesChanged) {
1318   FilePath test_dir1(
1319       temp_dir_.GetPath().AppendASCII("DirAttributesChangedDir1"));
1320   FilePath test_dir2(test_dir1.AppendASCII("DirAttributesChangedDir2"));
1321   FilePath test_file(test_dir2.AppendASCII("DirAttributesChangedFile"));
1322   // Setup a directory hierarchy.
1323   ASSERT_TRUE(base::CreateDirectory(test_dir1));
1324   ASSERT_TRUE(base::CreateDirectory(test_dir2));
1325   ASSERT_TRUE(WriteFile(test_file, "content"));
1326 
1327   FilePathWatcher watcher;
1328   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
1329   ASSERT_TRUE(SetupWatch(test_file, &watcher, delegate.get(),
1330                          FilePathWatcher::Type::kNonRecursive));
1331 
1332   // We should not get notified in this case as it hasn't affected our ability
1333   // to access the file.
1334   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, false));
1335   ASSERT_FALSE(WaitForEventWithTimeout(TestTimeouts::tiny_timeout()));
1336   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, true));
1337 
1338   // We should get notified in this case because filepathwatcher can no
1339   // longer access the file
1340   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, false));
1341   ASSERT_TRUE(WaitForEvent());
1342   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, true));
1343 }
1344 
1345 #endif  // BUILDFLAG(IS_APPLE)
1346 
1347 #if BUILDFLAG(IS_APPLE)
1348 
1349 // Fail fast if trying to trivially watch a non-existent item.
TEST_F(FilePathWatcherTest,TrivialNoDir)1350 TEST_F(FilePathWatcherTest, TrivialNoDir) {
1351   const FilePath tmp_dir = temp_dir_.GetPath();
1352   const FilePath non_existent = tmp_dir.Append(FILE_PATH_LITERAL("nope"));
1353 
1354   FilePathWatcher watcher;
1355   auto delegate = std::make_unique<TestDelegate>(collector());
1356   ASSERT_FALSE(SetupWatch(non_existent, &watcher, delegate.get(),
1357                           FilePathWatcher::Type::kTrivial));
1358 }
1359 
1360 // Succeed starting a watch on a directory.
TEST_F(FilePathWatcherTest,TrivialDirStart)1361 TEST_F(FilePathWatcherTest, TrivialDirStart) {
1362   const FilePath tmp_dir = temp_dir_.GetPath();
1363 
1364   FilePathWatcher watcher;
1365   auto delegate = std::make_unique<TestDelegate>(collector());
1366   ASSERT_TRUE(SetupWatch(tmp_dir, &watcher, delegate.get(),
1367                          FilePathWatcher::Type::kTrivial));
1368 }
1369 
1370 // Observe a change on a directory
TEST_F(FilePathWatcherTest,TrivialDirChange)1371 TEST_F(FilePathWatcherTest, TrivialDirChange) {
1372   const FilePath tmp_dir = temp_dir_.GetPath();
1373 
1374   FilePathWatcher watcher;
1375   auto delegate = std::make_unique<TestDelegate>(collector());
1376   ASSERT_TRUE(SetupWatch(tmp_dir, &watcher, delegate.get(),
1377                          FilePathWatcher::Type::kTrivial));
1378 
1379   ASSERT_TRUE(TouchFile(tmp_dir, base::Time::Now(), base::Time::Now()));
1380   ASSERT_TRUE(WaitForEvent());
1381 }
1382 
1383 // Observe no change when a parent is modified.
TEST_F(FilePathWatcherTest,TrivialParentDirChange)1384 TEST_F(FilePathWatcherTest, TrivialParentDirChange) {
1385   const FilePath tmp_dir = temp_dir_.GetPath();
1386   const FilePath sub_dir1 = tmp_dir.Append(FILE_PATH_LITERAL("subdir"));
1387   const FilePath sub_dir2 = sub_dir1.Append(FILE_PATH_LITERAL("subdir_redux"));
1388 
1389   ASSERT_TRUE(CreateDirectory(sub_dir1));
1390   ASSERT_TRUE(CreateDirectory(sub_dir2));
1391 
1392   FilePathWatcher watcher;
1393   auto delegate = std::make_unique<TestDelegate>(collector());
1394   ASSERT_TRUE(SetupWatch(sub_dir2, &watcher, delegate.get(),
1395                          FilePathWatcher::Type::kTrivial));
1396 
1397   // There should be no notification for a change to |sub_dir2|'s parent.
1398   ASSERT_TRUE(Move(sub_dir1, tmp_dir.Append(FILE_PATH_LITERAL("over_here"))));
1399   ASSERT_FALSE(WaitForEventWithTimeout(TestTimeouts::tiny_timeout()));
1400 }
1401 
1402 // Do not crash when a directory is moved; https://crbug.com/1156603.
TEST_F(FilePathWatcherTest,TrivialDirMove)1403 TEST_F(FilePathWatcherTest, TrivialDirMove) {
1404   const FilePath tmp_dir = temp_dir_.GetPath();
1405   const FilePath sub_dir = tmp_dir.Append(FILE_PATH_LITERAL("subdir"));
1406 
1407   ASSERT_TRUE(CreateDirectory(sub_dir));
1408 
1409   FilePathWatcher watcher;
1410   auto delegate = std::make_unique<TestDelegate>(collector());
1411   delegate->set_expect_error();
1412   ASSERT_TRUE(SetupWatch(sub_dir, &watcher, delegate.get(),
1413                          FilePathWatcher::Type::kTrivial));
1414 
1415   ASSERT_TRUE(Move(sub_dir, tmp_dir.Append(FILE_PATH_LITERAL("over_here"))));
1416   ASSERT_TRUE(WaitForEvent());
1417 }
1418 
1419 #endif  // BUILDFLAG(IS_APPLE)
1420 
1421 }  // namespace
1422 
1423 }  // namespace base
1424