local_file_sync_service_unittest.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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 <vector>
6
7#include "base/basictypes.h"
8#include "base/bind.h"
9#include "base/file_util.h"
10#include "base/location.h"
11#include "base/run_loop.h"
12#include "base/stl_util.h"
13#include "base/thread_task_runner_handle.h"
14#include "base/threading/thread.h"
15#include "chrome/browser/sync_file_system/file_change.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/local_file_sync_context.h"
19#include "chrome/browser/sync_file_system/local/local_file_sync_service.h"
20#include "chrome/browser/sync_file_system/local/local_file_sync_status.h"
21#include "chrome/browser/sync_file_system/local/mock_sync_status_observer.h"
22#include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
23#include "chrome/browser/sync_file_system/mock_local_change_processor.h"
24#include "chrome/browser/sync_file_system/sync_file_metadata.h"
25#include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
26#include "chrome/browser/sync_file_system/sync_status_code.h"
27#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
28#include "chrome/test/base/testing_profile.h"
29#include "content/public/browser/browser_thread.h"
30#include "content/public/test/test_browser_thread_bundle.h"
31#include "content/public/test/test_utils.h"
32#include "testing/gmock/include/gmock/gmock.h"
33#include "testing/gtest/include/gtest/gtest.h"
34#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
35#include "third_party/leveldatabase/src/include/leveldb/env.h"
36#include "webkit/browser/fileapi/file_system_context.h"
37
38using content::BrowserThread;
39using fileapi::FileSystemURL;
40using ::testing::_;
41using ::testing::AtLeast;
42using ::testing::InvokeWithoutArgs;
43using ::testing::StrictMock;
44
45namespace sync_file_system {
46
47namespace {
48
49const char kOrigin[] = "http://example.com";
50
51void DidPrepareForProcessRemoteChange(const tracked_objects::Location& where,
52                                      const base::Closure& oncompleted,
53                                      SyncStatusCode expected_status,
54                                      const SyncFileMetadata& expected_metadata,
55                                      SyncStatusCode status,
56                                      const SyncFileMetadata& metadata,
57                                      const FileChangeList& changes) {
58  SCOPED_TRACE(testing::Message() << where.ToString());
59  ASSERT_EQ(expected_status, status);
60  ASSERT_EQ(expected_metadata.file_type, metadata.file_type);
61  ASSERT_EQ(expected_metadata.size, metadata.size);
62  ASSERT_TRUE(changes.empty());
63  oncompleted.Run();
64}
65
66void OnSyncCompleted(const tracked_objects::Location& where,
67                     const base::Closure& oncompleted,
68                     SyncStatusCode expected_status,
69                     const FileSystemURL& expected_url,
70                     SyncStatusCode status,
71                     const FileSystemURL& url) {
72  SCOPED_TRACE(testing::Message() << where.ToString());
73  ASSERT_EQ(expected_status, status);
74  ASSERT_EQ(expected_url, url);
75  oncompleted.Run();
76}
77
78void OnGetFileMetadata(const tracked_objects::Location& where,
79                       const base::Closure& oncompleted,
80                       SyncStatusCode* status_out,
81                       SyncFileMetadata* metadata_out,
82                       SyncStatusCode status,
83                       const SyncFileMetadata& metadata) {
84  SCOPED_TRACE(testing::Message() << where.ToString());
85  *status_out = status;
86  *metadata_out = metadata;
87  oncompleted.Run();
88}
89
90ACTION_P(MockStatusCallback, status) {
91  base::ThreadTaskRunnerHandle::Get()->PostTask(
92      FROM_HERE, base::Bind(arg4, status));
93}
94
95ACTION_P2(MockStatusCallbackAndRecordChange, status, changes) {
96  base::ThreadTaskRunnerHandle::Get()->PostTask(
97      FROM_HERE, base::Bind(arg4, status));
98  changes->push_back(arg0);
99}
100
101}  // namespace
102
103class LocalFileSyncServiceTest
104    : public testing::Test,
105      public LocalFileSyncService::Observer {
106 protected:
107  LocalFileSyncServiceTest()
108      : thread_bundle_(content::TestBrowserThreadBundle::REAL_FILE_THREAD |
109                       content::TestBrowserThreadBundle::REAL_IO_THREAD),
110        num_changes_(0) {}
111
112  virtual void SetUp() OVERRIDE {
113    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
114    in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
115
116    file_system_.reset(new CannedSyncableFileSystem(
117        GURL(kOrigin),
118        in_memory_env_.get(),
119        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
120        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)));
121
122    local_service_ = LocalFileSyncService::CreateForTesting(
123        &profile_, in_memory_env_.get());
124
125    file_system_->SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
126
127    base::RunLoop run_loop;
128    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
129    local_service_->MaybeInitializeFileSystemContext(
130        GURL(kOrigin), file_system_->file_system_context(),
131        AssignAndQuitCallback(&run_loop, &status));
132    run_loop.Run();
133
134    local_service_->AddChangeObserver(this);
135
136    EXPECT_EQ(base::File::FILE_OK, file_system_->OpenFileSystem());
137
138    file_system_->backend()->sync_context()->
139        set_mock_notify_changes_duration_in_sec(0);
140  }
141
142  virtual void TearDown() OVERRIDE {
143    local_service_->Shutdown();
144    file_system_->TearDown();
145    RevokeSyncableFileSystem();
146    content::RunAllPendingInMessageLoop(BrowserThread::FILE);
147    content::RunAllPendingInMessageLoop(BrowserThread::IO);
148  }
149
150  // LocalChangeObserver overrides.
151  virtual void OnLocalChangeAvailable(int64 num_changes) OVERRIDE {
152    num_changes_ = num_changes;
153  }
154
155  void PrepareForProcessRemoteChange(
156      const FileSystemURL& url,
157      const tracked_objects::Location& where,
158      SyncStatusCode expected_status,
159      const SyncFileMetadata& expected_metadata) {
160    base::RunLoop run_loop;
161    local_service_->PrepareForProcessRemoteChange(
162        url,
163        base::Bind(&DidPrepareForProcessRemoteChange,
164                   where,
165                   run_loop.QuitClosure(),
166                   expected_status,
167                   expected_metadata));
168    run_loop.Run();
169  }
170
171  SyncStatusCode ApplyRemoteChange(const FileChange& change,
172                                   const base::FilePath& local_path,
173                                   const FileSystemURL& url) {
174    SyncStatusCode sync_status = SYNC_STATUS_UNKNOWN;
175    {
176      base::RunLoop run_loop;
177      local_service_->ApplyRemoteChange(
178          change, local_path, url,
179          AssignAndQuitCallback(&run_loop, &sync_status));
180      run_loop.Run();
181    }
182    {
183      base::RunLoop run_loop;
184      local_service_->FinalizeRemoteSync(
185          url,
186          sync_status == SYNC_STATUS_OK,
187          run_loop.QuitClosure());
188      run_loop.Run();
189    }
190    return sync_status;
191  }
192
193  int64 GetNumChangesInTracker() const {
194    return file_system_->backend()->change_tracker()->num_changes();
195  }
196
197  content::TestBrowserThreadBundle thread_bundle_;
198
199  base::ScopedTempDir temp_dir_;
200  scoped_ptr<leveldb::Env> in_memory_env_;
201  TestingProfile profile_;
202
203  scoped_ptr<CannedSyncableFileSystem> file_system_;
204  scoped_ptr<LocalFileSyncService> local_service_;
205
206  int64 num_changes_;
207};
208
209// More complete tests for PrepareForProcessRemoteChange and ApplyRemoteChange
210// are also in content_unittest:LocalFileSyncContextTest.
211TEST_F(LocalFileSyncServiceTest, RemoteSyncStepsSimple) {
212  const FileSystemURL kFile(file_system_->URL("file"));
213  const FileSystemURL kDir(file_system_->URL("dir"));
214  const char kTestFileData[] = "0123456789";
215  const int kTestFileDataSize = static_cast<int>(arraysize(kTestFileData) - 1);
216
217  base::FilePath local_path;
218  ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &local_path));
219  ASSERT_EQ(kTestFileDataSize,
220            base::WriteFile(local_path, kTestFileData, kTestFileDataSize));
221
222  // Run PrepareForProcessRemoteChange for kFile.
223  SyncFileMetadata expected_metadata;
224  expected_metadata.file_type = SYNC_FILE_TYPE_UNKNOWN;
225  expected_metadata.size = 0;
226  PrepareForProcessRemoteChange(kFile, FROM_HERE,
227                                SYNC_STATUS_OK,
228                                expected_metadata);
229
230  // Run ApplyRemoteChange for kFile.
231  FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
232                    SYNC_FILE_TYPE_FILE);
233  EXPECT_EQ(SYNC_STATUS_OK,
234            ApplyRemoteChange(change, local_path, kFile));
235
236  // Verify the file is synced.
237  EXPECT_EQ(base::File::FILE_OK,
238            file_system_->VerifyFile(kFile, kTestFileData));
239
240  // Run PrepareForProcessRemoteChange for kDir.
241  PrepareForProcessRemoteChange(kDir, FROM_HERE,
242                                SYNC_STATUS_OK,
243                                expected_metadata);
244
245  // Run ApplyRemoteChange for kDir.
246  change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
247                      SYNC_FILE_TYPE_DIRECTORY);
248  EXPECT_EQ(SYNC_STATUS_OK,
249            ApplyRemoteChange(change, base::FilePath(), kDir));
250
251  // Verify the directory.
252  EXPECT_EQ(base::File::FILE_OK,
253            file_system_->DirectoryExists(kDir));
254
255  // Run PrepareForProcessRemoteChange and ApplyRemoteChange for
256  // kDir once again for deletion.
257  expected_metadata.file_type = SYNC_FILE_TYPE_DIRECTORY;
258  expected_metadata.size = 0;
259  PrepareForProcessRemoteChange(kDir, FROM_HERE,
260                                SYNC_STATUS_OK,
261                                expected_metadata);
262
263  change = FileChange(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_UNKNOWN);
264  EXPECT_EQ(SYNC_STATUS_OK, ApplyRemoteChange(change, base::FilePath(), kDir));
265
266  // Now the directory must have deleted.
267  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND,
268            file_system_->DirectoryExists(kDir));
269}
270
271TEST_F(LocalFileSyncServiceTest, LocalChangeObserver) {
272  const FileSystemURL kFile(file_system_->URL("file"));
273  const FileSystemURL kDir(file_system_->URL("dir"));
274  const char kTestFileData[] = "0123456789";
275  const int kTestFileDataSize = static_cast<int>(arraysize(kTestFileData) - 1);
276
277  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kFile));
278
279  EXPECT_EQ(1, num_changes_);
280
281  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateDirectory(kDir));
282  EXPECT_EQ(kTestFileDataSize,
283            file_system_->WriteString(kFile, kTestFileData));
284
285  EXPECT_EQ(2, num_changes_);
286}
287
288#if defined(OS_WIN)
289// Flaky: http://crbug.com/171487
290#define MAYBE_LocalChangeObserverMultipleContexts\
291    DISABLED_LocalChangeObserverMultipleContexts
292#else
293#define MAYBE_LocalChangeObserverMultipleContexts\
294    LocalChangeObserverMultipleContexts
295#endif
296
297TEST_F(LocalFileSyncServiceTest, MAYBE_LocalChangeObserverMultipleContexts) {
298  const char kOrigin2[] = "http://foo";
299  CannedSyncableFileSystem file_system2(
300      GURL(kOrigin2),
301      in_memory_env_.get(),
302      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
303      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
304  file_system2.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
305
306  base::RunLoop run_loop;
307  SyncStatusCode status = SYNC_STATUS_UNKNOWN;
308  local_service_->MaybeInitializeFileSystemContext(
309      GURL(kOrigin2), file_system2.file_system_context(),
310      AssignAndQuitCallback(&run_loop, &status));
311  run_loop.Run();
312
313  EXPECT_EQ(base::File::FILE_OK, file_system2.OpenFileSystem());
314  file_system2.backend()->sync_context()->
315      set_mock_notify_changes_duration_in_sec(0);
316
317  const FileSystemURL kFile1(file_system_->URL("file1"));
318  const FileSystemURL kFile2(file_system_->URL("file2"));
319  const FileSystemURL kFile3(file_system2.URL("file3"));
320  const FileSystemURL kFile4(file_system2.URL("file4"));
321
322  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kFile1));
323  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kFile2));
324  EXPECT_EQ(base::File::FILE_OK, file_system2.CreateFile(kFile3));
325  EXPECT_EQ(base::File::FILE_OK, file_system2.CreateFile(kFile4));
326
327  EXPECT_EQ(4, num_changes_);
328
329  file_system2.TearDown();
330}
331
332TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_CreateFile) {
333  const FileSystemURL kFile(file_system_->URL("foo"));
334  const char kTestFileData[] = "0123456789";
335  const int kTestFileDataSize = static_cast<int>(arraysize(kTestFileData) - 1);
336
337  base::RunLoop run_loop;
338
339  // We should get called OnSyncEnabled and OnWriteEnabled on kFile.
340  // (OnWriteEnabled is called because we release lock before returning
341  // from ApplyLocalChange)
342  StrictMock<MockSyncStatusObserver> status_observer;
343  EXPECT_CALL(status_observer, OnSyncEnabled(kFile)).Times(AtLeast(1));
344  EXPECT_CALL(status_observer, OnWriteEnabled(kFile)).Times(AtLeast(0));
345  file_system_->AddSyncStatusObserver(&status_observer);
346
347  // Creates and writes into a file.
348  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kFile));
349  EXPECT_EQ(kTestFileDataSize,
350            file_system_->WriteString(kFile, std::string(kTestFileData)));
351
352  // Retrieve the expected file info.
353  base::File::Info info;
354  base::FilePath platform_path;
355  EXPECT_EQ(base::File::FILE_OK,
356            file_system_->GetMetadataAndPlatformPath(
357                kFile, &info, &platform_path));
358
359  ASSERT_FALSE(info.is_directory);
360  ASSERT_EQ(kTestFileDataSize, info.size);
361
362  SyncFileMetadata metadata;
363  metadata.file_type = SYNC_FILE_TYPE_FILE;
364  metadata.size = info.size;
365  metadata.last_modified = info.last_modified;
366
367  // The local_change_processor's ApplyLocalChange should be called once
368  // with ADD_OR_UPDATE change for TYPE_FILE.
369  StrictMock<MockLocalChangeProcessor> local_change_processor;
370  const FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
371                          SYNC_FILE_TYPE_FILE);
372  EXPECT_CALL(local_change_processor,
373              ApplyLocalChange(change, _, metadata, kFile, _))
374      .WillOnce(MockStatusCallback(SYNC_STATUS_OK));
375
376  local_service_->SetLocalChangeProcessor(&local_change_processor);
377  local_service_->ProcessLocalChange(
378      base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(),
379                 SYNC_STATUS_OK, kFile));
380
381  run_loop.Run();
382
383  file_system_->RemoveSyncStatusObserver(&status_observer);
384
385  EXPECT_EQ(0, GetNumChangesInTracker());
386}
387
388TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_CreateAndRemoveFile) {
389  const FileSystemURL kFile(file_system_->URL("foo"));
390
391  base::RunLoop run_loop;
392
393  // We should get called OnSyncEnabled and possibly OnWriteEnabled (depends
394  // on timing) on kFile.
395  StrictMock<MockSyncStatusObserver> status_observer;
396  EXPECT_CALL(status_observer, OnSyncEnabled(kFile)).Times(AtLeast(1));
397  EXPECT_CALL(status_observer, OnWriteEnabled(kFile)).Times(AtLeast(0));
398  file_system_->AddSyncStatusObserver(&status_observer);
399
400  // Creates and then deletes a file.
401  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kFile));
402  EXPECT_EQ(base::File::FILE_OK, file_system_->Remove(kFile, false));
403
404  // The local_change_processor's ApplyLocalChange should be called once
405  // with DELETE change for TYPE_FILE.
406  // The file will NOT exist in the remote side and the processor might
407  // return SYNC_FILE_ERROR_NOT_FOUND (as mocked).
408  StrictMock<MockLocalChangeProcessor> local_change_processor;
409  const FileChange change(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_FILE);
410  EXPECT_CALL(local_change_processor, ApplyLocalChange(change, _, _, kFile, _))
411      .WillOnce(MockStatusCallback(SYNC_FILE_ERROR_NOT_FOUND));
412
413  // The sync should succeed anyway.
414  local_service_->SetLocalChangeProcessor(&local_change_processor);
415  local_service_->ProcessLocalChange(
416      base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(),
417                 SYNC_STATUS_OK, kFile));
418
419  run_loop.Run();
420
421  file_system_->RemoveSyncStatusObserver(&status_observer);
422
423  EXPECT_EQ(0, GetNumChangesInTracker());
424}
425
426TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_CreateAndRemoveDirectory) {
427  const FileSystemURL kDir(file_system_->URL("foo"));
428
429  base::RunLoop run_loop;
430
431  // OnSyncEnabled is expected to be called at least or more than once.
432  StrictMock<MockSyncStatusObserver> status_observer;
433  EXPECT_CALL(status_observer, OnSyncEnabled(kDir)).Times(AtLeast(1));
434  file_system_->AddSyncStatusObserver(&status_observer);
435
436  // Creates and then deletes a directory.
437  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateDirectory(kDir));
438  EXPECT_EQ(base::File::FILE_OK, file_system_->Remove(kDir, false));
439
440  // The local_change_processor's ApplyLocalChange should never be called.
441  StrictMock<MockLocalChangeProcessor> local_change_processor;
442
443  local_service_->SetLocalChangeProcessor(&local_change_processor);
444  local_service_->ProcessLocalChange(
445      base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(),
446                 SYNC_STATUS_NO_CHANGE_TO_SYNC, FileSystemURL()));
447
448  run_loop.Run();
449
450  file_system_->RemoveSyncStatusObserver(&status_observer);
451
452  EXPECT_EQ(0, GetNumChangesInTracker());
453}
454
455TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_MultipleChanges) {
456  const FileSystemURL kPath(file_system_->URL("foo"));
457  const FileSystemURL kOther(file_system_->URL("bar"));
458
459  base::RunLoop run_loop;
460
461  // We should get called OnSyncEnabled and OnWriteEnabled on kPath and
462  // OnSyncEnabled on kOther.
463  StrictMock<MockSyncStatusObserver> status_observer;
464  EXPECT_CALL(status_observer, OnSyncEnabled(kPath)).Times(AtLeast(1));
465  EXPECT_CALL(status_observer, OnSyncEnabled(kOther)).Times(AtLeast(1));
466  file_system_->AddSyncStatusObserver(&status_observer);
467
468  // Creates a file, delete the file and creates a directory with the same
469  // name.
470  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kPath));
471  EXPECT_EQ(base::File::FILE_OK, file_system_->Remove(kPath, false));
472  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateDirectory(kPath));
473
474  // Creates one more file.
475  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kOther));
476
477  // The local_change_processor's ApplyLocalChange will be called
478  // twice for FILE_TYPE and FILE_DIRECTORY.
479  StrictMock<MockLocalChangeProcessor> local_change_processor;
480  std::vector<FileChange> changes;
481  EXPECT_CALL(local_change_processor, ApplyLocalChange(_, _, _, kPath, _))
482      .Times(2)
483      .WillOnce(MockStatusCallbackAndRecordChange(SYNC_STATUS_OK, &changes))
484      .WillOnce(MockStatusCallbackAndRecordChange(SYNC_STATUS_OK, &changes));
485  local_service_->SetLocalChangeProcessor(&local_change_processor);
486
487  // OnWriteEnabled will be notified on kPath (in multi-threaded this
488  // could be delayed, so AtLeast(0)).
489  EXPECT_CALL(status_observer, OnWriteEnabled(kPath)).Times(AtLeast(0));
490
491  local_service_->ProcessLocalChange(
492      base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(),
493                 SYNC_STATUS_OK, kPath));
494
495  run_loop.Run();
496
497  EXPECT_EQ(2U, changes.size());
498  EXPECT_EQ(FileChange(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_FILE),
499            changes[0]);
500  EXPECT_EQ(FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
501                       SYNC_FILE_TYPE_DIRECTORY),
502            changes[1]);
503
504  file_system_->RemoveSyncStatusObserver(&status_observer);
505
506  // We have one more change for kOther.
507  EXPECT_EQ(1, GetNumChangesInTracker());
508}
509
510TEST_F(LocalFileSyncServiceTest, ProcessLocalChange_GetLocalMetadata) {
511  const FileSystemURL kURL(file_system_->URL("foo"));
512  const base::Time kTime = base::Time::FromDoubleT(333);
513  const int kSize = 555;
514
515  base::RunLoop run_loop;
516
517  // Creates a file.
518  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kURL));
519  EXPECT_EQ(base::File::FILE_OK, file_system_->TruncateFile(kURL, kSize));
520  EXPECT_EQ(base::File::FILE_OK,
521            file_system_->TouchFile(kURL, base::Time(), kTime));
522
523  SyncStatusCode status = SYNC_STATUS_UNKNOWN;
524  SyncFileMetadata metadata;
525  local_service_->GetLocalFileMetadata(
526      kURL,
527      base::Bind(&OnGetFileMetadata, FROM_HERE, run_loop.QuitClosure(),
528                 &status, &metadata));
529
530  run_loop.Run();
531
532  EXPECT_EQ(SYNC_STATUS_OK, status);
533  EXPECT_EQ(kTime, metadata.last_modified);
534  EXPECT_EQ(kSize, metadata.size);
535}
536
537TEST_F(LocalFileSyncServiceTest, RecordFakeChange) {
538  const FileSystemURL kURL(file_system_->URL("foo"));
539
540  // Create a file and reset the changes (as preparation).
541  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kURL));
542  file_system_->ClearChangeForURLInTracker(kURL);
543
544  EXPECT_EQ(0, GetNumChangesInTracker());
545
546  fileapi::FileSystemURLSet urlset;
547  file_system_->GetChangedURLsInTracker(&urlset);
548  EXPECT_TRUE(urlset.empty());
549
550  const FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
551                          SYNC_FILE_TYPE_FILE);
552
553  // Call RecordFakeLocalChange to add an ADD_OR_UPDATE change.
554  {
555    base::RunLoop run_loop;
556    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
557    local_service_->RecordFakeLocalChange(
558        kURL, change, AssignAndQuitCallback(&run_loop, &status));
559    run_loop.Run();
560    EXPECT_EQ(SYNC_STATUS_OK, status);
561  }
562
563  EXPECT_EQ(1, GetNumChangesInTracker());
564  file_system_->GetChangedURLsInTracker(&urlset);
565  EXPECT_EQ(1U, urlset.size());
566  EXPECT_TRUE(urlset.find(kURL) != urlset.end());
567
568  // Next local sync should pick up the recorded change.
569  StrictMock<MockLocalChangeProcessor> local_change_processor;
570  std::vector<FileChange> changes;
571  EXPECT_CALL(local_change_processor, ApplyLocalChange(_, _, _, kURL, _))
572      .WillOnce(MockStatusCallbackAndRecordChange(SYNC_STATUS_OK, &changes));
573  {
574    base::RunLoop run_loop;
575    local_service_->SetLocalChangeProcessor(&local_change_processor);
576    local_service_->ProcessLocalChange(
577        base::Bind(&OnSyncCompleted, FROM_HERE, run_loop.QuitClosure(),
578                   SYNC_STATUS_OK, kURL));
579    run_loop.Run();
580  }
581
582  EXPECT_EQ(1U, changes.size());
583  EXPECT_EQ(change, changes[0]);
584}
585
586// TODO(kinuko): Add tests for multiple file changes and multiple
587// FileSystemContexts.
588
589// Unit test for OriginChangeMap ---------------------------------------------
590
591class OriginChangeMapTest : public testing::Test {
592 protected:
593  OriginChangeMapTest() {}
594  virtual ~OriginChangeMapTest() {}
595
596  bool NextOriginToProcess(GURL* origin) {
597    return map_.NextOriginToProcess(origin);
598  }
599
600  int64 GetTotalChangeCount() const {
601    return map_.GetTotalChangeCount();
602  }
603
604  void SetOriginChangeCount(const GURL& origin, int64 changes) {
605    map_.SetOriginChangeCount(origin, changes);
606  }
607
608  void SetOriginEnabled(const GURL& origin, bool enabled) {
609    map_.SetOriginEnabled(origin, enabled);
610  }
611
612  LocalFileSyncService::OriginChangeMap map_;
613};
614
615TEST_F(OriginChangeMapTest, Basic) {
616  const GURL kOrigin1("chrome-extension://foo");
617  const GURL kOrigin2("chrome-extension://bar");
618  const GURL kOrigin3("chrome-extension://baz");
619
620  ASSERT_EQ(0, GetTotalChangeCount());
621
622  SetOriginChangeCount(kOrigin1, 1);
623  SetOriginChangeCount(kOrigin2, 2);
624
625  ASSERT_EQ(1 + 2, GetTotalChangeCount());
626
627  SetOriginChangeCount(kOrigin3, 4);
628
629  ASSERT_EQ(1 + 2 + 4, GetTotalChangeCount());
630
631  const GURL kOrigins[] = { kOrigin1, kOrigin2, kOrigin3 };
632  std::set<GURL> all_origins;
633  all_origins.insert(kOrigins, kOrigins + ARRAYSIZE_UNSAFE(kOrigins));
634
635  GURL origin;
636  while (!all_origins.empty()) {
637    ASSERT_TRUE(NextOriginToProcess(&origin));
638    ASSERT_TRUE(ContainsKey(all_origins, origin));
639    all_origins.erase(origin);
640  }
641
642  // Set kOrigin2's change count 0.
643  SetOriginChangeCount(kOrigin2, 0);
644  ASSERT_EQ(1 + 4, GetTotalChangeCount());
645
646  // kOrigin2 won't return this time.
647  all_origins.insert(kOrigin1);
648  all_origins.insert(kOrigin3);
649  while (!all_origins.empty()) {
650    ASSERT_TRUE(NextOriginToProcess(&origin));
651    ASSERT_TRUE(ContainsKey(all_origins, origin));
652    all_origins.erase(origin);
653  }
654
655  // Calling NextOriginToProcess() again will just return
656  // the same set of origins (as far as we don't change the
657  // change count).
658  all_origins.insert(kOrigin1);
659  all_origins.insert(kOrigin3);
660  while (!all_origins.empty()) {
661    ASSERT_TRUE(NextOriginToProcess(&origin));
662    ASSERT_TRUE(ContainsKey(all_origins, origin));
663    all_origins.erase(origin);
664  }
665
666  // Set kOrigin2's change count 8.
667  SetOriginChangeCount(kOrigin2, 8);
668  ASSERT_EQ(1 + 4 + 8, GetTotalChangeCount());
669
670  all_origins.insert(kOrigins, kOrigins + ARRAYSIZE_UNSAFE(kOrigins));
671  while (!all_origins.empty()) {
672    ASSERT_TRUE(NextOriginToProcess(&origin));
673    ASSERT_TRUE(ContainsKey(all_origins, origin));
674    all_origins.erase(origin);
675  }
676}
677
678TEST_F(OriginChangeMapTest, WithDisabled) {
679  const GURL kOrigin1("chrome-extension://foo");
680  const GURL kOrigin2("chrome-extension://bar");
681  const GURL kOrigin3("chrome-extension://baz");
682  const GURL kOrigins[] = { kOrigin1, kOrigin2, kOrigin3 };
683
684  ASSERT_EQ(0, GetTotalChangeCount());
685
686  SetOriginChangeCount(kOrigin1, 1);
687  SetOriginChangeCount(kOrigin2, 2);
688  SetOriginChangeCount(kOrigin3, 4);
689
690  ASSERT_EQ(1 + 2 + 4, GetTotalChangeCount());
691
692  std::set<GURL> all_origins;
693  all_origins.insert(kOrigins, kOrigins + ARRAYSIZE_UNSAFE(kOrigins));
694
695  GURL origin;
696  while (!all_origins.empty()) {
697    ASSERT_TRUE(NextOriginToProcess(&origin));
698    ASSERT_TRUE(ContainsKey(all_origins, origin));
699    all_origins.erase(origin);
700  }
701
702  SetOriginEnabled(kOrigin2, false);
703  ASSERT_EQ(1 + 4, GetTotalChangeCount());
704
705  // kOrigin2 won't return this time.
706  all_origins.insert(kOrigin1);
707  all_origins.insert(kOrigin3);
708  while (!all_origins.empty()) {
709    ASSERT_TRUE(NextOriginToProcess(&origin));
710    ASSERT_TRUE(ContainsKey(all_origins, origin));
711    all_origins.erase(origin);
712  }
713
714  // kOrigin1 and kOrigin2 are now disabled.
715  SetOriginEnabled(kOrigin1, false);
716  ASSERT_EQ(4, GetTotalChangeCount());
717
718  ASSERT_TRUE(NextOriginToProcess(&origin));
719  ASSERT_EQ(kOrigin3, origin);
720
721  // Re-enable kOrigin2.
722  SetOriginEnabled(kOrigin2, true);
723  ASSERT_EQ(2 + 4, GetTotalChangeCount());
724
725  // kOrigin1 won't return this time.
726  all_origins.insert(kOrigin2);
727  all_origins.insert(kOrigin3);
728  while (!all_origins.empty()) {
729    ASSERT_TRUE(NextOriginToProcess(&origin));
730    ASSERT_TRUE(ContainsKey(all_origins, origin));
731    all_origins.erase(origin);
732  }
733}
734
735}  // namespace sync_file_system
736