- add sources.
[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/file_util.h"
21 #include "base/files/file_path.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 void QuitLoopWatchCallback(MessageLoop* loop,
142                            const FilePath& expected_path,
143                            bool expected_error,
144                            bool* flag,
145                            const FilePath& path,
146                            bool error) {
147   ASSERT_TRUE(flag);
148   *flag = true;
149   EXPECT_EQ(expected_path, path);
150   EXPECT_EQ(expected_error, error);
151   loop->PostTask(FROM_HERE, loop->QuitWhenIdleClosure());
152 }
153
154 class FilePathWatcherTest : public testing::Test {
155  public:
156   FilePathWatcherTest()
157       : file_thread_("FilePathWatcherTest") {}
158
159   virtual ~FilePathWatcherTest() {}
160
161  protected:
162   virtual void SetUp() OVERRIDE {
163     // Create a separate file thread in order to test proper thread usage.
164     base::Thread::Options options(MessageLoop::TYPE_IO, 0);
165     ASSERT_TRUE(file_thread_.StartWithOptions(options));
166     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
167     collector_ = new NotificationCollector();
168   }
169
170   virtual void TearDown() OVERRIDE {
171     RunLoop().RunUntilIdle();
172   }
173
174   void DeleteDelegateOnFileThread(TestDelegate* delegate) {
175     file_thread_.message_loop_proxy()->DeleteSoon(FROM_HERE, delegate);
176   }
177
178   FilePath test_file() {
179     return temp_dir_.path().AppendASCII("FilePathWatcherTest");
180   }
181
182   FilePath test_link() {
183     return temp_dir_.path().AppendASCII("FilePathWatcherTest.lnk");
184   }
185
186   // Write |content| to |file|. Returns true on success.
187   bool WriteFile(const FilePath& file, const std::string& content) {
188     int write_size = file_util::WriteFile(file, content.c_str(),
189                                           content.length());
190     return write_size == static_cast<int>(content.length());
191   }
192
193   bool SetupWatch(const FilePath& target,
194                   FilePathWatcher* watcher,
195                   TestDelegateBase* delegate,
196                   bool recursive_watch) WARN_UNUSED_RESULT;
197
198   bool WaitForEvents() WARN_UNUSED_RESULT {
199     collector_->Reset();
200     loop_.Run();
201     return collector_->Success();
202   }
203
204   NotificationCollector* collector() { return collector_.get(); }
205
206   MessageLoop loop_;
207   base::Thread file_thread_;
208   ScopedTempDir temp_dir_;
209   scoped_refptr<NotificationCollector> collector_;
210
211   DISALLOW_COPY_AND_ASSIGN(FilePathWatcherTest);
212 };
213
214 bool FilePathWatcherTest::SetupWatch(const FilePath& target,
215                                      FilePathWatcher* watcher,
216                                      TestDelegateBase* delegate,
217                                      bool recursive_watch) {
218   base::WaitableEvent completion(false, false);
219   bool result;
220   file_thread_.message_loop_proxy()->PostTask(
221       FROM_HERE,
222       base::Bind(SetupWatchCallback,
223                  target, watcher, delegate, recursive_watch, &result,
224                  &completion));
225   completion.Wait();
226   return result;
227 }
228
229 // Basic test: Create the file and verify that we notice.
230 TEST_F(FilePathWatcherTest, NewFile) {
231   FilePathWatcher watcher;
232   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
233   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
234
235   ASSERT_TRUE(WriteFile(test_file(), "content"));
236   ASSERT_TRUE(WaitForEvents());
237   DeleteDelegateOnFileThread(delegate.release());
238 }
239
240 // Verify that modifying the file is caught.
241 TEST_F(FilePathWatcherTest, ModifiedFile) {
242   ASSERT_TRUE(WriteFile(test_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(WriteFile(test_file(), "new content"));
250   ASSERT_TRUE(WaitForEvents());
251   DeleteDelegateOnFileThread(delegate.release());
252 }
253
254 // Verify that moving the file into place is caught.
255 TEST_F(FilePathWatcherTest, MovedFile) {
256   FilePath source_file(temp_dir_.path().AppendASCII("source"));
257   ASSERT_TRUE(WriteFile(source_file, "content"));
258
259   FilePathWatcher watcher;
260   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
261   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
262
263   // Now make sure we get notified if the file is modified.
264   ASSERT_TRUE(base::Move(source_file, test_file()));
265   ASSERT_TRUE(WaitForEvents());
266   DeleteDelegateOnFileThread(delegate.release());
267 }
268
269 TEST_F(FilePathWatcherTest, DeletedFile) {
270   ASSERT_TRUE(WriteFile(test_file(), "content"));
271
272   FilePathWatcher watcher;
273   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
274   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
275
276   // Now make sure we get notified if the file is deleted.
277   base::DeleteFile(test_file(), false);
278   ASSERT_TRUE(WaitForEvents());
279   DeleteDelegateOnFileThread(delegate.release());
280 }
281
282 // Used by the DeleteDuringNotify test below.
283 // Deletes the FilePathWatcher when it's notified.
284 class Deleter : public TestDelegateBase {
285  public:
286   Deleter(FilePathWatcher* watcher, MessageLoop* loop)
287       : watcher_(watcher),
288         loop_(loop) {
289   }
290   virtual ~Deleter() {}
291
292   virtual void OnFileChanged(const FilePath&, bool) OVERRIDE {
293     watcher_.reset();
294     loop_->PostTask(FROM_HERE, MessageLoop::QuitWhenIdleClosure());
295   }
296
297   FilePathWatcher* watcher() const { return watcher_.get(); }
298
299  private:
300   scoped_ptr<FilePathWatcher> watcher_;
301   MessageLoop* loop_;
302
303   DISALLOW_COPY_AND_ASSIGN(Deleter);
304 };
305
306 // Verify that deleting a watcher during the callback doesn't crash.
307 TEST_F(FilePathWatcherTest, DeleteDuringNotify) {
308   FilePathWatcher* watcher = new FilePathWatcher;
309   // Takes ownership of watcher.
310   scoped_ptr<Deleter> deleter(new Deleter(watcher, &loop_));
311   ASSERT_TRUE(SetupWatch(test_file(), watcher, deleter.get(), false));
312
313   ASSERT_TRUE(WriteFile(test_file(), "content"));
314   ASSERT_TRUE(WaitForEvents());
315
316   // We win if we haven't crashed yet.
317   // Might as well double-check it got deleted, too.
318   ASSERT_TRUE(deleter->watcher() == NULL);
319 }
320
321 // Verify that deleting the watcher works even if there is a pending
322 // notification.
323 // Flaky on MacOS (and ARM linux): http://crbug.com/85930
324 TEST_F(FilePathWatcherTest, DISABLED_DestroyWithPendingNotification) {
325   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
326   FilePathWatcher* watcher = new FilePathWatcher;
327   ASSERT_TRUE(SetupWatch(test_file(), watcher, delegate.get(), false));
328   ASSERT_TRUE(WriteFile(test_file(), "content"));
329   file_thread_.message_loop_proxy()->DeleteSoon(FROM_HERE, watcher);
330   DeleteDelegateOnFileThread(delegate.release());
331 }
332
333 TEST_F(FilePathWatcherTest, MultipleWatchersSingleFile) {
334   FilePathWatcher watcher1, watcher2;
335   scoped_ptr<TestDelegate> delegate1(new TestDelegate(collector()));
336   scoped_ptr<TestDelegate> delegate2(new TestDelegate(collector()));
337   ASSERT_TRUE(SetupWatch(test_file(), &watcher1, delegate1.get(), false));
338   ASSERT_TRUE(SetupWatch(test_file(), &watcher2, delegate2.get(), false));
339
340   ASSERT_TRUE(WriteFile(test_file(), "content"));
341   ASSERT_TRUE(WaitForEvents());
342   DeleteDelegateOnFileThread(delegate1.release());
343   DeleteDelegateOnFileThread(delegate2.release());
344 }
345
346 // Verify that watching a file whose parent directory doesn't exist yet works if
347 // the directory and file are created eventually.
348 TEST_F(FilePathWatcherTest, NonExistentDirectory) {
349   FilePathWatcher watcher;
350   FilePath dir(temp_dir_.path().AppendASCII("dir"));
351   FilePath file(dir.AppendASCII("file"));
352   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
353   ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
354
355   ASSERT_TRUE(file_util::CreateDirectory(dir));
356
357   ASSERT_TRUE(WriteFile(file, "content"));
358
359   VLOG(1) << "Waiting for file creation";
360   ASSERT_TRUE(WaitForEvents());
361
362   ASSERT_TRUE(WriteFile(file, "content v2"));
363   VLOG(1) << "Waiting for file change";
364   ASSERT_TRUE(WaitForEvents());
365
366   ASSERT_TRUE(base::DeleteFile(file, false));
367   VLOG(1) << "Waiting for file deletion";
368   ASSERT_TRUE(WaitForEvents());
369   DeleteDelegateOnFileThread(delegate.release());
370 }
371
372 // Exercises watch reconfiguration for the case that directories on the path
373 // are rapidly created.
374 TEST_F(FilePathWatcherTest, DirectoryChain) {
375   FilePath path(temp_dir_.path());
376   std::vector<std::string> dir_names;
377   for (int i = 0; i < 20; i++) {
378     std::string dir(base::StringPrintf("d%d", i));
379     dir_names.push_back(dir);
380     path = path.AppendASCII(dir);
381   }
382
383   FilePathWatcher watcher;
384   FilePath file(path.AppendASCII("file"));
385   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
386   ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
387
388   FilePath sub_path(temp_dir_.path());
389   for (std::vector<std::string>::const_iterator d(dir_names.begin());
390        d != dir_names.end(); ++d) {
391     sub_path = sub_path.AppendASCII(*d);
392     ASSERT_TRUE(file_util::CreateDirectory(sub_path));
393   }
394   VLOG(1) << "Create File";
395   ASSERT_TRUE(WriteFile(file, "content"));
396   VLOG(1) << "Waiting for file creation";
397   ASSERT_TRUE(WaitForEvents());
398
399   ASSERT_TRUE(WriteFile(file, "content v2"));
400   VLOG(1) << "Waiting for file modification";
401   ASSERT_TRUE(WaitForEvents());
402   DeleteDelegateOnFileThread(delegate.release());
403 }
404
405 #if defined(OS_MACOSX)
406 // http://crbug.com/85930
407 #define DisappearingDirectory DISABLED_DisappearingDirectory
408 #endif
409 TEST_F(FilePathWatcherTest, DisappearingDirectory) {
410   FilePathWatcher watcher;
411   FilePath dir(temp_dir_.path().AppendASCII("dir"));
412   FilePath file(dir.AppendASCII("file"));
413   ASSERT_TRUE(file_util::CreateDirectory(dir));
414   ASSERT_TRUE(WriteFile(file, "content"));
415   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
416   ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
417
418   ASSERT_TRUE(base::DeleteFile(dir, true));
419   ASSERT_TRUE(WaitForEvents());
420   DeleteDelegateOnFileThread(delegate.release());
421 }
422
423 // Tests that a file that is deleted and reappears is tracked correctly.
424 TEST_F(FilePathWatcherTest, DeleteAndRecreate) {
425   ASSERT_TRUE(WriteFile(test_file(), "content"));
426   FilePathWatcher watcher;
427   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
428   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
429
430   ASSERT_TRUE(base::DeleteFile(test_file(), false));
431   VLOG(1) << "Waiting for file deletion";
432   ASSERT_TRUE(WaitForEvents());
433
434   ASSERT_TRUE(WriteFile(test_file(), "content"));
435   VLOG(1) << "Waiting for file creation";
436   ASSERT_TRUE(WaitForEvents());
437   DeleteDelegateOnFileThread(delegate.release());
438 }
439
440 TEST_F(FilePathWatcherTest, WatchDirectory) {
441   FilePathWatcher watcher;
442   FilePath dir(temp_dir_.path().AppendASCII("dir"));
443   FilePath file1(dir.AppendASCII("file1"));
444   FilePath file2(dir.AppendASCII("file2"));
445   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
446   ASSERT_TRUE(SetupWatch(dir, &watcher, delegate.get(), false));
447
448   ASSERT_TRUE(file_util::CreateDirectory(dir));
449   VLOG(1) << "Waiting for directory creation";
450   ASSERT_TRUE(WaitForEvents());
451
452   ASSERT_TRUE(WriteFile(file1, "content"));
453   VLOG(1) << "Waiting for file1 creation";
454   ASSERT_TRUE(WaitForEvents());
455
456 #if !defined(OS_MACOSX)
457   // Mac implementation does not detect files modified in a directory.
458   ASSERT_TRUE(WriteFile(file1, "content v2"));
459   VLOG(1) << "Waiting for file1 modification";
460   ASSERT_TRUE(WaitForEvents());
461 #endif  // !OS_MACOSX
462
463   ASSERT_TRUE(base::DeleteFile(file1, false));
464   VLOG(1) << "Waiting for file1 deletion";
465   ASSERT_TRUE(WaitForEvents());
466
467   ASSERT_TRUE(WriteFile(file2, "content"));
468   VLOG(1) << "Waiting for file2 creation";
469   ASSERT_TRUE(WaitForEvents());
470   DeleteDelegateOnFileThread(delegate.release());
471 }
472
473 TEST_F(FilePathWatcherTest, MoveParent) {
474   FilePathWatcher file_watcher;
475   FilePathWatcher subdir_watcher;
476   FilePath dir(temp_dir_.path().AppendASCII("dir"));
477   FilePath dest(temp_dir_.path().AppendASCII("dest"));
478   FilePath subdir(dir.AppendASCII("subdir"));
479   FilePath file(subdir.AppendASCII("file"));
480   scoped_ptr<TestDelegate> file_delegate(new TestDelegate(collector()));
481   ASSERT_TRUE(SetupWatch(file, &file_watcher, file_delegate.get(), false));
482   scoped_ptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
483   ASSERT_TRUE(SetupWatch(subdir, &subdir_watcher, subdir_delegate.get(),
484                          false));
485
486   // Setup a directory hierarchy.
487   ASSERT_TRUE(file_util::CreateDirectory(subdir));
488   ASSERT_TRUE(WriteFile(file, "content"));
489   VLOG(1) << "Waiting for file creation";
490   ASSERT_TRUE(WaitForEvents());
491
492   // Move the parent directory.
493   base::Move(dir, dest);
494   VLOG(1) << "Waiting for directory move";
495   ASSERT_TRUE(WaitForEvents());
496   DeleteDelegateOnFileThread(file_delegate.release());
497   DeleteDelegateOnFileThread(subdir_delegate.release());
498 }
499
500 #if defined(OS_WIN)
501 TEST_F(FilePathWatcherTest, RecursiveWatch) {
502   FilePathWatcher watcher;
503   FilePath dir(temp_dir_.path().AppendASCII("dir"));
504   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
505   ASSERT_TRUE(SetupWatch(dir, &watcher, delegate.get(), true));
506
507   // Main directory("dir") creation.
508   ASSERT_TRUE(file_util::CreateDirectory(dir));
509   ASSERT_TRUE(WaitForEvents());
510
511   // Create "$dir/file1".
512   FilePath file1(dir.AppendASCII("file1"));
513   ASSERT_TRUE(WriteFile(file1, "content"));
514   ASSERT_TRUE(WaitForEvents());
515
516   // Create "$dir/subdir".
517   FilePath subdir(dir.AppendASCII("subdir"));
518   ASSERT_TRUE(file_util::CreateDirectory(subdir));
519   ASSERT_TRUE(WaitForEvents());
520
521   // Create "$dir/subdir/subdir_file1".
522   FilePath subdir_file1(subdir.AppendASCII("subdir_file1"));
523   ASSERT_TRUE(WriteFile(subdir_file1, "content"));
524   ASSERT_TRUE(WaitForEvents());
525
526   // Create "$dir/subdir/subdir_child_dir".
527   FilePath subdir_child_dir(subdir.AppendASCII("subdir_child_dir"));
528   ASSERT_TRUE(file_util::CreateDirectory(subdir_child_dir));
529   ASSERT_TRUE(WaitForEvents());
530
531   // Create "$dir/subdir/subdir_child_dir/child_dir_file1".
532   FilePath child_dir_file1(subdir_child_dir.AppendASCII("child_dir_file1"));
533   ASSERT_TRUE(WriteFile(child_dir_file1, "content v2"));
534   ASSERT_TRUE(WaitForEvents());
535
536   // Write into "$dir/subdir/subdir_child_dir/child_dir_file1".
537   ASSERT_TRUE(WriteFile(child_dir_file1, "content"));
538   ASSERT_TRUE(WaitForEvents());
539
540   // Modify "$dir/subdir/subdir_child_dir/child_dir_file1" attributes.
541   ASSERT_TRUE(file_util::MakeFileUnreadable(child_dir_file1));
542   ASSERT_TRUE(WaitForEvents());
543
544   // Delete "$dir/subdir/subdir_file1".
545   ASSERT_TRUE(base::DeleteFile(subdir_file1, false));
546   ASSERT_TRUE(WaitForEvents());
547
548   // Delete "$dir/subdir/subdir_child_dir/child_dir_file1".
549   ASSERT_TRUE(base::DeleteFile(child_dir_file1, false));
550   ASSERT_TRUE(WaitForEvents());
551   DeleteDelegateOnFileThread(delegate.release());
552 }
553 #else
554 TEST_F(FilePathWatcherTest, RecursiveWatch) {
555   FilePathWatcher watcher;
556   FilePath dir(temp_dir_.path().AppendASCII("dir"));
557   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
558   // Non-Windows implementaion does not support recursive watching.
559   ASSERT_FALSE(SetupWatch(dir, &watcher, delegate.get(), true));
560   DeleteDelegateOnFileThread(delegate.release());
561 }
562 #endif
563
564 TEST_F(FilePathWatcherTest, MoveChild) {
565   FilePathWatcher file_watcher;
566   FilePathWatcher subdir_watcher;
567   FilePath source_dir(temp_dir_.path().AppendASCII("source"));
568   FilePath source_subdir(source_dir.AppendASCII("subdir"));
569   FilePath source_file(source_subdir.AppendASCII("file"));
570   FilePath dest_dir(temp_dir_.path().AppendASCII("dest"));
571   FilePath dest_subdir(dest_dir.AppendASCII("subdir"));
572   FilePath dest_file(dest_subdir.AppendASCII("file"));
573
574   // Setup a directory hierarchy.
575   ASSERT_TRUE(file_util::CreateDirectory(source_subdir));
576   ASSERT_TRUE(WriteFile(source_file, "content"));
577
578   scoped_ptr<TestDelegate> file_delegate(new TestDelegate(collector()));
579   ASSERT_TRUE(SetupWatch(dest_file, &file_watcher, file_delegate.get(), false));
580   scoped_ptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
581   ASSERT_TRUE(SetupWatch(dest_subdir, &subdir_watcher, subdir_delegate.get(),
582                          false));
583
584   // Move the directory into place, s.t. the watched file appears.
585   ASSERT_TRUE(base::Move(source_dir, dest_dir));
586   ASSERT_TRUE(WaitForEvents());
587   DeleteDelegateOnFileThread(file_delegate.release());
588   DeleteDelegateOnFileThread(subdir_delegate.release());
589 }
590
591 #if !defined(OS_LINUX)
592 // Linux implementation of FilePathWatcher doesn't catch attribute changes.
593 // http://crbug.com/78043
594
595 // Verify that changing attributes on a file is caught
596 TEST_F(FilePathWatcherTest, FileAttributesChanged) {
597   ASSERT_TRUE(WriteFile(test_file(), "content"));
598   FilePathWatcher watcher;
599   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
600   ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
601
602   // Now make sure we get notified if the file is modified.
603   ASSERT_TRUE(file_util::MakeFileUnreadable(test_file()));
604   ASSERT_TRUE(WaitForEvents());
605   DeleteDelegateOnFileThread(delegate.release());
606 }
607
608 #endif  // !OS_LINUX
609
610 #if defined(OS_LINUX)
611
612 // Verify that creating a symlink is caught.
613 TEST_F(FilePathWatcherTest, CreateLink) {
614   FilePathWatcher watcher;
615   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
616   // Note that we are watching the symlink
617   ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
618
619   // Now make sure we get notified if the link is created.
620   // Note that test_file() doesn't have to exist.
621   ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
622   ASSERT_TRUE(WaitForEvents());
623   DeleteDelegateOnFileThread(delegate.release());
624 }
625
626 // Verify that deleting a symlink is caught.
627 TEST_F(FilePathWatcherTest, DeleteLink) {
628   // Unfortunately this test case only works if the link target exists.
629   // TODO(craig) fix this as part of crbug.com/91561.
630   ASSERT_TRUE(WriteFile(test_file(), "content"));
631   ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
632   FilePathWatcher watcher;
633   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
634   ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
635
636   // Now make sure we get notified if the link is deleted.
637   ASSERT_TRUE(base::DeleteFile(test_link(), false));
638   ASSERT_TRUE(WaitForEvents());
639   DeleteDelegateOnFileThread(delegate.release());
640 }
641
642 // Verify that modifying a target file that a link is pointing to
643 // when we are watching the link is caught.
644 TEST_F(FilePathWatcherTest, ModifiedLinkedFile) {
645   ASSERT_TRUE(WriteFile(test_file(), "content"));
646   ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
647   FilePathWatcher watcher;
648   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
649   // Note that we are watching the symlink.
650   ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
651
652   // Now make sure we get notified if the file is modified.
653   ASSERT_TRUE(WriteFile(test_file(), "new content"));
654   ASSERT_TRUE(WaitForEvents());
655   DeleteDelegateOnFileThread(delegate.release());
656 }
657
658 // Verify that creating a target file that a link is pointing to
659 // when we are watching the link is caught.
660 TEST_F(FilePathWatcherTest, CreateTargetLinkedFile) {
661   ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
662   FilePathWatcher watcher;
663   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
664   // Note that we are watching the symlink.
665   ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
666
667   // Now make sure we get notified if the target file is created.
668   ASSERT_TRUE(WriteFile(test_file(), "content"));
669   ASSERT_TRUE(WaitForEvents());
670   DeleteDelegateOnFileThread(delegate.release());
671 }
672
673 // Verify that deleting a target file that a link is pointing to
674 // when we are watching the link is caught.
675 TEST_F(FilePathWatcherTest, DeleteTargetLinkedFile) {
676   ASSERT_TRUE(WriteFile(test_file(), "content"));
677   ASSERT_TRUE(file_util::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 deleted.
684   ASSERT_TRUE(base::DeleteFile(test_file(), false));
685   ASSERT_TRUE(WaitForEvents());
686   DeleteDelegateOnFileThread(delegate.release());
687 }
688
689 // Verify that watching a file whose parent directory is a link that
690 // doesn't exist yet works if the symlink is created eventually.
691 TEST_F(FilePathWatcherTest, LinkedDirectoryPart1) {
692   FilePathWatcher watcher;
693   FilePath dir(temp_dir_.path().AppendASCII("dir"));
694   FilePath link_dir(temp_dir_.path().AppendASCII("dir.lnk"));
695   FilePath file(dir.AppendASCII("file"));
696   FilePath linkfile(link_dir.AppendASCII("file"));
697   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
698   // dir/file should exist.
699   ASSERT_TRUE(file_util::CreateDirectory(dir));
700   ASSERT_TRUE(WriteFile(file, "content"));
701   // Note that we are watching dir.lnk/file which doesn't exist yet.
702   ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
703
704   ASSERT_TRUE(file_util::CreateSymbolicLink(dir, link_dir));
705   VLOG(1) << "Waiting for link creation";
706   ASSERT_TRUE(WaitForEvents());
707
708   ASSERT_TRUE(WriteFile(file, "content v2"));
709   VLOG(1) << "Waiting for file change";
710   ASSERT_TRUE(WaitForEvents());
711
712   ASSERT_TRUE(base::DeleteFile(file, false));
713   VLOG(1) << "Waiting for file deletion";
714   ASSERT_TRUE(WaitForEvents());
715   DeleteDelegateOnFileThread(delegate.release());
716 }
717
718 // Verify that watching a file whose parent directory is a
719 // dangling symlink works if the directory is created eventually.
720 TEST_F(FilePathWatcherTest, LinkedDirectoryPart2) {
721   FilePathWatcher watcher;
722   FilePath dir(temp_dir_.path().AppendASCII("dir"));
723   FilePath link_dir(temp_dir_.path().AppendASCII("dir.lnk"));
724   FilePath file(dir.AppendASCII("file"));
725   FilePath linkfile(link_dir.AppendASCII("file"));
726   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
727   // Now create the link from dir.lnk pointing to dir but
728   // neither dir nor dir/file exist yet.
729   ASSERT_TRUE(file_util::CreateSymbolicLink(dir, link_dir));
730   // Note that we are watching dir.lnk/file.
731   ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
732
733   ASSERT_TRUE(file_util::CreateDirectory(dir));
734   ASSERT_TRUE(WriteFile(file, "content"));
735   VLOG(1) << "Waiting for dir/file creation";
736   ASSERT_TRUE(WaitForEvents());
737
738   ASSERT_TRUE(WriteFile(file, "content v2"));
739   VLOG(1) << "Waiting for file change";
740   ASSERT_TRUE(WaitForEvents());
741
742   ASSERT_TRUE(base::DeleteFile(file, false));
743   VLOG(1) << "Waiting for file deletion";
744   ASSERT_TRUE(WaitForEvents());
745   DeleteDelegateOnFileThread(delegate.release());
746 }
747
748 // Verify that watching a file with a symlink on the path
749 // to the file works.
750 TEST_F(FilePathWatcherTest, LinkedDirectoryPart3) {
751   FilePathWatcher watcher;
752   FilePath dir(temp_dir_.path().AppendASCII("dir"));
753   FilePath link_dir(temp_dir_.path().AppendASCII("dir.lnk"));
754   FilePath file(dir.AppendASCII("file"));
755   FilePath linkfile(link_dir.AppendASCII("file"));
756   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
757   ASSERT_TRUE(file_util::CreateDirectory(dir));
758   ASSERT_TRUE(file_util::CreateSymbolicLink(dir, link_dir));
759   // Note that we are watching dir.lnk/file but the file doesn't exist yet.
760   ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
761
762   ASSERT_TRUE(WriteFile(file, "content"));
763   VLOG(1) << "Waiting for file 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 #endif  // OS_LINUX
777
778 enum Permission {
779   Read,
780   Write,
781   Execute
782 };
783
784 bool ChangeFilePermissions(const FilePath& path, Permission perm, bool allow) {
785 #if defined(OS_POSIX)
786   struct stat stat_buf;
787
788   if (stat(path.value().c_str(), &stat_buf) != 0)
789     return false;
790
791   mode_t mode = 0;
792   switch (perm) {
793     case Read:
794       mode = S_IRUSR | S_IRGRP | S_IROTH;
795       break;
796     case Write:
797       mode = S_IWUSR | S_IWGRP | S_IWOTH;
798       break;
799     case Execute:
800       mode = S_IXUSR | S_IXGRP | S_IXOTH;
801       break;
802     default:
803       ADD_FAILURE() << "unknown perm " << perm;
804       return false;
805   }
806   if (allow) {
807     stat_buf.st_mode |= mode;
808   } else {
809     stat_buf.st_mode &= ~mode;
810   }
811   return chmod(path.value().c_str(), stat_buf.st_mode) == 0;
812
813 #elif defined(OS_WIN)
814   PACL old_dacl;
815   PSECURITY_DESCRIPTOR security_descriptor;
816   if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
817                            SE_FILE_OBJECT,
818                            DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl,
819                            NULL, &security_descriptor) != ERROR_SUCCESS)
820     return false;
821
822   DWORD mode = 0;
823   switch (perm) {
824     case Read:
825       mode = GENERIC_READ;
826       break;
827     case Write:
828       mode = GENERIC_WRITE;
829       break;
830     case Execute:
831       mode = GENERIC_EXECUTE;
832       break;
833     default:
834       ADD_FAILURE() << "unknown perm " << perm;
835       return false;
836   }
837
838   // Deny Read access for the current user.
839   EXPLICIT_ACCESS change;
840   change.grfAccessPermissions = mode;
841   change.grfAccessMode = allow ? GRANT_ACCESS : DENY_ACCESS;
842   change.grfInheritance = 0;
843   change.Trustee.pMultipleTrustee = NULL;
844   change.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
845   change.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
846   change.Trustee.TrusteeType = TRUSTEE_IS_USER;
847   change.Trustee.ptstrName = L"CURRENT_USER";
848
849   PACL new_dacl;
850   if (SetEntriesInAcl(1, &change, old_dacl, &new_dacl) != ERROR_SUCCESS) {
851     LocalFree(security_descriptor);
852     return false;
853   }
854
855   DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
856                                   SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
857                                   NULL, NULL, new_dacl, NULL);
858   LocalFree(security_descriptor);
859   LocalFree(new_dacl);
860
861   return rc == ERROR_SUCCESS;
862 #else
863   NOTIMPLEMENTED();
864   return false;
865 #endif
866 }
867
868 #if defined(OS_MACOSX)
869 // Linux implementation of FilePathWatcher doesn't catch attribute changes.
870 // http://crbug.com/78043
871 // Windows implementation of FilePathWatcher catches attribute changes that
872 // don't affect the path being watched.
873 // http://crbug.com/78045
874
875 // Verify that changing attributes on a directory works.
876 TEST_F(FilePathWatcherTest, DirAttributesChanged) {
877   FilePath test_dir1(temp_dir_.path().AppendASCII("DirAttributesChangedDir1"));
878   FilePath test_dir2(test_dir1.AppendASCII("DirAttributesChangedDir2"));
879   FilePath test_file(test_dir2.AppendASCII("DirAttributesChangedFile"));
880   // Setup a directory hierarchy.
881   ASSERT_TRUE(file_util::CreateDirectory(test_dir1));
882   ASSERT_TRUE(file_util::CreateDirectory(test_dir2));
883   ASSERT_TRUE(WriteFile(test_file, "content"));
884
885   FilePathWatcher watcher;
886   scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
887   ASSERT_TRUE(SetupWatch(test_file, &watcher, delegate.get(), false));
888
889   // We should not get notified in this case as it hasn't affected our ability
890   // to access the file.
891   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, false));
892   loop_.PostDelayedTask(FROM_HERE,
893                         MessageLoop::QuitWhenIdleClosure(),
894                         TestTimeouts::tiny_timeout());
895   ASSERT_FALSE(WaitForEvents());
896   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, true));
897
898   // We should get notified in this case because filepathwatcher can no
899   // longer access the file
900   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, false));
901   ASSERT_TRUE(WaitForEvents());
902   ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, true));
903   DeleteDelegateOnFileThread(delegate.release());
904 }
905
906 #endif  // OS_MACOSX
907 }  // namespace
908
909 }  // namespace base