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/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/stl_util.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/sync_file_system_backend.h"
18 #include "chrome/browser/sync_file_system/sync_file_metadata.h"
19 #include "chrome/browser/sync_file_system/sync_status_code.h"
20 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/test/mock_blob_url_request_context.h"
23 #include "content/public/test/test_browser_thread_bundle.h"
24 #include "storage/browser/blob/scoped_file.h"
25 #include "storage/browser/fileapi/file_system_context.h"
26 #include "storage/browser/fileapi/file_system_operation_runner.h"
27 #include "storage/browser/fileapi/isolated_context.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
30 #include "third_party/leveldatabase/src/include/leveldb/env.h"
32 #define FPL FILE_PATH_LITERAL
34 using content::BrowserThread
;
35 using storage::FileSystemContext
;
36 using storage::FileSystemURL
;
37 using storage::FileSystemURLSet
;
39 // This tests LocalFileSyncContext behavior in multi-thread /
40 // multi-file-system-context environment.
41 // Basic combined tests (single-thread / single-file-system-context)
42 // that involve LocalFileSyncContext are also in
43 // syncable_file_system_unittests.cc.
45 namespace sync_file_system
{
48 const char kOrigin1
[] = "http://example.com";
49 const char kOrigin2
[] = "http://chromium.org";
52 class LocalFileSyncContextTest
: public testing::Test
{
54 LocalFileSyncContextTest()
56 content::TestBrowserThreadBundle::REAL_FILE_THREAD
|
57 content::TestBrowserThreadBundle::REAL_IO_THREAD
),
58 status_(SYNC_FILE_ERROR_FAILED
),
59 file_error_(base::File::FILE_ERROR_FAILED
),
60 async_modify_finished_(false),
61 has_inflight_prepare_for_sync_(false) {}
63 void SetUp() override
{
64 RegisterSyncableFileSystem();
65 ASSERT_TRUE(dir_
.CreateUniqueTempDir());
66 in_memory_env_
.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
68 ui_task_runner_
= base::MessageLoop::current()->message_loop_proxy();
69 io_task_runner_
= BrowserThread::GetMessageLoopProxyForThread(
71 file_task_runner_
= BrowserThread::GetMessageLoopProxyForThread(
75 void TearDown() override
{ RevokeSyncableFileSystem(); }
77 void StartPrepareForSync(FileSystemContext
* file_system_context
,
78 const FileSystemURL
& url
,
79 LocalFileSyncContext::SyncMode sync_mode
,
80 SyncFileMetadata
* metadata
,
81 FileChangeList
* changes
,
82 storage::ScopedFile
* snapshot
) {
83 ASSERT_TRUE(changes
!= nullptr);
84 ASSERT_FALSE(has_inflight_prepare_for_sync_
);
85 status_
= SYNC_STATUS_UNKNOWN
;
86 has_inflight_prepare_for_sync_
= true;
87 sync_context_
->PrepareForSync(
91 base::Bind(&LocalFileSyncContextTest::DidPrepareForSync
,
92 base::Unretained(this), metadata
, changes
, snapshot
));
95 SyncStatusCode
PrepareForSync(FileSystemContext
* file_system_context
,
96 const FileSystemURL
& url
,
97 LocalFileSyncContext::SyncMode sync_mode
,
98 SyncFileMetadata
* metadata
,
99 FileChangeList
* changes
,
100 storage::ScopedFile
* snapshot
) {
101 StartPrepareForSync(file_system_context
, url
, sync_mode
,
102 metadata
, changes
, snapshot
);
103 base::MessageLoop::current()->Run();
107 base::Closure
GetPrepareForSyncClosure(
108 FileSystemContext
* file_system_context
,
109 const FileSystemURL
& url
,
110 LocalFileSyncContext::SyncMode sync_mode
,
111 SyncFileMetadata
* metadata
,
112 FileChangeList
* changes
,
113 storage::ScopedFile
* snapshot
) {
114 return base::Bind(&LocalFileSyncContextTest::StartPrepareForSync
,
115 base::Unretained(this),
116 base::Unretained(file_system_context
),
117 url
, sync_mode
, metadata
, changes
, snapshot
);
120 void DidPrepareForSync(SyncFileMetadata
* metadata_out
,
121 FileChangeList
* changes_out
,
122 storage::ScopedFile
* snapshot_out
,
123 SyncStatusCode status
,
124 const LocalFileSyncInfo
& sync_file_info
,
125 storage::ScopedFile snapshot
) {
126 ASSERT_TRUE(ui_task_runner_
->RunsTasksOnCurrentThread());
127 has_inflight_prepare_for_sync_
= false;
129 *metadata_out
= sync_file_info
.metadata
;
130 *changes_out
= sync_file_info
.changes
;
132 *snapshot_out
= snapshot
.Pass();
133 base::MessageLoop::current()->Quit();
136 SyncStatusCode
ApplyRemoteChange(FileSystemContext
* file_system_context
,
137 const FileChange
& change
,
138 const base::FilePath
& local_path
,
139 const FileSystemURL
& url
,
140 SyncFileType expected_file_type
) {
141 SCOPED_TRACE(testing::Message() << "ApplyChange for " <<
144 // First we should call PrepareForSync to disable writing.
145 SyncFileMetadata metadata
;
146 FileChangeList changes
;
147 EXPECT_EQ(SYNC_STATUS_OK
,
148 PrepareForSync(file_system_context
, url
,
149 LocalFileSyncContext::SYNC_EXCLUSIVE
,
150 &metadata
, &changes
, nullptr));
151 EXPECT_EQ(expected_file_type
, metadata
.file_type
);
153 status_
= SYNC_STATUS_UNKNOWN
;
154 sync_context_
->ApplyRemoteChange(
155 file_system_context
, change
, local_path
, url
,
156 base::Bind(&LocalFileSyncContextTest::DidApplyRemoteChange
,
157 base::Unretained(this),
158 make_scoped_refptr(file_system_context
), url
));
159 base::MessageLoop::current()->Run();
163 void DidApplyRemoteChange(FileSystemContext
* file_system_context
,
164 const FileSystemURL
& url
,
165 SyncStatusCode status
) {
167 sync_context_
->FinalizeExclusiveSync(
168 file_system_context
, url
,
169 status
== SYNC_STATUS_OK
/* clear_local_changes */,
170 base::MessageLoop::QuitClosure());
173 void StartModifyFileOnIOThread(CannedSyncableFileSystem
* file_system
,
174 const FileSystemURL
& url
) {
175 ASSERT_TRUE(file_system
!= nullptr);
176 if (!io_task_runner_
->RunsTasksOnCurrentThread()) {
177 async_modify_finished_
= false;
178 ASSERT_TRUE(ui_task_runner_
->RunsTasksOnCurrentThread());
179 io_task_runner_
->PostTask(
181 base::Bind(&LocalFileSyncContextTest::StartModifyFileOnIOThread
,
182 base::Unretained(this), file_system
, url
));
185 ASSERT_TRUE(io_task_runner_
->RunsTasksOnCurrentThread());
186 file_error_
= base::File::FILE_ERROR_FAILED
;
187 file_system
->operation_runner()->Truncate(
188 url
, 1, base::Bind(&LocalFileSyncContextTest::DidModifyFile
,
189 base::Unretained(this)));
192 base::File::Error
WaitUntilModifyFileIsDone() {
193 while (!async_modify_finished_
)
194 base::MessageLoop::current()->RunUntilIdle();
198 void DidModifyFile(base::File::Error error
) {
199 if (!ui_task_runner_
->RunsTasksOnCurrentThread()) {
200 ASSERT_TRUE(io_task_runner_
->RunsTasksOnCurrentThread());
201 ui_task_runner_
->PostTask(
203 base::Bind(&LocalFileSyncContextTest::DidModifyFile
,
204 base::Unretained(this), error
));
207 ASSERT_TRUE(ui_task_runner_
->RunsTasksOnCurrentThread());
209 async_modify_finished_
= true;
212 void SimulateFinishSync(FileSystemContext
* file_system_context
,
213 const FileSystemURL
& url
,
214 SyncStatusCode status
,
215 LocalFileSyncContext::SyncMode sync_mode
) {
216 if (sync_mode
== LocalFileSyncContext::SYNC_SNAPSHOT
) {
217 sync_context_
->FinalizeSnapshotSync(
218 file_system_context
, url
, status
,
219 base::Bind(&base::DoNothing
));
221 sync_context_
->FinalizeExclusiveSync(
222 file_system_context
, url
,
223 status
== SYNC_STATUS_OK
/* clear_local_changes */,
224 base::Bind(&base::DoNothing
));
228 void PrepareForSync_Basic(LocalFileSyncContext::SyncMode sync_mode
,
229 SyncStatusCode simulate_sync_finish_status
) {
230 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
231 in_memory_env_
.get(),
232 io_task_runner_
.get(),
233 file_task_runner_
.get());
234 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
235 sync_context_
= new LocalFileSyncContext(
236 dir_
.path(), in_memory_env_
.get(),
237 ui_task_runner_
.get(), io_task_runner_
.get());
238 ASSERT_EQ(SYNC_STATUS_OK
,
239 file_system
.MaybeInitializeFileSystemContext(
240 sync_context_
.get()));
241 ASSERT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
243 const FileSystemURL
kFile(file_system
.URL("file"));
244 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kFile
));
246 SyncFileMetadata metadata
;
247 FileChangeList changes
;
248 EXPECT_EQ(SYNC_STATUS_OK
,
249 PrepareForSync(file_system
.file_system_context(), kFile
,
250 sync_mode
, &metadata
, &changes
, nullptr));
251 EXPECT_EQ(1U, changes
.size());
252 EXPECT_TRUE(changes
.list().back().IsFile());
253 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
255 // We should see the same set of changes.
256 file_system
.GetChangesForURLInTracker(kFile
, &changes
);
257 EXPECT_EQ(1U, changes
.size());
258 EXPECT_TRUE(changes
.list().back().IsFile());
259 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
261 SimulateFinishSync(file_system
.file_system_context(), kFile
,
262 simulate_sync_finish_status
, sync_mode
);
264 file_system
.GetChangesForURLInTracker(kFile
, &changes
);
265 if (simulate_sync_finish_status
== SYNC_STATUS_OK
) {
266 // The change's cleared.
267 EXPECT_TRUE(changes
.empty());
269 EXPECT_EQ(1U, changes
.size());
270 EXPECT_TRUE(changes
.list().back().IsFile());
271 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
274 sync_context_
->ShutdownOnUIThread();
275 sync_context_
= nullptr;
277 file_system
.TearDown();
280 void PrepareForSync_WriteDuringSync(
281 LocalFileSyncContext::SyncMode sync_mode
) {
282 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
283 in_memory_env_
.get(),
284 io_task_runner_
.get(),
285 file_task_runner_
.get());
286 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
287 sync_context_
= new LocalFileSyncContext(
288 dir_
.path(), in_memory_env_
.get(),
289 ui_task_runner_
.get(), io_task_runner_
.get());
290 ASSERT_EQ(SYNC_STATUS_OK
,
291 file_system
.MaybeInitializeFileSystemContext(
292 sync_context_
.get()));
293 ASSERT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
295 const FileSystemURL
kFile(file_system
.URL("file"));
296 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kFile
));
298 SyncFileMetadata metadata
;
299 FileChangeList changes
;
300 storage::ScopedFile snapshot
;
301 EXPECT_EQ(SYNC_STATUS_OK
,
302 PrepareForSync(file_system
.file_system_context(), kFile
,
303 sync_mode
, &metadata
, &changes
, &snapshot
));
304 EXPECT_EQ(1U, changes
.size());
305 EXPECT_TRUE(changes
.list().back().IsFile());
306 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
308 EXPECT_EQ(sync_mode
== LocalFileSyncContext::SYNC_SNAPSHOT
,
309 !snapshot
.path().empty());
311 // Tracker keeps same set of changes.
312 file_system
.GetChangesForURLInTracker(kFile
, &changes
);
313 EXPECT_EQ(1U, changes
.size());
314 EXPECT_TRUE(changes
.list().back().IsFile());
315 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
317 StartModifyFileOnIOThread(&file_system
, kFile
);
319 if (sync_mode
== LocalFileSyncContext::SYNC_SNAPSHOT
) {
320 // Write should succeed.
321 EXPECT_EQ(base::File::FILE_OK
, WaitUntilModifyFileIsDone());
323 base::MessageLoop::current()->RunUntilIdle();
324 EXPECT_FALSE(async_modify_finished_
);
327 SimulateFinishSync(file_system
.file_system_context(), kFile
,
328 SYNC_STATUS_OK
, sync_mode
);
330 EXPECT_EQ(base::File::FILE_OK
, WaitUntilModifyFileIsDone());
332 // Sync succeeded, but the other change that was made during or
333 // after sync is recorded.
334 file_system
.GetChangesForURLInTracker(kFile
, &changes
);
335 EXPECT_EQ(1U, changes
.size());
336 EXPECT_TRUE(changes
.list().back().IsFile());
337 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
339 sync_context_
->ShutdownOnUIThread();
340 sync_context_
= nullptr;
342 file_system
.TearDown();
345 base::ScopedTempDir dir_
;
346 scoped_ptr
<leveldb::Env
> in_memory_env_
;
348 // These need to remain until the very end.
349 content::TestBrowserThreadBundle thread_bundle_
;
351 scoped_refptr
<base::SingleThreadTaskRunner
> io_task_runner_
;
352 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner_
;
353 scoped_refptr
<base::SingleThreadTaskRunner
> file_task_runner_
;
355 scoped_refptr
<LocalFileSyncContext
> sync_context_
;
357 SyncStatusCode status_
;
358 base::File::Error file_error_
;
359 bool async_modify_finished_
;
360 bool has_inflight_prepare_for_sync_
;
363 TEST_F(LocalFileSyncContextTest
, ConstructAndDestruct
) {
365 new LocalFileSyncContext(
366 dir_
.path(), in_memory_env_
.get(),
367 ui_task_runner_
.get(), io_task_runner_
.get());
368 sync_context_
->ShutdownOnUIThread();
371 TEST_F(LocalFileSyncContextTest
, InitializeFileSystemContext
) {
372 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
373 in_memory_env_
.get(),
374 io_task_runner_
.get(),
375 file_task_runner_
.get());
376 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
378 sync_context_
= new LocalFileSyncContext(
379 dir_
.path(), in_memory_env_
.get(),
380 ui_task_runner_
.get(), io_task_runner_
.get());
382 // Initializes file_system using |sync_context_|.
383 EXPECT_EQ(SYNC_STATUS_OK
,
384 file_system
.MaybeInitializeFileSystemContext(sync_context_
.get()));
386 // Make sure everything's set up for file_system to be able to handle
387 // syncable file system operations.
388 EXPECT_TRUE(file_system
.backend()->sync_context() != nullptr);
389 EXPECT_TRUE(file_system
.backend()->change_tracker() != nullptr);
390 EXPECT_EQ(sync_context_
.get(), file_system
.backend()->sync_context());
392 // Calling MaybeInitialize for the same context multiple times must be ok.
393 EXPECT_EQ(SYNC_STATUS_OK
,
394 file_system
.MaybeInitializeFileSystemContext(sync_context_
.get()));
395 EXPECT_EQ(sync_context_
.get(), file_system
.backend()->sync_context());
397 // Opens the file_system, perform some operation and see if the change tracker
398 // correctly captures the change.
399 EXPECT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
401 const FileSystemURL
kURL(file_system
.URL("foo"));
402 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kURL
));
404 FileSystemURLSet urls
;
405 file_system
.GetChangedURLsInTracker(&urls
);
406 ASSERT_EQ(1U, urls
.size());
407 EXPECT_TRUE(ContainsKey(urls
, kURL
));
409 // Finishing the test.
410 sync_context_
->ShutdownOnUIThread();
411 file_system
.TearDown();
414 TEST_F(LocalFileSyncContextTest
, MultipleFileSystemContexts
) {
415 CannedSyncableFileSystem
file_system1(GURL(kOrigin1
),
416 in_memory_env_
.get(),
417 io_task_runner_
.get(),
418 file_task_runner_
.get());
419 CannedSyncableFileSystem
file_system2(GURL(kOrigin2
),
420 in_memory_env_
.get(),
421 io_task_runner_
.get(),
422 file_task_runner_
.get());
423 file_system1
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
424 file_system2
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
426 sync_context_
= new LocalFileSyncContext(
427 dir_
.path(), in_memory_env_
.get(),
428 ui_task_runner_
.get(), io_task_runner_
.get());
430 // Initializes file_system1 and file_system2.
431 EXPECT_EQ(SYNC_STATUS_OK
,
432 file_system1
.MaybeInitializeFileSystemContext(sync_context_
.get()));
433 EXPECT_EQ(SYNC_STATUS_OK
,
434 file_system2
.MaybeInitializeFileSystemContext(sync_context_
.get()));
436 EXPECT_EQ(base::File::FILE_OK
, file_system1
.OpenFileSystem());
437 EXPECT_EQ(base::File::FILE_OK
, file_system2
.OpenFileSystem());
439 const FileSystemURL
kURL1(file_system1
.URL("foo"));
440 const FileSystemURL
kURL2(file_system2
.URL("bar"));
442 // Creates a file in file_system1.
443 EXPECT_EQ(base::File::FILE_OK
, file_system1
.CreateFile(kURL1
));
445 // file_system1's tracker must have recorded the change.
446 FileSystemURLSet urls
;
447 file_system1
.GetChangedURLsInTracker(&urls
);
448 ASSERT_EQ(1U, urls
.size());
449 EXPECT_TRUE(ContainsKey(urls
, kURL1
));
451 // file_system1's tracker must have no change.
453 file_system2
.GetChangedURLsInTracker(&urls
);
454 ASSERT_TRUE(urls
.empty());
456 // Creates a directory in file_system2.
457 EXPECT_EQ(base::File::FILE_OK
, file_system2
.CreateDirectory(kURL2
));
459 // file_system1's tracker must have the change for kURL1 as before.
461 file_system1
.GetChangedURLsInTracker(&urls
);
462 ASSERT_EQ(1U, urls
.size());
463 EXPECT_TRUE(ContainsKey(urls
, kURL1
));
465 // file_system2's tracker now must have the change for kURL2.
467 file_system2
.GetChangedURLsInTracker(&urls
);
468 ASSERT_EQ(1U, urls
.size());
469 EXPECT_TRUE(ContainsKey(urls
, kURL2
));
471 SyncFileMetadata metadata
;
472 FileChangeList changes
;
473 EXPECT_EQ(SYNC_STATUS_OK
,
474 PrepareForSync(file_system1
.file_system_context(), kURL1
,
475 LocalFileSyncContext::SYNC_EXCLUSIVE
,
476 &metadata
, &changes
, nullptr));
477 EXPECT_EQ(1U, changes
.size());
478 EXPECT_TRUE(changes
.list().back().IsFile());
479 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
480 EXPECT_EQ(SYNC_FILE_TYPE_FILE
, metadata
.file_type
);
481 EXPECT_EQ(0, metadata
.size
);
484 EXPECT_EQ(SYNC_STATUS_OK
,
485 PrepareForSync(file_system2
.file_system_context(), kURL2
,
486 LocalFileSyncContext::SYNC_EXCLUSIVE
,
487 &metadata
, &changes
, nullptr));
488 EXPECT_EQ(1U, changes
.size());
489 EXPECT_FALSE(changes
.list().back().IsFile());
490 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
491 EXPECT_EQ(SYNC_FILE_TYPE_DIRECTORY
, metadata
.file_type
);
492 EXPECT_EQ(0, metadata
.size
);
494 sync_context_
->ShutdownOnUIThread();
495 sync_context_
= nullptr;
497 file_system1
.TearDown();
498 file_system2
.TearDown();
501 TEST_F(LocalFileSyncContextTest
, PrepareSync_SyncSuccess_Exclusive
) {
502 PrepareForSync_Basic(LocalFileSyncContext::SYNC_EXCLUSIVE
,
506 TEST_F(LocalFileSyncContextTest
, PrepareSync_SyncSuccess_Snapshot
) {
507 PrepareForSync_Basic(LocalFileSyncContext::SYNC_SNAPSHOT
,
511 TEST_F(LocalFileSyncContextTest
, PrepareSync_SyncFailure_Exclusive
) {
512 PrepareForSync_Basic(LocalFileSyncContext::SYNC_EXCLUSIVE
,
516 TEST_F(LocalFileSyncContextTest
, PrepareSync_SyncFailure_Snapshot
) {
517 PrepareForSync_Basic(LocalFileSyncContext::SYNC_SNAPSHOT
,
521 TEST_F(LocalFileSyncContextTest
, PrepareSync_WriteDuringSync_Exclusive
) {
522 PrepareForSync_WriteDuringSync(LocalFileSyncContext::SYNC_EXCLUSIVE
);
525 TEST_F(LocalFileSyncContextTest
, PrepareSync_WriteDuringSync_Snapshot
) {
526 PrepareForSync_WriteDuringSync(LocalFileSyncContext::SYNC_SNAPSHOT
);
529 // LocalFileSyncContextTest.PrepareSyncWhileWriting is flaky on android.
530 // http://crbug.com/239793
531 // It is also flaky on the TSAN v2 bots, and hangs other bots.
532 // http://crbug.com/305905.
533 TEST_F(LocalFileSyncContextTest
, DISABLED_PrepareSyncWhileWriting
) {
534 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
535 in_memory_env_
.get(),
536 io_task_runner_
.get(),
537 file_task_runner_
.get());
538 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
539 sync_context_
= new LocalFileSyncContext(
540 dir_
.path(), in_memory_env_
.get(),
541 ui_task_runner_
.get(), io_task_runner_
.get());
542 EXPECT_EQ(SYNC_STATUS_OK
,
543 file_system
.MaybeInitializeFileSystemContext(sync_context_
.get()));
545 EXPECT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
547 const FileSystemURL
kURL1(file_system
.URL("foo"));
549 // Creates a file in file_system.
550 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kURL1
));
552 // Kick file write on IO thread.
553 StartModifyFileOnIOThread(&file_system
, kURL1
);
555 // Until the operation finishes PrepareForSync should return BUSY error.
556 SyncFileMetadata metadata
;
557 metadata
.file_type
= SYNC_FILE_TYPE_UNKNOWN
;
558 FileChangeList changes
;
559 EXPECT_EQ(SYNC_STATUS_FILE_BUSY
,
560 PrepareForSync(file_system
.file_system_context(), kURL1
,
561 LocalFileSyncContext::SYNC_EXCLUSIVE
,
562 &metadata
, &changes
, nullptr));
563 EXPECT_EQ(SYNC_FILE_TYPE_FILE
, metadata
.file_type
);
565 // Register PrepareForSync method to be invoked when kURL1 becomes
566 // syncable. (Actually this may be done after all operations are done
567 // on IO thread in this test.)
568 metadata
.file_type
= SYNC_FILE_TYPE_UNKNOWN
;
570 sync_context_
->RegisterURLForWaitingSync(
571 kURL1
, GetPrepareForSyncClosure(file_system
.file_system_context(), kURL1
,
572 LocalFileSyncContext::SYNC_EXCLUSIVE
,
573 &metadata
, &changes
, nullptr));
575 // Wait for the completion.
576 EXPECT_EQ(base::File::FILE_OK
, WaitUntilModifyFileIsDone());
578 // The PrepareForSync must have been started; wait until DidPrepareForSync
580 base::MessageLoop::current()->Run();
581 ASSERT_FALSE(has_inflight_prepare_for_sync_
);
583 // Now PrepareForSync should have run and returned OK.
584 EXPECT_EQ(SYNC_STATUS_OK
, status_
);
585 EXPECT_EQ(1U, changes
.size());
586 EXPECT_TRUE(changes
.list().back().IsFile());
587 EXPECT_TRUE(changes
.list().back().IsAddOrUpdate());
588 EXPECT_EQ(SYNC_FILE_TYPE_FILE
, metadata
.file_type
);
589 EXPECT_EQ(1, metadata
.size
);
591 sync_context_
->ShutdownOnUIThread();
592 sync_context_
= nullptr;
593 file_system
.TearDown();
596 TEST_F(LocalFileSyncContextTest
, ApplyRemoteChangeForDeletion
) {
597 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
598 in_memory_env_
.get(),
599 io_task_runner_
.get(),
600 file_task_runner_
.get());
601 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
603 sync_context_
= new LocalFileSyncContext(
604 dir_
.path(), in_memory_env_
.get(),
605 ui_task_runner_
.get(), io_task_runner_
.get());
606 ASSERT_EQ(SYNC_STATUS_OK
,
607 file_system
.MaybeInitializeFileSystemContext(sync_context_
.get()));
608 ASSERT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
610 // Record the initial usage (likely 0).
611 int64 initial_usage
= -1;
613 EXPECT_EQ(storage::kQuotaStatusOk
,
614 file_system
.GetUsageAndQuota(&initial_usage
, "a
));
616 // Create a file and directory in the file_system.
617 const FileSystemURL
kFile(file_system
.URL("file"));
618 const FileSystemURL
kDir(file_system
.URL("dir"));
619 const FileSystemURL
kChild(file_system
.URL("dir/child"));
621 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kFile
));
622 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateDirectory(kDir
));
623 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kChild
));
625 // file_system's change tracker must have recorded the creation.
626 FileSystemURLSet urls
;
627 file_system
.GetChangedURLsInTracker(&urls
);
628 ASSERT_EQ(3U, urls
.size());
629 ASSERT_TRUE(ContainsKey(urls
, kFile
));
630 ASSERT_TRUE(ContainsKey(urls
, kDir
));
631 ASSERT_TRUE(ContainsKey(urls
, kChild
));
632 for (FileSystemURLSet::iterator iter
= urls
.begin();
633 iter
!= urls
.end(); ++iter
) {
634 file_system
.ClearChangeForURLInTracker(*iter
);
637 // At this point the usage must be greater than the initial usage.
638 int64 new_usage
= -1;
639 EXPECT_EQ(storage::kQuotaStatusOk
,
640 file_system
.GetUsageAndQuota(&new_usage
, "a
));
641 EXPECT_GT(new_usage
, initial_usage
);
643 // Now let's apply remote deletion changes.
644 FileChange
change(FileChange::FILE_CHANGE_DELETE
,
645 SYNC_FILE_TYPE_FILE
);
646 EXPECT_EQ(SYNC_STATUS_OK
,
647 ApplyRemoteChange(file_system
.file_system_context(),
648 change
, base::FilePath(), kFile
,
649 SYNC_FILE_TYPE_FILE
));
651 // The implementation doesn't check file type for deletion, and it must be ok
652 // even if we don't know if the deletion change was for a file or a directory.
653 change
= FileChange(FileChange::FILE_CHANGE_DELETE
,
654 SYNC_FILE_TYPE_UNKNOWN
);
655 EXPECT_EQ(SYNC_STATUS_OK
,
656 ApplyRemoteChange(file_system
.file_system_context(),
657 change
, base::FilePath(), kDir
,
658 SYNC_FILE_TYPE_DIRECTORY
));
660 // Check the directory/files are deleted successfully.
661 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
662 file_system
.FileExists(kFile
));
663 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
664 file_system
.DirectoryExists(kDir
));
665 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
666 file_system
.FileExists(kChild
));
668 // The changes applied by ApplyRemoteChange should not be recorded in
669 // the change tracker.
671 file_system
.GetChangedURLsInTracker(&urls
);
672 EXPECT_TRUE(urls
.empty());
674 // The quota usage data must have reflected the deletion.
675 EXPECT_EQ(storage::kQuotaStatusOk
,
676 file_system
.GetUsageAndQuota(&new_usage
, "a
));
677 EXPECT_EQ(new_usage
, initial_usage
);
679 sync_context_
->ShutdownOnUIThread();
680 sync_context_
= nullptr;
681 file_system
.TearDown();
684 TEST_F(LocalFileSyncContextTest
, ApplyRemoteChangeForDeletion_ForRoot
) {
685 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
686 in_memory_env_
.get(),
687 io_task_runner_
.get(),
688 file_task_runner_
.get());
689 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
691 sync_context_
= new LocalFileSyncContext(
692 dir_
.path(), in_memory_env_
.get(),
693 ui_task_runner_
.get(), io_task_runner_
.get());
694 ASSERT_EQ(SYNC_STATUS_OK
,
695 file_system
.MaybeInitializeFileSystemContext(sync_context_
.get()));
696 ASSERT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
698 // Record the initial usage (likely 0).
699 int64 initial_usage
= -1;
701 EXPECT_EQ(storage::kQuotaStatusOk
,
702 file_system
.GetUsageAndQuota(&initial_usage
, "a
));
704 // Create a file and directory in the file_system.
705 const FileSystemURL
kFile(file_system
.URL("file"));
706 const FileSystemURL
kDir(file_system
.URL("dir"));
707 const FileSystemURL
kChild(file_system
.URL("dir/child"));
709 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kFile
));
710 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateDirectory(kDir
));
711 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kChild
));
713 // At this point the usage must be greater than the initial usage.
714 int64 new_usage
= -1;
715 EXPECT_EQ(storage::kQuotaStatusOk
,
716 file_system
.GetUsageAndQuota(&new_usage
, "a
));
717 EXPECT_GT(new_usage
, initial_usage
);
719 const FileSystemURL
kRoot(file_system
.URL(""));
721 // Now let's apply remote deletion changes for the root.
722 FileChange
change(FileChange::FILE_CHANGE_DELETE
, SYNC_FILE_TYPE_DIRECTORY
);
723 EXPECT_EQ(SYNC_STATUS_OK
,
724 ApplyRemoteChange(file_system
.file_system_context(),
725 change
, base::FilePath(), kRoot
,
726 SYNC_FILE_TYPE_DIRECTORY
));
728 // Check the directory/files are deleted successfully.
729 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
730 file_system
.FileExists(kFile
));
731 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
732 file_system
.DirectoryExists(kDir
));
733 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
734 file_system
.FileExists(kChild
));
736 // All changes made for the previous creation must have been also reset.
737 FileSystemURLSet urls
;
738 file_system
.GetChangedURLsInTracker(&urls
);
739 EXPECT_TRUE(urls
.empty());
741 // The quota usage data must have reflected the deletion.
742 EXPECT_EQ(storage::kQuotaStatusOk
,
743 file_system
.GetUsageAndQuota(&new_usage
, "a
));
744 EXPECT_EQ(new_usage
, initial_usage
);
746 sync_context_
->ShutdownOnUIThread();
747 sync_context_
= nullptr;
748 file_system
.TearDown();
751 TEST_F(LocalFileSyncContextTest
, ApplyRemoteChangeForAddOrUpdate
) {
752 base::ScopedTempDir temp_dir
;
753 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
755 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
756 in_memory_env_
.get(),
757 io_task_runner_
.get(),
758 file_task_runner_
.get());
759 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
761 sync_context_
= new LocalFileSyncContext(
762 dir_
.path(), in_memory_env_
.get(),
763 ui_task_runner_
.get(), io_task_runner_
.get());
764 ASSERT_EQ(SYNC_STATUS_OK
,
765 file_system
.MaybeInitializeFileSystemContext(sync_context_
.get()));
766 ASSERT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
768 const FileSystemURL
kFile1(file_system
.URL("file1"));
769 const FileSystemURL
kFile2(file_system
.URL("file2"));
770 const FileSystemURL
kDir(file_system
.URL("dir"));
772 const char kTestFileData0
[] = "0123456789";
773 const char kTestFileData1
[] = "Lorem ipsum!";
774 const char kTestFileData2
[] = "This is sample test data.";
776 // Create kFile1 and populate it with kTestFileData0.
777 EXPECT_EQ(base::File::FILE_OK
, file_system
.CreateFile(kFile1
));
778 EXPECT_EQ(static_cast<int64
>(arraysize(kTestFileData0
) - 1),
779 file_system
.WriteString(kFile1
, kTestFileData0
));
781 // kFile2 and kDir are not there yet.
782 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
783 file_system
.FileExists(kFile2
));
784 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
785 file_system
.DirectoryExists(kDir
));
787 // file_system's change tracker must have recorded the creation.
788 FileSystemURLSet urls
;
789 file_system
.GetChangedURLsInTracker(&urls
);
790 ASSERT_EQ(1U, urls
.size());
791 EXPECT_TRUE(ContainsKey(urls
, kFile1
));
792 file_system
.ClearChangeForURLInTracker(*urls
.begin());
794 // Prepare temporary files which represent the remote file data.
795 const base::FilePath
kFilePath1(temp_dir
.path().Append(FPL("file1")));
796 const base::FilePath
kFilePath2(temp_dir
.path().Append(FPL("file2")));
798 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1
) - 1),
799 base::WriteFile(kFilePath1
, kTestFileData1
,
800 arraysize(kTestFileData1
) - 1));
801 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2
) - 1),
802 base::WriteFile(kFilePath2
, kTestFileData2
,
803 arraysize(kTestFileData2
) - 1));
806 int64 usage
= -1, new_usage
= -1;
808 EXPECT_EQ(storage::kQuotaStatusOk
,
809 file_system
.GetUsageAndQuota(&usage
, "a
));
811 // Here in the local filesystem we have:
812 // * kFile1 with kTestFileData0
814 // In the remote side let's assume we have:
815 // * kFile1 with kTestFileData1
816 // * kFile2 with kTestFileData2
819 // By calling ApplyChange's:
820 // * kFile1 will be updated to have kTestFileData1
821 // * kFile2 will be created
822 // * kDir will be created
824 // Apply the remote change to kFile1 (which will update the file).
825 FileChange
change(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
826 SYNC_FILE_TYPE_FILE
);
827 EXPECT_EQ(SYNC_STATUS_OK
,
828 ApplyRemoteChange(file_system
.file_system_context(),
829 change
, kFilePath1
, kFile1
,
830 SYNC_FILE_TYPE_FILE
));
832 // Check if the usage has been increased by (kTestFileData1 - kTestFileData0).
833 const int updated_size
=
834 arraysize(kTestFileData1
) - arraysize(kTestFileData0
);
835 EXPECT_EQ(storage::kQuotaStatusOk
,
836 file_system
.GetUsageAndQuota(&new_usage
, "a
));
837 EXPECT_EQ(updated_size
, new_usage
- usage
);
839 // Apply remote changes to kFile2 and kDir (should create a file and
840 // directory respectively).
841 // They are non-existent yet so their expected file type (the last
842 // parameter of ApplyRemoteChange) are
843 // SYNC_FILE_TYPE_UNKNOWN.
844 change
= FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
845 SYNC_FILE_TYPE_FILE
);
846 EXPECT_EQ(SYNC_STATUS_OK
,
847 ApplyRemoteChange(file_system
.file_system_context(),
848 change
, kFilePath2
, kFile2
,
849 SYNC_FILE_TYPE_UNKNOWN
));
851 change
= FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
852 SYNC_FILE_TYPE_DIRECTORY
);
853 EXPECT_EQ(SYNC_STATUS_OK
,
854 ApplyRemoteChange(file_system
.file_system_context(),
855 change
, base::FilePath(), kDir
,
856 SYNC_FILE_TYPE_UNKNOWN
));
858 // Calling ApplyRemoteChange with different file type should be handled as
861 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
, SYNC_FILE_TYPE_FILE
);
862 EXPECT_EQ(SYNC_STATUS_OK
,
863 ApplyRemoteChange(file_system
.file_system_context(),
867 SYNC_FILE_TYPE_DIRECTORY
));
868 EXPECT_EQ(base::File::FILE_OK
, file_system
.FileExists(kDir
));
870 change
= FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
871 SYNC_FILE_TYPE_DIRECTORY
);
872 EXPECT_EQ(SYNC_STATUS_OK
,
873 ApplyRemoteChange(file_system
.file_system_context(),
877 SYNC_FILE_TYPE_FILE
));
879 // Creating a file/directory must have increased the usage more than
880 // the size of kTestFileData2.
882 EXPECT_EQ(storage::kQuotaStatusOk
,
883 file_system
.GetUsageAndQuota(&new_usage
, "a
));
885 static_cast<int64
>(usage
+ arraysize(kTestFileData2
) - 1));
887 // The changes applied by ApplyRemoteChange should not be recorded in
888 // the change tracker.
890 file_system
.GetChangedURLsInTracker(&urls
);
891 EXPECT_TRUE(urls
.empty());
893 // Make sure all three files/directory exist.
894 EXPECT_EQ(base::File::FILE_OK
, file_system
.FileExists(kFile1
));
895 EXPECT_EQ(base::File::FILE_OK
, file_system
.FileExists(kFile2
));
896 EXPECT_EQ(base::File::FILE_OK
, file_system
.DirectoryExists(kDir
));
898 sync_context_
->ShutdownOnUIThread();
899 file_system
.TearDown();
902 TEST_F(LocalFileSyncContextTest
, ApplyRemoteChangeForAddOrUpdate_NoParent
) {
903 base::ScopedTempDir temp_dir
;
904 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
906 CannedSyncableFileSystem
file_system(GURL(kOrigin1
),
907 in_memory_env_
.get(),
908 io_task_runner_
.get(),
909 file_task_runner_
.get());
910 file_system
.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
912 sync_context_
= new LocalFileSyncContext(
913 dir_
.path(), in_memory_env_
.get(),
914 ui_task_runner_
.get(), io_task_runner_
.get());
915 ASSERT_EQ(SYNC_STATUS_OK
,
916 file_system
.MaybeInitializeFileSystemContext(sync_context_
.get()));
917 ASSERT_EQ(base::File::FILE_OK
, file_system
.OpenFileSystem());
919 const char kTestFileData
[] = "Lorem ipsum!";
920 const FileSystemURL
kDir(file_system
.URL("dir"));
921 const FileSystemURL
kFile(file_system
.URL("dir/file"));
923 // Either kDir or kFile not exist yet.
924 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
, file_system
.FileExists(kDir
));
925 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
, file_system
.FileExists(kFile
));
927 // Prepare a temporary file which represents remote file data.
928 const base::FilePath
kFilePath(temp_dir
.path().Append(FPL("file")));
929 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData
) - 1),
930 base::WriteFile(kFilePath
, kTestFileData
,
931 arraysize(kTestFileData
) - 1));
933 // Calling ApplyChange's with kFilePath should create
934 // kFile along with kDir.
935 FileChange
change(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
936 SYNC_FILE_TYPE_FILE
);
937 EXPECT_EQ(SYNC_STATUS_OK
,
938 ApplyRemoteChange(file_system
.file_system_context(),
939 change
, kFilePath
, kFile
,
940 SYNC_FILE_TYPE_UNKNOWN
));
942 // The changes applied by ApplyRemoteChange should not be recorded in
943 // the change tracker.
944 FileSystemURLSet urls
;
946 file_system
.GetChangedURLsInTracker(&urls
);
947 EXPECT_TRUE(urls
.empty());
949 // Make sure kDir and kFile are created by ApplyRemoteChange.
950 EXPECT_EQ(base::File::FILE_OK
, file_system
.FileExists(kFile
));
951 EXPECT_EQ(base::File::FILE_OK
, file_system
.DirectoryExists(kDir
));
953 sync_context_
->ShutdownOnUIThread();
954 file_system
.TearDown();
957 } // namespace sync_file_system