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

Side by Side Diff: content/common/file_path_watcher/file_path_watcher_browsertest.cc

Issue 6793020: Move FilePathWatcher to base/files. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: use ::operator<< Created 9 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 "content/common/file_path_watcher/file_path_watcher.h"
6
7 #include <set>
8
9 #if defined(OS_WIN)
10 #include <windows.h>
11 #include <aclapi.h>
12 #elif defined(OS_POSIX)
13 #include <sys/stat.h>
14 #endif
15
16 #include "base/basictypes.h"
17 #include "base/compiler_specific.h"
18 #include "base/file_path.h"
19 #include "base/file_util.h"
20 #include "base/memory/scoped_temp_dir.h"
21 #include "base/message_loop.h"
22 #include "base/message_loop_proxy.h"
23 #include "base/path_service.h"
24 #include "base/string_util.h"
25 #include "base/stl_util-inl.h"
26 #include "base/synchronization/waitable_event.h"
27 #include "base/test/test_timeouts.h"
28 #include "base/threading/thread.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30
31 namespace {
32
33 class TestDelegate;
34
35 // Aggregates notifications from the test delegates and breaks the message loop
36 // the test thread is waiting on once they all came in.
37 class NotificationCollector
38 : public base::RefCountedThreadSafe<NotificationCollector> {
39 public:
40 NotificationCollector()
41 : loop_(base::MessageLoopProxy::CreateForCurrentThread()) {}
42
43 // Called from the file thread by the delegates.
44 void OnChange(TestDelegate* delegate) {
45 loop_->PostTask(FROM_HERE,
46 NewRunnableMethod(this,
47 &NotificationCollector::RecordChange,
48 make_scoped_refptr(delegate)));
49 }
50
51 void Register(TestDelegate* delegate) {
52 delegates_.insert(delegate);
53 }
54
55 void Reset() {
56 signaled_.clear();
57 }
58
59 bool Success() {
60 return signaled_ == delegates_;
61 }
62
63 private:
64 void RecordChange(TestDelegate* delegate) {
65 ASSERT_TRUE(loop_->BelongsToCurrentThread());
66 ASSERT_TRUE(delegates_.count(delegate));
67 signaled_.insert(delegate);
68
69 // Check whether all delegates have been signaled.
70 if (signaled_ == delegates_)
71 loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
72 }
73
74 // Set of registered delegates.
75 std::set<TestDelegate*> delegates_;
76
77 // Set of signaled delegates.
78 std::set<TestDelegate*> signaled_;
79
80 // The loop we should break after all delegates signaled.
81 scoped_refptr<base::MessageLoopProxy> loop_;
82 };
83
84 // A mock FilePathWatcher::Delegate for testing. I'd rather use gmock, but it's
85 // not thread safe for setting expectations, so the test code couldn't safely
86 // reset expectations while the file watcher is running. In order to allow this,
87 // we keep simple thread safe status flags in TestDelegate.
88 class TestDelegate : public FilePathWatcher::Delegate {
89 public:
90 // The message loop specified by |loop| will be quit if a notification is
91 // received while the delegate is |armed_|. Note that the testing code must
92 // guarantee |loop| outlives the file thread on which OnFilePathChanged runs.
93 explicit TestDelegate(NotificationCollector* collector)
94 : collector_(collector) {
95 collector_->Register(this);
96 }
97
98 virtual void OnFilePathChanged(const FilePath&) {
99 collector_->OnChange(this);
100 }
101
102 virtual void OnFilePathError(const FilePath& path) {
103 ADD_FAILURE() << "Error " << path.value();
104 }
105
106 private:
107 scoped_refptr<NotificationCollector> collector_;
108
109 DISALLOW_COPY_AND_ASSIGN(TestDelegate);
110 };
111
112 // A helper class for setting up watches on the file thread.
113 class SetupWatchTask : public Task {
114 public:
115 SetupWatchTask(const FilePath& target,
116 FilePathWatcher* watcher,
117 FilePathWatcher::Delegate* delegate,
118 bool* result,
119 base::WaitableEvent* completion)
120 : target_(target),
121 watcher_(watcher),
122 delegate_(delegate),
123 result_(result),
124 completion_(completion) {}
125
126 void Run() {
127 *result_ = watcher_->Watch(target_, delegate_);
128 completion_->Signal();
129 }
130
131 private:
132 const FilePath target_;
133 FilePathWatcher* watcher_;
134 FilePathWatcher::Delegate* delegate_;
135 bool* result_;
136 base::WaitableEvent* completion_;
137
138 DISALLOW_COPY_AND_ASSIGN(SetupWatchTask);
139 };
140
141 class FilePathWatcherTest : public testing::Test {
142 public:
143 FilePathWatcherTest()
144 : file_thread_("FilePathWatcherTest") {}
145
146 virtual ~FilePathWatcherTest() {}
147
148 protected:
149 virtual void SetUp() {
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 virtual void TearDown() {
158 loop_.RunAllPending();
159 }
160
161 FilePath test_file() {
162 return temp_dir_.path().AppendASCII("FilePathWatcherTest");
163 }
164
165 // Write |content| to |file|. Returns true on success.
166 bool WriteFile(const FilePath& file, const std::string& content) {
167 int write_size = file_util::WriteFile(file, content.c_str(),
168 content.length());
169 return write_size == static_cast<int>(content.length());
170 }
171
172 bool SetupWatch(const FilePath& target,
173 FilePathWatcher* watcher,
174 FilePathWatcher::Delegate* delegate) WARN_UNUSED_RESULT {
175 base::WaitableEvent completion(false, false);
176 bool result;
177 file_thread_.message_loop_proxy()->PostTask(FROM_HERE,
178 new SetupWatchTask(target,
179 watcher,
180 delegate,
181 &result,
182 &completion));
183 completion.Wait();
184 return result;
185 }
186
187 bool WaitForEvents() WARN_UNUSED_RESULT {
188 collector_->Reset();
189 loop_.Run();
190 return collector_->Success();
191 }
192
193 NotificationCollector* collector() { return collector_.get(); }
194
195 MessageLoop loop_;
196 base::Thread file_thread_;
197 ScopedTempDir temp_dir_;
198 scoped_refptr<NotificationCollector> collector_;
199 };
200
201 // Basic test: Create the file and verify that we notice.
202 TEST_F(FilePathWatcherTest, NewFile) {
203 FilePathWatcher watcher;
204 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
205 ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get()));
206
207 ASSERT_TRUE(WriteFile(test_file(), "content"));
208 ASSERT_TRUE(WaitForEvents());
209 }
210
211 // Verify that modifying the file is caught.
212 TEST_F(FilePathWatcherTest, ModifiedFile) {
213 ASSERT_TRUE(WriteFile(test_file(), "content"));
214
215 FilePathWatcher watcher;
216 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
217 ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get()));
218
219 // Now make sure we get notified if the file is modified.
220 ASSERT_TRUE(WriteFile(test_file(), "new content"));
221 ASSERT_TRUE(WaitForEvents());
222 }
223
224 // Verify that moving the file into place is caught.
225 TEST_F(FilePathWatcherTest, MovedFile) {
226 FilePath source_file(temp_dir_.path().AppendASCII("source"));
227 ASSERT_TRUE(WriteFile(source_file, "content"));
228
229 FilePathWatcher watcher;
230 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
231 ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get()));
232
233 // Now make sure we get notified if the file is modified.
234 ASSERT_TRUE(file_util::Move(source_file, test_file()));
235 ASSERT_TRUE(WaitForEvents());
236 }
237
238 TEST_F(FilePathWatcherTest, DeletedFile) {
239 ASSERT_TRUE(WriteFile(test_file(), "content"));
240
241 FilePathWatcher watcher;
242 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
243 ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get()));
244
245 // Now make sure we get notified if the file is deleted.
246 file_util::Delete(test_file(), false);
247 ASSERT_TRUE(WaitForEvents());
248 }
249
250 // Used by the DeleteDuringNotify test below.
251 // Deletes the FilePathWatcher when it's notified.
252 class Deleter : public FilePathWatcher::Delegate {
253 public:
254 Deleter(FilePathWatcher* watcher, MessageLoop* loop)
255 : watcher_(watcher),
256 loop_(loop) {
257 }
258
259 virtual void OnFilePathChanged(const FilePath& path) {
260 watcher_.reset();
261 loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
262 }
263
264 scoped_ptr<FilePathWatcher> watcher_;
265 MessageLoop* loop_;
266 };
267
268 // Verify that deleting a watcher during the callback doesn't crash.
269 TEST_F(FilePathWatcherTest, DeleteDuringNotify) {
270 FilePathWatcher* watcher = new FilePathWatcher;
271 // Takes ownership of watcher.
272 scoped_refptr<Deleter> deleter(new Deleter(watcher, &loop_));
273 ASSERT_TRUE(SetupWatch(test_file(), watcher, deleter.get()));
274
275 ASSERT_TRUE(WriteFile(test_file(), "content"));
276 ASSERT_TRUE(WaitForEvents());
277
278 // We win if we haven't crashed yet.
279 // Might as well double-check it got deleted, too.
280 ASSERT_TRUE(deleter->watcher_.get() == NULL);
281 }
282
283 // Verify that deleting the watcher works even if there is a pending
284 // notification.
285 TEST_F(FilePathWatcherTest, DestroyWithPendingNotification) {
286 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
287 FilePathWatcher* watcher = new FilePathWatcher;
288 ASSERT_TRUE(SetupWatch(test_file(), watcher, delegate.get()));
289 ASSERT_TRUE(WriteFile(test_file(), "content"));
290 file_thread_.message_loop_proxy()->DeleteSoon(FROM_HERE, watcher);
291 }
292
293 TEST_F(FilePathWatcherTest, MultipleWatchersSingleFile) {
294 FilePathWatcher watcher1, watcher2;
295 scoped_refptr<TestDelegate> delegate1(new TestDelegate(collector()));
296 scoped_refptr<TestDelegate> delegate2(new TestDelegate(collector()));
297 ASSERT_TRUE(SetupWatch(test_file(), &watcher1, delegate1.get()));
298 ASSERT_TRUE(SetupWatch(test_file(), &watcher2, delegate2.get()));
299
300 ASSERT_TRUE(WriteFile(test_file(), "content"));
301 ASSERT_TRUE(WaitForEvents());
302 }
303
304 // Verify that watching a file whose parent directory doesn't exist yet works if
305 // the directory and file are created eventually.
306 TEST_F(FilePathWatcherTest, NonExistentDirectory) {
307 FilePathWatcher watcher;
308 FilePath dir(temp_dir_.path().AppendASCII("dir"));
309 FilePath file(dir.AppendASCII("file"));
310 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
311 ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get()));
312
313 ASSERT_TRUE(file_util::CreateDirectory(dir));
314
315 ASSERT_TRUE(WriteFile(file, "content"));
316
317 VLOG(1) << "Waiting for file creation";
318 ASSERT_TRUE(WaitForEvents());
319
320 ASSERT_TRUE(WriteFile(file, "content v2"));
321 VLOG(1) << "Waiting for file change";
322 ASSERT_TRUE(WaitForEvents());
323
324 ASSERT_TRUE(file_util::Delete(file, false));
325 VLOG(1) << "Waiting for file deletion";
326 ASSERT_TRUE(WaitForEvents());
327 }
328
329 // Exercises watch reconfiguration for the case that directories on the path
330 // are rapidly created.
331 TEST_F(FilePathWatcherTest, DirectoryChain) {
332 FilePath path(temp_dir_.path());
333 std::vector<std::string> dir_names;
334 for (int i = 0; i < 20; i++) {
335 std::string dir(StringPrintf("d%d", i));
336 dir_names.push_back(dir);
337 path = path.AppendASCII(dir);
338 }
339
340 FilePathWatcher watcher;
341 FilePath file(path.AppendASCII("file"));
342 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
343 ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get()));
344
345 FilePath sub_path(temp_dir_.path());
346 for (std::vector<std::string>::const_iterator d(dir_names.begin());
347 d != dir_names.end(); ++d) {
348 sub_path = sub_path.AppendASCII(*d);
349 ASSERT_TRUE(file_util::CreateDirectory(sub_path));
350 }
351 VLOG(1) << "Create File";
352 ASSERT_TRUE(WriteFile(file, "content"));
353 VLOG(1) << "Waiting for file creation";
354 ASSERT_TRUE(WaitForEvents());
355
356 ASSERT_TRUE(WriteFile(file, "content v2"));
357 VLOG(1) << "Waiting for file modification";
358 ASSERT_TRUE(WaitForEvents());
359 }
360
361 TEST_F(FilePathWatcherTest, DisappearingDirectory) {
362 FilePathWatcher watcher;
363 FilePath dir(temp_dir_.path().AppendASCII("dir"));
364 FilePath file(dir.AppendASCII("file"));
365 ASSERT_TRUE(file_util::CreateDirectory(dir));
366 ASSERT_TRUE(WriteFile(file, "content"));
367 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
368 ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get()));
369
370 ASSERT_TRUE(file_util::Delete(dir, true));
371 ASSERT_TRUE(WaitForEvents());
372 }
373
374 // Tests that a file that is deleted and reappears is tracked correctly.
375 TEST_F(FilePathWatcherTest, DeleteAndRecreate) {
376 ASSERT_TRUE(WriteFile(test_file(), "content"));
377 FilePathWatcher watcher;
378 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
379 ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get()));
380
381 ASSERT_TRUE(file_util::Delete(test_file(), false));
382 VLOG(1) << "Waiting for file deletion";
383 ASSERT_TRUE(WaitForEvents());
384
385 ASSERT_TRUE(WriteFile(test_file(), "content"));
386 VLOG(1) << "Waiting for file creation";
387 ASSERT_TRUE(WaitForEvents());
388 }
389
390 TEST_F(FilePathWatcherTest, WatchDirectory) {
391 FilePathWatcher watcher;
392 FilePath dir(temp_dir_.path().AppendASCII("dir"));
393 FilePath file1(dir.AppendASCII("file1"));
394 FilePath file2(dir.AppendASCII("file2"));
395 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
396 ASSERT_TRUE(SetupWatch(dir, &watcher, delegate.get()));
397
398 ASSERT_TRUE(file_util::CreateDirectory(dir));
399 VLOG(1) << "Waiting for directory creation";
400 ASSERT_TRUE(WaitForEvents());
401
402 ASSERT_TRUE(WriteFile(file1, "content"));
403 VLOG(1) << "Waiting for file1 creation";
404 ASSERT_TRUE(WaitForEvents());
405
406 #if !defined(OS_MACOSX)
407 // Mac implementation does not detect files modified in a directory.
408 ASSERT_TRUE(WriteFile(file1, "content v2"));
409 VLOG(1) << "Waiting for file1 modification";
410 ASSERT_TRUE(WaitForEvents());
411 #endif // !OS_MACOSX
412
413 ASSERT_TRUE(file_util::Delete(file1, false));
414 VLOG(1) << "Waiting for file1 deletion";
415 ASSERT_TRUE(WaitForEvents());
416
417 ASSERT_TRUE(WriteFile(file2, "content"));
418 VLOG(1) << "Waiting for file2 creation";
419 ASSERT_TRUE(WaitForEvents());
420 }
421
422 TEST_F(FilePathWatcherTest, MoveParent) {
423 FilePathWatcher file_watcher;
424 FilePathWatcher subdir_watcher;
425 FilePath dir(temp_dir_.path().AppendASCII("dir"));
426 FilePath dest(temp_dir_.path().AppendASCII("dest"));
427 FilePath subdir(dir.AppendASCII("subdir"));
428 FilePath file(subdir.AppendASCII("file"));
429 scoped_refptr<TestDelegate> file_delegate(new TestDelegate(collector()));
430 ASSERT_TRUE(SetupWatch(file, &file_watcher, file_delegate.get()));
431 scoped_refptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
432 ASSERT_TRUE(SetupWatch(subdir, &subdir_watcher, subdir_delegate.get()));
433
434 // Setup a directory hierarchy.
435 ASSERT_TRUE(file_util::CreateDirectory(subdir));
436 ASSERT_TRUE(WriteFile(file, "content"));
437 VLOG(1) << "Waiting for file creation";
438 ASSERT_TRUE(WaitForEvents());
439
440 // Move the parent directory.
441 file_util::Move(dir, dest);
442 VLOG(1) << "Waiting for directory move";
443 ASSERT_TRUE(WaitForEvents());
444 }
445
446 TEST_F(FilePathWatcherTest, MoveChild) {
447 FilePathWatcher file_watcher;
448 FilePathWatcher subdir_watcher;
449 FilePath source_dir(temp_dir_.path().AppendASCII("source"));
450 FilePath source_subdir(source_dir.AppendASCII("subdir"));
451 FilePath source_file(source_subdir.AppendASCII("file"));
452 FilePath dest_dir(temp_dir_.path().AppendASCII("dest"));
453 FilePath dest_subdir(dest_dir.AppendASCII("subdir"));
454 FilePath dest_file(dest_subdir.AppendASCII("file"));
455
456 // Setup a directory hierarchy.
457 ASSERT_TRUE(file_util::CreateDirectory(source_subdir));
458 ASSERT_TRUE(WriteFile(source_file, "content"));
459
460 scoped_refptr<TestDelegate> file_delegate(new TestDelegate(collector()));
461 ASSERT_TRUE(SetupWatch(dest_file, &file_watcher, file_delegate.get()));
462 scoped_refptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
463 ASSERT_TRUE(SetupWatch(dest_subdir, &subdir_watcher, subdir_delegate.get()));
464
465 // Move the directory into place, s.t. the watched file appears.
466 ASSERT_TRUE(file_util::Move(source_dir, dest_dir));
467 ASSERT_TRUE(WaitForEvents());
468 }
469
470 #if !defined(OS_LINUX)
471 // Linux implementation of FilePathWatcher doesn't catch attribute changes.
472 // http://crbug.com/78043
473
474 // Verify that changing attributes on a file is caught
475 TEST_F(FilePathWatcherTest, FileAttributesChanged) {
476 ASSERT_TRUE(WriteFile(test_file(), "content"));
477 FilePathWatcher watcher;
478 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
479 ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get()));
480
481 // Now make sure we get notified if the file is modified.
482 ASSERT_TRUE(file_util::MakeFileUnreadable(test_file()));
483 ASSERT_TRUE(WaitForEvents());
484 }
485
486 #endif // !OS_LINUX
487
488 enum Permission {
489 Read,
490 Write,
491 Execute
492 };
493
494 bool ChangeFilePermissions(const FilePath& path, Permission perm, bool allow) {
495 #if defined(OS_POSIX)
496 struct stat stat_buf;
497
498 if (stat(path.value().c_str(), &stat_buf) != 0)
499 return false;
500
501 mode_t mode = 0;
502 switch (perm) {
503 case Read:
504 mode = S_IRUSR | S_IRGRP | S_IROTH;
505 break;
506 case Write:
507 mode = S_IWUSR | S_IWGRP | S_IWOTH;
508 break;
509 case Execute:
510 mode = S_IXUSR | S_IXGRP | S_IXOTH;
511 break;
512 default:
513 ADD_FAILURE() << "unknown perm " << perm;
514 return false;
515 }
516 if (allow) {
517 stat_buf.st_mode |= mode;
518 } else {
519 stat_buf.st_mode &= ~mode;
520 }
521 return chmod(path.value().c_str(), stat_buf.st_mode) == 0;
522
523 #elif defined(OS_WIN)
524 PACL old_dacl;
525 PSECURITY_DESCRIPTOR security_descriptor;
526 if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
527 SE_FILE_OBJECT,
528 DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl,
529 NULL, &security_descriptor) != ERROR_SUCCESS)
530 return false;
531
532 DWORD mode = 0;
533 switch (perm) {
534 case Read:
535 mode = GENERIC_READ;
536 break;
537 case Write:
538 mode = GENERIC_WRITE;
539 break;
540 case Execute:
541 mode = GENERIC_EXECUTE;
542 break;
543 default:
544 ADD_FAILURE() << "unknown perm " << perm;
545 return false;
546 }
547
548 // Deny Read access for the current user.
549 EXPLICIT_ACCESS change;
550 change.grfAccessPermissions = mode;
551 change.grfAccessMode = allow ? GRANT_ACCESS : DENY_ACCESS;
552 change.grfInheritance = 0;
553 change.Trustee.pMultipleTrustee = NULL;
554 change.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
555 change.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
556 change.Trustee.TrusteeType = TRUSTEE_IS_USER;
557 change.Trustee.ptstrName = L"CURRENT_USER";
558
559 PACL new_dacl;
560 if (SetEntriesInAcl(1, &change, old_dacl, &new_dacl) != ERROR_SUCCESS) {
561 LocalFree(security_descriptor);
562 return false;
563 }
564
565 DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
566 SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
567 NULL, NULL, new_dacl, NULL);
568 LocalFree(security_descriptor);
569 LocalFree(new_dacl);
570
571 return rc == ERROR_SUCCESS;
572 #else
573 NOTIMPLEMENTED();
574 return false;
575 #endif
576 }
577
578 #if defined(OS_MACOSX)
579 // Linux implementation of FilePathWatcher doesn't catch attribute changes.
580 // http://crbug.com/78043
581 // Windows implementation of FilePathWatcher catches attribute changes that
582 // don't affect the path being watched.
583 // http://crbug.com/78045
584
585 // Verify that changing attributes on a directory works.
586 TEST_F(FilePathWatcherTest, DirAttributesChanged) {
587 FilePath test_dir1(temp_dir_.path().AppendASCII("DirAttributesChangedDir1"));
588 FilePath test_dir2(test_dir1.AppendASCII("DirAttributesChangedDir2"));
589 FilePath test_file(test_dir2.AppendASCII("DirAttributesChangedFile"));
590 // Setup a directory hierarchy.
591 ASSERT_TRUE(file_util::CreateDirectory(test_dir1));
592 ASSERT_TRUE(file_util::CreateDirectory(test_dir2));
593 ASSERT_TRUE(WriteFile(test_file, "content"));
594
595 FilePathWatcher watcher;
596 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
597 ASSERT_TRUE(SetupWatch(test_file, &watcher, delegate.get()));
598
599 // We should not get notified in this case as it hasn't affected our ability
600 // to access the file.
601 ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, false));
602 loop_.PostDelayedTask(FROM_HERE,
603 new MessageLoop::QuitTask,
604 TestTimeouts::tiny_timeout_ms());
605 ASSERT_FALSE(WaitForEvents());
606 ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, true));
607
608 // We should get notified in this case because filepathwatcher can no
609 // longer access the file
610 ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, false));
611 ASSERT_TRUE(WaitForEvents());
612 ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, true));
613 }
614
615 #endif // OS_MACOSX
616 } // namespace
OLDNEW
« no previous file with comments | « content/common/file_path_watcher/file_path_watcher.cc ('k') | content/common/file_path_watcher/file_path_watcher_inotify.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698