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/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/fileapi/file_system_context.h"
25#include "storage/browser/fileapi/file_system_operation_runner.h"
26#include "storage/browser/fileapi/isolated_context.h"
27#include "storage/common/blob/scoped_file.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"
31
32#define FPL FILE_PATH_LITERAL
33
34using content::BrowserThread;
35using storage::FileSystemContext;
36using storage::FileSystemURL;
37using storage::FileSystemURLSet;
38
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.
44
45namespace sync_file_system {
46
47namespace {
48const char kOrigin1[] = "http://example.com";
49const char kOrigin2[] = "http://chromium.org";
50}
51
52class LocalFileSyncContextTest : public testing::Test {
53 protected:
54  LocalFileSyncContextTest()
55      : thread_bundle_(
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) {}
62
63  virtual void SetUp() OVERRIDE {
64    RegisterSyncableFileSystem();
65    ASSERT_TRUE(dir_.CreateUniqueTempDir());
66    in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
67
68    ui_task_runner_ = base::MessageLoop::current()->message_loop_proxy();
69    io_task_runner_ = BrowserThread::GetMessageLoopProxyForThread(
70        BrowserThread::IO);
71    file_task_runner_ = BrowserThread::GetMessageLoopProxyForThread(
72        BrowserThread::IO);
73  }
74
75  virtual void TearDown() OVERRIDE {
76    RevokeSyncableFileSystem();
77  }
78
79  void StartPrepareForSync(FileSystemContext* file_system_context,
80                           const FileSystemURL& url,
81                           LocalFileSyncContext::SyncMode sync_mode,
82                           SyncFileMetadata* metadata,
83                           FileChangeList* changes,
84                           storage::ScopedFile* snapshot) {
85    ASSERT_TRUE(changes != NULL);
86    ASSERT_FALSE(has_inflight_prepare_for_sync_);
87    status_ = SYNC_STATUS_UNKNOWN;
88    has_inflight_prepare_for_sync_ = true;
89    sync_context_->PrepareForSync(
90        file_system_context,
91        url,
92        sync_mode,
93        base::Bind(&LocalFileSyncContextTest::DidPrepareForSync,
94                   base::Unretained(this), metadata, changes, snapshot));
95  }
96
97  SyncStatusCode PrepareForSync(FileSystemContext* file_system_context,
98                                const FileSystemURL& url,
99                                LocalFileSyncContext::SyncMode sync_mode,
100                                SyncFileMetadata* metadata,
101                                FileChangeList* changes,
102                                storage::ScopedFile* snapshot) {
103    StartPrepareForSync(file_system_context, url, sync_mode,
104                        metadata, changes, snapshot);
105    base::MessageLoop::current()->Run();
106    return status_;
107  }
108
109  base::Closure GetPrepareForSyncClosure(
110      FileSystemContext* file_system_context,
111      const FileSystemURL& url,
112      LocalFileSyncContext::SyncMode sync_mode,
113      SyncFileMetadata* metadata,
114      FileChangeList* changes,
115      storage::ScopedFile* snapshot) {
116    return base::Bind(&LocalFileSyncContextTest::StartPrepareForSync,
117                      base::Unretained(this),
118                      base::Unretained(file_system_context),
119                      url, sync_mode, metadata, changes, snapshot);
120  }
121
122  void DidPrepareForSync(SyncFileMetadata* metadata_out,
123                         FileChangeList* changes_out,
124                         storage::ScopedFile* snapshot_out,
125                         SyncStatusCode status,
126                         const LocalFileSyncInfo& sync_file_info,
127                         storage::ScopedFile snapshot) {
128    ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread());
129    has_inflight_prepare_for_sync_ = false;
130    status_ = status;
131    *metadata_out = sync_file_info.metadata;
132    *changes_out = sync_file_info.changes;
133    if (snapshot_out)
134      *snapshot_out = snapshot.Pass();
135    base::MessageLoop::current()->Quit();
136  }
137
138  SyncStatusCode ApplyRemoteChange(FileSystemContext* file_system_context,
139                                   const FileChange& change,
140                                   const base::FilePath& local_path,
141                                   const FileSystemURL& url,
142                                   SyncFileType expected_file_type) {
143    SCOPED_TRACE(testing::Message() << "ApplyChange for " <<
144                 url.DebugString());
145
146    // First we should call PrepareForSync to disable writing.
147    SyncFileMetadata metadata;
148    FileChangeList changes;
149    EXPECT_EQ(SYNC_STATUS_OK,
150              PrepareForSync(file_system_context, url,
151                             LocalFileSyncContext::SYNC_EXCLUSIVE,
152                             &metadata, &changes, NULL));
153    EXPECT_EQ(expected_file_type, metadata.file_type);
154
155    status_ = SYNC_STATUS_UNKNOWN;
156    sync_context_->ApplyRemoteChange(
157        file_system_context, change, local_path, url,
158        base::Bind(&LocalFileSyncContextTest::DidApplyRemoteChange,
159                   base::Unretained(this),
160                   make_scoped_refptr(file_system_context), url));
161    base::MessageLoop::current()->Run();
162    return status_;
163  }
164
165  void DidApplyRemoteChange(FileSystemContext* file_system_context,
166                            const FileSystemURL& url,
167                            SyncStatusCode status) {
168    status_ = status;
169    sync_context_->FinalizeExclusiveSync(
170        file_system_context, url,
171        status == SYNC_STATUS_OK /* clear_local_changes */,
172        base::MessageLoop::QuitClosure());
173  }
174
175  void StartModifyFileOnIOThread(CannedSyncableFileSystem* file_system,
176                                 const FileSystemURL& url) {
177    ASSERT_TRUE(file_system != NULL);
178    if (!io_task_runner_->RunsTasksOnCurrentThread()) {
179      async_modify_finished_ = false;
180      ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread());
181      io_task_runner_->PostTask(
182          FROM_HERE,
183          base::Bind(&LocalFileSyncContextTest::StartModifyFileOnIOThread,
184                     base::Unretained(this), file_system, url));
185      return;
186    }
187    ASSERT_TRUE(io_task_runner_->RunsTasksOnCurrentThread());
188    file_error_ = base::File::FILE_ERROR_FAILED;
189    file_system->operation_runner()->Truncate(
190        url, 1, base::Bind(&LocalFileSyncContextTest::DidModifyFile,
191                           base::Unretained(this)));
192  }
193
194  base::File::Error WaitUntilModifyFileIsDone() {
195    while (!async_modify_finished_)
196      base::MessageLoop::current()->RunUntilIdle();
197    return file_error_;
198  }
199
200  void DidModifyFile(base::File::Error error) {
201    if (!ui_task_runner_->RunsTasksOnCurrentThread()) {
202      ASSERT_TRUE(io_task_runner_->RunsTasksOnCurrentThread());
203      ui_task_runner_->PostTask(
204          FROM_HERE,
205          base::Bind(&LocalFileSyncContextTest::DidModifyFile,
206                     base::Unretained(this), error));
207      return;
208    }
209    ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread());
210    file_error_ = error;
211    async_modify_finished_ = true;
212  }
213
214  void SimulateFinishSync(FileSystemContext* file_system_context,
215                          const FileSystemURL& url,
216                          SyncStatusCode status,
217                          LocalFileSyncContext::SyncMode sync_mode) {
218    if (sync_mode == LocalFileSyncContext::SYNC_SNAPSHOT) {
219      sync_context_->FinalizeSnapshotSync(
220          file_system_context, url, status,
221          base::Bind(&base::DoNothing));
222    } else {
223      sync_context_->FinalizeExclusiveSync(
224          file_system_context, url,
225          status == SYNC_STATUS_OK /* clear_local_changes */,
226          base::Bind(&base::DoNothing));
227    }
228  }
229
230  void PrepareForSync_Basic(LocalFileSyncContext::SyncMode sync_mode,
231                            SyncStatusCode simulate_sync_finish_status) {
232    CannedSyncableFileSystem file_system(GURL(kOrigin1),
233                                         in_memory_env_.get(),
234                                         io_task_runner_.get(),
235                                         file_task_runner_.get());
236    file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
237    sync_context_ = new LocalFileSyncContext(
238        dir_.path(), in_memory_env_.get(),
239        ui_task_runner_.get(), io_task_runner_.get());
240    ASSERT_EQ(SYNC_STATUS_OK,
241              file_system.MaybeInitializeFileSystemContext(
242                  sync_context_.get()));
243    ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem());
244
245    const FileSystemURL kFile(file_system.URL("file"));
246    EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile));
247
248    SyncFileMetadata metadata;
249    FileChangeList changes;
250    EXPECT_EQ(SYNC_STATUS_OK,
251              PrepareForSync(file_system.file_system_context(), kFile,
252                             sync_mode, &metadata, &changes, NULL));
253    EXPECT_EQ(1U, changes.size());
254    EXPECT_TRUE(changes.list().back().IsFile());
255    EXPECT_TRUE(changes.list().back().IsAddOrUpdate());
256
257    // We should see the same set of changes.
258    file_system.GetChangesForURLInTracker(kFile, &changes);
259    EXPECT_EQ(1U, changes.size());
260    EXPECT_TRUE(changes.list().back().IsFile());
261    EXPECT_TRUE(changes.list().back().IsAddOrUpdate());
262
263    SimulateFinishSync(file_system.file_system_context(), kFile,
264                       simulate_sync_finish_status, sync_mode);
265
266    file_system.GetChangesForURLInTracker(kFile, &changes);
267    if (simulate_sync_finish_status == SYNC_STATUS_OK) {
268      // The change's cleared.
269      EXPECT_TRUE(changes.empty());
270    } else {
271      EXPECT_EQ(1U, changes.size());
272      EXPECT_TRUE(changes.list().back().IsFile());
273      EXPECT_TRUE(changes.list().back().IsAddOrUpdate());
274    }
275
276    sync_context_->ShutdownOnUIThread();
277    sync_context_ = NULL;
278
279    file_system.TearDown();
280  }
281
282  void PrepareForSync_WriteDuringSync(
283      LocalFileSyncContext::SyncMode sync_mode) {
284    CannedSyncableFileSystem file_system(GURL(kOrigin1),
285                                         in_memory_env_.get(),
286                                         io_task_runner_.get(),
287                                         file_task_runner_.get());
288    file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
289    sync_context_ = new LocalFileSyncContext(
290        dir_.path(), in_memory_env_.get(),
291        ui_task_runner_.get(), io_task_runner_.get());
292    ASSERT_EQ(SYNC_STATUS_OK,
293              file_system.MaybeInitializeFileSystemContext(
294                  sync_context_.get()));
295    ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem());
296
297    const FileSystemURL kFile(file_system.URL("file"));
298    EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile));
299
300    SyncFileMetadata metadata;
301    FileChangeList changes;
302    storage::ScopedFile snapshot;
303    EXPECT_EQ(SYNC_STATUS_OK,
304              PrepareForSync(file_system.file_system_context(), kFile,
305                             sync_mode, &metadata, &changes, &snapshot));
306    EXPECT_EQ(1U, changes.size());
307    EXPECT_TRUE(changes.list().back().IsFile());
308    EXPECT_TRUE(changes.list().back().IsAddOrUpdate());
309
310    EXPECT_EQ(sync_mode == LocalFileSyncContext::SYNC_SNAPSHOT,
311              !snapshot.path().empty());
312
313    // Tracker keeps same set of changes.
314    file_system.GetChangesForURLInTracker(kFile, &changes);
315    EXPECT_EQ(1U, changes.size());
316    EXPECT_TRUE(changes.list().back().IsFile());
317    EXPECT_TRUE(changes.list().back().IsAddOrUpdate());
318
319    StartModifyFileOnIOThread(&file_system, kFile);
320
321    if (sync_mode == LocalFileSyncContext::SYNC_SNAPSHOT) {
322      // Write should succeed.
323      EXPECT_EQ(base::File::FILE_OK, WaitUntilModifyFileIsDone());
324    } else {
325      base::MessageLoop::current()->RunUntilIdle();
326      EXPECT_FALSE(async_modify_finished_);
327    }
328
329    SimulateFinishSync(file_system.file_system_context(), kFile,
330                       SYNC_STATUS_OK, sync_mode);
331
332    EXPECT_EQ(base::File::FILE_OK, WaitUntilModifyFileIsDone());
333
334    // Sync succeeded, but the other change that was made during or
335    // after sync is recorded.
336    file_system.GetChangesForURLInTracker(kFile, &changes);
337    EXPECT_EQ(1U, changes.size());
338    EXPECT_TRUE(changes.list().back().IsFile());
339    EXPECT_TRUE(changes.list().back().IsAddOrUpdate());
340
341    sync_context_->ShutdownOnUIThread();
342    sync_context_ = NULL;
343
344    file_system.TearDown();
345  }
346
347  base::ScopedTempDir dir_;
348  scoped_ptr<leveldb::Env> in_memory_env_;
349
350  // These need to remain until the very end.
351  content::TestBrowserThreadBundle thread_bundle_;
352
353  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
354  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
355  scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
356
357  scoped_refptr<LocalFileSyncContext> sync_context_;
358
359  SyncStatusCode status_;
360  base::File::Error file_error_;
361  bool async_modify_finished_;
362  bool has_inflight_prepare_for_sync_;
363};
364
365TEST_F(LocalFileSyncContextTest, ConstructAndDestruct) {
366  sync_context_ =
367      new LocalFileSyncContext(
368          dir_.path(), in_memory_env_.get(),
369          ui_task_runner_.get(), io_task_runner_.get());
370  sync_context_->ShutdownOnUIThread();
371}
372
373TEST_F(LocalFileSyncContextTest, InitializeFileSystemContext) {
374  CannedSyncableFileSystem file_system(GURL(kOrigin1),
375                                       in_memory_env_.get(),
376                                       io_task_runner_.get(),
377                                       file_task_runner_.get());
378  file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
379
380  sync_context_ = new LocalFileSyncContext(
381      dir_.path(), in_memory_env_.get(),
382      ui_task_runner_.get(), io_task_runner_.get());
383
384  // Initializes file_system using |sync_context_|.
385  EXPECT_EQ(SYNC_STATUS_OK,
386            file_system.MaybeInitializeFileSystemContext(sync_context_.get()));
387
388  // Make sure everything's set up for file_system to be able to handle
389  // syncable file system operations.
390  EXPECT_TRUE(file_system.backend()->sync_context() != NULL);
391  EXPECT_TRUE(file_system.backend()->change_tracker() != NULL);
392  EXPECT_EQ(sync_context_.get(), file_system.backend()->sync_context());
393
394  // Calling MaybeInitialize for the same context multiple times must be ok.
395  EXPECT_EQ(SYNC_STATUS_OK,
396            file_system.MaybeInitializeFileSystemContext(sync_context_.get()));
397  EXPECT_EQ(sync_context_.get(), file_system.backend()->sync_context());
398
399  // Opens the file_system, perform some operation and see if the change tracker
400  // correctly captures the change.
401  EXPECT_EQ(base::File::FILE_OK, file_system.OpenFileSystem());
402
403  const FileSystemURL kURL(file_system.URL("foo"));
404  EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kURL));
405
406  FileSystemURLSet urls;
407  file_system.GetChangedURLsInTracker(&urls);
408  ASSERT_EQ(1U, urls.size());
409  EXPECT_TRUE(ContainsKey(urls, kURL));
410
411  // Finishing the test.
412  sync_context_->ShutdownOnUIThread();
413  file_system.TearDown();
414}
415
416TEST_F(LocalFileSyncContextTest, MultipleFileSystemContexts) {
417  CannedSyncableFileSystem file_system1(GURL(kOrigin1),
418                                        in_memory_env_.get(),
419                                        io_task_runner_.get(),
420                                        file_task_runner_.get());
421  CannedSyncableFileSystem file_system2(GURL(kOrigin2),
422                                        in_memory_env_.get(),
423                                        io_task_runner_.get(),
424                                        file_task_runner_.get());
425  file_system1.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
426  file_system2.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
427
428  sync_context_ = new LocalFileSyncContext(
429      dir_.path(), in_memory_env_.get(),
430      ui_task_runner_.get(), io_task_runner_.get());
431
432  // Initializes file_system1 and file_system2.
433  EXPECT_EQ(SYNC_STATUS_OK,
434            file_system1.MaybeInitializeFileSystemContext(sync_context_.get()));
435  EXPECT_EQ(SYNC_STATUS_OK,
436            file_system2.MaybeInitializeFileSystemContext(sync_context_.get()));
437
438  EXPECT_EQ(base::File::FILE_OK, file_system1.OpenFileSystem());
439  EXPECT_EQ(base::File::FILE_OK, file_system2.OpenFileSystem());
440
441  const FileSystemURL kURL1(file_system1.URL("foo"));
442  const FileSystemURL kURL2(file_system2.URL("bar"));
443
444  // Creates a file in file_system1.
445  EXPECT_EQ(base::File::FILE_OK, file_system1.CreateFile(kURL1));
446
447  // file_system1's tracker must have recorded the change.
448  FileSystemURLSet urls;
449  file_system1.GetChangedURLsInTracker(&urls);
450  ASSERT_EQ(1U, urls.size());
451  EXPECT_TRUE(ContainsKey(urls, kURL1));
452
453  // file_system1's tracker must have no change.
454  urls.clear();
455  file_system2.GetChangedURLsInTracker(&urls);
456  ASSERT_TRUE(urls.empty());
457
458  // Creates a directory in file_system2.
459  EXPECT_EQ(base::File::FILE_OK, file_system2.CreateDirectory(kURL2));
460
461  // file_system1's tracker must have the change for kURL1 as before.
462  urls.clear();
463  file_system1.GetChangedURLsInTracker(&urls);
464  ASSERT_EQ(1U, urls.size());
465  EXPECT_TRUE(ContainsKey(urls, kURL1));
466
467  // file_system2's tracker now must have the change for kURL2.
468  urls.clear();
469  file_system2.GetChangedURLsInTracker(&urls);
470  ASSERT_EQ(1U, urls.size());
471  EXPECT_TRUE(ContainsKey(urls, kURL2));
472
473  SyncFileMetadata metadata;
474  FileChangeList changes;
475  EXPECT_EQ(SYNC_STATUS_OK,
476            PrepareForSync(file_system1.file_system_context(), kURL1,
477                           LocalFileSyncContext::SYNC_EXCLUSIVE,
478                           &metadata, &changes, NULL));
479  EXPECT_EQ(1U, changes.size());
480  EXPECT_TRUE(changes.list().back().IsFile());
481  EXPECT_TRUE(changes.list().back().IsAddOrUpdate());
482  EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type);
483  EXPECT_EQ(0, metadata.size);
484
485  changes.clear();
486  EXPECT_EQ(SYNC_STATUS_OK,
487            PrepareForSync(file_system2.file_system_context(), kURL2,
488                           LocalFileSyncContext::SYNC_EXCLUSIVE,
489                           &metadata, &changes, NULL));
490  EXPECT_EQ(1U, changes.size());
491  EXPECT_FALSE(changes.list().back().IsFile());
492  EXPECT_TRUE(changes.list().back().IsAddOrUpdate());
493  EXPECT_EQ(SYNC_FILE_TYPE_DIRECTORY, metadata.file_type);
494  EXPECT_EQ(0, metadata.size);
495
496  sync_context_->ShutdownOnUIThread();
497  sync_context_ = NULL;
498
499  file_system1.TearDown();
500  file_system2.TearDown();
501}
502
503TEST_F(LocalFileSyncContextTest, PrepareSync_SyncSuccess_Exclusive) {
504  PrepareForSync_Basic(LocalFileSyncContext::SYNC_EXCLUSIVE,
505                       SYNC_STATUS_OK);
506}
507
508TEST_F(LocalFileSyncContextTest, PrepareSync_SyncSuccess_Snapshot) {
509  PrepareForSync_Basic(LocalFileSyncContext::SYNC_SNAPSHOT,
510                       SYNC_STATUS_OK);
511}
512
513TEST_F(LocalFileSyncContextTest, PrepareSync_SyncFailure_Exclusive) {
514  PrepareForSync_Basic(LocalFileSyncContext::SYNC_EXCLUSIVE,
515                       SYNC_STATUS_FAILED);
516}
517
518TEST_F(LocalFileSyncContextTest, PrepareSync_SyncFailure_Snapshot) {
519  PrepareForSync_Basic(LocalFileSyncContext::SYNC_SNAPSHOT,
520                       SYNC_STATUS_FAILED);
521}
522
523TEST_F(LocalFileSyncContextTest, PrepareSync_WriteDuringSync_Exclusive) {
524  PrepareForSync_WriteDuringSync(LocalFileSyncContext::SYNC_EXCLUSIVE);
525}
526
527TEST_F(LocalFileSyncContextTest, PrepareSync_WriteDuringSync_Snapshot) {
528  PrepareForSync_WriteDuringSync(LocalFileSyncContext::SYNC_SNAPSHOT);
529}
530
531// LocalFileSyncContextTest.PrepareSyncWhileWriting is flaky on android.
532// http://crbug.com/239793
533// It is also flaky on the TSAN v2 bots, and hangs other bots.
534// http://crbug.com/305905.
535TEST_F(LocalFileSyncContextTest, DISABLED_PrepareSyncWhileWriting) {
536  CannedSyncableFileSystem file_system(GURL(kOrigin1),
537                                       in_memory_env_.get(),
538                                       io_task_runner_.get(),
539                                       file_task_runner_.get());
540  file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
541  sync_context_ = new LocalFileSyncContext(
542      dir_.path(), in_memory_env_.get(),
543      ui_task_runner_.get(), io_task_runner_.get());
544  EXPECT_EQ(SYNC_STATUS_OK,
545            file_system.MaybeInitializeFileSystemContext(sync_context_.get()));
546
547  EXPECT_EQ(base::File::FILE_OK, file_system.OpenFileSystem());
548
549  const FileSystemURL kURL1(file_system.URL("foo"));
550
551  // Creates a file in file_system.
552  EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kURL1));
553
554  // Kick file write on IO thread.
555  StartModifyFileOnIOThread(&file_system, kURL1);
556
557  // Until the operation finishes PrepareForSync should return BUSY error.
558  SyncFileMetadata metadata;
559  metadata.file_type = SYNC_FILE_TYPE_UNKNOWN;
560  FileChangeList changes;
561  EXPECT_EQ(SYNC_STATUS_FILE_BUSY,
562            PrepareForSync(file_system.file_system_context(), kURL1,
563                           LocalFileSyncContext::SYNC_EXCLUSIVE,
564                           &metadata, &changes, NULL));
565  EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type);
566
567  // Register PrepareForSync method to be invoked when kURL1 becomes
568  // syncable. (Actually this may be done after all operations are done
569  // on IO thread in this test.)
570  metadata.file_type = SYNC_FILE_TYPE_UNKNOWN;
571  changes.clear();
572  sync_context_->RegisterURLForWaitingSync(
573      kURL1, GetPrepareForSyncClosure(file_system.file_system_context(), kURL1,
574                                      LocalFileSyncContext::SYNC_EXCLUSIVE,
575                                      &metadata, &changes, NULL));
576
577  // Wait for the completion.
578  EXPECT_EQ(base::File::FILE_OK, WaitUntilModifyFileIsDone());
579
580  // The PrepareForSync must have been started; wait until DidPrepareForSync
581  // is done.
582  base::MessageLoop::current()->Run();
583  ASSERT_FALSE(has_inflight_prepare_for_sync_);
584
585  // Now PrepareForSync should have run and returned OK.
586  EXPECT_EQ(SYNC_STATUS_OK, status_);
587  EXPECT_EQ(1U, changes.size());
588  EXPECT_TRUE(changes.list().back().IsFile());
589  EXPECT_TRUE(changes.list().back().IsAddOrUpdate());
590  EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type);
591  EXPECT_EQ(1, metadata.size);
592
593  sync_context_->ShutdownOnUIThread();
594  sync_context_ = NULL;
595  file_system.TearDown();
596}
597
598TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForDeletion) {
599  CannedSyncableFileSystem file_system(GURL(kOrigin1),
600                                       in_memory_env_.get(),
601                                       io_task_runner_.get(),
602                                       file_task_runner_.get());
603  file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
604
605  sync_context_ = new LocalFileSyncContext(
606      dir_.path(), in_memory_env_.get(),
607      ui_task_runner_.get(), io_task_runner_.get());
608  ASSERT_EQ(SYNC_STATUS_OK,
609            file_system.MaybeInitializeFileSystemContext(sync_context_.get()));
610  ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem());
611
612  // Record the initial usage (likely 0).
613  int64 initial_usage = -1;
614  int64 quota = -1;
615  EXPECT_EQ(storage::kQuotaStatusOk,
616            file_system.GetUsageAndQuota(&initial_usage, &quota));
617
618  // Create a file and directory in the file_system.
619  const FileSystemURL kFile(file_system.URL("file"));
620  const FileSystemURL kDir(file_system.URL("dir"));
621  const FileSystemURL kChild(file_system.URL("dir/child"));
622
623  EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile));
624  EXPECT_EQ(base::File::FILE_OK, file_system.CreateDirectory(kDir));
625  EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kChild));
626
627  // file_system's change tracker must have recorded the creation.
628  FileSystemURLSet urls;
629  file_system.GetChangedURLsInTracker(&urls);
630  ASSERT_EQ(3U, urls.size());
631  ASSERT_TRUE(ContainsKey(urls, kFile));
632  ASSERT_TRUE(ContainsKey(urls, kDir));
633  ASSERT_TRUE(ContainsKey(urls, kChild));
634  for (FileSystemURLSet::iterator iter = urls.begin();
635       iter != urls.end(); ++iter) {
636    file_system.ClearChangeForURLInTracker(*iter);
637  }
638
639  // At this point the usage must be greater than the initial usage.
640  int64 new_usage = -1;
641  EXPECT_EQ(storage::kQuotaStatusOk,
642            file_system.GetUsageAndQuota(&new_usage, &quota));
643  EXPECT_GT(new_usage, initial_usage);
644
645  // Now let's apply remote deletion changes.
646  FileChange change(FileChange::FILE_CHANGE_DELETE,
647                    SYNC_FILE_TYPE_FILE);
648  EXPECT_EQ(SYNC_STATUS_OK,
649            ApplyRemoteChange(file_system.file_system_context(),
650                              change, base::FilePath(), kFile,
651                              SYNC_FILE_TYPE_FILE));
652
653  // The implementation doesn't check file type for deletion, and it must be ok
654  // even if we don't know if the deletion change was for a file or a directory.
655  change = FileChange(FileChange::FILE_CHANGE_DELETE,
656                      SYNC_FILE_TYPE_UNKNOWN);
657  EXPECT_EQ(SYNC_STATUS_OK,
658            ApplyRemoteChange(file_system.file_system_context(),
659                              change, base::FilePath(), kDir,
660                              SYNC_FILE_TYPE_DIRECTORY));
661
662  // Check the directory/files are deleted successfully.
663  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
664            file_system.FileExists(kFile));
665  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
666            file_system.DirectoryExists(kDir));
667  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
668            file_system.FileExists(kChild));
669
670  // The changes applied by ApplyRemoteChange should not be recorded in
671  // the change tracker.
672  urls.clear();
673  file_system.GetChangedURLsInTracker(&urls);
674  EXPECT_TRUE(urls.empty());
675
676  // The quota usage data must have reflected the deletion.
677  EXPECT_EQ(storage::kQuotaStatusOk,
678            file_system.GetUsageAndQuota(&new_usage, &quota));
679  EXPECT_EQ(new_usage, initial_usage);
680
681  sync_context_->ShutdownOnUIThread();
682  sync_context_ = NULL;
683  file_system.TearDown();
684}
685
686TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForDeletion_ForRoot) {
687  CannedSyncableFileSystem file_system(GURL(kOrigin1),
688                                       in_memory_env_.get(),
689                                       io_task_runner_.get(),
690                                       file_task_runner_.get());
691  file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
692
693  sync_context_ = new LocalFileSyncContext(
694      dir_.path(), in_memory_env_.get(),
695      ui_task_runner_.get(), io_task_runner_.get());
696  ASSERT_EQ(SYNC_STATUS_OK,
697            file_system.MaybeInitializeFileSystemContext(sync_context_.get()));
698  ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem());
699
700  // Record the initial usage (likely 0).
701  int64 initial_usage = -1;
702  int64 quota = -1;
703  EXPECT_EQ(storage::kQuotaStatusOk,
704            file_system.GetUsageAndQuota(&initial_usage, &quota));
705
706  // Create a file and directory in the file_system.
707  const FileSystemURL kFile(file_system.URL("file"));
708  const FileSystemURL kDir(file_system.URL("dir"));
709  const FileSystemURL kChild(file_system.URL("dir/child"));
710
711  EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile));
712  EXPECT_EQ(base::File::FILE_OK, file_system.CreateDirectory(kDir));
713  EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kChild));
714
715  // At this point the usage must be greater than the initial usage.
716  int64 new_usage = -1;
717  EXPECT_EQ(storage::kQuotaStatusOk,
718            file_system.GetUsageAndQuota(&new_usage, &quota));
719  EXPECT_GT(new_usage, initial_usage);
720
721  const FileSystemURL kRoot(file_system.URL(""));
722
723  // Now let's apply remote deletion changes for the root.
724  FileChange change(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_DIRECTORY);
725  EXPECT_EQ(SYNC_STATUS_OK,
726            ApplyRemoteChange(file_system.file_system_context(),
727                              change, base::FilePath(), kRoot,
728                              SYNC_FILE_TYPE_DIRECTORY));
729
730  // Check the directory/files are deleted successfully.
731  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
732            file_system.FileExists(kFile));
733  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
734            file_system.DirectoryExists(kDir));
735  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
736            file_system.FileExists(kChild));
737
738  // All changes made for the previous creation must have been also reset.
739  FileSystemURLSet urls;
740  file_system.GetChangedURLsInTracker(&urls);
741  EXPECT_TRUE(urls.empty());
742
743  // The quota usage data must have reflected the deletion.
744  EXPECT_EQ(storage::kQuotaStatusOk,
745            file_system.GetUsageAndQuota(&new_usage, &quota));
746  EXPECT_EQ(new_usage, initial_usage);
747
748  sync_context_->ShutdownOnUIThread();
749  sync_context_ = NULL;
750  file_system.TearDown();
751}
752
753TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForAddOrUpdate) {
754  base::ScopedTempDir temp_dir;
755  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
756
757  CannedSyncableFileSystem file_system(GURL(kOrigin1),
758                                       in_memory_env_.get(),
759                                       io_task_runner_.get(),
760                                       file_task_runner_.get());
761  file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
762
763  sync_context_ = new LocalFileSyncContext(
764      dir_.path(), in_memory_env_.get(),
765      ui_task_runner_.get(), io_task_runner_.get());
766  ASSERT_EQ(SYNC_STATUS_OK,
767            file_system.MaybeInitializeFileSystemContext(sync_context_.get()));
768  ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem());
769
770  const FileSystemURL kFile1(file_system.URL("file1"));
771  const FileSystemURL kFile2(file_system.URL("file2"));
772  const FileSystemURL kDir(file_system.URL("dir"));
773
774  const char kTestFileData0[] = "0123456789";
775  const char kTestFileData1[] = "Lorem ipsum!";
776  const char kTestFileData2[] = "This is sample test data.";
777
778  // Create kFile1 and populate it with kTestFileData0.
779  EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile1));
780  EXPECT_EQ(static_cast<int64>(arraysize(kTestFileData0) - 1),
781            file_system.WriteString(kFile1, kTestFileData0));
782
783  // kFile2 and kDir are not there yet.
784  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
785            file_system.FileExists(kFile2));
786  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
787            file_system.DirectoryExists(kDir));
788
789  // file_system's change tracker must have recorded the creation.
790  FileSystemURLSet urls;
791  file_system.GetChangedURLsInTracker(&urls);
792  ASSERT_EQ(1U, urls.size());
793  EXPECT_TRUE(ContainsKey(urls, kFile1));
794  file_system.ClearChangeForURLInTracker(*urls.begin());
795
796  // Prepare temporary files which represent the remote file data.
797  const base::FilePath kFilePath1(temp_dir.path().Append(FPL("file1")));
798  const base::FilePath kFilePath2(temp_dir.path().Append(FPL("file2")));
799
800  ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1) - 1),
801            base::WriteFile(kFilePath1, kTestFileData1,
802                            arraysize(kTestFileData1) - 1));
803  ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2) - 1),
804            base::WriteFile(kFilePath2, kTestFileData2,
805                            arraysize(kTestFileData2) - 1));
806
807  // Record the usage.
808  int64 usage = -1, new_usage = -1;
809  int64 quota = -1;
810  EXPECT_EQ(storage::kQuotaStatusOk,
811            file_system.GetUsageAndQuota(&usage, &quota));
812
813  // Here in the local filesystem we have:
814  //  * kFile1 with kTestFileData0
815  //
816  // In the remote side let's assume we have:
817  //  * kFile1 with kTestFileData1
818  //  * kFile2 with kTestFileData2
819  //  * kDir
820  //
821  // By calling ApplyChange's:
822  //  * kFile1 will be updated to have kTestFileData1
823  //  * kFile2 will be created
824  //  * kDir will be created
825
826  // Apply the remote change to kFile1 (which will update the file).
827  FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
828                    SYNC_FILE_TYPE_FILE);
829  EXPECT_EQ(SYNC_STATUS_OK,
830            ApplyRemoteChange(file_system.file_system_context(),
831                              change, kFilePath1, kFile1,
832                              SYNC_FILE_TYPE_FILE));
833
834  // Check if the usage has been increased by (kTestFileData1 - kTestFileData0).
835  const int updated_size =
836      arraysize(kTestFileData1) - arraysize(kTestFileData0);
837  EXPECT_EQ(storage::kQuotaStatusOk,
838            file_system.GetUsageAndQuota(&new_usage, &quota));
839  EXPECT_EQ(updated_size, new_usage - usage);
840
841  // Apply remote changes to kFile2 and kDir (should create a file and
842  // directory respectively).
843  // They are non-existent yet so their expected file type (the last
844  // parameter of ApplyRemoteChange) are
845  // SYNC_FILE_TYPE_UNKNOWN.
846  change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
847                      SYNC_FILE_TYPE_FILE);
848  EXPECT_EQ(SYNC_STATUS_OK,
849            ApplyRemoteChange(file_system.file_system_context(),
850                              change, kFilePath2, kFile2,
851                              SYNC_FILE_TYPE_UNKNOWN));
852
853  change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
854                      SYNC_FILE_TYPE_DIRECTORY);
855  EXPECT_EQ(SYNC_STATUS_OK,
856            ApplyRemoteChange(file_system.file_system_context(),
857                              change, base::FilePath(), kDir,
858                              SYNC_FILE_TYPE_UNKNOWN));
859
860  // Calling ApplyRemoteChange with different file type should be handled as
861  // overwrite.
862  change =
863      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE);
864  EXPECT_EQ(SYNC_STATUS_OK,
865            ApplyRemoteChange(file_system.file_system_context(),
866                              change,
867                              kFilePath1,
868                              kDir,
869                              SYNC_FILE_TYPE_DIRECTORY));
870  EXPECT_EQ(base::File::FILE_OK, file_system.FileExists(kDir));
871
872  change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
873                      SYNC_FILE_TYPE_DIRECTORY);
874  EXPECT_EQ(SYNC_STATUS_OK,
875            ApplyRemoteChange(file_system.file_system_context(),
876                              change,
877                              kFilePath1,
878                              kDir,
879                              SYNC_FILE_TYPE_FILE));
880
881  // Creating a file/directory must have increased the usage more than
882  // the size of kTestFileData2.
883  new_usage = usage;
884  EXPECT_EQ(storage::kQuotaStatusOk,
885            file_system.GetUsageAndQuota(&new_usage, &quota));
886  EXPECT_GT(new_usage,
887            static_cast<int64>(usage + arraysize(kTestFileData2) - 1));
888
889  // The changes applied by ApplyRemoteChange should not be recorded in
890  // the change tracker.
891  urls.clear();
892  file_system.GetChangedURLsInTracker(&urls);
893  EXPECT_TRUE(urls.empty());
894
895  // Make sure all three files/directory exist.
896  EXPECT_EQ(base::File::FILE_OK, file_system.FileExists(kFile1));
897  EXPECT_EQ(base::File::FILE_OK, file_system.FileExists(kFile2));
898  EXPECT_EQ(base::File::FILE_OK, file_system.DirectoryExists(kDir));
899
900  sync_context_->ShutdownOnUIThread();
901  file_system.TearDown();
902}
903
904TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForAddOrUpdate_NoParent) {
905  base::ScopedTempDir temp_dir;
906  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
907
908  CannedSyncableFileSystem file_system(GURL(kOrigin1),
909                                       in_memory_env_.get(),
910                                       io_task_runner_.get(),
911                                       file_task_runner_.get());
912  file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
913
914  sync_context_ = new LocalFileSyncContext(
915      dir_.path(), in_memory_env_.get(),
916      ui_task_runner_.get(), io_task_runner_.get());
917  ASSERT_EQ(SYNC_STATUS_OK,
918            file_system.MaybeInitializeFileSystemContext(sync_context_.get()));
919  ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem());
920
921  const char kTestFileData[] = "Lorem ipsum!";
922  const FileSystemURL kDir(file_system.URL("dir"));
923  const FileSystemURL kFile(file_system.URL("dir/file"));
924
925  // Either kDir or kFile not exist yet.
926  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, file_system.FileExists(kDir));
927  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, file_system.FileExists(kFile));
928
929  // Prepare a temporary file which represents remote file data.
930  const base::FilePath kFilePath(temp_dir.path().Append(FPL("file")));
931  ASSERT_EQ(static_cast<int>(arraysize(kTestFileData) - 1),
932            base::WriteFile(kFilePath, kTestFileData,
933                            arraysize(kTestFileData) - 1));
934
935  // Calling ApplyChange's with kFilePath should create
936  // kFile along with kDir.
937  FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
938                    SYNC_FILE_TYPE_FILE);
939  EXPECT_EQ(SYNC_STATUS_OK,
940            ApplyRemoteChange(file_system.file_system_context(),
941                              change, kFilePath, kFile,
942                              SYNC_FILE_TYPE_UNKNOWN));
943
944  // The changes applied by ApplyRemoteChange should not be recorded in
945  // the change tracker.
946  FileSystemURLSet urls;
947  urls.clear();
948  file_system.GetChangedURLsInTracker(&urls);
949  EXPECT_TRUE(urls.empty());
950
951  // Make sure kDir and kFile are created by ApplyRemoteChange.
952  EXPECT_EQ(base::File::FILE_OK, file_system.FileExists(kFile));
953  EXPECT_EQ(base::File::FILE_OK, file_system.DirectoryExists(kDir));
954
955  sync_context_->ShutdownOnUIThread();
956  file_system.TearDown();
957}
958
959}  // namespace sync_file_system
960