local_file_sync_context_unittest.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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
35using content::BrowserThread;
36using fileapi::FileSystemContext;
37using fileapi::FileSystemURL;
38using 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
46namespace sync_file_system {
47
48namespace {
49const char kOrigin1[] = "http://example.com";
50const char kOrigin2[] = "http://chromium.org";
51}
52
53class 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
368TEST_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
376TEST_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
419TEST_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
506TEST_F(LocalFileSyncContextTest, PrepareSync_SyncSuccess_Exclusive) {
507  PrepareForSync_Basic(LocalFileSyncContext::SYNC_EXCLUSIVE,
508                       SYNC_STATUS_OK);
509}
510
511TEST_F(LocalFileSyncContextTest, PrepareSync_SyncSuccess_Snapshot) {
512  PrepareForSync_Basic(LocalFileSyncContext::SYNC_SNAPSHOT,
513                       SYNC_STATUS_OK);
514}
515
516TEST_F(LocalFileSyncContextTest, PrepareSync_SyncFailure_Exclusive) {
517  PrepareForSync_Basic(LocalFileSyncContext::SYNC_EXCLUSIVE,
518                       SYNC_STATUS_FAILED);
519}
520
521TEST_F(LocalFileSyncContextTest, PrepareSync_SyncFailure_Snapshot) {
522  PrepareForSync_Basic(LocalFileSyncContext::SYNC_SNAPSHOT,
523                       SYNC_STATUS_FAILED);
524}
525
526TEST_F(LocalFileSyncContextTest, PrepareSync_WriteDuringSync_Exclusive) {
527  PrepareForSync_WriteDuringSync(LocalFileSyncContext::SYNC_EXCLUSIVE);
528}
529
530TEST_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.
538TEST_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
601TEST_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
689TEST_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
756TEST_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            base::WriteFile(kFilePath1, kTestFileData1,
805                            arraysize(kTestFileData1) - 1));
806  ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2) - 1),
807            base::WriteFile(kFilePath2, kTestFileData2,
808                            arraysize(kTestFileData2) - 1));
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
907TEST_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            base::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
963