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