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

Side by Side Diff: chrome/browser/file_watcher_unittest.cc

Issue 2868114: Rework FileWatcher to avoid race condition upon deletion. (Closed) Base URL: http://src.chromium.org/git/chromium.git
Patch Set: Add missing return... Created 10 years, 4 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 | « chrome/browser/file_watcher_mac.cc ('k') | chrome/browser/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
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 "chrome/browser/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 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h" 19 #include "testing/gtest/include/gtest/gtest.h"
19 20
21 using testing::_;
22 using testing::AnyNumber;
23 using testing::AtLeast;
24 using testing::Mock;
25
20 #if defined(OS_MACOSX) 26 #if defined(OS_MACOSX)
21 // TODO(tony): Tests are flaky on mac. http://crbug.com/38188 27 // TODO(tony): Tests are flaky on mac. http://crbug.com/38188
22 #define MAYBE(name) FLAKY_ ## name 28 #define MAYBE(name) FLAKY_ ## name
23 #else 29 #else
24 #define MAYBE(name) name 30 #define MAYBE(name) name
25 #endif 31 #endif
26 32
27 namespace { 33 namespace {
28 34
29 // For tests where we wait a bit to verify nothing happened 35 // For tests where we wait a bit to verify nothing happened
30 const int kWaitForEventTime = 500; 36 const int kWaitForEventTime = 500;
31 37
32 // Maximum amount of time to wait on a test. 38 // Maximum amount of time to wait on a test.
33 const int kMaxTestTimeMs = 10 * 1000; 39 const int kMaxTestTimeMs = 10 * 1000;
34 40
41 // A mock FileWatcher::Delegate for testing.
42 class TestDelegate : public FileWatcher::Delegate {
43 public:
44 MOCK_METHOD1(OnFileChanged, void(const FilePath&));
45 };
46
35 class FileWatcherTest : public testing::Test { 47 class FileWatcherTest : public testing::Test {
36 public: 48 public:
37 // Implementation of FileWatcher on Mac requires UI loop. 49 // Implementation of FileWatcher on Mac requires UI loop.
38 FileWatcherTest() 50 FileWatcherTest()
39 : loop_(MessageLoop::TYPE_UI), 51 : loop_(MessageLoop::TYPE_UI),
40 ui_thread_(ChromeThread::UI, &loop_), 52 ui_thread_(ChromeThread::UI, &loop_),
41 file_thread_(ChromeThread::FILE, &loop_), 53 file_thread_(ChromeThread::FILE, &loop_) {
42 notified_delegates_(0),
43 expected_notified_delegates_(0) {
44 }
45
46 void OnTestDelegateFirstNotification() {
47 notified_delegates_++;
48 if (notified_delegates_ >= expected_notified_delegates_)
49 MessageLoop::current()->Quit();
50 } 54 }
51 55
52 protected: 56 protected:
53 virtual void SetUp() { 57 virtual void SetUp() {
54 temp_dir_.reset(new ScopedTempDir); 58 temp_dir_.reset(new ScopedTempDir);
55 ASSERT_TRUE(temp_dir_->CreateUniqueTempDir()); 59 ASSERT_TRUE(temp_dir_->CreateUniqueTempDir());
56 // Make sure that not getting an event doesn't cause the whole 60 // Make sure that not getting an event doesn't cause the whole
57 // test suite to hang. 61 // test suite to hang.
58 loop_.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask, 62 loop_.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask,
59 kMaxTestTimeMs); 63 kMaxTestTimeMs);
60 } 64 }
61 65
66 virtual void TearDown() {
67 loop_.RunAllPending();
68 }
69
62 FilePath test_file() { 70 FilePath test_file() {
63 return temp_dir_->path().AppendASCII("FileWatcherTest"); 71 return temp_dir_->path().AppendASCII("FileWatcherTest");
64 } 72 }
65 73
66 // Write |content| to the test file. Returns true on success. 74 // Write |content| to the test file. Returns true on success.
67 bool WriteTestFile(const std::string& content) { 75 bool WriteTestFile(const std::string& content) {
68 // Logging to try and figure out why these tests are flaky on mac. 76 // Logging to try and figure out why these tests are flaky on mac.
69 LOG(INFO) << "WriteTestFile"; 77 LOG(INFO) << "WriteTestFile";
70 int write_size = file_util::WriteFile(test_file(), content.c_str(), 78 int write_size = file_util::WriteFile(test_file(), content.c_str(),
71 content.length()); 79 content.length());
72 SyncIfPOSIX(); 80 SyncIfPOSIX();
73 return write_size == static_cast<int>(content.length()); 81 return write_size == static_cast<int>(content.length());
74 } 82 }
75 83
76 void SetExpectedNumberOfNotifiedDelegates(int n) { 84 void VerifyDelegate(TestDelegate* delegate) {
77 notified_delegates_ = 0;
78 expected_notified_delegates_ = n;
79 }
80
81 void VerifyExpectedNumberOfNotifiedDelegates() {
82 // Check that we get at least the expected number of notified delegates. 85 // Check that we get at least the expected number of notified delegates.
83 if (expected_notified_delegates_ - notified_delegates_ > 0)
84 loop_.Run();
85 EXPECT_EQ(expected_notified_delegates_, notified_delegates_);
86 }
87
88 void VerifyNoExtraNotifications() {
89 // Check that we get no more than the expected number of notified delegates.
90 loop_.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask, 86 loop_.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask,
91 kWaitForEventTime); 87 kWaitForEventTime);
92 loop_.Run(); 88 loop_.Run();
93 EXPECT_EQ(expected_notified_delegates_, notified_delegates_); 89 Mock::VerifyAndClearExpectations(delegate);
94 } 90 }
95 91
96 // We need this function for reliable tests on Mac OS X. FSEvents API 92 // We need this function for reliable tests on Mac OS X. FSEvents API
97 // has a latency interval and can merge multiple events into one, 93 // has a latency interval and can merge multiple events into one,
98 // and we need a clear distinction between events triggered by test setup code 94 // and we need a clear distinction between events triggered by test setup code
99 // and test code. 95 // and test code.
100 void SyncIfPOSIX() { 96 void SyncIfPOSIX() {
101 #if defined(OS_POSIX) 97 #if defined(OS_POSIX)
102 sync(); 98 sync();
103 #endif // defined(OS_POSIX) 99 #endif // defined(OS_POSIX)
104 } 100 }
105 101
106 MessageLoop loop_; 102 MessageLoop loop_;
107 ChromeThread ui_thread_; 103 ChromeThread ui_thread_;
108 ChromeThread file_thread_; 104 ChromeThread file_thread_;
109 scoped_ptr<ScopedTempDir> temp_dir_; 105 scoped_ptr<ScopedTempDir> temp_dir_;
110
111 // The number of test delegates which received their notification.
112 int notified_delegates_;
113
114 // The number of notified test delegates after which we quit the message loop.
115 int expected_notified_delegates_;
116 }; 106 };
117 107
118 class TestDelegate : public FileWatcher::Delegate {
119 public:
120 explicit TestDelegate(FileWatcherTest* test)
121 : test_(test),
122 got_notification_(false) {
123 }
124
125 bool got_notification() const {
126 return got_notification_;
127 }
128
129 void reset() {
130 got_notification_ = false;
131 }
132
133 virtual void OnFileChanged(const FilePath& path) {
134 EXPECT_TRUE(ChromeThread::CurrentlyOn(ChromeThread::UI));
135 if (!got_notification_)
136 test_->OnTestDelegateFirstNotification();
137 got_notification_ = true;
138 }
139
140 private:
141 // Hold a pointer to current test fixture to inform it on first notification.
142 FileWatcherTest* test_;
143
144 // Set to true after first notification.
145 bool got_notification_;
146 };
147
148
149 // Basic test: Create the file and verify that we notice. 108 // Basic test: Create the file and verify that we notice.
150 TEST_F(FileWatcherTest, MAYBE(NewFile)) { 109 TEST_F(FileWatcherTest, MAYBE(NewFile)) {
151 FileWatcher watcher; 110 FileWatcher watcher;
152 TestDelegate delegate(this); 111 scoped_refptr<TestDelegate> delegate(new TestDelegate);
153 ASSERT_TRUE(watcher.Watch(test_file(), &delegate)); 112 ASSERT_TRUE(watcher.Watch(test_file(), delegate.get()));
154 113
155 SetExpectedNumberOfNotifiedDelegates(1); 114 EXPECT_CALL(*delegate, OnFileChanged(_)).Times(AtLeast(1));
156 ASSERT_TRUE(WriteTestFile("content")); 115 ASSERT_TRUE(WriteTestFile("content"));
157 VerifyExpectedNumberOfNotifiedDelegates(); 116 VerifyDelegate(delegate.get());
158 } 117 }
159 118
160 // Verify that modifying the file is caught. 119 // Verify that modifying the file is caught.
161 TEST_F(FileWatcherTest, MAYBE(ModifiedFile)) { 120 TEST_F(FileWatcherTest, MAYBE(ModifiedFile)) {
162 ASSERT_TRUE(WriteTestFile("content")); 121 ASSERT_TRUE(WriteTestFile("content"));
163 122
164 FileWatcher watcher; 123 FileWatcher watcher;
165 TestDelegate delegate(this); 124 scoped_refptr<TestDelegate> delegate(new TestDelegate);
166 ASSERT_TRUE(watcher.Watch(test_file(), &delegate)); 125 ASSERT_TRUE(watcher.Watch(test_file(), delegate.get()));
167 126
168 // Now make sure we get notified if the file is modified. 127 // Now make sure we get notified if the file is modified.
169 SetExpectedNumberOfNotifiedDelegates(1); 128 EXPECT_CALL(*delegate, OnFileChanged(_)).Times(AtLeast(1));
170 ASSERT_TRUE(WriteTestFile("new content")); 129 ASSERT_TRUE(WriteTestFile("new content"));
171 VerifyExpectedNumberOfNotifiedDelegates(); 130 VerifyDelegate(delegate.get());
172 } 131 }
173 132
174 TEST_F(FileWatcherTest, MAYBE(DeletedFile)) { 133 TEST_F(FileWatcherTest, MAYBE(DeletedFile)) {
175 ASSERT_TRUE(WriteTestFile("content")); 134 ASSERT_TRUE(WriteTestFile("content"));
176 135
177 FileWatcher watcher; 136 FileWatcher watcher;
178 TestDelegate delegate(this); 137 scoped_refptr<TestDelegate> delegate(new TestDelegate);
179 ASSERT_TRUE(watcher.Watch(test_file(), &delegate)); 138 ASSERT_TRUE(watcher.Watch(test_file(), delegate.get()));
180 139
181 // Now make sure we get notified if the file is deleted. 140 // Now make sure we get notified if the file is deleted.
182 SetExpectedNumberOfNotifiedDelegates(1); 141 EXPECT_CALL(*delegate, OnFileChanged(_)).Times(AtLeast(1));
183 file_util::Delete(test_file(), false); 142 file_util::Delete(test_file(), false);
184 SyncIfPOSIX(); 143 SyncIfPOSIX();
185 VerifyExpectedNumberOfNotifiedDelegates(); 144 VerifyDelegate(delegate.get());
186 } 145 }
187 146
188 // Verify that letting the watcher go out of scope stops notifications. 147 // Verify that letting the watcher go out of scope stops notifications.
189 TEST_F(FileWatcherTest, MAYBE(Unregister)) { 148 TEST_F(FileWatcherTest, MAYBE(Unregister)) {
190 TestDelegate delegate(this); 149 scoped_refptr<TestDelegate> delegate(new TestDelegate);
191 150
192 { 151 {
193 FileWatcher watcher; 152 FileWatcher watcher;
194 ASSERT_TRUE(watcher.Watch(test_file(), &delegate)); 153 ASSERT_TRUE(watcher.Watch(test_file(), delegate.get()));
195 154
196 // And then let it fall out of scope, clearing its watch. 155 // And then let it fall out of scope, clearing its watch.
197 } 156 }
198 157
199 // Write a file to the test dir. 158 // Write a file to the test dir.
200 SetExpectedNumberOfNotifiedDelegates(0); 159 EXPECT_CALL(*delegate, OnFileChanged(_)).Times(0);
201 ASSERT_TRUE(WriteTestFile("content")); 160 ASSERT_TRUE(WriteTestFile("content"));
202 VerifyExpectedNumberOfNotifiedDelegates(); 161 VerifyDelegate(delegate.get());
203 VerifyNoExtraNotifications();
204 } 162 }
205 163
206
207 namespace { 164 namespace {
208 // Used by the DeleteDuringNotify test below. 165 // Used by the DeleteDuringNotify test below.
209 // Deletes the FileWatcher when it's notified. 166 // Deletes the FileWatcher when it's notified.
210 class Deleter : public FileWatcher::Delegate { 167 class Deleter : public FileWatcher::Delegate {
211 public: 168 public:
212 Deleter(FileWatcher* watcher, MessageLoop* loop) 169 Deleter(FileWatcher* watcher, MessageLoop* loop)
213 : watcher_(watcher), 170 : watcher_(watcher),
214 loop_(loop) { 171 loop_(loop) {
215 } 172 }
216 173
217 virtual void OnFileChanged(const FilePath& path) { 174 virtual void OnFileChanged(const FilePath& path) {
218 watcher_.reset(NULL); 175 watcher_.reset(NULL);
219 loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); 176 loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
220 } 177 }
221 178
222 scoped_ptr<FileWatcher> watcher_; 179 scoped_ptr<FileWatcher> watcher_;
223 MessageLoop* loop_; 180 MessageLoop* loop_;
224 }; 181 };
225 } // anonymous namespace 182 } // anonymous namespace
226 183
227 // Verify that deleting a watcher during the callback doesn't crash. 184 // Verify that deleting a watcher during the callback doesn't crash.
228 TEST_F(FileWatcherTest, MAYBE(DeleteDuringNotify)) { 185 TEST_F(FileWatcherTest, MAYBE(DeleteDuringNotify)) {
229 FileWatcher* watcher = new FileWatcher; 186 FileWatcher* watcher = new FileWatcher;
230 Deleter deleter(watcher, &loop_); // Takes ownership of watcher. 187 // Takes ownership of watcher.
231 ASSERT_TRUE(watcher->Watch(test_file(), &deleter)); 188 scoped_refptr<Deleter> deleter(new Deleter(watcher, &loop_));
189 ASSERT_TRUE(watcher->Watch(test_file(), deleter.get()));
232 190
233 ASSERT_TRUE(WriteTestFile("content")); 191 ASSERT_TRUE(WriteTestFile("content"));
234 loop_.Run(); 192 loop_.Run();
235 193
236 // We win if we haven't crashed yet. 194 // We win if we haven't crashed yet.
237 // Might as well double-check it got deleted, too. 195 // Might as well double-check it got deleted, too.
238 ASSERT_TRUE(deleter.watcher_.get() == NULL); 196 ASSERT_TRUE(deleter->watcher_.get() == NULL);
197 }
198
199 // Verify that deleting the watcher works even if there is a pending
200 // notification.
201 //
202 // It's hard to test this, since both a change event and deletion of the file
203 // watcher must happen before the task that runs the callback executes. The code
204 // below only triggers the situation with the Linux implementation. Change
205 // detection runs on a separate thread in the Linux implementation, so we can
206 // schedule the FileWatcher deletion in advance. For Mac and Windows, the
207 // DeleteTask runs before the message loop processes the platform-specific
208 // change notifications, so the whole FileWatcher is destroyed before the
209 // callback gets scheduled.
210 TEST_F(FileWatcherTest, MAYBE(DestroyWithPendingNotification)) {
211 scoped_refptr<TestDelegate> delegate(new TestDelegate);
212 EXPECT_CALL(*delegate, OnFileChanged(_)).Times(AnyNumber());
213 FileWatcher* watcher = new FileWatcher;
214 ASSERT_TRUE(watcher->Watch(test_file(), delegate.get()));
215 ASSERT_TRUE(WriteTestFile("content"));
216 ChromeThread::DeleteSoon(ChromeThread::FILE, FROM_HERE, watcher);
217 // Success if there is no crash or DCHECK when running the callback.
218 VerifyDelegate(delegate.get());
239 } 219 }
240 220
241 TEST_F(FileWatcherTest, MAYBE(MultipleWatchersSingleFile)) { 221 TEST_F(FileWatcherTest, MAYBE(MultipleWatchersSingleFile)) {
242 FileWatcher watcher1, watcher2; 222 FileWatcher watcher1, watcher2;
243 TestDelegate delegate1(this), delegate2(this); 223 scoped_refptr<TestDelegate> delegate1(new TestDelegate);
244 ASSERT_TRUE(watcher1.Watch(test_file(), &delegate1)); 224 scoped_refptr<TestDelegate> delegate2(new TestDelegate);
245 ASSERT_TRUE(watcher2.Watch(test_file(), &delegate2)); 225 ASSERT_TRUE(watcher1.Watch(test_file(), delegate1.get()));
226 ASSERT_TRUE(watcher2.Watch(test_file(), delegate2.get()));
246 227
247 SetExpectedNumberOfNotifiedDelegates(2); 228 EXPECT_CALL(*delegate1, OnFileChanged(_)).Times(AtLeast(1));
229 EXPECT_CALL(*delegate2, OnFileChanged(_)).Times(AtLeast(1));
248 ASSERT_TRUE(WriteTestFile("content")); 230 ASSERT_TRUE(WriteTestFile("content"));
249 VerifyExpectedNumberOfNotifiedDelegates(); 231 VerifyDelegate(delegate1.get());
232 VerifyDelegate(delegate2.get());
250 } 233 }
251 234
252 // 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
253 // fails, but doesn't asssert. 236 // fails, but doesn't asssert.
254 TEST_F(FileWatcherTest, NonExistentDirectory) { 237 TEST_F(FileWatcherTest, NonExistentDirectory) {
255 FileWatcher watcher; 238 FileWatcher watcher;
256 ASSERT_FALSE(watcher.Watch(test_file().AppendASCII("FileToWatch"), NULL)); 239 ASSERT_FALSE(watcher.Watch(test_file().AppendASCII("FileToWatch"), NULL));
257 } 240 }
258 241
259 } // namespace 242 } // namespace
OLDNEW
« no previous file with comments | « chrome/browser/file_watcher_mac.cc ('k') | chrome/browser/file_watcher_win.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698