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