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/sync_file_system/local/local_file_sync_context.h" 6 7 #include <vector> 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/file_util.h" 12 #include "base/files/file_path.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/stl_util.h" 15 #include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h" 16 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h" 17 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h" 18 #include "chrome/browser/sync_file_system/sync_file_metadata.h" 19 #include "chrome/browser/sync_file_system/sync_status_code.h" 20 #include "chrome/browser/sync_file_system/syncable_file_system_util.h" 21 #include "content/public/browser/browser_thread.h" 22 #include "content/public/test/mock_blob_url_request_context.h" 23 #include "content/public/test/test_browser_thread_bundle.h" 24 #include "testing/gtest/include/gtest/gtest.h" 25 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h" 26 #include "third_party/leveldatabase/src/include/leveldb/env.h" 27 #include "webkit/browser/fileapi/file_system_context.h" 28 #include "webkit/browser/fileapi/file_system_operation_runner.h" 29 #include "webkit/browser/fileapi/isolated_context.h" 30 #include "webkit/common/blob/scoped_file.h" 31 32 #define FPL FILE_PATH_LITERAL 33 34 using content::BrowserThread; 35 using fileapi::FileSystemContext; 36 using fileapi::FileSystemURL; 37 using fileapi::FileSystemURLSet; 38 39 // This tests LocalFileSyncContext behavior in multi-thread / 40 // multi-file-system-context environment. 41 // Basic combined tests (single-thread / single-file-system-context) 42 // that involve LocalFileSyncContext are also in 43 // syncable_file_system_unittests.cc. 44 45 namespace sync_file_system { 46 47 namespace { 48 const char kOrigin1[] = "http://example.com"; 49 const char kOrigin2[] = "http://chromium.org"; 50 } 51 52 class LocalFileSyncContextTest : public testing::Test { 53 protected: 54 LocalFileSyncContextTest() 55 : thread_bundle_( 56 content::TestBrowserThreadBundle::REAL_FILE_THREAD | 57 content::TestBrowserThreadBundle::REAL_IO_THREAD), 58 status_(SYNC_FILE_ERROR_FAILED), 59 file_error_(base::File::FILE_ERROR_FAILED), 60 async_modify_finished_(false), 61 has_inflight_prepare_for_sync_(false) {} 62 63 virtual void SetUp() OVERRIDE { 64 RegisterSyncableFileSystem(); 65 ASSERT_TRUE(dir_.CreateUniqueTempDir()); 66 in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default())); 67 68 ui_task_runner_ = base::MessageLoop::current()->message_loop_proxy(); 69 io_task_runner_ = BrowserThread::GetMessageLoopProxyForThread( 70 BrowserThread::IO); 71 file_task_runner_ = BrowserThread::GetMessageLoopProxyForThread( 72 BrowserThread::IO); 73 } 74 75 virtual void TearDown() OVERRIDE { 76 RevokeSyncableFileSystem(); 77 } 78 79 void StartPrepareForSync(FileSystemContext* file_system_context, 80 const FileSystemURL& url, 81 LocalFileSyncContext::SyncMode sync_mode, 82 SyncFileMetadata* metadata, 83 FileChangeList* changes, 84 webkit_blob::ScopedFile* snapshot) { 85 ASSERT_TRUE(changes != NULL); 86 ASSERT_FALSE(has_inflight_prepare_for_sync_); 87 status_ = SYNC_STATUS_UNKNOWN; 88 has_inflight_prepare_for_sync_ = true; 89 sync_context_->PrepareForSync( 90 file_system_context, 91 url, 92 sync_mode, 93 base::Bind(&LocalFileSyncContextTest::DidPrepareForSync, 94 base::Unretained(this), metadata, changes, snapshot)); 95 } 96 97 SyncStatusCode PrepareForSync(FileSystemContext* file_system_context, 98 const FileSystemURL& url, 99 LocalFileSyncContext::SyncMode sync_mode, 100 SyncFileMetadata* metadata, 101 FileChangeList* changes, 102 webkit_blob::ScopedFile* snapshot) { 103 StartPrepareForSync(file_system_context, url, sync_mode, 104 metadata, changes, snapshot); 105 base::MessageLoop::current()->Run(); 106 return status_; 107 } 108 109 base::Closure GetPrepareForSyncClosure( 110 FileSystemContext* file_system_context, 111 const FileSystemURL& url, 112 LocalFileSyncContext::SyncMode sync_mode, 113 SyncFileMetadata* metadata, 114 FileChangeList* changes, 115 webkit_blob::ScopedFile* snapshot) { 116 return base::Bind(&LocalFileSyncContextTest::StartPrepareForSync, 117 base::Unretained(this), 118 base::Unretained(file_system_context), 119 url, sync_mode, metadata, changes, snapshot); 120 } 121 122 void DidPrepareForSync(SyncFileMetadata* metadata_out, 123 FileChangeList* changes_out, 124 webkit_blob::ScopedFile* snapshot_out, 125 SyncStatusCode status, 126 const LocalFileSyncInfo& sync_file_info, 127 webkit_blob::ScopedFile snapshot) { 128 ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread()); 129 has_inflight_prepare_for_sync_ = false; 130 status_ = status; 131 *metadata_out = sync_file_info.metadata; 132 *changes_out = sync_file_info.changes; 133 if (snapshot_out) 134 *snapshot_out = snapshot.Pass(); 135 base::MessageLoop::current()->Quit(); 136 } 137 138 SyncStatusCode ApplyRemoteChange(FileSystemContext* file_system_context, 139 const FileChange& change, 140 const base::FilePath& local_path, 141 const FileSystemURL& url, 142 SyncFileType expected_file_type) { 143 SCOPED_TRACE(testing::Message() << "ApplyChange for " << 144 url.DebugString()); 145 146 // First we should call PrepareForSync to disable writing. 147 SyncFileMetadata metadata; 148 FileChangeList changes; 149 EXPECT_EQ(SYNC_STATUS_OK, 150 PrepareForSync(file_system_context, url, 151 LocalFileSyncContext::SYNC_EXCLUSIVE, 152 &metadata, &changes, NULL)); 153 EXPECT_EQ(expected_file_type, metadata.file_type); 154 155 status_ = SYNC_STATUS_UNKNOWN; 156 sync_context_->ApplyRemoteChange( 157 file_system_context, change, local_path, url, 158 base::Bind(&LocalFileSyncContextTest::DidApplyRemoteChange, 159 base::Unretained(this), 160 make_scoped_refptr(file_system_context), url)); 161 base::MessageLoop::current()->Run(); 162 return status_; 163 } 164 165 void DidApplyRemoteChange(FileSystemContext* file_system_context, 166 const FileSystemURL& url, 167 SyncStatusCode status) { 168 status_ = status; 169 sync_context_->FinalizeExclusiveSync( 170 file_system_context, url, 171 status == SYNC_STATUS_OK /* clear_local_changes */, 172 base::MessageLoop::QuitClosure()); 173 } 174 175 void StartModifyFileOnIOThread(CannedSyncableFileSystem* file_system, 176 const FileSystemURL& url) { 177 ASSERT_TRUE(file_system != NULL); 178 if (!io_task_runner_->RunsTasksOnCurrentThread()) { 179 async_modify_finished_ = false; 180 ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread()); 181 io_task_runner_->PostTask( 182 FROM_HERE, 183 base::Bind(&LocalFileSyncContextTest::StartModifyFileOnIOThread, 184 base::Unretained(this), file_system, url)); 185 return; 186 } 187 ASSERT_TRUE(io_task_runner_->RunsTasksOnCurrentThread()); 188 file_error_ = base::File::FILE_ERROR_FAILED; 189 file_system->operation_runner()->Truncate( 190 url, 1, base::Bind(&LocalFileSyncContextTest::DidModifyFile, 191 base::Unretained(this))); 192 } 193 194 base::File::Error WaitUntilModifyFileIsDone() { 195 while (!async_modify_finished_) 196 base::MessageLoop::current()->RunUntilIdle(); 197 return file_error_; 198 } 199 200 void DidModifyFile(base::File::Error error) { 201 if (!ui_task_runner_->RunsTasksOnCurrentThread()) { 202 ASSERT_TRUE(io_task_runner_->RunsTasksOnCurrentThread()); 203 ui_task_runner_->PostTask( 204 FROM_HERE, 205 base::Bind(&LocalFileSyncContextTest::DidModifyFile, 206 base::Unretained(this), error)); 207 return; 208 } 209 ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread()); 210 file_error_ = error; 211 async_modify_finished_ = true; 212 } 213 214 void SimulateFinishSync(FileSystemContext* file_system_context, 215 const FileSystemURL& url, 216 SyncStatusCode status, 217 LocalFileSyncContext::SyncMode sync_mode) { 218 if (sync_mode == LocalFileSyncContext::SYNC_SNAPSHOT) { 219 sync_context_->FinalizeSnapshotSync( 220 file_system_context, url, status, 221 base::Bind(&base::DoNothing)); 222 } else { 223 sync_context_->FinalizeExclusiveSync( 224 file_system_context, url, 225 status == SYNC_STATUS_OK /* clear_local_changes */, 226 base::Bind(&base::DoNothing)); 227 } 228 } 229 230 void PrepareForSync_Basic(LocalFileSyncContext::SyncMode sync_mode, 231 SyncStatusCode simulate_sync_finish_status) { 232 CannedSyncableFileSystem file_system(GURL(kOrigin1), 233 in_memory_env_.get(), 234 io_task_runner_.get(), 235 file_task_runner_.get()); 236 file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 237 sync_context_ = new LocalFileSyncContext( 238 dir_.path(), in_memory_env_.get(), 239 ui_task_runner_.get(), io_task_runner_.get()); 240 ASSERT_EQ(SYNC_STATUS_OK, 241 file_system.MaybeInitializeFileSystemContext( 242 sync_context_.get())); 243 ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem()); 244 245 const FileSystemURL kFile(file_system.URL("file")); 246 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile)); 247 248 SyncFileMetadata metadata; 249 FileChangeList changes; 250 EXPECT_EQ(SYNC_STATUS_OK, 251 PrepareForSync(file_system.file_system_context(), kFile, 252 sync_mode, &metadata, &changes, NULL)); 253 EXPECT_EQ(1U, changes.size()); 254 EXPECT_TRUE(changes.list().back().IsFile()); 255 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); 256 257 // We should see the same set of changes. 258 file_system.GetChangesForURLInTracker(kFile, &changes); 259 EXPECT_EQ(1U, changes.size()); 260 EXPECT_TRUE(changes.list().back().IsFile()); 261 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); 262 263 SimulateFinishSync(file_system.file_system_context(), kFile, 264 simulate_sync_finish_status, sync_mode); 265 266 file_system.GetChangesForURLInTracker(kFile, &changes); 267 if (simulate_sync_finish_status == SYNC_STATUS_OK) { 268 // The change's cleared. 269 EXPECT_TRUE(changes.empty()); 270 } else { 271 EXPECT_EQ(1U, changes.size()); 272 EXPECT_TRUE(changes.list().back().IsFile()); 273 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); 274 } 275 276 sync_context_->ShutdownOnUIThread(); 277 sync_context_ = NULL; 278 279 file_system.TearDown(); 280 } 281 282 void PrepareForSync_WriteDuringSync( 283 LocalFileSyncContext::SyncMode sync_mode) { 284 CannedSyncableFileSystem file_system(GURL(kOrigin1), 285 in_memory_env_.get(), 286 io_task_runner_.get(), 287 file_task_runner_.get()); 288 file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 289 sync_context_ = new LocalFileSyncContext( 290 dir_.path(), in_memory_env_.get(), 291 ui_task_runner_.get(), io_task_runner_.get()); 292 ASSERT_EQ(SYNC_STATUS_OK, 293 file_system.MaybeInitializeFileSystemContext( 294 sync_context_.get())); 295 ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem()); 296 297 const FileSystemURL kFile(file_system.URL("file")); 298 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile)); 299 300 SyncFileMetadata metadata; 301 FileChangeList changes; 302 webkit_blob::ScopedFile snapshot; 303 EXPECT_EQ(SYNC_STATUS_OK, 304 PrepareForSync(file_system.file_system_context(), kFile, 305 sync_mode, &metadata, &changes, &snapshot)); 306 EXPECT_EQ(1U, changes.size()); 307 EXPECT_TRUE(changes.list().back().IsFile()); 308 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); 309 310 EXPECT_EQ(sync_mode == LocalFileSyncContext::SYNC_SNAPSHOT, 311 !snapshot.path().empty()); 312 313 // Tracker keeps same set of changes. 314 file_system.GetChangesForURLInTracker(kFile, &changes); 315 EXPECT_EQ(1U, changes.size()); 316 EXPECT_TRUE(changes.list().back().IsFile()); 317 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); 318 319 StartModifyFileOnIOThread(&file_system, kFile); 320 321 if (sync_mode == LocalFileSyncContext::SYNC_SNAPSHOT) { 322 // Write should succeed. 323 EXPECT_EQ(base::File::FILE_OK, WaitUntilModifyFileIsDone()); 324 } else { 325 base::MessageLoop::current()->RunUntilIdle(); 326 EXPECT_FALSE(async_modify_finished_); 327 } 328 329 SimulateFinishSync(file_system.file_system_context(), kFile, 330 SYNC_STATUS_OK, sync_mode); 331 332 EXPECT_EQ(base::File::FILE_OK, WaitUntilModifyFileIsDone()); 333 334 // Sync succeeded, but the other change that was made during or 335 // after sync is recorded. 336 file_system.GetChangesForURLInTracker(kFile, &changes); 337 EXPECT_EQ(1U, changes.size()); 338 EXPECT_TRUE(changes.list().back().IsFile()); 339 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); 340 341 sync_context_->ShutdownOnUIThread(); 342 sync_context_ = NULL; 343 344 file_system.TearDown(); 345 } 346 347 ScopedEnableSyncFSDirectoryOperation enable_directory_operation_; 348 349 base::ScopedTempDir dir_; 350 scoped_ptr<leveldb::Env> in_memory_env_; 351 352 // These need to remain until the very end. 353 content::TestBrowserThreadBundle thread_bundle_; 354 355 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; 356 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; 357 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_; 358 359 scoped_refptr<LocalFileSyncContext> sync_context_; 360 361 SyncStatusCode status_; 362 base::File::Error file_error_; 363 bool async_modify_finished_; 364 bool has_inflight_prepare_for_sync_; 365 }; 366 367 TEST_F(LocalFileSyncContextTest, ConstructAndDestruct) { 368 sync_context_ = 369 new LocalFileSyncContext( 370 dir_.path(), in_memory_env_.get(), 371 ui_task_runner_.get(), io_task_runner_.get()); 372 sync_context_->ShutdownOnUIThread(); 373 } 374 375 TEST_F(LocalFileSyncContextTest, InitializeFileSystemContext) { 376 CannedSyncableFileSystem file_system(GURL(kOrigin1), 377 in_memory_env_.get(), 378 io_task_runner_.get(), 379 file_task_runner_.get()); 380 file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 381 382 sync_context_ = new LocalFileSyncContext( 383 dir_.path(), in_memory_env_.get(), 384 ui_task_runner_.get(), io_task_runner_.get()); 385 386 // Initializes file_system using |sync_context_|. 387 EXPECT_EQ(SYNC_STATUS_OK, 388 file_system.MaybeInitializeFileSystemContext(sync_context_.get())); 389 390 // Make sure everything's set up for file_system to be able to handle 391 // syncable file system operations. 392 EXPECT_TRUE(file_system.backend()->sync_context() != NULL); 393 EXPECT_TRUE(file_system.backend()->change_tracker() != NULL); 394 EXPECT_EQ(sync_context_.get(), file_system.backend()->sync_context()); 395 396 // Calling MaybeInitialize for the same context multiple times must be ok. 397 EXPECT_EQ(SYNC_STATUS_OK, 398 file_system.MaybeInitializeFileSystemContext(sync_context_.get())); 399 EXPECT_EQ(sync_context_.get(), file_system.backend()->sync_context()); 400 401 // Opens the file_system, perform some operation and see if the change tracker 402 // correctly captures the change. 403 EXPECT_EQ(base::File::FILE_OK, file_system.OpenFileSystem()); 404 405 const FileSystemURL kURL(file_system.URL("foo")); 406 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kURL)); 407 408 FileSystemURLSet urls; 409 file_system.GetChangedURLsInTracker(&urls); 410 ASSERT_EQ(1U, urls.size()); 411 EXPECT_TRUE(ContainsKey(urls, kURL)); 412 413 // Finishing the test. 414 sync_context_->ShutdownOnUIThread(); 415 file_system.TearDown(); 416 } 417 418 TEST_F(LocalFileSyncContextTest, MultipleFileSystemContexts) { 419 CannedSyncableFileSystem file_system1(GURL(kOrigin1), 420 in_memory_env_.get(), 421 io_task_runner_.get(), 422 file_task_runner_.get()); 423 CannedSyncableFileSystem file_system2(GURL(kOrigin2), 424 in_memory_env_.get(), 425 io_task_runner_.get(), 426 file_task_runner_.get()); 427 file_system1.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 428 file_system2.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 429 430 sync_context_ = new LocalFileSyncContext( 431 dir_.path(), in_memory_env_.get(), 432 ui_task_runner_.get(), io_task_runner_.get()); 433 434 // Initializes file_system1 and file_system2. 435 EXPECT_EQ(SYNC_STATUS_OK, 436 file_system1.MaybeInitializeFileSystemContext(sync_context_.get())); 437 EXPECT_EQ(SYNC_STATUS_OK, 438 file_system2.MaybeInitializeFileSystemContext(sync_context_.get())); 439 440 EXPECT_EQ(base::File::FILE_OK, file_system1.OpenFileSystem()); 441 EXPECT_EQ(base::File::FILE_OK, file_system2.OpenFileSystem()); 442 443 const FileSystemURL kURL1(file_system1.URL("foo")); 444 const FileSystemURL kURL2(file_system2.URL("bar")); 445 446 // Creates a file in file_system1. 447 EXPECT_EQ(base::File::FILE_OK, file_system1.CreateFile(kURL1)); 448 449 // file_system1's tracker must have recorded the change. 450 FileSystemURLSet urls; 451 file_system1.GetChangedURLsInTracker(&urls); 452 ASSERT_EQ(1U, urls.size()); 453 EXPECT_TRUE(ContainsKey(urls, kURL1)); 454 455 // file_system1's tracker must have no change. 456 urls.clear(); 457 file_system2.GetChangedURLsInTracker(&urls); 458 ASSERT_TRUE(urls.empty()); 459 460 // Creates a directory in file_system2. 461 EXPECT_EQ(base::File::FILE_OK, file_system2.CreateDirectory(kURL2)); 462 463 // file_system1's tracker must have the change for kURL1 as before. 464 urls.clear(); 465 file_system1.GetChangedURLsInTracker(&urls); 466 ASSERT_EQ(1U, urls.size()); 467 EXPECT_TRUE(ContainsKey(urls, kURL1)); 468 469 // file_system2's tracker now must have the change for kURL2. 470 urls.clear(); 471 file_system2.GetChangedURLsInTracker(&urls); 472 ASSERT_EQ(1U, urls.size()); 473 EXPECT_TRUE(ContainsKey(urls, kURL2)); 474 475 SyncFileMetadata metadata; 476 FileChangeList changes; 477 EXPECT_EQ(SYNC_STATUS_OK, 478 PrepareForSync(file_system1.file_system_context(), kURL1, 479 LocalFileSyncContext::SYNC_EXCLUSIVE, 480 &metadata, &changes, NULL)); 481 EXPECT_EQ(1U, changes.size()); 482 EXPECT_TRUE(changes.list().back().IsFile()); 483 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); 484 EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type); 485 EXPECT_EQ(0, metadata.size); 486 487 changes.clear(); 488 EXPECT_EQ(SYNC_STATUS_OK, 489 PrepareForSync(file_system2.file_system_context(), kURL2, 490 LocalFileSyncContext::SYNC_EXCLUSIVE, 491 &metadata, &changes, NULL)); 492 EXPECT_EQ(1U, changes.size()); 493 EXPECT_FALSE(changes.list().back().IsFile()); 494 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); 495 EXPECT_EQ(SYNC_FILE_TYPE_DIRECTORY, metadata.file_type); 496 EXPECT_EQ(0, metadata.size); 497 498 sync_context_->ShutdownOnUIThread(); 499 sync_context_ = NULL; 500 501 file_system1.TearDown(); 502 file_system2.TearDown(); 503 } 504 505 TEST_F(LocalFileSyncContextTest, PrepareSync_SyncSuccess_Exclusive) { 506 PrepareForSync_Basic(LocalFileSyncContext::SYNC_EXCLUSIVE, 507 SYNC_STATUS_OK); 508 } 509 510 TEST_F(LocalFileSyncContextTest, PrepareSync_SyncSuccess_Snapshot) { 511 PrepareForSync_Basic(LocalFileSyncContext::SYNC_SNAPSHOT, 512 SYNC_STATUS_OK); 513 } 514 515 TEST_F(LocalFileSyncContextTest, PrepareSync_SyncFailure_Exclusive) { 516 PrepareForSync_Basic(LocalFileSyncContext::SYNC_EXCLUSIVE, 517 SYNC_STATUS_FAILED); 518 } 519 520 TEST_F(LocalFileSyncContextTest, PrepareSync_SyncFailure_Snapshot) { 521 PrepareForSync_Basic(LocalFileSyncContext::SYNC_SNAPSHOT, 522 SYNC_STATUS_FAILED); 523 } 524 525 TEST_F(LocalFileSyncContextTest, PrepareSync_WriteDuringSync_Exclusive) { 526 PrepareForSync_WriteDuringSync(LocalFileSyncContext::SYNC_EXCLUSIVE); 527 } 528 529 TEST_F(LocalFileSyncContextTest, PrepareSync_WriteDuringSync_Snapshot) { 530 PrepareForSync_WriteDuringSync(LocalFileSyncContext::SYNC_SNAPSHOT); 531 } 532 533 // LocalFileSyncContextTest.PrepareSyncWhileWriting is flaky on android. 534 // http://crbug.com/239793 535 // It is also flaky on the TSAN v2 bots, and hangs other bots. 536 // http://crbug.com/305905. 537 TEST_F(LocalFileSyncContextTest, DISABLED_PrepareSyncWhileWriting) { 538 CannedSyncableFileSystem file_system(GURL(kOrigin1), 539 in_memory_env_.get(), 540 io_task_runner_.get(), 541 file_task_runner_.get()); 542 file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 543 sync_context_ = new LocalFileSyncContext( 544 dir_.path(), in_memory_env_.get(), 545 ui_task_runner_.get(), io_task_runner_.get()); 546 EXPECT_EQ(SYNC_STATUS_OK, 547 file_system.MaybeInitializeFileSystemContext(sync_context_.get())); 548 549 EXPECT_EQ(base::File::FILE_OK, file_system.OpenFileSystem()); 550 551 const FileSystemURL kURL1(file_system.URL("foo")); 552 553 // Creates a file in file_system. 554 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kURL1)); 555 556 // Kick file write on IO thread. 557 StartModifyFileOnIOThread(&file_system, kURL1); 558 559 // Until the operation finishes PrepareForSync should return BUSY error. 560 SyncFileMetadata metadata; 561 metadata.file_type = SYNC_FILE_TYPE_UNKNOWN; 562 FileChangeList changes; 563 EXPECT_EQ(SYNC_STATUS_FILE_BUSY, 564 PrepareForSync(file_system.file_system_context(), kURL1, 565 LocalFileSyncContext::SYNC_EXCLUSIVE, 566 &metadata, &changes, NULL)); 567 EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type); 568 569 // Register PrepareForSync method to be invoked when kURL1 becomes 570 // syncable. (Actually this may be done after all operations are done 571 // on IO thread in this test.) 572 metadata.file_type = SYNC_FILE_TYPE_UNKNOWN; 573 changes.clear(); 574 sync_context_->RegisterURLForWaitingSync( 575 kURL1, GetPrepareForSyncClosure(file_system.file_system_context(), kURL1, 576 LocalFileSyncContext::SYNC_EXCLUSIVE, 577 &metadata, &changes, NULL)); 578 579 // Wait for the completion. 580 EXPECT_EQ(base::File::FILE_OK, WaitUntilModifyFileIsDone()); 581 582 // The PrepareForSync must have been started; wait until DidPrepareForSync 583 // is done. 584 base::MessageLoop::current()->Run(); 585 ASSERT_FALSE(has_inflight_prepare_for_sync_); 586 587 // Now PrepareForSync should have run and returned OK. 588 EXPECT_EQ(SYNC_STATUS_OK, status_); 589 EXPECT_EQ(1U, changes.size()); 590 EXPECT_TRUE(changes.list().back().IsFile()); 591 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); 592 EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type); 593 EXPECT_EQ(1, metadata.size); 594 595 sync_context_->ShutdownOnUIThread(); 596 sync_context_ = NULL; 597 file_system.TearDown(); 598 } 599 600 TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForDeletion) { 601 CannedSyncableFileSystem file_system(GURL(kOrigin1), 602 in_memory_env_.get(), 603 io_task_runner_.get(), 604 file_task_runner_.get()); 605 file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 606 607 sync_context_ = new LocalFileSyncContext( 608 dir_.path(), in_memory_env_.get(), 609 ui_task_runner_.get(), io_task_runner_.get()); 610 ASSERT_EQ(SYNC_STATUS_OK, 611 file_system.MaybeInitializeFileSystemContext(sync_context_.get())); 612 ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem()); 613 614 // Record the initial usage (likely 0). 615 int64 initial_usage = -1; 616 int64 quota = -1; 617 EXPECT_EQ(quota::kQuotaStatusOk, 618 file_system.GetUsageAndQuota(&initial_usage, "a)); 619 620 // Create a file and directory in the file_system. 621 const FileSystemURL kFile(file_system.URL("file")); 622 const FileSystemURL kDir(file_system.URL("dir")); 623 const FileSystemURL kChild(file_system.URL("dir/child")); 624 625 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile)); 626 EXPECT_EQ(base::File::FILE_OK, file_system.CreateDirectory(kDir)); 627 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kChild)); 628 629 // file_system's change tracker must have recorded the creation. 630 FileSystemURLSet urls; 631 file_system.GetChangedURLsInTracker(&urls); 632 ASSERT_EQ(3U, urls.size()); 633 ASSERT_TRUE(ContainsKey(urls, kFile)); 634 ASSERT_TRUE(ContainsKey(urls, kDir)); 635 ASSERT_TRUE(ContainsKey(urls, kChild)); 636 for (FileSystemURLSet::iterator iter = urls.begin(); 637 iter != urls.end(); ++iter) { 638 file_system.ClearChangeForURLInTracker(*iter); 639 } 640 641 // At this point the usage must be greater than the initial usage. 642 int64 new_usage = -1; 643 EXPECT_EQ(quota::kQuotaStatusOk, 644 file_system.GetUsageAndQuota(&new_usage, "a)); 645 EXPECT_GT(new_usage, initial_usage); 646 647 // Now let's apply remote deletion changes. 648 FileChange change(FileChange::FILE_CHANGE_DELETE, 649 SYNC_FILE_TYPE_FILE); 650 EXPECT_EQ(SYNC_STATUS_OK, 651 ApplyRemoteChange(file_system.file_system_context(), 652 change, base::FilePath(), kFile, 653 SYNC_FILE_TYPE_FILE)); 654 655 // The implementation doesn't check file type for deletion, and it must be ok 656 // even if we don't know if the deletion change was for a file or a directory. 657 change = FileChange(FileChange::FILE_CHANGE_DELETE, 658 SYNC_FILE_TYPE_UNKNOWN); 659 EXPECT_EQ(SYNC_STATUS_OK, 660 ApplyRemoteChange(file_system.file_system_context(), 661 change, base::FilePath(), kDir, 662 SYNC_FILE_TYPE_DIRECTORY)); 663 664 // Check the directory/files are deleted successfully. 665 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 666 file_system.FileExists(kFile)); 667 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 668 file_system.DirectoryExists(kDir)); 669 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 670 file_system.FileExists(kChild)); 671 672 // The changes applied by ApplyRemoteChange should not be recorded in 673 // the change tracker. 674 urls.clear(); 675 file_system.GetChangedURLsInTracker(&urls); 676 EXPECT_TRUE(urls.empty()); 677 678 // The quota usage data must have reflected the deletion. 679 EXPECT_EQ(quota::kQuotaStatusOk, 680 file_system.GetUsageAndQuota(&new_usage, "a)); 681 EXPECT_EQ(new_usage, initial_usage); 682 683 sync_context_->ShutdownOnUIThread(); 684 sync_context_ = NULL; 685 file_system.TearDown(); 686 } 687 688 TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForDeletion_ForRoot) { 689 CannedSyncableFileSystem file_system(GURL(kOrigin1), 690 in_memory_env_.get(), 691 io_task_runner_.get(), 692 file_task_runner_.get()); 693 file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 694 695 sync_context_ = new LocalFileSyncContext( 696 dir_.path(), in_memory_env_.get(), 697 ui_task_runner_.get(), io_task_runner_.get()); 698 ASSERT_EQ(SYNC_STATUS_OK, 699 file_system.MaybeInitializeFileSystemContext(sync_context_.get())); 700 ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem()); 701 702 // Record the initial usage (likely 0). 703 int64 initial_usage = -1; 704 int64 quota = -1; 705 EXPECT_EQ(quota::kQuotaStatusOk, 706 file_system.GetUsageAndQuota(&initial_usage, "a)); 707 708 // Create a file and directory in the file_system. 709 const FileSystemURL kFile(file_system.URL("file")); 710 const FileSystemURL kDir(file_system.URL("dir")); 711 const FileSystemURL kChild(file_system.URL("dir/child")); 712 713 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile)); 714 EXPECT_EQ(base::File::FILE_OK, file_system.CreateDirectory(kDir)); 715 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kChild)); 716 717 // At this point the usage must be greater than the initial usage. 718 int64 new_usage = -1; 719 EXPECT_EQ(quota::kQuotaStatusOk, 720 file_system.GetUsageAndQuota(&new_usage, "a)); 721 EXPECT_GT(new_usage, initial_usage); 722 723 const FileSystemURL kRoot(file_system.URL("")); 724 725 // Now let's apply remote deletion changes for the root. 726 FileChange change(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_DIRECTORY); 727 EXPECT_EQ(SYNC_STATUS_OK, 728 ApplyRemoteChange(file_system.file_system_context(), 729 change, base::FilePath(), kRoot, 730 SYNC_FILE_TYPE_DIRECTORY)); 731 732 // Check the directory/files are deleted successfully. 733 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 734 file_system.FileExists(kFile)); 735 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 736 file_system.DirectoryExists(kDir)); 737 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 738 file_system.FileExists(kChild)); 739 740 // All changes made for the previous creation must have been also reset. 741 FileSystemURLSet urls; 742 file_system.GetChangedURLsInTracker(&urls); 743 EXPECT_TRUE(urls.empty()); 744 745 // The quota usage data must have reflected the deletion. 746 EXPECT_EQ(quota::kQuotaStatusOk, 747 file_system.GetUsageAndQuota(&new_usage, "a)); 748 EXPECT_EQ(new_usage, initial_usage); 749 750 sync_context_->ShutdownOnUIThread(); 751 sync_context_ = NULL; 752 file_system.TearDown(); 753 } 754 755 TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForAddOrUpdate) { 756 base::ScopedTempDir temp_dir; 757 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 758 759 CannedSyncableFileSystem file_system(GURL(kOrigin1), 760 in_memory_env_.get(), 761 io_task_runner_.get(), 762 file_task_runner_.get()); 763 file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 764 765 sync_context_ = new LocalFileSyncContext( 766 dir_.path(), in_memory_env_.get(), 767 ui_task_runner_.get(), io_task_runner_.get()); 768 ASSERT_EQ(SYNC_STATUS_OK, 769 file_system.MaybeInitializeFileSystemContext(sync_context_.get())); 770 ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem()); 771 772 const FileSystemURL kFile1(file_system.URL("file1")); 773 const FileSystemURL kFile2(file_system.URL("file2")); 774 const FileSystemURL kDir(file_system.URL("dir")); 775 776 const char kTestFileData0[] = "0123456789"; 777 const char kTestFileData1[] = "Lorem ipsum!"; 778 const char kTestFileData2[] = "This is sample test data."; 779 780 // Create kFile1 and populate it with kTestFileData0. 781 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile1)); 782 EXPECT_EQ(static_cast<int64>(arraysize(kTestFileData0) - 1), 783 file_system.WriteString(kFile1, kTestFileData0)); 784 785 // kFile2 and kDir are not there yet. 786 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 787 file_system.FileExists(kFile2)); 788 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 789 file_system.DirectoryExists(kDir)); 790 791 // file_system's change tracker must have recorded the creation. 792 FileSystemURLSet urls; 793 file_system.GetChangedURLsInTracker(&urls); 794 ASSERT_EQ(1U, urls.size()); 795 EXPECT_TRUE(ContainsKey(urls, kFile1)); 796 file_system.ClearChangeForURLInTracker(*urls.begin()); 797 798 // Prepare temporary files which represent the remote file data. 799 const base::FilePath kFilePath1(temp_dir.path().Append(FPL("file1"))); 800 const base::FilePath kFilePath2(temp_dir.path().Append(FPL("file2"))); 801 802 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1) - 1), 803 base::WriteFile(kFilePath1, kTestFileData1, 804 arraysize(kTestFileData1) - 1)); 805 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2) - 1), 806 base::WriteFile(kFilePath2, kTestFileData2, 807 arraysize(kTestFileData2) - 1)); 808 809 // Record the usage. 810 int64 usage = -1, new_usage = -1; 811 int64 quota = -1; 812 EXPECT_EQ(quota::kQuotaStatusOk, 813 file_system.GetUsageAndQuota(&usage, "a)); 814 815 // Here in the local filesystem we have: 816 // * kFile1 with kTestFileData0 817 // 818 // In the remote side let's assume we have: 819 // * kFile1 with kTestFileData1 820 // * kFile2 with kTestFileData2 821 // * kDir 822 // 823 // By calling ApplyChange's: 824 // * kFile1 will be updated to have kTestFileData1 825 // * kFile2 will be created 826 // * kDir will be created 827 828 // Apply the remote change to kFile1 (which will update the file). 829 FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 830 SYNC_FILE_TYPE_FILE); 831 EXPECT_EQ(SYNC_STATUS_OK, 832 ApplyRemoteChange(file_system.file_system_context(), 833 change, kFilePath1, kFile1, 834 SYNC_FILE_TYPE_FILE)); 835 836 // Check if the usage has been increased by (kTestFileData1 - kTestFileData0). 837 const int updated_size = 838 arraysize(kTestFileData1) - arraysize(kTestFileData0); 839 EXPECT_EQ(quota::kQuotaStatusOk, 840 file_system.GetUsageAndQuota(&new_usage, "a)); 841 EXPECT_EQ(updated_size, new_usage - usage); 842 843 // Apply remote changes to kFile2 and kDir (should create a file and 844 // directory respectively). 845 // They are non-existent yet so their expected file type (the last 846 // parameter of ApplyRemoteChange) are 847 // SYNC_FILE_TYPE_UNKNOWN. 848 change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 849 SYNC_FILE_TYPE_FILE); 850 EXPECT_EQ(SYNC_STATUS_OK, 851 ApplyRemoteChange(file_system.file_system_context(), 852 change, kFilePath2, kFile2, 853 SYNC_FILE_TYPE_UNKNOWN)); 854 855 change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 856 SYNC_FILE_TYPE_DIRECTORY); 857 EXPECT_EQ(SYNC_STATUS_OK, 858 ApplyRemoteChange(file_system.file_system_context(), 859 change, base::FilePath(), kDir, 860 SYNC_FILE_TYPE_UNKNOWN)); 861 862 // Calling ApplyRemoteChange with different file type should be handled as 863 // overwrite. 864 change = 865 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE); 866 EXPECT_EQ(SYNC_STATUS_OK, 867 ApplyRemoteChange(file_system.file_system_context(), 868 change, 869 kFilePath1, 870 kDir, 871 SYNC_FILE_TYPE_DIRECTORY)); 872 EXPECT_EQ(base::File::FILE_OK, file_system.FileExists(kDir)); 873 874 change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 875 SYNC_FILE_TYPE_DIRECTORY); 876 EXPECT_EQ(SYNC_STATUS_OK, 877 ApplyRemoteChange(file_system.file_system_context(), 878 change, 879 kFilePath1, 880 kDir, 881 SYNC_FILE_TYPE_FILE)); 882 883 // Creating a file/directory must have increased the usage more than 884 // the size of kTestFileData2. 885 new_usage = usage; 886 EXPECT_EQ(quota::kQuotaStatusOk, 887 file_system.GetUsageAndQuota(&new_usage, "a)); 888 EXPECT_GT(new_usage, 889 static_cast<int64>(usage + arraysize(kTestFileData2) - 1)); 890 891 // The changes applied by ApplyRemoteChange should not be recorded in 892 // the change tracker. 893 urls.clear(); 894 file_system.GetChangedURLsInTracker(&urls); 895 EXPECT_TRUE(urls.empty()); 896 897 // Make sure all three files/directory exist. 898 EXPECT_EQ(base::File::FILE_OK, file_system.FileExists(kFile1)); 899 EXPECT_EQ(base::File::FILE_OK, file_system.FileExists(kFile2)); 900 EXPECT_EQ(base::File::FILE_OK, file_system.DirectoryExists(kDir)); 901 902 sync_context_->ShutdownOnUIThread(); 903 file_system.TearDown(); 904 } 905 906 TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForAddOrUpdate_NoParent) { 907 base::ScopedTempDir temp_dir; 908 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 909 910 CannedSyncableFileSystem file_system(GURL(kOrigin1), 911 in_memory_env_.get(), 912 io_task_runner_.get(), 913 file_task_runner_.get()); 914 file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 915 916 sync_context_ = new LocalFileSyncContext( 917 dir_.path(), in_memory_env_.get(), 918 ui_task_runner_.get(), io_task_runner_.get()); 919 ASSERT_EQ(SYNC_STATUS_OK, 920 file_system.MaybeInitializeFileSystemContext(sync_context_.get())); 921 ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem()); 922 923 const char kTestFileData[] = "Lorem ipsum!"; 924 const FileSystemURL kDir(file_system.URL("dir")); 925 const FileSystemURL kFile(file_system.URL("dir/file")); 926 927 // Either kDir or kFile not exist yet. 928 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, file_system.FileExists(kDir)); 929 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, file_system.FileExists(kFile)); 930 931 // Prepare a temporary file which represents remote file data. 932 const base::FilePath kFilePath(temp_dir.path().Append(FPL("file"))); 933 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData) - 1), 934 base::WriteFile(kFilePath, kTestFileData, 935 arraysize(kTestFileData) - 1)); 936 937 // Calling ApplyChange's with kFilePath should create 938 // kFile along with kDir. 939 FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 940 SYNC_FILE_TYPE_FILE); 941 EXPECT_EQ(SYNC_STATUS_OK, 942 ApplyRemoteChange(file_system.file_system_context(), 943 change, kFilePath, kFile, 944 SYNC_FILE_TYPE_UNKNOWN)); 945 946 // The changes applied by ApplyRemoteChange should not be recorded in 947 // the change tracker. 948 FileSystemURLSet urls; 949 urls.clear(); 950 file_system.GetChangedURLsInTracker(&urls); 951 EXPECT_TRUE(urls.empty()); 952 953 // Make sure kDir and kFile are created by ApplyRemoteChange. 954 EXPECT_EQ(base::File::FILE_OK, file_system.FileExists(kFile)); 955 EXPECT_EQ(base::File::FILE_OK, file_system.DirectoryExists(kDir)); 956 957 sync_context_->ShutdownOnUIThread(); 958 file_system.TearDown(); 959 } 960 961 } // namespace sync_file_system 962