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 "chrome/browser/chromeos/drive/file_system/download_operation.h" 6 7 #include "base/file_util.h" 8 #include "base/task_runner_util.h" 9 #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h" 10 #include "chrome/browser/chromeos/drive/file_cache.h" 11 #include "chrome/browser/chromeos/drive/file_system/operation_test_base.h" 12 #include "chrome/browser/chromeos/drive/file_system_util.h" 13 #include "chrome/browser/chromeos/drive/job_scheduler.h" 14 #include "chrome/browser/drive/fake_drive_service.h" 15 #include "google_apis/drive/test_util.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 #include "third_party/cros_system_api/constants/cryptohome.h" 18 19 namespace drive { 20 namespace file_system { 21 22 class DownloadOperationTest : public OperationTestBase { 23 protected: 24 virtual void SetUp() OVERRIDE { 25 OperationTestBase::SetUp(); 26 27 operation_.reset(new DownloadOperation( 28 blocking_task_runner(), observer(), scheduler(), metadata(), cache(), 29 temp_dir())); 30 } 31 32 scoped_ptr<DownloadOperation> operation_; 33 }; 34 35 TEST_F(DownloadOperationTest, 36 EnsureFileDownloadedByPath_FromServer_EnoughSpace) { 37 base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt")); 38 ResourceEntry src_entry; 39 ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry)); 40 const int64 file_size = src_entry.file_info().size(); 41 42 // Pretend we have enough space. 43 fake_free_disk_space_getter()->set_default_value( 44 file_size + cryptohome::kMinFreeSpaceInBytes); 45 46 FileError error = FILE_ERROR_FAILED; 47 base::FilePath file_path; 48 scoped_ptr<ResourceEntry> entry; 49 operation_->EnsureFileDownloadedByPath( 50 file_in_root, 51 ClientContext(USER_INITIATED), 52 GetFileContentInitializedCallback(), 53 google_apis::GetContentCallback(), 54 google_apis::test_util::CreateCopyResultCallback( 55 &error, &file_path, &entry)); 56 test_util::RunBlockingPoolTask(); 57 58 EXPECT_EQ(FILE_ERROR_OK, error); 59 ASSERT_TRUE(entry); 60 EXPECT_FALSE(entry->file_specific_info().is_hosted_document()); 61 62 // The transfered file is cached and the change of "offline available" 63 // attribute is notified. 64 EXPECT_EQ(1U, observer()->get_changed_paths().size()); 65 EXPECT_EQ(1U, observer()->get_changed_paths().count(file_in_root.DirName())); 66 } 67 68 TEST_F(DownloadOperationTest, 69 EnsureFileDownloadedByPath_FromServer_NoSpaceAtAll) { 70 base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt")); 71 72 // Pretend we have no space at all. 73 fake_free_disk_space_getter()->set_default_value(0); 74 75 FileError error = FILE_ERROR_OK; 76 base::FilePath file_path; 77 scoped_ptr<ResourceEntry> entry; 78 operation_->EnsureFileDownloadedByPath( 79 file_in_root, 80 ClientContext(USER_INITIATED), 81 GetFileContentInitializedCallback(), 82 google_apis::GetContentCallback(), 83 google_apis::test_util::CreateCopyResultCallback( 84 &error, &file_path, &entry)); 85 test_util::RunBlockingPoolTask(); 86 87 EXPECT_EQ(FILE_ERROR_NO_LOCAL_SPACE, error); 88 } 89 90 TEST_F(DownloadOperationTest, 91 EnsureFileDownloadedByPath_FromServer_NoEnoughSpaceButCanFreeUp) { 92 base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt")); 93 ResourceEntry src_entry; 94 ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry)); 95 const int64 file_size = src_entry.file_info().size(); 96 97 // Make another file cached. 98 // This file's cache file will be removed to free up the disk space. 99 base::FilePath cached_file( 100 FILE_PATH_LITERAL("drive/root/Duplicate Name.txt")); 101 FileError error = FILE_ERROR_FAILED; 102 base::FilePath file_path; 103 scoped_ptr<ResourceEntry> entry; 104 operation_->EnsureFileDownloadedByPath( 105 cached_file, 106 ClientContext(USER_INITIATED), 107 GetFileContentInitializedCallback(), 108 google_apis::GetContentCallback(), 109 google_apis::test_util::CreateCopyResultCallback( 110 &error, &file_path, &entry)); 111 test_util::RunBlockingPoolTask(); 112 EXPECT_EQ(FILE_ERROR_OK, error); 113 ASSERT_TRUE(entry); 114 EXPECT_TRUE(entry->file_specific_info().cache_state().is_present()); 115 116 // Pretend we have no space first (checked before downloading a file), 117 // but then start reporting we have space. This is to emulate that 118 // the disk space was freed up by removing temporary files. 119 fake_free_disk_space_getter()->set_default_value( 120 file_size + cryptohome::kMinFreeSpaceInBytes); 121 fake_free_disk_space_getter()->PushFakeValue(0); 122 123 operation_->EnsureFileDownloadedByPath( 124 file_in_root, 125 ClientContext(USER_INITIATED), 126 GetFileContentInitializedCallback(), 127 google_apis::GetContentCallback(), 128 google_apis::test_util::CreateCopyResultCallback( 129 &error, &file_path, &entry)); 130 test_util::RunBlockingPoolTask(); 131 132 EXPECT_EQ(FILE_ERROR_OK, error); 133 ASSERT_TRUE(entry); 134 EXPECT_FALSE(entry->file_specific_info().is_hosted_document()); 135 136 // The transfered file is cached and the change of "offline available" 137 // attribute is notified. 138 EXPECT_EQ(1U, observer()->get_changed_paths().size()); 139 EXPECT_EQ(1U, observer()->get_changed_paths().count(file_in_root.DirName())); 140 141 // The cache for the other file should be removed in order to free up space. 142 ResourceEntry cached_file_entry; 143 EXPECT_EQ(FILE_ERROR_OK, 144 GetLocalResourceEntry(cached_file, &cached_file_entry)); 145 EXPECT_FALSE( 146 cached_file_entry.file_specific_info().cache_state().is_present()); 147 } 148 149 TEST_F(DownloadOperationTest, 150 EnsureFileDownloadedByPath_FromServer_EnoughSpaceButBecomeFull) { 151 base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt")); 152 ResourceEntry src_entry; 153 ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry)); 154 const int64 file_size = src_entry.file_info().size(); 155 156 // Pretend we have enough space first (checked before downloading a file), 157 // but then start reporting we have not enough space. This is to emulate that 158 // the disk space becomes full after the file is downloaded for some reason 159 // (ex. the actual file was larger than the expected size). 160 fake_free_disk_space_getter()->PushFakeValue( 161 file_size + cryptohome::kMinFreeSpaceInBytes); 162 fake_free_disk_space_getter()->set_default_value( 163 cryptohome::kMinFreeSpaceInBytes - 1); 164 165 FileError error = FILE_ERROR_OK; 166 base::FilePath file_path; 167 scoped_ptr<ResourceEntry> entry; 168 operation_->EnsureFileDownloadedByPath( 169 file_in_root, 170 ClientContext(USER_INITIATED), 171 GetFileContentInitializedCallback(), 172 google_apis::GetContentCallback(), 173 google_apis::test_util::CreateCopyResultCallback( 174 &error, &file_path, &entry)); 175 test_util::RunBlockingPoolTask(); 176 177 EXPECT_EQ(FILE_ERROR_NO_LOCAL_SPACE, error); 178 } 179 180 TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_FromCache) { 181 base::FilePath temp_file; 182 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir(), &temp_file)); 183 184 base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt")); 185 ResourceEntry src_entry; 186 ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry)); 187 188 // Store something as cached version of this file. 189 FileError error = FILE_ERROR_OK; 190 base::PostTaskAndReplyWithResult( 191 blocking_task_runner(), 192 FROM_HERE, 193 base::Bind(&internal::FileCache::Store, 194 base::Unretained(cache()), 195 GetLocalId(file_in_root), 196 src_entry.file_specific_info().md5(), 197 temp_file, 198 internal::FileCache::FILE_OPERATION_COPY), 199 google_apis::test_util::CreateCopyResultCallback(&error)); 200 test_util::RunBlockingPoolTask(); 201 EXPECT_EQ(FILE_ERROR_OK, error); 202 203 base::FilePath file_path; 204 scoped_ptr<ResourceEntry> entry; 205 operation_->EnsureFileDownloadedByPath( 206 file_in_root, 207 ClientContext(USER_INITIATED), 208 GetFileContentInitializedCallback(), 209 google_apis::GetContentCallback(), 210 google_apis::test_util::CreateCopyResultCallback( 211 &error, &file_path, &entry)); 212 test_util::RunBlockingPoolTask(); 213 214 EXPECT_EQ(FILE_ERROR_OK, error); 215 ASSERT_TRUE(entry); 216 EXPECT_FALSE(entry->file_specific_info().is_hosted_document()); 217 } 218 219 TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_HostedDocument) { 220 base::FilePath file_in_root(FILE_PATH_LITERAL( 221 "drive/root/Document 1 excludeDir-test.gdoc")); 222 223 FileError error = FILE_ERROR_FAILED; 224 base::FilePath file_path; 225 scoped_ptr<ResourceEntry> entry; 226 operation_->EnsureFileDownloadedByPath( 227 file_in_root, 228 ClientContext(USER_INITIATED), 229 GetFileContentInitializedCallback(), 230 google_apis::GetContentCallback(), 231 google_apis::test_util::CreateCopyResultCallback( 232 &error, &file_path, &entry)); 233 test_util::RunBlockingPoolTask(); 234 235 EXPECT_EQ(FILE_ERROR_OK, error); 236 ASSERT_TRUE(entry); 237 EXPECT_TRUE(entry->file_specific_info().is_hosted_document()); 238 EXPECT_FALSE(file_path.empty()); 239 240 EXPECT_EQ(GURL(entry->file_specific_info().alternate_url()), 241 util::ReadUrlFromGDocFile(file_path)); 242 EXPECT_EQ(entry->resource_id(), util::ReadResourceIdFromGDocFile(file_path)); 243 EXPECT_EQ(FILE_PATH_LITERAL(".gdoc"), file_path.Extension()); 244 } 245 246 TEST_F(DownloadOperationTest, EnsureFileDownloadedByLocalId) { 247 base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt")); 248 ResourceEntry src_entry; 249 ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry)); 250 251 FileError error = FILE_ERROR_OK; 252 base::FilePath file_path; 253 scoped_ptr<ResourceEntry> entry; 254 operation_->EnsureFileDownloadedByLocalId( 255 GetLocalId(file_in_root), 256 ClientContext(USER_INITIATED), 257 GetFileContentInitializedCallback(), 258 google_apis::GetContentCallback(), 259 google_apis::test_util::CreateCopyResultCallback( 260 &error, &file_path, &entry)); 261 test_util::RunBlockingPoolTask(); 262 263 EXPECT_EQ(FILE_ERROR_OK, error); 264 ASSERT_TRUE(entry); 265 EXPECT_FALSE(entry->file_specific_info().is_hosted_document()); 266 267 // The transfered file is cached and the change of "offline available" 268 // attribute is notified. 269 EXPECT_EQ(1U, observer()->get_changed_paths().size()); 270 EXPECT_EQ(1U, observer()->get_changed_paths().count(file_in_root.DirName())); 271 } 272 273 TEST_F(DownloadOperationTest, 274 EnsureFileDownloadedByPath_WithGetContentCallback) { 275 base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt")); 276 277 { 278 FileError initialized_error = FILE_ERROR_FAILED; 279 scoped_ptr<ResourceEntry> entry, entry_dontcare; 280 base::FilePath local_path, local_path_dontcare; 281 google_apis::test_util::TestGetContentCallback get_content_callback; 282 FileError completion_error = FILE_ERROR_FAILED; 283 base::Closure cancel_download = operation_->EnsureFileDownloadedByPath( 284 file_in_root, 285 ClientContext(USER_INITIATED), 286 google_apis::test_util::CreateCopyResultCallback( 287 &initialized_error, &local_path, &entry), 288 get_content_callback.callback(), 289 google_apis::test_util::CreateCopyResultCallback( 290 &completion_error, &local_path_dontcare, &entry_dontcare)); 291 test_util::RunBlockingPoolTask(); 292 293 // For the first time, file is downloaded from the remote server. 294 // In this case, |local_path| is empty. 295 EXPECT_EQ(FILE_ERROR_OK, initialized_error); 296 ASSERT_TRUE(entry); 297 ASSERT_TRUE(local_path.empty()); 298 EXPECT_FALSE(cancel_download.is_null()); 299 // Content is available through the second callback argument. 300 EXPECT_EQ(static_cast<size_t>(entry->file_info().size()), 301 get_content_callback.GetConcatenatedData().size()); 302 EXPECT_EQ(FILE_ERROR_OK, completion_error); 303 304 // The transfered file is cached and the change of "offline available" 305 // attribute is notified. 306 EXPECT_EQ(1U, observer()->get_changed_paths().size()); 307 EXPECT_EQ(1U, 308 observer()->get_changed_paths().count(file_in_root.DirName())); 309 } 310 311 { 312 FileError initialized_error = FILE_ERROR_FAILED; 313 scoped_ptr<ResourceEntry> entry, entry_dontcare; 314 base::FilePath local_path, local_path_dontcare; 315 google_apis::test_util::TestGetContentCallback get_content_callback; 316 FileError completion_error = FILE_ERROR_FAILED; 317 base::Closure cancel_download = operation_->EnsureFileDownloadedByPath( 318 file_in_root, 319 ClientContext(USER_INITIATED), 320 google_apis::test_util::CreateCopyResultCallback( 321 &initialized_error, &local_path, &entry), 322 get_content_callback.callback(), 323 google_apis::test_util::CreateCopyResultCallback( 324 &completion_error, &local_path_dontcare, &entry_dontcare)); 325 test_util::RunBlockingPoolTask(); 326 327 // Try second download. In this case, the file should be cached, so 328 // |local_path| should not be empty. 329 EXPECT_EQ(FILE_ERROR_OK, initialized_error); 330 ASSERT_TRUE(entry); 331 ASSERT_TRUE(!local_path.empty()); 332 EXPECT_FALSE(cancel_download.is_null()); 333 // The content is available from the cache file. 334 EXPECT_TRUE(get_content_callback.data().empty()); 335 int64 local_file_size = 0; 336 base::GetFileSize(local_path, &local_file_size); 337 EXPECT_EQ(entry->file_info().size(), local_file_size); 338 EXPECT_EQ(FILE_ERROR_OK, completion_error); 339 } 340 } 341 342 TEST_F(DownloadOperationTest, EnsureFileDownloadedByLocalId_FromCache) { 343 base::FilePath temp_file; 344 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir(), &temp_file)); 345 346 base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt")); 347 ResourceEntry src_entry; 348 ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry)); 349 350 // Store something as cached version of this file. 351 FileError error = FILE_ERROR_FAILED; 352 base::PostTaskAndReplyWithResult( 353 blocking_task_runner(), 354 FROM_HERE, 355 base::Bind(&internal::FileCache::Store, 356 base::Unretained(cache()), 357 GetLocalId(file_in_root), 358 src_entry.file_specific_info().md5(), 359 temp_file, 360 internal::FileCache::FILE_OPERATION_COPY), 361 google_apis::test_util::CreateCopyResultCallback(&error)); 362 test_util::RunBlockingPoolTask(); 363 EXPECT_EQ(FILE_ERROR_OK, error); 364 365 // The file is obtained from the cache. 366 // Hence the downloading should work even if the drive service is offline. 367 fake_service()->set_offline(true); 368 369 base::FilePath file_path; 370 scoped_ptr<ResourceEntry> entry; 371 operation_->EnsureFileDownloadedByLocalId( 372 GetLocalId(file_in_root), 373 ClientContext(USER_INITIATED), 374 GetFileContentInitializedCallback(), 375 google_apis::GetContentCallback(), 376 google_apis::test_util::CreateCopyResultCallback( 377 &error, &file_path, &entry)); 378 test_util::RunBlockingPoolTask(); 379 380 EXPECT_EQ(FILE_ERROR_OK, error); 381 ASSERT_TRUE(entry); 382 EXPECT_FALSE(entry->file_specific_info().is_hosted_document()); 383 } 384 385 TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_DirtyCache) { 386 base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt")); 387 ResourceEntry src_entry; 388 ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry)); 389 390 // Prepare a dirty file to store to cache that has a different size than 391 // stored in resource metadata. 392 base::FilePath dirty_file = temp_dir().AppendASCII("dirty.txt"); 393 size_t dirty_size = src_entry.file_info().size() + 10; 394 google_apis::test_util::WriteStringToFile(dirty_file, 395 std::string(dirty_size, 'x')); 396 397 // Store the file as a cache, marking it to be dirty. 398 FileError error = FILE_ERROR_FAILED; 399 base::PostTaskAndReplyWithResult( 400 blocking_task_runner(), 401 FROM_HERE, 402 base::Bind(&internal::FileCache::Store, 403 base::Unretained(cache()), 404 GetLocalId(file_in_root), 405 std::string(), 406 dirty_file, 407 internal::FileCache::FILE_OPERATION_COPY), 408 google_apis::test_util::CreateCopyResultCallback(&error)); 409 test_util::RunBlockingPoolTask(); 410 EXPECT_EQ(FILE_ERROR_OK, error); 411 412 // Record values passed to GetFileContentInitializedCallback(). 413 FileError init_error; 414 base::FilePath init_path; 415 scoped_ptr<ResourceEntry> init_entry; 416 base::FilePath file_path; 417 scoped_ptr<ResourceEntry> entry; 418 base::Closure cancel_callback = operation_->EnsureFileDownloadedByPath( 419 file_in_root, 420 ClientContext(USER_INITIATED), 421 google_apis::test_util::CreateCopyResultCallback( 422 &init_error, &init_path, &init_entry), 423 google_apis::GetContentCallback(), 424 google_apis::test_util::CreateCopyResultCallback( 425 &error, &file_path, &entry)); 426 test_util::RunBlockingPoolTask(); 427 428 EXPECT_EQ(FILE_ERROR_OK, error); 429 // Check that the result of local modification is propagated. 430 EXPECT_EQ(static_cast<int64>(dirty_size), init_entry->file_info().size()); 431 EXPECT_EQ(static_cast<int64>(dirty_size), entry->file_info().size()); 432 } 433 434 TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_LocallyCreatedFile) { 435 // Add a new file with an empty resource ID. 436 base::FilePath file_path(FILE_PATH_LITERAL("drive/root/New File.txt")); 437 ResourceEntry parent; 438 ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_path.DirName(), &parent)); 439 440 ResourceEntry new_file; 441 new_file.set_title("New File.txt"); 442 new_file.set_parent_local_id(parent.local_id()); 443 444 FileError error = FILE_ERROR_FAILED; 445 std::string local_id; 446 base::PostTaskAndReplyWithResult( 447 blocking_task_runner(), 448 FROM_HERE, 449 base::Bind(&internal::ResourceMetadata::AddEntry, 450 base::Unretained(metadata()), 451 new_file, 452 &local_id), 453 google_apis::test_util::CreateCopyResultCallback(&error)); 454 test_util::RunBlockingPoolTask(); 455 EXPECT_EQ(FILE_ERROR_OK, error); 456 457 // Empty cache file should be returned. 458 base::FilePath cache_file_path; 459 scoped_ptr<ResourceEntry> entry; 460 operation_->EnsureFileDownloadedByPath( 461 file_path, 462 ClientContext(USER_INITIATED), 463 GetFileContentInitializedCallback(), 464 google_apis::GetContentCallback(), 465 google_apis::test_util::CreateCopyResultCallback( 466 &error, &cache_file_path, &entry)); 467 test_util::RunBlockingPoolTask(); 468 EXPECT_EQ(FILE_ERROR_OK, error); 469 470 int64 cache_file_size = 0; 471 EXPECT_TRUE(base::GetFileSize(cache_file_path, &cache_file_size)); 472 EXPECT_EQ(static_cast<int64>(0), cache_file_size); 473 ASSERT_TRUE(entry); 474 EXPECT_EQ(cache_file_size, entry->file_info().size()); 475 } 476 477 TEST_F(DownloadOperationTest, CancelBeforeDownloadStarts) { 478 base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt")); 479 ResourceEntry src_entry; 480 ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry)); 481 482 // Start operation. 483 FileError error = FILE_ERROR_OK; 484 base::FilePath file_path; 485 scoped_ptr<ResourceEntry> entry; 486 base::Closure cancel_closure = operation_->EnsureFileDownloadedByLocalId( 487 GetLocalId(file_in_root), 488 ClientContext(USER_INITIATED), 489 GetFileContentInitializedCallback(), 490 google_apis::GetContentCallback(), 491 google_apis::test_util::CreateCopyResultCallback( 492 &error, &file_path, &entry)); 493 494 // Cancel immediately. 495 ASSERT_FALSE(cancel_closure.is_null()); 496 cancel_closure.Run(); 497 test_util::RunBlockingPoolTask(); 498 499 EXPECT_EQ(FILE_ERROR_ABORT, error); 500 } 501 502 } // namespace file_system 503 } // namespace drive 504