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/location.h"
24 #include "base/run_loop.h"
25 #include "base/single_thread_task_runner.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/thread_task_runner_handle.h"
32 #include "base/threading/thread.h"
33 #include "testing/gtest/include/gtest/gtest.h"
35 #if defined(OS_ANDROID)
36 #include "base/android/path_utils.h"
37 #endif // defined(OS_ANDROID)
45 // Aggregates notifications from the test delegates and breaks the message loop
46 // the test thread is waiting on once they all came in.
47 class NotificationCollector
48 : public base::RefCountedThreadSafe
<NotificationCollector
> {
50 NotificationCollector() : task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
52 // Called from the file thread by the delegates.
53 void OnChange(TestDelegate
* delegate
) {
54 task_runner_
->PostTask(
55 FROM_HERE
, base::Bind(&NotificationCollector::RecordChange
, this,
56 base::Unretained(delegate
)));
59 void Register(TestDelegate
* delegate
) {
60 delegates_
.insert(delegate
);
68 return signaled_
== delegates_
;
72 friend class base::RefCountedThreadSafe
<NotificationCollector
>;
73 ~NotificationCollector() {}
75 void RecordChange(TestDelegate
* delegate
) {
76 // Warning: |delegate| is Unretained. Do not dereference.
77 ASSERT_TRUE(task_runner_
->BelongsToCurrentThread());
78 ASSERT_TRUE(delegates_
.count(delegate
));
79 signaled_
.insert(delegate
);
81 // Check whether all delegates have been signaled.
82 if (signaled_
== delegates_
)
83 task_runner_
->PostTask(FROM_HERE
, MessageLoop::QuitWhenIdleClosure());
86 // Set of registered delegates.
87 std::set
<TestDelegate
*> delegates_
;
89 // Set of signaled delegates.
90 std::set
<TestDelegate
*> signaled_
;
92 // The loop we should break after all delegates signaled.
93 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
96 class TestDelegateBase
: public SupportsWeakPtr
<TestDelegateBase
> {
99 virtual ~TestDelegateBase() {}
101 virtual void OnFileChanged(const FilePath
& path
, bool error
) = 0;
104 DISALLOW_COPY_AND_ASSIGN(TestDelegateBase
);
107 // A mock class for testing. Gmock is not appropriate because it is not
108 // thread-safe for setting expectations. Thus the test code cannot safely
109 // reset expectations while the file watcher is running.
110 // Instead, TestDelegate gets the notifications from FilePathWatcher and uses
111 // NotificationCollector to aggregate the results.
112 class TestDelegate
: public TestDelegateBase
{
114 explicit TestDelegate(NotificationCollector
* collector
)
115 : collector_(collector
) {
116 collector_
->Register(this);
118 ~TestDelegate() override
{}
120 void OnFileChanged(const FilePath
& path
, bool error
) override
{
122 ADD_FAILURE() << "Error " << path
.value();
124 collector_
->OnChange(this);
128 scoped_refptr
<NotificationCollector
> collector_
;
130 DISALLOW_COPY_AND_ASSIGN(TestDelegate
);
133 void SetupWatchCallback(const FilePath
& target
,
134 FilePathWatcher
* watcher
,
135 TestDelegateBase
* delegate
,
136 bool recursive_watch
,
138 base::WaitableEvent
* completion
) {
139 *result
= watcher
->Watch(target
, recursive_watch
,
140 base::Bind(&TestDelegateBase::OnFileChanged
,
141 delegate
->AsWeakPtr()));
142 completion
->Signal();
145 class FilePathWatcherTest
: public testing::Test
{
147 FilePathWatcherTest()
148 : file_thread_("FilePathWatcherTest") {}
150 ~FilePathWatcherTest() override
{}
153 void SetUp() override
{
154 // Create a separate file thread in order to test proper thread usage.
155 base::Thread::Options
options(MessageLoop::TYPE_IO
, 0);
156 ASSERT_TRUE(file_thread_
.StartWithOptions(options
));
157 #if defined(OS_ANDROID)
158 // Watching files is only permitted when all parent directories are
159 // accessible, which is not the case for the default temp directory
160 // on Android which is under /data/data. Use /sdcard instead.
161 // TODO(pauljensen): Remove this when crbug.com/475568 is fixed.
163 ASSERT_TRUE(android::GetExternalStorageDirectory(&parent_dir
));
164 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDirUnderPath(parent_dir
));
165 #else // defined(OS_ANDROID)
166 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
167 #endif // defined(OS_ANDROID)
168 collector_
= new NotificationCollector();
171 void TearDown() override
{ RunLoop().RunUntilIdle(); }
173 void DeleteDelegateOnFileThread(TestDelegate
* delegate
) {
174 file_thread_
.task_runner()->DeleteSoon(FROM_HERE
, delegate
);
177 FilePath
test_file() {
178 return temp_dir_
.path().AppendASCII("FilePathWatcherTest");
181 FilePath
test_link() {
182 return temp_dir_
.path().AppendASCII("FilePathWatcherTest.lnk");
185 // Write |content| to |file|. Returns true on success.
186 bool WriteFile(const FilePath
& file
, const std::string
& content
) {
187 int write_size
= ::base::WriteFile(file
, content
.c_str(), content
.length());
188 return write_size
== static_cast<int>(content
.length());
191 bool SetupWatch(const FilePath
& target
,
192 FilePathWatcher
* watcher
,
193 TestDelegateBase
* delegate
,
194 bool recursive_watch
) WARN_UNUSED_RESULT
;
196 bool WaitForEvents() WARN_UNUSED_RESULT
{
199 return collector_
->Success();
202 NotificationCollector
* collector() { return collector_
.get(); }
205 base::Thread file_thread_
;
206 ScopedTempDir temp_dir_
;
207 scoped_refptr
<NotificationCollector
> collector_
;
210 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherTest
);
213 bool FilePathWatcherTest::SetupWatch(const FilePath
& target
,
214 FilePathWatcher
* watcher
,
215 TestDelegateBase
* delegate
,
216 bool recursive_watch
) {
217 base::WaitableEvent
completion(false, false);
219 file_thread_
.task_runner()->PostTask(
220 FROM_HERE
, base::Bind(SetupWatchCallback
, target
, watcher
, delegate
,
221 recursive_watch
, &result
, &completion
));
226 // Basic test: Create the file and verify that we notice.
227 TEST_F(FilePathWatcherTest
, NewFile
) {
228 FilePathWatcher watcher
;
229 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
230 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
232 ASSERT_TRUE(WriteFile(test_file(), "content"));
233 ASSERT_TRUE(WaitForEvents());
234 DeleteDelegateOnFileThread(delegate
.release());
237 // Verify that modifying the file is caught.
238 TEST_F(FilePathWatcherTest
, ModifiedFile
) {
239 ASSERT_TRUE(WriteFile(test_file(), "content"));
241 FilePathWatcher watcher
;
242 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
243 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
245 // Now make sure we get notified if the file is modified.
246 ASSERT_TRUE(WriteFile(test_file(), "new content"));
247 ASSERT_TRUE(WaitForEvents());
248 DeleteDelegateOnFileThread(delegate
.release());
251 // Verify that moving the file into place is caught.
252 TEST_F(FilePathWatcherTest
, MovedFile
) {
253 FilePath
source_file(temp_dir_
.path().AppendASCII("source"));
254 ASSERT_TRUE(WriteFile(source_file
, "content"));
256 FilePathWatcher watcher
;
257 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
258 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
260 // Now make sure we get notified if the file is modified.
261 ASSERT_TRUE(base::Move(source_file
, test_file()));
262 ASSERT_TRUE(WaitForEvents());
263 DeleteDelegateOnFileThread(delegate
.release());
266 TEST_F(FilePathWatcherTest
, DeletedFile
) {
267 ASSERT_TRUE(WriteFile(test_file(), "content"));
269 FilePathWatcher watcher
;
270 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
271 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
273 // Now make sure we get notified if the file is deleted.
274 base::DeleteFile(test_file(), false);
275 ASSERT_TRUE(WaitForEvents());
276 DeleteDelegateOnFileThread(delegate
.release());
279 // Used by the DeleteDuringNotify test below.
280 // Deletes the FilePathWatcher when it's notified.
281 class Deleter
: public TestDelegateBase
{
283 Deleter(FilePathWatcher
* watcher
, MessageLoop
* loop
)
287 ~Deleter() override
{}
289 void OnFileChanged(const FilePath
&, bool) override
{
291 loop_
->task_runner()->PostTask(FROM_HERE
,
292 MessageLoop::QuitWhenIdleClosure());
295 FilePathWatcher
* watcher() const { return watcher_
.get(); }
298 scoped_ptr
<FilePathWatcher
> watcher_
;
301 DISALLOW_COPY_AND_ASSIGN(Deleter
);
304 // Verify that deleting a watcher during the callback doesn't crash.
305 TEST_F(FilePathWatcherTest
, DeleteDuringNotify
) {
306 FilePathWatcher
* watcher
= new FilePathWatcher
;
307 // Takes ownership of watcher.
308 scoped_ptr
<Deleter
> deleter(new Deleter(watcher
, &loop_
));
309 ASSERT_TRUE(SetupWatch(test_file(), watcher
, deleter
.get(), false));
311 ASSERT_TRUE(WriteFile(test_file(), "content"));
312 ASSERT_TRUE(WaitForEvents());
314 // We win if we haven't crashed yet.
315 // Might as well double-check it got deleted, too.
316 ASSERT_TRUE(deleter
->watcher() == NULL
);
319 // Verify that deleting the watcher works even if there is a pending
321 // Flaky on MacOS (and ARM linux): http://crbug.com/85930
322 TEST_F(FilePathWatcherTest
, DISABLED_DestroyWithPendingNotification
) {
323 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
324 FilePathWatcher
* watcher
= new FilePathWatcher
;
325 ASSERT_TRUE(SetupWatch(test_file(), watcher
, delegate
.get(), false));
326 ASSERT_TRUE(WriteFile(test_file(), "content"));
327 file_thread_
.task_runner()->DeleteSoon(FROM_HERE
, watcher
);
328 DeleteDelegateOnFileThread(delegate
.release());
331 TEST_F(FilePathWatcherTest
, MultipleWatchersSingleFile
) {
332 FilePathWatcher watcher1
, watcher2
;
333 scoped_ptr
<TestDelegate
> delegate1(new TestDelegate(collector()));
334 scoped_ptr
<TestDelegate
> delegate2(new TestDelegate(collector()));
335 ASSERT_TRUE(SetupWatch(test_file(), &watcher1
, delegate1
.get(), false));
336 ASSERT_TRUE(SetupWatch(test_file(), &watcher2
, delegate2
.get(), false));
338 ASSERT_TRUE(WriteFile(test_file(), "content"));
339 ASSERT_TRUE(WaitForEvents());
340 DeleteDelegateOnFileThread(delegate1
.release());
341 DeleteDelegateOnFileThread(delegate2
.release());
344 // Verify that watching a file whose parent directory doesn't exist yet works if
345 // the directory and file are created eventually.
346 TEST_F(FilePathWatcherTest
, NonExistentDirectory
) {
347 FilePathWatcher watcher
;
348 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
349 FilePath
file(dir
.AppendASCII("file"));
350 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
351 ASSERT_TRUE(SetupWatch(file
, &watcher
, delegate
.get(), false));
353 ASSERT_TRUE(base::CreateDirectory(dir
));
355 ASSERT_TRUE(WriteFile(file
, "content"));
357 VLOG(1) << "Waiting for file creation";
358 ASSERT_TRUE(WaitForEvents());
360 ASSERT_TRUE(WriteFile(file
, "content v2"));
361 VLOG(1) << "Waiting for file change";
362 ASSERT_TRUE(WaitForEvents());
364 ASSERT_TRUE(base::DeleteFile(file
, false));
365 VLOG(1) << "Waiting for file deletion";
366 ASSERT_TRUE(WaitForEvents());
367 DeleteDelegateOnFileThread(delegate
.release());
370 // Exercises watch reconfiguration for the case that directories on the path
371 // are rapidly created.
372 TEST_F(FilePathWatcherTest
, DirectoryChain
) {
373 FilePath
path(temp_dir_
.path());
374 std::vector
<std::string
> dir_names
;
375 for (int i
= 0; i
< 20; i
++) {
376 std::string
dir(base::StringPrintf("d%d", i
));
377 dir_names
.push_back(dir
);
378 path
= path
.AppendASCII(dir
);
381 FilePathWatcher watcher
;
382 FilePath
file(path
.AppendASCII("file"));
383 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
384 ASSERT_TRUE(SetupWatch(file
, &watcher
, delegate
.get(), false));
386 FilePath
sub_path(temp_dir_
.path());
387 for (std::vector
<std::string
>::const_iterator
d(dir_names
.begin());
388 d
!= dir_names
.end(); ++d
) {
389 sub_path
= sub_path
.AppendASCII(*d
);
390 ASSERT_TRUE(base::CreateDirectory(sub_path
));
392 VLOG(1) << "Create File";
393 ASSERT_TRUE(WriteFile(file
, "content"));
394 VLOG(1) << "Waiting for file creation";
395 ASSERT_TRUE(WaitForEvents());
397 ASSERT_TRUE(WriteFile(file
, "content v2"));
398 VLOG(1) << "Waiting for file modification";
399 ASSERT_TRUE(WaitForEvents());
400 DeleteDelegateOnFileThread(delegate
.release());
403 #if defined(OS_MACOSX)
404 // http://crbug.com/85930
405 #define DisappearingDirectory DISABLED_DisappearingDirectory
407 TEST_F(FilePathWatcherTest
, DisappearingDirectory
) {
408 FilePathWatcher watcher
;
409 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
410 FilePath
file(dir
.AppendASCII("file"));
411 ASSERT_TRUE(base::CreateDirectory(dir
));
412 ASSERT_TRUE(WriteFile(file
, "content"));
413 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
414 ASSERT_TRUE(SetupWatch(file
, &watcher
, delegate
.get(), false));
416 ASSERT_TRUE(base::DeleteFile(dir
, true));
417 ASSERT_TRUE(WaitForEvents());
418 DeleteDelegateOnFileThread(delegate
.release());
421 // Tests that a file that is deleted and reappears is tracked correctly.
422 TEST_F(FilePathWatcherTest
, DeleteAndRecreate
) {
423 ASSERT_TRUE(WriteFile(test_file(), "content"));
424 FilePathWatcher watcher
;
425 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
426 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
428 ASSERT_TRUE(base::DeleteFile(test_file(), false));
429 VLOG(1) << "Waiting for file deletion";
430 ASSERT_TRUE(WaitForEvents());
432 ASSERT_TRUE(WriteFile(test_file(), "content"));
433 VLOG(1) << "Waiting for file creation";
434 ASSERT_TRUE(WaitForEvents());
435 DeleteDelegateOnFileThread(delegate
.release());
438 TEST_F(FilePathWatcherTest
, WatchDirectory
) {
439 FilePathWatcher watcher
;
440 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
441 FilePath
file1(dir
.AppendASCII("file1"));
442 FilePath
file2(dir
.AppendASCII("file2"));
443 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
444 ASSERT_TRUE(SetupWatch(dir
, &watcher
, delegate
.get(), false));
446 ASSERT_TRUE(base::CreateDirectory(dir
));
447 VLOG(1) << "Waiting for directory creation";
448 ASSERT_TRUE(WaitForEvents());
450 ASSERT_TRUE(WriteFile(file1
, "content"));
451 VLOG(1) << "Waiting for file1 creation";
452 ASSERT_TRUE(WaitForEvents());
454 #if !defined(OS_MACOSX)
455 // Mac implementation does not detect files modified in a directory.
456 ASSERT_TRUE(WriteFile(file1
, "content v2"));
457 VLOG(1) << "Waiting for file1 modification";
458 ASSERT_TRUE(WaitForEvents());
461 ASSERT_TRUE(base::DeleteFile(file1
, false));
462 VLOG(1) << "Waiting for file1 deletion";
463 ASSERT_TRUE(WaitForEvents());
465 ASSERT_TRUE(WriteFile(file2
, "content"));
466 VLOG(1) << "Waiting for file2 creation";
467 ASSERT_TRUE(WaitForEvents());
468 DeleteDelegateOnFileThread(delegate
.release());
471 TEST_F(FilePathWatcherTest
, MoveParent
) {
472 FilePathWatcher file_watcher
;
473 FilePathWatcher subdir_watcher
;
474 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
475 FilePath
dest(temp_dir_
.path().AppendASCII("dest"));
476 FilePath
subdir(dir
.AppendASCII("subdir"));
477 FilePath
file(subdir
.AppendASCII("file"));
478 scoped_ptr
<TestDelegate
> file_delegate(new TestDelegate(collector()));
479 ASSERT_TRUE(SetupWatch(file
, &file_watcher
, file_delegate
.get(), false));
480 scoped_ptr
<TestDelegate
> subdir_delegate(new TestDelegate(collector()));
481 ASSERT_TRUE(SetupWatch(subdir
, &subdir_watcher
, subdir_delegate
.get(),
484 // Setup a directory hierarchy.
485 ASSERT_TRUE(base::CreateDirectory(subdir
));
486 ASSERT_TRUE(WriteFile(file
, "content"));
487 VLOG(1) << "Waiting for file creation";
488 ASSERT_TRUE(WaitForEvents());
490 // Move the parent directory.
491 base::Move(dir
, dest
);
492 VLOG(1) << "Waiting for directory move";
493 ASSERT_TRUE(WaitForEvents());
494 DeleteDelegateOnFileThread(file_delegate
.release());
495 DeleteDelegateOnFileThread(subdir_delegate
.release());
498 TEST_F(FilePathWatcherTest
, RecursiveWatch
) {
499 FilePathWatcher watcher
;
500 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
501 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
502 bool setup_result
= SetupWatch(dir
, &watcher
, delegate
.get(), true);
503 if (!FilePathWatcher::RecursiveWatchAvailable()) {
504 ASSERT_FALSE(setup_result
);
505 DeleteDelegateOnFileThread(delegate
.release());
508 ASSERT_TRUE(setup_result
);
510 // Main directory("dir") creation.
511 ASSERT_TRUE(base::CreateDirectory(dir
));
512 ASSERT_TRUE(WaitForEvents());
514 // Create "$dir/file1".
515 FilePath
file1(dir
.AppendASCII("file1"));
516 ASSERT_TRUE(WriteFile(file1
, "content"));
517 ASSERT_TRUE(WaitForEvents());
519 // Create "$dir/subdir".
520 FilePath
subdir(dir
.AppendASCII("subdir"));
521 ASSERT_TRUE(base::CreateDirectory(subdir
));
522 ASSERT_TRUE(WaitForEvents());
524 // Create "$dir/subdir/subdir_file1".
525 FilePath
subdir_file1(subdir
.AppendASCII("subdir_file1"));
526 ASSERT_TRUE(WriteFile(subdir_file1
, "content"));
527 ASSERT_TRUE(WaitForEvents());
529 // Create "$dir/subdir/subdir_child_dir".
530 FilePath
subdir_child_dir(subdir
.AppendASCII("subdir_child_dir"));
531 ASSERT_TRUE(base::CreateDirectory(subdir_child_dir
));
532 ASSERT_TRUE(WaitForEvents());
534 // Create "$dir/subdir/subdir_child_dir/child_dir_file1".
535 FilePath
child_dir_file1(subdir_child_dir
.AppendASCII("child_dir_file1"));
536 ASSERT_TRUE(WriteFile(child_dir_file1
, "content v2"));
537 ASSERT_TRUE(WaitForEvents());
539 // Write into "$dir/subdir/subdir_child_dir/child_dir_file1".
540 ASSERT_TRUE(WriteFile(child_dir_file1
, "content"));
541 ASSERT_TRUE(WaitForEvents());
543 // Apps cannot change file attributes on Android in /sdcard as /sdcard uses the
544 // "fuse" file system, while /data uses "ext4". Running these tests in /data
545 // would be preferable and allow testing file attributes and symlinks.
546 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
547 // the |temp_dir_| in /data.
548 #if !defined(OS_ANDROID)
549 // Modify "$dir/subdir/subdir_child_dir/child_dir_file1" attributes.
550 ASSERT_TRUE(base::MakeFileUnreadable(child_dir_file1
));
551 ASSERT_TRUE(WaitForEvents());
554 // Delete "$dir/subdir/subdir_file1".
555 ASSERT_TRUE(base::DeleteFile(subdir_file1
, false));
556 ASSERT_TRUE(WaitForEvents());
558 // Delete "$dir/subdir/subdir_child_dir/child_dir_file1".
559 ASSERT_TRUE(base::DeleteFile(child_dir_file1
, false));
560 ASSERT_TRUE(WaitForEvents());
561 DeleteDelegateOnFileThread(delegate
.release());
564 #if defined(OS_POSIX)
565 #if defined(OS_ANDROID)
566 // Apps cannot create symlinks on Android in /sdcard as /sdcard uses the
567 // "fuse" file system, while /data uses "ext4". Running these tests in /data
568 // would be preferable and allow testing file attributes and symlinks.
569 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
570 // the |temp_dir_| in /data.
571 #define RecursiveWithSymLink DISABLED_RecursiveWithSymLink
572 #endif // defined(OS_ANDROID)
573 TEST_F(FilePathWatcherTest
, RecursiveWithSymLink
) {
574 if (!FilePathWatcher::RecursiveWatchAvailable())
577 FilePathWatcher watcher
;
578 FilePath
test_dir(temp_dir_
.path().AppendASCII("test_dir"));
579 ASSERT_TRUE(base::CreateDirectory(test_dir
));
580 FilePath
symlink(test_dir
.AppendASCII("symlink"));
581 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
582 ASSERT_TRUE(SetupWatch(symlink
, &watcher
, delegate
.get(), true));
585 FilePath
target1(temp_dir_
.path().AppendASCII("target1"));
586 ASSERT_TRUE(base::CreateSymbolicLink(target1
, symlink
));
587 ASSERT_TRUE(WaitForEvents());
590 ASSERT_TRUE(base::CreateDirectory(target1
));
591 ASSERT_TRUE(WaitForEvents());
593 // Create a file in target1.
594 FilePath
target1_file(target1
.AppendASCII("file"));
595 ASSERT_TRUE(WriteFile(target1_file
, "content"));
596 ASSERT_TRUE(WaitForEvents());
599 FilePath
target2(temp_dir_
.path().AppendASCII("target2"));
600 ASSERT_TRUE(base::CreateDirectory(target2
));
601 ASSERT_TRUE(base::DeleteFile(symlink
, false));
602 ASSERT_TRUE(base::CreateSymbolicLink(target2
, symlink
));
603 ASSERT_TRUE(WaitForEvents());
605 // Create a file in target2.
606 FilePath
target2_file(target2
.AppendASCII("file"));
607 ASSERT_TRUE(WriteFile(target2_file
, "content"));
608 ASSERT_TRUE(WaitForEvents());
610 DeleteDelegateOnFileThread(delegate
.release());
614 TEST_F(FilePathWatcherTest
, MoveChild
) {
615 FilePathWatcher file_watcher
;
616 FilePathWatcher subdir_watcher
;
617 FilePath
source_dir(temp_dir_
.path().AppendASCII("source"));
618 FilePath
source_subdir(source_dir
.AppendASCII("subdir"));
619 FilePath
source_file(source_subdir
.AppendASCII("file"));
620 FilePath
dest_dir(temp_dir_
.path().AppendASCII("dest"));
621 FilePath
dest_subdir(dest_dir
.AppendASCII("subdir"));
622 FilePath
dest_file(dest_subdir
.AppendASCII("file"));
624 // Setup a directory hierarchy.
625 ASSERT_TRUE(base::CreateDirectory(source_subdir
));
626 ASSERT_TRUE(WriteFile(source_file
, "content"));
628 scoped_ptr
<TestDelegate
> file_delegate(new TestDelegate(collector()));
629 ASSERT_TRUE(SetupWatch(dest_file
, &file_watcher
, file_delegate
.get(), false));
630 scoped_ptr
<TestDelegate
> subdir_delegate(new TestDelegate(collector()));
631 ASSERT_TRUE(SetupWatch(dest_subdir
, &subdir_watcher
, subdir_delegate
.get(),
634 // Move the directory into place, s.t. the watched file appears.
635 ASSERT_TRUE(base::Move(source_dir
, dest_dir
));
636 ASSERT_TRUE(WaitForEvents());
637 DeleteDelegateOnFileThread(file_delegate
.release());
638 DeleteDelegateOnFileThread(subdir_delegate
.release());
641 // Verify that changing attributes on a file is caught
642 #if defined(OS_ANDROID)
643 // Apps cannot change file attributes on Android in /sdcard as /sdcard uses the
644 // "fuse" file system, while /data uses "ext4". Running these tests in /data
645 // would be preferable and allow testing file attributes and symlinks.
646 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
647 // the |temp_dir_| in /data.
648 #define FileAttributesChanged DISABLED_FileAttributesChanged
649 #endif // defined(OS_ANDROID
650 TEST_F(FilePathWatcherTest
, FileAttributesChanged
) {
651 ASSERT_TRUE(WriteFile(test_file(), "content"));
652 FilePathWatcher watcher
;
653 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
654 ASSERT_TRUE(SetupWatch(test_file(), &watcher
, delegate
.get(), false));
656 // Now make sure we get notified if the file is modified.
657 ASSERT_TRUE(base::MakeFileUnreadable(test_file()));
658 ASSERT_TRUE(WaitForEvents());
659 DeleteDelegateOnFileThread(delegate
.release());
662 #if defined(OS_LINUX)
664 // Verify that creating a symlink is caught.
665 TEST_F(FilePathWatcherTest
, CreateLink
) {
666 FilePathWatcher watcher
;
667 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
668 // Note that we are watching the symlink
669 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
671 // Now make sure we get notified if the link is created.
672 // Note that test_file() doesn't have to exist.
673 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
674 ASSERT_TRUE(WaitForEvents());
675 DeleteDelegateOnFileThread(delegate
.release());
678 // Verify that deleting a symlink is caught.
679 TEST_F(FilePathWatcherTest
, DeleteLink
) {
680 // Unfortunately this test case only works if the link target exists.
681 // TODO(craig) fix this as part of crbug.com/91561.
682 ASSERT_TRUE(WriteFile(test_file(), "content"));
683 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
684 FilePathWatcher watcher
;
685 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
686 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
688 // Now make sure we get notified if the link is deleted.
689 ASSERT_TRUE(base::DeleteFile(test_link(), false));
690 ASSERT_TRUE(WaitForEvents());
691 DeleteDelegateOnFileThread(delegate
.release());
694 // Verify that modifying a target file that a link is pointing to
695 // when we are watching the link is caught.
696 TEST_F(FilePathWatcherTest
, ModifiedLinkedFile
) {
697 ASSERT_TRUE(WriteFile(test_file(), "content"));
698 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
699 FilePathWatcher watcher
;
700 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
701 // Note that we are watching the symlink.
702 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
704 // Now make sure we get notified if the file is modified.
705 ASSERT_TRUE(WriteFile(test_file(), "new content"));
706 ASSERT_TRUE(WaitForEvents());
707 DeleteDelegateOnFileThread(delegate
.release());
710 // Verify that creating a target file that a link is pointing to
711 // when we are watching the link is caught.
712 TEST_F(FilePathWatcherTest
, CreateTargetLinkedFile
) {
713 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
714 FilePathWatcher watcher
;
715 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
716 // Note that we are watching the symlink.
717 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
719 // Now make sure we get notified if the target file is created.
720 ASSERT_TRUE(WriteFile(test_file(), "content"));
721 ASSERT_TRUE(WaitForEvents());
722 DeleteDelegateOnFileThread(delegate
.release());
725 // Verify that deleting a target file that a link is pointing to
726 // when we are watching the link is caught.
727 TEST_F(FilePathWatcherTest
, DeleteTargetLinkedFile
) {
728 ASSERT_TRUE(WriteFile(test_file(), "content"));
729 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
730 FilePathWatcher watcher
;
731 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
732 // Note that we are watching the symlink.
733 ASSERT_TRUE(SetupWatch(test_link(), &watcher
, delegate
.get(), false));
735 // Now make sure we get notified if the target file is deleted.
736 ASSERT_TRUE(base::DeleteFile(test_file(), false));
737 ASSERT_TRUE(WaitForEvents());
738 DeleteDelegateOnFileThread(delegate
.release());
741 // Verify that watching a file whose parent directory is a link that
742 // doesn't exist yet works if the symlink is created eventually.
743 TEST_F(FilePathWatcherTest
, LinkedDirectoryPart1
) {
744 FilePathWatcher watcher
;
745 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
746 FilePath
link_dir(temp_dir_
.path().AppendASCII("dir.lnk"));
747 FilePath
file(dir
.AppendASCII("file"));
748 FilePath
linkfile(link_dir
.AppendASCII("file"));
749 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
750 // dir/file should exist.
751 ASSERT_TRUE(base::CreateDirectory(dir
));
752 ASSERT_TRUE(WriteFile(file
, "content"));
753 // Note that we are watching dir.lnk/file which doesn't exist yet.
754 ASSERT_TRUE(SetupWatch(linkfile
, &watcher
, delegate
.get(), false));
756 ASSERT_TRUE(CreateSymbolicLink(dir
, link_dir
));
757 VLOG(1) << "Waiting for link creation";
758 ASSERT_TRUE(WaitForEvents());
760 ASSERT_TRUE(WriteFile(file
, "content v2"));
761 VLOG(1) << "Waiting for file change";
762 ASSERT_TRUE(WaitForEvents());
764 ASSERT_TRUE(base::DeleteFile(file
, false));
765 VLOG(1) << "Waiting for file deletion";
766 ASSERT_TRUE(WaitForEvents());
767 DeleteDelegateOnFileThread(delegate
.release());
770 // Verify that watching a file whose parent directory is a
771 // dangling symlink works if the directory is created eventually.
772 TEST_F(FilePathWatcherTest
, LinkedDirectoryPart2
) {
773 FilePathWatcher watcher
;
774 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
775 FilePath
link_dir(temp_dir_
.path().AppendASCII("dir.lnk"));
776 FilePath
file(dir
.AppendASCII("file"));
777 FilePath
linkfile(link_dir
.AppendASCII("file"));
778 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
779 // Now create the link from dir.lnk pointing to dir but
780 // neither dir nor dir/file exist yet.
781 ASSERT_TRUE(CreateSymbolicLink(dir
, link_dir
));
782 // Note that we are watching dir.lnk/file.
783 ASSERT_TRUE(SetupWatch(linkfile
, &watcher
, delegate
.get(), false));
785 ASSERT_TRUE(base::CreateDirectory(dir
));
786 ASSERT_TRUE(WriteFile(file
, "content"));
787 VLOG(1) << "Waiting for dir/file creation";
788 ASSERT_TRUE(WaitForEvents());
790 ASSERT_TRUE(WriteFile(file
, "content v2"));
791 VLOG(1) << "Waiting for file change";
792 ASSERT_TRUE(WaitForEvents());
794 ASSERT_TRUE(base::DeleteFile(file
, false));
795 VLOG(1) << "Waiting for file deletion";
796 ASSERT_TRUE(WaitForEvents());
797 DeleteDelegateOnFileThread(delegate
.release());
800 // Verify that watching a file with a symlink on the path
801 // to the file works.
802 TEST_F(FilePathWatcherTest
, LinkedDirectoryPart3
) {
803 FilePathWatcher watcher
;
804 FilePath
dir(temp_dir_
.path().AppendASCII("dir"));
805 FilePath
link_dir(temp_dir_
.path().AppendASCII("dir.lnk"));
806 FilePath
file(dir
.AppendASCII("file"));
807 FilePath
linkfile(link_dir
.AppendASCII("file"));
808 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
809 ASSERT_TRUE(base::CreateDirectory(dir
));
810 ASSERT_TRUE(CreateSymbolicLink(dir
, link_dir
));
811 // Note that we are watching dir.lnk/file but the file doesn't exist yet.
812 ASSERT_TRUE(SetupWatch(linkfile
, &watcher
, delegate
.get(), false));
814 ASSERT_TRUE(WriteFile(file
, "content"));
815 VLOG(1) << "Waiting for file creation";
816 ASSERT_TRUE(WaitForEvents());
818 ASSERT_TRUE(WriteFile(file
, "content v2"));
819 VLOG(1) << "Waiting for file change";
820 ASSERT_TRUE(WaitForEvents());
822 ASSERT_TRUE(base::DeleteFile(file
, false));
823 VLOG(1) << "Waiting for file deletion";
824 ASSERT_TRUE(WaitForEvents());
825 DeleteDelegateOnFileThread(delegate
.release());
836 #if defined(OS_MACOSX)
837 bool ChangeFilePermissions(const FilePath
& path
, Permission perm
, bool allow
) {
838 struct stat stat_buf
;
840 if (stat(path
.value().c_str(), &stat_buf
) != 0)
846 mode
= S_IRUSR
| S_IRGRP
| S_IROTH
;
849 mode
= S_IWUSR
| S_IWGRP
| S_IWOTH
;
852 mode
= S_IXUSR
| S_IXGRP
| S_IXOTH
;
855 ADD_FAILURE() << "unknown perm " << perm
;
859 stat_buf
.st_mode
|= mode
;
861 stat_buf
.st_mode
&= ~mode
;
863 return chmod(path
.value().c_str(), stat_buf
.st_mode
) == 0;
865 #endif // defined(OS_MACOSX)
867 #if defined(OS_MACOSX)
868 // Linux implementation of FilePathWatcher doesn't catch attribute changes.
869 // http://crbug.com/78043
870 // Windows implementation of FilePathWatcher catches attribute changes that
871 // don't affect the path being watched.
872 // http://crbug.com/78045
874 // Verify that changing attributes on a directory works.
875 TEST_F(FilePathWatcherTest
, DirAttributesChanged
) {
876 FilePath
test_dir1(temp_dir_
.path().AppendASCII("DirAttributesChangedDir1"));
877 FilePath
test_dir2(test_dir1
.AppendASCII("DirAttributesChangedDir2"));
878 FilePath
test_file(test_dir2
.AppendASCII("DirAttributesChangedFile"));
879 // Setup a directory hierarchy.
880 ASSERT_TRUE(base::CreateDirectory(test_dir1
));
881 ASSERT_TRUE(base::CreateDirectory(test_dir2
));
882 ASSERT_TRUE(WriteFile(test_file
, "content"));
884 FilePathWatcher watcher
;
885 scoped_ptr
<TestDelegate
> delegate(new TestDelegate(collector()));
886 ASSERT_TRUE(SetupWatch(test_file
, &watcher
, delegate
.get(), false));
888 // We should not get notified in this case as it hasn't affected our ability
889 // to access the file.
890 ASSERT_TRUE(ChangeFilePermissions(test_dir1
, Read
, false));
891 loop_
.PostDelayedTask(FROM_HERE
,
892 MessageLoop::QuitWhenIdleClosure(),
893 TestTimeouts::tiny_timeout());
894 ASSERT_FALSE(WaitForEvents());
895 ASSERT_TRUE(ChangeFilePermissions(test_dir1
, Read
, true));
897 // We should get notified in this case because filepathwatcher can no
898 // longer access the file
899 ASSERT_TRUE(ChangeFilePermissions(test_dir1
, Execute
, false));
900 ASSERT_TRUE(WaitForEvents());
901 ASSERT_TRUE(ChangeFilePermissions(test_dir1
, Execute
, true));
902 DeleteDelegateOnFileThread(delegate
.release());