Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / base / files / file_path_watcher_browsertest.cc
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:
45   NotificationCollector()
46       : loop_(base::MessageLoopProxy::current()) {}
47
48   // Called from the file thread by the delegates.
49   void OnChange(TestDelegate* delegate) {
50     loop_->PostTask(FROM_HERE,
51                     base::Bind(&NotificationCollector::RecordChange, this,
52                                base::Unretained(delegate)));
53   }
54
55   void Register(TestDelegate* delegate) {
56     delegates_.insert(delegate);
57   }
58
59   void Reset() {
60     signaled_.clear();
61   }
62
63   bool Success() {
64     return signaled_ == delegates_;
65   }
66
67  private:
68   friend class base::RefCountedThreadSafe<NotificationCollector>;
69   ~NotificationCollector() {}
70
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:
94   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:
110   explicit TestDelegate(NotificationCollector* collector)
111       : collector_(collector) {
112     collector_->Register(this);
113   }
114   virtual ~TestDelegate() {}
115
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
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:
143   FilePathWatcherTest()
144       : file_thread_("FilePathWatcherTest") {}
145
146   virtual ~FilePathWatcherTest() {}
147
148  protected:
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
157   virtual void TearDown() OVERRIDE {
158     RunLoop().RunUntilIdle();
159   }
160
161   void DeleteDelegateOnFileThread(TestDelegate* delegate) {
162     file_thread_.message_loop_proxy()->DeleteSoon(FROM_HERE, delegate);
163   }
164
165   FilePath test_file() {
166     return temp_dir_.path().AppendASCII("FilePathWatcherTest");
167   }
168
169   FilePath test_link() {
170     return temp_dir_.path().AppendASCII("FilePathWatcherTest.lnk");
171   }
172
173   // Write |content| to |file|. Returns true on success.
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
184   bool WaitForEvents() WARN_UNUSED_RESULT {
185     collector_->Reset();
186     loop_.Run();
187     return collector_->Success();
188   }
189
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
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.
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.
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.
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
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:
271   Deleter(FilePathWatcher* watcher, MessageLoop* loop)
272       : watcher_(watcher),
273         loop_(loop) {
274   }
275   virtual ~Deleter() {}
276
277   virtual void OnFileChanged(const FilePath&, bool) OVERRIDE {
278     watcher_.reset();
279     loop_->PostTask(FROM_HERE, MessageLoop::QuitWhenIdleClosure());
280   }
281
282   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.
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
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
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.
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.
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
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.
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
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
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
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)
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
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
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.
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.
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.
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.
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.
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.
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.
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.
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)
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.
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