syncable_file_operation_runner_unittest.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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 "base/thread_task_runner_handle.h" 14#include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h" 15#include "chrome/browser/sync_file_system/local/local_file_change_tracker.h" 16#include "chrome/browser/sync_file_system/local/local_file_sync_context.h" 17#include "chrome/browser/sync_file_system/local/local_file_sync_status.h" 18#include "chrome/browser/sync_file_system/local/sync_file_system_backend.h" 19#include "chrome/browser/sync_file_system/local/syncable_file_operation_runner.h" 20#include "chrome/browser/sync_file_system/local/syncable_file_system_operation.h" 21#include "chrome/browser/sync_file_system/syncable_file_system_util.h" 22#include "content/public/test/mock_blob_url_request_context.h" 23#include "content/public/test/test_browser_thread_bundle.h" 24#include "testing/gtest/include/gtest/gtest.h" 25#include "third_party/leveldatabase/src/helpers/memenv/memenv.h" 26#include "third_party/leveldatabase/src/include/leveldb/env.h" 27#include "webkit/browser/fileapi/file_system_context.h" 28#include "webkit/browser/fileapi/file_system_operation_runner.h" 29 30using storage::FileSystemOperation; 31using storage::FileSystemURL; 32using content::MockBlobURLRequestContext; 33using content::ScopedTextBlob; 34using base::File; 35 36namespace sync_file_system { 37 38namespace { 39const std::string kParent = "foo"; 40const std::string kFile = "foo/file"; 41const std::string kDir = "foo/dir"; 42const std::string kChild = "foo/dir/bar"; 43const std::string kOther = "bar"; 44} // namespace 45 46class SyncableFileOperationRunnerTest : public testing::Test { 47 protected: 48 typedef FileSystemOperation::StatusCallback StatusCallback; 49 50 // Use the current thread as IO thread so that we can directly call 51 // operations in the tests. 52 SyncableFileOperationRunnerTest() 53 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP), 54 in_memory_env_(leveldb::NewMemEnv(leveldb::Env::Default())), 55 file_system_(GURL("http://example.com"), 56 in_memory_env_.get(), 57 base::ThreadTaskRunnerHandle::Get().get(), 58 base::ThreadTaskRunnerHandle::Get().get()), 59 callback_count_(0), 60 write_status_(File::FILE_ERROR_FAILED), 61 write_bytes_(0), 62 write_complete_(false), 63 url_request_context_(file_system_.file_system_context()), 64 weak_factory_(this) {} 65 66 virtual void SetUp() OVERRIDE { 67 ASSERT_TRUE(dir_.CreateUniqueTempDir()); 68 69 file_system_.SetUp(CannedSyncableFileSystem::QUOTA_ENABLED); 70 sync_context_ = new LocalFileSyncContext( 71 dir_.path(), 72 in_memory_env_.get(), 73 base::ThreadTaskRunnerHandle::Get().get(), 74 base::ThreadTaskRunnerHandle::Get().get()); 75 ASSERT_EQ( 76 SYNC_STATUS_OK, 77 file_system_.MaybeInitializeFileSystemContext(sync_context_.get())); 78 79 ASSERT_EQ(File::FILE_OK, file_system_.OpenFileSystem()); 80 ASSERT_EQ(File::FILE_OK, 81 file_system_.CreateDirectory(URL(kParent))); 82 } 83 84 virtual void TearDown() OVERRIDE { 85 if (sync_context_.get()) 86 sync_context_->ShutdownOnUIThread(); 87 sync_context_ = NULL; 88 89 file_system_.TearDown(); 90 RevokeSyncableFileSystem(); 91 } 92 93 FileSystemURL URL(const std::string& path) { 94 return file_system_.URL(path); 95 } 96 97 LocalFileSyncStatus* sync_status() { 98 return file_system_.backend()->sync_context()->sync_status(); 99 } 100 101 void ResetCallbackStatus() { 102 write_status_ = File::FILE_ERROR_FAILED; 103 write_bytes_ = 0; 104 write_complete_ = false; 105 callback_count_ = 0; 106 } 107 108 StatusCallback ExpectStatus(const tracked_objects::Location& location, 109 File::Error expect) { 110 return base::Bind(&SyncableFileOperationRunnerTest::DidFinish, 111 weak_factory_.GetWeakPtr(), location, expect); 112 } 113 114 FileSystemOperation::WriteCallback GetWriteCallback( 115 const tracked_objects::Location& location) { 116 return base::Bind(&SyncableFileOperationRunnerTest::DidWrite, 117 weak_factory_.GetWeakPtr(), location); 118 } 119 120 void DidWrite(const tracked_objects::Location& location, 121 File::Error status, int64 bytes, bool complete) { 122 SCOPED_TRACE(testing::Message() << location.ToString()); 123 write_status_ = status; 124 write_bytes_ += bytes; 125 write_complete_ = complete; 126 ++callback_count_; 127 } 128 129 void DidFinish(const tracked_objects::Location& location, 130 File::Error expect, File::Error status) { 131 SCOPED_TRACE(testing::Message() << location.ToString()); 132 EXPECT_EQ(expect, status); 133 ++callback_count_; 134 } 135 136 bool CreateTempFile(base::FilePath* path) { 137 return base::CreateTemporaryFileInDir(dir_.path(), path); 138 } 139 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), 253 URL("dest-copy"), 254 storage::FileSystemOperation::OPTION_NONE, 255 storage::FileSystemOperationRunner::CopyProgressCallback(), 256 ExpectStatus(FROM_HERE, File::FILE_OK)); 257 file_system_.operation_runner()->Move( 258 URL(kDir), 259 URL("dest-move"), 260 storage::FileSystemOperation::OPTION_NONE, 261 ExpectStatus(FROM_HERE, File::FILE_OK)); 262 base::MessageLoop::current()->RunUntilIdle(); 263 EXPECT_EQ(1, callback_count_); 264 265 // Only "dest-copy1" should exist. 266 EXPECT_EQ(File::FILE_OK, 267 file_system_.DirectoryExists(URL("dest-copy"))); 268 EXPECT_EQ(File::FILE_ERROR_NOT_FOUND, 269 file_system_.DirectoryExists(URL("dest-move"))); 270 271 // Start syncing the "dest-copy2" directory. 272 sync_status()->StartSyncing(URL("dest-copy2")); 273 274 // Now the destination is also locked copying kDir should be queued. 275 ResetCallbackStatus(); 276 file_system_.operation_runner()->Copy( 277 URL(kDir), 278 URL("dest-copy2"), 279 storage::FileSystemOperation::OPTION_NONE, 280 storage::FileSystemOperationRunner::CopyProgressCallback(), 281 ExpectStatus(FROM_HERE, File::FILE_OK)); 282 base::MessageLoop::current()->RunUntilIdle(); 283 EXPECT_EQ(0, callback_count_); 284 285 // Finish syncing the "dest-copy2" directory to unlock Copy. 286 sync_status()->EndSyncing(URL("dest-copy2")); 287 ResetCallbackStatus(); 288 base::MessageLoop::current()->RunUntilIdle(); 289 EXPECT_EQ(1, callback_count_); 290 291 // Now we should have "dest-copy2". 292 EXPECT_EQ(File::FILE_OK, 293 file_system_.DirectoryExists(URL("dest-copy2"))); 294 295 // Finish syncing the kParent to unlock Move. 296 sync_status()->EndSyncing(URL(kParent)); 297 ResetCallbackStatus(); 298 base::MessageLoop::current()->RunUntilIdle(); 299 EXPECT_EQ(1, callback_count_); 300 301 // Now we should have "dest-move". 302 EXPECT_EQ(File::FILE_OK, 303 file_system_.DirectoryExists(URL("dest-move"))); 304} 305 306TEST_F(SyncableFileOperationRunnerTest, Write) { 307 EXPECT_EQ(File::FILE_OK, file_system_.CreateFile(URL(kFile))); 308 const std::string kData("Lorem ipsum."); 309 ScopedTextBlob blob(url_request_context_, "blob:foo", kData); 310 311 sync_status()->StartSyncing(URL(kFile)); 312 313 ResetCallbackStatus(); 314 file_system_.operation_runner()->Write( 315 &url_request_context_, 316 URL(kFile), blob.GetBlobDataHandle(), 0, GetWriteCallback(FROM_HERE)); 317 base::MessageLoop::current()->RunUntilIdle(); 318 EXPECT_EQ(0, callback_count_); 319 320 sync_status()->EndSyncing(URL(kFile)); 321 ResetCallbackStatus(); 322 323 while (!write_complete_) 324 base::MessageLoop::current()->RunUntilIdle(); 325 326 EXPECT_EQ(File::FILE_OK, write_status_); 327 EXPECT_EQ(kData.size(), write_bytes_); 328 EXPECT_TRUE(write_complete_); 329} 330 331TEST_F(SyncableFileOperationRunnerTest, QueueAndCancel) { 332 sync_status()->StartSyncing(URL(kFile)); 333 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile))); 334 335 ResetCallbackStatus(); 336 file_system_.operation_runner()->CreateFile( 337 URL(kFile), false /* exclusive */, 338 ExpectStatus(FROM_HERE, File::FILE_ERROR_ABORT)); 339 file_system_.operation_runner()->Truncate( 340 URL(kFile), 1, 341 ExpectStatus(FROM_HERE, File::FILE_ERROR_ABORT)); 342 base::MessageLoop::current()->RunUntilIdle(); 343 EXPECT_EQ(0, callback_count_); 344 345 ResetCallbackStatus(); 346 347 // This shouldn't crash nor leak memory. 348 sync_context_->ShutdownOnUIThread(); 349 sync_context_ = NULL; 350 base::MessageLoop::current()->RunUntilIdle(); 351 EXPECT_EQ(2, callback_count_); 352} 353 354// Test if CopyInForeignFile runs cooperatively with other Sync operations. 355TEST_F(SyncableFileOperationRunnerTest, CopyInForeignFile) { 356 const std::string kTestData("test data"); 357 358 base::FilePath temp_path; 359 ASSERT_TRUE(CreateTempFile(&temp_path)); 360 ASSERT_EQ(static_cast<int>(kTestData.size()), 361 base::WriteFile( 362 temp_path, kTestData.data(), kTestData.size())); 363 364 sync_status()->StartSyncing(URL(kFile)); 365 ASSERT_FALSE(sync_status()->IsWritable(URL(kFile))); 366 367 // The URL is in syncing so CopyIn (which is a write operation) won't run. 368 ResetCallbackStatus(); 369 file_system_.operation_runner()->CopyInForeignFile( 370 temp_path, URL(kFile), 371 ExpectStatus(FROM_HERE, File::FILE_OK)); 372 base::MessageLoop::current()->RunUntilIdle(); 373 EXPECT_EQ(0, callback_count_); 374 375 // End syncing (to enable write). 376 sync_status()->EndSyncing(URL(kFile)); 377 ASSERT_TRUE(sync_status()->IsWritable(URL(kFile))); 378 379 ResetCallbackStatus(); 380 base::MessageLoop::current()->RunUntilIdle(); 381 EXPECT_EQ(1, callback_count_); 382 383 // Now the file must have been created and have the same content as temp_path. 384 ResetCallbackStatus(); 385 file_system_.DoVerifyFile( 386 URL(kFile), kTestData, 387 ExpectStatus(FROM_HERE, File::FILE_OK)); 388 base::MessageLoop::current()->RunUntilIdle(); 389 EXPECT_EQ(1, callback_count_); 390} 391 392TEST_F(SyncableFileOperationRunnerTest, Cancel) { 393 // Prepare a file. 394 file_system_.operation_runner()->CreateFile( 395 URL(kFile), false /* exclusive */, 396 ExpectStatus(FROM_HERE, File::FILE_OK)); 397 base::MessageLoop::current()->RunUntilIdle(); 398 EXPECT_EQ(1, callback_count_); 399 400 // Run Truncate and immediately cancel. This shouldn't crash. 401 ResetCallbackStatus(); 402 storage::FileSystemOperationRunner::OperationID id = 403 file_system_.operation_runner()->Truncate( 404 URL(kFile), 10, ExpectStatus(FROM_HERE, File::FILE_OK)); 405 file_system_.operation_runner()->Cancel( 406 id, ExpectStatus(FROM_HERE, File::FILE_ERROR_INVALID_OPERATION)); 407 base::MessageLoop::current()->RunUntilIdle(); 408 EXPECT_EQ(2, callback_count_); 409} 410 411} // namespace sync_file_system 412