local_file_sync_context_unittest.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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 34using content::BrowserThread; 35using storage::FileSystemContext; 36using storage::FileSystemURL; 37using storage::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 45namespace sync_file_system { 46 47namespace { 48const char kOrigin1[] = "http://example.com"; 49const char kOrigin2[] = "http://chromium.org"; 50} 51 52class 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 storage::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 storage::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 storage::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 storage::ScopedFile* snapshot_out, 125 SyncStatusCode status, 126 const LocalFileSyncInfo& sync_file_info, 127 storage::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 storage::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 base::ScopedTempDir dir_; 348 scoped_ptr<leveldb::Env> in_memory_env_; 349 350 // These need to remain until the very end. 351 content::TestBrowserThreadBundle thread_bundle_; 352 353 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; 354 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; 355 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_; 356 357 scoped_refptr<LocalFileSyncContext> sync_context_; 358 359 SyncStatusCode status_; 360 base::File::Error file_error_; 361 bool async_modify_finished_; 362 bool has_inflight_prepare_for_sync_; 363}; 364 365TEST_F(LocalFileSyncContextTest, ConstructAndDestruct) { 366 sync_context_ = 367 new LocalFileSyncContext( 368 dir_.path(), in_memory_env_.get(), 369 ui_task_runner_.get(), io_task_runner_.get()); 370 sync_context_->ShutdownOnUIThread(); 371} 372 373TEST_F(LocalFileSyncContextTest, InitializeFileSystemContext) { 374 CannedSyncableFileSystem file_system(GURL(kOrigin1), 375 in_memory_env_.get(), 376 io_task_runner_.get(), 377 file_task_runner_.get()); 378 file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 379 380 sync_context_ = new LocalFileSyncContext( 381 dir_.path(), in_memory_env_.get(), 382 ui_task_runner_.get(), io_task_runner_.get()); 383 384 // Initializes file_system using |sync_context_|. 385 EXPECT_EQ(SYNC_STATUS_OK, 386 file_system.MaybeInitializeFileSystemContext(sync_context_.get())); 387 388 // Make sure everything's set up for file_system to be able to handle 389 // syncable file system operations. 390 EXPECT_TRUE(file_system.backend()->sync_context() != NULL); 391 EXPECT_TRUE(file_system.backend()->change_tracker() != NULL); 392 EXPECT_EQ(sync_context_.get(), file_system.backend()->sync_context()); 393 394 // Calling MaybeInitialize for the same context multiple times must be ok. 395 EXPECT_EQ(SYNC_STATUS_OK, 396 file_system.MaybeInitializeFileSystemContext(sync_context_.get())); 397 EXPECT_EQ(sync_context_.get(), file_system.backend()->sync_context()); 398 399 // Opens the file_system, perform some operation and see if the change tracker 400 // correctly captures the change. 401 EXPECT_EQ(base::File::FILE_OK, file_system.OpenFileSystem()); 402 403 const FileSystemURL kURL(file_system.URL("foo")); 404 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kURL)); 405 406 FileSystemURLSet urls; 407 file_system.GetChangedURLsInTracker(&urls); 408 ASSERT_EQ(1U, urls.size()); 409 EXPECT_TRUE(ContainsKey(urls, kURL)); 410 411 // Finishing the test. 412 sync_context_->ShutdownOnUIThread(); 413 file_system.TearDown(); 414} 415 416TEST_F(LocalFileSyncContextTest, MultipleFileSystemContexts) { 417 CannedSyncableFileSystem file_system1(GURL(kOrigin1), 418 in_memory_env_.get(), 419 io_task_runner_.get(), 420 file_task_runner_.get()); 421 CannedSyncableFileSystem file_system2(GURL(kOrigin2), 422 in_memory_env_.get(), 423 io_task_runner_.get(), 424 file_task_runner_.get()); 425 file_system1.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 426 file_system2.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 427 428 sync_context_ = new LocalFileSyncContext( 429 dir_.path(), in_memory_env_.get(), 430 ui_task_runner_.get(), io_task_runner_.get()); 431 432 // Initializes file_system1 and file_system2. 433 EXPECT_EQ(SYNC_STATUS_OK, 434 file_system1.MaybeInitializeFileSystemContext(sync_context_.get())); 435 EXPECT_EQ(SYNC_STATUS_OK, 436 file_system2.MaybeInitializeFileSystemContext(sync_context_.get())); 437 438 EXPECT_EQ(base::File::FILE_OK, file_system1.OpenFileSystem()); 439 EXPECT_EQ(base::File::FILE_OK, file_system2.OpenFileSystem()); 440 441 const FileSystemURL kURL1(file_system1.URL("foo")); 442 const FileSystemURL kURL2(file_system2.URL("bar")); 443 444 // Creates a file in file_system1. 445 EXPECT_EQ(base::File::FILE_OK, file_system1.CreateFile(kURL1)); 446 447 // file_system1's tracker must have recorded the change. 448 FileSystemURLSet urls; 449 file_system1.GetChangedURLsInTracker(&urls); 450 ASSERT_EQ(1U, urls.size()); 451 EXPECT_TRUE(ContainsKey(urls, kURL1)); 452 453 // file_system1's tracker must have no change. 454 urls.clear(); 455 file_system2.GetChangedURLsInTracker(&urls); 456 ASSERT_TRUE(urls.empty()); 457 458 // Creates a directory in file_system2. 459 EXPECT_EQ(base::File::FILE_OK, file_system2.CreateDirectory(kURL2)); 460 461 // file_system1's tracker must have the change for kURL1 as before. 462 urls.clear(); 463 file_system1.GetChangedURLsInTracker(&urls); 464 ASSERT_EQ(1U, urls.size()); 465 EXPECT_TRUE(ContainsKey(urls, kURL1)); 466 467 // file_system2's tracker now must have the change for kURL2. 468 urls.clear(); 469 file_system2.GetChangedURLsInTracker(&urls); 470 ASSERT_EQ(1U, urls.size()); 471 EXPECT_TRUE(ContainsKey(urls, kURL2)); 472 473 SyncFileMetadata metadata; 474 FileChangeList changes; 475 EXPECT_EQ(SYNC_STATUS_OK, 476 PrepareForSync(file_system1.file_system_context(), kURL1, 477 LocalFileSyncContext::SYNC_EXCLUSIVE, 478 &metadata, &changes, NULL)); 479 EXPECT_EQ(1U, changes.size()); 480 EXPECT_TRUE(changes.list().back().IsFile()); 481 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); 482 EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type); 483 EXPECT_EQ(0, metadata.size); 484 485 changes.clear(); 486 EXPECT_EQ(SYNC_STATUS_OK, 487 PrepareForSync(file_system2.file_system_context(), kURL2, 488 LocalFileSyncContext::SYNC_EXCLUSIVE, 489 &metadata, &changes, NULL)); 490 EXPECT_EQ(1U, changes.size()); 491 EXPECT_FALSE(changes.list().back().IsFile()); 492 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); 493 EXPECT_EQ(SYNC_FILE_TYPE_DIRECTORY, metadata.file_type); 494 EXPECT_EQ(0, metadata.size); 495 496 sync_context_->ShutdownOnUIThread(); 497 sync_context_ = NULL; 498 499 file_system1.TearDown(); 500 file_system2.TearDown(); 501} 502 503TEST_F(LocalFileSyncContextTest, PrepareSync_SyncSuccess_Exclusive) { 504 PrepareForSync_Basic(LocalFileSyncContext::SYNC_EXCLUSIVE, 505 SYNC_STATUS_OK); 506} 507 508TEST_F(LocalFileSyncContextTest, PrepareSync_SyncSuccess_Snapshot) { 509 PrepareForSync_Basic(LocalFileSyncContext::SYNC_SNAPSHOT, 510 SYNC_STATUS_OK); 511} 512 513TEST_F(LocalFileSyncContextTest, PrepareSync_SyncFailure_Exclusive) { 514 PrepareForSync_Basic(LocalFileSyncContext::SYNC_EXCLUSIVE, 515 SYNC_STATUS_FAILED); 516} 517 518TEST_F(LocalFileSyncContextTest, PrepareSync_SyncFailure_Snapshot) { 519 PrepareForSync_Basic(LocalFileSyncContext::SYNC_SNAPSHOT, 520 SYNC_STATUS_FAILED); 521} 522 523TEST_F(LocalFileSyncContextTest, PrepareSync_WriteDuringSync_Exclusive) { 524 PrepareForSync_WriteDuringSync(LocalFileSyncContext::SYNC_EXCLUSIVE); 525} 526 527TEST_F(LocalFileSyncContextTest, PrepareSync_WriteDuringSync_Snapshot) { 528 PrepareForSync_WriteDuringSync(LocalFileSyncContext::SYNC_SNAPSHOT); 529} 530 531// LocalFileSyncContextTest.PrepareSyncWhileWriting is flaky on android. 532// http://crbug.com/239793 533// It is also flaky on the TSAN v2 bots, and hangs other bots. 534// http://crbug.com/305905. 535TEST_F(LocalFileSyncContextTest, DISABLED_PrepareSyncWhileWriting) { 536 CannedSyncableFileSystem file_system(GURL(kOrigin1), 537 in_memory_env_.get(), 538 io_task_runner_.get(), 539 file_task_runner_.get()); 540 file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 541 sync_context_ = new LocalFileSyncContext( 542 dir_.path(), in_memory_env_.get(), 543 ui_task_runner_.get(), io_task_runner_.get()); 544 EXPECT_EQ(SYNC_STATUS_OK, 545 file_system.MaybeInitializeFileSystemContext(sync_context_.get())); 546 547 EXPECT_EQ(base::File::FILE_OK, file_system.OpenFileSystem()); 548 549 const FileSystemURL kURL1(file_system.URL("foo")); 550 551 // Creates a file in file_system. 552 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kURL1)); 553 554 // Kick file write on IO thread. 555 StartModifyFileOnIOThread(&file_system, kURL1); 556 557 // Until the operation finishes PrepareForSync should return BUSY error. 558 SyncFileMetadata metadata; 559 metadata.file_type = SYNC_FILE_TYPE_UNKNOWN; 560 FileChangeList changes; 561 EXPECT_EQ(SYNC_STATUS_FILE_BUSY, 562 PrepareForSync(file_system.file_system_context(), kURL1, 563 LocalFileSyncContext::SYNC_EXCLUSIVE, 564 &metadata, &changes, NULL)); 565 EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type); 566 567 // Register PrepareForSync method to be invoked when kURL1 becomes 568 // syncable. (Actually this may be done after all operations are done 569 // on IO thread in this test.) 570 metadata.file_type = SYNC_FILE_TYPE_UNKNOWN; 571 changes.clear(); 572 sync_context_->RegisterURLForWaitingSync( 573 kURL1, GetPrepareForSyncClosure(file_system.file_system_context(), kURL1, 574 LocalFileSyncContext::SYNC_EXCLUSIVE, 575 &metadata, &changes, NULL)); 576 577 // Wait for the completion. 578 EXPECT_EQ(base::File::FILE_OK, WaitUntilModifyFileIsDone()); 579 580 // The PrepareForSync must have been started; wait until DidPrepareForSync 581 // is done. 582 base::MessageLoop::current()->Run(); 583 ASSERT_FALSE(has_inflight_prepare_for_sync_); 584 585 // Now PrepareForSync should have run and returned OK. 586 EXPECT_EQ(SYNC_STATUS_OK, status_); 587 EXPECT_EQ(1U, changes.size()); 588 EXPECT_TRUE(changes.list().back().IsFile()); 589 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); 590 EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type); 591 EXPECT_EQ(1, metadata.size); 592 593 sync_context_->ShutdownOnUIThread(); 594 sync_context_ = NULL; 595 file_system.TearDown(); 596} 597 598TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForDeletion) { 599 CannedSyncableFileSystem file_system(GURL(kOrigin1), 600 in_memory_env_.get(), 601 io_task_runner_.get(), 602 file_task_runner_.get()); 603 file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 604 605 sync_context_ = new LocalFileSyncContext( 606 dir_.path(), in_memory_env_.get(), 607 ui_task_runner_.get(), io_task_runner_.get()); 608 ASSERT_EQ(SYNC_STATUS_OK, 609 file_system.MaybeInitializeFileSystemContext(sync_context_.get())); 610 ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem()); 611 612 // Record the initial usage (likely 0). 613 int64 initial_usage = -1; 614 int64 quota = -1; 615 EXPECT_EQ(storage::kQuotaStatusOk, 616 file_system.GetUsageAndQuota(&initial_usage, "a)); 617 618 // Create a file and directory in the file_system. 619 const FileSystemURL kFile(file_system.URL("file")); 620 const FileSystemURL kDir(file_system.URL("dir")); 621 const FileSystemURL kChild(file_system.URL("dir/child")); 622 623 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile)); 624 EXPECT_EQ(base::File::FILE_OK, file_system.CreateDirectory(kDir)); 625 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kChild)); 626 627 // file_system's change tracker must have recorded the creation. 628 FileSystemURLSet urls; 629 file_system.GetChangedURLsInTracker(&urls); 630 ASSERT_EQ(3U, urls.size()); 631 ASSERT_TRUE(ContainsKey(urls, kFile)); 632 ASSERT_TRUE(ContainsKey(urls, kDir)); 633 ASSERT_TRUE(ContainsKey(urls, kChild)); 634 for (FileSystemURLSet::iterator iter = urls.begin(); 635 iter != urls.end(); ++iter) { 636 file_system.ClearChangeForURLInTracker(*iter); 637 } 638 639 // At this point the usage must be greater than the initial usage. 640 int64 new_usage = -1; 641 EXPECT_EQ(storage::kQuotaStatusOk, 642 file_system.GetUsageAndQuota(&new_usage, "a)); 643 EXPECT_GT(new_usage, initial_usage); 644 645 // Now let's apply remote deletion changes. 646 FileChange change(FileChange::FILE_CHANGE_DELETE, 647 SYNC_FILE_TYPE_FILE); 648 EXPECT_EQ(SYNC_STATUS_OK, 649 ApplyRemoteChange(file_system.file_system_context(), 650 change, base::FilePath(), kFile, 651 SYNC_FILE_TYPE_FILE)); 652 653 // The implementation doesn't check file type for deletion, and it must be ok 654 // even if we don't know if the deletion change was for a file or a directory. 655 change = FileChange(FileChange::FILE_CHANGE_DELETE, 656 SYNC_FILE_TYPE_UNKNOWN); 657 EXPECT_EQ(SYNC_STATUS_OK, 658 ApplyRemoteChange(file_system.file_system_context(), 659 change, base::FilePath(), kDir, 660 SYNC_FILE_TYPE_DIRECTORY)); 661 662 // Check the directory/files are deleted successfully. 663 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 664 file_system.FileExists(kFile)); 665 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 666 file_system.DirectoryExists(kDir)); 667 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 668 file_system.FileExists(kChild)); 669 670 // The changes applied by ApplyRemoteChange should not be recorded in 671 // the change tracker. 672 urls.clear(); 673 file_system.GetChangedURLsInTracker(&urls); 674 EXPECT_TRUE(urls.empty()); 675 676 // The quota usage data must have reflected the deletion. 677 EXPECT_EQ(storage::kQuotaStatusOk, 678 file_system.GetUsageAndQuota(&new_usage, "a)); 679 EXPECT_EQ(new_usage, initial_usage); 680 681 sync_context_->ShutdownOnUIThread(); 682 sync_context_ = NULL; 683 file_system.TearDown(); 684} 685 686TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForDeletion_ForRoot) { 687 CannedSyncableFileSystem file_system(GURL(kOrigin1), 688 in_memory_env_.get(), 689 io_task_runner_.get(), 690 file_task_runner_.get()); 691 file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 692 693 sync_context_ = new LocalFileSyncContext( 694 dir_.path(), in_memory_env_.get(), 695 ui_task_runner_.get(), io_task_runner_.get()); 696 ASSERT_EQ(SYNC_STATUS_OK, 697 file_system.MaybeInitializeFileSystemContext(sync_context_.get())); 698 ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem()); 699 700 // Record the initial usage (likely 0). 701 int64 initial_usage = -1; 702 int64 quota = -1; 703 EXPECT_EQ(storage::kQuotaStatusOk, 704 file_system.GetUsageAndQuota(&initial_usage, "a)); 705 706 // Create a file and directory in the file_system. 707 const FileSystemURL kFile(file_system.URL("file")); 708 const FileSystemURL kDir(file_system.URL("dir")); 709 const FileSystemURL kChild(file_system.URL("dir/child")); 710 711 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile)); 712 EXPECT_EQ(base::File::FILE_OK, file_system.CreateDirectory(kDir)); 713 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kChild)); 714 715 // At this point the usage must be greater than the initial usage. 716 int64 new_usage = -1; 717 EXPECT_EQ(storage::kQuotaStatusOk, 718 file_system.GetUsageAndQuota(&new_usage, "a)); 719 EXPECT_GT(new_usage, initial_usage); 720 721 const FileSystemURL kRoot(file_system.URL("")); 722 723 // Now let's apply remote deletion changes for the root. 724 FileChange change(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_DIRECTORY); 725 EXPECT_EQ(SYNC_STATUS_OK, 726 ApplyRemoteChange(file_system.file_system_context(), 727 change, base::FilePath(), kRoot, 728 SYNC_FILE_TYPE_DIRECTORY)); 729 730 // Check the directory/files are deleted successfully. 731 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 732 file_system.FileExists(kFile)); 733 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 734 file_system.DirectoryExists(kDir)); 735 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 736 file_system.FileExists(kChild)); 737 738 // All changes made for the previous creation must have been also reset. 739 FileSystemURLSet urls; 740 file_system.GetChangedURLsInTracker(&urls); 741 EXPECT_TRUE(urls.empty()); 742 743 // The quota usage data must have reflected the deletion. 744 EXPECT_EQ(storage::kQuotaStatusOk, 745 file_system.GetUsageAndQuota(&new_usage, "a)); 746 EXPECT_EQ(new_usage, initial_usage); 747 748 sync_context_->ShutdownOnUIThread(); 749 sync_context_ = NULL; 750 file_system.TearDown(); 751} 752 753TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForAddOrUpdate) { 754 base::ScopedTempDir temp_dir; 755 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 756 757 CannedSyncableFileSystem file_system(GURL(kOrigin1), 758 in_memory_env_.get(), 759 io_task_runner_.get(), 760 file_task_runner_.get()); 761 file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 762 763 sync_context_ = new LocalFileSyncContext( 764 dir_.path(), in_memory_env_.get(), 765 ui_task_runner_.get(), io_task_runner_.get()); 766 ASSERT_EQ(SYNC_STATUS_OK, 767 file_system.MaybeInitializeFileSystemContext(sync_context_.get())); 768 ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem()); 769 770 const FileSystemURL kFile1(file_system.URL("file1")); 771 const FileSystemURL kFile2(file_system.URL("file2")); 772 const FileSystemURL kDir(file_system.URL("dir")); 773 774 const char kTestFileData0[] = "0123456789"; 775 const char kTestFileData1[] = "Lorem ipsum!"; 776 const char kTestFileData2[] = "This is sample test data."; 777 778 // Create kFile1 and populate it with kTestFileData0. 779 EXPECT_EQ(base::File::FILE_OK, file_system.CreateFile(kFile1)); 780 EXPECT_EQ(static_cast<int64>(arraysize(kTestFileData0) - 1), 781 file_system.WriteString(kFile1, kTestFileData0)); 782 783 // kFile2 and kDir are not there yet. 784 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 785 file_system.FileExists(kFile2)); 786 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, 787 file_system.DirectoryExists(kDir)); 788 789 // file_system's change tracker must have recorded the creation. 790 FileSystemURLSet urls; 791 file_system.GetChangedURLsInTracker(&urls); 792 ASSERT_EQ(1U, urls.size()); 793 EXPECT_TRUE(ContainsKey(urls, kFile1)); 794 file_system.ClearChangeForURLInTracker(*urls.begin()); 795 796 // Prepare temporary files which represent the remote file data. 797 const base::FilePath kFilePath1(temp_dir.path().Append(FPL("file1"))); 798 const base::FilePath kFilePath2(temp_dir.path().Append(FPL("file2"))); 799 800 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1) - 1), 801 base::WriteFile(kFilePath1, kTestFileData1, 802 arraysize(kTestFileData1) - 1)); 803 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2) - 1), 804 base::WriteFile(kFilePath2, kTestFileData2, 805 arraysize(kTestFileData2) - 1)); 806 807 // Record the usage. 808 int64 usage = -1, new_usage = -1; 809 int64 quota = -1; 810 EXPECT_EQ(storage::kQuotaStatusOk, 811 file_system.GetUsageAndQuota(&usage, "a)); 812 813 // Here in the local filesystem we have: 814 // * kFile1 with kTestFileData0 815 // 816 // In the remote side let's assume we have: 817 // * kFile1 with kTestFileData1 818 // * kFile2 with kTestFileData2 819 // * kDir 820 // 821 // By calling ApplyChange's: 822 // * kFile1 will be updated to have kTestFileData1 823 // * kFile2 will be created 824 // * kDir will be created 825 826 // Apply the remote change to kFile1 (which will update the file). 827 FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 828 SYNC_FILE_TYPE_FILE); 829 EXPECT_EQ(SYNC_STATUS_OK, 830 ApplyRemoteChange(file_system.file_system_context(), 831 change, kFilePath1, kFile1, 832 SYNC_FILE_TYPE_FILE)); 833 834 // Check if the usage has been increased by (kTestFileData1 - kTestFileData0). 835 const int updated_size = 836 arraysize(kTestFileData1) - arraysize(kTestFileData0); 837 EXPECT_EQ(storage::kQuotaStatusOk, 838 file_system.GetUsageAndQuota(&new_usage, "a)); 839 EXPECT_EQ(updated_size, new_usage - usage); 840 841 // Apply remote changes to kFile2 and kDir (should create a file and 842 // directory respectively). 843 // They are non-existent yet so their expected file type (the last 844 // parameter of ApplyRemoteChange) are 845 // SYNC_FILE_TYPE_UNKNOWN. 846 change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 847 SYNC_FILE_TYPE_FILE); 848 EXPECT_EQ(SYNC_STATUS_OK, 849 ApplyRemoteChange(file_system.file_system_context(), 850 change, kFilePath2, kFile2, 851 SYNC_FILE_TYPE_UNKNOWN)); 852 853 change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 854 SYNC_FILE_TYPE_DIRECTORY); 855 EXPECT_EQ(SYNC_STATUS_OK, 856 ApplyRemoteChange(file_system.file_system_context(), 857 change, base::FilePath(), kDir, 858 SYNC_FILE_TYPE_UNKNOWN)); 859 860 // Calling ApplyRemoteChange with different file type should be handled as 861 // overwrite. 862 change = 863 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE); 864 EXPECT_EQ(SYNC_STATUS_OK, 865 ApplyRemoteChange(file_system.file_system_context(), 866 change, 867 kFilePath1, 868 kDir, 869 SYNC_FILE_TYPE_DIRECTORY)); 870 EXPECT_EQ(base::File::FILE_OK, file_system.FileExists(kDir)); 871 872 change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 873 SYNC_FILE_TYPE_DIRECTORY); 874 EXPECT_EQ(SYNC_STATUS_OK, 875 ApplyRemoteChange(file_system.file_system_context(), 876 change, 877 kFilePath1, 878 kDir, 879 SYNC_FILE_TYPE_FILE)); 880 881 // Creating a file/directory must have increased the usage more than 882 // the size of kTestFileData2. 883 new_usage = usage; 884 EXPECT_EQ(storage::kQuotaStatusOk, 885 file_system.GetUsageAndQuota(&new_usage, "a)); 886 EXPECT_GT(new_usage, 887 static_cast<int64>(usage + arraysize(kTestFileData2) - 1)); 888 889 // The changes applied by ApplyRemoteChange should not be recorded in 890 // the change tracker. 891 urls.clear(); 892 file_system.GetChangedURLsInTracker(&urls); 893 EXPECT_TRUE(urls.empty()); 894 895 // Make sure all three files/directory exist. 896 EXPECT_EQ(base::File::FILE_OK, file_system.FileExists(kFile1)); 897 EXPECT_EQ(base::File::FILE_OK, file_system.FileExists(kFile2)); 898 EXPECT_EQ(base::File::FILE_OK, file_system.DirectoryExists(kDir)); 899 900 sync_context_->ShutdownOnUIThread(); 901 file_system.TearDown(); 902} 903 904TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForAddOrUpdate_NoParent) { 905 base::ScopedTempDir temp_dir; 906 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 907 908 CannedSyncableFileSystem file_system(GURL(kOrigin1), 909 in_memory_env_.get(), 910 io_task_runner_.get(), 911 file_task_runner_.get()); 912 file_system.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 913 914 sync_context_ = new LocalFileSyncContext( 915 dir_.path(), in_memory_env_.get(), 916 ui_task_runner_.get(), io_task_runner_.get()); 917 ASSERT_EQ(SYNC_STATUS_OK, 918 file_system.MaybeInitializeFileSystemContext(sync_context_.get())); 919 ASSERT_EQ(base::File::FILE_OK, file_system.OpenFileSystem()); 920 921 const char kTestFileData[] = "Lorem ipsum!"; 922 const FileSystemURL kDir(file_system.URL("dir")); 923 const FileSystemURL kFile(file_system.URL("dir/file")); 924 925 // Either kDir or kFile not exist yet. 926 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, file_system.FileExists(kDir)); 927 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, file_system.FileExists(kFile)); 928 929 // Prepare a temporary file which represents remote file data. 930 const base::FilePath kFilePath(temp_dir.path().Append(FPL("file"))); 931 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData) - 1), 932 base::WriteFile(kFilePath, kTestFileData, 933 arraysize(kTestFileData) - 1)); 934 935 // Calling ApplyChange's with kFilePath should create 936 // kFile along with kDir. 937 FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 938 SYNC_FILE_TYPE_FILE); 939 EXPECT_EQ(SYNC_STATUS_OK, 940 ApplyRemoteChange(file_system.file_system_context(), 941 change, kFilePath, kFile, 942 SYNC_FILE_TYPE_UNKNOWN)); 943 944 // The changes applied by ApplyRemoteChange should not be recorded in 945 // the change tracker. 946 FileSystemURLSet urls; 947 urls.clear(); 948 file_system.GetChangedURLsInTracker(&urls); 949 EXPECT_TRUE(urls.empty()); 950 951 // Make sure kDir and kFile are created by ApplyRemoteChange. 952 EXPECT_EQ(base::File::FILE_OK, file_system.FileExists(kFile)); 953 EXPECT_EQ(base::File::FILE_OK, file_system.DirectoryExists(kDir)); 954 955 sync_context_->ShutdownOnUIThread(); 956 file_system.TearDown(); 957} 958 959} // namespace sync_file_system 960