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