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 <string> 6 7 #include "base/basictypes.h" 8 #include "base/file_util.h" 9 #include "base/location.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/message_loop/message_loop.h" 12 #include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h" 13 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h" 14 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h" 15 #include "chrome/browser/sync_file_system/local/local_file_sync_status.h" 16 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h" 17 #include "chrome/browser/sync_file_system/local/syncable_file_operation_runner.h" 18 #include "chrome/browser/sync_file_system/local/syncable_file_system_operation.h" 19 #include "chrome/browser/sync_file_system/syncable_file_system_util.h" 20 #include "testing/gtest/include/gtest/gtest.h" 21 #include "webkit/browser/blob/mock_blob_url_request_context.h" 22 #include "webkit/browser/fileapi/file_system_context.h" 23 #include "webkit/browser/fileapi/file_system_operation_runner.h" 24 25 using fileapi::FileSystemOperation; 26 using fileapi::FileSystemURL; 27 using webkit_blob::MockBlobURLRequestContext; 28 using webkit_blob::ScopedTextBlob; 29 using base::PlatformFileError; 30 31 namespace sync_file_system { 32 33 namespace { 34 const std::string kParent = "foo"; 35 const std::string kFile = "foo/file"; 36 const std::string kDir = "foo/dir"; 37 const std::string kChild = "foo/dir/bar"; 38 const std::string kOther = "bar"; 39 } // namespace 40 41 class SyncableFileOperationRunnerTest : public testing::Test { 42 protected: 43 typedef FileSystemOperation::StatusCallback StatusCallback; 44 45 // Use the current thread as IO thread so that we can directly call 46 // operations in the tests. 47 SyncableFileOperationRunnerTest() 48 : message_loop_(base::MessageLoop::TYPE_IO), 49 file_system_(GURL("http://example.com"), 50 base::MessageLoopProxy::current().get(), 51 base::MessageLoopProxy::current().get()), 52 callback_count_(0), 53 write_status_(base::PLATFORM_FILE_ERROR_FAILED), 54 write_bytes_(0), 55 write_complete_(false), 56 url_request_context_(file_system_.file_system_context()), 57 weak_factory_(this) {} 58 59 virtual void SetUp() OVERRIDE { 60 ASSERT_TRUE(dir_.CreateUniqueTempDir()); 61 file_system_.SetUp(); 62 sync_context_ = 63 new LocalFileSyncContext(base::MessageLoopProxy::current().get(), 64 base::MessageLoopProxy::current().get()); 65 ASSERT_EQ( 66 SYNC_STATUS_OK, 67 file_system_.MaybeInitializeFileSystemContext(sync_context_.get())); 68 69 ASSERT_EQ(base::PLATFORM_FILE_OK, file_system_.OpenFileSystem()); 70 ASSERT_EQ(base::PLATFORM_FILE_OK, 71 file_system_.CreateDirectory(URL(kParent))); 72 } 73 74 virtual void TearDown() OVERRIDE { 75 if (sync_context_.get()) 76 sync_context_->ShutdownOnUIThread(); 77 sync_context_ = NULL; 78 79 file_system_.TearDown(); 80 message_loop_.RunUntilIdle(); 81 RevokeSyncableFileSystem(); 82 } 83 84 FileSystemURL URL(const std::string& path) { 85 return file_system_.URL(path); 86 } 87 88 LocalFileSyncStatus* sync_status() { 89 return file_system_.backend()->sync_context()->sync_status(); 90 } 91 92 void ResetCallbackStatus() { 93 write_status_ = base::PLATFORM_FILE_ERROR_FAILED; 94 write_bytes_ = 0; 95 write_complete_ = false; 96 callback_count_ = 0; 97 } 98 99 StatusCallback ExpectStatus(const tracked_objects::Location& location, 100 PlatformFileError expect) { 101 return base::Bind(&SyncableFileOperationRunnerTest::DidFinish, 102 weak_factory_.GetWeakPtr(), location, expect); 103 } 104 105 FileSystemOperation::WriteCallback GetWriteCallback( 106 const tracked_objects::Location& location) { 107 return base::Bind(&SyncableFileOperationRunnerTest::DidWrite, 108 weak_factory_.GetWeakPtr(), location); 109 } 110 111 void DidWrite(const tracked_objects::Location& location, 112 PlatformFileError status, int64 bytes, bool complete) { 113 SCOPED_TRACE(testing::Message() << location.ToString()); 114 write_status_ = status; 115 write_bytes_ += bytes; 116 write_complete_ = complete; 117 ++callback_count_; 118 } 119 120 void DidFinish(const tracked_objects::Location& location, 121 PlatformFileError expect, PlatformFileError status) { 122 SCOPED_TRACE(testing::Message() << location.ToString()); 123 EXPECT_EQ(expect, status); 124 ++callback_count_; 125 } 126 127 bool CreateTempFile(base::FilePath* path) { 128 return file_util::CreateTemporaryFileInDir(dir_.path(), path); 129 } 130 131 ScopedEnableSyncFSDirectoryOperation enable_directory_operation_; 132 base::ScopedTempDir dir_; 133 134 base::MessageLoop message_loop_; 135 CannedSyncableFileSystem file_system_; 136 scoped_refptr<LocalFileSyncContext> sync_context_; 137 138 int callback_count_; 139 PlatformFileError write_status_; 140 size_t write_bytes_; 141 bool write_complete_; 142 143 MockBlobURLRequestContext url_request_context_; 144 145 base::WeakPtrFactory<SyncableFileOperationRunnerTest> weak_factory_; 146 147 DISALLOW_COPY_AND_ASSIGN(SyncableFileOperationRunnerTest); 148 }; 149 150 TEST_F(SyncableFileOperationRunnerTest, SimpleQueue) { 151 sync_status()->StartSyncing(URL(kFile)); 152 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile))); 153 154 // The URL is in syncing so the write operations won't run. 155 ResetCallbackStatus(); 156 file_system_.operation_runner()->CreateFile( 157 URL(kFile), false /* exclusive */, 158 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 159 file_system_.operation_runner()->Truncate( 160 URL(kFile), 1, 161 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 162 base::MessageLoop::current()->RunUntilIdle(); 163 EXPECT_EQ(0, callback_count_); 164 165 // Read operations are not blocked (and are executed before queued ones). 166 file_system_.operation_runner()->FileExists( 167 URL(kFile), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_NOT_FOUND)); 168 base::MessageLoop::current()->RunUntilIdle(); 169 EXPECT_EQ(1, callback_count_); 170 171 // End syncing (to enable write). 172 sync_status()->EndSyncing(URL(kFile)); 173 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile))); 174 175 ResetCallbackStatus(); 176 base::MessageLoop::current()->RunUntilIdle(); 177 EXPECT_EQ(2, callback_count_); 178 179 // Now the file must have been created and updated. 180 ResetCallbackStatus(); 181 file_system_.operation_runner()->FileExists( 182 URL(kFile), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 183 base::MessageLoop::current()->RunUntilIdle(); 184 EXPECT_EQ(1, callback_count_); 185 } 186 187 TEST_F(SyncableFileOperationRunnerTest, WriteToParentAndChild) { 188 // First create the kDir directory and kChild in the dir. 189 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateDirectory(URL(kDir))); 190 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kChild))); 191 192 // Start syncing the kDir directory. 193 sync_status()->StartSyncing(URL(kDir)); 194 ASSERT_FALSE(sync_status()->IsWritable(URL(kDir))); 195 196 // Writes to kParent and kChild should be all queued up. 197 ResetCallbackStatus(); 198 file_system_.operation_runner()->Truncate( 199 URL(kChild), 1, ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 200 file_system_.operation_runner()->Remove( 201 URL(kParent), true /* recursive */, 202 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 203 base::MessageLoop::current()->RunUntilIdle(); 204 EXPECT_EQ(0, callback_count_); 205 206 // Read operations are not blocked (and are executed before queued ones). 207 file_system_.operation_runner()->DirectoryExists( 208 URL(kDir), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 209 base::MessageLoop::current()->RunUntilIdle(); 210 EXPECT_EQ(1, callback_count_); 211 212 // Writes to unrelated files must succeed as well. 213 ResetCallbackStatus(); 214 file_system_.operation_runner()->CreateDirectory( 215 URL(kOther), false /* exclusive */, false /* recursive */, 216 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 217 base::MessageLoop::current()->RunUntilIdle(); 218 EXPECT_EQ(1, callback_count_); 219 220 // End syncing (to enable write). 221 sync_status()->EndSyncing(URL(kDir)); 222 ASSERT_TRUE(sync_status()->IsWritable(URL(kDir))); 223 224 ResetCallbackStatus(); 225 base::MessageLoop::current()->RunUntilIdle(); 226 EXPECT_EQ(2, callback_count_); 227 } 228 229 TEST_F(SyncableFileOperationRunnerTest, CopyAndMove) { 230 // First create the kDir directory and kChild in the dir. 231 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateDirectory(URL(kDir))); 232 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kChild))); 233 234 // Start syncing the kParent directory. 235 sync_status()->StartSyncing(URL(kParent)); 236 237 // Copying kDir to other directory should succeed, while moving would fail 238 // (since the source directory is in syncing). 239 ResetCallbackStatus(); 240 file_system_.operation_runner()->Copy( 241 URL(kDir), URL("dest-copy"), 242 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 243 file_system_.operation_runner()->Move( 244 URL(kDir), URL("dest-move"), 245 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 246 base::MessageLoop::current()->RunUntilIdle(); 247 EXPECT_EQ(1, callback_count_); 248 249 // Only "dest-copy1" should exist. 250 EXPECT_EQ(base::PLATFORM_FILE_OK, 251 file_system_.DirectoryExists(URL("dest-copy"))); 252 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, 253 file_system_.DirectoryExists(URL("dest-move"))); 254 255 // Start syncing the "dest-copy2" directory. 256 sync_status()->StartSyncing(URL("dest-copy2")); 257 258 // Now the destination is also locked copying kDir should be queued. 259 ResetCallbackStatus(); 260 file_system_.operation_runner()->Copy( 261 URL(kDir), URL("dest-copy2"), 262 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 263 base::MessageLoop::current()->RunUntilIdle(); 264 EXPECT_EQ(0, callback_count_); 265 266 // Finish syncing the "dest-copy2" directory to unlock Copy. 267 sync_status()->EndSyncing(URL("dest-copy2")); 268 ResetCallbackStatus(); 269 base::MessageLoop::current()->RunUntilIdle(); 270 EXPECT_EQ(1, callback_count_); 271 272 // Now we should have "dest-copy2". 273 EXPECT_EQ(base::PLATFORM_FILE_OK, 274 file_system_.DirectoryExists(URL("dest-copy2"))); 275 276 // Finish syncing the kParent to unlock Move. 277 sync_status()->EndSyncing(URL(kParent)); 278 ResetCallbackStatus(); 279 base::MessageLoop::current()->RunUntilIdle(); 280 EXPECT_EQ(1, callback_count_); 281 282 // Now we should have "dest-move". 283 EXPECT_EQ(base::PLATFORM_FILE_OK, 284 file_system_.DirectoryExists(URL("dest-move"))); 285 } 286 287 TEST_F(SyncableFileOperationRunnerTest, Write) { 288 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kFile))); 289 const GURL kBlobURL("blob:foo"); 290 const std::string kData("Lorem ipsum."); 291 ScopedTextBlob blob(url_request_context_, kBlobURL, kData); 292 293 sync_status()->StartSyncing(URL(kFile)); 294 295 ResetCallbackStatus(); 296 file_system_.operation_runner()->Write( 297 &url_request_context_, 298 URL(kFile), kBlobURL, 0, GetWriteCallback(FROM_HERE)); 299 base::MessageLoop::current()->RunUntilIdle(); 300 EXPECT_EQ(0, callback_count_); 301 302 sync_status()->EndSyncing(URL(kFile)); 303 ResetCallbackStatus(); 304 305 while (!write_complete_) 306 base::MessageLoop::current()->RunUntilIdle(); 307 308 EXPECT_EQ(base::PLATFORM_FILE_OK, write_status_); 309 EXPECT_EQ(kData.size(), write_bytes_); 310 EXPECT_TRUE(write_complete_); 311 } 312 313 TEST_F(SyncableFileOperationRunnerTest, QueueAndCancel) { 314 sync_status()->StartSyncing(URL(kFile)); 315 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile))); 316 317 ResetCallbackStatus(); 318 file_system_.operation_runner()->CreateFile( 319 URL(kFile), false /* exclusive */, 320 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT)); 321 file_system_.operation_runner()->Truncate( 322 URL(kFile), 1, 323 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT)); 324 base::MessageLoop::current()->RunUntilIdle(); 325 EXPECT_EQ(0, callback_count_); 326 327 ResetCallbackStatus(); 328 329 // This shouldn't crash nor leak memory. 330 sync_context_->ShutdownOnUIThread(); 331 sync_context_ = NULL; 332 base::MessageLoop::current()->RunUntilIdle(); 333 EXPECT_EQ(2, callback_count_); 334 } 335 336 // Test if CopyInForeignFile runs cooperatively with other Sync operations 337 // when it is called directly via AsFileSystemOperationImpl. 338 TEST_F(SyncableFileOperationRunnerTest, CopyInForeignFile) { 339 const std::string kTestData("test data"); 340 341 base::FilePath temp_path; 342 ASSERT_TRUE(CreateTempFile(&temp_path)); 343 ASSERT_EQ(static_cast<int>(kTestData.size()), 344 file_util::WriteFile( 345 temp_path, kTestData.data(), kTestData.size())); 346 347 sync_status()->StartSyncing(URL(kFile)); 348 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile))); 349 350 // The URL is in syncing so CopyIn (which is a write operation) won't run. 351 ResetCallbackStatus(); 352 file_system_.operation_runner()->CopyInForeignFile( 353 temp_path, URL(kFile), 354 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 355 base::MessageLoop::current()->RunUntilIdle(); 356 EXPECT_EQ(0, callback_count_); 357 358 // End syncing (to enable write). 359 sync_status()->EndSyncing(URL(kFile)); 360 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile))); 361 362 ResetCallbackStatus(); 363 base::MessageLoop::current()->RunUntilIdle(); 364 EXPECT_EQ(1, callback_count_); 365 366 // Now the file must have been created and have the same content as temp_path. 367 ResetCallbackStatus(); 368 file_system_.DoVerifyFile( 369 URL(kFile), kTestData, 370 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 371 base::MessageLoop::current()->RunUntilIdle(); 372 EXPECT_EQ(1, callback_count_); 373 } 374 375 TEST_F(SyncableFileOperationRunnerTest, Cancel) { 376 // Prepare a file. 377 file_system_.operation_runner()->CreateFile( 378 URL(kFile), false /* exclusive */, 379 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 380 base::MessageLoop::current()->RunUntilIdle(); 381 EXPECT_EQ(1, callback_count_); 382 383 // Run Truncate and immediately cancel. This shouldn't crash. 384 ResetCallbackStatus(); 385 fileapi::FileSystemOperationRunner::OperationID id = 386 file_system_.operation_runner()->Truncate( 387 URL(kFile), 10, 388 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT)); 389 file_system_.operation_runner()->Cancel( 390 id, ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 391 base::MessageLoop::current()->RunUntilIdle(); 392 EXPECT_EQ(2, callback_count_); 393 } 394 395 } // namespace sync_file_system 396