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