Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / local / local_file_sync_context_unittest.cc
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.
4
5 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
6
7 #include <vector>
8
9 #include "base/bind.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"
32
33 #define FPL FILE_PATH_LITERAL
34
35 using content::BrowserThread;
36 using fileapi::FileSystemContext;
37 using fileapi::FileSystemURL;
38 using fileapi::FileSystemURLSet;
39
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.
45
46 namespace sync_file_system {
47
48 namespace {
49 const char kOrigin1[] = "http://example.com";
50 const char kOrigin2[] = "http://chromium.org";
51 }
52
53 class LocalFileSyncContextTest : public testing::Test {
54  protected:
55   LocalFileSyncContextTest()
56       : thread_bundle_(
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) {}
63
64   virtual void SetUp() OVERRIDE {
65     RegisterSyncableFileSystem();
66     ASSERT_TRUE(dir_.CreateUniqueTempDir());
67     in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
68
69     ui_task_runner_ = base::MessageLoop::current()->message_loop_proxy();
70     io_task_runner_ = BrowserThread::GetMessageLoopProxyForThread(
71         BrowserThread::IO);
72     file_task_runner_ = BrowserThread::GetMessageLoopProxyForThread(
73         BrowserThread::IO);
74   }
75
76   virtual void TearDown() OVERRIDE {
77     RevokeSyncableFileSystem();
78   }
79
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(
91         file_system_context,
92         url,
93         sync_mode,
94         base::Bind(&LocalFileSyncContextTest::DidPrepareForSync,
95                    base::Unretained(this), metadata, changes, snapshot));
96   }
97
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();
107     return status_;
108   }
109
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);
121   }
122
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;
131     status_ = status;
132     *metadata_out = sync_file_info.metadata;
133     *changes_out = sync_file_info.changes;
134     if (snapshot_out)
135       *snapshot_out = snapshot.Pass();
136     base::MessageLoop::current()->Quit();
137   }
138
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 " <<
145                  url.DebugString());
146
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);
155
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();
163     return status_;
164   }
165
166   void DidApplyRemoteChange(FileSystemContext* file_system_context,
167                             const FileSystemURL& url,
168                             SyncStatusCode status) {
169     status_ = status;
170     sync_context_->FinalizeExclusiveSync(
171         file_system_context, url,
172         status == SYNC_STATUS_OK /* clear_local_changes */,
173         base::MessageLoop::QuitClosure());
174   }
175
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(
183           FROM_HERE,
184           base::Bind(&LocalFileSyncContextTest::StartModifyFileOnIOThread,
185                      base::Unretained(this), file_system, url));
186       return;
187     }
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)));
193   }
194
195   base::File::Error WaitUntilModifyFileIsDone() {
196     while (!async_modify_finished_)
197       base::MessageLoop::current()->RunUntilIdle();
198     return file_error_;
199   }
200
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(
205           FROM_HERE,
206           base::Bind(&LocalFileSyncContextTest::DidModifyFile,
207                      base::Unretained(this), error));
208       return;
209     }
210     ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread());
211     file_error_ = error;
212     async_modify_finished_ = true;
213   }
214
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));
223     } else {
224       sync_context_->FinalizeExclusiveSync(
225           file_system_context, url,
226           status == SYNC_STATUS_OK /* clear_local_changes */,
227           base::Bind(&base::DoNothing));
228     }
229   }
230
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());
245
246     const FileSystemURL kFile(file_system.URL("file"));
247     EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile));
248
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());
257
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());
263
264     SimulateFinishSync(file_system.file_system_context(), kFile,
265                        simulate_sync_finish_status, sync_mode);
266
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());
271     } else {
272       EXPECT_EQ(1U, changes.size());
273       EXPECT_TRUE(changes.list().back().IsFile());
274       EXPECT_TRUE(changes.list().back().IsAddOrUpdate());
275     }
276
277     sync_context_->ShutdownOnUIThread();
278     sync_context_ = NULL;
279
280     file_system.TearDown();
281   }
282
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());
297
298     const FileSystemURL kFile(file_system.URL("file"));
299     EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile));
300
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());
310
311     EXPECT_EQ(sync_mode == LocalFileSyncContext::SYNC_SNAPSHOT,
312               !snapshot.path().empty());
313
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());
319
320     StartModifyFileOnIOThread(&file_system, kFile);
321
322     if (sync_mode == LocalFileSyncContext::SYNC_SNAPSHOT) {
323       // Write should succeed.
324       EXPECT_EQ(base::File::FILE_OK, WaitUntilModifyFileIsDone());
325     } else {
326       base::MessageLoop::current()->RunUntilIdle();
327       EXPECT_FALSE(async_modify_finished_);
328     }
329
330     SimulateFinishSync(file_system.file_system_context(), kFile,
331                        SYNC_STATUS_OK, sync_mode);
332
333     EXPECT_EQ(base::File::FILE_OK, WaitUntilModifyFileIsDone());
334
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());
341
342     sync_context_->ShutdownOnUIThread();
343     sync_context_ = NULL;
344
345     file_system.TearDown();
346   }
347
348   ScopedEnableSyncFSDirectoryOperation enable_directory_operation_;
349
350   base::ScopedTempDir dir_;
351   scoped_ptr<leveldb::Env> in_memory_env_;
352
353   // These need to remain until the very end.
354   content::TestBrowserThreadBundle thread_bundle_;
355
356   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
357   scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
358   scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
359
360   scoped_refptr<LocalFileSyncContext> sync_context_;
361
362   SyncStatusCode status_;
363   base::File::Error file_error_;
364   bool async_modify_finished_;
365   bool has_inflight_prepare_for_sync_;
366 };
367
368 TEST_F(LocalFileSyncContextTest, ConstructAndDestruct) {
369   sync_context_ =
370       new LocalFileSyncContext(
371           dir_.path(), in_memory_env_.get(),
372           ui_task_runner_.get(), io_task_runner_.get());
373   sync_context_->ShutdownOnUIThread();
374 }
375
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);
382
383   sync_context_ = new LocalFileSyncContext(
384       dir_.path(), in_memory_env_.get(),
385       ui_task_runner_.get(), io_task_runner_.get());
386
387   // Initializes file_system using |sync_context_|.
388   EXPECT_EQ(SYNC_STATUS_OK,
389             file_system.MaybeInitializeFileSystemContext(sync_context_.get()));
390
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());
396
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());
401
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());
405
406   const FileSystemURL kURL(file_system.URL("foo"));
407   EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kURL));
408
409   FileSystemURLSet urls;
410   file_system.GetChangedURLsInTracker(&urls);
411   ASSERT_EQ(1U, urls.size());
412   EXPECT_TRUE(ContainsKey(urls, kURL));
413
414   // Finishing the test.
415   sync_context_->ShutdownOnUIThread();
416   file_system.TearDown();
417 }
418
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);
430
431   sync_context_ = new LocalFileSyncContext(
432       dir_.path(), in_memory_env_.get(),
433       ui_task_runner_.get(), io_task_runner_.get());
434
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()));
440
441   EXPECT_EQ(base::File::FILE_OK, file_system1.OpenFileSystem());
442   EXPECT_EQ(base::File::FILE_OK, file_system2.OpenFileSystem());
443
444   const FileSystemURL kURL1(file_system1.URL("foo"));
445   const FileSystemURL kURL2(file_system2.URL("bar"));
446
447   // Creates a file in file_system1.
448   EXPECT_EQ(base::File::FILE_OK, file_system1.CreateFile(kURL1));
449
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));
455
456   // file_system1's tracker must have no change.
457   urls.clear();
458   file_system2.GetChangedURLsInTracker(&urls);
459   ASSERT_TRUE(urls.empty());
460
461   // Creates a directory in file_system2.
462   EXPECT_EQ(base::File::FILE_OK, file_system2.CreateDirectory(kURL2));
463
464   // file_system1's tracker must have the change for kURL1 as before.
465   urls.clear();
466   file_system1.GetChangedURLsInTracker(&urls);
467   ASSERT_EQ(1U, urls.size());
468   EXPECT_TRUE(ContainsKey(urls, kURL1));
469
470   // file_system2's tracker now must have the change for kURL2.
471   urls.clear();
472   file_system2.GetChangedURLsInTracker(&urls);
473   ASSERT_EQ(1U, urls.size());
474   EXPECT_TRUE(ContainsKey(urls, kURL2));
475
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);
487
488   changes.clear();
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);
498
499   sync_context_->ShutdownOnUIThread();
500   sync_context_ = NULL;
501
502   file_system1.TearDown();
503   file_system2.TearDown();
504 }
505
506 TEST_F(LocalFileSyncContextTest, PrepareSync_SyncSuccess_Exclusive) {
507   PrepareForSync_Basic(LocalFileSyncContext::SYNC_EXCLUSIVE,
508                        SYNC_STATUS_OK);
509 }
510
511 TEST_F(LocalFileSyncContextTest, PrepareSync_SyncSuccess_Snapshot) {
512   PrepareForSync_Basic(LocalFileSyncContext::SYNC_SNAPSHOT,
513                        SYNC_STATUS_OK);
514 }
515
516 TEST_F(LocalFileSyncContextTest, PrepareSync_SyncFailure_Exclusive) {
517   PrepareForSync_Basic(LocalFileSyncContext::SYNC_EXCLUSIVE,
518                        SYNC_STATUS_FAILED);
519 }
520
521 TEST_F(LocalFileSyncContextTest, PrepareSync_SyncFailure_Snapshot) {
522   PrepareForSync_Basic(LocalFileSyncContext::SYNC_SNAPSHOT,
523                        SYNC_STATUS_FAILED);
524 }
525
526 TEST_F(LocalFileSyncContextTest, PrepareSync_WriteDuringSync_Exclusive) {
527   PrepareForSync_WriteDuringSync(LocalFileSyncContext::SYNC_EXCLUSIVE);
528 }
529
530 TEST_F(LocalFileSyncContextTest, PrepareSync_WriteDuringSync_Snapshot) {
531   PrepareForSync_WriteDuringSync(LocalFileSyncContext::SYNC_SNAPSHOT);
532 }
533
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()));
549
550   EXPECT_EQ(base::File::FILE_OK, file_system.OpenFileSystem());
551
552   const FileSystemURL kURL1(file_system.URL("foo"));
553
554   // Creates a file in file_system.
555   EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kURL1));
556
557   // Kick file write on IO thread.
558   StartModifyFileOnIOThread(&file_system, kURL1);
559
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);
569
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;
574   changes.clear();
575   sync_context_->RegisterURLForWaitingSync(
576       kURL1, GetPrepareForSyncClosure(file_system.file_system_context(), kURL1,
577                                       LocalFileSyncContext::SYNC_EXCLUSIVE,
578                                       &metadata, &changes, NULL));
579
580   // Wait for the completion.
581   EXPECT_EQ(base::File::FILE_OK, WaitUntilModifyFileIsDone());
582
583   // The PrepareForSync must have been started; wait until DidPrepareForSync
584   // is done.
585   base::MessageLoop::current()->Run();
586   ASSERT_FALSE(has_inflight_prepare_for_sync_);
587
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);
595
596   sync_context_->ShutdownOnUIThread();
597   sync_context_ = NULL;
598   file_system.TearDown();
599 }
600
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);
607
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());
614
615   // Record the initial usage (likely 0).
616   int64 initial_usage = -1;
617   int64 quota = -1;
618   EXPECT_EQ(quota::kQuotaStatusOk,
619             file_system.GetUsageAndQuota(&initial_usage, &quota));
620
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"));
625
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));
629
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);
640   }
641
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, &quota));
646   EXPECT_GT(new_usage, initial_usage);
647
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));
655
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));
664
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));
672
673   // The changes applied by ApplyRemoteChange should not be recorded in
674   // the change tracker.
675   urls.clear();
676   file_system.GetChangedURLsInTracker(&urls);
677   EXPECT_TRUE(urls.empty());
678
679   // The quota usage data must have reflected the deletion.
680   EXPECT_EQ(quota::kQuotaStatusOk,
681             file_system.GetUsageAndQuota(&new_usage, &quota));
682   EXPECT_EQ(new_usage, initial_usage);
683
684   sync_context_->ShutdownOnUIThread();
685   sync_context_ = NULL;
686   file_system.TearDown();
687 }
688
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);
695
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());
702
703   // Record the initial usage (likely 0).
704   int64 initial_usage = -1;
705   int64 quota = -1;
706   EXPECT_EQ(quota::kQuotaStatusOk,
707             file_system.GetUsageAndQuota(&initial_usage, &quota));
708
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"));
713
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));
717
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, &quota));
722   EXPECT_GT(new_usage, initial_usage);
723
724   const FileSystemURL kRoot(file_system.URL(""));
725
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));
732
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));
740
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());
745
746   // The quota usage data must have reflected the deletion.
747   EXPECT_EQ(quota::kQuotaStatusOk,
748             file_system.GetUsageAndQuota(&new_usage, &quota));
749   EXPECT_EQ(new_usage, initial_usage);
750
751   sync_context_->ShutdownOnUIThread();
752   sync_context_ = NULL;
753   file_system.TearDown();
754 }
755
756 TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForAddOrUpdate) {
757   base::ScopedTempDir temp_dir;
758   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
759
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);
765
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());
772
773   const FileSystemURL kFile1(file_system.URL("file1"));
774   const FileSystemURL kFile2(file_system.URL("file2"));
775   const FileSystemURL kDir(file_system.URL("dir"));
776
777   const char kTestFileData0[] = "0123456789";
778   const char kTestFileData1[] = "Lorem ipsum!";
779   const char kTestFileData2[] = "This is sample test data.";
780
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));
785
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));
791
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());
798
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")));
802
803   ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1) - 1),
804             file_util::WriteFile(kFilePath1, kTestFileData1,
805                                  arraysize(kTestFileData1) - 1));
806   ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2) - 1),
807             file_util::WriteFile(kFilePath2, kTestFileData2,
808                                  arraysize(kTestFileData2) - 1));
809
810   // Record the usage.
811   int64 usage = -1, new_usage = -1;
812   int64 quota = -1;
813   EXPECT_EQ(quota::kQuotaStatusOk,
814             file_system.GetUsageAndQuota(&usage, &quota));
815
816   // Here in the local filesystem we have:
817   //  * kFile1 with kTestFileData0
818   //
819   // In the remote side let's assume we have:
820   //  * kFile1 with kTestFileData1
821   //  * kFile2 with kTestFileData2
822   //  * kDir
823   //
824   // By calling ApplyChange's:
825   //  * kFile1 will be updated to have kTestFileData1
826   //  * kFile2 will be created
827   //  * kDir will be created
828
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));
836
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, &quota));
842   EXPECT_EQ(updated_size, new_usage - usage);
843
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));
855
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));
862
863   // Calling ApplyRemoteChange with different file type should be handled as
864   // overwrite.
865   change =
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(),
869                               change,
870                               kFilePath1,
871                               kDir,
872                               SYNC_FILE_TYPE_DIRECTORY));
873   EXPECT_EQ(base::File::FILE_OK, file_system.FileExists(kDir));
874
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(),
879                               change,
880                               kFilePath1,
881                               kDir,
882                               SYNC_FILE_TYPE_FILE));
883
884   // Creating a file/directory must have increased the usage more than
885   // the size of kTestFileData2.
886   new_usage = usage;
887   EXPECT_EQ(quota::kQuotaStatusOk,
888             file_system.GetUsageAndQuota(&new_usage, &quota));
889   EXPECT_GT(new_usage,
890             static_cast<int64>(usage + arraysize(kTestFileData2) - 1));
891
892   // The changes applied by ApplyRemoteChange should not be recorded in
893   // the change tracker.
894   urls.clear();
895   file_system.GetChangedURLsInTracker(&urls);
896   EXPECT_TRUE(urls.empty());
897
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));
902
903   sync_context_->ShutdownOnUIThread();
904   file_system.TearDown();
905 }
906
907 TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForAddOrUpdate_NoParent) {
908   base::ScopedTempDir temp_dir;
909   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
910
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);
916
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());
923
924   const char kTestFileData[] = "Lorem ipsum!";
925   const FileSystemURL kDir(file_system.URL("dir"));
926   const FileSystemURL kFile(file_system.URL("dir/file"));
927
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));
931
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             file_util::WriteFile(kFilePath, kTestFileData,
936                                  arraysize(kTestFileData) - 1));
937
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));
946
947   // The changes applied by ApplyRemoteChange should not be recorded in
948   // the change tracker.
949   FileSystemURLSet urls;
950   urls.clear();
951   file_system.GetChangedURLsInTracker(&urls);
952   EXPECT_TRUE(urls.empty());
953
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));
957
958   sync_context_->ShutdownOnUIThread();
959   file_system.TearDown();
960 }
961
962 }  // namespace sync_file_system