Home | History | Annotate | Download | only in local
      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 
     34 using content::BrowserThread;
     35 using fileapi::FileSystemURL;
     36 using ::testing::_;
     37 using ::testing::AtLeast;
     38 using ::testing::InvokeWithoutArgs;
     39 using ::testing::StrictMock;
     40 
     41 namespace sync_file_system {
     42 
     43 namespace {
     44 
     45 const char kOrigin[] = "http://example.com";
     46 
     47 void 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 
     62 void 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 
     74 void 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 
     86 ACTION_P(MockStatusCallback, status) {
     87   base::MessageLoopProxy::current()->PostTask(
     88       FROM_HERE, base::Bind(arg4, status));
     89 }
     90 
     91 ACTION_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 
     99 class 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.
    194 TEST_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 
    255 TEST_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 
    281 TEST_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 
    315 TEST_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 
    369 TEST_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 
    406 TEST_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 
    435 TEST_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 
    485 TEST_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 
    512 TEST_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 
    566 class 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 
    590 TEST_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 
    653 TEST_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