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/file_util.h"
21 #include "base/files/file_path.h"
22 #include "base/files/scoped_temp_dir.h"
23 #include "base/message_loop.h"
24 #include "base/message_loop_proxy.h"
25 #include "base/run_loop.h"
26 #include "base/stl_util.h"
27 #include "base/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 virtual ~TestDelegate() {}
116 virtual 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 void QuitLoopWatchCallback(MessageLoop
* loop
,
142 const FilePath
& expected_path
,
145 const FilePath
& path
,
149 EXPECT_EQ(expected_path
, path
);
150 EXPECT_EQ(expected_error
, error
);
151 loop
->PostTask(FROM_HERE
, loop
->QuitWhenIdleClosure());
154 class FilePathWatcherTest
: public testing::Test
{
156 FilePathWatcherTest()
157 : file_thread_("FilePathWatcherTest") {}
159 virtual ~FilePathWatcherTest() {}
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();
170 virtual void TearDown() OVERRIDE
{
171 RunLoop().RunUntilIdle();
174 void DeleteDelegateOnFileThread(TestDelegate
* delegate
) {
175 file_thread_
.message_loop_proxy()->DeleteSoon(FROM_HERE
, delegate
);
178 FilePath
test_file() {
179 return temp_dir_
.path().AppendASCII("FilePathWatcherTest");
182 FilePath
test_link() {
183 return temp_dir_
.path().AppendASCII("FilePathWatcherTest.lnk");
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(),
190 return write_size
== static_cast<int>(content
.length());
193 bool SetupWatch(const FilePath
& target
,
194 FilePathWatcher
* watcher
,
195 TestDelegateBase
* delegate
,
196 bool recursive_watch
) WARN_UNUSED_RESULT
;
198 bool WaitForEvents() WARN_UNUSED_RESULT
{
201 return collector_
->Success();
204 NotificationCollector
* collector() { return collector_
.get(); }
207 base::Thread file_thread_
;
208 ScopedTempDir temp_dir_
;
209 scoped_refptr
<NotificationCollector
> collector_
;
211 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherTest
);
214 bool FilePathWatcherTest::SetupWatch(const FilePath
& target
,
215 FilePathWatcher
* watcher
,
216 TestDelegateBase
* delegate
,
217 bool recursive_watch
) {
218 base::WaitableEvent
completion(false, false);
220 file_thread_
.message_loop_proxy()->PostTask(
222 base::Bind(SetupWatchCallback
,
223 target
, watcher
, delegate
, recursive_watch
, &result
,
229 // Basic test: Create the file and verify that we notice.
230 TEST_F(FilePathWatcherTest
, NewFile
) {
231 FilePathWatcher watcher
;
232 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
233 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
235 ASSERT_TRUE(WriteFile(test_file(), "content"));
236 ASSERT_TRUE(WaitForEvents());
237 DeleteDelegateOnFileThread(delegate
.release());
240 // Verify that modifying the file is caught.
241 TEST_F(FilePathWatcherTest
, ModifiedFile
) {
242 ASSERT_TRUE(WriteFile(test_file(), "content"));
244 FilePathWatcher watcher
;
245 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
246 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
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());
254 // Verify that moving the file into place is caught.
255 TEST_F(FilePathWatcherTest
, MovedFile
) {
256 FilePath
source_file(temp_dir_
.path().AppendASCII("source"));
257 ASSERT_TRUE(WriteFile(source_file
, "content"));
259 FilePathWatcher watcher
;
260 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
261 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
263 // Now make sure we get notified if the file is modified.
264 ASSERT_TRUE(file_util::Move(source_file
, test_file()));
265 ASSERT_TRUE(WaitForEvents());
266 DeleteDelegateOnFileThread(delegate
.release());
269 TEST_F(FilePathWatcherTest
, DeletedFile
) {
270 ASSERT_TRUE(WriteFile(test_file(), "content"));
272 FilePathWatcher watcher
;
273 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
274 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
276 // Now make sure we get notified if the file is deleted.
277 file_util::Delete(test_file(), false);
278 ASSERT_TRUE(WaitForEvents());
279 DeleteDelegateOnFileThread(delegate
.release());
282 // Used by the DeleteDuringNotify test below.
283 // Deletes the FilePathWatcher when it's notified.
284 class Deleter
: public TestDelegateBase
{
286 Deleter(FilePathWatcher
* watcher
, MessageLoop
* loop
)
290 virtual ~Deleter() {}
292 virtual void OnFileChanged(const FilePath
&, bool) OVERRIDE
{
294 loop_
->PostTask(FROM_HERE
, MessageLoop::QuitWhenIdleClosure());
297 FilePathWatcher
* watcher() const { return watcher_
.get(); }
300 scoped_ptr
<FilePathWatcher
> watcher_
;
303 DISALLOW_COPY_AND_ASSIGN(Deleter
);
306 // Verify that deleting a watcher during the callback doesn't crash.
307 TEST_F(FilePathWatcherTest
, DeleteDuringNotify
) {
308 FilePathWatcher
* watcher
= new FilePathWatcher
;
309 // Takes ownership of watcher.
310 scoped_ptr
<Deleter
> deleter(new Deleter(watcher
, &loop_
));
311 ASSERT_TRUE(SetupWatch(test_file(), watcher
, deleter
.get(), false));
313 ASSERT_TRUE(WriteFile(test_file(), "content"));
314 ASSERT_TRUE(WaitForEvents());
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
);
321 // Verify that deleting the watcher works even if there is a pending
323 // Flaky on MacOS. http://crbug.com/85930
324 #if defined(OS_MACOSX)
325 #define MAYBE_DestroyWithPendingNotification \
326 DISABLED_DestroyWithPendingNotification
328 #define MAYBE_DestroyWithPendingNotification DestroyWithPendingNotification
330 TEST_F(FilePathWatcherTest
, MAYBE_DestroyWithPendingNotification
) {
331 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
332 FilePathWatcher
* watcher
= new FilePathWatcher
;
333 ASSERT_TRUE(SetupWatch(test_file(), watcher
, delegate
.get(), false));
334 ASSERT_TRUE(WriteFile(test_file(), "content"));
335 file_thread_
.message_loop_proxy()->DeleteSoon(FROM_HERE
, watcher
);
336 DeleteDelegateOnFileThread(delegate
.release());
339 TEST_F(FilePathWatcherTest
, MultipleWatchersSingleFile
) {
340 FilePathWatcher watcher1
, watcher2
;
341 scoped_ptr
<TestDelegate
> delegate1(new TestDelegate(collector()));
342 scoped_ptr
<TestDelegate
> delegate2(new TestDelegate(collector()));
343 ASSERT_TRUE(SetupWatch(test_file(), &watcher1
, delegate1
.get(), false));
344 ASSERT_TRUE(SetupWatch(test_file(), &watcher2
, delegate2
.get(), false));
346 ASSERT_TRUE(WriteFile(test_file(), "content"));
347 ASSERT_TRUE(WaitForEvents());
348 DeleteDelegateOnFileThread(delegate1
.release());
349 DeleteDelegateOnFileThread(delegate2
.release());
352 // Verify that watching a file whose parent directory doesn't exist yet works if
353 // the directory and file are created eventually.
354 TEST_F(FilePathWatcherTest
, NonExistentDirectory
) {
355 FilePathWatcher watcher
;
356 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
357 FilePath
file(dir
.AppendASCII("file"));
358 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
359 ASSERT_TRUE(SetupWatch(file
, &watcher
, delegate
.get(), false));
361 ASSERT_TRUE(file_util::CreateDirectory(dir
));
363 ASSERT_TRUE(WriteFile(file
, "content"));
365 VLOG(1) << "Waiting for file creation";
366 ASSERT_TRUE(WaitForEvents());
368 ASSERT_TRUE(WriteFile(file
, "content v2"));
369 VLOG(1) << "Waiting for file change";
370 ASSERT_TRUE(WaitForEvents());
372 ASSERT_TRUE(file_util::Delete(file
, false));
373 VLOG(1) << "Waiting for file deletion";
374 ASSERT_TRUE(WaitForEvents());
375 DeleteDelegateOnFileThread(delegate
.release());
378 // Exercises watch reconfiguration for the case that directories on the path
379 // are rapidly created.
380 TEST_F(FilePathWatcherTest
, DirectoryChain
) {
381 FilePath
path(temp_dir_
.path());
382 std::vector
<std::string
> dir_names
;
383 for (int i
= 0; i
< 20; i
++) {
384 std::string
dir(base::StringPrintf("d%d", i
));
385 dir_names
.push_back(dir
);
386 path
= path
.AppendASCII(dir
);
389 FilePathWatcher watcher
;
390 FilePath
file(path
.AppendASCII("file"));
391 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
392 ASSERT_TRUE(SetupWatch(file
, &watcher
, delegate
.get(), false));
394 FilePath
sub_path(temp_dir_
.path());
395 for (std::vector
<std::string
>::const_iterator
d(dir_names
.begin());
396 d
!= dir_names
.end(); ++d
) {
397 sub_path
= sub_path
.AppendASCII(*d
);
398 ASSERT_TRUE(file_util::CreateDirectory(sub_path
));
400 VLOG(1) << "Create File";
401 ASSERT_TRUE(WriteFile(file
, "content"));
402 VLOG(1) << "Waiting for file creation";
403 ASSERT_TRUE(WaitForEvents());
405 ASSERT_TRUE(WriteFile(file
, "content v2"));
406 VLOG(1) << "Waiting for file modification";
407 ASSERT_TRUE(WaitForEvents());
408 DeleteDelegateOnFileThread(delegate
.release());
411 #if defined(OS_MACOSX)
412 // http://crbug.com/85930
413 #define DisappearingDirectory DISABLED_DisappearingDirectory
415 TEST_F(FilePathWatcherTest
, DisappearingDirectory
) {
416 FilePathWatcher watcher
;
417 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
418 FilePath
file(dir
.AppendASCII("file"));
419 ASSERT_TRUE(file_util::CreateDirectory(dir
));
420 ASSERT_TRUE(WriteFile(file
, "content"));
421 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
422 ASSERT_TRUE(SetupWatch(file
, &watcher
, delegate
.get(), false));
424 ASSERT_TRUE(file_util::Delete(dir
, true));
425 ASSERT_TRUE(WaitForEvents());
426 DeleteDelegateOnFileThread(delegate
.release());
429 // Tests that a file that is deleted and reappears is tracked correctly.
430 TEST_F(FilePathWatcherTest
, DeleteAndRecreate
) {
431 ASSERT_TRUE(WriteFile(test_file(), "content"));
432 FilePathWatcher watcher
;
433 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
434 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
436 ASSERT_TRUE(file_util::Delete(test_file(), false));
437 VLOG(1) << "Waiting for file deletion";
438 ASSERT_TRUE(WaitForEvents());
440 ASSERT_TRUE(WriteFile(test_file(), "content"));
441 VLOG(1) << "Waiting for file creation";
442 ASSERT_TRUE(WaitForEvents());
443 DeleteDelegateOnFileThread(delegate
.release());
446 TEST_F(FilePathWatcherTest
, WatchDirectory
) {
447 FilePathWatcher watcher
;
448 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
449 FilePath
file1(dir
.AppendASCII("file1"));
450 FilePath
file2(dir
.AppendASCII("file2"));
451 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
452 ASSERT_TRUE(SetupWatch(dir
, &watcher
, delegate
.get(), false));
454 ASSERT_TRUE(file_util::CreateDirectory(dir
));
455 VLOG(1) << "Waiting for directory creation";
456 ASSERT_TRUE(WaitForEvents());
458 ASSERT_TRUE(WriteFile(file1
, "content"));
459 VLOG(1) << "Waiting for file1 creation";
460 ASSERT_TRUE(WaitForEvents());
462 #if !defined(OS_MACOSX)
463 // Mac implementation does not detect files modified in a directory.
464 ASSERT_TRUE(WriteFile(file1
, "content v2"));
465 VLOG(1) << "Waiting for file1 modification";
466 ASSERT_TRUE(WaitForEvents());
469 ASSERT_TRUE(file_util::Delete(file1
, false));
470 VLOG(1) << "Waiting for file1 deletion";
471 ASSERT_TRUE(WaitForEvents());
473 ASSERT_TRUE(WriteFile(file2
, "content"));
474 VLOG(1) << "Waiting for file2 creation";
475 ASSERT_TRUE(WaitForEvents());
476 DeleteDelegateOnFileThread(delegate
.release());
479 TEST_F(FilePathWatcherTest
, MoveParent
) {
480 FilePathWatcher file_watcher
;
481 FilePathWatcher subdir_watcher
;
482 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
483 FilePath
dest(temp_dir_
.path().AppendASCII("dest"));
484 FilePath
subdir(dir
.AppendASCII("subdir"));
485 FilePath
file(subdir
.AppendASCII("file"));
486 scoped_ptr
<TestDelegate
> file_delegate(new TestDelegate(collector()));
487 ASSERT_TRUE(SetupWatch(file
, &file_watcher
, file_delegate
.get(), false));
488 scoped_ptr
<TestDelegate
> subdir_delegate(new TestDelegate(collector()));
489 ASSERT_TRUE(SetupWatch(subdir
, &subdir_watcher
, subdir_delegate
.get(),
492 // Setup a directory hierarchy.
493 ASSERT_TRUE(file_util::CreateDirectory(subdir
));
494 ASSERT_TRUE(WriteFile(file
, "content"));
495 VLOG(1) << "Waiting for file creation";
496 ASSERT_TRUE(WaitForEvents());
498 // Move the parent directory.
499 file_util::Move(dir
, dest
);
500 VLOG(1) << "Waiting for directory move";
501 ASSERT_TRUE(WaitForEvents());
502 DeleteDelegateOnFileThread(file_delegate
.release());
503 DeleteDelegateOnFileThread(subdir_delegate
.release());
507 TEST_F(FilePathWatcherTest
, RecursiveWatch
) {
508 FilePathWatcher watcher
;
509 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
510 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
511 ASSERT_TRUE(SetupWatch(dir
, &watcher
, delegate
.get(), true));
513 // Main directory("dir") creation.
514 ASSERT_TRUE(file_util::CreateDirectory(dir
));
515 ASSERT_TRUE(WaitForEvents());
517 // Create "$dir/file1".
518 FilePath
file1(dir
.AppendASCII("file1"));
519 ASSERT_TRUE(WriteFile(file1
, "content"));
520 ASSERT_TRUE(WaitForEvents());
522 // Create "$dir/subdir".
523 FilePath
subdir(dir
.AppendASCII("subdir"));
524 ASSERT_TRUE(file_util::CreateDirectory(subdir
));
525 ASSERT_TRUE(WaitForEvents());
527 // Create "$dir/subdir/subdir_file1".
528 FilePath
subdir_file1(subdir
.AppendASCII("subdir_file1"));
529 ASSERT_TRUE(WriteFile(subdir_file1
, "content"));
530 ASSERT_TRUE(WaitForEvents());
532 // Create "$dir/subdir/subdir_child_dir".
533 FilePath
subdir_child_dir(subdir
.AppendASCII("subdir_child_dir"));
534 ASSERT_TRUE(file_util::CreateDirectory(subdir_child_dir
));
535 ASSERT_TRUE(WaitForEvents());
537 // Create "$dir/subdir/subdir_child_dir/child_dir_file1".
538 FilePath
child_dir_file1(subdir_child_dir
.AppendASCII("child_dir_file1"));
539 ASSERT_TRUE(WriteFile(child_dir_file1
, "content v2"));
540 ASSERT_TRUE(WaitForEvents());
542 // Write into "$dir/subdir/subdir_child_dir/child_dir_file1".
543 ASSERT_TRUE(WriteFile(child_dir_file1
, "content"));
544 ASSERT_TRUE(WaitForEvents());
546 // Modify "$dir/subdir/subdir_child_dir/child_dir_file1" attributes.
547 ASSERT_TRUE(file_util::MakeFileUnreadable(child_dir_file1
));
548 ASSERT_TRUE(WaitForEvents());
550 // Delete "$dir/subdir/subdir_file1".
551 ASSERT_TRUE(file_util::Delete(subdir_file1
, false));
552 ASSERT_TRUE(WaitForEvents());
554 // Delete "$dir/subdir/subdir_child_dir/child_dir_file1".
555 ASSERT_TRUE(file_util::Delete(child_dir_file1
, false));
556 ASSERT_TRUE(WaitForEvents());
557 DeleteDelegateOnFileThread(delegate
.release());
560 TEST_F(FilePathWatcherTest
, RecursiveWatch
) {
561 FilePathWatcher watcher
;
562 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
563 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
564 // Non-Windows implementaion does not support recursive watching.
565 ASSERT_FALSE(SetupWatch(dir
, &watcher
, delegate
.get(), true));
566 DeleteDelegateOnFileThread(delegate
.release());
570 TEST_F(FilePathWatcherTest
, MoveChild
) {
571 FilePathWatcher file_watcher
;
572 FilePathWatcher subdir_watcher
;
573 FilePath
source_dir(temp_dir_
.path().AppendASCII("source"));
574 FilePath
source_subdir(source_dir
.AppendASCII("subdir"));
575 FilePath
source_file(source_subdir
.AppendASCII("file"));
576 FilePath
dest_dir(temp_dir_
.path().AppendASCII("dest"));
577 FilePath
dest_subdir(dest_dir
.AppendASCII("subdir"));
578 FilePath
dest_file(dest_subdir
.AppendASCII("file"));
580 // Setup a directory hierarchy.
581 ASSERT_TRUE(file_util::CreateDirectory(source_subdir
));
582 ASSERT_TRUE(WriteFile(source_file
, "content"));
584 scoped_ptr
<TestDelegate
> file_delegate(new TestDelegate(collector()));
585 ASSERT_TRUE(SetupWatch(dest_file
, &file_watcher
, file_delegate
.get(), false));
586 scoped_ptr
<TestDelegate
> subdir_delegate(new TestDelegate(collector()));
587 ASSERT_TRUE(SetupWatch(dest_subdir
, &subdir_watcher
, subdir_delegate
.get(),
590 // Move the directory into place, s.t. the watched file appears.
591 ASSERT_TRUE(file_util::Move(source_dir
, dest_dir
));
592 ASSERT_TRUE(WaitForEvents());
593 DeleteDelegateOnFileThread(file_delegate
.release());
594 DeleteDelegateOnFileThread(subdir_delegate
.release());
597 #if !defined(OS_LINUX)
598 // Linux implementation of FilePathWatcher doesn't catch attribute changes.
599 // http://crbug.com/78043
601 // Verify that changing attributes on a file is caught
602 TEST_F(FilePathWatcherTest
, FileAttributesChanged
) {
603 ASSERT_TRUE(WriteFile(test_file(), "content"));
604 FilePathWatcher watcher
;
605 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
606 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
608 // Now make sure we get notified if the file is modified.
609 ASSERT_TRUE(file_util::MakeFileUnreadable(test_file()));
610 ASSERT_TRUE(WaitForEvents());
611 DeleteDelegateOnFileThread(delegate
.release());
616 #if defined(OS_LINUX)
618 // Verify that creating a symlink is caught.
619 TEST_F(FilePathWatcherTest
, CreateLink
) {
620 FilePathWatcher watcher
;
621 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
622 // Note that we are watching the symlink
623 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
625 // Now make sure we get notified if the link is created.
626 // Note that test_file() doesn't have to exist.
627 ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
628 ASSERT_TRUE(WaitForEvents());
629 DeleteDelegateOnFileThread(delegate
.release());
632 // Verify that deleting a symlink is caught.
633 TEST_F(FilePathWatcherTest
, DeleteLink
) {
634 // Unfortunately this test case only works if the link target exists.
635 // TODO(craig) fix this as part of crbug.com/91561.
636 ASSERT_TRUE(WriteFile(test_file(), "content"));
637 ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
638 FilePathWatcher watcher
;
639 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
640 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
642 // Now make sure we get notified if the link is deleted.
643 ASSERT_TRUE(file_util::Delete(test_link(), false));
644 ASSERT_TRUE(WaitForEvents());
645 DeleteDelegateOnFileThread(delegate
.release());
648 // Verify that modifying a target file that a link is pointing to
649 // when we are watching the link is caught.
650 TEST_F(FilePathWatcherTest
, ModifiedLinkedFile
) {
651 ASSERT_TRUE(WriteFile(test_file(), "content"));
652 ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
653 FilePathWatcher watcher
;
654 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
655 // Note that we are watching the symlink.
656 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
658 // Now make sure we get notified if the file is modified.
659 ASSERT_TRUE(WriteFile(test_file(), "new content"));
660 ASSERT_TRUE(WaitForEvents());
661 DeleteDelegateOnFileThread(delegate
.release());
664 // Verify that creating a target file that a link is pointing to
665 // when we are watching the link is caught.
666 TEST_F(FilePathWatcherTest
, CreateTargetLinkedFile
) {
667 ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
668 FilePathWatcher watcher
;
669 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
670 // Note that we are watching the symlink.
671 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
673 // Now make sure we get notified if the target file is created.
674 ASSERT_TRUE(WriteFile(test_file(), "content"));
675 ASSERT_TRUE(WaitForEvents());
676 DeleteDelegateOnFileThread(delegate
.release());
679 // Verify that deleting a target file that a link is pointing to
680 // when we are watching the link is caught.
681 TEST_F(FilePathWatcherTest
, DeleteTargetLinkedFile
) {
682 ASSERT_TRUE(WriteFile(test_file(), "content"));
683 ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
684 FilePathWatcher watcher
;
685 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
686 // Note that we are watching the symlink.
687 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
689 // Now make sure we get notified if the target file is deleted.
690 ASSERT_TRUE(file_util::Delete(test_file(), false));
691 ASSERT_TRUE(WaitForEvents());
692 DeleteDelegateOnFileThread(delegate
.release());
695 // Verify that watching a file whose parent directory is a link that
696 // doesn't exist yet works if the symlink is created eventually.
697 TEST_F(FilePathWatcherTest
, LinkedDirectoryPart1
) {
698 FilePathWatcher watcher
;
699 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
700 FilePath
link_dir(temp_dir_
.path().AppendASCII("dir.lnk"));
701 FilePath
file(dir
.AppendASCII("file"));
702 FilePath
linkfile(link_dir
.AppendASCII("file"));
703 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
704 // dir/file should exist.
705 ASSERT_TRUE(file_util::CreateDirectory(dir
));
706 ASSERT_TRUE(WriteFile(file
, "content"));
707 // Note that we are watching dir.lnk/file which doesn't exist yet.
708 ASSERT_TRUE(SetupWatch(linkfile
, &watcher
, delegate
.get(), false));
710 ASSERT_TRUE(file_util::CreateSymbolicLink(dir
, link_dir
));
711 VLOG(1) << "Waiting for link creation";
712 ASSERT_TRUE(WaitForEvents());
714 ASSERT_TRUE(WriteFile(file
, "content v2"));
715 VLOG(1) << "Waiting for file change";
716 ASSERT_TRUE(WaitForEvents());
718 ASSERT_TRUE(file_util::Delete(file
, false));
719 VLOG(1) << "Waiting for file deletion";
720 ASSERT_TRUE(WaitForEvents());
721 DeleteDelegateOnFileThread(delegate
.release());
724 // Verify that watching a file whose parent directory is a
725 // dangling symlink works if the directory is created eventually.
726 TEST_F(FilePathWatcherTest
, LinkedDirectoryPart2
) {
727 FilePathWatcher watcher
;
728 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
729 FilePath
link_dir(temp_dir_
.path().AppendASCII("dir.lnk"));
730 FilePath
file(dir
.AppendASCII("file"));
731 FilePath
linkfile(link_dir
.AppendASCII("file"));
732 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
733 // Now create the link from dir.lnk pointing to dir but
734 // neither dir nor dir/file exist yet.
735 ASSERT_TRUE(file_util::CreateSymbolicLink(dir
, link_dir
));
736 // Note that we are watching dir.lnk/file.
737 ASSERT_TRUE(SetupWatch(linkfile
, &watcher
, delegate
.get(), false));
739 ASSERT_TRUE(file_util::CreateDirectory(dir
));
740 ASSERT_TRUE(WriteFile(file
, "content"));
741 VLOG(1) << "Waiting for dir/file creation";
742 ASSERT_TRUE(WaitForEvents());
744 ASSERT_TRUE(WriteFile(file
, "content v2"));
745 VLOG(1) << "Waiting for file change";
746 ASSERT_TRUE(WaitForEvents());
748 ASSERT_TRUE(file_util::Delete(file
, false));
749 VLOG(1) << "Waiting for file deletion";
750 ASSERT_TRUE(WaitForEvents());
751 DeleteDelegateOnFileThread(delegate
.release());
754 // Verify that watching a file with a symlink on the path
755 // to the file works.
756 TEST_F(FilePathWatcherTest
, LinkedDirectoryPart3
) {
757 FilePathWatcher watcher
;
758 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
759 FilePath
link_dir(temp_dir_
.path().AppendASCII("dir.lnk"));
760 FilePath
file(dir
.AppendASCII("file"));
761 FilePath
linkfile(link_dir
.AppendASCII("file"));
762 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
763 ASSERT_TRUE(file_util::CreateDirectory(dir
));
764 ASSERT_TRUE(file_util::CreateSymbolicLink(dir
, link_dir
));
765 // Note that we are watching dir.lnk/file but the file doesn't exist yet.
766 ASSERT_TRUE(SetupWatch(linkfile
, &watcher
, delegate
.get(), false));
768 ASSERT_TRUE(WriteFile(file
, "content"));
769 VLOG(1) << "Waiting for file creation";
770 ASSERT_TRUE(WaitForEvents());
772 ASSERT_TRUE(WriteFile(file
, "content v2"));
773 VLOG(1) << "Waiting for file change";
774 ASSERT_TRUE(WaitForEvents());
776 ASSERT_TRUE(file_util::Delete(file
, false));
777 VLOG(1) << "Waiting for file deletion";
778 ASSERT_TRUE(WaitForEvents());
779 DeleteDelegateOnFileThread(delegate
.release());
790 bool ChangeFilePermissions(const FilePath
& path
, Permission perm
, bool allow
) {
791 #if defined(OS_POSIX)
792 struct stat stat_buf
;
794 if (stat(path
.value().c_str(), &stat_buf
) != 0)
800 mode
= S_IRUSR
| S_IRGRP
| S_IROTH
;
803 mode
= S_IWUSR
| S_IWGRP
| S_IWOTH
;
806 mode
= S_IXUSR
| S_IXGRP
| S_IXOTH
;
809 ADD_FAILURE() << "unknown perm " << perm
;
813 stat_buf
.st_mode
|= mode
;
815 stat_buf
.st_mode
&= ~mode
;
817 return chmod(path
.value().c_str(), stat_buf
.st_mode
) == 0;
819 #elif defined(OS_WIN)
821 PSECURITY_DESCRIPTOR security_descriptor
;
822 if (GetNamedSecurityInfo(const_cast<wchar_t*>(path
.value().c_str()),
824 DACL_SECURITY_INFORMATION
, NULL
, NULL
, &old_dacl
,
825 NULL
, &security_descriptor
) != ERROR_SUCCESS
)
834 mode
= GENERIC_WRITE
;
837 mode
= GENERIC_EXECUTE
;
840 ADD_FAILURE() << "unknown perm " << perm
;
844 // Deny Read access for the current user.
845 EXPLICIT_ACCESS change
;
846 change
.grfAccessPermissions
= mode
;
847 change
.grfAccessMode
= allow
? GRANT_ACCESS
: DENY_ACCESS
;
848 change
.grfInheritance
= 0;
849 change
.Trustee
.pMultipleTrustee
= NULL
;
850 change
.Trustee
.MultipleTrusteeOperation
= NO_MULTIPLE_TRUSTEE
;
851 change
.Trustee
.TrusteeForm
= TRUSTEE_IS_NAME
;
852 change
.Trustee
.TrusteeType
= TRUSTEE_IS_USER
;
853 change
.Trustee
.ptstrName
= L
"CURRENT_USER";
856 if (SetEntriesInAcl(1, &change
, old_dacl
, &new_dacl
) != ERROR_SUCCESS
) {
857 LocalFree(security_descriptor
);
861 DWORD rc
= SetNamedSecurityInfo(const_cast<wchar_t*>(path
.value().c_str()),
862 SE_FILE_OBJECT
, DACL_SECURITY_INFORMATION
,
863 NULL
, NULL
, new_dacl
, NULL
);
864 LocalFree(security_descriptor
);
867 return rc
== ERROR_SUCCESS
;
874 #if defined(OS_MACOSX)
875 // Linux implementation of FilePathWatcher doesn't catch attribute changes.
876 // http://crbug.com/78043
877 // Windows implementation of FilePathWatcher catches attribute changes that
878 // don't affect the path being watched.
879 // http://crbug.com/78045
881 // Verify that changing attributes on a directory works.
882 TEST_F(FilePathWatcherTest
, DirAttributesChanged
) {
883 FilePath
test_dir1(temp_dir_
.path().AppendASCII("DirAttributesChangedDir1"));
884 FilePath
test_dir2(test_dir1
.AppendASCII("DirAttributesChangedDir2"));
885 FilePath
test_file(test_dir2
.AppendASCII("DirAttributesChangedFile"));
886 // Setup a directory hierarchy.
887 ASSERT_TRUE(file_util::CreateDirectory(test_dir1
));
888 ASSERT_TRUE(file_util::CreateDirectory(test_dir2
));
889 ASSERT_TRUE(WriteFile(test_file
, "content"));
891 FilePathWatcher watcher
;
892 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
893 ASSERT_TRUE(SetupWatch(test_file
, &watcher
, delegate
.get(), false));
895 // We should not get notified in this case as it hasn't affected our ability
896 // to access the file.
897 ASSERT_TRUE(ChangeFilePermissions(test_dir1
, Read
, false));
898 loop_
.PostDelayedTask(FROM_HERE
,
899 MessageLoop::QuitWhenIdleClosure(),
900 TestTimeouts::tiny_timeout());
901 ASSERT_FALSE(WaitForEvents());
902 ASSERT_TRUE(ChangeFilePermissions(test_dir1
, Read
, true));
904 // We should get notified in this case because filepathwatcher can no
905 // longer access the file
906 ASSERT_TRUE(ChangeFilePermissions(test_dir1
, Execute
, false));
907 ASSERT_TRUE(WaitForEvents());
908 ASSERT_TRUE(ChangeFilePermissions(test_dir1
, Execute
, true));
909 DeleteDelegateOnFileThread(delegate
.release());