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