syncable_file_operation_runner_unittest.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
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 25using fileapi::FileSystemOperation; 26using fileapi::FileSystemURL; 27using webkit_blob::MockBlobURLRequestContext; 28using webkit_blob::ScopedTextBlob; 29using base::PlatformFileError; 30 31namespace sync_file_system { 32 33namespace { 34const std::string kParent = "foo"; 35const std::string kFile = "foo/file"; 36const std::string kDir = "foo/dir"; 37const std::string kChild = "foo/dir/bar"; 38const std::string kOther = "bar"; 39} // namespace 40 41class 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 private: 146 base::WeakPtrFactory<SyncableFileOperationRunnerTest> weak_factory_; 147 148 DISALLOW_COPY_AND_ASSIGN(SyncableFileOperationRunnerTest); 149}; 150 151TEST_F(SyncableFileOperationRunnerTest, SimpleQueue) { 152 sync_status()->StartSyncing(URL(kFile)); 153 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile))); 154 155 // The URL is in syncing so the write operations won't run. 156 ResetCallbackStatus(); 157 file_system_.operation_runner()->CreateFile( 158 URL(kFile), false /* exclusive */, 159 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 160 file_system_.operation_runner()->Truncate( 161 URL(kFile), 1, 162 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 163 base::MessageLoop::current()->RunUntilIdle(); 164 EXPECT_EQ(0, callback_count_); 165 166 // Read operations are not blocked (and are executed before queued ones). 167 file_system_.operation_runner()->FileExists( 168 URL(kFile), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_NOT_FOUND)); 169 base::MessageLoop::current()->RunUntilIdle(); 170 EXPECT_EQ(1, callback_count_); 171 172 // End syncing (to enable write). 173 sync_status()->EndSyncing(URL(kFile)); 174 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile))); 175 176 ResetCallbackStatus(); 177 base::MessageLoop::current()->RunUntilIdle(); 178 EXPECT_EQ(2, callback_count_); 179 180 // Now the file must have been created and updated. 181 ResetCallbackStatus(); 182 file_system_.operation_runner()->FileExists( 183 URL(kFile), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 184 base::MessageLoop::current()->RunUntilIdle(); 185 EXPECT_EQ(1, callback_count_); 186} 187 188TEST_F(SyncableFileOperationRunnerTest, WriteToParentAndChild) { 189 // First create the kDir directory and kChild in the dir. 190 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateDirectory(URL(kDir))); 191 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kChild))); 192 193 // Start syncing the kDir directory. 194 sync_status()->StartSyncing(URL(kDir)); 195 ASSERT_FALSE(sync_status()->IsWritable(URL(kDir))); 196 197 // Writes to kParent and kChild should be all queued up. 198 ResetCallbackStatus(); 199 file_system_.operation_runner()->Truncate( 200 URL(kChild), 1, ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 201 file_system_.operation_runner()->Remove( 202 URL(kParent), true /* recursive */, 203 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 204 base::MessageLoop::current()->RunUntilIdle(); 205 EXPECT_EQ(0, callback_count_); 206 207 // Read operations are not blocked (and are executed before queued ones). 208 file_system_.operation_runner()->DirectoryExists( 209 URL(kDir), ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 210 base::MessageLoop::current()->RunUntilIdle(); 211 EXPECT_EQ(1, callback_count_); 212 213 // Writes to unrelated files must succeed as well. 214 ResetCallbackStatus(); 215 file_system_.operation_runner()->CreateDirectory( 216 URL(kOther), false /* exclusive */, false /* recursive */, 217 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 218 base::MessageLoop::current()->RunUntilIdle(); 219 EXPECT_EQ(1, callback_count_); 220 221 // End syncing (to enable write). 222 sync_status()->EndSyncing(URL(kDir)); 223 ASSERT_TRUE(sync_status()->IsWritable(URL(kDir))); 224 225 ResetCallbackStatus(); 226 base::MessageLoop::current()->RunUntilIdle(); 227 EXPECT_EQ(2, callback_count_); 228} 229 230TEST_F(SyncableFileOperationRunnerTest, CopyAndMove) { 231 // First create the kDir directory and kChild in the dir. 232 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateDirectory(URL(kDir))); 233 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kChild))); 234 235 // Start syncing the kParent directory. 236 sync_status()->StartSyncing(URL(kParent)); 237 238 // Copying kDir to other directory should succeed, while moving would fail 239 // (since the source directory is in syncing). 240 ResetCallbackStatus(); 241 file_system_.operation_runner()->Copy( 242 URL(kDir), URL("dest-copy"), 243 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 244 file_system_.operation_runner()->Move( 245 URL(kDir), URL("dest-move"), 246 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 247 base::MessageLoop::current()->RunUntilIdle(); 248 EXPECT_EQ(1, callback_count_); 249 250 // Only "dest-copy1" should exist. 251 EXPECT_EQ(base::PLATFORM_FILE_OK, 252 file_system_.DirectoryExists(URL("dest-copy"))); 253 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, 254 file_system_.DirectoryExists(URL("dest-move"))); 255 256 // Start syncing the "dest-copy2" directory. 257 sync_status()->StartSyncing(URL("dest-copy2")); 258 259 // Now the destination is also locked copying kDir should be queued. 260 ResetCallbackStatus(); 261 file_system_.operation_runner()->Copy( 262 URL(kDir), URL("dest-copy2"), 263 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 264 base::MessageLoop::current()->RunUntilIdle(); 265 EXPECT_EQ(0, callback_count_); 266 267 // Finish syncing the "dest-copy2" directory to unlock Copy. 268 sync_status()->EndSyncing(URL("dest-copy2")); 269 ResetCallbackStatus(); 270 base::MessageLoop::current()->RunUntilIdle(); 271 EXPECT_EQ(1, callback_count_); 272 273 // Now we should have "dest-copy2". 274 EXPECT_EQ(base::PLATFORM_FILE_OK, 275 file_system_.DirectoryExists(URL("dest-copy2"))); 276 277 // Finish syncing the kParent to unlock Move. 278 sync_status()->EndSyncing(URL(kParent)); 279 ResetCallbackStatus(); 280 base::MessageLoop::current()->RunUntilIdle(); 281 EXPECT_EQ(1, callback_count_); 282 283 // Now we should have "dest-move". 284 EXPECT_EQ(base::PLATFORM_FILE_OK, 285 file_system_.DirectoryExists(URL("dest-move"))); 286} 287 288TEST_F(SyncableFileOperationRunnerTest, Write) { 289 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kFile))); 290 const GURL kBlobURL("blob:foo"); 291 const std::string kData("Lorem ipsum."); 292 ScopedTextBlob blob(url_request_context_, kBlobURL, kData); 293 294 sync_status()->StartSyncing(URL(kFile)); 295 296 ResetCallbackStatus(); 297 file_system_.operation_runner()->Write( 298 &url_request_context_, 299 URL(kFile), kBlobURL, 0, GetWriteCallback(FROM_HERE)); 300 base::MessageLoop::current()->RunUntilIdle(); 301 EXPECT_EQ(0, callback_count_); 302 303 sync_status()->EndSyncing(URL(kFile)); 304 ResetCallbackStatus(); 305 306 while (!write_complete_) 307 base::MessageLoop::current()->RunUntilIdle(); 308 309 EXPECT_EQ(base::PLATFORM_FILE_OK, write_status_); 310 EXPECT_EQ(kData.size(), write_bytes_); 311 EXPECT_TRUE(write_complete_); 312} 313 314TEST_F(SyncableFileOperationRunnerTest, QueueAndCancel) { 315 sync_status()->StartSyncing(URL(kFile)); 316 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile))); 317 318 ResetCallbackStatus(); 319 file_system_.operation_runner()->CreateFile( 320 URL(kFile), false /* exclusive */, 321 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT)); 322 file_system_.operation_runner()->Truncate( 323 URL(kFile), 1, 324 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT)); 325 base::MessageLoop::current()->RunUntilIdle(); 326 EXPECT_EQ(0, callback_count_); 327 328 ResetCallbackStatus(); 329 330 // This shouldn't crash nor leak memory. 331 sync_context_->ShutdownOnUIThread(); 332 sync_context_ = NULL; 333 base::MessageLoop::current()->RunUntilIdle(); 334 EXPECT_EQ(2, callback_count_); 335} 336 337// Test if CopyInForeignFile runs cooperatively with other Sync operations 338// when it is called directly via AsFileSystemOperationImpl. 339TEST_F(SyncableFileOperationRunnerTest, CopyInForeignFile) { 340 const std::string kTestData("test data"); 341 342 base::FilePath temp_path; 343 ASSERT_TRUE(CreateTempFile(&temp_path)); 344 ASSERT_EQ(static_cast<int>(kTestData.size()), 345 file_util::WriteFile( 346 temp_path, kTestData.data(), kTestData.size())); 347 348 sync_status()->StartSyncing(URL(kFile)); 349 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile))); 350 351 // The URL is in syncing so CopyIn (which is a write operation) won't run. 352 ResetCallbackStatus(); 353 file_system_.operation_runner()->CopyInForeignFile( 354 temp_path, URL(kFile), 355 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 356 base::MessageLoop::current()->RunUntilIdle(); 357 EXPECT_EQ(0, callback_count_); 358 359 // End syncing (to enable write). 360 sync_status()->EndSyncing(URL(kFile)); 361 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile))); 362 363 ResetCallbackStatus(); 364 base::MessageLoop::current()->RunUntilIdle(); 365 EXPECT_EQ(1, callback_count_); 366 367 // Now the file must have been created and have the same content as temp_path. 368 ResetCallbackStatus(); 369 file_system_.DoVerifyFile( 370 URL(kFile), kTestData, 371 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 372 base::MessageLoop::current()->RunUntilIdle(); 373 EXPECT_EQ(1, callback_count_); 374} 375 376TEST_F(SyncableFileOperationRunnerTest, Cancel) { 377 // Prepare a file. 378 file_system_.operation_runner()->CreateFile( 379 URL(kFile), false /* exclusive */, 380 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 381 base::MessageLoop::current()->RunUntilIdle(); 382 EXPECT_EQ(1, callback_count_); 383 384 // Run Truncate and immediately cancel. This shouldn't crash. 385 ResetCallbackStatus(); 386 fileapi::FileSystemOperationRunner::OperationID id = 387 file_system_.operation_runner()->Truncate( 388 URL(kFile), 10, 389 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT)); 390 file_system_.operation_runner()->Cancel( 391 id, ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 392 base::MessageLoop::current()->RunUntilIdle(); 393 EXPECT_EQ(2, callback_count_); 394} 395 396} // namespace sync_file_system 397