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