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