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