entry_update_performer_unittest.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/sync/entry_update_performer.h" 6 7#include "base/callback_helpers.h" 8#include "base/file_util.h" 9#include "base/md5.h" 10#include "base/task_runner_util.h" 11#include "chrome/browser/chromeos/drive/file_cache.h" 12#include "chrome/browser/chromeos/drive/file_system/operation_test_base.h" 13#include "chrome/browser/chromeos/drive/job_scheduler.h" 14#include "chrome/browser/chromeos/drive/resource_metadata.h" 15#include "chrome/browser/drive/drive_api_util.h" 16#include "chrome/browser/drive/fake_drive_service.h" 17#include "google_apis/drive/drive_api_parser.h" 18#include "google_apis/drive/gdata_wapi_parser.h" 19#include "google_apis/drive/test_util.h" 20#include "testing/gtest/include/gtest/gtest.h" 21 22namespace drive { 23namespace internal { 24 25class EntryUpdatePerformerTest : public file_system::OperationTestBase { 26 protected: 27 virtual void SetUp() OVERRIDE { 28 OperationTestBase::SetUp(); 29 performer_.reset(new EntryUpdatePerformer(blocking_task_runner(), 30 observer(), 31 scheduler(), 32 metadata(), 33 cache(), 34 loader_controller())); 35 } 36 37 // Stores |content| to the cache and mark it as dirty. 38 FileError StoreAndMarkDirty(const std::string& local_id, 39 const std::string& content) { 40 base::FilePath path; 41 if (!base::CreateTemporaryFileInDir(temp_dir(), &path) || 42 !google_apis::test_util::WriteStringToFile(path, content)) 43 return FILE_ERROR_FAILED; 44 45 // Store the file to cache. 46 FileError error = FILE_ERROR_FAILED; 47 base::PostTaskAndReplyWithResult( 48 blocking_task_runner(), 49 FROM_HERE, 50 base::Bind(&FileCache::Store, 51 base::Unretained(cache()), 52 local_id, std::string(), path, 53 FileCache::FILE_OPERATION_COPY), 54 google_apis::test_util::CreateCopyResultCallback(&error)); 55 test_util::RunBlockingPoolTask(); 56 return error; 57 } 58 59 scoped_ptr<EntryUpdatePerformer> performer_; 60}; 61 62TEST_F(EntryUpdatePerformerTest, UpdateEntry) { 63 base::FilePath src_path( 64 FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt")); 65 base::FilePath dest_path( 66 FILE_PATH_LITERAL("drive/root/Directory 1/Sub Directory Folder")); 67 68 ResourceEntry src_entry, dest_entry; 69 EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(src_path, &src_entry)); 70 EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(dest_path, &dest_entry)); 71 72 // Update local entry. 73 base::Time new_last_modified = base::Time::FromInternalValue( 74 src_entry.file_info().last_modified()) + base::TimeDelta::FromSeconds(1); 75 base::Time new_last_accessed = base::Time::FromInternalValue( 76 src_entry.file_info().last_accessed()) + base::TimeDelta::FromSeconds(2); 77 78 src_entry.set_parent_local_id(dest_entry.local_id()); 79 src_entry.set_title("Moved" + src_entry.title()); 80 src_entry.mutable_file_info()->set_last_modified( 81 new_last_modified.ToInternalValue()); 82 src_entry.mutable_file_info()->set_last_accessed( 83 new_last_accessed.ToInternalValue()); 84 src_entry.set_metadata_edit_state(ResourceEntry::DIRTY); 85 86 FileError error = FILE_ERROR_FAILED; 87 base::PostTaskAndReplyWithResult( 88 blocking_task_runner(), 89 FROM_HERE, 90 base::Bind(&ResourceMetadata::RefreshEntry, 91 base::Unretained(metadata()), 92 src_entry), 93 google_apis::test_util::CreateCopyResultCallback(&error)); 94 test_util::RunBlockingPoolTask(); 95 EXPECT_EQ(FILE_ERROR_OK, error); 96 97 // Perform server side update. 98 error = FILE_ERROR_FAILED; 99 performer_->UpdateEntry( 100 src_entry.local_id(), 101 ClientContext(USER_INITIATED), 102 google_apis::test_util::CreateCopyResultCallback(&error)); 103 test_util::RunBlockingPoolTask(); 104 EXPECT_EQ(FILE_ERROR_OK, error); 105 106 // Verify the file is updated on the server. 107 google_apis::GDataErrorCode gdata_error = google_apis::GDATA_OTHER_ERROR; 108 scoped_ptr<google_apis::ResourceEntry> gdata_entry; 109 fake_service()->GetResourceEntry( 110 src_entry.resource_id(), 111 google_apis::test_util::CreateCopyResultCallback(&gdata_error, 112 &gdata_entry)); 113 test_util::RunBlockingPoolTask(); 114 EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error); 115 ASSERT_TRUE(gdata_entry); 116 117 EXPECT_EQ(src_entry.title(), gdata_entry->title()); 118 EXPECT_EQ(new_last_modified, gdata_entry->updated_time()); 119 EXPECT_EQ(new_last_accessed, gdata_entry->last_viewed_time()); 120 121 const google_apis::Link* parent_link = 122 gdata_entry->GetLinkByType(google_apis::Link::LINK_PARENT); 123 ASSERT_TRUE(parent_link); 124 EXPECT_EQ(dest_entry.resource_id(), 125 util::ExtractResourceIdFromUrl(parent_link->href())); 126} 127 128TEST_F(EntryUpdatePerformerTest, UpdateEntry_NotFound) { 129 const std::string id = "this ID should result in NOT_FOUND"; 130 FileError error = FILE_ERROR_FAILED; 131 performer_->UpdateEntry( 132 id, ClientContext(USER_INITIATED), 133 google_apis::test_util::CreateCopyResultCallback(&error)); 134 test_util::RunBlockingPoolTask(); 135 EXPECT_EQ(FILE_ERROR_NOT_FOUND, error); 136} 137 138TEST_F(EntryUpdatePerformerTest, UpdateEntry_ContentUpdate) { 139 const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt")); 140 const std::string kResourceId("file:2_file_resource_id"); 141 142 const std::string local_id = GetLocalId(kFilePath); 143 EXPECT_FALSE(local_id.empty()); 144 145 const std::string kTestFileContent = "I'm being uploaded! Yay!"; 146 EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent)); 147 148 int64 original_changestamp = 149 fake_service()->about_resource().largest_change_id(); 150 151 // The callback will be called upon completion of UpdateEntry(). 152 FileError error = FILE_ERROR_FAILED; 153 performer_->UpdateEntry( 154 local_id, 155 ClientContext(USER_INITIATED), 156 google_apis::test_util::CreateCopyResultCallback(&error)); 157 test_util::RunBlockingPoolTask(); 158 EXPECT_EQ(FILE_ERROR_OK, error); 159 160 // Check that the server has received an update. 161 EXPECT_LT(original_changestamp, 162 fake_service()->about_resource().largest_change_id()); 163 164 // Check that the file size is updated to that of the updated content. 165 google_apis::GDataErrorCode gdata_error = google_apis::GDATA_OTHER_ERROR; 166 scoped_ptr<google_apis::ResourceEntry> server_entry; 167 fake_service()->GetResourceEntry( 168 kResourceId, 169 google_apis::test_util::CreateCopyResultCallback(&gdata_error, 170 &server_entry)); 171 test_util::RunBlockingPoolTask(); 172 EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error); 173 EXPECT_EQ(static_cast<int64>(kTestFileContent.size()), 174 server_entry->file_size()); 175 176 // Make sure that the cache is no longer dirty. 177 bool success = false; 178 FileCacheEntry cache_entry; 179 base::PostTaskAndReplyWithResult( 180 blocking_task_runner(), 181 FROM_HERE, 182 base::Bind(&FileCache::GetCacheEntry, 183 base::Unretained(cache()), 184 local_id, 185 &cache_entry), 186 google_apis::test_util::CreateCopyResultCallback(&success)); 187 test_util::RunBlockingPoolTask(); 188 ASSERT_TRUE(success); 189 EXPECT_FALSE(cache_entry.is_dirty()); 190} 191 192TEST_F(EntryUpdatePerformerTest, UpdateEntry_ContentUpdateMd5Check) { 193 const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt")); 194 const std::string kResourceId("file:2_file_resource_id"); 195 196 const std::string local_id = GetLocalId(kFilePath); 197 EXPECT_FALSE(local_id.empty()); 198 199 const std::string kTestFileContent = "I'm being uploaded! Yay!"; 200 EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent)); 201 202 int64 original_changestamp = 203 fake_service()->about_resource().largest_change_id(); 204 205 // The callback will be called upon completion of UpdateEntry(). 206 FileError error = FILE_ERROR_FAILED; 207 performer_->UpdateEntry( 208 local_id, 209 ClientContext(USER_INITIATED), 210 google_apis::test_util::CreateCopyResultCallback(&error)); 211 test_util::RunBlockingPoolTask(); 212 EXPECT_EQ(FILE_ERROR_OK, error); 213 214 // Check that the server has received an update. 215 EXPECT_LT(original_changestamp, 216 fake_service()->about_resource().largest_change_id()); 217 218 // Check that the file size is updated to that of the updated content. 219 google_apis::GDataErrorCode gdata_error = google_apis::GDATA_OTHER_ERROR; 220 scoped_ptr<google_apis::ResourceEntry> server_entry; 221 fake_service()->GetResourceEntry( 222 kResourceId, 223 google_apis::test_util::CreateCopyResultCallback(&gdata_error, 224 &server_entry)); 225 test_util::RunBlockingPoolTask(); 226 EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error); 227 EXPECT_EQ(static_cast<int64>(kTestFileContent.size()), 228 server_entry->file_size()); 229 230 // Make sure that the cache is no longer dirty. 231 bool success = false; 232 FileCacheEntry cache_entry; 233 base::PostTaskAndReplyWithResult( 234 blocking_task_runner(), 235 FROM_HERE, 236 base::Bind(&FileCache::GetCacheEntry, 237 base::Unretained(cache()), 238 local_id, 239 &cache_entry), 240 google_apis::test_util::CreateCopyResultCallback(&success)); 241 test_util::RunBlockingPoolTask(); 242 ASSERT_TRUE(success); 243 EXPECT_FALSE(cache_entry.is_dirty()); 244 245 // Again mark the cache file dirty. 246 scoped_ptr<base::ScopedClosureRunner> file_closer; 247 error = FILE_ERROR_FAILED; 248 base::PostTaskAndReplyWithResult( 249 blocking_task_runner(), 250 FROM_HERE, 251 base::Bind(&FileCache::OpenForWrite, 252 base::Unretained(cache()), 253 local_id, 254 &file_closer), 255 google_apis::test_util::CreateCopyResultCallback(&error)); 256 test_util::RunBlockingPoolTask(); 257 EXPECT_EQ(FILE_ERROR_OK, error); 258 file_closer.reset(); 259 260 // And call UpdateEntry again. 261 // In this case, although the file is marked as dirty, but the content 262 // hasn't been changed. Thus, the actual uploading should be skipped. 263 original_changestamp = fake_service()->about_resource().largest_change_id(); 264 error = FILE_ERROR_FAILED; 265 performer_->UpdateEntry( 266 local_id, 267 ClientContext(USER_INITIATED), 268 google_apis::test_util::CreateCopyResultCallback(&error)); 269 test_util::RunBlockingPoolTask(); 270 EXPECT_EQ(FILE_ERROR_OK, error); 271 272 EXPECT_EQ(original_changestamp, 273 fake_service()->about_resource().largest_change_id()); 274 275 // Make sure that the cache is no longer dirty. 276 success = false; 277 base::PostTaskAndReplyWithResult( 278 blocking_task_runner(), 279 FROM_HERE, 280 base::Bind(&FileCache::GetCacheEntry, 281 base::Unretained(cache()), 282 local_id, 283 &cache_entry), 284 google_apis::test_util::CreateCopyResultCallback(&success)); 285 test_util::RunBlockingPoolTask(); 286 ASSERT_TRUE(success); 287 EXPECT_FALSE(cache_entry.is_dirty()); 288} 289 290TEST_F(EntryUpdatePerformerTest, UpdateEntry_OpenedForWrite) { 291 const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt")); 292 const std::string kResourceId("file:2_file_resource_id"); 293 294 const std::string local_id = GetLocalId(kFilePath); 295 EXPECT_FALSE(local_id.empty()); 296 297 const std::string kTestFileContent = "I'm being uploaded! Yay!"; 298 EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent)); 299 300 // Emulate a situation where someone is writing to the file. 301 scoped_ptr<base::ScopedClosureRunner> file_closer; 302 FileError error = FILE_ERROR_FAILED; 303 base::PostTaskAndReplyWithResult( 304 blocking_task_runner(), 305 FROM_HERE, 306 base::Bind(&FileCache::OpenForWrite, 307 base::Unretained(cache()), 308 local_id, 309 &file_closer), 310 google_apis::test_util::CreateCopyResultCallback(&error)); 311 test_util::RunBlockingPoolTask(); 312 EXPECT_EQ(FILE_ERROR_OK, error); 313 314 // Update. This should not clear the dirty bit. 315 error = FILE_ERROR_FAILED; 316 performer_->UpdateEntry( 317 local_id, 318 ClientContext(USER_INITIATED), 319 google_apis::test_util::CreateCopyResultCallback(&error)); 320 test_util::RunBlockingPoolTask(); 321 EXPECT_EQ(FILE_ERROR_OK, error); 322 323 // Make sure that the cache is still dirty. 324 bool success = false; 325 FileCacheEntry cache_entry; 326 base::PostTaskAndReplyWithResult( 327 blocking_task_runner(), 328 FROM_HERE, 329 base::Bind(&FileCache::GetCacheEntry, 330 base::Unretained(cache()), 331 local_id, 332 &cache_entry), 333 google_apis::test_util::CreateCopyResultCallback(&success)); 334 test_util::RunBlockingPoolTask(); 335 EXPECT_TRUE(success); 336 EXPECT_TRUE(cache_entry.is_dirty()); 337 338 // Close the file. 339 file_closer.reset(); 340 341 // Update. This should clear the dirty bit. 342 error = FILE_ERROR_FAILED; 343 performer_->UpdateEntry( 344 local_id, 345 ClientContext(USER_INITIATED), 346 google_apis::test_util::CreateCopyResultCallback(&error)); 347 test_util::RunBlockingPoolTask(); 348 EXPECT_EQ(FILE_ERROR_OK, error); 349 350 // Make sure that the cache is no longer dirty. 351 base::PostTaskAndReplyWithResult( 352 blocking_task_runner(), 353 FROM_HERE, 354 base::Bind(&FileCache::GetCacheEntry, 355 base::Unretained(cache()), 356 local_id, 357 &cache_entry), 358 google_apis::test_util::CreateCopyResultCallback(&success)); 359 test_util::RunBlockingPoolTask(); 360 EXPECT_TRUE(success); 361 EXPECT_FALSE(cache_entry.is_dirty()); 362} 363 364TEST_F(EntryUpdatePerformerTest, UpdateEntry_UploadNewFile) { 365 // Create a new file locally. 366 const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/New File.txt")); 367 368 ResourceEntry parent; 369 EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath.DirName(), &parent)); 370 371 ResourceEntry entry; 372 entry.set_parent_local_id(parent.local_id()); 373 entry.set_title(kFilePath.BaseName().AsUTF8Unsafe()); 374 entry.mutable_file_specific_info()->set_content_mime_type("text/plain"); 375 entry.set_metadata_edit_state(ResourceEntry::DIRTY); 376 377 FileError error = FILE_ERROR_FAILED; 378 std::string local_id; 379 base::PostTaskAndReplyWithResult( 380 blocking_task_runner(), 381 FROM_HERE, 382 base::Bind(&internal::ResourceMetadata::AddEntry, 383 base::Unretained(metadata()), 384 entry, 385 &local_id), 386 google_apis::test_util::CreateCopyResultCallback(&error)); 387 test_util::RunBlockingPoolTask(); 388 EXPECT_EQ(FILE_ERROR_OK, error); 389 390 // Update. This should result in creating a new file on the server. 391 error = FILE_ERROR_FAILED; 392 performer_->UpdateEntry( 393 local_id, 394 ClientContext(USER_INITIATED), 395 google_apis::test_util::CreateCopyResultCallback(&error)); 396 test_util::RunBlockingPoolTask(); 397 EXPECT_EQ(FILE_ERROR_OK, error); 398 399 // The entry got a resource ID. 400 EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry)); 401 EXPECT_FALSE(entry.resource_id().empty()); 402 EXPECT_EQ(ResourceEntry::CLEAN, entry.metadata_edit_state()); 403 404 // Make sure that the cache is no longer dirty. 405 bool success = false; 406 FileCacheEntry cache_entry; 407 base::PostTaskAndReplyWithResult( 408 blocking_task_runner(), 409 FROM_HERE, 410 base::Bind(&FileCache::GetCacheEntry, 411 base::Unretained(cache()), 412 local_id, 413 &cache_entry), 414 google_apis::test_util::CreateCopyResultCallback(&success)); 415 test_util::RunBlockingPoolTask(); 416 EXPECT_TRUE(success); 417 EXPECT_FALSE(cache_entry.is_dirty()); 418 419 // Make sure that we really created a file. 420 google_apis::GDataErrorCode status = google_apis::GDATA_OTHER_ERROR; 421 scoped_ptr<google_apis::ResourceEntry> resource_entry; 422 fake_service()->GetResourceEntry( 423 entry.resource_id(), 424 google_apis::test_util::CreateCopyResultCallback(&status, 425 &resource_entry)); 426 test_util::RunBlockingPoolTask(); 427 EXPECT_EQ(google_apis::HTTP_SUCCESS, status); 428 ASSERT_TRUE(resource_entry); 429 EXPECT_FALSE(resource_entry->is_folder()); 430} 431 432TEST_F(EntryUpdatePerformerTest, UpdateEntry_CreateDirectory) { 433 // Create a new directory locally. 434 const base::FilePath kPath(FILE_PATH_LITERAL("drive/root/New Directory")); 435 436 ResourceEntry parent; 437 EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kPath.DirName(), &parent)); 438 439 ResourceEntry entry; 440 entry.set_parent_local_id(parent.local_id()); 441 entry.set_title(kPath.BaseName().AsUTF8Unsafe()); 442 entry.mutable_file_info()->set_is_directory(true); 443 entry.set_metadata_edit_state(ResourceEntry::DIRTY); 444 445 FileError error = FILE_ERROR_FAILED; 446 std::string local_id; 447 base::PostTaskAndReplyWithResult( 448 blocking_task_runner(), 449 FROM_HERE, 450 base::Bind(&internal::ResourceMetadata::AddEntry, 451 base::Unretained(metadata()), 452 entry, 453 &local_id), 454 google_apis::test_util::CreateCopyResultCallback(&error)); 455 test_util::RunBlockingPoolTask(); 456 EXPECT_EQ(FILE_ERROR_OK, error); 457 458 // Update. This should result in creating a new directory on the server. 459 error = FILE_ERROR_FAILED; 460 performer_->UpdateEntry( 461 local_id, 462 ClientContext(USER_INITIATED), 463 google_apis::test_util::CreateCopyResultCallback(&error)); 464 test_util::RunBlockingPoolTask(); 465 EXPECT_EQ(FILE_ERROR_OK, error); 466 467 // The entry got a resource ID. 468 EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kPath, &entry)); 469 EXPECT_FALSE(entry.resource_id().empty()); 470 EXPECT_EQ(ResourceEntry::CLEAN, entry.metadata_edit_state()); 471 472 // Make sure that we really created a directory. 473 google_apis::GDataErrorCode status = google_apis::GDATA_OTHER_ERROR; 474 scoped_ptr<google_apis::ResourceEntry> resource_entry; 475 fake_service()->GetResourceEntry( 476 entry.resource_id(), 477 google_apis::test_util::CreateCopyResultCallback(&status, 478 &resource_entry)); 479 test_util::RunBlockingPoolTask(); 480 EXPECT_EQ(google_apis::HTTP_SUCCESS, status); 481 ASSERT_TRUE(resource_entry); 482 EXPECT_TRUE(resource_entry->is_folder()); 483} 484 485} // namespace internal 486} // namespace drive 487