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