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