OLD | NEW |
(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 |
OLD | NEW |