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/files/file.h"
9 #include "base/files/file_util.h"
10 #include "base/location.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/run_loop.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h"
16 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
17 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
18 #include "chrome/browser/sync_file_system/local/local_file_sync_status.h"
19 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
20 #include "chrome/browser/sync_file_system/local/syncable_file_operation_runner.h"
21 #include "chrome/browser/sync_file_system/local/syncable_file_system_operation.h"
22 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
23 #include "content/public/test/mock_blob_url_request_context.h"
24 #include "content/public/test/test_browser_thread_bundle.h"
25 #include "storage/browser/fileapi/file_system_context.h"
26 #include "storage/browser/fileapi/file_system_operation_runner.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
29 #include "third_party/leveldatabase/src/include/leveldb/env.h"
31 using storage::FileSystemOperation
;
32 using storage::FileSystemURL
;
33 using content::MockBlobURLRequestContext
;
34 using content::ScopedTextBlob
;
37 namespace sync_file_system
{
40 const std::string kParent
= "foo";
41 const std::string kFile
= "foo/file";
42 const std::string kDir
= "foo/dir";
43 const std::string kChild
= "foo/dir/bar";
44 const std::string kOther
= "bar";
47 class SyncableFileOperationRunnerTest
: public testing::Test
{
49 typedef FileSystemOperation::StatusCallback StatusCallback
;
51 // Use the current thread as IO thread so that we can directly call
52 // operations in the tests.
53 SyncableFileOperationRunnerTest()
54 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
),
55 in_memory_env_(leveldb::NewMemEnv(leveldb::Env::Default())),
56 file_system_(GURL("http://example.com"),
58 base::ThreadTaskRunnerHandle::Get().get(),
59 base::ThreadTaskRunnerHandle::Get().get()),
61 write_status_(File::FILE_ERROR_FAILED
),
63 write_complete_(false),
64 url_request_context_(file_system_
.file_system_context()),
65 weak_factory_(this) {}
67 void SetUp() override
{
68 ASSERT_TRUE(dir_
.CreateUniqueTempDir());
70 file_system_
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
71 sync_context_
= new LocalFileSyncContext(
74 base::ThreadTaskRunnerHandle::Get().get(),
75 base::ThreadTaskRunnerHandle::Get().get());
78 file_system_
.MaybeInitializeFileSystemContext(sync_context_
.get()));
80 ASSERT_EQ(File::FILE_OK
, file_system_
.OpenFileSystem());
81 ASSERT_EQ(File::FILE_OK
,
82 file_system_
.CreateDirectory(URL(kParent
)));
85 void TearDown() override
{
86 if (sync_context_
.get())
87 sync_context_
->ShutdownOnUIThread();
88 sync_context_
= nullptr;
90 file_system_
.TearDown();
91 RevokeSyncableFileSystem();
94 FileSystemURL
URL(const std::string
& path
) {
95 return file_system_
.URL(path
);
98 LocalFileSyncStatus
* sync_status() {
99 return file_system_
.backend()->sync_context()->sync_status();
102 void ResetCallbackStatus() {
103 write_status_
= File::FILE_ERROR_FAILED
;
105 write_complete_
= false;
109 StatusCallback
ExpectStatus(const tracked_objects::Location
& location
,
110 File::Error expect
) {
111 return base::Bind(&SyncableFileOperationRunnerTest::DidFinish
,
112 weak_factory_
.GetWeakPtr(), location
, expect
);
115 FileSystemOperation::WriteCallback
GetWriteCallback(
116 const tracked_objects::Location
& location
) {
117 return base::Bind(&SyncableFileOperationRunnerTest::DidWrite
,
118 weak_factory_
.GetWeakPtr(), location
);
121 void DidWrite(const tracked_objects::Location
& location
,
122 File::Error status
, int64 bytes
, bool complete
) {
123 SCOPED_TRACE(testing::Message() << location
.ToString());
124 write_status_
= status
;
125 write_bytes_
+= bytes
;
126 write_complete_
= complete
;
130 void DidFinish(const tracked_objects::Location
& location
,
131 File::Error expect
, File::Error status
) {
132 SCOPED_TRACE(testing::Message() << location
.ToString());
133 EXPECT_EQ(expect
, status
);
135 base::MessageLoop::current()->Quit();
138 bool CreateTempFile(base::FilePath
* path
) {
139 return base::CreateTemporaryFileInDir(dir_
.path(), path
);
142 content::TestBrowserThreadBundle thread_bundle_
;
144 base::ScopedTempDir dir_
;
145 scoped_ptr
<leveldb::Env
> in_memory_env_
;
147 CannedSyncableFileSystem file_system_
;
148 scoped_refptr
<LocalFileSyncContext
> sync_context_
;
151 File::Error write_status_
;
153 bool write_complete_
;
155 MockBlobURLRequestContext url_request_context_
;
158 base::WeakPtrFactory
<SyncableFileOperationRunnerTest
> weak_factory_
;
160 DISALLOW_COPY_AND_ASSIGN(SyncableFileOperationRunnerTest
);
163 TEST_F(SyncableFileOperationRunnerTest
, SimpleQueue
) {
164 sync_status()->StartSyncing(URL(kFile
));
165 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile
)));
167 // The URL is in syncing so the write operations won't run.
168 ResetCallbackStatus();
169 file_system_
.operation_runner()->CreateFile(
170 URL(kFile
), false /* exclusive */,
171 ExpectStatus(FROM_HERE
, File::FILE_OK
));
172 file_system_
.operation_runner()->Truncate(
174 ExpectStatus(FROM_HERE
, File::FILE_OK
));
175 base::RunLoop().RunUntilIdle();
176 EXPECT_EQ(0, callback_count_
);
178 // Read operations are not blocked (and are executed before queued ones).
179 file_system_
.operation_runner()->FileExists(
180 URL(kFile
), ExpectStatus(FROM_HERE
, File::FILE_ERROR_NOT_FOUND
));
181 base::RunLoop().RunUntilIdle();
182 EXPECT_EQ(1, callback_count_
);
184 // End syncing (to enable write).
185 sync_status()->EndSyncing(URL(kFile
));
186 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile
)));
188 ResetCallbackStatus();
189 base::RunLoop().RunUntilIdle();
190 EXPECT_EQ(2, callback_count_
);
192 // Now the file must have been created and updated.
193 ResetCallbackStatus();
194 file_system_
.operation_runner()->FileExists(
195 URL(kFile
), ExpectStatus(FROM_HERE
, File::FILE_OK
));
196 base::RunLoop().RunUntilIdle();
197 EXPECT_EQ(1, callback_count_
);
200 TEST_F(SyncableFileOperationRunnerTest
, WriteToParentAndChild
) {
201 // First create the kDir directory and kChild in the dir.
202 EXPECT_EQ(File::FILE_OK
, file_system_
.CreateDirectory(URL(kDir
)));
203 EXPECT_EQ(File::FILE_OK
, file_system_
.CreateFile(URL(kChild
)));
205 // Start syncing the kDir directory.
206 sync_status()->StartSyncing(URL(kDir
));
207 ASSERT_FALSE(sync_status()->IsWritable(URL(kDir
)));
209 // Writes to kParent and kChild should be all queued up.
210 ResetCallbackStatus();
211 file_system_
.operation_runner()->Truncate(
212 URL(kChild
), 1, ExpectStatus(FROM_HERE
, File::FILE_OK
));
213 file_system_
.operation_runner()->Remove(
214 URL(kParent
), true /* recursive */,
215 ExpectStatus(FROM_HERE
, File::FILE_OK
));
216 base::RunLoop().RunUntilIdle();
217 EXPECT_EQ(0, callback_count_
);
219 // Read operations are not blocked (and are executed before queued ones).
220 file_system_
.operation_runner()->DirectoryExists(
221 URL(kDir
), ExpectStatus(FROM_HERE
, File::FILE_OK
));
222 base::RunLoop().Run();
223 EXPECT_EQ(1, callback_count_
);
225 // Writes to unrelated files must succeed as well.
226 ResetCallbackStatus();
227 file_system_
.operation_runner()->CreateDirectory(
228 URL(kOther
), false /* exclusive */, false /* recursive */,
229 ExpectStatus(FROM_HERE
, File::FILE_OK
));
230 base::RunLoop().Run();
231 EXPECT_EQ(1, callback_count_
);
233 // End syncing (to enable write).
234 sync_status()->EndSyncing(URL(kDir
));
235 ASSERT_TRUE(sync_status()->IsWritable(URL(kDir
)));
237 ResetCallbackStatus();
238 base::RunLoop().Run();
239 EXPECT_EQ(2, callback_count_
);
242 TEST_F(SyncableFileOperationRunnerTest
, CopyAndMove
) {
243 // First create the kDir directory and kChild in the dir.
244 EXPECT_EQ(File::FILE_OK
, file_system_
.CreateDirectory(URL(kDir
)));
245 EXPECT_EQ(File::FILE_OK
, file_system_
.CreateFile(URL(kChild
)));
247 // Start syncing the kParent directory.
248 sync_status()->StartSyncing(URL(kParent
));
250 // Copying kDir to other directory should succeed, while moving would fail
251 // (since the source directory is in syncing).
252 ResetCallbackStatus();
253 file_system_
.operation_runner()->Copy(
254 URL(kDir
), URL("dest-copy"), storage::FileSystemOperation::OPTION_NONE
,
255 storage::FileSystemOperation::ERROR_BEHAVIOR_ABORT
,
256 storage::FileSystemOperationRunner::CopyProgressCallback(),
257 ExpectStatus(FROM_HERE
, File::FILE_OK
));
258 file_system_
.operation_runner()->Move(
261 storage::FileSystemOperation::OPTION_NONE
,
262 ExpectStatus(FROM_HERE
, File::FILE_OK
));
263 base::RunLoop().Run();
264 EXPECT_EQ(1, callback_count_
);
266 // Only "dest-copy1" should exist.
267 EXPECT_EQ(File::FILE_OK
,
268 file_system_
.DirectoryExists(URL("dest-copy")));
269 EXPECT_EQ(File::FILE_ERROR_NOT_FOUND
,
270 file_system_
.DirectoryExists(URL("dest-move")));
272 // Start syncing the "dest-copy2" directory.
273 sync_status()->StartSyncing(URL("dest-copy2"));
275 // Now the destination is also locked copying kDir should be queued.
276 ResetCallbackStatus();
277 file_system_
.operation_runner()->Copy(
278 URL(kDir
), URL("dest-copy2"), storage::FileSystemOperation::OPTION_NONE
,
279 storage::FileSystemOperation::ERROR_BEHAVIOR_ABORT
,
280 storage::FileSystemOperationRunner::CopyProgressCallback(),
281 ExpectStatus(FROM_HERE
, File::FILE_OK
));
282 base::RunLoop().RunUntilIdle();
283 EXPECT_EQ(0, callback_count_
);
285 // Finish syncing the "dest-copy2" directory to unlock Copy.
286 sync_status()->EndSyncing(URL("dest-copy2"));
287 ResetCallbackStatus();
288 base::RunLoop().Run();
289 EXPECT_EQ(1, callback_count_
);
291 // Now we should have "dest-copy2".
292 EXPECT_EQ(File::FILE_OK
,
293 file_system_
.DirectoryExists(URL("dest-copy2")));
295 // Finish syncing the kParent to unlock Move.
296 sync_status()->EndSyncing(URL(kParent
));
297 ResetCallbackStatus();
298 base::RunLoop().Run();
299 EXPECT_EQ(1, callback_count_
);
301 // Now we should have "dest-move".
302 EXPECT_EQ(File::FILE_OK
,
303 file_system_
.DirectoryExists(URL("dest-move")));
306 TEST_F(SyncableFileOperationRunnerTest
, Write
) {
307 EXPECT_EQ(File::FILE_OK
, file_system_
.CreateFile(URL(kFile
)));
308 const std::string
kData("Lorem ipsum.");
309 ScopedTextBlob
blob(url_request_context_
, "blob:foo", kData
);
311 sync_status()->StartSyncing(URL(kFile
));
313 ResetCallbackStatus();
314 file_system_
.operation_runner()->Write(
315 &url_request_context_
,
316 URL(kFile
), blob
.GetBlobDataHandle(), 0, GetWriteCallback(FROM_HERE
));
317 base::RunLoop().RunUntilIdle();
318 EXPECT_EQ(0, callback_count_
);
320 sync_status()->EndSyncing(URL(kFile
));
321 ResetCallbackStatus();
323 while (!write_complete_
)
324 base::MessageLoop::current()->RunUntilIdle();
326 EXPECT_EQ(File::FILE_OK
, write_status_
);
327 EXPECT_EQ(kData
.size(), write_bytes_
);
328 EXPECT_TRUE(write_complete_
);
331 TEST_F(SyncableFileOperationRunnerTest
, QueueAndCancel
) {
332 sync_status()->StartSyncing(URL(kFile
));
333 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile
)));
335 ResetCallbackStatus();
336 file_system_
.operation_runner()->CreateFile(
337 URL(kFile
), false /* exclusive */,
338 ExpectStatus(FROM_HERE
, File::FILE_ERROR_ABORT
));
339 file_system_
.operation_runner()->Truncate(
341 ExpectStatus(FROM_HERE
, File::FILE_ERROR_ABORT
));
342 base::MessageLoop::current()->RunUntilIdle();
343 EXPECT_EQ(0, callback_count_
);
345 ResetCallbackStatus();
347 // This shouldn't crash nor leak memory.
348 sync_context_
->ShutdownOnUIThread();
349 sync_context_
= nullptr;
350 base::MessageLoop::current()->RunUntilIdle();
351 EXPECT_EQ(2, callback_count_
);
354 // Test if CopyInForeignFile runs cooperatively with other Sync operations.
355 TEST_F(SyncableFileOperationRunnerTest
, CopyInForeignFile
) {
356 const std::string
kTestData("test data");
358 base::FilePath temp_path
;
359 ASSERT_TRUE(CreateTempFile(&temp_path
));
360 ASSERT_EQ(static_cast<int>(kTestData
.size()),
362 temp_path
, kTestData
.data(), kTestData
.size()));
364 sync_status()->StartSyncing(URL(kFile
));
365 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile
)));
367 // The URL is in syncing so CopyIn (which is a write operation) won't run.
368 ResetCallbackStatus();
369 file_system_
.operation_runner()->CopyInForeignFile(
370 temp_path
, URL(kFile
),
371 ExpectStatus(FROM_HERE
, File::FILE_OK
));
372 base::MessageLoop::current()->RunUntilIdle();
373 EXPECT_EQ(0, callback_count_
);
375 // End syncing (to enable write).
376 sync_status()->EndSyncing(URL(kFile
));
377 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile
)));
379 ResetCallbackStatus();
380 base::MessageLoop::current()->RunUntilIdle();
381 EXPECT_EQ(1, callback_count_
);
383 // Now the file must have been created and have the same content as temp_path.
384 ResetCallbackStatus();
385 file_system_
.DoVerifyFile(
386 URL(kFile
), kTestData
,
387 ExpectStatus(FROM_HERE
, File::FILE_OK
));
388 base::MessageLoop::current()->RunUntilIdle();
389 EXPECT_EQ(1, callback_count_
);
392 TEST_F(SyncableFileOperationRunnerTest
, Cancel
) {
394 file_system_
.operation_runner()->CreateFile(
395 URL(kFile
), false /* exclusive */,
396 ExpectStatus(FROM_HERE
, File::FILE_OK
));
397 base::MessageLoop::current()->RunUntilIdle();
398 EXPECT_EQ(1, callback_count_
);
400 // Run Truncate and immediately cancel. This shouldn't crash.
401 ResetCallbackStatus();
402 storage::FileSystemOperationRunner::OperationID id
=
403 file_system_
.operation_runner()->Truncate(
404 URL(kFile
), 10, ExpectStatus(FROM_HERE
, File::FILE_OK
));
405 file_system_
.operation_runner()->Cancel(
406 id
, ExpectStatus(FROM_HERE
, File::FILE_ERROR_INVALID_OPERATION
));
407 base::RunLoop().Run();
408 EXPECT_EQ(2, callback_count_
);
411 } // namespace sync_file_system