sync_client_unittest.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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 28namespace drive { 29namespace internal { 30 31namespace { 32 33// The content of files initially stored in the cache. 34const char kLocalContent[] = "Hello!"; 35 36// The content of files stored in the service. 37const char kRemoteContent[] = "World!"; 38 39// SyncClientTestDriveService will return GDATA_CANCELLED when a request is 40// made with the specified resource ID. 41class 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 73class 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 83class 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, 182 cache_->MarkDirty(resource_ids_["dirty"], md5_dirty)); 183 184 // Load data from the service to the metadata. 185 FileError error = FILE_ERROR_FAILED; 186 internal::ChangeListLoader change_list_loader( 187 base::MessageLoopProxy::current().get(), 188 metadata_.get(), 189 scheduler_.get()); 190 change_list_loader.LoadIfNeeded( 191 DirectoryFetchInfo(), 192 google_apis::test_util::CreateCopyResultCallback(&error)); 193 base::RunLoop().RunUntilIdle(); 194 EXPECT_EQ(FILE_ERROR_OK, error); 195 } 196 197 protected: 198 content::TestBrowserThreadBundle thread_bundle_; 199 base::ScopedTempDir temp_dir_; 200 scoped_ptr<TestingPrefServiceSimple> pref_service_; 201 scoped_ptr<test_util::FakeNetworkChangeNotifier> 202 fake_network_change_notifier_; 203 scoped_ptr<SyncClientTestDriveService> drive_service_; 204 DummyOperationObserver observer_; 205 scoped_ptr<JobScheduler> scheduler_; 206 scoped_ptr<ResourceMetadataStorage, 207 test_util::DestroyHelperForTests> metadata_storage_; 208 scoped_ptr<ResourceMetadata, test_util::DestroyHelperForTests> metadata_; 209 scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_; 210 scoped_ptr<SyncClient> sync_client_; 211 212 std::map<std::string, std::string> resource_ids_; // Name-to-id map. 213}; 214 215TEST_F(SyncClientTest, StartProcessingBacklog) { 216 sync_client_->StartProcessingBacklog(); 217 base::RunLoop().RunUntilIdle(); 218 219 FileCacheEntry cache_entry; 220 // Pinned files get downloaded. 221 EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["foo"], std::string(), 222 &cache_entry)); 223 EXPECT_TRUE(cache_entry.is_present()); 224 225 EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["bar"], std::string(), 226 &cache_entry)); 227 EXPECT_TRUE(cache_entry.is_present()); 228 229 EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["baz"], std::string(), 230 &cache_entry)); 231 EXPECT_TRUE(cache_entry.is_present()); 232 233 // Dirty file gets uploaded. 234 EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["dirty"], std::string(), 235 &cache_entry)); 236 EXPECT_FALSE(cache_entry.is_dirty()); 237} 238 239TEST_F(SyncClientTest, AddFetchTask) { 240 sync_client_->AddFetchTask(resource_ids_["foo"]); 241 base::RunLoop().RunUntilIdle(); 242 243 FileCacheEntry cache_entry; 244 EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["foo"], std::string(), 245 &cache_entry)); 246 EXPECT_TRUE(cache_entry.is_present()); 247} 248 249TEST_F(SyncClientTest, AddFetchTaskAndCancelled) { 250 // Trigger fetching of a file which results in cancellation. 251 drive_service_->set_resource_id_to_be_cancelled(resource_ids_["foo"]); 252 sync_client_->AddFetchTask(resource_ids_["foo"]); 253 base::RunLoop().RunUntilIdle(); 254 255 // The file should be unpinned if the user wants the download to be cancelled. 256 FileCacheEntry cache_entry; 257 EXPECT_FALSE(cache_->GetCacheEntry(resource_ids_["foo"], std::string(), 258 &cache_entry)); 259} 260 261TEST_F(SyncClientTest, RemoveFetchTask) { 262 sync_client_->AddFetchTask(resource_ids_["foo"]); 263 sync_client_->AddFetchTask(resource_ids_["bar"]); 264 sync_client_->AddFetchTask(resource_ids_["baz"]); 265 266 sync_client_->RemoveFetchTask(resource_ids_["foo"]); 267 sync_client_->RemoveFetchTask(resource_ids_["baz"]); 268 base::RunLoop().RunUntilIdle(); 269 270 // Only "bar" should be fetched. 271 FileCacheEntry cache_entry; 272 EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["foo"], std::string(), 273 &cache_entry)); 274 EXPECT_FALSE(cache_entry.is_present()); 275 276 EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["bar"], std::string(), 277 &cache_entry)); 278 EXPECT_TRUE(cache_entry.is_present()); 279 280 EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["baz"], std::string(), 281 &cache_entry)); 282 EXPECT_FALSE(cache_entry.is_present()); 283 284} 285 286TEST_F(SyncClientTest, ExistingPinnedFiles) { 287 // Start checking the existing pinned files. This will collect the resource 288 // IDs of pinned files, with stale local cache files. 289 sync_client_->StartCheckingExistingPinnedFiles(); 290 base::RunLoop().RunUntilIdle(); 291 292 // "fetched" and "dirty" are the existing pinned files. 293 // The non-dirty one should be synced, but the dirty one should not. 294 base::FilePath cache_file; 295 std::string content; 296 EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(resource_ids_["fetched"], 297 std::string(), &cache_file)); 298 EXPECT_TRUE(file_util::ReadFileToString(cache_file, &content)); 299 EXPECT_EQ(kRemoteContent, content); 300 content.clear(); 301 302 EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(resource_ids_["dirty"], 303 std::string(), &cache_file)); 304 EXPECT_TRUE(file_util::ReadFileToString(cache_file, &content)); 305 EXPECT_EQ(kLocalContent, content); 306} 307 308TEST_F(SyncClientTest, RetryOnDisconnection) { 309 // Let the service go down. 310 drive_service_->set_offline(true); 311 // Change the network connection state after some delay, to test that 312 // FILE_ERROR_NO_CONNECTION is handled by SyncClient correctly. 313 // Without this delay, JobScheduler will keep the jobs unrun and SyncClient 314 // will receive no error. 315 base::MessageLoopProxy::current()->PostDelayedTask( 316 FROM_HERE, 317 base::Bind(&test_util::FakeNetworkChangeNotifier::SetConnectionType, 318 base::Unretained(fake_network_change_notifier_.get()), 319 net::NetworkChangeNotifier::CONNECTION_NONE), 320 TestTimeouts::tiny_timeout()); 321 322 // Try fetch and upload. 323 sync_client_->AddFetchTask(resource_ids_["foo"]); 324 sync_client_->AddUploadTask(resource_ids_["dirty"]); 325 base::RunLoop().RunUntilIdle(); 326 327 // Not yet fetched nor uploaded. 328 FileCacheEntry cache_entry; 329 EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["foo"], std::string(), 330 &cache_entry)); 331 EXPECT_FALSE(cache_entry.is_present()); 332 EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["dirty"], std::string(), 333 &cache_entry)); 334 EXPECT_TRUE(cache_entry.is_dirty()); 335 336 // Switch to online. 337 fake_network_change_notifier_->SetConnectionType( 338 net::NetworkChangeNotifier::CONNECTION_WIFI); 339 drive_service_->set_offline(false); 340 base::RunLoop().RunUntilIdle(); 341 342 // Fetched and uploaded. 343 EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["foo"], std::string(), 344 &cache_entry)); 345 EXPECT_TRUE(cache_entry.is_present()); 346 EXPECT_TRUE(cache_->GetCacheEntry(resource_ids_["dirty"], std::string(), 347 &cache_entry)); 348 EXPECT_FALSE(cache_entry.is_dirty()); 349} 350 351} // namespace internal 352} // namespace drive 353