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