Vectorize website settings icons in omnibox
[chromium-blink-merge.git] / base / files / file_path_watcher_unittest.cc
blob21e9dd137e2dc5cf080112595d74200c90d80002
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"
7 #if defined(OS_WIN)
8 #include <windows.h>
9 #include <aclapi.h>
10 #elif defined(OS_POSIX)
11 #include <sys/stat.h>
12 #endif
14 #include <set>
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)
39 namespace base {
41 namespace {
43 class TestDelegate;
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> {
49 public:
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);
63 void Reset() {
64 signaled_.clear();
67 bool Success() {
68 return signaled_ == delegates_;
71 private:
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> {
97 public:
98 TestDelegateBase() {}
99 virtual ~TestDelegateBase() {}
101 virtual void OnFileChanged(const FilePath& path, bool error) = 0;
103 private:
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 {
113 public:
114 explicit TestDelegate(NotificationCollector* collector)
115 : collector_(collector) {
116 collector_->Register(this);
118 ~TestDelegate() override {}
120 void OnFileChanged(const FilePath& path, bool error) override {
121 if (error)
122 ADD_FAILURE() << "Error " << path.value();
123 else
124 collector_->OnChange(this);
127 private:
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,
137 bool* result,
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 {
146 public:
147 FilePathWatcherTest()
148 : file_thread_("FilePathWatcherTest") {}
150 ~FilePathWatcherTest() override {}
152 protected:
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.
162 FilePath parent_dir;
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 {
197 collector_->Reset();
198 loop_.Run();
199 return collector_->Success();
202 NotificationCollector* collector() { return collector_.get(); }
204 MessageLoop loop_;
205 base::Thread file_thread_;
206 ScopedTempDir temp_dir_;
207 scoped_refptr<NotificationCollector> collector_;
209 private:
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);
218 bool result;
219 file_thread_.task_runner()->PostTask(
220 FROM_HERE, base::Bind(SetupWatchCallback, target, watcher, delegate,
221 recursive_watch, &result, &completion));
222 completion.Wait();
223 return result;
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 {
282 public:
283 Deleter(FilePathWatcher* watcher, MessageLoop* loop)
284 : watcher_(watcher),
285 loop_(loop) {
287 ~Deleter() override {}
289 void OnFileChanged(const FilePath&, bool) override {
290 watcher_.reset();
291 loop_->task_runner()->PostTask(FROM_HERE,
292 MessageLoop::QuitWhenIdleClosure());
295 FilePathWatcher* watcher() const { return watcher_.get(); }
297 private:
298 scoped_ptr<FilePathWatcher> watcher_;
299 MessageLoop* loop_;
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
320 // notification.
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
406 #endif
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());
459 #endif // !OS_MACOSX
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(),
482 false));
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());
506 return;
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());
552 #endif
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())
575 return;
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));
584 // Link creation.
585 FilePath target1(temp_dir_.path().AppendASCII("target1"));
586 ASSERT_TRUE(base::CreateSymbolicLink(target1, symlink));
587 ASSERT_TRUE(WaitForEvents());
589 // Target1 creation.
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());
598 // Link change.
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());
612 #endif // OS_POSIX
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(),
632 false));
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());
828 #endif // OS_LINUX
830 enum Permission {
831 Read,
832 Write,
833 Execute
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)
841 return false;
843 mode_t mode = 0;
844 switch (perm) {
845 case Read:
846 mode = S_IRUSR | S_IRGRP | S_IROTH;
847 break;
848 case Write:
849 mode = S_IWUSR | S_IWGRP | S_IWOTH;
850 break;
851 case Execute:
852 mode = S_IXUSR | S_IXGRP | S_IXOTH;
853 break;
854 default:
855 ADD_FAILURE() << "unknown perm " << perm;
856 return false;
858 if (allow) {
859 stat_buf.st_mode |= mode;
860 } else {
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());
905 #endif // OS_MACOSX
906 } // namespace
908 } // namespace base