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