syncable_file_operation_runner_unittest.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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::FileSystemOperationRunner::CopyProgressCallback(), 245 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 246 file_system_.operation_runner()->Move( 247 URL(kDir), URL("dest-move"), 248 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 249 base::MessageLoop::current()->RunUntilIdle(); 250 EXPECT_EQ(1, callback_count_); 251 252 // Only "dest-copy1" should exist. 253 EXPECT_EQ(base::PLATFORM_FILE_OK, 254 file_system_.DirectoryExists(URL("dest-copy"))); 255 EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, 256 file_system_.DirectoryExists(URL("dest-move"))); 257 258 // Start syncing the "dest-copy2" directory. 259 sync_status()->StartSyncing(URL("dest-copy2")); 260 261 // Now the destination is also locked copying kDir should be queued. 262 ResetCallbackStatus(); 263 file_system_.operation_runner()->Copy( 264 URL(kDir), URL("dest-copy2"), 265 fileapi::FileSystemOperationRunner::CopyProgressCallback(), 266 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 267 base::MessageLoop::current()->RunUntilIdle(); 268 EXPECT_EQ(0, callback_count_); 269 270 // Finish syncing the "dest-copy2" directory to unlock Copy. 271 sync_status()->EndSyncing(URL("dest-copy2")); 272 ResetCallbackStatus(); 273 base::MessageLoop::current()->RunUntilIdle(); 274 EXPECT_EQ(1, callback_count_); 275 276 // Now we should have "dest-copy2". 277 EXPECT_EQ(base::PLATFORM_FILE_OK, 278 file_system_.DirectoryExists(URL("dest-copy2"))); 279 280 // Finish syncing the kParent to unlock Move. 281 sync_status()->EndSyncing(URL(kParent)); 282 ResetCallbackStatus(); 283 base::MessageLoop::current()->RunUntilIdle(); 284 EXPECT_EQ(1, callback_count_); 285 286 // Now we should have "dest-move". 287 EXPECT_EQ(base::PLATFORM_FILE_OK, 288 file_system_.DirectoryExists(URL("dest-move"))); 289} 290 291TEST_F(SyncableFileOperationRunnerTest, Write) { 292 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_.CreateFile(URL(kFile))); 293 const std::string kData("Lorem ipsum."); 294 ScopedTextBlob blob(url_request_context_, "blob:foo", kData); 295 296 sync_status()->StartSyncing(URL(kFile)); 297 298 ResetCallbackStatus(); 299 file_system_.operation_runner()->Write( 300 &url_request_context_, 301 URL(kFile), blob.GetBlobDataHandle(), 0, GetWriteCallback(FROM_HERE)); 302 base::MessageLoop::current()->RunUntilIdle(); 303 EXPECT_EQ(0, callback_count_); 304 305 sync_status()->EndSyncing(URL(kFile)); 306 ResetCallbackStatus(); 307 308 while (!write_complete_) 309 base::MessageLoop::current()->RunUntilIdle(); 310 311 EXPECT_EQ(base::PLATFORM_FILE_OK, write_status_); 312 EXPECT_EQ(kData.size(), write_bytes_); 313 EXPECT_TRUE(write_complete_); 314} 315 316TEST_F(SyncableFileOperationRunnerTest, QueueAndCancel) { 317 sync_status()->StartSyncing(URL(kFile)); 318 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile))); 319 320 ResetCallbackStatus(); 321 file_system_.operation_runner()->CreateFile( 322 URL(kFile), false /* exclusive */, 323 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT)); 324 file_system_.operation_runner()->Truncate( 325 URL(kFile), 1, 326 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_ABORT)); 327 base::MessageLoop::current()->RunUntilIdle(); 328 EXPECT_EQ(0, callback_count_); 329 330 ResetCallbackStatus(); 331 332 // This shouldn't crash nor leak memory. 333 sync_context_->ShutdownOnUIThread(); 334 sync_context_ = NULL; 335 base::MessageLoop::current()->RunUntilIdle(); 336 EXPECT_EQ(2, callback_count_); 337} 338 339// Test if CopyInForeignFile runs cooperatively with other Sync operations. 340TEST_F(SyncableFileOperationRunnerTest, CopyInForeignFile) { 341 const std::string kTestData("test data"); 342 343 base::FilePath temp_path; 344 ASSERT_TRUE(CreateTempFile(&temp_path)); 345 ASSERT_EQ(static_cast<int>(kTestData.size()), 346 file_util::WriteFile( 347 temp_path, kTestData.data(), kTestData.size())); 348 349 sync_status()->StartSyncing(URL(kFile)); 350 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile))); 351 352 // The URL is in syncing so CopyIn (which is a write operation) won't run. 353 ResetCallbackStatus(); 354 file_system_.operation_runner()->CopyInForeignFile( 355 temp_path, URL(kFile), 356 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 357 base::MessageLoop::current()->RunUntilIdle(); 358 EXPECT_EQ(0, callback_count_); 359 360 // End syncing (to enable write). 361 sync_status()->EndSyncing(URL(kFile)); 362 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile))); 363 364 ResetCallbackStatus(); 365 base::MessageLoop::current()->RunUntilIdle(); 366 EXPECT_EQ(1, callback_count_); 367 368 // Now the file must have been created and have the same content as temp_path. 369 ResetCallbackStatus(); 370 file_system_.DoVerifyFile( 371 URL(kFile), kTestData, 372 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 373 base::MessageLoop::current()->RunUntilIdle(); 374 EXPECT_EQ(1, callback_count_); 375} 376 377TEST_F(SyncableFileOperationRunnerTest, Cancel) { 378 // Prepare a file. 379 file_system_.operation_runner()->CreateFile( 380 URL(kFile), false /* exclusive */, 381 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 382 base::MessageLoop::current()->RunUntilIdle(); 383 EXPECT_EQ(1, callback_count_); 384 385 // Run Truncate and immediately cancel. This shouldn't crash. 386 ResetCallbackStatus(); 387 fileapi::FileSystemOperationRunner::OperationID id = 388 file_system_.operation_runner()->Truncate( 389 URL(kFile), 10, 390 ExpectStatus(FROM_HERE, base::PLATFORM_FILE_OK)); 391 file_system_.operation_runner()->Cancel( 392 id, ExpectStatus(FROM_HERE, base::PLATFORM_FILE_ERROR_INVALID_OPERATION)); 393 base::MessageLoop::current()->RunUntilIdle(); 394 EXPECT_EQ(2, callback_count_); 395} 396 397} // namespace sync_file_system 398