• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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 #if defined(OS_WIN)
8 #include <windows.h>
9 #include <aclapi.h>
10 #elif defined(OS_POSIX)
11 #include <sys/stat.h>
12 #endif
13 
14 #include <set>
15 
16 #include "base/bind.h"
17 #include "base/bind_helpers.h"
18 #include "base/compiler_specific.h"
19 #include "base/files/file_path.h"
20 #include "base/files/file_util.h"
21 #include "base/files/scoped_temp_dir.h"
22 #include "base/location.h"
23 #include "base/macros.h"
24 #include "base/run_loop.h"
25 #include "base/single_thread_task_runner.h"
26 #include "base/stl_util.h"
27 #include "base/strings/stringprintf.h"
28 #include "base/synchronization/waitable_event.h"
29 #include "base/test/test_file_util.h"
30 #include "base/test/test_timeouts.h"
31 #include "base/threading/thread_task_runner_handle.h"
32 #include "build/build_config.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34 
35 #if defined(OS_ANDROID)
36 #include "base/android/path_utils.h"
37 #endif  // defined(OS_ANDROID)
38 
39 #if defined(OS_POSIX)
40 #include "base/files/file_descriptor_watcher_posix.h"
41 #endif  // defined(OS_POSIX)
42 
43 namespace base {
44 
45 namespace {
46 
47 class TestDelegate;
48 
49 // Aggregates notifications from the test delegates and breaks the message loop
50 // the test thread is waiting on once they all came in.
51 class NotificationCollector
52     : public base::RefCountedThreadSafe<NotificationCollector> {
53  public:
NotificationCollector()54   NotificationCollector() : task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
55 
56   // Called from the file thread by the delegates.
OnChange(TestDelegate * delegate)57   void OnChange(TestDelegate* delegate) {
58     task_runner_->PostTask(
59         FROM_HERE, base::Bind(&NotificationCollector::RecordChange, this,
60                               base::Unretained(delegate)));
61   }
62 
Register(TestDelegate * delegate)63   void Register(TestDelegate* delegate) {
64     delegates_.insert(delegate);
65   }
66 
Reset()67   void Reset() {
68     signaled_.clear();
69   }
70 
Success()71   bool Success() {
72     return signaled_ == delegates_;
73   }
74 
75  private:
76   friend class base::RefCountedThreadSafe<NotificationCollector>;
~NotificationCollector()77   ~NotificationCollector() {}
78 
RecordChange(TestDelegate * delegate)79   void RecordChange(TestDelegate* delegate) {
80     // Warning: |delegate| is Unretained. Do not dereference.
81     ASSERT_TRUE(task_runner_->BelongsToCurrentThread());
82     ASSERT_TRUE(delegates_.count(delegate));
83     signaled_.insert(delegate);
84 
85     // Check whether all delegates have been signaled.
86     if (signaled_ == delegates_)
87       task_runner_->PostTask(FROM_HERE, MessageLoop::QuitWhenIdleClosure());
88   }
89 
90   // Set of registered delegates.
91   std::set<TestDelegate*> delegates_;
92 
93   // Set of signaled delegates.
94   std::set<TestDelegate*> signaled_;
95 
96   // The loop we should break after all delegates signaled.
97   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
98 };
99 
100 class TestDelegateBase : public SupportsWeakPtr<TestDelegateBase> {
101  public:
TestDelegateBase()102   TestDelegateBase() {}
~TestDelegateBase()103   virtual ~TestDelegateBase() {}
104 
105   virtual void OnFileChanged(const FilePath& path, bool error) = 0;
106 
107  private:
108   DISALLOW_COPY_AND_ASSIGN(TestDelegateBase);
109 };
110 
111 // A mock class for testing. Gmock is not appropriate because it is not
112 // thread-safe for setting expectations. Thus the test code cannot safely
113 // reset expectations while the file watcher is running.
114 // Instead, TestDelegate gets the notifications from FilePathWatcher and uses
115 // NotificationCollector to aggregate the results.
116 class TestDelegate : public TestDelegateBase {
117  public:
TestDelegate(NotificationCollector * collector)118   explicit TestDelegate(NotificationCollector* collector)
119       : collector_(collector) {
120     collector_->Register(this);
121   }
~TestDelegate()122   ~TestDelegate() override {}
123 
OnFileChanged(const FilePath & path,bool error)124   void OnFileChanged(const FilePath& path, bool error) override {
125     if (error)
126       ADD_FAILURE() << "Error " << path.value();
127     else
128       collector_->OnChange(this);
129   }
130 
131  private:
132   scoped_refptr<NotificationCollector> collector_;
133 
134   DISALLOW_COPY_AND_ASSIGN(TestDelegate);
135 };
136 
137 class FilePathWatcherTest : public testing::Test {
138  public:
FilePathWatcherTest()139   FilePathWatcherTest()
140 #if defined(OS_POSIX)
141       : file_descriptor_watcher_(&loop_)
142 #endif
143   {
144   }
145 
~FilePathWatcherTest()146   ~FilePathWatcherTest() override {}
147 
148  protected:
SetUp()149   void SetUp() override {
150 #if defined(OS_ANDROID)
151     // Watching files is only permitted when all parent directories are
152     // accessible, which is not the case for the default temp directory
153     // on Android which is under /data/data.  Use /sdcard instead.
154     // TODO(pauljensen): Remove this when crbug.com/475568 is fixed.
155     FilePath parent_dir;
156     ASSERT_TRUE(android::GetExternalStorageDirectory(&parent_dir));
157     ASSERT_TRUE(temp_dir_.CreateUniqueTempDirUnderPath(parent_dir));
158 #else   // defined(OS_ANDROID)
159     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
160 #endif  // defined(OS_ANDROID)
161     collector_ = new NotificationCollector();
162   }
163 
TearDown()164   void TearDown() override { RunLoop().RunUntilIdle(); }
165 
test_file()166   FilePath test_file() {
167     return temp_dir_.GetPath().AppendASCII("FilePathWatcherTest");
168   }
169 
test_link()170   FilePath test_link() {
171     return temp_dir_.GetPath().AppendASCII("FilePathWatcherTest.lnk");
172   }
173 
174   // Write |content| to |file|. Returns true on success.
WriteFile(const FilePath & file,const std::string & content)175   bool WriteFile(const FilePath& file, const std::string& content) {
176     int write_size = ::base::WriteFile(file, content.c_str(), content.length());
177     return write_size == static_cast<int>(content.length());
178   }
179 
180   bool SetupWatch(const FilePath& target,
181                   FilePathWatcher* watcher,
182                   TestDelegateBase* delegate,
183                   bool recursive_watch) WARN_UNUSED_RESULT;
184 
WaitForEvents()185   bool WaitForEvents() WARN_UNUSED_RESULT {
186     collector_->Reset();
187 
188     RunLoop run_loop;
189     // Make sure we timeout if we don't get notified.
190     ThreadTaskRunnerHandle::Get()->PostDelayedTask(
191         FROM_HERE, run_loop.QuitWhenIdleClosure(),
192         TestTimeouts::action_timeout());
193     run_loop.Run();
194     return collector_->Success();
195   }
196 
collector()197   NotificationCollector* collector() { return collector_.get(); }
198 
199   MessageLoopForIO loop_;
200 #if defined(OS_POSIX)
201   FileDescriptorWatcher file_descriptor_watcher_;
202 #endif
203 
204   ScopedTempDir temp_dir_;
205   scoped_refptr<NotificationCollector> collector_;
206 
207  private:
208   DISALLOW_COPY_AND_ASSIGN(FilePathWatcherTest);
209 };
210 
SetupWatch(const FilePath & target,FilePathWatcher * watcher,TestDelegateBase * delegate,bool recursive_watch)211 bool FilePathWatcherTest::SetupWatch(const FilePath& target,
212                                      FilePathWatcher* watcher,
213                                      TestDelegateBase* delegate,
214                                      bool recursive_watch) {
215   return watcher->Watch(
216       target, recursive_watch,
217       base::Bind(&TestDelegateBase::OnFileChanged, delegate->AsWeakPtr()));
218 }
219 
220 // Basic test: Create the file and verify that we notice.
TEST_F(FilePathWatcherTest,NewFile)221 TEST_F(FilePathWatcherTest, NewFile) {
222   FilePathWatcher watcher;
223   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
224   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
225 
226   ASSERT_TRUE(WriteFile(test_file(), "content"));
227   ASSERT_TRUE(WaitForEvents());
228 }
229 
230 // Verify that modifying the file is caught.
TEST_F(FilePathWatcherTest,ModifiedFile)231 TEST_F(FilePathWatcherTest, ModifiedFile) {
232   ASSERT_TRUE(WriteFile(test_file(), "content"));
233 
234   FilePathWatcher watcher;
235   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
236   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
237 
238   // Now make sure we get notified if the file is modified.
239   ASSERT_TRUE(WriteFile(test_file(), "new content"));
240   ASSERT_TRUE(WaitForEvents());
241 }
242 
243 // Verify that moving the file into place is caught.
TEST_F(FilePathWatcherTest,MovedFile)244 TEST_F(FilePathWatcherTest, MovedFile) {
245   FilePath source_file(temp_dir_.GetPath().AppendASCII("source"));
246   ASSERT_TRUE(WriteFile(source_file, "content"));
247 
248   FilePathWatcher watcher;
249   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
250   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
251 
252   // Now make sure we get notified if the file is modified.
253   ASSERT_TRUE(base::Move(source_file, test_file()));
254   ASSERT_TRUE(WaitForEvents());
255 }
256 
TEST_F(FilePathWatcherTest,DeletedFile)257 TEST_F(FilePathWatcherTest, DeletedFile) {
258   ASSERT_TRUE(WriteFile(test_file(), "content"));
259 
260   FilePathWatcher watcher;
261   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
262   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
263 
264   // Now make sure we get notified if the file is deleted.
265   base::DeleteFile(test_file(), false);
266   ASSERT_TRUE(WaitForEvents());
267 }
268 
269 // Used by the DeleteDuringNotify test below.
270 // Deletes the FilePathWatcher when it's notified.
271 class Deleter : public TestDelegateBase {
272  public:
Deleter(FilePathWatcher * watcher,MessageLoop * loop)273   Deleter(FilePathWatcher* watcher, MessageLoop* loop)
274       : watcher_(watcher),
275         loop_(loop) {
276   }
~Deleter()277   ~Deleter() override {}
278 
OnFileChanged(const FilePath &,bool)279   void OnFileChanged(const FilePath&, bool) override {
280     watcher_.reset();
281     loop_->task_runner()->PostTask(FROM_HERE,
282                                    MessageLoop::QuitWhenIdleClosure());
283   }
284 
watcher() const285   FilePathWatcher* watcher() const { return watcher_.get(); }
286 
287  private:
288   std::unique_ptr<FilePathWatcher> watcher_;
289   MessageLoop* loop_;
290 
291   DISALLOW_COPY_AND_ASSIGN(Deleter);
292 };
293 
294 // Verify that deleting a watcher during the callback doesn't crash.
TEST_F(FilePathWatcherTest,DeleteDuringNotify)295 TEST_F(FilePathWatcherTest, DeleteDuringNotify) {
296   FilePathWatcher* watcher = new FilePathWatcher;
297   // Takes ownership of watcher.
298   std::unique_ptr<Deleter> deleter(new Deleter(watcher, &loop_));
299   ASSERT_TRUE(SetupWatch(test_file(), watcher, deleter.get(), false));
300 
301   ASSERT_TRUE(WriteFile(test_file(), "content"));
302   ASSERT_TRUE(WaitForEvents());
303 
304   // We win if we haven't crashed yet.
305   // Might as well double-check it got deleted, too.
306   ASSERT_TRUE(deleter->watcher() == NULL);
307 }
308 
309 // Verify that deleting the watcher works even if there is a pending
310 // notification.
311 // Flaky on MacOS (and ARM linux): http://crbug.com/85930
TEST_F(FilePathWatcherTest,DISABLED_DestroyWithPendingNotification)312 TEST_F(FilePathWatcherTest, DISABLED_DestroyWithPendingNotification) {
313   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
314   FilePathWatcher watcher;
315   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
316   ASSERT_TRUE(WriteFile(test_file(), "content"));
317 }
318 
TEST_F(FilePathWatcherTest,MultipleWatchersSingleFile)319 TEST_F(FilePathWatcherTest, MultipleWatchersSingleFile) {
320   FilePathWatcher watcher1, watcher2;
321   std::unique_ptr<TestDelegate> delegate1(new TestDelegate(collector()));
322   std::unique_ptr<TestDelegate> delegate2(new TestDelegate(collector()));
323   ASSERT_TRUE(SetupWatch(test_file(), &watcher1, delegate1.get(), false));
324   ASSERT_TRUE(SetupWatch(test_file(), &watcher2, delegate2.get(), false));
325 
326   ASSERT_TRUE(WriteFile(test_file(), "content"));
327   ASSERT_TRUE(WaitForEvents());
328 }
329 
330 // Verify that watching a file whose parent directory doesn't exist yet works if
331 // the directory and file are created eventually.
TEST_F(FilePathWatcherTest,NonExistentDirectory)332 TEST_F(FilePathWatcherTest, NonExistentDirectory) {
333   FilePathWatcher watcher;
334   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
335   FilePath file(dir.AppendASCII("file"));
336   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
337   ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
338 
339   ASSERT_TRUE(base::CreateDirectory(dir));
340 
341   ASSERT_TRUE(WriteFile(file, "content"));
342 
343   VLOG(1) << "Waiting for file creation";
344   ASSERT_TRUE(WaitForEvents());
345 
346   ASSERT_TRUE(WriteFile(file, "content v2"));
347   VLOG(1) << "Waiting for file change";
348   ASSERT_TRUE(WaitForEvents());
349 
350   ASSERT_TRUE(base::DeleteFile(file, false));
351   VLOG(1) << "Waiting for file deletion";
352   ASSERT_TRUE(WaitForEvents());
353 }
354 
355 // Exercises watch reconfiguration for the case that directories on the path
356 // are rapidly created.
TEST_F(FilePathWatcherTest,DirectoryChain)357 TEST_F(FilePathWatcherTest, DirectoryChain) {
358   FilePath path(temp_dir_.GetPath());
359   std::vector<std::string> dir_names;
360   for (int i = 0; i < 20; i++) {
361     std::string dir(base::StringPrintf("d%d", i));
362     dir_names.push_back(dir);
363     path = path.AppendASCII(dir);
364   }
365 
366   FilePathWatcher watcher;
367   FilePath file(path.AppendASCII("file"));
368   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
369   ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
370 
371   FilePath sub_path(temp_dir_.GetPath());
372   for (std::vector<std::string>::const_iterator d(dir_names.begin());
373        d != dir_names.end(); ++d) {
374     sub_path = sub_path.AppendASCII(*d);
375     ASSERT_TRUE(base::CreateDirectory(sub_path));
376   }
377   VLOG(1) << "Create File";
378   ASSERT_TRUE(WriteFile(file, "content"));
379   VLOG(1) << "Waiting for file creation";
380   ASSERT_TRUE(WaitForEvents());
381 
382   ASSERT_TRUE(WriteFile(file, "content v2"));
383   VLOG(1) << "Waiting for file modification";
384   ASSERT_TRUE(WaitForEvents());
385 }
386 
387 #if defined(OS_MACOSX)
388 // http://crbug.com/85930
389 #define DisappearingDirectory DISABLED_DisappearingDirectory
390 #endif
TEST_F(FilePathWatcherTest,DisappearingDirectory)391 TEST_F(FilePathWatcherTest, DisappearingDirectory) {
392   FilePathWatcher watcher;
393   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
394   FilePath file(dir.AppendASCII("file"));
395   ASSERT_TRUE(base::CreateDirectory(dir));
396   ASSERT_TRUE(WriteFile(file, "content"));
397   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
398   ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
399 
400   ASSERT_TRUE(base::DeleteFile(dir, true));
401   ASSERT_TRUE(WaitForEvents());
402 }
403 
404 // Tests that a file that is deleted and reappears is tracked correctly.
TEST_F(FilePathWatcherTest,DeleteAndRecreate)405 TEST_F(FilePathWatcherTest, DeleteAndRecreate) {
406   ASSERT_TRUE(WriteFile(test_file(), "content"));
407   FilePathWatcher watcher;
408   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
409   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
410 
411   ASSERT_TRUE(base::DeleteFile(test_file(), false));
412   VLOG(1) << "Waiting for file deletion";
413   ASSERT_TRUE(WaitForEvents());
414 
415   ASSERT_TRUE(WriteFile(test_file(), "content"));
416   VLOG(1) << "Waiting for file creation";
417   ASSERT_TRUE(WaitForEvents());
418 }
419 
TEST_F(FilePathWatcherTest,WatchDirectory)420 TEST_F(FilePathWatcherTest, WatchDirectory) {
421   FilePathWatcher watcher;
422   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
423   FilePath file1(dir.AppendASCII("file1"));
424   FilePath file2(dir.AppendASCII("file2"));
425   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
426   ASSERT_TRUE(SetupWatch(dir, &watcher, delegate.get(), false));
427 
428   ASSERT_TRUE(base::CreateDirectory(dir));
429   VLOG(1) << "Waiting for directory creation";
430   ASSERT_TRUE(WaitForEvents());
431 
432   ASSERT_TRUE(WriteFile(file1, "content"));
433   VLOG(1) << "Waiting for file1 creation";
434   ASSERT_TRUE(WaitForEvents());
435 
436 #if !defined(OS_MACOSX)
437   // Mac implementation does not detect files modified in a directory.
438   ASSERT_TRUE(WriteFile(file1, "content v2"));
439   VLOG(1) << "Waiting for file1 modification";
440   ASSERT_TRUE(WaitForEvents());
441 #endif  // !OS_MACOSX
442 
443   ASSERT_TRUE(base::DeleteFile(file1, false));
444   VLOG(1) << "Waiting for file1 deletion";
445   ASSERT_TRUE(WaitForEvents());
446 
447   ASSERT_TRUE(WriteFile(file2, "content"));
448   VLOG(1) << "Waiting for file2 creation";
449   ASSERT_TRUE(WaitForEvents());
450 }
451 
TEST_F(FilePathWatcherTest,MoveParent)452 TEST_F(FilePathWatcherTest, MoveParent) {
453   FilePathWatcher file_watcher;
454   FilePathWatcher subdir_watcher;
455   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
456   FilePath dest(temp_dir_.GetPath().AppendASCII("dest"));
457   FilePath subdir(dir.AppendASCII("subdir"));
458   FilePath file(subdir.AppendASCII("file"));
459   std::unique_ptr<TestDelegate> file_delegate(new TestDelegate(collector()));
460   ASSERT_TRUE(SetupWatch(file, &file_watcher, file_delegate.get(), false));
461   std::unique_ptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
462   ASSERT_TRUE(SetupWatch(subdir, &subdir_watcher, subdir_delegate.get(),
463                          false));
464 
465   // Setup a directory hierarchy.
466   ASSERT_TRUE(base::CreateDirectory(subdir));
467   ASSERT_TRUE(WriteFile(file, "content"));
468   VLOG(1) << "Waiting for file creation";
469   ASSERT_TRUE(WaitForEvents());
470 
471   // Move the parent directory.
472   base::Move(dir, dest);
473   VLOG(1) << "Waiting for directory move";
474   ASSERT_TRUE(WaitForEvents());
475 }
476 
TEST_F(FilePathWatcherTest,RecursiveWatch)477 TEST_F(FilePathWatcherTest, RecursiveWatch) {
478   FilePathWatcher watcher;
479   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
480   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
481   bool setup_result = SetupWatch(dir, &watcher, delegate.get(), true);
482   if (!FilePathWatcher::RecursiveWatchAvailable()) {
483     ASSERT_FALSE(setup_result);
484     return;
485   }
486   ASSERT_TRUE(setup_result);
487 
488   // Main directory("dir") creation.
489   ASSERT_TRUE(base::CreateDirectory(dir));
490   ASSERT_TRUE(WaitForEvents());
491 
492   // Create "$dir/file1".
493   FilePath file1(dir.AppendASCII("file1"));
494   ASSERT_TRUE(WriteFile(file1, "content"));
495   ASSERT_TRUE(WaitForEvents());
496 
497   // Create "$dir/subdir".
498   FilePath subdir(dir.AppendASCII("subdir"));
499   ASSERT_TRUE(base::CreateDirectory(subdir));
500   ASSERT_TRUE(WaitForEvents());
501 
502   // Create "$dir/subdir/subdir_file1".
503   FilePath subdir_file1(subdir.AppendASCII("subdir_file1"));
504   ASSERT_TRUE(WriteFile(subdir_file1, "content"));
505   ASSERT_TRUE(WaitForEvents());
506 
507   // Create "$dir/subdir/subdir_child_dir".
508   FilePath subdir_child_dir(subdir.AppendASCII("subdir_child_dir"));
509   ASSERT_TRUE(base::CreateDirectory(subdir_child_dir));
510   ASSERT_TRUE(WaitForEvents());
511 
512   // Create "$dir/subdir/subdir_child_dir/child_dir_file1".
513   FilePath child_dir_file1(subdir_child_dir.AppendASCII("child_dir_file1"));
514   ASSERT_TRUE(WriteFile(child_dir_file1, "content v2"));
515   ASSERT_TRUE(WaitForEvents());
516 
517   // Write into "$dir/subdir/subdir_child_dir/child_dir_file1".
518   ASSERT_TRUE(WriteFile(child_dir_file1, "content"));
519   ASSERT_TRUE(WaitForEvents());
520 
521 // Apps cannot change file attributes on Android in /sdcard as /sdcard uses the
522 // "fuse" file system, while /data uses "ext4".  Running these tests in /data
523 // would be preferable and allow testing file attributes and symlinks.
524 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
525 // the |temp_dir_| in /data.
526 #if !defined(OS_ANDROID)
527   // Modify "$dir/subdir/subdir_child_dir/child_dir_file1" attributes.
528   ASSERT_TRUE(base::MakeFileUnreadable(child_dir_file1));
529   ASSERT_TRUE(WaitForEvents());
530 #endif
531 
532   // Delete "$dir/subdir/subdir_file1".
533   ASSERT_TRUE(base::DeleteFile(subdir_file1, false));
534   ASSERT_TRUE(WaitForEvents());
535 
536   // Delete "$dir/subdir/subdir_child_dir/child_dir_file1".
537   ASSERT_TRUE(base::DeleteFile(child_dir_file1, false));
538   ASSERT_TRUE(WaitForEvents());
539 }
540 
541 #if defined(OS_POSIX)
542 #if defined(OS_ANDROID)
543 // Apps cannot create symlinks on Android in /sdcard as /sdcard uses the
544 // "fuse" file system, while /data uses "ext4".  Running these tests in /data
545 // would be preferable and allow testing file attributes and symlinks.
546 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
547 // the |temp_dir_| in /data.
548 #define RecursiveWithSymLink DISABLED_RecursiveWithSymLink
549 #endif  // defined(OS_ANDROID)
TEST_F(FilePathWatcherTest,RecursiveWithSymLink)550 TEST_F(FilePathWatcherTest, RecursiveWithSymLink) {
551   if (!FilePathWatcher::RecursiveWatchAvailable())
552     return;
553 
554   FilePathWatcher watcher;
555   FilePath test_dir(temp_dir_.GetPath().AppendASCII("test_dir"));
556   ASSERT_TRUE(base::CreateDirectory(test_dir));
557   FilePath symlink(test_dir.AppendASCII("symlink"));
558   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
559   ASSERT_TRUE(SetupWatch(symlink, &watcher, delegate.get(), true));
560 
561   // Link creation.
562   FilePath target1(temp_dir_.GetPath().AppendASCII("target1"));
563   ASSERT_TRUE(base::CreateSymbolicLink(target1, symlink));
564   ASSERT_TRUE(WaitForEvents());
565 
566   // Target1 creation.
567   ASSERT_TRUE(base::CreateDirectory(target1));
568   ASSERT_TRUE(WaitForEvents());
569 
570   // Create a file in target1.
571   FilePath target1_file(target1.AppendASCII("file"));
572   ASSERT_TRUE(WriteFile(target1_file, "content"));
573   ASSERT_TRUE(WaitForEvents());
574 
575   // Link change.
576   FilePath target2(temp_dir_.GetPath().AppendASCII("target2"));
577   ASSERT_TRUE(base::CreateDirectory(target2));
578   ASSERT_TRUE(base::DeleteFile(symlink, false));
579   ASSERT_TRUE(base::CreateSymbolicLink(target2, symlink));
580   ASSERT_TRUE(WaitForEvents());
581 
582   // Create a file in target2.
583   FilePath target2_file(target2.AppendASCII("file"));
584   ASSERT_TRUE(WriteFile(target2_file, "content"));
585   ASSERT_TRUE(WaitForEvents());
586 }
587 #endif  // OS_POSIX
588 
TEST_F(FilePathWatcherTest,MoveChild)589 TEST_F(FilePathWatcherTest, MoveChild) {
590   FilePathWatcher file_watcher;
591   FilePathWatcher subdir_watcher;
592   FilePath source_dir(temp_dir_.GetPath().AppendASCII("source"));
593   FilePath source_subdir(source_dir.AppendASCII("subdir"));
594   FilePath source_file(source_subdir.AppendASCII("file"));
595   FilePath dest_dir(temp_dir_.GetPath().AppendASCII("dest"));
596   FilePath dest_subdir(dest_dir.AppendASCII("subdir"));
597   FilePath dest_file(dest_subdir.AppendASCII("file"));
598 
599   // Setup a directory hierarchy.
600   ASSERT_TRUE(base::CreateDirectory(source_subdir));
601   ASSERT_TRUE(WriteFile(source_file, "content"));
602 
603   std::unique_ptr<TestDelegate> file_delegate(new TestDelegate(collector()));
604   ASSERT_TRUE(SetupWatch(dest_file, &file_watcher, file_delegate.get(), false));
605   std::unique_ptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
606   ASSERT_TRUE(SetupWatch(dest_subdir, &subdir_watcher, subdir_delegate.get(),
607                          false));
608 
609   // Move the directory into place, s.t. the watched file appears.
610   ASSERT_TRUE(base::Move(source_dir, dest_dir));
611   ASSERT_TRUE(WaitForEvents());
612 }
613 
614 // Verify that changing attributes on a file is caught
615 #if defined(OS_ANDROID)
616 // Apps cannot change file attributes on Android in /sdcard as /sdcard uses the
617 // "fuse" file system, while /data uses "ext4".  Running these tests in /data
618 // would be preferable and allow testing file attributes and symlinks.
619 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
620 // the |temp_dir_| in /data.
621 #define FileAttributesChanged DISABLED_FileAttributesChanged
622 #endif  // defined(OS_ANDROID
TEST_F(FilePathWatcherTest,FileAttributesChanged)623 TEST_F(FilePathWatcherTest, FileAttributesChanged) {
624   ASSERT_TRUE(WriteFile(test_file(), "content"));
625   FilePathWatcher watcher;
626   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
627   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
628 
629   // Now make sure we get notified if the file is modified.
630   ASSERT_TRUE(base::MakeFileUnreadable(test_file()));
631   ASSERT_TRUE(WaitForEvents());
632 }
633 
634 #if defined(OS_LINUX)
635 
636 // Verify that creating a symlink is caught.
TEST_F(FilePathWatcherTest,CreateLink)637 TEST_F(FilePathWatcherTest, CreateLink) {
638   FilePathWatcher watcher;
639   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
640   // Note that we are watching the symlink
641   ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
642 
643   // Now make sure we get notified if the link is created.
644   // Note that test_file() doesn't have to exist.
645   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
646   ASSERT_TRUE(WaitForEvents());
647 }
648 
649 // Verify that deleting a symlink is caught.
TEST_F(FilePathWatcherTest,DeleteLink)650 TEST_F(FilePathWatcherTest, DeleteLink) {
651   // Unfortunately this test case only works if the link target exists.
652   // TODO(craig) fix this as part of crbug.com/91561.
653   ASSERT_TRUE(WriteFile(test_file(), "content"));
654   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
655   FilePathWatcher watcher;
656   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
657   ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
658 
659   // Now make sure we get notified if the link is deleted.
660   ASSERT_TRUE(base::DeleteFile(test_link(), false));
661   ASSERT_TRUE(WaitForEvents());
662 }
663 
664 // Verify that modifying a target file that a link is pointing to
665 // when we are watching the link is caught.
TEST_F(FilePathWatcherTest,ModifiedLinkedFile)666 TEST_F(FilePathWatcherTest, ModifiedLinkedFile) {
667   ASSERT_TRUE(WriteFile(test_file(), "content"));
668   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
669   FilePathWatcher watcher;
670   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
671   // Note that we are watching the symlink.
672   ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
673 
674   // Now make sure we get notified if the file is modified.
675   ASSERT_TRUE(WriteFile(test_file(), "new content"));
676   ASSERT_TRUE(WaitForEvents());
677 }
678 
679 // Verify that creating a target file that a link is pointing to
680 // when we are watching the link is caught.
TEST_F(FilePathWatcherTest,CreateTargetLinkedFile)681 TEST_F(FilePathWatcherTest, CreateTargetLinkedFile) {
682   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
683   FilePathWatcher watcher;
684   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
685   // Note that we are watching the symlink.
686   ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
687 
688   // Now make sure we get notified if the target file is created.
689   ASSERT_TRUE(WriteFile(test_file(), "content"));
690   ASSERT_TRUE(WaitForEvents());
691 }
692 
693 // Verify that deleting a target file that a link is pointing to
694 // when we are watching the link is caught.
TEST_F(FilePathWatcherTest,DeleteTargetLinkedFile)695 TEST_F(FilePathWatcherTest, DeleteTargetLinkedFile) {
696   ASSERT_TRUE(WriteFile(test_file(), "content"));
697   ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
698   FilePathWatcher watcher;
699   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
700   // Note that we are watching the symlink.
701   ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
702 
703   // Now make sure we get notified if the target file is deleted.
704   ASSERT_TRUE(base::DeleteFile(test_file(), false));
705   ASSERT_TRUE(WaitForEvents());
706 }
707 
708 // Verify that watching a file whose parent directory is a link that
709 // doesn't exist yet works if the symlink is created eventually.
TEST_F(FilePathWatcherTest,LinkedDirectoryPart1)710 TEST_F(FilePathWatcherTest, LinkedDirectoryPart1) {
711   FilePathWatcher watcher;
712   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
713   FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
714   FilePath file(dir.AppendASCII("file"));
715   FilePath linkfile(link_dir.AppendASCII("file"));
716   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
717   // dir/file should exist.
718   ASSERT_TRUE(base::CreateDirectory(dir));
719   ASSERT_TRUE(WriteFile(file, "content"));
720   // Note that we are watching dir.lnk/file which doesn't exist yet.
721   ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
722 
723   ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
724   VLOG(1) << "Waiting for link creation";
725   ASSERT_TRUE(WaitForEvents());
726 
727   ASSERT_TRUE(WriteFile(file, "content v2"));
728   VLOG(1) << "Waiting for file change";
729   ASSERT_TRUE(WaitForEvents());
730 
731   ASSERT_TRUE(base::DeleteFile(file, false));
732   VLOG(1) << "Waiting for file deletion";
733   ASSERT_TRUE(WaitForEvents());
734 }
735 
736 // Verify that watching a file whose parent directory is a
737 // dangling symlink works if the directory is created eventually.
TEST_F(FilePathWatcherTest,LinkedDirectoryPart2)738 TEST_F(FilePathWatcherTest, LinkedDirectoryPart2) {
739   FilePathWatcher watcher;
740   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
741   FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
742   FilePath file(dir.AppendASCII("file"));
743   FilePath linkfile(link_dir.AppendASCII("file"));
744   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
745   // Now create the link from dir.lnk pointing to dir but
746   // neither dir nor dir/file exist yet.
747   ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
748   // Note that we are watching dir.lnk/file.
749   ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
750 
751   ASSERT_TRUE(base::CreateDirectory(dir));
752   ASSERT_TRUE(WriteFile(file, "content"));
753   VLOG(1) << "Waiting for dir/file creation";
754   ASSERT_TRUE(WaitForEvents());
755 
756   ASSERT_TRUE(WriteFile(file, "content v2"));
757   VLOG(1) << "Waiting for file change";
758   ASSERT_TRUE(WaitForEvents());
759 
760   ASSERT_TRUE(base::DeleteFile(file, false));
761   VLOG(1) << "Waiting for file deletion";
762   ASSERT_TRUE(WaitForEvents());
763 }
764 
765 // Verify that watching a file with a symlink on the path
766 // to the file works.
TEST_F(FilePathWatcherTest,LinkedDirectoryPart3)767 TEST_F(FilePathWatcherTest, LinkedDirectoryPart3) {
768   FilePathWatcher watcher;
769   FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
770   FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
771   FilePath file(dir.AppendASCII("file"));
772   FilePath linkfile(link_dir.AppendASCII("file"));
773   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
774   ASSERT_TRUE(base::CreateDirectory(dir));
775   ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
776   // Note that we are watching dir.lnk/file but the file doesn't exist yet.
777   ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
778 
779   ASSERT_TRUE(WriteFile(file, "content"));
780   VLOG(1) << "Waiting for file creation";
781   ASSERT_TRUE(WaitForEvents());
782 
783   ASSERT_TRUE(WriteFile(file, "content v2"));
784   VLOG(1) << "Waiting for file change";
785   ASSERT_TRUE(WaitForEvents());
786 
787   ASSERT_TRUE(base::DeleteFile(file, false));
788   VLOG(1) << "Waiting for file deletion";
789   ASSERT_TRUE(WaitForEvents());
790 }
791 
792 #endif  // OS_LINUX
793 
794 enum Permission {
795   Read,
796   Write,
797   Execute
798 };
799 
800 #if defined(OS_MACOSX)
ChangeFilePermissions(const FilePath & path,Permission perm,bool allow)801 bool ChangeFilePermissions(const FilePath& path, Permission perm, bool allow) {
802   struct stat stat_buf;
803 
804   if (stat(path.value().c_str(), &stat_buf) != 0)
805     return false;
806 
807   mode_t mode = 0;
808   switch (perm) {
809     case Read:
810       mode = S_IRUSR | S_IRGRP | S_IROTH;
811       break;
812     case Write:
813       mode = S_IWUSR | S_IWGRP | S_IWOTH;
814       break;
815     case Execute:
816       mode = S_IXUSR | S_IXGRP | S_IXOTH;
817       break;
818     default:
819       ADD_FAILURE() << "unknown perm " << perm;
820       return false;
821   }
822   if (allow) {
823     stat_buf.st_mode |= mode;
824   } else {
825     stat_buf.st_mode &= ~mode;
826   }
827   return chmod(path.value().c_str(), stat_buf.st_mode) == 0;
828 }
829 #endif  // defined(OS_MACOSX)
830 
831 #if defined(OS_MACOSX)
832 // Linux implementation of FilePathWatcher doesn't catch attribute changes.
833 // http://crbug.com/78043
834 // Windows implementation of FilePathWatcher catches attribute changes that
835 // don't affect the path being watched.
836 // http://crbug.com/78045
837 
838 // Verify that changing attributes on a directory works.
TEST_F(FilePathWatcherTest,DirAttributesChanged)839 TEST_F(FilePathWatcherTest, DirAttributesChanged) {
840   FilePath test_dir1(
841       temp_dir_.GetPath().AppendASCII("DirAttributesChangedDir1"));
842   FilePath test_dir2(test_dir1.AppendASCII("DirAttributesChangedDir2"));
843   FilePath test_file(test_dir2.AppendASCII("DirAttributesChangedFile"));
844   // Setup a directory hierarchy.
845   ASSERT_TRUE(base::CreateDirectory(test_dir1));
846   ASSERT_TRUE(base::CreateDirectory(test_dir2));
847   ASSERT_TRUE(WriteFile(test_file, "content"));
848 
849   FilePathWatcher watcher;
850   std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
851   ASSERT_TRUE(SetupWatch(test_file, &watcher, delegate.get(), false));
852 
853   // We should not get notified in this case as it hasn't affected our ability
854   // to access the file.
855   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, false));
856   loop_.task_runner()->PostDelayedTask(FROM_HERE,
857                                        MessageLoop::QuitWhenIdleClosure(),
858                                        TestTimeouts::tiny_timeout());
859   ASSERT_FALSE(WaitForEvents());
860   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, true));
861 
862   // We should get notified in this case because filepathwatcher can no
863   // longer access the file
864   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, false));
865   ASSERT_TRUE(WaitForEvents());
866   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, true));
867 }
868 
869 #endif  // OS_MACOSX
870 }  // namespace
871 
872 }  // namespace base
873