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