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.
5 #include "base/files/file_path_watcher.h"
10 #elif defined(OS_POSIX)
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/files/file_path.h"
21 #include "base/files/file_util.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"
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
> {
45 NotificationCollector()
46 : loop_(base::MessageLoopProxy::current()) {}
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
)));
55 void Register(TestDelegate
* delegate
) {
56 delegates_
.insert(delegate
);
64 return signaled_
== delegates_
;
68 friend class base::RefCountedThreadSafe
<NotificationCollector
>;
69 ~NotificationCollector() {}
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
);
77 // Check whether all delegates have been signaled.
78 if (signaled_
== delegates_
)
79 loop_
->PostTask(FROM_HERE
, MessageLoop::QuitWhenIdleClosure());
82 // Set of registered delegates.
83 std::set
<TestDelegate
*> delegates_
;
85 // Set of signaled delegates.
86 std::set
<TestDelegate
*> signaled_
;
88 // The loop we should break after all delegates signaled.
89 scoped_refptr
<base::MessageLoopProxy
> loop_
;
92 class TestDelegateBase
: public SupportsWeakPtr
<TestDelegateBase
> {
95 virtual ~TestDelegateBase() {}
97 virtual void OnFileChanged(const FilePath
& path
, bool error
) = 0;
100 DISALLOW_COPY_AND_ASSIGN(TestDelegateBase
);
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
{
110 explicit TestDelegate(NotificationCollector
* collector
)
111 : collector_(collector
) {
112 collector_
->Register(this);
114 ~TestDelegate() override
{}
116 void OnFileChanged(const FilePath
& path
, bool error
) override
{
118 ADD_FAILURE() << "Error " << path
.value();
120 collector_
->OnChange(this);
124 scoped_refptr
<NotificationCollector
> collector_
;
126 DISALLOW_COPY_AND_ASSIGN(TestDelegate
);
129 void SetupWatchCallback(const FilePath
& target
,
130 FilePathWatcher
* watcher
,
131 TestDelegateBase
* delegate
,
132 bool recursive_watch
,
134 base::WaitableEvent
* completion
) {
135 *result
= watcher
->Watch(target
, recursive_watch
,
136 base::Bind(&TestDelegateBase::OnFileChanged
,
137 delegate
->AsWeakPtr()));
138 completion
->Signal();
141 class FilePathWatcherTest
: public testing::Test
{
143 FilePathWatcherTest()
144 : file_thread_("FilePathWatcherTest") {}
146 ~FilePathWatcherTest() override
{}
149 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();
157 void TearDown() override
{ RunLoop().RunUntilIdle(); }
159 void DeleteDelegateOnFileThread(TestDelegate
* delegate
) {
160 file_thread_
.message_loop_proxy()->DeleteSoon(FROM_HERE
, delegate
);
163 FilePath
test_file() {
164 return temp_dir_
.path().AppendASCII("FilePathWatcherTest");
167 FilePath
test_link() {
168 return temp_dir_
.path().AppendASCII("FilePathWatcherTest.lnk");
171 // Write |content| to |file|. Returns true on success.
172 bool WriteFile(const FilePath
& file
, const std::string
& content
) {
173 int write_size
= ::base::WriteFile(file
, content
.c_str(), content
.length());
174 return write_size
== static_cast<int>(content
.length());
177 bool SetupWatch(const FilePath
& target
,
178 FilePathWatcher
* watcher
,
179 TestDelegateBase
* delegate
,
180 bool recursive_watch
) WARN_UNUSED_RESULT
;
182 bool WaitForEvents() WARN_UNUSED_RESULT
{
185 return collector_
->Success();
188 NotificationCollector
* collector() { return collector_
.get(); }
191 base::Thread file_thread_
;
192 ScopedTempDir temp_dir_
;
193 scoped_refptr
<NotificationCollector
> collector_
;
195 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherTest
);
198 bool FilePathWatcherTest::SetupWatch(const FilePath
& target
,
199 FilePathWatcher
* watcher
,
200 TestDelegateBase
* delegate
,
201 bool recursive_watch
) {
202 base::WaitableEvent
completion(false, false);
204 file_thread_
.message_loop_proxy()->PostTask(
206 base::Bind(SetupWatchCallback
, target
, watcher
, delegate
, recursive_watch
,
207 &result
, &completion
));
212 // Basic test: Create the file and verify that we notice.
213 TEST_F(FilePathWatcherTest
, NewFile
) {
214 FilePathWatcher watcher
;
215 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
216 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
218 ASSERT_TRUE(WriteFile(test_file(), "content"));
219 ASSERT_TRUE(WaitForEvents());
220 DeleteDelegateOnFileThread(delegate
.release());
223 // Verify that modifying the file is caught.
224 TEST_F(FilePathWatcherTest
, ModifiedFile
) {
225 ASSERT_TRUE(WriteFile(test_file(), "content"));
227 FilePathWatcher watcher
;
228 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
229 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
231 // Now make sure we get notified if the file is modified.
232 ASSERT_TRUE(WriteFile(test_file(), "new content"));
233 ASSERT_TRUE(WaitForEvents());
234 DeleteDelegateOnFileThread(delegate
.release());
237 // Verify that moving the file into place is caught.
238 TEST_F(FilePathWatcherTest
, MovedFile
) {
239 FilePath
source_file(temp_dir_
.path().AppendASCII("source"));
240 ASSERT_TRUE(WriteFile(source_file
, "content"));
242 FilePathWatcher watcher
;
243 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
244 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
246 // Now make sure we get notified if the file is modified.
247 ASSERT_TRUE(base::Move(source_file
, test_file()));
248 ASSERT_TRUE(WaitForEvents());
249 DeleteDelegateOnFileThread(delegate
.release());
252 TEST_F(FilePathWatcherTest
, DeletedFile
) {
253 ASSERT_TRUE(WriteFile(test_file(), "content"));
255 FilePathWatcher watcher
;
256 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
257 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
259 // Now make sure we get notified if the file is deleted.
260 base::DeleteFile(test_file(), false);
261 ASSERT_TRUE(WaitForEvents());
262 DeleteDelegateOnFileThread(delegate
.release());
265 // Used by the DeleteDuringNotify test below.
266 // Deletes the FilePathWatcher when it's notified.
267 class Deleter
: public TestDelegateBase
{
269 Deleter(FilePathWatcher
* watcher
, MessageLoop
* loop
)
273 ~Deleter() override
{}
275 void OnFileChanged(const FilePath
&, bool) override
{
277 loop_
->PostTask(FROM_HERE
, MessageLoop::QuitWhenIdleClosure());
280 FilePathWatcher
* watcher() const { return watcher_
.get(); }
283 scoped_ptr
<FilePathWatcher
> watcher_
;
286 DISALLOW_COPY_AND_ASSIGN(Deleter
);
289 // Verify that deleting a watcher during the callback doesn't crash.
290 TEST_F(FilePathWatcherTest
, DeleteDuringNotify
) {
291 FilePathWatcher
* watcher
= new FilePathWatcher
;
292 // Takes ownership of watcher.
293 scoped_ptr
<Deleter
> deleter(new Deleter(watcher
, &loop_
));
294 ASSERT_TRUE(SetupWatch(test_file(), watcher
, deleter
.get(), false));
296 ASSERT_TRUE(WriteFile(test_file(), "content"));
297 ASSERT_TRUE(WaitForEvents());
299 // We win if we haven't crashed yet.
300 // Might as well double-check it got deleted, too.
301 ASSERT_TRUE(deleter
->watcher() == NULL
);
304 // Verify that deleting the watcher works even if there is a pending
306 // Flaky on MacOS (and ARM linux): http://crbug.com/85930
307 TEST_F(FilePathWatcherTest
, DISABLED_DestroyWithPendingNotification
) {
308 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
309 FilePathWatcher
* watcher
= new FilePathWatcher
;
310 ASSERT_TRUE(SetupWatch(test_file(), watcher
, delegate
.get(), false));
311 ASSERT_TRUE(WriteFile(test_file(), "content"));
312 file_thread_
.message_loop_proxy()->DeleteSoon(FROM_HERE
, watcher
);
313 DeleteDelegateOnFileThread(delegate
.release());
316 TEST_F(FilePathWatcherTest
, MultipleWatchersSingleFile
) {
317 FilePathWatcher watcher1
, watcher2
;
318 scoped_ptr
<TestDelegate
> delegate1(new TestDelegate(collector()));
319 scoped_ptr
<TestDelegate
> delegate2(new TestDelegate(collector()));
320 ASSERT_TRUE(SetupWatch(test_file(), &watcher1
, delegate1
.get(), false));
321 ASSERT_TRUE(SetupWatch(test_file(), &watcher2
, delegate2
.get(), false));
323 ASSERT_TRUE(WriteFile(test_file(), "content"));
324 ASSERT_TRUE(WaitForEvents());
325 DeleteDelegateOnFileThread(delegate1
.release());
326 DeleteDelegateOnFileThread(delegate2
.release());
329 // Verify that watching a file whose parent directory doesn't exist yet works if
330 // the directory and file are created eventually.
331 TEST_F(FilePathWatcherTest
, NonExistentDirectory
) {
332 FilePathWatcher watcher
;
333 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
334 FilePath
file(dir
.AppendASCII("file"));
335 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
336 ASSERT_TRUE(SetupWatch(file
, &watcher
, delegate
.get(), false));
338 ASSERT_TRUE(base::CreateDirectory(dir
));
340 ASSERT_TRUE(WriteFile(file
, "content"));
342 VLOG(1) << "Waiting for file creation";
343 ASSERT_TRUE(WaitForEvents());
345 ASSERT_TRUE(WriteFile(file
, "content v2"));
346 VLOG(1) << "Waiting for file change";
347 ASSERT_TRUE(WaitForEvents());
349 ASSERT_TRUE(base::DeleteFile(file
, false));
350 VLOG(1) << "Waiting for file deletion";
351 ASSERT_TRUE(WaitForEvents());
352 DeleteDelegateOnFileThread(delegate
.release());
355 // Exercises watch reconfiguration for the case that directories on the path
356 // are rapidly created.
357 TEST_F(FilePathWatcherTest
, DirectoryChain
) {
358 FilePath
path(temp_dir_
.path());
359 std::vector
<std::string
> dir_names
;
360 for (int i
= 0; i
< 20; i
++) {
361 std::string
dir(base::StringPrintf("d%d", i
));
362 dir_names
.push_back(dir
);
363 path
= path
.AppendASCII(dir
);
366 FilePathWatcher watcher
;
367 FilePath
file(path
.AppendASCII("file"));
368 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
369 ASSERT_TRUE(SetupWatch(file
, &watcher
, delegate
.get(), false));
371 FilePath
sub_path(temp_dir_
.path());
372 for (std::vector
<std::string
>::const_iterator
d(dir_names
.begin());
373 d
!= dir_names
.end(); ++d
) {
374 sub_path
= sub_path
.AppendASCII(*d
);
375 ASSERT_TRUE(base::CreateDirectory(sub_path
));
377 VLOG(1) << "Create File";
378 ASSERT_TRUE(WriteFile(file
, "content"));
379 VLOG(1) << "Waiting for file creation";
380 ASSERT_TRUE(WaitForEvents());
382 ASSERT_TRUE(WriteFile(file
, "content v2"));
383 VLOG(1) << "Waiting for file modification";
384 ASSERT_TRUE(WaitForEvents());
385 DeleteDelegateOnFileThread(delegate
.release());
388 #if defined(OS_MACOSX)
389 // http://crbug.com/85930
390 #define DisappearingDirectory DISABLED_DisappearingDirectory
392 TEST_F(FilePathWatcherTest
, DisappearingDirectory
) {
393 FilePathWatcher watcher
;
394 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
395 FilePath
file(dir
.AppendASCII("file"));
396 ASSERT_TRUE(base::CreateDirectory(dir
));
397 ASSERT_TRUE(WriteFile(file
, "content"));
398 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
399 ASSERT_TRUE(SetupWatch(file
, &watcher
, delegate
.get(), false));
401 ASSERT_TRUE(base::DeleteFile(dir
, true));
402 ASSERT_TRUE(WaitForEvents());
403 DeleteDelegateOnFileThread(delegate
.release());
406 // Tests that a file that is deleted and reappears is tracked correctly.
407 TEST_F(FilePathWatcherTest
, DeleteAndRecreate
) {
408 ASSERT_TRUE(WriteFile(test_file(), "content"));
409 FilePathWatcher watcher
;
410 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
411 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
413 ASSERT_TRUE(base::DeleteFile(test_file(), false));
414 VLOG(1) << "Waiting for file deletion";
415 ASSERT_TRUE(WaitForEvents());
417 ASSERT_TRUE(WriteFile(test_file(), "content"));
418 VLOG(1) << "Waiting for file creation";
419 ASSERT_TRUE(WaitForEvents());
420 DeleteDelegateOnFileThread(delegate
.release());
423 TEST_F(FilePathWatcherTest
, WatchDirectory
) {
424 FilePathWatcher watcher
;
425 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
426 FilePath
file1(dir
.AppendASCII("file1"));
427 FilePath
file2(dir
.AppendASCII("file2"));
428 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
429 ASSERT_TRUE(SetupWatch(dir
, &watcher
, delegate
.get(), false));
431 ASSERT_TRUE(base::CreateDirectory(dir
));
432 VLOG(1) << "Waiting for directory creation";
433 ASSERT_TRUE(WaitForEvents());
435 ASSERT_TRUE(WriteFile(file1
, "content"));
436 VLOG(1) << "Waiting for file1 creation";
437 ASSERT_TRUE(WaitForEvents());
439 #if !defined(OS_MACOSX)
440 // Mac implementation does not detect files modified in a directory.
441 ASSERT_TRUE(WriteFile(file1
, "content v2"));
442 VLOG(1) << "Waiting for file1 modification";
443 ASSERT_TRUE(WaitForEvents());
446 ASSERT_TRUE(base::DeleteFile(file1
, false));
447 VLOG(1) << "Waiting for file1 deletion";
448 ASSERT_TRUE(WaitForEvents());
450 ASSERT_TRUE(WriteFile(file2
, "content"));
451 VLOG(1) << "Waiting for file2 creation";
452 ASSERT_TRUE(WaitForEvents());
453 DeleteDelegateOnFileThread(delegate
.release());
456 TEST_F(FilePathWatcherTest
, MoveParent
) {
457 FilePathWatcher file_watcher
;
458 FilePathWatcher subdir_watcher
;
459 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
460 FilePath
dest(temp_dir_
.path().AppendASCII("dest"));
461 FilePath
subdir(dir
.AppendASCII("subdir"));
462 FilePath
file(subdir
.AppendASCII("file"));
463 scoped_ptr
<TestDelegate
> file_delegate(new TestDelegate(collector()));
464 ASSERT_TRUE(SetupWatch(file
, &file_watcher
, file_delegate
.get(), false));
465 scoped_ptr
<TestDelegate
> subdir_delegate(new TestDelegate(collector()));
466 ASSERT_TRUE(SetupWatch(subdir
, &subdir_watcher
, subdir_delegate
.get(),
469 // Setup a directory hierarchy.
470 ASSERT_TRUE(base::CreateDirectory(subdir
));
471 ASSERT_TRUE(WriteFile(file
, "content"));
472 VLOG(1) << "Waiting for file creation";
473 ASSERT_TRUE(WaitForEvents());
475 // Move the parent directory.
476 base::Move(dir
, dest
);
477 VLOG(1) << "Waiting for directory move";
478 ASSERT_TRUE(WaitForEvents());
479 DeleteDelegateOnFileThread(file_delegate
.release());
480 DeleteDelegateOnFileThread(subdir_delegate
.release());
483 TEST_F(FilePathWatcherTest
, RecursiveWatch
) {
484 FilePathWatcher watcher
;
485 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
486 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
487 bool setup_result
= SetupWatch(dir
, &watcher
, delegate
.get(), true);
488 if (!FilePathWatcher::RecursiveWatchAvailable()) {
489 ASSERT_FALSE(setup_result
);
490 DeleteDelegateOnFileThread(delegate
.release());
493 ASSERT_TRUE(setup_result
);
495 // Main directory("dir") creation.
496 ASSERT_TRUE(base::CreateDirectory(dir
));
497 ASSERT_TRUE(WaitForEvents());
499 // Create "$dir/file1".
500 FilePath
file1(dir
.AppendASCII("file1"));
501 ASSERT_TRUE(WriteFile(file1
, "content"));
502 ASSERT_TRUE(WaitForEvents());
504 // Create "$dir/subdir".
505 FilePath
subdir(dir
.AppendASCII("subdir"));
506 ASSERT_TRUE(base::CreateDirectory(subdir
));
507 ASSERT_TRUE(WaitForEvents());
509 // Create "$dir/subdir/subdir_file1".
510 FilePath
subdir_file1(subdir
.AppendASCII("subdir_file1"));
511 ASSERT_TRUE(WriteFile(subdir_file1
, "content"));
512 ASSERT_TRUE(WaitForEvents());
514 // Create "$dir/subdir/subdir_child_dir".
515 FilePath
subdir_child_dir(subdir
.AppendASCII("subdir_child_dir"));
516 ASSERT_TRUE(base::CreateDirectory(subdir_child_dir
));
517 ASSERT_TRUE(WaitForEvents());
519 // Create "$dir/subdir/subdir_child_dir/child_dir_file1".
520 FilePath
child_dir_file1(subdir_child_dir
.AppendASCII("child_dir_file1"));
521 ASSERT_TRUE(WriteFile(child_dir_file1
, "content v2"));
522 ASSERT_TRUE(WaitForEvents());
524 // Write into "$dir/subdir/subdir_child_dir/child_dir_file1".
525 ASSERT_TRUE(WriteFile(child_dir_file1
, "content"));
526 ASSERT_TRUE(WaitForEvents());
528 // Modify "$dir/subdir/subdir_child_dir/child_dir_file1" attributes.
529 ASSERT_TRUE(base::MakeFileUnreadable(child_dir_file1
));
530 ASSERT_TRUE(WaitForEvents());
532 // Delete "$dir/subdir/subdir_file1".
533 ASSERT_TRUE(base::DeleteFile(subdir_file1
, false));
534 ASSERT_TRUE(WaitForEvents());
536 // Delete "$dir/subdir/subdir_child_dir/child_dir_file1".
537 ASSERT_TRUE(base::DeleteFile(child_dir_file1
, false));
538 ASSERT_TRUE(WaitForEvents());
539 DeleteDelegateOnFileThread(delegate
.release());
542 #if defined(OS_POSIX)
543 TEST_F(FilePathWatcherTest
, RecursiveWithSymLink
) {
544 if (!FilePathWatcher::RecursiveWatchAvailable())
547 FilePathWatcher watcher
;
548 FilePath
test_dir(temp_dir_
.path().AppendASCII("test_dir"));
549 ASSERT_TRUE(base::CreateDirectory(test_dir
));
550 FilePath
symlink(test_dir
.AppendASCII("symlink"));
551 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
552 ASSERT_TRUE(SetupWatch(symlink
, &watcher
, delegate
.get(), true));
555 FilePath
target1(temp_dir_
.path().AppendASCII("target1"));
556 ASSERT_TRUE(base::CreateSymbolicLink(target1
, symlink
));
557 ASSERT_TRUE(WaitForEvents());
560 ASSERT_TRUE(base::CreateDirectory(target1
));
561 ASSERT_TRUE(WaitForEvents());
563 // Create a file in target1.
564 FilePath
target1_file(target1
.AppendASCII("file"));
565 ASSERT_TRUE(WriteFile(target1_file
, "content"));
566 ASSERT_TRUE(WaitForEvents());
569 FilePath
target2(temp_dir_
.path().AppendASCII("target2"));
570 ASSERT_TRUE(base::CreateDirectory(target2
));
571 ASSERT_TRUE(base::DeleteFile(symlink
, false));
572 ASSERT_TRUE(base::CreateSymbolicLink(target2
, symlink
));
573 ASSERT_TRUE(WaitForEvents());
575 // Create a file in target2.
576 FilePath
target2_file(target2
.AppendASCII("file"));
577 ASSERT_TRUE(WriteFile(target2_file
, "content"));
578 ASSERT_TRUE(WaitForEvents());
580 DeleteDelegateOnFileThread(delegate
.release());
584 TEST_F(FilePathWatcherTest
, MoveChild
) {
585 FilePathWatcher file_watcher
;
586 FilePathWatcher subdir_watcher
;
587 FilePath
source_dir(temp_dir_
.path().AppendASCII("source"));
588 FilePath
source_subdir(source_dir
.AppendASCII("subdir"));
589 FilePath
source_file(source_subdir
.AppendASCII("file"));
590 FilePath
dest_dir(temp_dir_
.path().AppendASCII("dest"));
591 FilePath
dest_subdir(dest_dir
.AppendASCII("subdir"));
592 FilePath
dest_file(dest_subdir
.AppendASCII("file"));
594 // Setup a directory hierarchy.
595 ASSERT_TRUE(base::CreateDirectory(source_subdir
));
596 ASSERT_TRUE(WriteFile(source_file
, "content"));
598 scoped_ptr
<TestDelegate
> file_delegate(new TestDelegate(collector()));
599 ASSERT_TRUE(SetupWatch(dest_file
, &file_watcher
, file_delegate
.get(), false));
600 scoped_ptr
<TestDelegate
> subdir_delegate(new TestDelegate(collector()));
601 ASSERT_TRUE(SetupWatch(dest_subdir
, &subdir_watcher
, subdir_delegate
.get(),
604 // Move the directory into place, s.t. the watched file appears.
605 ASSERT_TRUE(base::Move(source_dir
, dest_dir
));
606 ASSERT_TRUE(WaitForEvents());
607 DeleteDelegateOnFileThread(file_delegate
.release());
608 DeleteDelegateOnFileThread(subdir_delegate
.release());
611 // Verify that changing attributes on a file is caught
612 TEST_F(FilePathWatcherTest
, FileAttributesChanged
) {
613 ASSERT_TRUE(WriteFile(test_file(), "content"));
614 FilePathWatcher watcher
;
615 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
616 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
618 // Now make sure we get notified if the file is modified.
619 ASSERT_TRUE(base::MakeFileUnreadable(test_file()));
620 ASSERT_TRUE(WaitForEvents());
621 DeleteDelegateOnFileThread(delegate
.release());
624 #if defined(OS_LINUX)
626 // Verify that creating a symlink is caught.
627 TEST_F(FilePathWatcherTest
, CreateLink
) {
628 FilePathWatcher watcher
;
629 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
630 // Note that we are watching the symlink
631 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
633 // Now make sure we get notified if the link is created.
634 // Note that test_file() doesn't have to exist.
635 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
636 ASSERT_TRUE(WaitForEvents());
637 DeleteDelegateOnFileThread(delegate
.release());
640 // Verify that deleting a symlink is caught.
641 TEST_F(FilePathWatcherTest
, DeleteLink
) {
642 // Unfortunately this test case only works if the link target exists.
643 // TODO(craig) fix this as part of crbug.com/91561.
644 ASSERT_TRUE(WriteFile(test_file(), "content"));
645 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
646 FilePathWatcher watcher
;
647 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
648 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
650 // Now make sure we get notified if the link is deleted.
651 ASSERT_TRUE(base::DeleteFile(test_link(), false));
652 ASSERT_TRUE(WaitForEvents());
653 DeleteDelegateOnFileThread(delegate
.release());
656 // Verify that modifying a target file that a link is pointing to
657 // when we are watching the link is caught.
658 TEST_F(FilePathWatcherTest
, ModifiedLinkedFile
) {
659 ASSERT_TRUE(WriteFile(test_file(), "content"));
660 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
661 FilePathWatcher watcher
;
662 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
663 // Note that we are watching the symlink.
664 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
666 // Now make sure we get notified if the file is modified.
667 ASSERT_TRUE(WriteFile(test_file(), "new content"));
668 ASSERT_TRUE(WaitForEvents());
669 DeleteDelegateOnFileThread(delegate
.release());
672 // Verify that creating a target file that a link is pointing to
673 // when we are watching the link is caught.
674 TEST_F(FilePathWatcherTest
, CreateTargetLinkedFile
) {
675 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
676 FilePathWatcher watcher
;
677 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
678 // Note that we are watching the symlink.
679 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
681 // Now make sure we get notified if the target file is created.
682 ASSERT_TRUE(WriteFile(test_file(), "content"));
683 ASSERT_TRUE(WaitForEvents());
684 DeleteDelegateOnFileThread(delegate
.release());
687 // Verify that deleting a target file that a link is pointing to
688 // when we are watching the link is caught.
689 TEST_F(FilePathWatcherTest
, DeleteTargetLinkedFile
) {
690 ASSERT_TRUE(WriteFile(test_file(), "content"));
691 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
692 FilePathWatcher watcher
;
693 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
694 // Note that we are watching the symlink.
695 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
697 // Now make sure we get notified if the target file is deleted.
698 ASSERT_TRUE(base::DeleteFile(test_file(), false));
699 ASSERT_TRUE(WaitForEvents());
700 DeleteDelegateOnFileThread(delegate
.release());
703 // Verify that watching a file whose parent directory is a link that
704 // doesn't exist yet works if the symlink is created eventually.
705 TEST_F(FilePathWatcherTest
, LinkedDirectoryPart1
) {
706 FilePathWatcher watcher
;
707 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
708 FilePath
link_dir(temp_dir_
.path().AppendASCII("dir.lnk"));
709 FilePath
file(dir
.AppendASCII("file"));
710 FilePath
linkfile(link_dir
.AppendASCII("file"));
711 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
712 // dir/file should exist.
713 ASSERT_TRUE(base::CreateDirectory(dir
));
714 ASSERT_TRUE(WriteFile(file
, "content"));
715 // Note that we are watching dir.lnk/file which doesn't exist yet.
716 ASSERT_TRUE(SetupWatch(linkfile
, &watcher
, delegate
.get(), false));
718 ASSERT_TRUE(CreateSymbolicLink(dir
, link_dir
));
719 VLOG(1) << "Waiting for link creation";
720 ASSERT_TRUE(WaitForEvents());
722 ASSERT_TRUE(WriteFile(file
, "content v2"));
723 VLOG(1) << "Waiting for file change";
724 ASSERT_TRUE(WaitForEvents());
726 ASSERT_TRUE(base::DeleteFile(file
, false));
727 VLOG(1) << "Waiting for file deletion";
728 ASSERT_TRUE(WaitForEvents());
729 DeleteDelegateOnFileThread(delegate
.release());
732 // Verify that watching a file whose parent directory is a
733 // dangling symlink works if the directory is created eventually.
734 TEST_F(FilePathWatcherTest
, LinkedDirectoryPart2
) {
735 FilePathWatcher watcher
;
736 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
737 FilePath
link_dir(temp_dir_
.path().AppendASCII("dir.lnk"));
738 FilePath
file(dir
.AppendASCII("file"));
739 FilePath
linkfile(link_dir
.AppendASCII("file"));
740 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
741 // Now create the link from dir.lnk pointing to dir but
742 // neither dir nor dir/file exist yet.
743 ASSERT_TRUE(CreateSymbolicLink(dir
, link_dir
));
744 // Note that we are watching dir.lnk/file.
745 ASSERT_TRUE(SetupWatch(linkfile
, &watcher
, delegate
.get(), false));
747 ASSERT_TRUE(base::CreateDirectory(dir
));
748 ASSERT_TRUE(WriteFile(file
, "content"));
749 VLOG(1) << "Waiting for dir/file creation";
750 ASSERT_TRUE(WaitForEvents());
752 ASSERT_TRUE(WriteFile(file
, "content v2"));
753 VLOG(1) << "Waiting for file change";
754 ASSERT_TRUE(WaitForEvents());
756 ASSERT_TRUE(base::DeleteFile(file
, false));
757 VLOG(1) << "Waiting for file deletion";
758 ASSERT_TRUE(WaitForEvents());
759 DeleteDelegateOnFileThread(delegate
.release());
762 // Verify that watching a file with a symlink on the path
763 // to the file works.
764 TEST_F(FilePathWatcherTest
, LinkedDirectoryPart3
) {
765 FilePathWatcher watcher
;
766 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
767 FilePath
link_dir(temp_dir_
.path().AppendASCII("dir.lnk"));
768 FilePath
file(dir
.AppendASCII("file"));
769 FilePath
linkfile(link_dir
.AppendASCII("file"));
770 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
771 ASSERT_TRUE(base::CreateDirectory(dir
));
772 ASSERT_TRUE(CreateSymbolicLink(dir
, link_dir
));
773 // Note that we are watching dir.lnk/file but the file doesn't exist yet.
774 ASSERT_TRUE(SetupWatch(linkfile
, &watcher
, delegate
.get(), false));
776 ASSERT_TRUE(WriteFile(file
, "content"));
777 VLOG(1) << "Waiting for file creation";
778 ASSERT_TRUE(WaitForEvents());
780 ASSERT_TRUE(WriteFile(file
, "content v2"));
781 VLOG(1) << "Waiting for file change";
782 ASSERT_TRUE(WaitForEvents());
784 ASSERT_TRUE(base::DeleteFile(file
, false));
785 VLOG(1) << "Waiting for file deletion";
786 ASSERT_TRUE(WaitForEvents());
787 DeleteDelegateOnFileThread(delegate
.release());
798 #if defined(OS_MACOSX)
799 bool ChangeFilePermissions(const FilePath
& path
, Permission perm
, bool allow
) {
800 struct stat stat_buf
;
802 if (stat(path
.value().c_str(), &stat_buf
) != 0)
808 mode
= S_IRUSR
| S_IRGRP
| S_IROTH
;
811 mode
= S_IWUSR
| S_IWGRP
| S_IWOTH
;
814 mode
= S_IXUSR
| S_IXGRP
| S_IXOTH
;
817 ADD_FAILURE() << "unknown perm " << perm
;
821 stat_buf
.st_mode
|= mode
;
823 stat_buf
.st_mode
&= ~mode
;
825 return chmod(path
.value().c_str(), stat_buf
.st_mode
) == 0;
827 #endif // defined(OS_MACOSX)
829 #if defined(OS_MACOSX)
830 // Linux implementation of FilePathWatcher doesn't catch attribute changes.
831 // http://crbug.com/78043
832 // Windows implementation of FilePathWatcher catches attribute changes that
833 // don't affect the path being watched.
834 // http://crbug.com/78045
836 // Verify that changing attributes on a directory works.
837 TEST_F(FilePathWatcherTest
, DirAttributesChanged
) {
838 FilePath
test_dir1(temp_dir_
.path().AppendASCII("DirAttributesChangedDir1"));
839 FilePath
test_dir2(test_dir1
.AppendASCII("DirAttributesChangedDir2"));
840 FilePath
test_file(test_dir2
.AppendASCII("DirAttributesChangedFile"));
841 // Setup a directory hierarchy.
842 ASSERT_TRUE(base::CreateDirectory(test_dir1
));
843 ASSERT_TRUE(base::CreateDirectory(test_dir2
));
844 ASSERT_TRUE(WriteFile(test_file
, "content"));
846 FilePathWatcher watcher
;
847 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
848 ASSERT_TRUE(SetupWatch(test_file
, &watcher
, delegate
.get(), false));
850 // We should not get notified in this case as it hasn't affected our ability
851 // to access the file.
852 ASSERT_TRUE(ChangeFilePermissions(test_dir1
, Read
, false));
853 loop_
.PostDelayedTask(FROM_HERE
,
854 MessageLoop::QuitWhenIdleClosure(),
855 TestTimeouts::tiny_timeout());
856 ASSERT_FALSE(WaitForEvents());
857 ASSERT_TRUE(ChangeFilePermissions(test_dir1
, Read
, true));
859 // We should get notified in this case because filepathwatcher can no
860 // longer access the file
861 ASSERT_TRUE(ChangeFilePermissions(test_dir1
, Execute
, false));
862 ASSERT_TRUE(WaitForEvents());
863 ASSERT_TRUE(ChangeFilePermissions(test_dir1
, Execute
, true));
864 DeleteDelegateOnFileThread(delegate
.release());