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