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