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 34namespace base { 35 36namespace { 37 38class 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. 42class 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 92class 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. 108class 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 129void 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 141void 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 154class 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 214bool 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. 230TEST_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. 241TEST_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. 255TEST_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 269TEST_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. 284class 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. 307TEST_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 324TEST_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 333TEST_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. 348TEST_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. 374TEST_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 409TEST_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. 424TEST_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 440TEST_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 473TEST_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) 501TEST_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 554TEST_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 564TEST_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 596TEST_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. 613TEST_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. 627TEST_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. 644TEST_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. 660TEST_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. 675TEST_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. 691TEST_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. 720TEST_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. 750TEST_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 778enum Permission { 779 Read, 780 Write, 781 Execute 782}; 783 784bool 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. 876TEST_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