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.
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/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"
23 using fileapi::FileSystemOperation
;
24 using fileapi::FileSystemURL
;
25 using webkit_blob::MockBlobURLRequestContext
;
26 using webkit_blob::ScopedTextBlob
;
27 using base::PlatformFileError
;
29 namespace sync_file_system
{
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";
40 class SyncableFileOperationRunnerTest
: public testing::Test
{
42 typedef FileSystemOperation::StatusCallback StatusCallback
;
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()),
52 write_status_(base::PLATFORM_FILE_ERROR_FAILED
),
54 write_complete_(false),
55 url_request_context_(file_system_
.file_system_context()),
56 weak_factory_(this) {}
58 virtual void SetUp() OVERRIDE
{
59 ASSERT_TRUE(dir_
.CreateUniqueTempDir());
61 sync_context_
= new LocalFileSyncContext(base::MessageLoopProxy::current(),
62 base::MessageLoopProxy::current());
63 ASSERT_EQ(SYNC_STATUS_OK
,
64 file_system_
.MaybeInitializeFileSystemContext(sync_context_
));
66 ASSERT_EQ(base::PLATFORM_FILE_OK
, file_system_
.OpenFileSystem());
67 ASSERT_EQ(base::PLATFORM_FILE_OK
,
68 file_system_
.CreateDirectory(URL(kParent
)));
71 virtual void TearDown() OVERRIDE
{
73 sync_context_
->ShutdownOnUIThread();
76 file_system_
.TearDown();
77 message_loop_
.RunUntilIdle();
78 RevokeSyncableFileSystem(kServiceName
);
81 FileSystemURL
URL(const std::string
& path
) {
82 return file_system_
.URL(path
);
85 LocalFileSyncStatus
* sync_status() {
86 return file_system_
.file_system_context()->sync_context()->sync_status();
89 void ResetCallbackStatus() {
90 write_status_
= base::PLATFORM_FILE_ERROR_FAILED
;
92 write_complete_
= false;
96 StatusCallback
ExpectStatus(const tracked_objects::Location
& location
,
97 PlatformFileError expect
) {
98 return base::Bind(&SyncableFileOperationRunnerTest::DidFinish
,
99 weak_factory_
.GetWeakPtr(), location
, expect
);
102 FileSystemOperation::WriteCallback
GetWriteCallback(
103 const tracked_objects::Location
& location
) {
104 return base::Bind(&SyncableFileOperationRunnerTest::DidWrite
,
105 weak_factory_
.GetWeakPtr(), location
);
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
;
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
);
124 bool CreateTempFile(base::FilePath
* path
) {
125 return file_util::CreateTemporaryFileInDir(dir_
.path(), path
);
128 base::ScopedTempDir dir_
;
130 base::MessageLoop message_loop_
;
131 CannedSyncableFileSystem file_system_
;
132 scoped_refptr
<LocalFileSyncContext
> sync_context_
;
135 PlatformFileError write_status_
;
137 bool write_complete_
;
139 MockBlobURLRequestContext url_request_context_
;
141 base::WeakPtrFactory
<SyncableFileOperationRunnerTest
> weak_factory_
;
143 DISALLOW_COPY_AND_ASSIGN(SyncableFileOperationRunnerTest
);
146 TEST_F(SyncableFileOperationRunnerTest
, SimpleQueue
) {
147 sync_status()->StartSyncing(URL(kFile
));
148 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile
)));
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(
157 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_OK
));
158 base::MessageLoop::current()->RunUntilIdle();
159 EXPECT_EQ(0, callback_count_
);
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_
);
167 // End syncing (to enable write).
168 sync_status()->EndSyncing(URL(kFile
));
169 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile
)));
171 ResetCallbackStatus();
172 base::MessageLoop::current()->RunUntilIdle();
173 EXPECT_EQ(2, callback_count_
);
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_
);
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
)));
188 // Start syncing the kDir directory.
189 sync_status()->StartSyncing(URL(kDir
));
190 ASSERT_FALSE(sync_status()->IsWritable(URL(kDir
)));
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_
);
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_
);
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_
);
216 // End syncing (to enable write).
217 sync_status()->EndSyncing(URL(kDir
));
218 ASSERT_TRUE(sync_status()->IsWritable(URL(kDir
)));
220 ResetCallbackStatus();
221 base::MessageLoop::current()->RunUntilIdle();
222 EXPECT_EQ(2, callback_count_
);
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
)));
230 // Start syncing the kParent directory.
231 sync_status()->StartSyncing(URL(kParent
));
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_
);
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")));
251 // Start syncing the "dest-copy2" directory.
252 sync_status()->StartSyncing(URL("dest-copy2"));
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_
);
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_
);
268 // Now we should have "dest-copy2".
269 EXPECT_EQ(base::PLATFORM_FILE_OK
,
270 file_system_
.DirectoryExists(URL("dest-copy2")));
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_
);
278 // Now we should have "dest-move".
279 EXPECT_EQ(base::PLATFORM_FILE_OK
,
280 file_system_
.DirectoryExists(URL("dest-move")));
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
);
289 sync_status()->StartSyncing(URL(kFile
));
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_
);
298 sync_status()->EndSyncing(URL(kFile
));
299 ResetCallbackStatus();
301 while (!write_complete_
)
302 base::MessageLoop::current()->RunUntilIdle();
304 EXPECT_EQ(base::PLATFORM_FILE_OK
, write_status_
);
305 EXPECT_EQ(kData
.size(), write_bytes_
);
306 EXPECT_TRUE(write_complete_
);
309 TEST_F(SyncableFileOperationRunnerTest
, QueueAndCancel
) {
310 sync_status()->StartSyncing(URL(kFile
));
311 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile
)));
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(
319 ExpectStatus(FROM_HERE
, base::PLATFORM_FILE_ERROR_ABORT
));
320 base::MessageLoop::current()->RunUntilIdle();
321 EXPECT_EQ(0, callback_count_
);
323 ResetCallbackStatus();
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_
);
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");
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()));
343 sync_status()->StartSyncing(URL(kFile
));
344 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile
)));
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_
);
354 // End syncing (to enable write).
355 sync_status()->EndSyncing(URL(kFile
));
356 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile
)));
358 ResetCallbackStatus();
359 base::MessageLoop::current()->RunUntilIdle();
360 EXPECT_EQ(1, callback_count_
);
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_
);
371 } // namespace sync_file_system