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: chrome/browser/file_path_watcher/file_path_watcher_browsertest.cc

Issue 6825063: Patch for bug 74983 (among others) to be applied to M11 696 branch. (Closed) Base URL: svn://svn.chromium.org/chrome/branches/696/src
Patch Set: intentionally disabled mac tests. tested by hand. 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) 2010 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 "chrome/browser/file_path_watcher/file_path_watcher.h"
6
7 #include <set>
8
9 #include "base/basictypes.h"
10 #include "base/file_path.h"
11 #include "base/file_util.h"
12 #include "base/message_loop.h"
13 #include "base/message_loop_proxy.h"
14 #include "base/path_service.h"
15 #include "base/scoped_temp_dir.h"
16 #include "base/string_util.h"
17 #include "base/stl_util-inl.h"
18 #include "base/synchronization/waitable_event.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 #if defined(OS_MACOSX)
22 // TODO(mnissler): There are flakes on Mac (http://crbug.com/54822) at least for
23 // FilePathWatcherTest.MultipleWatchersSingleFile.
24 #define MAYBE(name) FLAKY_ ## name
25 #else
26 #define MAYBE(name) name
27 #endif
28
29 namespace {
30
31 class TestDelegate;
32
33 // Aggregates notifications from the test delegates and breaks the message loop
34 // the test thread is waiting on once they all came in.
35 class NotificationCollector
36 : public base::RefCountedThreadSafe<NotificationCollector> {
37 public:
38 NotificationCollector()
39 : loop_(base::MessageLoopProxy::CreateForCurrentThread()) {}
40
41 // Called from the file thread by the delegates.
42 void OnChange(TestDelegate* delegate) {
43 loop_->PostTask(FROM_HERE,
44 NewRunnableMethod(this,
45 &NotificationCollector::RecordChange,
46 make_scoped_refptr(delegate)));
47 }
48
49 void Register(TestDelegate* delegate) {
50 delegates_.insert(delegate);
51 }
52
53 void Reset() {
54 signaled_.clear();
55 }
56
57 private:
58 void RecordChange(TestDelegate* delegate) {
59 ASSERT_TRUE(loop_->BelongsToCurrentThread());
60 ASSERT_TRUE(delegates_.count(delegate));
61 signaled_.insert(delegate);
62
63 // Check whether all delegates have been signaled.
64 if (signaled_ == delegates_)
65 loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
66 }
67
68 // Set of registered delegates.
69 std::set<TestDelegate*> delegates_;
70
71 // Set of signaled delegates.
72 std::set<TestDelegate*> signaled_;
73
74 // The loop we should break after all delegates signaled.
75 scoped_refptr<base::MessageLoopProxy> loop_;
76 };
77
78 // A mock FilePathWatcher::Delegate for testing. I'd rather use gmock, but it's
79 // not thread safe for setting expectations, so the test code couldn't safely
80 // reset expectations while the file watcher is running. In order to allow this,
81 // we keep simple thread safe status flags in TestDelegate.
82 class TestDelegate : public FilePathWatcher::Delegate {
83 public:
84 // The message loop specified by |loop| will be quit if a notification is
85 // received while the delegate is |armed_|. Note that the testing code must
86 // guarantee |loop| outlives the file thread on which OnFilePathChanged runs.
87 explicit TestDelegate(NotificationCollector* collector)
88 : collector_(collector) {
89 collector_->Register(this);
90 }
91
92 virtual void OnFilePathChanged(const FilePath&) {
93 collector_->OnChange(this);
94 }
95
96 private:
97 scoped_refptr<NotificationCollector> collector_;
98
99 DISALLOW_COPY_AND_ASSIGN(TestDelegate);
100 };
101
102 // A helper class for setting up watches on the file thread.
103 class SetupWatchTask : public Task {
104 public:
105 SetupWatchTask(const FilePath& target,
106 FilePathWatcher* watcher,
107 FilePathWatcher::Delegate* delegate,
108 bool* result,
109 base::WaitableEvent* completion)
110 : target_(target),
111 watcher_(watcher),
112 delegate_(delegate),
113 result_(result),
114 completion_(completion) {}
115
116 void Run() {
117 *result_ = watcher_->Watch(target_, delegate_);
118 completion_->Signal();
119 }
120
121 private:
122 const FilePath target_;
123 FilePathWatcher* watcher_;
124 FilePathWatcher::Delegate* delegate_;
125 bool* result_;
126 base::WaitableEvent* completion_;
127
128 DISALLOW_COPY_AND_ASSIGN(SetupWatchTask);
129 };
130
131 class FilePathWatcherTest : public testing::Test {
132 public:
133 // Implementation of FilePathWatcher on Mac requires UI loop.
134 FilePathWatcherTest()
135 : loop_(MessageLoop::TYPE_UI),
136 ui_thread_(BrowserThread::UI, &loop_) {
137 }
138
139 protected:
140 virtual void SetUp() {
141 // Create a separate file thread in order to test proper thread usage.
142 file_thread_.reset(new BrowserThread(BrowserThread::FILE));
143 file_thread_->Start();
144 temp_dir_.reset(new ScopedTempDir);
145 ASSERT_TRUE(temp_dir_->CreateUniqueTempDir());
146 collector_ = new NotificationCollector();
147 }
148
149 virtual void TearDown() {
150 loop_.RunAllPending();
151 file_thread_.reset();
152 }
153
154 FilePath test_file() {
155 return temp_dir_->path().AppendASCII("FilePathWatcherTest");
156 }
157
158 // Write |content| to |file|. Returns true on success.
159 bool WriteFile(const FilePath& file, const std::string& content) {
160 int write_size = file_util::WriteFile(file, content.c_str(),
161 content.length());
162 return write_size == static_cast<int>(content.length());
163 }
164
165 void SetupWatch(const FilePath& target,
166 FilePathWatcher* watcher,
167 FilePathWatcher::Delegate* delegate) {
168 base::WaitableEvent completion(false, false);
169 bool result;
170 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
171 new SetupWatchTask(target, watcher, delegate, &result, &completion));
172 completion.Wait();
173 ASSERT_TRUE(result);
174 }
175
176 void WaitForEvents() {
177 collector_->Reset();
178 loop_.Run();
179 }
180
181 NotificationCollector* collector() { return collector_.get(); }
182
183 MessageLoop loop_;
184 BrowserThread ui_thread_;
185 scoped_ptr<BrowserThread> file_thread_;
186 scoped_ptr<ScopedTempDir> temp_dir_;
187 scoped_refptr<NotificationCollector> collector_;
188 };
189
190 // Basic test: Create the file and verify that we notice.
191 TEST_F(FilePathWatcherTest, MAYBE(NewFile)) {
192 FilePathWatcher watcher;
193 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
194 SetupWatch(test_file(), &watcher, delegate.get());
195
196 ASSERT_TRUE(WriteFile(test_file(), "content"));
197 WaitForEvents();
198 }
199
200 // Verify that modifying the file is caught.
201 TEST_F(FilePathWatcherTest, MAYBE(ModifiedFile)) {
202 ASSERT_TRUE(WriteFile(test_file(), "content"));
203
204 FilePathWatcher watcher;
205 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
206 SetupWatch(test_file(), &watcher, delegate.get());
207
208 // Now make sure we get notified if the file is modified.
209 ASSERT_TRUE(WriteFile(test_file(), "new content"));
210 WaitForEvents();
211 }
212
213 // Verify that moving the file into place is caught.
214 TEST_F(FilePathWatcherTest, MAYBE(MovedFile)) {
215 FilePath source_file(temp_dir_->path().AppendASCII("source"));
216 ASSERT_TRUE(WriteFile(source_file, "content"));
217
218 FilePathWatcher watcher;
219 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
220 SetupWatch(test_file(), &watcher, delegate.get());
221
222 // Now make sure we get notified if the file is modified.
223 ASSERT_TRUE(file_util::Move(source_file, test_file()));
224 WaitForEvents();
225 }
226
227 TEST_F(FilePathWatcherTest, MAYBE(DeletedFile)) {
228 ASSERT_TRUE(WriteFile(test_file(), "content"));
229
230 FilePathWatcher watcher;
231 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
232 SetupWatch(test_file(), &watcher, delegate.get());
233
234 // Now make sure we get notified if the file is deleted.
235 file_util::Delete(test_file(), false);
236 WaitForEvents();
237 }
238
239 // Used by the DeleteDuringNotify test below.
240 // Deletes the FilePathWatcher when it's notified.
241 class Deleter : public FilePathWatcher::Delegate {
242 public:
243 Deleter(FilePathWatcher* watcher, MessageLoop* loop)
244 : watcher_(watcher),
245 loop_(loop) {
246 }
247
248 virtual void OnFilePathChanged(const FilePath& path) {
249 watcher_.reset(NULL);
250 loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
251 }
252
253 scoped_ptr<FilePathWatcher> watcher_;
254 MessageLoop* loop_;
255 };
256
257 // Verify that deleting a watcher during the callback doesn't crash.
258 TEST_F(FilePathWatcherTest, DeleteDuringNotify) {
259 FilePathWatcher* watcher = new FilePathWatcher;
260 // Takes ownership of watcher.
261 scoped_refptr<Deleter> deleter(new Deleter(watcher, &loop_));
262 SetupWatch(test_file(), watcher, deleter.get());
263
264 ASSERT_TRUE(WriteFile(test_file(), "content"));
265 WaitForEvents();
266
267 // We win if we haven't crashed yet.
268 // Might as well double-check it got deleted, too.
269 ASSERT_TRUE(deleter->watcher_.get() == NULL);
270 }
271
272 // Verify that deleting the watcher works even if there is a pending
273 // notification.
274 TEST_F(FilePathWatcherTest, DestroyWithPendingNotification) {
275 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
276 FilePathWatcher* watcher = new FilePathWatcher;
277 SetupWatch(test_file(), watcher, delegate.get());
278 ASSERT_TRUE(WriteFile(test_file(), "content"));
279 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, watcher);
280 }
281
282 TEST_F(FilePathWatcherTest, MAYBE(MultipleWatchersSingleFile)) {
283 FilePathWatcher watcher1, watcher2;
284 scoped_refptr<TestDelegate> delegate1(new TestDelegate(collector()));
285 scoped_refptr<TestDelegate> delegate2(new TestDelegate(collector()));
286 SetupWatch(test_file(), &watcher1, delegate1.get());
287 SetupWatch(test_file(), &watcher2, delegate2.get());
288
289 ASSERT_TRUE(WriteFile(test_file(), "content"));
290 WaitForEvents();
291 }
292
293 // Verify that watching a file whose parent directory doesn't exist yet works if
294 // the directory and file are created eventually.
295 TEST_F(FilePathWatcherTest, NonExistentDirectory) {
296 FilePathWatcher watcher;
297 FilePath dir(temp_dir_->path().AppendASCII("dir"));
298 FilePath file(dir.AppendASCII("file"));
299 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
300 SetupWatch(file, &watcher, delegate.get());
301
302 ASSERT_TRUE(file_util::CreateDirectory(dir));
303
304 ASSERT_TRUE(WriteFile(file, "content"));
305 VLOG(1) << "Waiting for file creation";
306 WaitForEvents();
307
308 ASSERT_TRUE(WriteFile(file, "content v2"));
309 VLOG(1) << "Waiting for file change";
310 WaitForEvents();
311
312 ASSERT_TRUE(file_util::Delete(file, false));
313 VLOG(1) << "Waiting for file deletion";
314 WaitForEvents();
315 }
316
317 // Exercises watch reconfiguration for the case that directories on the path
318 // are rapidly created.
319 TEST_F(FilePathWatcherTest, DirectoryChain) {
320 FilePath path(temp_dir_->path());
321 std::vector<std::string> dir_names;
322 for (int i = 0; i < 20; i++) {
323 std::string dir(StringPrintf("d%d", i));
324 dir_names.push_back(dir);
325 path = path.AppendASCII(dir);
326 }
327
328 FilePathWatcher watcher;
329 FilePath file(path.AppendASCII("file"));
330 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
331 SetupWatch(file, &watcher, delegate.get());
332
333 FilePath sub_path(temp_dir_->path());
334 for (std::vector<std::string>::const_iterator d(dir_names.begin());
335 d != dir_names.end(); ++d) {
336 sub_path = sub_path.AppendASCII(*d);
337 ASSERT_TRUE(file_util::CreateDirectory(sub_path));
338 }
339 ASSERT_TRUE(WriteFile(file, "content"));
340 VLOG(1) << "Waiting for file creation";
341 WaitForEvents();
342
343 ASSERT_TRUE(WriteFile(file, "content v2"));
344 VLOG(1) << "Waiting for file modification";
345 WaitForEvents();
346 }
347
348 TEST_F(FilePathWatcherTest, DisappearingDirectory) {
349 FilePathWatcher watcher;
350 FilePath dir(temp_dir_->path().AppendASCII("dir"));
351 FilePath file(dir.AppendASCII("file"));
352 ASSERT_TRUE(file_util::CreateDirectory(dir));
353 ASSERT_TRUE(WriteFile(file, "content"));
354 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
355 SetupWatch(file, &watcher, delegate.get());
356
357 ASSERT_TRUE(file_util::Delete(dir, true));
358 WaitForEvents();
359 }
360
361 // Tests that a file that is deleted and reappears is tracked correctly.
362 TEST_F(FilePathWatcherTest, DeleteAndRecreate) {
363 ASSERT_TRUE(WriteFile(test_file(), "content"));
364 FilePathWatcher watcher;
365 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
366 SetupWatch(test_file(), &watcher, delegate.get());
367
368 ASSERT_TRUE(file_util::Delete(test_file(), false));
369 VLOG(1) << "Waiting for file deletion";
370 WaitForEvents();
371
372 ASSERT_TRUE(WriteFile(test_file(), "content"));
373 VLOG(1) << "Waiting for file creation";
374 WaitForEvents();
375 }
376
377 TEST_F(FilePathWatcherTest, WatchDirectory) {
378 FilePathWatcher watcher;
379 FilePath dir(temp_dir_->path().AppendASCII("dir"));
380 FilePath file1(dir.AppendASCII("file1"));
381 FilePath file2(dir.AppendASCII("file2"));
382 scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
383 SetupWatch(dir, &watcher, delegate.get());
384
385 ASSERT_TRUE(file_util::CreateDirectory(dir));
386 VLOG(1) << "Waiting for directory creation";
387 WaitForEvents();
388
389 ASSERT_TRUE(WriteFile(file1, "content"));
390 VLOG(1) << "Waiting for file1 creation";
391 WaitForEvents();
392
393 ASSERT_TRUE(WriteFile(file1, "content v2"));
394 VLOG(1) << "Waiting for file1 modification";
395 WaitForEvents();
396
397 ASSERT_TRUE(file_util::Delete(file1, false));
398 VLOG(1) << "Waiting for file1 deletion";
399 WaitForEvents();
400
401 ASSERT_TRUE(WriteFile(file2, "content"));
402 VLOG(1) << "Waiting for file2 creation";
403 WaitForEvents();
404 }
405
406 TEST_F(FilePathWatcherTest, MoveParent) {
407 FilePathWatcher file_watcher;
408 FilePathWatcher subdir_watcher;
409 FilePath dir(temp_dir_->path().AppendASCII("dir"));
410 FilePath dest(temp_dir_->path().AppendASCII("dest"));
411 FilePath subdir(dir.AppendASCII("subdir"));
412 FilePath file(subdir.AppendASCII("file"));
413 scoped_refptr<TestDelegate> file_delegate(new TestDelegate(collector()));
414 SetupWatch(file, &file_watcher, file_delegate.get());
415 scoped_refptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
416 SetupWatch(subdir, &subdir_watcher, subdir_delegate.get());
417
418 // Setup a directory hierarchy.
419 ASSERT_TRUE(file_util::CreateDirectory(subdir));
420 ASSERT_TRUE(WriteFile(file, "content"));
421 VLOG(1) << "Waiting for file creation";
422 WaitForEvents();
423
424 // Move the parent directory.
425 file_util::Move(dir, dest);
426 VLOG(1) << "Waiting for directory move";
427 WaitForEvents();
428 }
429
430 TEST_F(FilePathWatcherTest, MoveChild) {
431 FilePathWatcher file_watcher;
432 FilePathWatcher subdir_watcher;
433 FilePath source_dir(temp_dir_->path().AppendASCII("source"));
434 FilePath source_subdir(source_dir.AppendASCII("subdir"));
435 FilePath source_file(source_subdir.AppendASCII("file"));
436 FilePath dest_dir(temp_dir_->path().AppendASCII("dest"));
437 FilePath dest_subdir(dest_dir.AppendASCII("subdir"));
438 FilePath dest_file(dest_subdir.AppendASCII("file"));
439
440 // Setup a directory hierarchy.
441 ASSERT_TRUE(file_util::CreateDirectory(source_subdir));
442 ASSERT_TRUE(WriteFile(source_file, "content"));
443
444 scoped_refptr<TestDelegate> file_delegate(new TestDelegate(collector()));
445 SetupWatch(dest_file, &file_watcher, file_delegate.get());
446 scoped_refptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
447 SetupWatch(dest_subdir, &subdir_watcher, subdir_delegate.get());
448
449 // Move the directory into place, s.t. the watched file appears.
450 ASSERT_TRUE(file_util::Move(source_dir, dest_dir));
451 WaitForEvents();
452 }
453
454 } // namespace
OLDNEW
« no previous file with comments | « chrome/browser/file_path_watcher/file_path_watcher.cc ('k') | chrome/browser/file_path_watcher/file_path_watcher_inotify.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698