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