Home | History | Annotate | Download | only in drive
      1 // Copyright (c) 2012 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 "chrome/browser/chromeos/drive/sync_client.h"
      6 
      7 #include "base/file_util.h"
      8 #include "base/files/file_path.h"
      9 #include "base/files/scoped_temp_dir.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/prefs/testing_pref_service.h"
     12 #include "base/run_loop.h"
     13 #include "base/test/test_timeouts.h"
     14 #include "chrome/browser/chromeos/drive/change_list_loader.h"
     15 #include "chrome/browser/chromeos/drive/drive.pb.h"
     16 #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h"
     17 #include "chrome/browser/chromeos/drive/file_cache.h"
     18 #include "chrome/browser/chromeos/drive/file_system/operation_observer.h"
     19 #include "chrome/browser/chromeos/drive/job_scheduler.h"
     20 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
     21 #include "chrome/browser/chromeos/drive/resource_metadata.h"
     22 #include "chrome/browser/chromeos/drive/test_util.h"
     23 #include "chrome/browser/drive/fake_drive_service.h"
     24 #include "chrome/browser/google_apis/test_util.h"
     25 #include "content/public/test/test_browser_thread_bundle.h"
     26 #include "testing/gtest/include/gtest/gtest.h"
     27 
     28 namespace drive {
     29 namespace internal {
     30 
     31 namespace {
     32 
     33 // The content of files initially stored in the cache.
     34 const char kLocalContent[] = "Hello!";
     35 
     36 // The content of files stored in the service.
     37 const char kRemoteContent[] = "World!";
     38 
     39 // SyncClientTestDriveService will return GDATA_CANCELLED when a request is
     40 // made with the specified resource ID.
     41 class SyncClientTestDriveService : public ::drive::FakeDriveService {
     42  public:
     43   // FakeDriveService override:
     44   virtual google_apis::CancelCallback DownloadFile(
     45       const base::FilePath& local_cache_path,
     46       const std::string& resource_id,
     47       const google_apis::DownloadActionCallback& download_action_callback,
     48       const google_apis::GetContentCallback& get_content_callback,
     49       const google_apis::ProgressCallback& progress_callback) OVERRIDE {
     50     if (resource_id == resource_id_to_be_cancelled_) {
     51       base::MessageLoopProxy::current()->PostTask(
     52           FROM_HERE,
     53           base::Bind(download_action_callback,
     54                      google_apis::GDATA_CANCELLED,
     55                      base::FilePath()));
     56       return google_apis::CancelCallback();
     57     }
     58     return FakeDriveService::DownloadFile(local_cache_path,
     59                                           resource_id,
     60                                           download_action_callback,
     61                                           get_content_callback,
     62                                           progress_callback);
     63   }
     64 
     65   void set_resource_id_to_be_cancelled(const std::string& resource_id) {
     66     resource_id_to_be_cancelled_ = resource_id;
     67   }
     68 
     69  private:
     70   std::string resource_id_to_be_cancelled_;
     71 };
     72 
     73 class DummyOperationObserver : public file_system::OperationObserver {
     74   // OperationObserver override:
     75   virtual void OnDirectoryChangedByOperation(
     76       const base::FilePath& path) OVERRIDE {}
     77   virtual void OnCacheFileUploadNeededByOperation(
     78       const std::string& resource_id) OVERRIDE {}
     79 };
     80 
     81 }  // namespace
     82 
     83 class SyncClientTest : public testing::Test {
     84  public:
     85   virtual void SetUp() OVERRIDE {
     86     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     87 
     88     pref_service_.reset(new TestingPrefServiceSimple);
     89     test_util::RegisterDrivePrefs(pref_service_->registry());
     90 
     91     fake_network_change_notifier_.reset(
     92         new test_util::FakeNetworkChangeNotifier);
     93 
     94     drive_service_.reset(new SyncClientTestDriveService);
     95     drive_service_->LoadResourceListForWapi("gdata/empty_feed.json");
     96     drive_service_->LoadAccountMetadataForWapi(
     97         "gdata/account_metadata.json");
     98 
     99     scheduler_.reset(new JobScheduler(pref_service_.get(),
    100                                       drive_service_.get(),
    101                                       base::MessageLoopProxy::current().get()));
    102 
    103     metadata_storage_.reset(new ResourceMetadataStorage(
    104         temp_dir_.path(), base::MessageLoopProxy::current().get()));
    105     ASSERT_TRUE(metadata_storage_->Initialize());
    106 
    107     metadata_.reset(new internal::ResourceMetadata(
    108         metadata_storage_.get(), base::MessageLoopProxy::current()));
    109     ASSERT_EQ(FILE_ERROR_OK, metadata_->Initialize());
    110 
    111     cache_.reset(new FileCache(metadata_storage_.get(),
    112                                temp_dir_.path(),
    113                                base::MessageLoopProxy::current().get(),
    114                                NULL /* free_disk_space_getter */));
    115     ASSERT_TRUE(cache_->Initialize());
    116 
    117     ASSERT_NO_FATAL_FAILURE(SetUpTestData());
    118 
    119     sync_client_.reset(new SyncClient(base::MessageLoopProxy::current().get(),
    120                                       &observer_,
    121                                       scheduler_.get(),
    122                                       metadata_.get(),
    123                                       cache_.get(),
    124                                       temp_dir_.path()));
    125 
    126     // Disable delaying so that DoSyncLoop() starts immediately.
    127     sync_client_->set_delay_for_testing(base::TimeDelta::FromSeconds(0));
    128   }
    129 
    130   // Adds a file to the service root and |resource_ids_|.
    131   void AddFileEntry(const std::string& title) {
    132     google_apis::GDataErrorCode error = google_apis::GDATA_FILE_ERROR;
    133     scoped_ptr<google_apis::ResourceEntry> entry;
    134     drive_service_->AddNewFile(
    135         "text/plain",
    136         kRemoteContent,
    137         drive_service_->GetRootResourceId(),
    138         title,
    139         false,  // shared_with_me
    140         google_apis::test_util::CreateCopyResultCallback(&error, &entry));
    141     base::RunLoop().RunUntilIdle();
    142     ASSERT_EQ(google_apis::HTTP_CREATED, error);
    143     ASSERT_TRUE(entry);
    144     resource_ids_[title] = entry->resource_id();
    145   }
    146 
    147   // Sets up data for tests.
    148   void SetUpTestData() {
    149     // Prepare a temp file.
    150     base::FilePath temp_file;
    151     EXPECT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(),
    152                                                     &temp_file));
    153     ASSERT_TRUE(google_apis::test_util::WriteStringToFile(temp_file,
    154                                                           kLocalContent));
    155 
    156     // Prepare 3 pinned-but-not-present files.
    157     ASSERT_NO_FATAL_FAILURE(AddFileEntry("foo"));
    158     EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(resource_ids_["foo"]));
    159 
    160     ASSERT_NO_FATAL_FAILURE(AddFileEntry("bar"));
    161     EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(resource_ids_["bar"]));
    162 
    163     ASSERT_NO_FATAL_FAILURE(AddFileEntry("baz"));
    164     EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(resource_ids_["baz"]));
    165 
    166     // Prepare a pinned-and-fetched file.
    167     const std::string md5_fetched = "md5";
    168     ASSERT_NO_FATAL_FAILURE(AddFileEntry("fetched"));
    169     EXPECT_EQ(FILE_ERROR_OK,
    170               cache_->Store(resource_ids_["fetched"], md5_fetched,
    171                             temp_file, FileCache::FILE_OPERATION_COPY));
    172     EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(resource_ids_["fetched"]));
    173 
    174     // Prepare a pinned-and-fetched-and-dirty file.
    175     const std::string md5_dirty = "";  // Don't care.
    176     ASSERT_NO_FATAL_FAILURE(AddFileEntry("dirty"));
    177     EXPECT_EQ(FILE_ERROR_OK,
    178               cache_->Store(resource_ids_["dirty"], md5_dirty,
    179                             temp_file, FileCache::FILE_OPERATION_COPY));
    180     EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(resource_ids_["dirty"]));
    181     EXPECT_EQ(FILE_ERROR_OK, cache_->MarkDirty(resource_ids_["dirty"]));
    182 
    183     // Load data from the service to the metadata.
    184     FileError error = FILE_ERROR_FAILED;
    185     internal::ChangeListLoader change_list_loader(
    186         base::MessageLoopProxy::current().get(),
    187         metadata_.get(),
    188         scheduler_.get());
    189     change_list_loader.LoadIfNeeded(
    190         DirectoryFetchInfo(),
    191         google_apis::test_util::CreateCopyResultCallback(&error));
    192     base::RunLoop().RunUntilIdle();
    193     EXPECT_EQ(FILE_ERROR_OK, error);
    194   }
    195 
    196  protected:
    197   content::TestBrowserThreadBundle thread_bundle_;
    198   base::ScopedTempDir temp_dir_;
    199   scoped_ptr<TestingPrefServiceSimple> pref_service_;
    200   scoped_ptr<test_util::FakeNetworkChangeNotifier>
    201       fake_network_change_notifier_;
    202   scoped_ptr<SyncClientTestDriveService> drive_service_;
    203   DummyOperationObserver observer_;
    204   scoped_ptr<JobScheduler> scheduler_;
    205   scoped_ptr<ResourceMetadataStorage,
    206              test_util::DestroyHelperForTests> metadata_storage_;
    207   scoped_ptr<ResourceMetadata, test_util::DestroyHelperForTests> metadata_;
    208   scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_;
    209   scoped_ptr<SyncClient> sync_client_;
    210 
    211   std::map<std::string, std::string> resource_ids_;  // Name-to-id map.
    212 };
    213 
    214 TEST_F(SyncClientTest, StartProcessingBacklog) {
    215   sync_client_->StartProcessingBacklog();
    216   base::RunLoop().RunUntilIdle();
    217 
    218   FileCacheEntry cache_entry;
    219   // Pinned files get downloaded.
    220   EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["foo"], &cache_entry));
    221   EXPECT_TRUE(cache_entry.is_present());
    222 
    223   EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["bar"], &cache_entry));
    224   EXPECT_TRUE(cache_entry.is_present());
    225 
    226   EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["baz"], &cache_entry));
    227   EXPECT_TRUE(cache_entry.is_present());
    228 
    229   // Dirty file gets uploaded.
    230   EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["dirty"], &cache_entry));
    231   EXPECT_FALSE(cache_entry.is_dirty());
    232 }
    233 
    234 TEST_F(SyncClientTest, AddFetchTask) {
    235   sync_client_->AddFetchTask(resource_ids_["foo"]);
    236   base::RunLoop().RunUntilIdle();
    237 
    238   FileCacheEntry cache_entry;
    239   EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["foo"], &cache_entry));
    240   EXPECT_TRUE(cache_entry.is_present());
    241 }
    242 
    243 TEST_F(SyncClientTest, AddFetchTaskAndCancelled) {
    244   // Trigger fetching of a file which results in cancellation.
    245   drive_service_->set_resource_id_to_be_cancelled(resource_ids_["foo"]);
    246   sync_client_->AddFetchTask(resource_ids_["foo"]);
    247   base::RunLoop().RunUntilIdle();
    248 
    249   // The file should be unpinned if the user wants the download to be cancelled.
    250   FileCacheEntry cache_entry;
    251   EXPECT_FALSE(cache_->GetCacheEntry(resource_ids_["foo"], &cache_entry));
    252 }
    253 
    254 TEST_F(SyncClientTest, RemoveFetchTask) {
    255   sync_client_->AddFetchTask(resource_ids_["foo"]);
    256   sync_client_->AddFetchTask(resource_ids_["bar"]);
    257   sync_client_->AddFetchTask(resource_ids_["baz"]);
    258 
    259   sync_client_->RemoveFetchTask(resource_ids_["foo"]);
    260   sync_client_->RemoveFetchTask(resource_ids_["baz"]);
    261   base::RunLoop().RunUntilIdle();
    262 
    263   // Only "bar" should be fetched.
    264   FileCacheEntry cache_entry;
    265   EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["foo"], &cache_entry));
    266   EXPECT_FALSE(cache_entry.is_present());
    267 
    268   EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["bar"], &cache_entry));
    269   EXPECT_TRUE(cache_entry.is_present());
    270 
    271   EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["baz"], &cache_entry));
    272   EXPECT_FALSE(cache_entry.is_present());
    273 
    274 }
    275 
    276 TEST_F(SyncClientTest, ExistingPinnedFiles) {
    277   // Start checking the existing pinned files. This will collect the resource
    278   // IDs of pinned files, with stale local cache files.
    279   sync_client_->StartCheckingExistingPinnedFiles();
    280   base::RunLoop().RunUntilIdle();
    281 
    282   // "fetched" and "dirty" are the existing pinned files.
    283   // The non-dirty one should be synced, but the dirty one should not.
    284   base::FilePath cache_file;
    285   std::string content;
    286   EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(resource_ids_["fetched"],
    287                                            &cache_file));
    288   EXPECT_TRUE(file_util::ReadFileToString(cache_file, &content));
    289   EXPECT_EQ(kRemoteContent, content);
    290   content.clear();
    291 
    292   EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(resource_ids_["dirty"],
    293                                            &cache_file));
    294   EXPECT_TRUE(file_util::ReadFileToString(cache_file, &content));
    295   EXPECT_EQ(kLocalContent, content);
    296 }
    297 
    298 TEST_F(SyncClientTest, RetryOnDisconnection) {
    299   // Let the service go down.
    300   drive_service_->set_offline(true);
    301   // Change the network connection state after some delay, to test that
    302   // FILE_ERROR_NO_CONNECTION is handled by SyncClient correctly.
    303   // Without this delay, JobScheduler will keep the jobs unrun and SyncClient
    304   // will receive no error.
    305   base::MessageLoopProxy::current()->PostDelayedTask(
    306       FROM_HERE,
    307       base::Bind(&test_util::FakeNetworkChangeNotifier::SetConnectionType,
    308                  base::Unretained(fake_network_change_notifier_.get()),
    309                  net::NetworkChangeNotifier::CONNECTION_NONE),
    310       TestTimeouts::tiny_timeout());
    311 
    312   // Try fetch and upload.
    313   sync_client_->AddFetchTask(resource_ids_["foo"]);
    314   sync_client_->AddUploadTask(resource_ids_["dirty"]);
    315   base::RunLoop().RunUntilIdle();
    316 
    317   // Not yet fetched nor uploaded.
    318   FileCacheEntry cache_entry;
    319   EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["foo"], &cache_entry));
    320   EXPECT_FALSE(cache_entry.is_present());
    321   EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["dirty"], &cache_entry));
    322   EXPECT_TRUE(cache_entry.is_dirty());
    323 
    324   // Switch to online.
    325   fake_network_change_notifier_->SetConnectionType(
    326       net::NetworkChangeNotifier::CONNECTION_WIFI);
    327   drive_service_->set_offline(false);
    328   base::RunLoop().RunUntilIdle();
    329 
    330   // Fetched and uploaded.
    331   EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["foo"], &cache_entry));
    332   EXPECT_TRUE(cache_entry.is_present());
    333   EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["dirty"], &cache_entry));
    334   EXPECT_FALSE(cache_entry.is_dirty());
    335 }
    336 
    337 }  // namespace internal
    338 }  // namespace drive
    339