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 = ::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(file_util::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(file_util::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
    873