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

Side by Side Diff: base/directory_watcher_unittest.cc

Issue 99057: Add DirectoryWatcher implementation for Mac. (Closed)
Patch Set: scoped_cftyperef Created 11 years, 7 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/directory_watcher_mac.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/directory_watcher.h" 5 #include "base/directory_watcher.h"
6 6
7 #include <limits> 7 #include <limits>
8 8
9 #include "base/basictypes.h" 9 #include "base/basictypes.h"
10 #include "base/file_path.h" 10 #include "base/file_path.h"
11 #include "base/file_util.h" 11 #include "base/file_util.h"
12 #include "base/message_loop.h" 12 #include "base/message_loop.h"
13 #include "base/path_service.h" 13 #include "base/path_service.h"
14 #include "base/platform_thread.h" 14 #include "base/platform_thread.h"
15 #include "base/string_util.h" 15 #include "base/string_util.h"
16 #if defined(OS_WIN) 16 #if defined(OS_WIN)
17 #include "base/win_util.h" 17 #include "base/win_util.h"
18 #endif // defined(OS_WIN) 18 #endif // defined(OS_WIN)
19 #include "testing/gtest/include/gtest/gtest.h" 19 #include "testing/gtest/include/gtest/gtest.h"
20 20
21 namespace { 21 namespace {
22 22
23 // For tests where we wait a bit to verify nothing happened 23 // For tests where we wait a bit to verify nothing happened
24 const int kWaitForEventTime = 500; 24 const int kWaitForEventTime = 1000;
25 25
26 } // namespace 26 class DirectoryWatcherTest : public testing::Test {
27 public:
28 // Implementation of DirectoryWatcher on Mac requires UI loop.
29 DirectoryWatcherTest() : loop_(MessageLoop::TYPE_UI) {
30 }
27 31
28 class DirectoryWatcherTest : public testing::Test, 32 void OnTestDelegateFirstNotification(const FilePath& path) {
29 public DirectoryWatcher::Delegate { 33 notified_delegates_++;
34 if (notified_delegates_ >= expected_notified_delegates_)
35 MessageLoop::current()->Quit();
36 }
37
30 protected: 38 protected:
31 virtual void SetUp() { 39 virtual void SetUp() {
32 // Name a subdirectory of the temp directory. 40 // Name a subdirectory of the temp directory.
33 FilePath path; 41 FilePath path;
34 ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &path)); 42 ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &path));
35 test_dir_ = path.Append(FILE_PATH_LITERAL("DirectoryWatcherTest")); 43 test_dir_ = path.Append(FILE_PATH_LITERAL("DirectoryWatcherTest"));
36 44
37 // Create a fresh, empty copy of this directory. 45 // Create a fresh, empty copy of this directory.
38 file_util::Delete(test_dir_, true); 46 file_util::Delete(test_dir_, true);
39 file_util::CreateDirectory(test_dir_); 47 file_util::CreateDirectory(test_dir_);
40
41 directory_mods_ = 0;
42 quit_mod_count_ = 0;
43
44 original_thread_id_ = PlatformThread::CurrentId();
45 }
46
47 virtual void OnDirectoryChanged(const FilePath& path) {
48 EXPECT_EQ(original_thread_id_, PlatformThread::CurrentId());
49 ++directory_mods_;
50 // The exact number is verified by VerifyExpectedNumberOfModifications.
51 // Sometimes we don't want such check, see WaitForFirstNotification.
52 if (directory_mods_ >= quit_mod_count_)
53 MessageLoop::current()->Quit();
54 } 48 }
55 49
56 virtual void TearDown() { 50 virtual void TearDown() {
57 // Make sure there are no tasks in the loop. 51 // Make sure there are no tasks in the loop.
58 loop_.RunAllPending(); 52 loop_.RunAllPending();
59 53
60 // Clean up test directory. 54 // Clean up test directory.
61 ASSERT_TRUE(file_util::Delete(test_dir_, true)); 55 ASSERT_TRUE(file_util::Delete(test_dir_, true));
62 ASSERT_FALSE(file_util::PathExists(test_dir_)); 56 ASSERT_FALSE(file_util::PathExists(test_dir_));
63 } 57 }
64 58
65 // Write |content| to a file under the test directory. 59 // Write |content| to a file under the test directory.
66 void WriteTestDirFile(const FilePath::StringType& filename, 60 void WriteTestDirFile(const FilePath::StringType& filename,
67 const std::string& content) { 61 const std::string& content) {
68 FilePath path = test_dir_.Append(filename); 62 FilePath path = test_dir_.Append(filename);
69 file_util::WriteFile(path, content.c_str(), content.length()); 63 file_util::WriteFile(path, content.c_str(), content.length());
70 } 64 }
71 65
72 void SetExpectedNumberOfModifications(int n) { 66 void SetExpectedNumberOfNotifiedDelegates(int n) {
73 quit_mod_count_ = n; 67 notified_delegates_ = 0;
68 expected_notified_delegates_ = n;
74 } 69 }
75 70
76 void VerifyExpectedNumberOfModifications() { 71 void VerifyExpectedNumberOfNotifiedDelegates() {
77 // Check that we get at least the expected number of notifications. 72 // Check that we get at least the expected number of notified delegates.
78 if (quit_mod_count_ - directory_mods_ > 0) 73 if (expected_notified_delegates_ - notified_delegates_ > 0)
79 loop_.Run(); 74 loop_.Run();
80 75
81 // Check that we get no more than the expected number of notifications. 76 // Check that we get no more than the expected number of notified delegates.
82 loop_.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask, 77 loop_.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask,
83 kWaitForEventTime); 78 kWaitForEventTime);
84 loop_.Run(); 79 loop_.Run();
85 EXPECT_EQ(quit_mod_count_, directory_mods_); 80 EXPECT_EQ(expected_notified_delegates_, notified_delegates_);
86 } 81 }
87 82
88 // Only use this function if you don't care about getting 83 // We need this function for reliable tests on Mac OS X. FSEvents API
89 // too many notifications. Useful for tests where you get 84 // has a latency interval and can merge multiple events into one,
90 // different number of notifications on different platforms. 85 // and we need a clear distinction between events triggered by test setup code
91 void WaitForFirstNotification() { 86 // and test code.
92 directory_mods_ = 0; 87 void SyncIfPOSIX() {
93 SetExpectedNumberOfModifications(1); 88 #if defined(OS_POSIX)
94 loop_.Run(); 89 sync();
90 #endif // defined(OS_POSIX)
95 } 91 }
96 92
97 MessageLoop loop_; 93 MessageLoop loop_;
98 94
99 // The path to a temporary directory used for testing. 95 // The path to a temporary directory used for testing.
100 FilePath test_dir_; 96 FilePath test_dir_;
101 97
102 // The number of times a directory modification has been observed. 98 // The number of test delegates which received their notification.
103 int directory_mods_; 99 int notified_delegates_;
104 100
105 // The number of directory mods which, when reached, cause us to quit 101 // The number of notified test delegates after which we quit the message loop.
106 // our message loop. 102 int expected_notified_delegates_;
107 int quit_mod_count_; 103 };
104
105 class TestDelegate : public DirectoryWatcher::Delegate {
106 public:
107 TestDelegate(DirectoryWatcherTest* 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 OnDirectoryChanged(const FilePath& path) {
122 EXPECT_EQ(original_thread_id_, PlatformThread::CurrentId());
123 if (!got_notification_)
124 test_->OnTestDelegateFirstNotification(path);
125 got_notification_ = true;
126 }
127
128 private:
129 // Hold a pointer to current test fixture to inform it on first notification.
130 DirectoryWatcherTest* test_;
131
132 // Set to true after first notification.
133 bool got_notification_;
108 134
109 // Keep track of original thread id to verify that callbacks are called 135 // Keep track of original thread id to verify that callbacks are called
110 // on the same thread. 136 // on the same thread.
111 PlatformThreadId original_thread_id_; 137 PlatformThreadId original_thread_id_;
112 }; 138 };
113 139
114 // Basic test: add a file and verify we notice it. 140 // Basic test: add a file and verify we notice it.
115 TEST_F(DirectoryWatcherTest, NewFile) { 141 TEST_F(DirectoryWatcherTest, NewFile) {
116 DirectoryWatcher watcher; 142 DirectoryWatcher watcher;
117 ASSERT_TRUE(watcher.Watch(test_dir_, this, false)); 143 TestDelegate delegate(this);
144 ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false));
118 145
119 SetExpectedNumberOfModifications(2); 146 SetExpectedNumberOfNotifiedDelegates(1);
120 WriteTestDirFile(FILE_PATH_LITERAL("test_file"), "some content"); 147 WriteTestDirFile(FILE_PATH_LITERAL("test_file"), "some content");
121 VerifyExpectedNumberOfModifications(); 148 VerifyExpectedNumberOfNotifiedDelegates();
122 } 149 }
123 150
124 // Verify that modifying a file is caught. 151 // Verify that modifying a file is caught.
125 TEST_F(DirectoryWatcherTest, ModifiedFile) { 152 TEST_F(DirectoryWatcherTest, ModifiedFile) {
153 // Write a file to the test dir.
154 WriteTestDirFile(FILE_PATH_LITERAL("test_file"), "some content");
155
156 SyncIfPOSIX();
157
126 DirectoryWatcher watcher; 158 DirectoryWatcher watcher;
127 ASSERT_TRUE(watcher.Watch(test_dir_, this, false)); 159 TestDelegate delegate(this);
128 160 ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false));
129 // Write a file to the test dir.
130 SetExpectedNumberOfModifications(2);
131 WriteTestDirFile(FILE_PATH_LITERAL("test_file"), "some content");
132 VerifyExpectedNumberOfModifications();
133 161
134 // Now make sure we get notified if the file is modified. 162 // Now make sure we get notified if the file is modified.
163 SetExpectedNumberOfNotifiedDelegates(1);
135 WriteTestDirFile(FILE_PATH_LITERAL("test_file"), "some new content"); 164 WriteTestDirFile(FILE_PATH_LITERAL("test_file"), "some new content");
136 // Use a more forgiving function to check because on Linux you will get 165 VerifyExpectedNumberOfNotifiedDelegates();
137 // 1 notification, and on Windows 2 (and nothing seems to convince it to
138 // send less notifications).
139 WaitForFirstNotification();
140 } 166 }
141 167
142 // Verify that letting the watcher go out of scope stops notifications. 168 // Verify that letting the watcher go out of scope stops notifications.
143 TEST_F(DirectoryWatcherTest, Unregister) { 169 TEST_F(DirectoryWatcherTest, Unregister) {
170 TestDelegate delegate(this);
171
144 { 172 {
145 DirectoryWatcher watcher; 173 DirectoryWatcher watcher;
146 ASSERT_TRUE(watcher.Watch(test_dir_, this, false)); 174 ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false));
147 175
148 // And then let it fall out of scope, clearing its watch. 176 // And then let it fall out of scope, clearing its watch.
149 } 177 }
150 178
151 // Write a file to the test dir. 179 // Write a file to the test dir.
152 SetExpectedNumberOfModifications(0); 180 SetExpectedNumberOfNotifiedDelegates(0);
153 WriteTestDirFile(FILE_PATH_LITERAL("test_file"), "some content"); 181 WriteTestDirFile(FILE_PATH_LITERAL("test_file"), "some content");
154 VerifyExpectedNumberOfModifications(); 182 VerifyExpectedNumberOfNotifiedDelegates();
155 } 183 }
156 184
157 TEST_F(DirectoryWatcherTest, SubDirRecursive) { 185 TEST_F(DirectoryWatcherTest, SubDirRecursive) {
158 FilePath subdir(FILE_PATH_LITERAL("SubDir")); 186 FilePath subdir(FILE_PATH_LITERAL("SubDir"));
159 ASSERT_TRUE(file_util::CreateDirectory(test_dir_.Append(subdir))); 187 ASSERT_TRUE(file_util::CreateDirectory(test_dir_.Append(subdir)));
160 188
161 #if !defined(OS_WIN) 189 #if defined(OS_LINUX)
162 // TODO(port): Recursive watches are not implemented on Linux. 190 // TODO(port): Recursive watches are not implemented on Linux.
163 return; 191 return;
164 #endif // !defined(OS_WIN) 192 #endif // !defined(OS_WIN)
165 193
194 SyncIfPOSIX();
195
166 // Verify that modifications to a subdirectory are noticed by recursive watch. 196 // Verify that modifications to a subdirectory are noticed by recursive watch.
197 TestDelegate delegate(this);
167 DirectoryWatcher watcher; 198 DirectoryWatcher watcher;
168 ASSERT_TRUE(watcher.Watch(test_dir_, this, true)); 199 ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, true));
169 // Write a file to the subdir. 200 // Write a file to the subdir.
170 SetExpectedNumberOfModifications(2); 201 SetExpectedNumberOfNotifiedDelegates(1);
171 FilePath test_path = subdir.AppendASCII("test_file"); 202 FilePath test_path = subdir.AppendASCII("test_file");
172 WriteTestDirFile(test_path.value(), "some content"); 203 WriteTestDirFile(test_path.value(), "some content");
173 VerifyExpectedNumberOfModifications(); 204 VerifyExpectedNumberOfNotifiedDelegates();
174 } 205 }
175 206
176 TEST_F(DirectoryWatcherTest, SubDirNonRecursive) { 207 TEST_F(DirectoryWatcherTest, SubDirNonRecursive) {
177 #if defined(OS_WIN) 208 #if defined(OS_WIN)
178 // Disable this test for earlier version of Windows. It turned out to be 209 // Disable this test for earlier version of Windows. It turned out to be
179 // very difficult to create a reliable test for them. 210 // very difficult to create a reliable test for them.
180 if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) 211 if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA)
181 return; 212 return;
182 #endif // defined(OS_WIN) 213 #endif // defined(OS_WIN)
183 214
184 FilePath subdir(FILE_PATH_LITERAL("SubDir")); 215 FilePath subdir(FILE_PATH_LITERAL("SubDir"));
185 ASSERT_TRUE(file_util::CreateDirectory(test_dir_.Append(subdir))); 216 ASSERT_TRUE(file_util::CreateDirectory(test_dir_.Append(subdir)));
186 217
187 // Create a test file before the test. On Windows we get a notification 218 // Create a test file before the test. On Windows we get a notification
188 // when creating a file in a subdir even with a non-recursive watch. 219 // when creating a file in a subdir even with a non-recursive watch.
189 FilePath test_path = subdir.AppendASCII("test_file"); 220 FilePath test_path = subdir.AppendASCII("test_file");
190 WriteTestDirFile(test_path.value(), "some content"); 221 WriteTestDirFile(test_path.value(), "some content");
191 222
223 SyncIfPOSIX();
224
192 // Verify that modifications to a subdirectory are not noticed 225 // Verify that modifications to a subdirectory are not noticed
193 // by a not-recursive watch. 226 // by a not-recursive watch.
194 DirectoryWatcher watcher; 227 DirectoryWatcher watcher;
195 ASSERT_TRUE(watcher.Watch(test_dir_, this, false)); 228 TestDelegate delegate(this);
229 ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false));
196 230
197 // Modify the test file. There should be no notifications. 231 // Modify the test file. There should be no notifications.
198 SetExpectedNumberOfModifications(0); 232 SetExpectedNumberOfNotifiedDelegates(0);
199 WriteTestDirFile(test_path.value(), "some other content"); 233 WriteTestDirFile(test_path.value(), "some other content");
200 VerifyExpectedNumberOfModifications(); 234 VerifyExpectedNumberOfNotifiedDelegates();
201 } 235 }
202 236
203 namespace { 237 namespace {
204 // Used by the DeleteDuringNotify test below. 238 // Used by the DeleteDuringNotify test below.
205 // Deletes the DirectoryWatcher when it's notified. 239 // Deletes the DirectoryWatcher when it's notified.
206 class Deleter : public DirectoryWatcher::Delegate { 240 class Deleter : public DirectoryWatcher::Delegate {
207 public: 241 public:
208 Deleter(DirectoryWatcher* watcher, MessageLoop* loop) 242 Deleter(DirectoryWatcher* watcher, MessageLoop* loop)
209 : watcher_(watcher), 243 : watcher_(watcher),
210 loop_(loop) { 244 loop_(loop) {
(...skipping 18 matching lines...) Expand all
229 WriteTestDirFile(FILE_PATH_LITERAL("test_file"), "some content"); 263 WriteTestDirFile(FILE_PATH_LITERAL("test_file"), "some content");
230 loop_.Run(); 264 loop_.Run();
231 265
232 // We win if we haven't crashed yet. 266 // We win if we haven't crashed yet.
233 // Might as well double-check it got deleted, too. 267 // Might as well double-check it got deleted, too.
234 ASSERT_TRUE(deleter.watcher_.get() == NULL); 268 ASSERT_TRUE(deleter.watcher_.get() == NULL);
235 } 269 }
236 270
237 TEST_F(DirectoryWatcherTest, MultipleWatchersSingleFile) { 271 TEST_F(DirectoryWatcherTest, MultipleWatchersSingleFile) {
238 DirectoryWatcher watcher1, watcher2; 272 DirectoryWatcher watcher1, watcher2;
239 ASSERT_TRUE(watcher1.Watch(test_dir_, this, false)); 273 TestDelegate delegate1(this), delegate2(this);
240 ASSERT_TRUE(watcher2.Watch(test_dir_, this, false)); 274 ASSERT_TRUE(watcher1.Watch(test_dir_, &delegate1, false));
275 ASSERT_TRUE(watcher2.Watch(test_dir_, &delegate2, false));
241 276
242 SetExpectedNumberOfModifications(4); // Each watcher should fire twice. 277 SetExpectedNumberOfNotifiedDelegates(2);
243 WriteTestDirFile(FILE_PATH_LITERAL("test_file"), "some content"); 278 WriteTestDirFile(FILE_PATH_LITERAL("test_file"), "some content");
244 VerifyExpectedNumberOfModifications(); 279 VerifyExpectedNumberOfNotifiedDelegates();
245 } 280 }
246 281
247 TEST_F(DirectoryWatcherTest, MultipleWatchersDifferentFiles) { 282 TEST_F(DirectoryWatcherTest, MultipleWatchersDifferentFiles) {
248 const int kNumberOfWatchers = 5; 283 const int kNumberOfWatchers = 5;
249 DirectoryWatcher watchers[kNumberOfWatchers]; 284 DirectoryWatcher watchers[kNumberOfWatchers];
285 TestDelegate delegates[kNumberOfWatchers] = {this, this, this, this, this};
250 FilePath subdirs[kNumberOfWatchers]; 286 FilePath subdirs[kNumberOfWatchers];
251 for (int i = 0; i < kNumberOfWatchers; i++) { 287 for (int i = 0; i < kNumberOfWatchers; i++) {
252 subdirs[i] = FilePath(FILE_PATH_LITERAL("Dir")).AppendASCII(IntToString(i)); 288 subdirs[i] = FilePath(FILE_PATH_LITERAL("Dir")).AppendASCII(IntToString(i));
253 ASSERT_TRUE(file_util::CreateDirectory(test_dir_.Append(subdirs[i]))); 289 ASSERT_TRUE(file_util::CreateDirectory(test_dir_.Append(subdirs[i])));
254 290
255 ASSERT_TRUE(watchers[i].Watch(test_dir_.Append(subdirs[i]), this, false)); 291 ASSERT_TRUE(watchers[i].Watch(test_dir_.Append(subdirs[i]), &delegates[i],
292 false));
256 } 293 }
257 for (int i = 0; i < kNumberOfWatchers; i++) { 294 for (int i = 0; i < kNumberOfWatchers; i++) {
258 // Verify that we only get modifications from one watcher (each watcher has 295 // Verify that we only get modifications from one watcher (each watcher has
259 // different directory). 296 // different directory).
260 297
261 ASSERT_EQ(0, directory_mods_); 298 for (int j = 0; j < kNumberOfWatchers; j++)
299 delegates[j].reset();
262 300
263 // Write a file to the subdir. 301 // Write a file to the subdir.
264 FilePath test_path = subdirs[i].AppendASCII("test_file"); 302 FilePath test_path = subdirs[i].AppendASCII("test_file");
265 SetExpectedNumberOfModifications(2); 303 SetExpectedNumberOfNotifiedDelegates(1);
266 WriteTestDirFile(test_path.value(), "some content"); 304 WriteTestDirFile(test_path.value(), "some content");
267 VerifyExpectedNumberOfModifications(); 305 VerifyExpectedNumberOfNotifiedDelegates();
268 306
269 directory_mods_ = 0; 307 loop_.RunAllPending();
270 } 308 }
271 } 309 }
272 310
273 // Verify that watching a directory that doesn't exist fails, but doesn't 311 // Verify that watching a directory that doesn't exist fails, but doesn't
274 // asssert. 312 // asssert.
275 // Basic test: add a file and verify we notice it. 313 // Basic test: add a file and verify we notice it.
276 TEST_F(DirectoryWatcherTest, NonExistentDirectory) { 314 TEST_F(DirectoryWatcherTest, NonExistentDirectory) {
277 DirectoryWatcher watcher; 315 DirectoryWatcher watcher;
278 ASSERT_FALSE(watcher.Watch(test_dir_.AppendASCII("does-not-exist"), this, 316 ASSERT_FALSE(watcher.Watch(test_dir_.AppendASCII("does-not-exist"), NULL,
279 false)); 317 false));
280 } 318 }
319
320 } // namespace
OLDNEW
« no previous file with comments | « base/directory_watcher_mac.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698