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.
5 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
10 #include "base/bind_helpers.h"
11 #include "base/file_util.h"
12 #include "base/files/file_path.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/platform_file.h"
15 #include "base/stl_util.h"
16 #include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h"
17 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
18 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
19 #include "chrome/browser/sync_file_system/sync_file_metadata.h"
20 #include "chrome/browser/sync_file_system/sync_status_code.h"
21 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/test/mock_blob_url_request_context.h"
24 #include "content/public/test/test_browser_thread_bundle.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
27 #include "third_party/leveldatabase/src/include/leveldb/env.h"
28 #include "webkit/browser/fileapi/file_system_context.h"
29 #include "webkit/browser/fileapi/file_system_operation_runner.h"
30 #include "webkit/browser/fileapi/isolated_context.h"
31 #include "webkit/common/blob/scoped_file.h"
33 #define FPL FILE_PATH_LITERAL
35 using content::BrowserThread
;
36 using fileapi::FileSystemContext
;
37 using fileapi::FileSystemURL
;
38 using fileapi::FileSystemURLSet
;
40 // This tests LocalFileSyncContext behavior in multi-thread /
41 // multi-file-system-context environment.
42 // Basic combined tests (single-thread / single-file-system-context)
43 // that involve LocalFileSyncContext are also in
44 // syncable_file_system_unittests.cc.
46 namespace sync_file_system
{
49 const char kOrigin1
[] = "http://example.com";
50 const char kOrigin2
[] = "http://chromium.org";
53 class LocalFileSyncContextTest
: public testing::Test
{
55 LocalFileSyncContextTest()
57 content::TestBrowserThreadBundle::REAL_FILE_THREAD
|
58 content::TestBrowserThreadBundle::REAL_IO_THREAD
),
59 status_(SYNC_FILE_ERROR_FAILED
),
60 file_error_(base::File::FILE_ERROR_FAILED
),
61 async_modify_finished_(false),
62 has_inflight_prepare_for_sync_(false) {}
64 virtual void SetUp() OVERRIDE
{
65 RegisterSyncableFileSystem();
66 ASSERT_TRUE(dir_
.CreateUniqueTempDir());
67 in_memory_env_
.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
69 ui_task_runner_
= base::MessageLoop::current()->message_loop_proxy();
70 io_task_runner_
= BrowserThread::GetMessageLoopProxyForThread(
72 file_task_runner_
= BrowserThread::GetMessageLoopProxyForThread(
76 virtual void TearDown() OVERRIDE
{
77 RevokeSyncableFileSystem();
80 void StartPrepareForSync(FileSystemContext
* file_system_context
,
81 const FileSystemURL
& url
,
82 LocalFileSyncContext::SyncMode sync_mode
,
83 SyncFileMetadata
* metadata
,
84 FileChangeList
* changes
,
85 webkit_blob::ScopedFile
* snapshot
) {
86 ASSERT_TRUE(changes
!= NULL
);
87 ASSERT_FALSE(has_inflight_prepare_for_sync_
);
88 status_
= SYNC_STATUS_UNKNOWN
;
89 has_inflight_prepare_for_sync_
= true;
90 sync_context_
->PrepareForSync(
94 base::Bind(&LocalFileSyncContextTest::DidPrepareForSync
,
95 base::Unretained(this), metadata
, changes
, snapshot
));
98 SyncStatusCode
PrepareForSync(FileSystemContext
* file_system_context
,
99 const FileSystemURL
& url
,
100 LocalFileSyncContext::SyncMode sync_mode
,
101 SyncFileMetadata
* metadata
,
102 FileChangeList
* changes
,
103 webkit_blob::ScopedFile
* snapshot
) {
104 StartPrepareForSync(file_system_context
, url
, sync_mode
,
105 metadata
, changes
, snapshot
);
106 base::MessageLoop::current()->Run();
110 base::Closure
GetPrepareForSyncClosure(
111 FileSystemContext
* file_system_context
,
112 const FileSystemURL
& url
,
113 LocalFileSyncContext::SyncMode sync_mode
,
114 SyncFileMetadata
* metadata
,
115 FileChangeList
* changes
,
116 webkit_blob::ScopedFile
* snapshot
) {
117 return base::Bind(&LocalFileSyncContextTest::StartPrepareForSync
,
118 base::Unretained(this),
119 base::Unretained(file_system_context
),
120 url
, sync_mode
, metadata
, changes
, snapshot
);
123 void DidPrepareForSync(SyncFileMetadata
* metadata_out
,
124 FileChangeList
* changes_out
,
125 webkit_blob::ScopedFile
* snapshot_out
,
126 SyncStatusCode status
,
127 const LocalFileSyncInfo
& sync_file_info
,
128 webkit_blob::ScopedFile snapshot
) {
129 ASSERT_TRUE(ui_task_runner_
->RunsTasksOnCurrentThread());
130 has_inflight_prepare_for_sync_
= false;
132 *metadata_out
= sync_file_info
.metadata
;
133 *changes_out
= sync_file_info
.changes
;
135 *snapshot_out
= snapshot
.Pass();
136 base::MessageLoop::current()->Quit();
139 SyncStatusCode
ApplyRemoteChange(FileSystemContext
* file_system_context
,
140 const FileChange
& change
,
141 const base::FilePath
& local_path
,
142 const FileSystemURL
& url
,
143 SyncFileType expected_file_type
) {
144 SCOPED_TRACE(testing::Message() << "ApplyChange for " <<
147 // First we should call PrepareForSync to disable writing.
148 SyncFileMetadata metadata
;
149 FileChangeList changes
;
150 EXPECT_EQ(SYNC_STATUS_OK
,
151 PrepareForSync(file_system_context
, url
,
152 LocalFileSyncContext::SYNC_EXCLUSIVE
,
153 &metadata
, &changes
, NULL
));
154 EXPECT_EQ(expected_file_type
, metadata
.file_type
);
156 status_
= SYNC_STATUS_UNKNOWN
;
157 sync_context_
->ApplyRemoteChange(
158 file_system_context
, change
, local_path
, url
,
159 base::Bind(&LocalFileSyncContextTest::DidApplyRemoteChange
,
160 base::Unretained(this),
161 make_scoped_refptr(file_system_context
), url
));
162 base::MessageLoop::current()->Run();
166 void DidApplyRemoteChange(FileSystemContext
* file_system_context
,
167 const FileSystemURL
& url
,
168 SyncStatusCode status
) {
170 sync_context_
->FinalizeExclusiveSync(
171 file_system_context
, url
,
172 status
== SYNC_STATUS_OK
/* clear_local_changes */,
173 base::MessageLoop::QuitClosure());
176 void StartModifyFileOnIOThread(CannedSyncableFileSystem
* file_system
,
177 const FileSystemURL
& url
) {
178 ASSERT_TRUE(file_system
!= NULL
);
179 if (!io_task_runner_
->RunsTasksOnCurrentThread()) {
180 async_modify_finished_
= false;
181 ASSERT_TRUE(ui_task_runner_
->RunsTasksOnCurrentThread());
182 io_task_runner_
->PostTask(
184 base::Bind(&LocalFileSyncContextTest::StartModifyFileOnIOThread
,
185 base::Unretained(this), file_system
, url
));
188 ASSERT_TRUE(io_task_runner_
->RunsTasksOnCurrentThread());
189 file_error_
= base::File::FILE_ERROR_FAILED
;
190 file_system
->operation_runner()->Truncate(
191 url
, 1, base::Bind(&LocalFileSyncContextTest::DidModifyFile
,
192 base::Unretained(this)));
195 base::File::Error
WaitUntilModifyFileIsDone() {
196 while (!async_modify_finished_
)
197 base::MessageLoop::current()->RunUntilIdle();
201 void DidModifyFile(base::File::Error error
) {
202 if (!ui_task_runner_
->RunsTasksOnCurrentThread()) {
203 ASSERT_TRUE(io_task_runner_
->RunsTasksOnCurrentThread());
204 ui_task_runner_
->PostTask(
206 base::Bind(&LocalFileSyncContextTest::DidModifyFile
,
207 base::Unretained(this), error
));
210 ASSERT_TRUE(ui_task_runner_
->RunsTasksOnCurrentThread());
212 async_modify_finished_
= true;
215 void SimulateFinishSync(FileSystemContext
* file_system_context
,
216 const FileSystemURL
& url
,
217 SyncStatusCode status
,
218 LocalFileSyncContext::SyncMode sync_mode
) {
219 if (sync_mode
== LocalFileSyncContext::SYNC_SNAPSHOT
) {
220 sync_context_
->FinalizeSnapshotSync(
221 file_system_context
, url
, status
,
222 base::Bind(&base::DoNothing
));
224 sync_context_
->FinalizeExclusiveSync(
225 file_system_context
, url
,
226 status
== SYNC_STATUS_OK
/* clear_local_changes */,
227 base::Bind(&base::DoNothing
));
231 void PrepareForSync_Basic(LocalFileSyncContext::SyncMode sync_mode
,
232 SyncStatusCode simulate_sync_finish_status
) {
233 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
234 in_memory_env_
.get(),
235 io_task_runner_
.get(),
236 file_task_runner_
.get());
237 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
238 sync_context_
= new LocalFileSyncContext(
239 dir_
.path(), in_memory_env_
.get(),
240 ui_task_runner_
.get(), io_task_runner_
.get());
241 ASSERT_EQ(SYNC_STATUS_OK
,
242 file_system
.MaybeInitializeFileSystemContext(
243 sync_context_
.get()));
244 ASSERT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
246 const FileSystemURL
kFile(file_system
.URL("file"));
247 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kFile
));
249 SyncFileMetadata metadata
;
250 FileChangeList changes
;
251 EXPECT_EQ(SYNC_STATUS_OK
,
252 PrepareForSync(file_system
.file_system_context(), kFile
,
253 sync_mode
, &metadata
, &changes
, NULL
));
254 EXPECT_EQ(1U, changes
.size());
255 EXPECT_TRUE(changes
.list().back().IsFile());
256 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
258 // We should see the same set of changes.
259 file_system
.GetChangesForURLInTracker(kFile
, &changes
);
260 EXPECT_EQ(1U, changes
.size());
261 EXPECT_TRUE(changes
.list().back().IsFile());
262 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
264 SimulateFinishSync(file_system
.file_system_context(), kFile
,
265 simulate_sync_finish_status
, sync_mode
);
267 file_system
.GetChangesForURLInTracker(kFile
, &changes
);
268 if (simulate_sync_finish_status
== SYNC_STATUS_OK
) {
269 // The change's cleared.
270 EXPECT_TRUE(changes
.empty());
272 EXPECT_EQ(1U, changes
.size());
273 EXPECT_TRUE(changes
.list().back().IsFile());
274 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
277 sync_context_
->ShutdownOnUIThread();
278 sync_context_
= NULL
;
280 file_system
.TearDown();
283 void PrepareForSync_WriteDuringSync(
284 LocalFileSyncContext::SyncMode sync_mode
) {
285 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
286 in_memory_env_
.get(),
287 io_task_runner_
.get(),
288 file_task_runner_
.get());
289 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
290 sync_context_
= new LocalFileSyncContext(
291 dir_
.path(), in_memory_env_
.get(),
292 ui_task_runner_
.get(), io_task_runner_
.get());
293 ASSERT_EQ(SYNC_STATUS_OK
,
294 file_system
.MaybeInitializeFileSystemContext(
295 sync_context_
.get()));
296 ASSERT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
298 const FileSystemURL
kFile(file_system
.URL("file"));
299 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kFile
));
301 SyncFileMetadata metadata
;
302 FileChangeList changes
;
303 webkit_blob::ScopedFile snapshot
;
304 EXPECT_EQ(SYNC_STATUS_OK
,
305 PrepareForSync(file_system
.file_system_context(), kFile
,
306 sync_mode
, &metadata
, &changes
, &snapshot
));
307 EXPECT_EQ(1U, changes
.size());
308 EXPECT_TRUE(changes
.list().back().IsFile());
309 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
311 EXPECT_EQ(sync_mode
== LocalFileSyncContext::SYNC_SNAPSHOT
,
312 !snapshot
.path().empty());
314 // Tracker keeps same set of changes.
315 file_system
.GetChangesForURLInTracker(kFile
, &changes
);
316 EXPECT_EQ(1U, changes
.size());
317 EXPECT_TRUE(changes
.list().back().IsFile());
318 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
320 StartModifyFileOnIOThread(&file_system
, kFile
);
322 if (sync_mode
== LocalFileSyncContext::SYNC_SNAPSHOT
) {
323 // Write should succeed.
324 EXPECT_EQ(base::File::FILE_OK
, WaitUntilModifyFileIsDone());
326 base::MessageLoop::current()->RunUntilIdle();
327 EXPECT_FALSE(async_modify_finished_
);
330 SimulateFinishSync(file_system
.file_system_context(), kFile
,
331 SYNC_STATUS_OK
, sync_mode
);
333 EXPECT_EQ(base::File::FILE_OK
, WaitUntilModifyFileIsDone());
335 // Sync succeeded, but the other change that was made during or
336 // after sync is recorded.
337 file_system
.GetChangesForURLInTracker(kFile
, &changes
);
338 EXPECT_EQ(1U, changes
.size());
339 EXPECT_TRUE(changes
.list().back().IsFile());
340 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
342 sync_context_
->ShutdownOnUIThread();
343 sync_context_
= NULL
;
345 file_system
.TearDown();
348 ScopedEnableSyncFSDirectoryOperation enable_directory_operation_
;
350 base::ScopedTempDir dir_
;
351 scoped_ptr
<leveldb::Env
> in_memory_env_
;
353 // These need to remain until the very end.
354 content::TestBrowserThreadBundle thread_bundle_
;
356 scoped_refptr
<base::SingleThreadTaskRunner
> io_task_runner_
;
357 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner_
;
358 scoped_refptr
<base::SingleThreadTaskRunner
> file_task_runner_
;
360 scoped_refptr
<LocalFileSyncContext
> sync_context_
;
362 SyncStatusCode status_
;
363 base::File::Error file_error_
;
364 bool async_modify_finished_
;
365 bool has_inflight_prepare_for_sync_
;
368 TEST_F(LocalFileSyncContextTest
, ConstructAndDestruct
) {
370 new LocalFileSyncContext(
371 dir_
.path(), in_memory_env_
.get(),
372 ui_task_runner_
.get(), io_task_runner_
.get());
373 sync_context_
->ShutdownOnUIThread();
376 TEST_F(LocalFileSyncContextTest
, InitializeFileSystemContext
) {
377 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
378 in_memory_env_
.get(),
379 io_task_runner_
.get(),
380 file_task_runner_
.get());
381 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
383 sync_context_
= new LocalFileSyncContext(
384 dir_
.path(), in_memory_env_
.get(),
385 ui_task_runner_
.get(), io_task_runner_
.get());
387 // Initializes file_system using |sync_context_|.
388 EXPECT_EQ(SYNC_STATUS_OK
,
389 file_system
.MaybeInitializeFileSystemContext(sync_context_
.get()));
391 // Make sure everything's set up for file_system to be able to handle
392 // syncable file system operations.
393 EXPECT_TRUE(file_system
.backend()->sync_context() != NULL
);
394 EXPECT_TRUE(file_system
.backend()->change_tracker() != NULL
);
395 EXPECT_EQ(sync_context_
.get(), file_system
.backend()->sync_context());
397 // Calling MaybeInitialize for the same context multiple times must be ok.
398 EXPECT_EQ(SYNC_STATUS_OK
,
399 file_system
.MaybeInitializeFileSystemContext(sync_context_
.get()));
400 EXPECT_EQ(sync_context_
.get(), file_system
.backend()->sync_context());
402 // Opens the file_system, perform some operation and see if the change tracker
403 // correctly captures the change.
404 EXPECT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
406 const FileSystemURL
kURL(file_system
.URL("foo"));
407 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kURL
));
409 FileSystemURLSet urls
;
410 file_system
.GetChangedURLsInTracker(&urls
);
411 ASSERT_EQ(1U, urls
.size());
412 EXPECT_TRUE(ContainsKey(urls
, kURL
));
414 // Finishing the test.
415 sync_context_
->ShutdownOnUIThread();
416 file_system
.TearDown();
419 TEST_F(LocalFileSyncContextTest
, MultipleFileSystemContexts
) {
420 CannedSyncableFileSystem
file_system1(GURL(kOrigin1
),
421 in_memory_env_
.get(),
422 io_task_runner_
.get(),
423 file_task_runner_
.get());
424 CannedSyncableFileSystem
file_system2(GURL(kOrigin2
),
425 in_memory_env_
.get(),
426 io_task_runner_
.get(),
427 file_task_runner_
.get());
428 file_system1
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
429 file_system2
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
431 sync_context_
= new LocalFileSyncContext(
432 dir_
.path(), in_memory_env_
.get(),
433 ui_task_runner_
.get(), io_task_runner_
.get());
435 // Initializes file_system1 and file_system2.
436 EXPECT_EQ(SYNC_STATUS_OK
,
437 file_system1
.MaybeInitializeFileSystemContext(sync_context_
.get()));
438 EXPECT_EQ(SYNC_STATUS_OK
,
439 file_system2
.MaybeInitializeFileSystemContext(sync_context_
.get()));
441 EXPECT_EQ(base::File::FILE_OK
, file_system1
.OpenFileSystem());
442 EXPECT_EQ(base::File::FILE_OK
, file_system2
.OpenFileSystem());
444 const FileSystemURL
kURL1(file_system1
.URL("foo"));
445 const FileSystemURL
kURL2(file_system2
.URL("bar"));
447 // Creates a file in file_system1.
448 EXPECT_EQ(base::File::FILE_OK
, file_system1
.CreateFile(kURL1
));
450 // file_system1's tracker must have recorded the change.
451 FileSystemURLSet urls
;
452 file_system1
.GetChangedURLsInTracker(&urls
);
453 ASSERT_EQ(1U, urls
.size());
454 EXPECT_TRUE(ContainsKey(urls
, kURL1
));
456 // file_system1's tracker must have no change.
458 file_system2
.GetChangedURLsInTracker(&urls
);
459 ASSERT_TRUE(urls
.empty());
461 // Creates a directory in file_system2.
462 EXPECT_EQ(base::File::FILE_OK
, file_system2
.CreateDirectory(kURL2
));
464 // file_system1's tracker must have the change for kURL1 as before.
466 file_system1
.GetChangedURLsInTracker(&urls
);
467 ASSERT_EQ(1U, urls
.size());
468 EXPECT_TRUE(ContainsKey(urls
, kURL1
));
470 // file_system2's tracker now must have the change for kURL2.
472 file_system2
.GetChangedURLsInTracker(&urls
);
473 ASSERT_EQ(1U, urls
.size());
474 EXPECT_TRUE(ContainsKey(urls
, kURL2
));
476 SyncFileMetadata metadata
;
477 FileChangeList changes
;
478 EXPECT_EQ(SYNC_STATUS_OK
,
479 PrepareForSync(file_system1
.file_system_context(), kURL1
,
480 LocalFileSyncContext::SYNC_EXCLUSIVE
,
481 &metadata
, &changes
, NULL
));
482 EXPECT_EQ(1U, changes
.size());
483 EXPECT_TRUE(changes
.list().back().IsFile());
484 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
485 EXPECT_EQ(SYNC_FILE_TYPE_FILE
, metadata
.file_type
);
486 EXPECT_EQ(0, metadata
.size
);
489 EXPECT_EQ(SYNC_STATUS_OK
,
490 PrepareForSync(file_system2
.file_system_context(), kURL2
,
491 LocalFileSyncContext::SYNC_EXCLUSIVE
,
492 &metadata
, &changes
, NULL
));
493 EXPECT_EQ(1U, changes
.size());
494 EXPECT_FALSE(changes
.list().back().IsFile());
495 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
496 EXPECT_EQ(SYNC_FILE_TYPE_DIRECTORY
, metadata
.file_type
);
497 EXPECT_EQ(0, metadata
.size
);
499 sync_context_
->ShutdownOnUIThread();
500 sync_context_
= NULL
;
502 file_system1
.TearDown();
503 file_system2
.TearDown();
506 TEST_F(LocalFileSyncContextTest
, PrepareSync_SyncSuccess_Exclusive
) {
507 PrepareForSync_Basic(LocalFileSyncContext::SYNC_EXCLUSIVE
,
511 TEST_F(LocalFileSyncContextTest
, PrepareSync_SyncSuccess_Snapshot
) {
512 PrepareForSync_Basic(LocalFileSyncContext::SYNC_SNAPSHOT
,
516 TEST_F(LocalFileSyncContextTest
, PrepareSync_SyncFailure_Exclusive
) {
517 PrepareForSync_Basic(LocalFileSyncContext::SYNC_EXCLUSIVE
,
521 TEST_F(LocalFileSyncContextTest
, PrepareSync_SyncFailure_Snapshot
) {
522 PrepareForSync_Basic(LocalFileSyncContext::SYNC_SNAPSHOT
,
526 TEST_F(LocalFileSyncContextTest
, PrepareSync_WriteDuringSync_Exclusive
) {
527 PrepareForSync_WriteDuringSync(LocalFileSyncContext::SYNC_EXCLUSIVE
);
530 TEST_F(LocalFileSyncContextTest
, PrepareSync_WriteDuringSync_Snapshot
) {
531 PrepareForSync_WriteDuringSync(LocalFileSyncContext::SYNC_SNAPSHOT
);
534 // LocalFileSyncContextTest.PrepareSyncWhileWriting is flaky on android.
535 // http://crbug.com/239793
536 // It is also flaky on the TSAN v2 bots, and hangs other bots.
537 // http://crbug.com/305905.
538 TEST_F(LocalFileSyncContextTest
, DISABLED_PrepareSyncWhileWriting
) {
539 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
540 in_memory_env_
.get(),
541 io_task_runner_
.get(),
542 file_task_runner_
.get());
543 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
544 sync_context_
= new LocalFileSyncContext(
545 dir_
.path(), in_memory_env_
.get(),
546 ui_task_runner_
.get(), io_task_runner_
.get());
547 EXPECT_EQ(SYNC_STATUS_OK
,
548 file_system
.MaybeInitializeFileSystemContext(sync_context_
.get()));
550 EXPECT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
552 const FileSystemURL
kURL1(file_system
.URL("foo"));
554 // Creates a file in file_system.
555 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kURL1
));
557 // Kick file write on IO thread.
558 StartModifyFileOnIOThread(&file_system
, kURL1
);
560 // Until the operation finishes PrepareForSync should return BUSY error.
561 SyncFileMetadata metadata
;
562 metadata
.file_type
= SYNC_FILE_TYPE_UNKNOWN
;
563 FileChangeList changes
;
564 EXPECT_EQ(SYNC_STATUS_FILE_BUSY
,
565 PrepareForSync(file_system
.file_system_context(), kURL1
,
566 LocalFileSyncContext::SYNC_EXCLUSIVE
,
567 &metadata
, &changes
, NULL
));
568 EXPECT_EQ(SYNC_FILE_TYPE_FILE
, metadata
.file_type
);
570 // Register PrepareForSync method to be invoked when kURL1 becomes
571 // syncable. (Actually this may be done after all operations are done
572 // on IO thread in this test.)
573 metadata
.file_type
= SYNC_FILE_TYPE_UNKNOWN
;
575 sync_context_
->RegisterURLForWaitingSync(
576 kURL1
, GetPrepareForSyncClosure(file_system
.file_system_context(), kURL1
,
577 LocalFileSyncContext::SYNC_EXCLUSIVE
,
578 &metadata
, &changes
, NULL
));
580 // Wait for the completion.
581 EXPECT_EQ(base::File::FILE_OK
, WaitUntilModifyFileIsDone());
583 // The PrepareForSync must have been started; wait until DidPrepareForSync
585 base::MessageLoop::current()->Run();
586 ASSERT_FALSE(has_inflight_prepare_for_sync_
);
588 // Now PrepareForSync should have run and returned OK.
589 EXPECT_EQ(SYNC_STATUS_OK
, status_
);
590 EXPECT_EQ(1U, changes
.size());
591 EXPECT_TRUE(changes
.list().back().IsFile());
592 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
593 EXPECT_EQ(SYNC_FILE_TYPE_FILE
, metadata
.file_type
);
594 EXPECT_EQ(1, metadata
.size
);
596 sync_context_
->ShutdownOnUIThread();
597 sync_context_
= NULL
;
598 file_system
.TearDown();
601 TEST_F(LocalFileSyncContextTest
, ApplyRemoteChangeForDeletion
) {
602 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
603 in_memory_env_
.get(),
604 io_task_runner_
.get(),
605 file_task_runner_
.get());
606 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
608 sync_context_
= new LocalFileSyncContext(
609 dir_
.path(), in_memory_env_
.get(),
610 ui_task_runner_
.get(), io_task_runner_
.get());
611 ASSERT_EQ(SYNC_STATUS_OK
,
612 file_system
.MaybeInitializeFileSystemContext(sync_context_
.get()));
613 ASSERT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
615 // Record the initial usage (likely 0).
616 int64 initial_usage
= -1;
618 EXPECT_EQ(quota::kQuotaStatusOk
,
619 file_system
.GetUsageAndQuota(&initial_usage
, "a
));
621 // Create a file and directory in the file_system.
622 const FileSystemURL
kFile(file_system
.URL("file"));
623 const FileSystemURL
kDir(file_system
.URL("dir"));
624 const FileSystemURL
kChild(file_system
.URL("dir/child"));
626 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kFile
));
627 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateDirectory(kDir
));
628 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kChild
));
630 // file_system's change tracker must have recorded the creation.
631 FileSystemURLSet urls
;
632 file_system
.GetChangedURLsInTracker(&urls
);
633 ASSERT_EQ(3U, urls
.size());
634 ASSERT_TRUE(ContainsKey(urls
, kFile
));
635 ASSERT_TRUE(ContainsKey(urls
, kDir
));
636 ASSERT_TRUE(ContainsKey(urls
, kChild
));
637 for (FileSystemURLSet::iterator iter
= urls
.begin();
638 iter
!= urls
.end(); ++iter
) {
639 file_system
.ClearChangeForURLInTracker(*iter
);
642 // At this point the usage must be greater than the initial usage.
643 int64 new_usage
= -1;
644 EXPECT_EQ(quota::kQuotaStatusOk
,
645 file_system
.GetUsageAndQuota(&new_usage
, "a
));
646 EXPECT_GT(new_usage
, initial_usage
);
648 // Now let's apply remote deletion changes.
649 FileChange
change(FileChange::FILE_CHANGE_DELETE
,
650 SYNC_FILE_TYPE_FILE
);
651 EXPECT_EQ(SYNC_STATUS_OK
,
652 ApplyRemoteChange(file_system
.file_system_context(),
653 change
, base::FilePath(), kFile
,
654 SYNC_FILE_TYPE_FILE
));
656 // The implementation doesn't check file type for deletion, and it must be ok
657 // even if we don't know if the deletion change was for a file or a directory.
658 change
= FileChange(FileChange::FILE_CHANGE_DELETE
,
659 SYNC_FILE_TYPE_UNKNOWN
);
660 EXPECT_EQ(SYNC_STATUS_OK
,
661 ApplyRemoteChange(file_system
.file_system_context(),
662 change
, base::FilePath(), kDir
,
663 SYNC_FILE_TYPE_DIRECTORY
));
665 // Check the directory/files are deleted successfully.
666 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
667 file_system
.FileExists(kFile
));
668 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
669 file_system
.DirectoryExists(kDir
));
670 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
671 file_system
.FileExists(kChild
));
673 // The changes applied by ApplyRemoteChange should not be recorded in
674 // the change tracker.
676 file_system
.GetChangedURLsInTracker(&urls
);
677 EXPECT_TRUE(urls
.empty());
679 // The quota usage data must have reflected the deletion.
680 EXPECT_EQ(quota::kQuotaStatusOk
,
681 file_system
.GetUsageAndQuota(&new_usage
, "a
));
682 EXPECT_EQ(new_usage
, initial_usage
);
684 sync_context_
->ShutdownOnUIThread();
685 sync_context_
= NULL
;
686 file_system
.TearDown();
689 TEST_F(LocalFileSyncContextTest
, ApplyRemoteChangeForDeletion_ForRoot
) {
690 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
691 in_memory_env_
.get(),
692 io_task_runner_
.get(),
693 file_task_runner_
.get());
694 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
696 sync_context_
= new LocalFileSyncContext(
697 dir_
.path(), in_memory_env_
.get(),
698 ui_task_runner_
.get(), io_task_runner_
.get());
699 ASSERT_EQ(SYNC_STATUS_OK
,
700 file_system
.MaybeInitializeFileSystemContext(sync_context_
.get()));
701 ASSERT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
703 // Record the initial usage (likely 0).
704 int64 initial_usage
= -1;
706 EXPECT_EQ(quota::kQuotaStatusOk
,
707 file_system
.GetUsageAndQuota(&initial_usage
, "a
));
709 // Create a file and directory in the file_system.
710 const FileSystemURL
kFile(file_system
.URL("file"));
711 const FileSystemURL
kDir(file_system
.URL("dir"));
712 const FileSystemURL
kChild(file_system
.URL("dir/child"));
714 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kFile
));
715 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateDirectory(kDir
));
716 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kChild
));
718 // At this point the usage must be greater than the initial usage.
719 int64 new_usage
= -1;
720 EXPECT_EQ(quota::kQuotaStatusOk
,
721 file_system
.GetUsageAndQuota(&new_usage
, "a
));
722 EXPECT_GT(new_usage
, initial_usage
);
724 const FileSystemURL
kRoot(file_system
.URL(""));
726 // Now let's apply remote deletion changes for the root.
727 FileChange
change(FileChange::FILE_CHANGE_DELETE
, SYNC_FILE_TYPE_DIRECTORY
);
728 EXPECT_EQ(SYNC_STATUS_OK
,
729 ApplyRemoteChange(file_system
.file_system_context(),
730 change
, base::FilePath(), kRoot
,
731 SYNC_FILE_TYPE_DIRECTORY
));
733 // Check the directory/files are deleted successfully.
734 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
735 file_system
.FileExists(kFile
));
736 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
737 file_system
.DirectoryExists(kDir
));
738 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
739 file_system
.FileExists(kChild
));
741 // All changes made for the previous creation must have been also reset.
742 FileSystemURLSet urls
;
743 file_system
.GetChangedURLsInTracker(&urls
);
744 EXPECT_TRUE(urls
.empty());
746 // The quota usage data must have reflected the deletion.
747 EXPECT_EQ(quota::kQuotaStatusOk
,
748 file_system
.GetUsageAndQuota(&new_usage
, "a
));
749 EXPECT_EQ(new_usage
, initial_usage
);
751 sync_context_
->ShutdownOnUIThread();
752 sync_context_
= NULL
;
753 file_system
.TearDown();
756 TEST_F(LocalFileSyncContextTest
, ApplyRemoteChangeForAddOrUpdate
) {
757 base::ScopedTempDir temp_dir
;
758 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
760 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
761 in_memory_env_
.get(),
762 io_task_runner_
.get(),
763 file_task_runner_
.get());
764 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
766 sync_context_
= new LocalFileSyncContext(
767 dir_
.path(), in_memory_env_
.get(),
768 ui_task_runner_
.get(), io_task_runner_
.get());
769 ASSERT_EQ(SYNC_STATUS_OK
,
770 file_system
.MaybeInitializeFileSystemContext(sync_context_
.get()));
771 ASSERT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
773 const FileSystemURL
kFile1(file_system
.URL("file1"));
774 const FileSystemURL
kFile2(file_system
.URL("file2"));
775 const FileSystemURL
kDir(file_system
.URL("dir"));
777 const char kTestFileData0
[] = "0123456789";
778 const char kTestFileData1
[] = "Lorem ipsum!";
779 const char kTestFileData2
[] = "This is sample test data.";
781 // Create kFile1 and populate it with kTestFileData0.
782 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kFile1
));
783 EXPECT_EQ(static_cast<int64
>(arraysize(kTestFileData0
) - 1),
784 file_system
.WriteString(kFile1
, kTestFileData0
));
786 // kFile2 and kDir are not there yet.
787 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
788 file_system
.FileExists(kFile2
));
789 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
790 file_system
.DirectoryExists(kDir
));
792 // file_system's change tracker must have recorded the creation.
793 FileSystemURLSet urls
;
794 file_system
.GetChangedURLsInTracker(&urls
);
795 ASSERT_EQ(1U, urls
.size());
796 EXPECT_TRUE(ContainsKey(urls
, kFile1
));
797 file_system
.ClearChangeForURLInTracker(*urls
.begin());
799 // Prepare temporary files which represent the remote file data.
800 const base::FilePath
kFilePath1(temp_dir
.path().Append(FPL("file1")));
801 const base::FilePath
kFilePath2(temp_dir
.path().Append(FPL("file2")));
803 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1
) - 1),
804 base::WriteFile(kFilePath1
, kTestFileData1
,
805 arraysize(kTestFileData1
) - 1));
806 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2
) - 1),
807 base::WriteFile(kFilePath2
, kTestFileData2
,
808 arraysize(kTestFileData2
) - 1));
811 int64 usage
= -1, new_usage
= -1;
813 EXPECT_EQ(quota::kQuotaStatusOk
,
814 file_system
.GetUsageAndQuota(&usage
, "a
));
816 // Here in the local filesystem we have:
817 // * kFile1 with kTestFileData0
819 // In the remote side let's assume we have:
820 // * kFile1 with kTestFileData1
821 // * kFile2 with kTestFileData2
824 // By calling ApplyChange's:
825 // * kFile1 will be updated to have kTestFileData1
826 // * kFile2 will be created
827 // * kDir will be created
829 // Apply the remote change to kFile1 (which will update the file).
830 FileChange
change(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
831 SYNC_FILE_TYPE_FILE
);
832 EXPECT_EQ(SYNC_STATUS_OK
,
833 ApplyRemoteChange(file_system
.file_system_context(),
834 change
, kFilePath1
, kFile1
,
835 SYNC_FILE_TYPE_FILE
));
837 // Check if the usage has been increased by (kTestFileData1 - kTestFileData0).
838 const int updated_size
=
839 arraysize(kTestFileData1
) - arraysize(kTestFileData0
);
840 EXPECT_EQ(quota::kQuotaStatusOk
,
841 file_system
.GetUsageAndQuota(&new_usage
, "a
));
842 EXPECT_EQ(updated_size
, new_usage
- usage
);
844 // Apply remote changes to kFile2 and kDir (should create a file and
845 // directory respectively).
846 // They are non-existent yet so their expected file type (the last
847 // parameter of ApplyRemoteChange) are
848 // SYNC_FILE_TYPE_UNKNOWN.
849 change
= FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
850 SYNC_FILE_TYPE_FILE
);
851 EXPECT_EQ(SYNC_STATUS_OK
,
852 ApplyRemoteChange(file_system
.file_system_context(),
853 change
, kFilePath2
, kFile2
,
854 SYNC_FILE_TYPE_UNKNOWN
));
856 change
= FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
857 SYNC_FILE_TYPE_DIRECTORY
);
858 EXPECT_EQ(SYNC_STATUS_OK
,
859 ApplyRemoteChange(file_system
.file_system_context(),
860 change
, base::FilePath(), kDir
,
861 SYNC_FILE_TYPE_UNKNOWN
));
863 // Calling ApplyRemoteChange with different file type should be handled as
866 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
, SYNC_FILE_TYPE_FILE
);
867 EXPECT_EQ(SYNC_STATUS_OK
,
868 ApplyRemoteChange(file_system
.file_system_context(),
872 SYNC_FILE_TYPE_DIRECTORY
));
873 EXPECT_EQ(base::File::FILE_OK
, file_system
.FileExists(kDir
));
875 change
= FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
876 SYNC_FILE_TYPE_DIRECTORY
);
877 EXPECT_EQ(SYNC_STATUS_OK
,
878 ApplyRemoteChange(file_system
.file_system_context(),
882 SYNC_FILE_TYPE_FILE
));
884 // Creating a file/directory must have increased the usage more than
885 // the size of kTestFileData2.
887 EXPECT_EQ(quota::kQuotaStatusOk
,
888 file_system
.GetUsageAndQuota(&new_usage
, "a
));
890 static_cast<int64
>(usage
+ arraysize(kTestFileData2
) - 1));
892 // The changes applied by ApplyRemoteChange should not be recorded in
893 // the change tracker.
895 file_system
.GetChangedURLsInTracker(&urls
);
896 EXPECT_TRUE(urls
.empty());
898 // Make sure all three files/directory exist.
899 EXPECT_EQ(base::File::FILE_OK
, file_system
.FileExists(kFile1
));
900 EXPECT_EQ(base::File::FILE_OK
, file_system
.FileExists(kFile2
));
901 EXPECT_EQ(base::File::FILE_OK
, file_system
.DirectoryExists(kDir
));
903 sync_context_
->ShutdownOnUIThread();
904 file_system
.TearDown();
907 TEST_F(LocalFileSyncContextTest
, ApplyRemoteChangeForAddOrUpdate_NoParent
) {
908 base::ScopedTempDir temp_dir
;
909 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
911 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
912 in_memory_env_
.get(),
913 io_task_runner_
.get(),
914 file_task_runner_
.get());
915 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
917 sync_context_
= new LocalFileSyncContext(
918 dir_
.path(), in_memory_env_
.get(),
919 ui_task_runner_
.get(), io_task_runner_
.get());
920 ASSERT_EQ(SYNC_STATUS_OK
,
921 file_system
.MaybeInitializeFileSystemContext(sync_context_
.get()));
922 ASSERT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
924 const char kTestFileData
[] = "Lorem ipsum!";
925 const FileSystemURL
kDir(file_system
.URL("dir"));
926 const FileSystemURL
kFile(file_system
.URL("dir/file"));
928 // Either kDir or kFile not exist yet.
929 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
, file_system
.FileExists(kDir
));
930 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
, file_system
.FileExists(kFile
));
932 // Prepare a temporary file which represents remote file data.
933 const base::FilePath
kFilePath(temp_dir
.path().Append(FPL("file")));
934 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData
) - 1),
935 base::WriteFile(kFilePath
, kTestFileData
,
936 arraysize(kTestFileData
) - 1));
938 // Calling ApplyChange's with kFilePath should create
939 // kFile along with kDir.
940 FileChange
change(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
941 SYNC_FILE_TYPE_FILE
);
942 EXPECT_EQ(SYNC_STATUS_OK
,
943 ApplyRemoteChange(file_system
.file_system_context(),
944 change
, kFilePath
, kFile
,
945 SYNC_FILE_TYPE_UNKNOWN
));
947 // The changes applied by ApplyRemoteChange should not be recorded in
948 // the change tracker.
949 FileSystemURLSet urls
;
951 file_system
.GetChangedURLsInTracker(&urls
);
952 EXPECT_TRUE(urls
.empty());
954 // Make sure kDir and kFile are created by ApplyRemoteChange.
955 EXPECT_EQ(base::File::FILE_OK
, file_system
.FileExists(kFile
));
956 EXPECT_EQ(base::File::FILE_OK
, file_system
.DirectoryExists(kDir
));
958 sync_context_
->ShutdownOnUIThread();
959 file_system
.TearDown();
962 } // namespace sync_file_system