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 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
    910