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