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

Side by Side Diff: base/file_watcher_unittest.cc

Issue 661359: Add a FileWatcher to base/. (Closed)
Patch Set: 2s Created 10 years, 9 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/file_watcher_stub.cc ('k') | base/file_watcher_win.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) 2008 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/file_watcher.h"
6
7 #include <limits>
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/path_service.h"
14 #include "base/platform_thread.h"
15 #include "base/scoped_temp_dir.h"
16 #include "base/string_util.h"
17 #include "base/thread.h"
18 #if defined(OS_WIN)
19 #include "base/win_util.h"
20 #endif // defined(OS_WIN)
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 namespace {
24
25 // For tests where we wait a bit to verify nothing happened
26 const int kWaitForEventTime = 500;
27
28 class FileWatcherTest : public testing::Test {
29 public:
30 // Implementation of FileWatcher on Mac requires UI loop.
31 FileWatcherTest()
32 : loop_(MessageLoop::TYPE_UI),
33 notified_delegates_(0),
34 expected_notified_delegates_(0) {
35 }
36
37 void OnTestDelegateFirstNotification() {
38 notified_delegates_++;
39 if (notified_delegates_ >= expected_notified_delegates_)
40 MessageLoop::current()->Quit();
41 }
42
43 protected:
44 virtual void SetUp() {
45 temp_dir_.reset(new ScopedTempDir);
46 ASSERT_TRUE(temp_dir_->CreateUniqueTempDir());
47 }
48
49 FilePath test_file() {
50 return temp_dir_->path().AppendASCII("FileWatcherTest");
51 }
52
53 virtual void TearDown() {
54 // Make sure there are no tasks in the loop.
55 loop_.RunAllPending();
56 }
57
58 // Write |content| to the test file. Returns true on success.
59 bool WriteTestFile(const std::string& content) {
60 int write_size = file_util::WriteFile(test_file(), content.c_str(),
61 content.length());
62 return write_size == static_cast<int>(content.length());
63 }
64
65 void SetExpectedNumberOfNotifiedDelegates(int n) {
66 notified_delegates_ = 0;
67 expected_notified_delegates_ = n;
68 }
69
70 void VerifyExpectedNumberOfNotifiedDelegates() {
71 // Check that we get at least the expected number of notified delegates.
72 if (expected_notified_delegates_ - notified_delegates_ > 0)
73 loop_.Run();
74 EXPECT_EQ(expected_notified_delegates_, notified_delegates_);
75 }
76
77 void VerifyNoExtraNotifications() {
78 // Check that we get no more than the expected number of notified delegates.
79 loop_.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask,
80 kWaitForEventTime);
81 loop_.Run();
82 EXPECT_EQ(expected_notified_delegates_, notified_delegates_);
83 }
84
85 // We need this function for reliable tests on Mac OS X. FSEvents API
86 // has a latency interval and can merge multiple events into one,
87 // and we need a clear distinction between events triggered by test setup code
88 // and test code.
89 void SyncIfPOSIX() {
90 #if defined(OS_POSIX)
91 sync();
92 #endif // defined(OS_POSIX)
93 }
94
95 MessageLoop loop_;
96 scoped_ptr<ScopedTempDir> temp_dir_;
97
98 // The number of test delegates which received their notification.
99 int notified_delegates_;
100
101 // The number of notified test delegates after which we quit the message loop.
102 int expected_notified_delegates_;
103 };
104
105 class TestDelegate : public FileWatcher::Delegate {
106 public:
107 explicit TestDelegate(FileWatcherTest* test)
108 : test_(test),
109 got_notification_(false),
110 original_thread_id_(PlatformThread::CurrentId()) {
111 }
112
113 bool got_notification() const {
114 return got_notification_;
115 }
116
117 void reset() {
118 got_notification_ = false;
119 }
120
121 virtual void OnFileChanged(const FilePath& path) {
122 EXPECT_EQ(original_thread_id_, PlatformThread::CurrentId());
123 if (!got_notification_)
124 test_->OnTestDelegateFirstNotification();
125 got_notification_ = true;
126 }
127
128 private:
129 // Hold a pointer to current test fixture to inform it on first notification.
130 FileWatcherTest* test_;
131
132 // Set to true after first notification.
133 bool got_notification_;
134
135 // Keep track of original thread id to verify that callbacks are called
136 // on the same thread.
137 PlatformThreadId original_thread_id_;
138 };
139
140 // Basic test: Create the file and verify that we notice.
141 TEST_F(FileWatcherTest, NewFile) {
142 FileWatcher watcher;
143 TestDelegate delegate(this);
144 ASSERT_TRUE(watcher.Watch(test_file(), &delegate, NULL));
145
146 SetExpectedNumberOfNotifiedDelegates(1);
147 ASSERT_TRUE(WriteTestFile("content"));
148 VerifyExpectedNumberOfNotifiedDelegates();
149 }
150
151 // Verify that modifying the file is caught.
152 TEST_F(FileWatcherTest, ModifiedFile) {
153 ASSERT_TRUE(WriteTestFile("content"));
154 SyncIfPOSIX();
155
156 FileWatcher watcher;
157 TestDelegate delegate(this);
158 ASSERT_TRUE(watcher.Watch(test_file(), &delegate, NULL));
159
160 // Now make sure we get notified if the file is modified.
161 SetExpectedNumberOfNotifiedDelegates(1);
162 ASSERT_TRUE(WriteTestFile("new content"));
163 VerifyExpectedNumberOfNotifiedDelegates();
164 }
165
166 TEST_F(FileWatcherTest, DeletedFile) {
167 ASSERT_TRUE(WriteTestFile("content"));
168 SyncIfPOSIX();
169
170 FileWatcher watcher;
171 TestDelegate delegate(this);
172 ASSERT_TRUE(watcher.Watch(test_file(), &delegate, NULL));
173
174 // Now make sure we get notified if the file is deleted.
175 SetExpectedNumberOfNotifiedDelegates(1);
176 file_util::Delete(test_file(), false);
177 VerifyExpectedNumberOfNotifiedDelegates();
178 }
179
180 // Verify that letting the watcher go out of scope stops notifications.
181 TEST_F(FileWatcherTest, Unregister) {
182 TestDelegate delegate(this);
183
184 {
185 FileWatcher watcher;
186 ASSERT_TRUE(watcher.Watch(test_file(), &delegate, NULL));
187
188 // And then let it fall out of scope, clearing its watch.
189 }
190
191 // Write a file to the test dir.
192 SetExpectedNumberOfNotifiedDelegates(0);
193 ASSERT_TRUE(WriteTestFile("content"));
194 VerifyExpectedNumberOfNotifiedDelegates();
195 VerifyNoExtraNotifications();
196 }
197
198
199 namespace {
200 // Used by the DeleteDuringNotify test below.
201 // Deletes the FileWatcher when it's notified.
202 class Deleter : public FileWatcher::Delegate {
203 public:
204 Deleter(FileWatcher* watcher, MessageLoop* loop)
205 : watcher_(watcher),
206 loop_(loop) {
207 }
208
209 virtual void OnFileChanged(const FilePath& path) {
210 watcher_.reset(NULL);
211 loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
212 }
213
214 scoped_ptr<FileWatcher> watcher_;
215 MessageLoop* loop_;
216 };
217 } // anonymous namespace
218
219 // Verify that deleting a watcher during the callback doesn't crash.
220 TEST_F(FileWatcherTest, DeleteDuringNotify) {
221 FileWatcher* watcher = new FileWatcher;
222 Deleter deleter(watcher, &loop_); // Takes ownership of watcher.
223 ASSERT_TRUE(watcher->Watch(test_file(), &deleter, NULL));
224
225 ASSERT_TRUE(WriteTestFile("content"));
226 loop_.Run();
227
228 // We win if we haven't crashed yet.
229 // Might as well double-check it got deleted, too.
230 ASSERT_TRUE(deleter.watcher_.get() == NULL);
231 }
232
233 TEST_F(FileWatcherTest, BackendLoop) {
234 base::Thread thread("test");
235 ASSERT_TRUE(thread.Start());
236
237 FileWatcher watcher;
238 TestDelegate delegate(this);
239 ASSERT_TRUE(watcher.Watch(test_file(), &delegate, thread.message_loop()));
240 }
241
242 TEST_F(FileWatcherTest, MultipleWatchersSingleFile) {
243 FileWatcher watcher1, watcher2;
244 TestDelegate delegate1(this), delegate2(this);
245 ASSERT_TRUE(watcher1.Watch(test_file(), &delegate1, NULL));
246 ASSERT_TRUE(watcher2.Watch(test_file(), &delegate2, NULL));
247
248 SetExpectedNumberOfNotifiedDelegates(2);
249 ASSERT_TRUE(WriteTestFile("content"));
250 VerifyExpectedNumberOfNotifiedDelegates();
251 }
252
253 // Verify that watching a file who's parent directory doesn't exist
254 // fails, but doesn't asssert.
255 TEST_F(FileWatcherTest, NonExistentDirectory) {
256 FileWatcher watcher;
257 ASSERT_FALSE(watcher.Watch(test_file().AppendASCII("FileToWatch"),
258 NULL, NULL));
259 }
260
261 } // namespace
OLDNEW
« no previous file with comments | « base/file_watcher_stub.cc ('k') | base/file_watcher_win.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698