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.
4
5 #include "base/files/file_path_watcher.h"
6
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
13
14 #include <set>
15
16 #include "base/bind.h"
17 #include "base/bind_helpers.h"
18 #include "base/compiler_specific.h"
19 #include "base/files/file_path.h"
20 #include "base/files/file_util.h"
21 #include "base/files/scoped_temp_dir.h"
22 #include "base/location.h"
23 #include "base/macros.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/threading/thread_task_runner_handle.h"
32 #include "build/build_config.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34
35 #if defined(OS_ANDROID)
36 #include "base/android/path_utils.h"
37 #endif // defined(OS_ANDROID)
38
39 #if defined(OS_POSIX)
40 #include "base/files/file_descriptor_watcher_posix.h"
41 #endif // defined(OS_POSIX)
42
43 namespace base {
44
45 namespace {
46
47 class TestDelegate;
48
49 // Aggregates notifications from the test delegates and breaks the message loop
50 // the test thread is waiting on once they all came in.
51 class NotificationCollector
52 : public base::RefCountedThreadSafe<NotificationCollector> {
53 public:
NotificationCollector()54 NotificationCollector() : task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
55
56 // Called from the file thread by the delegates.
OnChange(TestDelegate * delegate)57 void OnChange(TestDelegate* delegate) {
58 task_runner_->PostTask(
59 FROM_HERE, base::Bind(&NotificationCollector::RecordChange, this,
60 base::Unretained(delegate)));
61 }
62
Register(TestDelegate * delegate)63 void Register(TestDelegate* delegate) {
64 delegates_.insert(delegate);
65 }
66
Reset()67 void Reset() {
68 signaled_.clear();
69 }
70
Success()71 bool Success() {
72 return signaled_ == delegates_;
73 }
74
75 private:
76 friend class base::RefCountedThreadSafe<NotificationCollector>;
~NotificationCollector()77 ~NotificationCollector() {}
78
RecordChange(TestDelegate * delegate)79 void RecordChange(TestDelegate* delegate) {
80 // Warning: |delegate| is Unretained. Do not dereference.
81 ASSERT_TRUE(task_runner_->BelongsToCurrentThread());
82 ASSERT_TRUE(delegates_.count(delegate));
83 signaled_.insert(delegate);
84
85 // Check whether all delegates have been signaled.
86 if (signaled_ == delegates_)
87 task_runner_->PostTask(FROM_HERE, MessageLoop::QuitWhenIdleClosure());
88 }
89
90 // Set of registered delegates.
91 std::set<TestDelegate*> delegates_;
92
93 // Set of signaled delegates.
94 std::set<TestDelegate*> signaled_;
95
96 // The loop we should break after all delegates signaled.
97 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
98 };
99
100 class TestDelegateBase : public SupportsWeakPtr<TestDelegateBase> {
101 public:
TestDelegateBase()102 TestDelegateBase() {}
~TestDelegateBase()103 virtual ~TestDelegateBase() {}
104
105 virtual void OnFileChanged(const FilePath& path, bool error) = 0;
106
107 private:
108 DISALLOW_COPY_AND_ASSIGN(TestDelegateBase);
109 };
110
111 // A mock class for testing. Gmock is not appropriate because it is not
112 // thread-safe for setting expectations. Thus the test code cannot safely
113 // reset expectations while the file watcher is running.
114 // Instead, TestDelegate gets the notifications from FilePathWatcher and uses
115 // NotificationCollector to aggregate the results.
116 class TestDelegate : public TestDelegateBase {
117 public:
TestDelegate(NotificationCollector * collector)118 explicit TestDelegate(NotificationCollector* collector)
119 : collector_(collector) {
120 collector_->Register(this);
121 }
~TestDelegate()122 ~TestDelegate() override {}
123
OnFileChanged(const FilePath & path,bool error)124 void OnFileChanged(const FilePath& path, bool error) override {
125 if (error)
126 ADD_FAILURE() << "Error " << path.value();
127 else
128 collector_->OnChange(this);
129 }
130
131 private:
132 scoped_refptr<NotificationCollector> collector_;
133
134 DISALLOW_COPY_AND_ASSIGN(TestDelegate);
135 };
136
137 class FilePathWatcherTest : public testing::Test {
138 public:
FilePathWatcherTest()139 FilePathWatcherTest()
140 #if defined(OS_POSIX)
141 : file_descriptor_watcher_(&loop_)
142 #endif
143 {
144 }
145
~FilePathWatcherTest()146 ~FilePathWatcherTest() override {}
147
148 protected:
SetUp()149 void SetUp() override {
150 #if defined(OS_ANDROID)
151 // Watching files is only permitted when all parent directories are
152 // accessible, which is not the case for the default temp directory
153 // on Android which is under /data/data. Use /sdcard instead.
154 // TODO(pauljensen): Remove this when crbug.com/475568 is fixed.
155 FilePath parent_dir;
156 ASSERT_TRUE(android::GetExternalStorageDirectory(&parent_dir));
157 ASSERT_TRUE(temp_dir_.CreateUniqueTempDirUnderPath(parent_dir));
158 #else // defined(OS_ANDROID)
159 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
160 #endif // defined(OS_ANDROID)
161 collector_ = new NotificationCollector();
162 }
163
TearDown()164 void TearDown() override { RunLoop().RunUntilIdle(); }
165
test_file()166 FilePath test_file() {
167 return temp_dir_.GetPath().AppendASCII("FilePathWatcherTest");
168 }
169
test_link()170 FilePath test_link() {
171 return temp_dir_.GetPath().AppendASCII("FilePathWatcherTest.lnk");
172 }
173
174 // Write |content| to |file|. Returns true on success.
WriteFile(const FilePath & file,const std::string & content)175 bool WriteFile(const FilePath& file, const std::string& content) {
176 int write_size = ::base::WriteFile(file, content.c_str(), content.length());
177 return write_size == static_cast<int>(content.length());
178 }
179
180 bool SetupWatch(const FilePath& target,
181 FilePathWatcher* watcher,
182 TestDelegateBase* delegate,
183 bool recursive_watch) WARN_UNUSED_RESULT;
184
WaitForEvents()185 bool WaitForEvents() WARN_UNUSED_RESULT {
186 collector_->Reset();
187
188 RunLoop run_loop;
189 // Make sure we timeout if we don't get notified.
190 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
191 FROM_HERE, run_loop.QuitWhenIdleClosure(),
192 TestTimeouts::action_timeout());
193 run_loop.Run();
194 return collector_->Success();
195 }
196
collector()197 NotificationCollector* collector() { return collector_.get(); }
198
199 MessageLoopForIO loop_;
200 #if defined(OS_POSIX)
201 FileDescriptorWatcher file_descriptor_watcher_;
202 #endif
203
204 ScopedTempDir temp_dir_;
205 scoped_refptr<NotificationCollector> collector_;
206
207 private:
208 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherTest);
209 };
210
SetupWatch(const FilePath & target,FilePathWatcher * watcher,TestDelegateBase * delegate,bool recursive_watch)211 bool FilePathWatcherTest::SetupWatch(const FilePath& target,
212 FilePathWatcher* watcher,
213 TestDelegateBase* delegate,
214 bool recursive_watch) {
215 return watcher->Watch(
216 target, recursive_watch,
217 base::Bind(&TestDelegateBase::OnFileChanged, delegate->AsWeakPtr()));
218 }
219
220 // Basic test: Create the file and verify that we notice.
TEST_F(FilePathWatcherTest,NewFile)221 TEST_F(FilePathWatcherTest, NewFile) {
222 FilePathWatcher watcher;
223 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
224 ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
225
226 ASSERT_TRUE(WriteFile(test_file(), "content"));
227 ASSERT_TRUE(WaitForEvents());
228 }
229
230 // Verify that modifying the file is caught.
TEST_F(FilePathWatcherTest,ModifiedFile)231 TEST_F(FilePathWatcherTest, ModifiedFile) {
232 ASSERT_TRUE(WriteFile(test_file(), "content"));
233
234 FilePathWatcher watcher;
235 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
236 ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
237
238 // Now make sure we get notified if the file is modified.
239 ASSERT_TRUE(WriteFile(test_file(), "new content"));
240 ASSERT_TRUE(WaitForEvents());
241 }
242
243 // Verify that moving the file into place is caught.
TEST_F(FilePathWatcherTest,MovedFile)244 TEST_F(FilePathWatcherTest, MovedFile) {
245 FilePath source_file(temp_dir_.GetPath().AppendASCII("source"));
246 ASSERT_TRUE(WriteFile(source_file, "content"));
247
248 FilePathWatcher watcher;
249 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
250 ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
251
252 // Now make sure we get notified if the file is modified.
253 ASSERT_TRUE(base::Move(source_file, test_file()));
254 ASSERT_TRUE(WaitForEvents());
255 }
256
TEST_F(FilePathWatcherTest,DeletedFile)257 TEST_F(FilePathWatcherTest, DeletedFile) {
258 ASSERT_TRUE(WriteFile(test_file(), "content"));
259
260 FilePathWatcher watcher;
261 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
262 ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
263
264 // Now make sure we get notified if the file is deleted.
265 base::DeleteFile(test_file(), false);
266 ASSERT_TRUE(WaitForEvents());
267 }
268
269 // Used by the DeleteDuringNotify test below.
270 // Deletes the FilePathWatcher when it's notified.
271 class Deleter : public TestDelegateBase {
272 public:
Deleter(FilePathWatcher * watcher,MessageLoop * loop)273 Deleter(FilePathWatcher* watcher, MessageLoop* loop)
274 : watcher_(watcher),
275 loop_(loop) {
276 }
~Deleter()277 ~Deleter() override {}
278
OnFileChanged(const FilePath &,bool)279 void OnFileChanged(const FilePath&, bool) override {
280 watcher_.reset();
281 loop_->task_runner()->PostTask(FROM_HERE,
282 MessageLoop::QuitWhenIdleClosure());
283 }
284
watcher() const285 FilePathWatcher* watcher() const { return watcher_.get(); }
286
287 private:
288 std::unique_ptr<FilePathWatcher> watcher_;
289 MessageLoop* loop_;
290
291 DISALLOW_COPY_AND_ASSIGN(Deleter);
292 };
293
294 // Verify that deleting a watcher during the callback doesn't crash.
TEST_F(FilePathWatcherTest,DeleteDuringNotify)295 TEST_F(FilePathWatcherTest, DeleteDuringNotify) {
296 FilePathWatcher* watcher = new FilePathWatcher;
297 // Takes ownership of watcher.
298 std::unique_ptr<Deleter> deleter(new Deleter(watcher, &loop_));
299 ASSERT_TRUE(SetupWatch(test_file(), watcher, deleter.get(), false));
300
301 ASSERT_TRUE(WriteFile(test_file(), "content"));
302 ASSERT_TRUE(WaitForEvents());
303
304 // We win if we haven't crashed yet.
305 // Might as well double-check it got deleted, too.
306 ASSERT_TRUE(deleter->watcher() == NULL);
307 }
308
309 // Verify that deleting the watcher works even if there is a pending
310 // notification.
311 // Flaky on MacOS (and ARM linux): http://crbug.com/85930
TEST_F(FilePathWatcherTest,DISABLED_DestroyWithPendingNotification)312 TEST_F(FilePathWatcherTest, DISABLED_DestroyWithPendingNotification) {
313 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
314 FilePathWatcher watcher;
315 ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
316 ASSERT_TRUE(WriteFile(test_file(), "content"));
317 }
318
TEST_F(FilePathWatcherTest,MultipleWatchersSingleFile)319 TEST_F(FilePathWatcherTest, MultipleWatchersSingleFile) {
320 FilePathWatcher watcher1, watcher2;
321 std::unique_ptr<TestDelegate> delegate1(new TestDelegate(collector()));
322 std::unique_ptr<TestDelegate> delegate2(new TestDelegate(collector()));
323 ASSERT_TRUE(SetupWatch(test_file(), &watcher1, delegate1.get(), false));
324 ASSERT_TRUE(SetupWatch(test_file(), &watcher2, delegate2.get(), false));
325
326 ASSERT_TRUE(WriteFile(test_file(), "content"));
327 ASSERT_TRUE(WaitForEvents());
328 }
329
330 // Verify that watching a file whose parent directory doesn't exist yet works if
331 // the directory and file are created eventually.
TEST_F(FilePathWatcherTest,NonExistentDirectory)332 TEST_F(FilePathWatcherTest, NonExistentDirectory) {
333 FilePathWatcher watcher;
334 FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
335 FilePath file(dir.AppendASCII("file"));
336 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
337 ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
338
339 ASSERT_TRUE(base::CreateDirectory(dir));
340
341 ASSERT_TRUE(WriteFile(file, "content"));
342
343 VLOG(1) << "Waiting for file creation";
344 ASSERT_TRUE(WaitForEvents());
345
346 ASSERT_TRUE(WriteFile(file, "content v2"));
347 VLOG(1) << "Waiting for file change";
348 ASSERT_TRUE(WaitForEvents());
349
350 ASSERT_TRUE(base::DeleteFile(file, false));
351 VLOG(1) << "Waiting for file deletion";
352 ASSERT_TRUE(WaitForEvents());
353 }
354
355 // Exercises watch reconfiguration for the case that directories on the path
356 // are rapidly created.
TEST_F(FilePathWatcherTest,DirectoryChain)357 TEST_F(FilePathWatcherTest, DirectoryChain) {
358 FilePath path(temp_dir_.GetPath());
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);
364 }
365
366 FilePathWatcher watcher;
367 FilePath file(path.AppendASCII("file"));
368 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
369 ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
370
371 FilePath sub_path(temp_dir_.GetPath());
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));
376 }
377 VLOG(1) << "Create File";
378 ASSERT_TRUE(WriteFile(file, "content"));
379 VLOG(1) << "Waiting for file creation";
380 ASSERT_TRUE(WaitForEvents());
381
382 ASSERT_TRUE(WriteFile(file, "content v2"));
383 VLOG(1) << "Waiting for file modification";
384 ASSERT_TRUE(WaitForEvents());
385 }
386
387 #if defined(OS_MACOSX)
388 // http://crbug.com/85930
389 #define DisappearingDirectory DISABLED_DisappearingDirectory
390 #endif
TEST_F(FilePathWatcherTest,DisappearingDirectory)391 TEST_F(FilePathWatcherTest, DisappearingDirectory) {
392 FilePathWatcher watcher;
393 FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
394 FilePath file(dir.AppendASCII("file"));
395 ASSERT_TRUE(base::CreateDirectory(dir));
396 ASSERT_TRUE(WriteFile(file, "content"));
397 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
398 ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
399
400 ASSERT_TRUE(base::DeleteFile(dir, true));
401 ASSERT_TRUE(WaitForEvents());
402 }
403
404 // Tests that a file that is deleted and reappears is tracked correctly.
TEST_F(FilePathWatcherTest,DeleteAndRecreate)405 TEST_F(FilePathWatcherTest, DeleteAndRecreate) {
406 ASSERT_TRUE(WriteFile(test_file(), "content"));
407 FilePathWatcher watcher;
408 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
409 ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
410
411 ASSERT_TRUE(base::DeleteFile(test_file(), false));
412 VLOG(1) << "Waiting for file deletion";
413 ASSERT_TRUE(WaitForEvents());
414
415 ASSERT_TRUE(WriteFile(test_file(), "content"));
416 VLOG(1) << "Waiting for file creation";
417 ASSERT_TRUE(WaitForEvents());
418 }
419
TEST_F(FilePathWatcherTest,WatchDirectory)420 TEST_F(FilePathWatcherTest, WatchDirectory) {
421 FilePathWatcher watcher;
422 FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
423 FilePath file1(dir.AppendASCII("file1"));
424 FilePath file2(dir.AppendASCII("file2"));
425 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
426 ASSERT_TRUE(SetupWatch(dir, &watcher, delegate.get(), false));
427
428 ASSERT_TRUE(base::CreateDirectory(dir));
429 VLOG(1) << "Waiting for directory creation";
430 ASSERT_TRUE(WaitForEvents());
431
432 ASSERT_TRUE(WriteFile(file1, "content"));
433 VLOG(1) << "Waiting for file1 creation";
434 ASSERT_TRUE(WaitForEvents());
435
436 #if !defined(OS_MACOSX)
437 // Mac implementation does not detect files modified in a directory.
438 ASSERT_TRUE(WriteFile(file1, "content v2"));
439 VLOG(1) << "Waiting for file1 modification";
440 ASSERT_TRUE(WaitForEvents());
441 #endif // !OS_MACOSX
442
443 ASSERT_TRUE(base::DeleteFile(file1, false));
444 VLOG(1) << "Waiting for file1 deletion";
445 ASSERT_TRUE(WaitForEvents());
446
447 ASSERT_TRUE(WriteFile(file2, "content"));
448 VLOG(1) << "Waiting for file2 creation";
449 ASSERT_TRUE(WaitForEvents());
450 }
451
TEST_F(FilePathWatcherTest,MoveParent)452 TEST_F(FilePathWatcherTest, MoveParent) {
453 FilePathWatcher file_watcher;
454 FilePathWatcher subdir_watcher;
455 FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
456 FilePath dest(temp_dir_.GetPath().AppendASCII("dest"));
457 FilePath subdir(dir.AppendASCII("subdir"));
458 FilePath file(subdir.AppendASCII("file"));
459 std::unique_ptr<TestDelegate> file_delegate(new TestDelegate(collector()));
460 ASSERT_TRUE(SetupWatch(file, &file_watcher, file_delegate.get(), false));
461 std::unique_ptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
462 ASSERT_TRUE(SetupWatch(subdir, &subdir_watcher, subdir_delegate.get(),
463 false));
464
465 // Setup a directory hierarchy.
466 ASSERT_TRUE(base::CreateDirectory(subdir));
467 ASSERT_TRUE(WriteFile(file, "content"));
468 VLOG(1) << "Waiting for file creation";
469 ASSERT_TRUE(WaitForEvents());
470
471 // Move the parent directory.
472 base::Move(dir, dest);
473 VLOG(1) << "Waiting for directory move";
474 ASSERT_TRUE(WaitForEvents());
475 }
476
TEST_F(FilePathWatcherTest,RecursiveWatch)477 TEST_F(FilePathWatcherTest, RecursiveWatch) {
478 FilePathWatcher watcher;
479 FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
480 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
481 bool setup_result = SetupWatch(dir, &watcher, delegate.get(), true);
482 if (!FilePathWatcher::RecursiveWatchAvailable()) {
483 ASSERT_FALSE(setup_result);
484 return;
485 }
486 ASSERT_TRUE(setup_result);
487
488 // Main directory("dir") creation.
489 ASSERT_TRUE(base::CreateDirectory(dir));
490 ASSERT_TRUE(WaitForEvents());
491
492 // Create "$dir/file1".
493 FilePath file1(dir.AppendASCII("file1"));
494 ASSERT_TRUE(WriteFile(file1, "content"));
495 ASSERT_TRUE(WaitForEvents());
496
497 // Create "$dir/subdir".
498 FilePath subdir(dir.AppendASCII("subdir"));
499 ASSERT_TRUE(base::CreateDirectory(subdir));
500 ASSERT_TRUE(WaitForEvents());
501
502 // Create "$dir/subdir/subdir_file1".
503 FilePath subdir_file1(subdir.AppendASCII("subdir_file1"));
504 ASSERT_TRUE(WriteFile(subdir_file1, "content"));
505 ASSERT_TRUE(WaitForEvents());
506
507 // Create "$dir/subdir/subdir_child_dir".
508 FilePath subdir_child_dir(subdir.AppendASCII("subdir_child_dir"));
509 ASSERT_TRUE(base::CreateDirectory(subdir_child_dir));
510 ASSERT_TRUE(WaitForEvents());
511
512 // Create "$dir/subdir/subdir_child_dir/child_dir_file1".
513 FilePath child_dir_file1(subdir_child_dir.AppendASCII("child_dir_file1"));
514 ASSERT_TRUE(WriteFile(child_dir_file1, "content v2"));
515 ASSERT_TRUE(WaitForEvents());
516
517 // Write into "$dir/subdir/subdir_child_dir/child_dir_file1".
518 ASSERT_TRUE(WriteFile(child_dir_file1, "content"));
519 ASSERT_TRUE(WaitForEvents());
520
521 // Apps cannot change file attributes on Android in /sdcard as /sdcard uses the
522 // "fuse" file system, while /data uses "ext4". Running these tests in /data
523 // would be preferable and allow testing file attributes and symlinks.
524 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
525 // the |temp_dir_| in /data.
526 #if !defined(OS_ANDROID)
527 // Modify "$dir/subdir/subdir_child_dir/child_dir_file1" attributes.
528 ASSERT_TRUE(base::MakeFileUnreadable(child_dir_file1));
529 ASSERT_TRUE(WaitForEvents());
530 #endif
531
532 // Delete "$dir/subdir/subdir_file1".
533 ASSERT_TRUE(base::DeleteFile(subdir_file1, false));
534 ASSERT_TRUE(WaitForEvents());
535
536 // Delete "$dir/subdir/subdir_child_dir/child_dir_file1".
537 ASSERT_TRUE(base::DeleteFile(child_dir_file1, false));
538 ASSERT_TRUE(WaitForEvents());
539 }
540
541 #if defined(OS_POSIX)
542 #if defined(OS_ANDROID)
543 // Apps cannot create symlinks 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 #define RecursiveWithSymLink DISABLED_RecursiveWithSymLink
549 #endif // defined(OS_ANDROID)
TEST_F(FilePathWatcherTest,RecursiveWithSymLink)550 TEST_F(FilePathWatcherTest, RecursiveWithSymLink) {
551 if (!FilePathWatcher::RecursiveWatchAvailable())
552 return;
553
554 FilePathWatcher watcher;
555 FilePath test_dir(temp_dir_.GetPath().AppendASCII("test_dir"));
556 ASSERT_TRUE(base::CreateDirectory(test_dir));
557 FilePath symlink(test_dir.AppendASCII("symlink"));
558 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
559 ASSERT_TRUE(SetupWatch(symlink, &watcher, delegate.get(), true));
560
561 // Link creation.
562 FilePath target1(temp_dir_.GetPath().AppendASCII("target1"));
563 ASSERT_TRUE(base::CreateSymbolicLink(target1, symlink));
564 ASSERT_TRUE(WaitForEvents());
565
566 // Target1 creation.
567 ASSERT_TRUE(base::CreateDirectory(target1));
568 ASSERT_TRUE(WaitForEvents());
569
570 // Create a file in target1.
571 FilePath target1_file(target1.AppendASCII("file"));
572 ASSERT_TRUE(WriteFile(target1_file, "content"));
573 ASSERT_TRUE(WaitForEvents());
574
575 // Link change.
576 FilePath target2(temp_dir_.GetPath().AppendASCII("target2"));
577 ASSERT_TRUE(base::CreateDirectory(target2));
578 ASSERT_TRUE(base::DeleteFile(symlink, false));
579 ASSERT_TRUE(base::CreateSymbolicLink(target2, symlink));
580 ASSERT_TRUE(WaitForEvents());
581
582 // Create a file in target2.
583 FilePath target2_file(target2.AppendASCII("file"));
584 ASSERT_TRUE(WriteFile(target2_file, "content"));
585 ASSERT_TRUE(WaitForEvents());
586 }
587 #endif // OS_POSIX
588
TEST_F(FilePathWatcherTest,MoveChild)589 TEST_F(FilePathWatcherTest, MoveChild) {
590 FilePathWatcher file_watcher;
591 FilePathWatcher subdir_watcher;
592 FilePath source_dir(temp_dir_.GetPath().AppendASCII("source"));
593 FilePath source_subdir(source_dir.AppendASCII("subdir"));
594 FilePath source_file(source_subdir.AppendASCII("file"));
595 FilePath dest_dir(temp_dir_.GetPath().AppendASCII("dest"));
596 FilePath dest_subdir(dest_dir.AppendASCII("subdir"));
597 FilePath dest_file(dest_subdir.AppendASCII("file"));
598
599 // Setup a directory hierarchy.
600 ASSERT_TRUE(base::CreateDirectory(source_subdir));
601 ASSERT_TRUE(WriteFile(source_file, "content"));
602
603 std::unique_ptr<TestDelegate> file_delegate(new TestDelegate(collector()));
604 ASSERT_TRUE(SetupWatch(dest_file, &file_watcher, file_delegate.get(), false));
605 std::unique_ptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
606 ASSERT_TRUE(SetupWatch(dest_subdir, &subdir_watcher, subdir_delegate.get(),
607 false));
608
609 // Move the directory into place, s.t. the watched file appears.
610 ASSERT_TRUE(base::Move(source_dir, dest_dir));
611 ASSERT_TRUE(WaitForEvents());
612 }
613
614 // Verify that changing attributes on a file is caught
615 #if defined(OS_ANDROID)
616 // Apps cannot change file attributes on Android in /sdcard as /sdcard uses the
617 // "fuse" file system, while /data uses "ext4". Running these tests in /data
618 // would be preferable and allow testing file attributes and symlinks.
619 // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
620 // the |temp_dir_| in /data.
621 #define FileAttributesChanged DISABLED_FileAttributesChanged
622 #endif // defined(OS_ANDROID
TEST_F(FilePathWatcherTest,FileAttributesChanged)623 TEST_F(FilePathWatcherTest, FileAttributesChanged) {
624 ASSERT_TRUE(WriteFile(test_file(), "content"));
625 FilePathWatcher watcher;
626 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
627 ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
628
629 // Now make sure we get notified if the file is modified.
630 ASSERT_TRUE(base::MakeFileUnreadable(test_file()));
631 ASSERT_TRUE(WaitForEvents());
632 }
633
634 #if defined(OS_LINUX)
635
636 // Verify that creating a symlink is caught.
TEST_F(FilePathWatcherTest,CreateLink)637 TEST_F(FilePathWatcherTest, CreateLink) {
638 FilePathWatcher watcher;
639 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
640 // Note that we are watching the symlink
641 ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
642
643 // Now make sure we get notified if the link is created.
644 // Note that test_file() doesn't have to exist.
645 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
646 ASSERT_TRUE(WaitForEvents());
647 }
648
649 // Verify that deleting a symlink is caught.
TEST_F(FilePathWatcherTest,DeleteLink)650 TEST_F(FilePathWatcherTest, DeleteLink) {
651 // Unfortunately this test case only works if the link target exists.
652 // TODO(craig) fix this as part of crbug.com/91561.
653 ASSERT_TRUE(WriteFile(test_file(), "content"));
654 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
655 FilePathWatcher watcher;
656 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
657 ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
658
659 // Now make sure we get notified if the link is deleted.
660 ASSERT_TRUE(base::DeleteFile(test_link(), false));
661 ASSERT_TRUE(WaitForEvents());
662 }
663
664 // Verify that modifying a target file that a link is pointing to
665 // when we are watching the link is caught.
TEST_F(FilePathWatcherTest,ModifiedLinkedFile)666 TEST_F(FilePathWatcherTest, ModifiedLinkedFile) {
667 ASSERT_TRUE(WriteFile(test_file(), "content"));
668 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
669 FilePathWatcher watcher;
670 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
671 // Note that we are watching the symlink.
672 ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
673
674 // Now make sure we get notified if the file is modified.
675 ASSERT_TRUE(WriteFile(test_file(), "new content"));
676 ASSERT_TRUE(WaitForEvents());
677 }
678
679 // Verify that creating a target file that a link is pointing to
680 // when we are watching the link is caught.
TEST_F(FilePathWatcherTest,CreateTargetLinkedFile)681 TEST_F(FilePathWatcherTest, CreateTargetLinkedFile) {
682 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
683 FilePathWatcher watcher;
684 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
685 // Note that we are watching the symlink.
686 ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
687
688 // Now make sure we get notified if the target file is created.
689 ASSERT_TRUE(WriteFile(test_file(), "content"));
690 ASSERT_TRUE(WaitForEvents());
691 }
692
693 // Verify that deleting a target file that a link is pointing to
694 // when we are watching the link is caught.
TEST_F(FilePathWatcherTest,DeleteTargetLinkedFile)695 TEST_F(FilePathWatcherTest, DeleteTargetLinkedFile) {
696 ASSERT_TRUE(WriteFile(test_file(), "content"));
697 ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
698 FilePathWatcher watcher;
699 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
700 // Note that we are watching the symlink.
701 ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
702
703 // Now make sure we get notified if the target file is deleted.
704 ASSERT_TRUE(base::DeleteFile(test_file(), false));
705 ASSERT_TRUE(WaitForEvents());
706 }
707
708 // Verify that watching a file whose parent directory is a link that
709 // doesn't exist yet works if the symlink is created eventually.
TEST_F(FilePathWatcherTest,LinkedDirectoryPart1)710 TEST_F(FilePathWatcherTest, LinkedDirectoryPart1) {
711 FilePathWatcher watcher;
712 FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
713 FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
714 FilePath file(dir.AppendASCII("file"));
715 FilePath linkfile(link_dir.AppendASCII("file"));
716 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
717 // dir/file should exist.
718 ASSERT_TRUE(base::CreateDirectory(dir));
719 ASSERT_TRUE(WriteFile(file, "content"));
720 // Note that we are watching dir.lnk/file which doesn't exist yet.
721 ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
722
723 ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
724 VLOG(1) << "Waiting for link creation";
725 ASSERT_TRUE(WaitForEvents());
726
727 ASSERT_TRUE(WriteFile(file, "content v2"));
728 VLOG(1) << "Waiting for file change";
729 ASSERT_TRUE(WaitForEvents());
730
731 ASSERT_TRUE(base::DeleteFile(file, false));
732 VLOG(1) << "Waiting for file deletion";
733 ASSERT_TRUE(WaitForEvents());
734 }
735
736 // Verify that watching a file whose parent directory is a
737 // dangling symlink works if the directory is created eventually.
TEST_F(FilePathWatcherTest,LinkedDirectoryPart2)738 TEST_F(FilePathWatcherTest, LinkedDirectoryPart2) {
739 FilePathWatcher watcher;
740 FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
741 FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
742 FilePath file(dir.AppendASCII("file"));
743 FilePath linkfile(link_dir.AppendASCII("file"));
744 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
745 // Now create the link from dir.lnk pointing to dir but
746 // neither dir nor dir/file exist yet.
747 ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
748 // Note that we are watching dir.lnk/file.
749 ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
750
751 ASSERT_TRUE(base::CreateDirectory(dir));
752 ASSERT_TRUE(WriteFile(file, "content"));
753 VLOG(1) << "Waiting for dir/file creation";
754 ASSERT_TRUE(WaitForEvents());
755
756 ASSERT_TRUE(WriteFile(file, "content v2"));
757 VLOG(1) << "Waiting for file change";
758 ASSERT_TRUE(WaitForEvents());
759
760 ASSERT_TRUE(base::DeleteFile(file, false));
761 VLOG(1) << "Waiting for file deletion";
762 ASSERT_TRUE(WaitForEvents());
763 }
764
765 // Verify that watching a file with a symlink on the path
766 // to the file works.
TEST_F(FilePathWatcherTest,LinkedDirectoryPart3)767 TEST_F(FilePathWatcherTest, LinkedDirectoryPart3) {
768 FilePathWatcher watcher;
769 FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
770 FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
771 FilePath file(dir.AppendASCII("file"));
772 FilePath linkfile(link_dir.AppendASCII("file"));
773 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
774 ASSERT_TRUE(base::CreateDirectory(dir));
775 ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
776 // Note that we are watching dir.lnk/file but the file doesn't exist yet.
777 ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
778
779 ASSERT_TRUE(WriteFile(file, "content"));
780 VLOG(1) << "Waiting for file creation";
781 ASSERT_TRUE(WaitForEvents());
782
783 ASSERT_TRUE(WriteFile(file, "content v2"));
784 VLOG(1) << "Waiting for file change";
785 ASSERT_TRUE(WaitForEvents());
786
787 ASSERT_TRUE(base::DeleteFile(file, false));
788 VLOG(1) << "Waiting for file deletion";
789 ASSERT_TRUE(WaitForEvents());
790 }
791
792 #endif // OS_LINUX
793
794 enum Permission {
795 Read,
796 Write,
797 Execute
798 };
799
800 #if defined(OS_MACOSX)
ChangeFilePermissions(const FilePath & path,Permission perm,bool allow)801 bool ChangeFilePermissions(const FilePath& path, Permission perm, bool allow) {
802 struct stat stat_buf;
803
804 if (stat(path.value().c_str(), &stat_buf) != 0)
805 return false;
806
807 mode_t mode = 0;
808 switch (perm) {
809 case Read:
810 mode = S_IRUSR | S_IRGRP | S_IROTH;
811 break;
812 case Write:
813 mode = S_IWUSR | S_IWGRP | S_IWOTH;
814 break;
815 case Execute:
816 mode = S_IXUSR | S_IXGRP | S_IXOTH;
817 break;
818 default:
819 ADD_FAILURE() << "unknown perm " << perm;
820 return false;
821 }
822 if (allow) {
823 stat_buf.st_mode |= mode;
824 } else {
825 stat_buf.st_mode &= ~mode;
826 }
827 return chmod(path.value().c_str(), stat_buf.st_mode) == 0;
828 }
829 #endif // defined(OS_MACOSX)
830
831 #if defined(OS_MACOSX)
832 // Linux implementation of FilePathWatcher doesn't catch attribute changes.
833 // http://crbug.com/78043
834 // Windows implementation of FilePathWatcher catches attribute changes that
835 // don't affect the path being watched.
836 // http://crbug.com/78045
837
838 // Verify that changing attributes on a directory works.
TEST_F(FilePathWatcherTest,DirAttributesChanged)839 TEST_F(FilePathWatcherTest, DirAttributesChanged) {
840 FilePath test_dir1(
841 temp_dir_.GetPath().AppendASCII("DirAttributesChangedDir1"));
842 FilePath test_dir2(test_dir1.AppendASCII("DirAttributesChangedDir2"));
843 FilePath test_file(test_dir2.AppendASCII("DirAttributesChangedFile"));
844 // Setup a directory hierarchy.
845 ASSERT_TRUE(base::CreateDirectory(test_dir1));
846 ASSERT_TRUE(base::CreateDirectory(test_dir2));
847 ASSERT_TRUE(WriteFile(test_file, "content"));
848
849 FilePathWatcher watcher;
850 std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
851 ASSERT_TRUE(SetupWatch(test_file, &watcher, delegate.get(), false));
852
853 // We should not get notified in this case as it hasn't affected our ability
854 // to access the file.
855 ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, false));
856 loop_.task_runner()->PostDelayedTask(FROM_HERE,
857 MessageLoop::QuitWhenIdleClosure(),
858 TestTimeouts::tiny_timeout());
859 ASSERT_FALSE(WaitForEvents());
860 ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, true));
861
862 // We should get notified in this case because filepathwatcher can no
863 // longer access the file
864 ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, false));
865 ASSERT_TRUE(WaitForEvents());
866 ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, true));
867 }
868
869 #endif // OS_MACOSX
870 } // namespace
871
872 } // namespace base
873