| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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 "webkit/fileapi/syncable/local_file_sync_context.h" | |
| 6 | |
| 7 #include <vector> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/file_util.h" | |
| 11 #include "base/files/file_path.h" | |
| 12 #include "base/message_loop.h" | |
| 13 #include "base/platform_file.h" | |
| 14 #include "base/single_thread_task_runner.h" | |
| 15 #include "base/stl_util.h" | |
| 16 #include "base/threading/thread.h" | |
| 17 #include "testing/gtest/include/gtest/gtest.h" | |
| 18 #include "webkit/blob/mock_blob_url_request_context.h" | |
| 19 #include "webkit/fileapi/file_system_context.h" | |
| 20 #include "webkit/fileapi/file_system_operation.h" | |
| 21 #include "webkit/fileapi/isolated_context.h" | |
| 22 #include "webkit/fileapi/syncable/canned_syncable_file_system.h" | |
| 23 #include "webkit/fileapi/syncable/file_change.h" | |
| 24 #include "webkit/fileapi/syncable/local_file_change_tracker.h" | |
| 25 #include "webkit/fileapi/syncable/sync_file_metadata.h" | |
| 26 #include "webkit/fileapi/syncable/sync_status_code.h" | |
| 27 #include "webkit/fileapi/syncable/syncable_file_system_util.h" | |
| 28 | |
| 29 #define FPL FILE_PATH_LITERAL | |
| 30 | |
| 31 using fileapi::FileSystemContext; | |
| 32 using fileapi::FileSystemURL; | |
| 33 using fileapi::FileSystemURLSet; | |
| 34 | |
| 35 // This tests LocalFileSyncContext behavior in multi-thread / | |
| 36 // multi-file-system-context environment. | |
| 37 // Basic combined tests (single-thread / single-file-system-context) | |
| 38 // that involve LocalFileSyncContext are also in | |
| 39 // syncable_file_system_unittests.cc. | |
| 40 | |
| 41 namespace sync_file_system { | |
| 42 | |
| 43 namespace { | |
| 44 const char kOrigin1[] = "http://example.com"; | |
| 45 const char kOrigin2[] = "http://chromium.org"; | |
| 46 const char kServiceName[] = "test"; | |
| 47 } | |
| 48 | |
| 49 class LocalFileSyncContextTest : public testing::Test { | |
| 50 protected: | |
| 51 LocalFileSyncContextTest() | |
| 52 : status_(SYNC_FILE_ERROR_FAILED), | |
| 53 file_error_(base::PLATFORM_FILE_ERROR_FAILED), | |
| 54 async_modify_finished_(false), | |
| 55 has_inflight_prepare_for_sync_(false) {} | |
| 56 | |
| 57 virtual void SetUp() OVERRIDE { | |
| 58 EXPECT_TRUE(RegisterSyncableFileSystem(kServiceName)); | |
| 59 | |
| 60 io_thread_.reset(new base::Thread("Thread_IO")); | |
| 61 io_thread_->StartWithOptions( | |
| 62 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | |
| 63 | |
| 64 file_thread_.reset(new base::Thread("Thread_File")); | |
| 65 file_thread_->Start(); | |
| 66 | |
| 67 ui_task_runner_ = base::MessageLoop::current()->message_loop_proxy(); | |
| 68 io_task_runner_ = io_thread_->message_loop_proxy(); | |
| 69 file_task_runner_ = file_thread_->message_loop_proxy(); | |
| 70 } | |
| 71 | |
| 72 virtual void TearDown() OVERRIDE { | |
| 73 EXPECT_TRUE(RevokeSyncableFileSystem(kServiceName)); | |
| 74 io_thread_->Stop(); | |
| 75 file_thread_->Stop(); | |
| 76 } | |
| 77 | |
| 78 void StartPrepareForSync(FileSystemContext* file_system_context, | |
| 79 const FileSystemURL& url, | |
| 80 SyncFileMetadata* metadata, | |
| 81 FileChangeList* changes) { | |
| 82 ASSERT_TRUE(changes != NULL); | |
| 83 ASSERT_FALSE(has_inflight_prepare_for_sync_); | |
| 84 status_ = SYNC_STATUS_UNKNOWN; | |
| 85 has_inflight_prepare_for_sync_ = true; | |
| 86 sync_context_->PrepareForSync( | |
| 87 file_system_context, | |
| 88 url, | |
| 89 base::Bind(&LocalFileSyncContextTest::DidPrepareForSync, | |
| 90 base::Unretained(this), metadata, changes)); | |
| 91 } | |
| 92 | |
| 93 SyncStatusCode PrepareForSync(FileSystemContext* file_system_context, | |
| 94 const FileSystemURL& url, | |
| 95 SyncFileMetadata* metadata, | |
| 96 FileChangeList* changes) { | |
| 97 StartPrepareForSync(file_system_context, url, metadata, changes); | |
| 98 base::MessageLoop::current()->Run(); | |
| 99 return status_; | |
| 100 } | |
| 101 | |
| 102 base::Closure GetPrepareForSyncClosure(FileSystemContext* file_system_context, | |
| 103 const FileSystemURL& url, | |
| 104 SyncFileMetadata* metadata, | |
| 105 FileChangeList* changes) { | |
| 106 return base::Bind(&LocalFileSyncContextTest::StartPrepareForSync, | |
| 107 base::Unretained(this), | |
| 108 base::Unretained(file_system_context), | |
| 109 url, metadata, changes); | |
| 110 } | |
| 111 | |
| 112 void DidPrepareForSync(SyncFileMetadata* metadata_out, | |
| 113 FileChangeList* changes_out, | |
| 114 SyncStatusCode status, | |
| 115 const LocalFileSyncInfo& sync_file_info) { | |
| 116 ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread()); | |
| 117 has_inflight_prepare_for_sync_ = false; | |
| 118 status_ = status; | |
| 119 *metadata_out = sync_file_info.metadata; | |
| 120 *changes_out = sync_file_info.changes; | |
| 121 base::MessageLoop::current()->Quit(); | |
| 122 } | |
| 123 | |
| 124 SyncStatusCode ApplyRemoteChange(FileSystemContext* file_system_context, | |
| 125 const FileChange& change, | |
| 126 const base::FilePath& local_path, | |
| 127 const FileSystemURL& url, | |
| 128 SyncFileType expected_file_type) { | |
| 129 SCOPED_TRACE(testing::Message() << "ApplyChange for " << | |
| 130 url.DebugString()); | |
| 131 | |
| 132 // First we should call PrepareForSync to disable writing. | |
| 133 SyncFileMetadata metadata; | |
| 134 FileChangeList changes; | |
| 135 EXPECT_EQ(SYNC_STATUS_OK, | |
| 136 PrepareForSync(file_system_context, url, &metadata, &changes)); | |
| 137 EXPECT_EQ(expected_file_type, metadata.file_type); | |
| 138 | |
| 139 status_ = SYNC_STATUS_UNKNOWN; | |
| 140 sync_context_->ApplyRemoteChange( | |
| 141 file_system_context, change, local_path, url, | |
| 142 base::Bind(&LocalFileSyncContextTest::DidApplyRemoteChange, | |
| 143 base::Unretained(this))); | |
| 144 base::MessageLoop::current()->Run(); | |
| 145 return status_; | |
| 146 } | |
| 147 | |
| 148 void DidApplyRemoteChange(SyncStatusCode status) { | |
| 149 base::MessageLoop::current()->Quit(); | |
| 150 status_ = status; | |
| 151 } | |
| 152 | |
| 153 void StartModifyFileOnIOThread(CannedSyncableFileSystem* file_system, | |
| 154 const FileSystemURL& url) { | |
| 155 async_modify_finished_ = false; | |
| 156 ASSERT_TRUE(file_system != NULL); | |
| 157 if (!io_task_runner_->RunsTasksOnCurrentThread()) { | |
| 158 ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread()); | |
| 159 io_task_runner_->PostTask( | |
| 160 FROM_HERE, | |
| 161 base::Bind(&LocalFileSyncContextTest::StartModifyFileOnIOThread, | |
| 162 base::Unretained(this), file_system, url)); | |
| 163 return; | |
| 164 } | |
| 165 ASSERT_TRUE(io_task_runner_->RunsTasksOnCurrentThread()); | |
| 166 file_error_ = base::PLATFORM_FILE_ERROR_FAILED; | |
| 167 file_system->NewOperation()->Truncate( | |
| 168 url, 1, base::Bind(&LocalFileSyncContextTest::DidModifyFile, | |
| 169 base::Unretained(this))); | |
| 170 } | |
| 171 | |
| 172 base::PlatformFileError WaitUntilModifyFileIsDone() { | |
| 173 while (!async_modify_finished_) | |
| 174 base::MessageLoop::current()->RunUntilIdle(); | |
| 175 return file_error_; | |
| 176 } | |
| 177 | |
| 178 void DidModifyFile(base::PlatformFileError error) { | |
| 179 if (!ui_task_runner_->RunsTasksOnCurrentThread()) { | |
| 180 ASSERT_TRUE(io_task_runner_->RunsTasksOnCurrentThread()); | |
| 181 ui_task_runner_->PostTask( | |
| 182 FROM_HERE, | |
| 183 base::Bind(&LocalFileSyncContextTest::DidModifyFile, | |
| 184 base::Unretained(this), error)); | |
| 185 return; | |
| 186 } | |
| 187 ASSERT_TRUE(ui_task_runner_->RunsTasksOnCurrentThread()); | |
| 188 file_error_ = error; | |
| 189 async_modify_finished_ = true; | |
| 190 } | |
| 191 | |
| 192 // These need to remain until the very end. | |
| 193 scoped_ptr<base::Thread> io_thread_; | |
| 194 scoped_ptr<base::Thread> file_thread_; | |
| 195 base::MessageLoop loop_; | |
| 196 | |
| 197 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; | |
| 198 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; | |
| 199 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_; | |
| 200 | |
| 201 scoped_refptr<LocalFileSyncContext> sync_context_; | |
| 202 | |
| 203 SyncStatusCode status_; | |
| 204 base::PlatformFileError file_error_; | |
| 205 bool async_modify_finished_; | |
| 206 bool has_inflight_prepare_for_sync_; | |
| 207 }; | |
| 208 | |
| 209 TEST_F(LocalFileSyncContextTest, ConstructAndDestruct) { | |
| 210 sync_context_ = new LocalFileSyncContext( | |
| 211 ui_task_runner_, io_task_runner_); | |
| 212 sync_context_->ShutdownOnUIThread(); | |
| 213 } | |
| 214 | |
| 215 TEST_F(LocalFileSyncContextTest, InitializeFileSystemContext) { | |
| 216 CannedSyncableFileSystem file_system(GURL(kOrigin1), kServiceName, | |
| 217 io_task_runner_, file_task_runner_); | |
| 218 file_system.SetUp(); | |
| 219 | |
| 220 sync_context_ = new LocalFileSyncContext(ui_task_runner_, io_task_runner_); | |
| 221 | |
| 222 // Initializes file_system using |sync_context_|. | |
| 223 EXPECT_EQ(SYNC_STATUS_OK, | |
| 224 file_system.MaybeInitializeFileSystemContext(sync_context_)); | |
| 225 | |
| 226 // Make sure everything's set up for file_system to be able to handle | |
| 227 // syncable file system operations. | |
| 228 EXPECT_TRUE(file_system.file_system_context()->sync_context() != NULL); | |
| 229 EXPECT_TRUE(file_system.file_system_context()->change_tracker() != NULL); | |
| 230 EXPECT_EQ(sync_context_.get(), | |
| 231 file_system.file_system_context()->sync_context()); | |
| 232 | |
| 233 // Calling MaybeInitialize for the same context multiple times must be ok. | |
| 234 EXPECT_EQ(SYNC_STATUS_OK, | |
| 235 file_system.MaybeInitializeFileSystemContext(sync_context_)); | |
| 236 EXPECT_EQ(sync_context_.get(), | |
| 237 file_system.file_system_context()->sync_context()); | |
| 238 | |
| 239 // Opens the file_system, perform some operation and see if the change tracker | |
| 240 // correctly captures the change. | |
| 241 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.OpenFileSystem()); | |
| 242 | |
| 243 const FileSystemURL kURL(file_system.URL("foo")); | |
| 244 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.CreateFile(kURL)); | |
| 245 | |
| 246 FileSystemURLSet urls; | |
| 247 file_system.GetChangedURLsInTracker(&urls); | |
| 248 ASSERT_EQ(1U, urls.size()); | |
| 249 EXPECT_TRUE(ContainsKey(urls, kURL)); | |
| 250 | |
| 251 // Finishing the test. | |
| 252 sync_context_->ShutdownOnUIThread(); | |
| 253 file_system.TearDown(); | |
| 254 } | |
| 255 | |
| 256 TEST_F(LocalFileSyncContextTest, MultipleFileSystemContexts) { | |
| 257 CannedSyncableFileSystem file_system1(GURL(kOrigin1), kServiceName, | |
| 258 io_task_runner_, file_task_runner_); | |
| 259 CannedSyncableFileSystem file_system2(GURL(kOrigin2), kServiceName, | |
| 260 io_task_runner_, file_task_runner_); | |
| 261 file_system1.SetUp(); | |
| 262 file_system2.SetUp(); | |
| 263 | |
| 264 sync_context_ = new LocalFileSyncContext(ui_task_runner_, io_task_runner_); | |
| 265 | |
| 266 // Initializes file_system1 and file_system2. | |
| 267 EXPECT_EQ(SYNC_STATUS_OK, | |
| 268 file_system1.MaybeInitializeFileSystemContext(sync_context_)); | |
| 269 EXPECT_EQ(SYNC_STATUS_OK, | |
| 270 file_system2.MaybeInitializeFileSystemContext(sync_context_)); | |
| 271 | |
| 272 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system1.OpenFileSystem()); | |
| 273 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system2.OpenFileSystem()); | |
| 274 | |
| 275 const FileSystemURL kURL1(file_system1.URL("foo")); | |
| 276 const FileSystemURL kURL2(file_system2.URL("bar")); | |
| 277 | |
| 278 // Creates a file in file_system1. | |
| 279 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system1.CreateFile(kURL1)); | |
| 280 | |
| 281 // file_system1's tracker must have recorded the change. | |
| 282 FileSystemURLSet urls; | |
| 283 file_system1.GetChangedURLsInTracker(&urls); | |
| 284 ASSERT_EQ(1U, urls.size()); | |
| 285 EXPECT_TRUE(ContainsKey(urls, kURL1)); | |
| 286 | |
| 287 // file_system1's tracker must have no change. | |
| 288 urls.clear(); | |
| 289 file_system2.GetChangedURLsInTracker(&urls); | |
| 290 ASSERT_TRUE(urls.empty()); | |
| 291 | |
| 292 // Creates a directory in file_system2. | |
| 293 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system2.CreateDirectory(kURL2)); | |
| 294 | |
| 295 // file_system1's tracker must have the change for kURL1 as before. | |
| 296 urls.clear(); | |
| 297 file_system1.GetChangedURLsInTracker(&urls); | |
| 298 ASSERT_EQ(1U, urls.size()); | |
| 299 EXPECT_TRUE(ContainsKey(urls, kURL1)); | |
| 300 | |
| 301 // file_system2's tracker now must have the change for kURL2. | |
| 302 urls.clear(); | |
| 303 file_system2.GetChangedURLsInTracker(&urls); | |
| 304 ASSERT_EQ(1U, urls.size()); | |
| 305 EXPECT_TRUE(ContainsKey(urls, kURL2)); | |
| 306 | |
| 307 SyncFileMetadata metadata; | |
| 308 FileChangeList changes; | |
| 309 EXPECT_EQ(SYNC_STATUS_OK, | |
| 310 PrepareForSync(file_system1.file_system_context(), kURL1, | |
| 311 &metadata, &changes)); | |
| 312 EXPECT_EQ(1U, changes.size()); | |
| 313 EXPECT_TRUE(changes.list().back().IsFile()); | |
| 314 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); | |
| 315 EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type); | |
| 316 EXPECT_EQ(0, metadata.size); | |
| 317 | |
| 318 changes.clear(); | |
| 319 EXPECT_EQ(SYNC_STATUS_OK, | |
| 320 PrepareForSync(file_system2.file_system_context(), kURL2, | |
| 321 &metadata, &changes)); | |
| 322 EXPECT_EQ(1U, changes.size()); | |
| 323 EXPECT_FALSE(changes.list().back().IsFile()); | |
| 324 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); | |
| 325 EXPECT_EQ(SYNC_FILE_TYPE_DIRECTORY, metadata.file_type); | |
| 326 EXPECT_EQ(0, metadata.size); | |
| 327 | |
| 328 sync_context_->ShutdownOnUIThread(); | |
| 329 sync_context_ = NULL; | |
| 330 | |
| 331 file_system1.TearDown(); | |
| 332 file_system2.TearDown(); | |
| 333 } | |
| 334 | |
| 335 // LocalFileSyncContextTest.PrepareSyncWhileWriting is flaky on android. | |
| 336 // http://crbug.com/239793 | |
| 337 #if defined(OS_ANDROID) | |
| 338 #define MAYBE_PrepareSyncWhileWriting DISABLED_PrepareSyncWhileWriting | |
| 339 #else | |
| 340 #define MAYBE_PrepareSyncWhileWriting PrepareSyncWhileWriting | |
| 341 #endif | |
| 342 TEST_F(LocalFileSyncContextTest, MAYBE_PrepareSyncWhileWriting) { | |
| 343 CannedSyncableFileSystem file_system(GURL(kOrigin1), kServiceName, | |
| 344 io_task_runner_, file_task_runner_); | |
| 345 file_system.SetUp(); | |
| 346 sync_context_ = new LocalFileSyncContext(ui_task_runner_, io_task_runner_); | |
| 347 EXPECT_EQ(SYNC_STATUS_OK, | |
| 348 file_system.MaybeInitializeFileSystemContext(sync_context_)); | |
| 349 | |
| 350 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.OpenFileSystem()); | |
| 351 | |
| 352 const FileSystemURL kURL1(file_system.URL("foo")); | |
| 353 | |
| 354 // Creates a file in file_system. | |
| 355 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.CreateFile(kURL1)); | |
| 356 | |
| 357 // Kick file write on IO thread. | |
| 358 StartModifyFileOnIOThread(&file_system, kURL1); | |
| 359 | |
| 360 // Until the operation finishes PrepareForSync should return BUSY error. | |
| 361 SyncFileMetadata metadata; | |
| 362 metadata.file_type = SYNC_FILE_TYPE_UNKNOWN; | |
| 363 FileChangeList changes; | |
| 364 EXPECT_EQ(SYNC_STATUS_FILE_BUSY, | |
| 365 PrepareForSync(file_system.file_system_context(), | |
| 366 kURL1, &metadata, &changes)); | |
| 367 EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type); | |
| 368 | |
| 369 // Register PrepareForSync method to be invoked when kURL1 becomes | |
| 370 // syncable. (Actually this may be done after all operations are done | |
| 371 // on IO thread in this test.) | |
| 372 metadata.file_type = SYNC_FILE_TYPE_UNKNOWN; | |
| 373 changes.clear(); | |
| 374 sync_context_->RegisterURLForWaitingSync( | |
| 375 kURL1, GetPrepareForSyncClosure(file_system.file_system_context(), | |
| 376 kURL1, &metadata, &changes)); | |
| 377 | |
| 378 // Wait for the completion. | |
| 379 EXPECT_EQ(base::PLATFORM_FILE_OK, WaitUntilModifyFileIsDone()); | |
| 380 | |
| 381 // The PrepareForSync must have been started; wait until DidPrepareForSync | |
| 382 // is done. | |
| 383 base::MessageLoop::current()->Run(); | |
| 384 ASSERT_FALSE(has_inflight_prepare_for_sync_); | |
| 385 | |
| 386 // Now PrepareForSync should have run and returned OK. | |
| 387 EXPECT_EQ(SYNC_STATUS_OK, status_); | |
| 388 EXPECT_EQ(1U, changes.size()); | |
| 389 EXPECT_TRUE(changes.list().back().IsFile()); | |
| 390 EXPECT_TRUE(changes.list().back().IsAddOrUpdate()); | |
| 391 EXPECT_EQ(SYNC_FILE_TYPE_FILE, metadata.file_type); | |
| 392 EXPECT_EQ(1, metadata.size); | |
| 393 | |
| 394 sync_context_->ShutdownOnUIThread(); | |
| 395 sync_context_ = NULL; | |
| 396 file_system.TearDown(); | |
| 397 } | |
| 398 | |
| 399 TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForDeletion) { | |
| 400 CannedSyncableFileSystem file_system(GURL(kOrigin1), kServiceName, | |
| 401 io_task_runner_, file_task_runner_); | |
| 402 file_system.SetUp(); | |
| 403 | |
| 404 sync_context_ = new LocalFileSyncContext(ui_task_runner_, io_task_runner_); | |
| 405 ASSERT_EQ(SYNC_STATUS_OK, | |
| 406 file_system.MaybeInitializeFileSystemContext(sync_context_)); | |
| 407 ASSERT_EQ(base::PLATFORM_FILE_OK, file_system.OpenFileSystem()); | |
| 408 | |
| 409 // Record the initial usage (likely 0). | |
| 410 int64 initial_usage = -1; | |
| 411 int64 quota = -1; | |
| 412 EXPECT_EQ(quota::kQuotaStatusOk, | |
| 413 file_system.GetUsageAndQuota(&initial_usage, "a)); | |
| 414 | |
| 415 // Create a file and directory in the file_system. | |
| 416 const FileSystemURL kFile(file_system.URL("file")); | |
| 417 const FileSystemURL kDir(file_system.URL("dir")); | |
| 418 const FileSystemURL kChild(file_system.URL("dir/child")); | |
| 419 | |
| 420 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.CreateFile(kFile)); | |
| 421 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.CreateDirectory(kDir)); | |
| 422 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.CreateFile(kChild)); | |
| 423 | |
| 424 // file_system's change tracker must have recorded the creation. | |
| 425 FileSystemURLSet urls; | |
| 426 file_system.GetChangedURLsInTracker(&urls); | |
| 427 ASSERT_EQ(3U, urls.size()); | |
| 428 ASSERT_TRUE(ContainsKey(urls, kFile)); | |
| 429 ASSERT_TRUE(ContainsKey(urls, kDir)); | |
| 430 ASSERT_TRUE(ContainsKey(urls, kChild)); | |
| 431 for (FileSystemURLSet::iterator iter = urls.begin(); | |
| 432 iter != urls.end(); ++iter) { | |
| 433 file_system.ClearChangeForURLInTracker(*iter); | |
| 434 } | |
| 435 | |
| 436 // At this point the usage must be greater than the initial usage. | |
| 437 int64 new_usage = -1; | |
| 438 EXPECT_EQ(quota::kQuotaStatusOk, | |
| 439 file_system.GetUsageAndQuota(&new_usage, "a)); | |
| 440 EXPECT_GT(new_usage, initial_usage); | |
| 441 | |
| 442 // Now let's apply remote deletion changes. | |
| 443 FileChange change(FileChange::FILE_CHANGE_DELETE, | |
| 444 SYNC_FILE_TYPE_FILE); | |
| 445 EXPECT_EQ(SYNC_STATUS_OK, | |
| 446 ApplyRemoteChange(file_system.file_system_context(), | |
| 447 change, base::FilePath(), kFile, | |
| 448 SYNC_FILE_TYPE_FILE)); | |
| 449 | |
| 450 // The implementation doesn't check file type for deletion, and it must be ok | |
| 451 // even if we don't know if the deletion change was for a file or a directory. | |
| 452 change = FileChange(FileChange::FILE_CHANGE_DELETE, | |
| 453 SYNC_FILE_TYPE_UNKNOWN); | |
| 454 EXPECT_EQ(SYNC_STATUS_OK, | |
| 455 ApplyRemoteChange(file_system.file_system_context(), | |
| 456 change, base::FilePath(), kDir, | |
| 457 SYNC_FILE_TYPE_DIRECTORY)); | |
| 458 | |
| 459 // Check the directory/files are deleted successfully. | |
| 460 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, | |
| 461 file_system.FileExists(kFile)); | |
| 462 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, | |
| 463 file_system.DirectoryExists(kDir)); | |
| 464 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, | |
| 465 file_system.FileExists(kChild)); | |
| 466 | |
| 467 // The changes applied by ApplyRemoteChange should not be recorded in | |
| 468 // the change tracker. | |
| 469 urls.clear(); | |
| 470 file_system.GetChangedURLsInTracker(&urls); | |
| 471 EXPECT_TRUE(urls.empty()); | |
| 472 | |
| 473 // The quota usage data must have reflected the deletion. | |
| 474 EXPECT_EQ(quota::kQuotaStatusOk, | |
| 475 file_system.GetUsageAndQuota(&new_usage, "a)); | |
| 476 EXPECT_EQ(new_usage, initial_usage); | |
| 477 | |
| 478 sync_context_->ShutdownOnUIThread(); | |
| 479 sync_context_ = NULL; | |
| 480 file_system.TearDown(); | |
| 481 } | |
| 482 | |
| 483 TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForAddOrUpdate) { | |
| 484 base::ScopedTempDir temp_dir; | |
| 485 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
| 486 | |
| 487 CannedSyncableFileSystem file_system(GURL(kOrigin1), kServiceName, | |
| 488 io_task_runner_, file_task_runner_); | |
| 489 file_system.SetUp(); | |
| 490 | |
| 491 sync_context_ = new LocalFileSyncContext(ui_task_runner_, io_task_runner_); | |
| 492 ASSERT_EQ(SYNC_STATUS_OK, | |
| 493 file_system.MaybeInitializeFileSystemContext(sync_context_)); | |
| 494 ASSERT_EQ(base::PLATFORM_FILE_OK, file_system.OpenFileSystem()); | |
| 495 | |
| 496 const FileSystemURL kFile1(file_system.URL("file1")); | |
| 497 const FileSystemURL kFile2(file_system.URL("file2")); | |
| 498 const FileSystemURL kDir(file_system.URL("dir")); | |
| 499 | |
| 500 const char kTestFileData0[] = "0123456789"; | |
| 501 const char kTestFileData1[] = "Lorem ipsum!"; | |
| 502 const char kTestFileData2[] = "This is sample test data."; | |
| 503 | |
| 504 // Create kFile1 and populate it with kTestFileData0. | |
| 505 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.CreateFile(kFile1)); | |
| 506 EXPECT_EQ(static_cast<int64>(arraysize(kTestFileData0) - 1), | |
| 507 file_system.WriteString(kFile1, kTestFileData0)); | |
| 508 | |
| 509 // kFile2 and kDir are not there yet. | |
| 510 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, | |
| 511 file_system.FileExists(kFile2)); | |
| 512 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, | |
| 513 file_system.DirectoryExists(kDir)); | |
| 514 | |
| 515 // file_system's change tracker must have recorded the creation. | |
| 516 FileSystemURLSet urls; | |
| 517 file_system.GetChangedURLsInTracker(&urls); | |
| 518 ASSERT_EQ(1U, urls.size()); | |
| 519 EXPECT_TRUE(ContainsKey(urls, kFile1)); | |
| 520 file_system.ClearChangeForURLInTracker(*urls.begin()); | |
| 521 | |
| 522 // Prepare temporary files which represent the remote file data. | |
| 523 const base::FilePath kFilePath1(temp_dir.path().Append(FPL("file1"))); | |
| 524 const base::FilePath kFilePath2(temp_dir.path().Append(FPL("file2"))); | |
| 525 | |
| 526 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1) - 1), | |
| 527 file_util::WriteFile(kFilePath1, kTestFileData1, | |
| 528 arraysize(kTestFileData1) - 1)); | |
| 529 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2) - 1), | |
| 530 file_util::WriteFile(kFilePath2, kTestFileData2, | |
| 531 arraysize(kTestFileData2) - 1)); | |
| 532 | |
| 533 // Record the usage. | |
| 534 int64 usage = -1, new_usage = -1; | |
| 535 int64 quota = -1; | |
| 536 EXPECT_EQ(quota::kQuotaStatusOk, | |
| 537 file_system.GetUsageAndQuota(&usage, "a)); | |
| 538 | |
| 539 // Here in the local filesystem we have: | |
| 540 // * kFile1 with kTestFileData0 | |
| 541 // | |
| 542 // In the remote side let's assume we have: | |
| 543 // * kFile1 with kTestFileData1 | |
| 544 // * kFile2 with kTestFileData2 | |
| 545 // * kDir | |
| 546 // | |
| 547 // By calling ApplyChange's: | |
| 548 // * kFile1 will be updated to have kTestFileData1 | |
| 549 // * kFile2 will be created | |
| 550 // * kDir will be created | |
| 551 | |
| 552 // Apply the remote change to kFile1 (which will update the file). | |
| 553 FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE, | |
| 554 SYNC_FILE_TYPE_FILE); | |
| 555 EXPECT_EQ(SYNC_STATUS_OK, | |
| 556 ApplyRemoteChange(file_system.file_system_context(), | |
| 557 change, kFilePath1, kFile1, | |
| 558 SYNC_FILE_TYPE_FILE)); | |
| 559 | |
| 560 // Check if the usage has been increased by (kTestFileData1 - kTestFileData0). | |
| 561 const int updated_size = | |
| 562 arraysize(kTestFileData1) - arraysize(kTestFileData0); | |
| 563 EXPECT_EQ(quota::kQuotaStatusOk, | |
| 564 file_system.GetUsageAndQuota(&new_usage, "a)); | |
| 565 EXPECT_EQ(updated_size, new_usage - usage); | |
| 566 | |
| 567 // Apply remote changes to kFile2 and kDir (should create a file and | |
| 568 // directory respectively). | |
| 569 // They are non-existent yet so their expected file type (the last | |
| 570 // parameter of ApplyRemoteChange) are | |
| 571 // SYNC_FILE_TYPE_UNKNOWN. | |
| 572 change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, | |
| 573 SYNC_FILE_TYPE_FILE); | |
| 574 EXPECT_EQ(SYNC_STATUS_OK, | |
| 575 ApplyRemoteChange(file_system.file_system_context(), | |
| 576 change, kFilePath2, kFile2, | |
| 577 SYNC_FILE_TYPE_UNKNOWN)); | |
| 578 | |
| 579 change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, | |
| 580 SYNC_FILE_TYPE_DIRECTORY); | |
| 581 EXPECT_EQ(SYNC_STATUS_OK, | |
| 582 ApplyRemoteChange(file_system.file_system_context(), | |
| 583 change, base::FilePath(), kDir, | |
| 584 SYNC_FILE_TYPE_UNKNOWN)); | |
| 585 | |
| 586 // Calling ApplyRemoteChange with different file type should be handled as | |
| 587 // overwrite. | |
| 588 change = | |
| 589 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE); | |
| 590 EXPECT_EQ(SYNC_STATUS_OK, | |
| 591 ApplyRemoteChange(file_system.file_system_context(), | |
| 592 change, | |
| 593 kFilePath1, | |
| 594 kDir, | |
| 595 SYNC_FILE_TYPE_DIRECTORY)); | |
| 596 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.FileExists(kDir)); | |
| 597 | |
| 598 change = FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, | |
| 599 SYNC_FILE_TYPE_DIRECTORY); | |
| 600 EXPECT_EQ(SYNC_STATUS_OK, | |
| 601 ApplyRemoteChange(file_system.file_system_context(), | |
| 602 change, | |
| 603 kFilePath1, | |
| 604 kDir, | |
| 605 SYNC_FILE_TYPE_FILE)); | |
| 606 | |
| 607 // Creating a file/directory must have increased the usage more than | |
| 608 // the size of kTestFileData2. | |
| 609 new_usage = usage; | |
| 610 EXPECT_EQ(quota::kQuotaStatusOk, | |
| 611 file_system.GetUsageAndQuota(&new_usage, "a)); | |
| 612 EXPECT_GT(new_usage, | |
| 613 static_cast<int64>(usage + arraysize(kTestFileData2) - 1)); | |
| 614 | |
| 615 // The changes applied by ApplyRemoteChange should not be recorded in | |
| 616 // the change tracker. | |
| 617 urls.clear(); | |
| 618 file_system.GetChangedURLsInTracker(&urls); | |
| 619 EXPECT_TRUE(urls.empty()); | |
| 620 | |
| 621 // Make sure all three files/directory exist. | |
| 622 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.FileExists(kFile1)); | |
| 623 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.FileExists(kFile2)); | |
| 624 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.DirectoryExists(kDir)); | |
| 625 | |
| 626 sync_context_->ShutdownOnUIThread(); | |
| 627 file_system.TearDown(); | |
| 628 } | |
| 629 | |
| 630 TEST_F(LocalFileSyncContextTest, ApplyRemoteChangeForAddOrUpdate_NoParent) { | |
| 631 base::ScopedTempDir temp_dir; | |
| 632 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
| 633 | |
| 634 CannedSyncableFileSystem file_system(GURL(kOrigin1), kServiceName, | |
| 635 io_task_runner_, file_task_runner_); | |
| 636 file_system.SetUp(); | |
| 637 | |
| 638 sync_context_ = new LocalFileSyncContext(ui_task_runner_, io_task_runner_); | |
| 639 ASSERT_EQ(SYNC_STATUS_OK, | |
| 640 file_system.MaybeInitializeFileSystemContext(sync_context_)); | |
| 641 ASSERT_EQ(base::PLATFORM_FILE_OK, file_system.OpenFileSystem()); | |
| 642 | |
| 643 const char kTestFileData[] = "Lorem ipsum!"; | |
| 644 const FileSystemURL kDir(file_system.URL("dir")); | |
| 645 const FileSystemURL kFile(file_system.URL("dir/file")); | |
| 646 | |
| 647 // Either kDir or kFile not exist yet. | |
| 648 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, file_system.FileExists(kDir)); | |
| 649 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, file_system.FileExists(kFile)); | |
| 650 | |
| 651 // Prepare a temporary file which represents remote file data. | |
| 652 const base::FilePath kFilePath(temp_dir.path().Append(FPL("file"))); | |
| 653 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData) - 1), | |
| 654 file_util::WriteFile(kFilePath, kTestFileData, | |
| 655 arraysize(kTestFileData) - 1)); | |
| 656 | |
| 657 // Calling ApplyChange's with kFilePath should create | |
| 658 // kFile along with kDir. | |
| 659 FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE, | |
| 660 SYNC_FILE_TYPE_FILE); | |
| 661 EXPECT_EQ(SYNC_STATUS_OK, | |
| 662 ApplyRemoteChange(file_system.file_system_context(), | |
| 663 change, kFilePath, kFile, | |
| 664 SYNC_FILE_TYPE_UNKNOWN)); | |
| 665 | |
| 666 // The changes applied by ApplyRemoteChange should not be recorded in | |
| 667 // the change tracker. | |
| 668 FileSystemURLSet urls; | |
| 669 urls.clear(); | |
| 670 file_system.GetChangedURLsInTracker(&urls); | |
| 671 EXPECT_TRUE(urls.empty()); | |
| 672 | |
| 673 // Make sure kDir and kFile are created by ApplyRemoteChange. | |
| 674 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.FileExists(kFile)); | |
| 675 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system.DirectoryExists(kDir)); | |
| 676 | |
| 677 sync_context_->ShutdownOnUIThread(); | |
| 678 file_system.TearDown(); | |
| 679 } | |
| 680 | |
| 681 } // namespace sync_file_system | |
| OLD | NEW |