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