Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(230)

Side by Side Diff: base/files/file_path_watcher_browsertest.cc

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

Powered by Google App Engine
This is Rietveld 408576698