1 // Copyright 2013 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.
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/message_loop.h"
12 #include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h"
13 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
14 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
15 #include "chrome/browser/sync_file_system/local/local_file_sync_status.h"
16 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
17 #include "chrome/browser/sync_file_system/local/syncable_file_operation_runner.h"
18 #include "chrome/browser/sync_file_system/local/syncable_file_system_operation.h"
19 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
20 #include "content/public/test/test_browser_thread_bundle.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "webkit/browser/blob/mock_blob_url_request_context.h"
23 #include "webkit/browser/fileapi/file_system_context.h"
24 #include "webkit/browser/fileapi/file_system_operation_runner.h"
26 using fileapi::FileSystemOperation
;
27 using fileapi::FileSystemURL
;
28 using webkit_blob::MockBlobURLRequestContext
;
29 using webkit_blob::ScopedTextBlob
;
30 using base::PlatformFileError
;
32 namespace sync_file_system
{
35 const std::string kParent
= "foo";
36 const std::string kFile
= "foo/file";
37 const std::string kDir
= "foo/dir";
38 const std::string kChild
= "foo/dir/bar";
39 const std::string kOther
= "bar";
42 class SyncableFileOperationRunnerTest
: public testing::Test
{
44 typedef FileSystemOperation::StatusCallback StatusCallback
;
46 // Use the current thread as IO thread so that we can directly call
47 // operations in the tests.
48 SyncableFileOperationRunnerTest()
49 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
),
50 file_system_(GURL("http://example.com"),
51 base::MessageLoopProxy::current().get(),
52 base::MessageLoopProxy::current().get()),
54 write_status_(base::PLATFORM_FILE_ERROR_FAILED
),
56 write_complete_(false),
57 url_request_context_(file_system_
.file_system_context()),
58 weak_factory_(this) {}
60 virtual void SetUp() OVERRIDE
{
61 ASSERT_TRUE(dir_
.CreateUniqueTempDir());
63 sync_context_
= new LocalFileSyncContext(
65 base::MessageLoopProxy::current().get(),
66 base::MessageLoopProxy::current().get());
69 file_system_
.MaybeInitializeFileSystemContext(sync_context_
.get()));
71 ASSERT_EQ(base::PLATFORM_FILE_OK
, file_system_
.OpenFileSystem());
72 ASSERT_EQ(base::PLATFORM_FILE_OK
,
73 file_system_
.CreateDirectory(URL(kParent
)));
76 virtual void TearDown() OVERRIDE
{
77 if (sync_context_
.get())
78 sync_context_
->ShutdownOnUIThread();
81 file_system_
.TearDown();
82 RevokeSyncableFileSystem();
85 FileSystemURL
URL(const std::string
& path
) {
86 return file_system_
.URL(path
);
89 LocalFileSyncStatus
* sync_status() {
90 return file_system_
.backend()->sync_context()->sync_status();
93 void ResetCallbackStatus() {
94 write_status_
= base::PLATFORM_FILE_ERROR_FAILED
;
96 write_complete_
= false;
100 StatusCallback
ExpectStatus(const tracked_objects::Location
& location
,
101 PlatformFileError expect
) {
102 return base::Bind(&SyncableFileOperationRunnerTest::DidFinish
,
103 weak_factory_
.GetWeakPtr(), location
, expect
);
106 FileSystemOperation::WriteCallback
GetWriteCallback(
107 const tracked_objects::Location
& location
) {
108 return base::Bind(&SyncableFileOperationRunnerTest::DidWrite
,
109 weak_factory_
.GetWeakPtr(), location
);
112 void DidWrite(const tracked_objects::Location
& location
,
113 PlatformFileError status
, int64 bytes
, bool complete
) {
114 SCOPED_TRACE(testing::Message() << location
.ToString());
115 write_status_
= status
;
116 write_bytes_
+= bytes
;
117 write_complete_
= complete
;
121 void DidFinish(const tracked_objects::Location
& location
,
122 PlatformFileError expect
, PlatformFileError status
) {
123 SCOPED_TRACE(testing::Message() << location
.ToString());
124 EXPECT_EQ(expect
, status
);
128 bool CreateTempFile(base::FilePath
* path
) {
129 return base::CreateTemporaryFileInDir(dir_
.path(), path
);
132 ScopedEnableSyncFSDirectoryOperation enable_directory_operation_
;
133 base::ScopedTempDir dir_
;
135 content::TestBrowserThreadBundle thread_bundle_
;
136 CannedSyncableFileSystem file_system_
;
137 scoped_refptr
<LocalFileSyncContext
> sync_context_
;
140 PlatformFileError write_status_
;
142 bool write_complete_
;
144 MockBlobURLRequestContext url_request_context_
;
147 base::WeakPtrFactory
<SyncableFileOperationRunnerTest
> weak_factory_
;
149 DISALLOW_COPY_AND_ASSIGN(SyncableFileOperationRunnerTest
);
152 TEST_F(SyncableFileOperationRunnerTest
, SimpleQueue
) {
153 sync_status()->StartSyncing(URL(kFile
));
154 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile
)));
156 // The URL is in syncing so the write operations won't run.
157 ResetCallbackStatus();
158 file_system_
.operation_runner()->CreateFile(
159 URL(kFile
), false /* exclusive */,
160 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
161 file_system_
.operation_runner()->Truncate(
163 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
164 base::MessageLoop::current()->RunUntilIdle();
165 EXPECT_EQ(0, callback_count_
);
167 // Read operations are not blocked (and are executed before queued ones).
168 file_system_
.operation_runner()->FileExists(
169 URL(kFile
), ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_ERROR_NOT_FOUND
));
170 base::MessageLoop::current()->RunUntilIdle();
171 EXPECT_EQ(1, callback_count_
);
173 // End syncing (to enable write).
174 sync_status()->EndSyncing(URL(kFile
));
175 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile
)));
177 ResetCallbackStatus();
178 base::MessageLoop::current()->RunUntilIdle();
179 EXPECT_EQ(2, callback_count_
);
181 // Now the file must have been created and updated.
182 ResetCallbackStatus();
183 file_system_
.operation_runner()->FileExists(
184 URL(kFile
), ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
185 base::MessageLoop::current()->RunUntilIdle();
186 EXPECT_EQ(1, callback_count_
);
189 TEST_F(SyncableFileOperationRunnerTest
, WriteToParentAndChild
) {
190 // First create the kDir directory and kChild in the dir.
191 EXPECT_EQ(base::PLATFORM_FILE_OK
, file_system_
.CreateDirectory(URL(kDir
)));
192 EXPECT_EQ(base::PLATFORM_FILE_OK
, file_system_
.CreateFile(URL(kChild
)));
194 // Start syncing the kDir directory.
195 sync_status()->StartSyncing(URL(kDir
));
196 ASSERT_FALSE(sync_status()->IsWritable(URL(kDir
)));
198 // Writes to kParent and kChild should be all queued up.
199 ResetCallbackStatus();
200 file_system_
.operation_runner()->Truncate(
201 URL(kChild
), 1, ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
202 file_system_
.operation_runner()->Remove(
203 URL(kParent
), true /* recursive */,
204 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
205 base::MessageLoop::current()->RunUntilIdle();
206 EXPECT_EQ(0, callback_count_
);
208 // Read operations are not blocked (and are executed before queued ones).
209 file_system_
.operation_runner()->DirectoryExists(
210 URL(kDir
), ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
211 base::MessageLoop::current()->RunUntilIdle();
212 EXPECT_EQ(1, callback_count_
);
214 // Writes to unrelated files must succeed as well.
215 ResetCallbackStatus();
216 file_system_
.operation_runner()->CreateDirectory(
217 URL(kOther
), false /* exclusive */, false /* recursive */,
218 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
219 base::MessageLoop::current()->RunUntilIdle();
220 EXPECT_EQ(1, callback_count_
);
222 // End syncing (to enable write).
223 sync_status()->EndSyncing(URL(kDir
));
224 ASSERT_TRUE(sync_status()->IsWritable(URL(kDir
)));
226 ResetCallbackStatus();
227 base::MessageLoop::current()->RunUntilIdle();
228 EXPECT_EQ(2, callback_count_
);
231 TEST_F(SyncableFileOperationRunnerTest
, CopyAndMove
) {
232 // First create the kDir directory and kChild in the dir.
233 EXPECT_EQ(base::PLATFORM_FILE_OK
, file_system_
.CreateDirectory(URL(kDir
)));
234 EXPECT_EQ(base::PLATFORM_FILE_OK
, file_system_
.CreateFile(URL(kChild
)));
236 // Start syncing the kParent directory.
237 sync_status()->StartSyncing(URL(kParent
));
239 // Copying kDir to other directory should succeed, while moving would fail
240 // (since the source directory is in syncing).
241 ResetCallbackStatus();
242 file_system_
.operation_runner()->Copy(
243 URL(kDir
), URL("dest-copy"),
244 fileapi::FileSystemOperation::OPTION_NONE
,
245 fileapi::FileSystemOperationRunner::CopyProgressCallback(),
246 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
247 file_system_
.operation_runner()->Move(
248 URL(kDir
), URL("dest-move"),
249 fileapi::FileSystemOperation::OPTION_NONE
,
250 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
251 base::MessageLoop::current()->RunUntilIdle();
252 EXPECT_EQ(1, callback_count_
);
254 // Only "dest-copy1" should exist.
255 EXPECT_EQ(base::PLATFORM_FILE_OK
,
256 file_system_
.DirectoryExists(URL("dest-copy")));
257 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND
,
258 file_system_
.DirectoryExists(URL("dest-move")));
260 // Start syncing the "dest-copy2" directory.
261 sync_status()->StartSyncing(URL("dest-copy2"));
263 // Now the destination is also locked copying kDir should be queued.
264 ResetCallbackStatus();
265 file_system_
.operation_runner()->Copy(
266 URL(kDir
), URL("dest-copy2"),
267 fileapi::FileSystemOperation::OPTION_NONE
,
268 fileapi::FileSystemOperationRunner::CopyProgressCallback(),
269 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
270 base::MessageLoop::current()->RunUntilIdle();
271 EXPECT_EQ(0, callback_count_
);
273 // Finish syncing the "dest-copy2" directory to unlock Copy.
274 sync_status()->EndSyncing(URL("dest-copy2"));
275 ResetCallbackStatus();
276 base::MessageLoop::current()->RunUntilIdle();
277 EXPECT_EQ(1, callback_count_
);
279 // Now we should have "dest-copy2".
280 EXPECT_EQ(base::PLATFORM_FILE_OK
,
281 file_system_
.DirectoryExists(URL("dest-copy2")));
283 // Finish syncing the kParent to unlock Move.
284 sync_status()->EndSyncing(URL(kParent
));
285 ResetCallbackStatus();
286 base::MessageLoop::current()->RunUntilIdle();
287 EXPECT_EQ(1, callback_count_
);
289 // Now we should have "dest-move".
290 EXPECT_EQ(base::PLATFORM_FILE_OK
,
291 file_system_
.DirectoryExists(URL("dest-move")));
294 TEST_F(SyncableFileOperationRunnerTest
, Write
) {
295 EXPECT_EQ(base::PLATFORM_FILE_OK
, file_system_
.CreateFile(URL(kFile
)));
296 const std::string
kData("Lorem ipsum.");
297 ScopedTextBlob
blob(url_request_context_
, "blob:foo", kData
);
299 sync_status()->StartSyncing(URL(kFile
));
301 ResetCallbackStatus();
302 file_system_
.operation_runner()->Write(
303 &url_request_context_
,
304 URL(kFile
), blob
.GetBlobDataHandle(), 0, GetWriteCallback(FROM_HERE
));
305 base::MessageLoop::current()->RunUntilIdle();
306 EXPECT_EQ(0, callback_count_
);
308 sync_status()->EndSyncing(URL(kFile
));
309 ResetCallbackStatus();
311 while (!write_complete_
)
312 base::MessageLoop::current()->RunUntilIdle();
314 EXPECT_EQ(base::PLATFORM_FILE_OK
, write_status_
);
315 EXPECT_EQ(kData
.size(), write_bytes_
);
316 EXPECT_TRUE(write_complete_
);
319 TEST_F(SyncableFileOperationRunnerTest
, QueueAndCancel
) {
320 sync_status()->StartSyncing(URL(kFile
));
321 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile
)));
323 ResetCallbackStatus();
324 file_system_
.operation_runner()->CreateFile(
325 URL(kFile
), false /* exclusive */,
326 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_ERROR_ABORT
));
327 file_system_
.operation_runner()->Truncate(
329 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_ERROR_ABORT
));
330 base::MessageLoop::current()->RunUntilIdle();
331 EXPECT_EQ(0, callback_count_
);
333 ResetCallbackStatus();
335 // This shouldn't crash nor leak memory.
336 sync_context_
->ShutdownOnUIThread();
337 sync_context_
= NULL
;
338 base::MessageLoop::current()->RunUntilIdle();
339 EXPECT_EQ(2, callback_count_
);
342 // Test if CopyInForeignFile runs cooperatively with other Sync operations.
343 TEST_F(SyncableFileOperationRunnerTest
, CopyInForeignFile
) {
344 const std::string
kTestData("test data");
346 base::FilePath temp_path
;
347 ASSERT_TRUE(CreateTempFile(&temp_path
));
348 ASSERT_EQ(static_cast<int>(kTestData
.size()),
349 file_util::WriteFile(
350 temp_path
, kTestData
.data(), kTestData
.size()));
352 sync_status()->StartSyncing(URL(kFile
));
353 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile
)));
355 // The URL is in syncing so CopyIn (which is a write operation) won't run.
356 ResetCallbackStatus();
357 file_system_
.operation_runner()->CopyInForeignFile(
358 temp_path
, URL(kFile
),
359 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
360 base::MessageLoop::current()->RunUntilIdle();
361 EXPECT_EQ(0, callback_count_
);
363 // End syncing (to enable write).
364 sync_status()->EndSyncing(URL(kFile
));
365 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile
)));
367 ResetCallbackStatus();
368 base::MessageLoop::current()->RunUntilIdle();
369 EXPECT_EQ(1, callback_count_
);
371 // Now the file must have been created and have the same content as temp_path.
372 ResetCallbackStatus();
373 file_system_
.DoVerifyFile(
374 URL(kFile
), kTestData
,
375 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
376 base::MessageLoop::current()->RunUntilIdle();
377 EXPECT_EQ(1, callback_count_
);
380 TEST_F(SyncableFileOperationRunnerTest
, Cancel
) {
382 file_system_
.operation_runner()->CreateFile(
383 URL(kFile
), false /* exclusive */,
384 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
385 base::MessageLoop::current()->RunUntilIdle();
386 EXPECT_EQ(1, callback_count_
);
388 // Run Truncate and immediately cancel. This shouldn't crash.
389 ResetCallbackStatus();
390 fileapi::FileSystemOperationRunner::OperationID id
=
391 file_system_
.operation_runner()->Truncate(
393 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
394 file_system_
.operation_runner()->Cancel(
395 id
, ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_ERROR_INVALID_OPERATION
));
396 base::MessageLoop::current()->RunUntilIdle();
397 EXPECT_EQ(2, callback_count_
);
400 } // namespace sync_file_system