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