OLD | NEW |
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 Loading... |
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 |
OLD | NEW |