OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 <string> | |
6 | |
7 #include "base/basictypes.h" | |
8 #include "base/file_util.h" | |
9 #include "base/location.h" | |
10 #include "base/memory/scoped_ptr.h" | |
11 #include "base/message_loop.h" | |
12 #include "testing/gtest/include/gtest/gtest.h" | |
13 #include "webkit/blob/mock_blob_url_request_context.h" | |
14 #include "webkit/browser/fileapi/file_system_context.h" | |
15 #include "webkit/fileapi/syncable/canned_syncable_file_system.h" | |
16 #include "webkit/fileapi/syncable/local_file_change_tracker.h" | |
17 #include "webkit/fileapi/syncable/local_file_sync_context.h" | |
18 #include "webkit/fileapi/syncable/local_file_sync_status.h" | |
19 #include "webkit/fileapi/syncable/syncable_file_operation_runner.h" | |
20 #include "webkit/fileapi/syncable/syncable_file_system_operation.h" | |
21 #include "webkit/fileapi/syncable/syncable_file_system_util.h" | |
22 | |
23 using fileapi::FileSystemOperation; | |
24 using fileapi::FileSystemURL; | |
25 using webkit_blob::MockBlobURLRequestContext; | |
26 using webkit_blob::ScopedTextBlob; | |
27 using base::PlatformFileError; | |
28 | |
29 namespace sync_file_system { | |
30 | |
31 namespace { | |
32 const std::string kServiceName = "test"; | |
33 const std::string kParent = "foo"; | |
34 const std::string kFile = "foo/file"; | |
35 const std::string kDir = "foo/dir"; | |
36 const std::string kChild = "foo/dir/bar"; | |
37 const std::string kOther = "bar"; | |
38 } // namespace | |
39 | |
40 class SyncableFileOperationRunnerTest : public testing::Test { | |
41 protected: | |
42 typedef FileSystemOperation::StatusCallback StatusCallback; | |
43 | |
44 // Use the current thread as IO thread so that we can directly call | |
45 // operations in the tests. | |
46 SyncableFileOperationRunnerTest() | |
47 : message_loop_(base::MessageLoop::TYPE_IO), | |
48 file_system_(GURL("http://example.com"), kServiceName, | |
49 base::MessageLoopProxy::current(), | |
50 base::MessageLoopProxy::current()), | |
51 callback_count_(0), | |
52 write_status_(base::PLATFORM_FILE_ERROR_FAILED), | |
53 write_bytes_(0), | |
54 write_complete_(false), | |
55 url_request_context_(file_system_.file_system_context()), | |
56 weak_factory_(this) {} | |
57 | |
58 virtual void SetUp() OVERRIDE { | |
59 ASSERT_TRUE(dir_.CreateUniqueTempDir()); | |
60 file_system_.SetUp(); | |
61 sync_context_ = new LocalFileSyncContext(base::MessageLoopProxy::current(), | |
62 base::MessageLoopProxy::current()); | |
63 ASSERT_EQ(SYNC_STATUS_OK, | |
64 file_system_.MaybeInitializeFileSystemContext(sync_context_)); | |
65 | |
66 ASSERT_EQ(base::PLATFORM_FILE_OK, file_system_.OpenFileSystem()); | |
67 ASSERT_EQ(base::PLATFORM_FILE_OK, | |
68 file_system_.CreateDirectory(URL(kParent))); | |
69 } | |
70 | |
71 virtual void TearDown() OVERRIDE { | |
72 if (sync_context_) | |
73 sync_context_->ShutdownOnUIThread(); | |
74 sync_context_ = NULL; | |
75 | |
76 file_system_.TearDown(); | |
77 message_loop_.RunUntilIdle(); | |
78 RevokeSyncableFileSystem(kServiceName); | |
79 } | |
80 | |
81 FileSystemURL URL(const std::string& path) { | |
82 return file_system_.URL(path); | |
83 } | |
84 | |
85 LocalFileSyncStatus* sync_status() { | |
86 return file_system_.file_system_context()->sync_context()->sync_status(); | |
87 } | |
88 | |
89 void ResetCallbackStatus() { | |
90 write_status_ = base::PLATFORM_FILE_ERROR_FAILED; | |
91 write_bytes_ = 0; | |
92 write_complete_ = false; | |
93 callback_count_ = 0; | |
94 } | |
95 | |
96 StatusCallback ExpectStatus(const tracked_objects::Location& location, | |
97 PlatformFileError expect) { | |
98 return base::Bind(&SyncableFileOperationRunnerTest::DidFinish, | |
99 weak_factory_.GetWeakPtr(), location, expect); | |
100 } | |
101 | |
102 FileSystemOperation::WriteCallback GetWriteCallback( | |
103 const tracked_objects::Location& location) { | |
104 return base::Bind(&SyncableFileOperationRunnerTest::DidWrite, | |
105 weak_factory_.GetWeakPtr(), location); | |
106 } | |
107 | |
108 void DidWrite(const tracked_objects::Location& location, | |
109 PlatformFileError status, int64 bytes, bool complete) { | |
110 SCOPED_TRACE(testing::Message() << location.ToString()); | |
111 write_status_ = status; | |
112 write_bytes_ += bytes; | |
113 write_complete_ = complete; | |
114 ++callback_count_; | |
115 } | |
116 | |
117 void DidFinish(const tracked_objects::Location& location, | |
118 PlatformFileError expect, PlatformFileError status) { | |
119 SCOPED_TRACE(testing::Message() << location.ToString()); | |
120 EXPECT_EQ(expect, status); | |
121 ++callback_count_; | |
122 } | |
123 | |
124 bool CreateTempFile(base::FilePath* path) { | |
125 return file_util::CreateTemporaryFileInDir(dir_.path(), path); | |
126 } | |
127 | |
128 base::ScopedTempDir dir_; | |
129 | |
130 base::MessageLoop message_loop_; | |
131 CannedSyncableFileSystem file_system_; | |
132 scoped_refptr<LocalFileSyncContext> sync_context_; | |
133 | |
134 int callback_count_; | |
135 PlatformFileError write_status_; | |
136 size_t write_bytes_; | |
137 bool write_complete_; | |
138 | |
139 MockBlobURLRequestContext url_request_context_; | |
140 | |
141 base::WeakPtrFactory<SyncableFileOperationRunnerTest> weak_factory_; | |
142 | |
143 DISALLOW_COPY_AND_ASSIGN(SyncableFileOperationRunnerTest); | |
144 }; | |
145 | |
146 TEST_F(SyncableFileOperationRunnerTest, SimpleQueue) { | |
147 sync_status()->StartSyncing(URL(kFile)); | |
148 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile))); | |
149 | |
150 // The URL is in syncing so the write operations won't run. | |
151 ResetCallbackStatus(); | |
152 file_system_.NewOperation()->CreateFile( | |
153 URL(kFile), false /* exclusive */, | |
154 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); | |
155 file_system_.NewOperation()->Truncate( | |
156 URL(kFile), 1, | |
157 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); | |
158 base::MessageLoop::current()->RunUntilIdle(); | |
159 EXPECT_EQ(0, callback_count_); | |
160 | |
161 // Read operations are not blocked (and are executed before queued ones). | |
162 file_system_.NewOperation()->FileExists( | |
163 URL(kFile), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_NOT_FOUND)); | |
164 base::MessageLoop::current()->RunUntilIdle(); | |
165 EXPECT_EQ(1, callback_count_); | |
166 | |
167 // End syncing (to enable write). | |
168 sync_status()->EndSyncing(URL(kFile)); | |
169 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile))); | |
170 | |
171 ResetCallbackStatus(); | |
172 base::MessageLoop::current()->RunUntilIdle(); | |
173 EXPECT_EQ(2, callback_count_); | |
174 | |
175 // Now the file must have been created and updated. | |
176 ResetCallbackStatus(); | |
177 file_system_.NewOperation()->FileExists( | |
178 URL(kFile), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); | |
179 base::MessageLoop::current()->RunUntilIdle(); | |
180 EXPECT_EQ(1, callback_count_); | |
181 } | |
182 | |
183 TEST_F(SyncableFileOperationRunnerTest, WriteToParentAndChild) { | |
184 // First create the kDir directory and kChild in the dir. | |
185 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateDirectory(URL(kDir))); | |
186 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kChild))); | |
187 | |
188 // Start syncing the kDir directory. | |
189 sync_status()->StartSyncing(URL(kDir)); | |
190 ASSERT_FALSE(sync_status()->IsWritable(URL(kDir))); | |
191 | |
192 // Writes to kParent and kChild should be all queued up. | |
193 ResetCallbackStatus(); | |
194 file_system_.NewOperation()->Truncate( | |
195 URL(kChild), 1, ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); | |
196 file_system_.NewOperation()->Remove( | |
197 URL(kParent), true /* recursive */, | |
198 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); | |
199 base::MessageLoop::current()->RunUntilIdle(); | |
200 EXPECT_EQ(0, callback_count_); | |
201 | |
202 // Read operations are not blocked (and are executed before queued ones). | |
203 file_system_.NewOperation()->DirectoryExists( | |
204 URL(kDir), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); | |
205 base::MessageLoop::current()->RunUntilIdle(); | |
206 EXPECT_EQ(1, callback_count_); | |
207 | |
208 // Writes to unrelated files must succeed as well. | |
209 ResetCallbackStatus(); | |
210 file_system_.NewOperation()->CreateDirectory( | |
211 URL(kOther), false /* exclusive */, false /* recursive */, | |
212 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); | |
213 base::MessageLoop::current()->RunUntilIdle(); | |
214 EXPECT_EQ(1, callback_count_); | |
215 | |
216 // End syncing (to enable write). | |
217 sync_status()->EndSyncing(URL(kDir)); | |
218 ASSERT_TRUE(sync_status()->IsWritable(URL(kDir))); | |
219 | |
220 ResetCallbackStatus(); | |
221 base::MessageLoop::current()->RunUntilIdle(); | |
222 EXPECT_EQ(2, callback_count_); | |
223 } | |
224 | |
225 TEST_F(SyncableFileOperationRunnerTest, CopyAndMove) { | |
226 // First create the kDir directory and kChild in the dir. | |
227 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateDirectory(URL(kDir))); | |
228 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kChild))); | |
229 | |
230 // Start syncing the kParent directory. | |
231 sync_status()->StartSyncing(URL(kParent)); | |
232 | |
233 // Copying kDir to other directory should succeed, while moving would fail | |
234 // (since the source directory is in syncing). | |
235 ResetCallbackStatus(); | |
236 file_system_.NewOperation()->Copy( | |
237 URL(kDir), URL("dest-copy"), | |
238 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); | |
239 file_system_.NewOperation()->Move( | |
240 URL(kDir), URL("dest-move"), | |
241 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); | |
242 base::MessageLoop::current()->RunUntilIdle(); | |
243 EXPECT_EQ(1, callback_count_); | |
244 | |
245 // Only "dest-copy1" should exist. | |
246 EXPECT_EQ(base::PLATFORM_FILE_OK, | |
247 file_system_.DirectoryExists(URL("dest-copy"))); | |
248 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, | |
249 file_system_.DirectoryExists(URL("dest-move"))); | |
250 | |
251 // Start syncing the "dest-copy2" directory. | |
252 sync_status()->StartSyncing(URL("dest-copy2")); | |
253 | |
254 // Now the destination is also locked copying kDir should be queued. | |
255 ResetCallbackStatus(); | |
256 file_system_.NewOperation()->Copy( | |
257 URL(kDir), URL("dest-copy2"), | |
258 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); | |
259 base::MessageLoop::current()->RunUntilIdle(); | |
260 EXPECT_EQ(0, callback_count_); | |
261 | |
262 // Finish syncing the "dest-copy2" directory to unlock Copy. | |
263 sync_status()->EndSyncing(URL("dest-copy2")); | |
264 ResetCallbackStatus(); | |
265 base::MessageLoop::current()->RunUntilIdle(); | |
266 EXPECT_EQ(1, callback_count_); | |
267 | |
268 // Now we should have "dest-copy2". | |
269 EXPECT_EQ(base::PLATFORM_FILE_OK, | |
270 file_system_.DirectoryExists(URL("dest-copy2"))); | |
271 | |
272 // Finish syncing the kParent to unlock Move. | |
273 sync_status()->EndSyncing(URL(kParent)); | |
274 ResetCallbackStatus(); | |
275 base::MessageLoop::current()->RunUntilIdle(); | |
276 EXPECT_EQ(1, callback_count_); | |
277 | |
278 // Now we should have "dest-move". | |
279 EXPECT_EQ(base::PLATFORM_FILE_OK, | |
280 file_system_.DirectoryExists(URL("dest-move"))); | |
281 } | |
282 | |
283 TEST_F(SyncableFileOperationRunnerTest, Write) { | |
284 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kFile))); | |
285 const GURL kBlobURL("blob:foo"); | |
286 const std::string kData("Lorem ipsum."); | |
287 ScopedTextBlob blob(url_request_context_, kBlobURL, kData); | |
288 | |
289 sync_status()->StartSyncing(URL(kFile)); | |
290 | |
291 ResetCallbackStatus(); | |
292 file_system_.NewOperation()->Write( | |
293 &url_request_context_, | |
294 URL(kFile), kBlobURL, 0, GetWriteCallback(FROM_HERE)); | |
295 base::MessageLoop::current()->RunUntilIdle(); | |
296 EXPECT_EQ(0, callback_count_); | |
297 | |
298 sync_status()->EndSyncing(URL(kFile)); | |
299 ResetCallbackStatus(); | |
300 | |
301 while (!write_complete_) | |
302 base::MessageLoop::current()->RunUntilIdle(); | |
303 | |
304 EXPECT_EQ(base::PLATFORM_FILE_OK, write_status_); | |
305 EXPECT_EQ(kData.size(), write_bytes_); | |
306 EXPECT_TRUE(write_complete_); | |
307 } | |
308 | |
309 TEST_F(SyncableFileOperationRunnerTest, QueueAndCancel) { | |
310 sync_status()->StartSyncing(URL(kFile)); | |
311 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile))); | |
312 | |
313 ResetCallbackStatus(); | |
314 file_system_.NewOperation()->CreateFile( | |
315 URL(kFile), false /* exclusive */, | |
316 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT)); | |
317 file_system_.NewOperation()->Truncate( | |
318 URL(kFile), 1, | |
319 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT)); | |
320 base::MessageLoop::current()->RunUntilIdle(); | |
321 EXPECT_EQ(0, callback_count_); | |
322 | |
323 ResetCallbackStatus(); | |
324 | |
325 // This shouldn't crash nor leak memory. | |
326 sync_context_->ShutdownOnUIThread(); | |
327 sync_context_ = NULL; | |
328 base::MessageLoop::current()->RunUntilIdle(); | |
329 EXPECT_EQ(2, callback_count_); | |
330 } | |
331 | |
332 // Test if CopyInForeignFile runs cooperatively with other Sync operations | |
333 // when it is called directly via AsLocalFileSystemOperation. | |
334 TEST_F(SyncableFileOperationRunnerTest, CopyInForeignFile) { | |
335 const std::string kTestData("test data"); | |
336 | |
337 base::FilePath temp_path; | |
338 ASSERT_TRUE(CreateTempFile(&temp_path)); | |
339 ASSERT_EQ(static_cast<int>(kTestData.size()), | |
340 file_util::WriteFile( | |
341 temp_path, kTestData.data(), kTestData.size())); | |
342 | |
343 sync_status()->StartSyncing(URL(kFile)); | |
344 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile))); | |
345 | |
346 // The URL is in syncing so CopyIn (which is a write operation) won't run. | |
347 ResetCallbackStatus(); | |
348 file_system_.NewOperation()->AsLocalFileSystemOperation()->CopyInForeignFile( | |
349 temp_path, URL(kFile), | |
350 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); | |
351 base::MessageLoop::current()->RunUntilIdle(); | |
352 EXPECT_EQ(0, callback_count_); | |
353 | |
354 // End syncing (to enable write). | |
355 sync_status()->EndSyncing(URL(kFile)); | |
356 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile))); | |
357 | |
358 ResetCallbackStatus(); | |
359 base::MessageLoop::current()->RunUntilIdle(); | |
360 EXPECT_EQ(1, callback_count_); | |
361 | |
362 // Now the file must have been created and have the same content as temp_path. | |
363 ResetCallbackStatus(); | |
364 file_system_.DoVerifyFile( | |
365 URL(kFile), kTestData, | |
366 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); | |
367 base::MessageLoop::current()->RunUntilIdle(); | |
368 EXPECT_EQ(1, callback_count_); | |
369 } | |
370 | |
371 } // namespace sync_file_system | |
OLD | NEW |