| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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 "storage/browser/blob/blob_storage_context.h" | |
| 6 | |
| 7 #include <stdint.h> | |
| 8 | |
| 9 #include <limits> | |
| 10 #include <memory> | |
| 11 #include <string> | |
| 12 | |
| 13 #include "base/bind.h" | |
| 14 #include "base/files/file.h" | |
| 15 #include "base/files/file_path.h" | |
| 16 #include "base/files/file_util.h" | |
| 17 #include "base/memory/ptr_util.h" | |
| 18 #include "base/memory/ref_counted.h" | |
| 19 #include "base/message_loop/message_loop.h" | |
| 20 #include "base/run_loop.h" | |
| 21 #include "base/strings/string_number_conversions.h" | |
| 22 #include "base/test/test_simple_task_runner.h" | |
| 23 #include "base/threading/thread_task_runner_handle.h" | |
| 24 #include "base/time/time.h" | |
| 25 #include "content/browser/blob_storage/blob_dispatcher_host.h" | |
| 26 #include "content/browser/blob_storage/chrome_blob_storage_context.h" | |
| 27 #include "content/public/test/test_browser_context.h" | |
| 28 #include "net/base/io_buffer.h" | |
| 29 #include "net/base/test_completion_callback.h" | |
| 30 #include "net/disk_cache/disk_cache.h" | |
| 31 #include "storage/browser/blob/blob_data_builder.h" | |
| 32 #include "storage/browser/blob/blob_data_handle.h" | |
| 33 #include "storage/browser/blob/blob_data_item.h" | |
| 34 #include "storage/browser/blob/blob_data_snapshot.h" | |
| 35 #include "storage/browser/blob/blob_transport_host.h" | |
| 36 #include "storage/common/blob_storage/blob_item_bytes_request.h" | |
| 37 #include "storage/common/blob_storage/blob_item_bytes_response.h" | |
| 38 #include "testing/gtest/include/gtest/gtest.h" | |
| 39 | |
| 40 using RequestMemoryCallback = storage::BlobTransportHost::RequestMemoryCallback; | |
| 41 using FileCreationInfo = storage::BlobMemoryController::FileCreationInfo; | |
| 42 | |
| 43 namespace storage { | |
| 44 namespace { | |
| 45 using base::TestSimpleTaskRunner; | |
| 46 | |
| 47 const int kTestDiskCacheStreamIndex = 0; | |
| 48 | |
| 49 const std::string kBlobStorageDirectory = "blob_storage"; | |
| 50 const size_t kTestBlobStorageIPCThresholdBytes = 20; | |
| 51 const size_t kTestBlobStorageMaxSharedMemoryBytes = 50; | |
| 52 | |
| 53 const size_t kTestBlobStorageMaxBlobMemorySize = 400; | |
| 54 const uint64_t kTestBlobStorageMaxDiskSpace = 4000; | |
| 55 const uint64_t kTestBlobStorageMinFileSizeBytes = 10; | |
| 56 const uint64_t kTestBlobStorageMaxFileSizeBytes = 100; | |
| 57 | |
| 58 // Our disk cache tests don't need a real data handle since the tests themselves | |
| 59 // scope the disk cache and entries. | |
| 60 class EmptyDataHandle : public storage::BlobDataBuilder::DataHandle { | |
| 61 private: | |
| 62 ~EmptyDataHandle() override {} | |
| 63 }; | |
| 64 | |
| 65 std::unique_ptr<disk_cache::Backend> CreateInMemoryDiskCache() { | |
| 66 std::unique_ptr<disk_cache::Backend> cache; | |
| 67 net::TestCompletionCallback callback; | |
| 68 int rv = disk_cache::CreateCacheBackend(net::MEMORY_CACHE, | |
| 69 net::CACHE_BACKEND_DEFAULT, | |
| 70 base::FilePath(), 0, | |
| 71 false, nullptr, nullptr, &cache, | |
| 72 callback.callback()); | |
| 73 EXPECT_EQ(net::OK, callback.GetResult(rv)); | |
| 74 | |
| 75 return cache; | |
| 76 } | |
| 77 | |
| 78 disk_cache::ScopedEntryPtr CreateDiskCacheEntry(disk_cache::Backend* cache, | |
| 79 const char* key, | |
| 80 const std::string& data) { | |
| 81 disk_cache::Entry* temp_entry = nullptr; | |
| 82 net::TestCompletionCallback callback; | |
| 83 int rv = cache->CreateEntry(key, &temp_entry, callback.callback()); | |
| 84 if (callback.GetResult(rv) != net::OK) | |
| 85 return nullptr; | |
| 86 disk_cache::ScopedEntryPtr entry(temp_entry); | |
| 87 | |
| 88 scoped_refptr<net::StringIOBuffer> iobuffer = new net::StringIOBuffer(data); | |
| 89 rv = entry->WriteData(kTestDiskCacheStreamIndex, 0, iobuffer.get(), | |
| 90 iobuffer->size(), callback.callback(), false); | |
| 91 EXPECT_EQ(static_cast<int>(data.size()), callback.GetResult(rv)); | |
| 92 return entry; | |
| 93 } | |
| 94 | |
| 95 void SaveBlobStatus(BlobStatus* status_ptr, BlobStatus status) { | |
| 96 *status_ptr = status; | |
| 97 } | |
| 98 | |
| 99 void SaveBlobStatusAndFiles(BlobStatus* status_ptr, | |
| 100 std::vector<FileCreationInfo>* files_ptr, | |
| 101 BlobStatus status, | |
| 102 std::vector<FileCreationInfo> files) { | |
| 103 EXPECT_FALSE(BlobStatusIsError(status)); | |
| 104 *status_ptr = status; | |
| 105 for (FileCreationInfo& info : files) { | |
| 106 files_ptr->push_back(std::move(info)); | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 void IncrementNumber(size_t* number, BlobStatus status) { | |
| 111 EXPECT_EQ(BlobStatus::DONE, status); | |
| 112 *number = *number + 1; | |
| 113 } | |
| 114 | |
| 115 } // namespace | |
| 116 | |
| 117 class BlobStorageContextTest : public testing::Test { | |
| 118 protected: | |
| 119 BlobStorageContextTest() {} | |
| 120 ~BlobStorageContextTest() override {} | |
| 121 | |
| 122 void SetUp() override { | |
| 123 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | |
| 124 context_ = base::MakeUnique<BlobStorageContext>(); | |
| 125 } | |
| 126 | |
| 127 void TearDown() override { | |
| 128 base::RunLoop().RunUntilIdle(); | |
| 129 file_runner_->RunPendingTasks(); | |
| 130 ASSERT_TRUE(temp_dir_.Delete()); | |
| 131 } | |
| 132 | |
| 133 std::unique_ptr<BlobDataHandle> SetupBasicBlob(const std::string& id) { | |
| 134 BlobDataBuilder builder(id); | |
| 135 builder.AppendData("1", 1); | |
| 136 builder.set_content_type("text/plain"); | |
| 137 return context_->AddFinishedBlob(builder); | |
| 138 } | |
| 139 | |
| 140 void SetTestMemoryLimits() { | |
| 141 BlobStorageLimits limits; | |
| 142 limits.max_ipc_memory_size = kTestBlobStorageIPCThresholdBytes; | |
| 143 limits.max_shared_memory_size = kTestBlobStorageMaxSharedMemoryBytes; | |
| 144 limits.max_blob_in_memory_space = kTestBlobStorageMaxBlobMemorySize; | |
| 145 limits.desired_max_disk_space = kTestBlobStorageMaxDiskSpace; | |
| 146 limits.effective_max_disk_space = kTestBlobStorageMaxDiskSpace; | |
| 147 limits.min_page_file_size = kTestBlobStorageMinFileSizeBytes; | |
| 148 limits.max_file_size = kTestBlobStorageMaxFileSizeBytes; | |
| 149 context_->mutable_memory_controller()->set_limits_for_testing(limits); | |
| 150 } | |
| 151 | |
| 152 void IncrementRefCount(const std::string& uuid) { | |
| 153 context_->IncrementBlobRefCount(uuid); | |
| 154 } | |
| 155 | |
| 156 void DecrementRefCount(const std::string& uuid) { | |
| 157 context_->DecrementBlobRefCount(uuid); | |
| 158 } | |
| 159 | |
| 160 std::vector<FileCreationInfo> files_; | |
| 161 base::ScopedTempDir temp_dir_; | |
| 162 scoped_refptr<TestSimpleTaskRunner> file_runner_ = new TestSimpleTaskRunner(); | |
| 163 | |
| 164 base::MessageLoop fake_io_message_loop_; | |
| 165 std::unique_ptr<BlobStorageContext> context_; | |
| 166 }; | |
| 167 | |
| 168 TEST_F(BlobStorageContextTest, BuildBlobAsync) { | |
| 169 const std::string kId("id"); | |
| 170 const size_t kSize = 10u; | |
| 171 BlobStatus status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
| 172 | |
| 173 BlobDataBuilder builder(kId); | |
| 174 builder.AppendFutureData(kSize); | |
| 175 builder.set_content_type("text/plain"); | |
| 176 EXPECT_EQ(0lu, context_->memory_controller().memory_usage()); | |
| 177 std::unique_ptr<BlobDataHandle> handle = context_->BuildBlob( | |
| 178 builder, base::Bind(&SaveBlobStatusAndFiles, &status, &files_)); | |
| 179 EXPECT_EQ(10lu, context_->memory_controller().memory_usage()); | |
| 180 EXPECT_TRUE(handle->IsBeingBuilt()) | |
| 181 << static_cast<int>(handle->GetBlobStatus()); | |
| 182 EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, status); | |
| 183 | |
| 184 BlobStatus construction_done = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
| 185 handle->RunOnConstructionComplete( | |
| 186 base::Bind(&SaveBlobStatus, &construction_done)); | |
| 187 | |
| 188 EXPECT_EQ(10u, context_->memory_controller().memory_usage()); | |
| 189 | |
| 190 builder.PopulateFutureData(0, "abcdefghij", 0, 10u); | |
| 191 context_->NotifyTransportComplete(kId); | |
| 192 | |
| 193 // Check we're done. | |
| 194 EXPECT_EQ(BlobStatus::DONE, handle->GetBlobStatus()); | |
| 195 base::RunLoop().RunUntilIdle(); | |
| 196 EXPECT_EQ(BlobStatus::DONE, construction_done); | |
| 197 | |
| 198 EXPECT_EQ(builder, *handle->CreateSnapshot()); | |
| 199 | |
| 200 handle.reset(); | |
| 201 base::RunLoop().RunUntilIdle(); | |
| 202 EXPECT_EQ(0lu, context_->memory_controller().memory_usage()); | |
| 203 } | |
| 204 | |
| 205 TEST_F(BlobStorageContextTest, BuildBlobAndCancel) { | |
| 206 const std::string kId("id"); | |
| 207 const size_t kSize = 10u; | |
| 208 BlobStatus status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
| 209 | |
| 210 BlobDataBuilder builder(kId); | |
| 211 builder.AppendFutureData(kSize); | |
| 212 builder.set_content_type("text/plain"); | |
| 213 EXPECT_EQ(0lu, context_->memory_controller().memory_usage()); | |
| 214 std::unique_ptr<BlobDataHandle> handle = context_->BuildBlob( | |
| 215 builder, base::Bind(&SaveBlobStatusAndFiles, &status, &files_)); | |
| 216 EXPECT_EQ(10lu, context_->memory_controller().memory_usage()); | |
| 217 EXPECT_TRUE(handle->IsBeingBuilt()); | |
| 218 EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, status); | |
| 219 EXPECT_EQ(10u, context_->memory_controller().memory_usage()); | |
| 220 | |
| 221 BlobStatus construction_done = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
| 222 handle->RunOnConstructionComplete( | |
| 223 base::Bind(&SaveBlobStatus, &construction_done)); | |
| 224 | |
| 225 context_->CancelBuildingBlob(kId, BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT); | |
| 226 EXPECT_TRUE(handle->IsBroken()); | |
| 227 EXPECT_EQ(0lu, context_->memory_controller().memory_usage()); | |
| 228 | |
| 229 // Check we're broken. | |
| 230 EXPECT_EQ(BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT, handle->GetBlobStatus()); | |
| 231 base::RunLoop().RunUntilIdle(); | |
| 232 EXPECT_EQ(BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT, construction_done); | |
| 233 } | |
| 234 | |
| 235 TEST_F(BlobStorageContextTest, CancelledReference) { | |
| 236 const std::string kId1("id1"); | |
| 237 const std::string kId2("id2"); | |
| 238 const size_t kSize = 10u; | |
| 239 BlobStatus status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
| 240 | |
| 241 // Start our first blob. | |
| 242 BlobDataBuilder builder(kId1); | |
| 243 builder.AppendFutureData(kSize); | |
| 244 builder.set_content_type("text/plain"); | |
| 245 EXPECT_EQ(0lu, context_->memory_controller().memory_usage()); | |
| 246 std::unique_ptr<BlobDataHandle> handle = context_->BuildBlob( | |
| 247 builder, base::Bind(&SaveBlobStatusAndFiles, &status, &files_)); | |
| 248 EXPECT_EQ(10lu, context_->memory_controller().memory_usage()); | |
| 249 EXPECT_TRUE(handle->IsBeingBuilt()); | |
| 250 EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, status); | |
| 251 | |
| 252 BlobStatus construction_done = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
| 253 handle->RunOnConstructionComplete( | |
| 254 base::Bind(&SaveBlobStatus, &construction_done)); | |
| 255 | |
| 256 EXPECT_EQ(10u, context_->memory_controller().memory_usage()); | |
| 257 | |
| 258 // Create our second blob, which depends on the first. | |
| 259 BlobDataBuilder builder2(kId2); | |
| 260 builder2.AppendBlob(kId1); | |
| 261 builder2.set_content_type("text/plain"); | |
| 262 std::unique_ptr<BlobDataHandle> handle2 = context_->BuildBlob( | |
| 263 builder2, BlobStorageContext::TransportAllowedCallback()); | |
| 264 BlobStatus construction_done2 = | |
| 265 BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
| 266 handle->RunOnConstructionComplete( | |
| 267 base::Bind(&SaveBlobStatus, &construction_done2)); | |
| 268 EXPECT_TRUE(handle2->IsBeingBuilt()); | |
| 269 | |
| 270 EXPECT_EQ(10lu, context_->memory_controller().memory_usage()); | |
| 271 | |
| 272 // Cancel the first blob. | |
| 273 context_->CancelBuildingBlob(kId1, BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT); | |
| 274 | |
| 275 base::RunLoop().RunUntilIdle(); | |
| 276 // Check we broke successfully. | |
| 277 EXPECT_EQ(BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT, construction_done); | |
| 278 EXPECT_EQ(BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT, handle->GetBlobStatus()); | |
| 279 EXPECT_EQ(0lu, context_->memory_controller().memory_usage()); | |
| 280 EXPECT_TRUE(handle->IsBroken()); | |
| 281 | |
| 282 // Check that it propagated. | |
| 283 EXPECT_TRUE(handle2->IsBroken()); | |
| 284 EXPECT_EQ(BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT, construction_done2); | |
| 285 EXPECT_EQ(BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT, handle->GetBlobStatus()); | |
| 286 } | |
| 287 | |
| 288 TEST_F(BlobStorageContextTest, IncorrectSlice) { | |
| 289 const std::string kId1("id1"); | |
| 290 const std::string kId2("id2"); | |
| 291 | |
| 292 std::unique_ptr<BlobDataHandle> handle = SetupBasicBlob(kId1); | |
| 293 | |
| 294 EXPECT_EQ(1lu, context_->memory_controller().memory_usage()); | |
| 295 | |
| 296 BlobDataBuilder builder(kId2); | |
| 297 builder.AppendBlob(kId1, 1, 10); | |
| 298 std::unique_ptr<BlobDataHandle> handle2 = context_->BuildBlob( | |
| 299 builder, BlobStorageContext::TransportAllowedCallback()); | |
| 300 | |
| 301 EXPECT_TRUE(handle2->IsBroken()); | |
| 302 EXPECT_EQ(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, | |
| 303 handle2->GetBlobStatus()); | |
| 304 } | |
| 305 | |
| 306 TEST_F(BlobStorageContextTest, IncrementDecrementRef) { | |
| 307 // Build up a basic blob. | |
| 308 const std::string kId("id"); | |
| 309 std::unique_ptr<BlobDataHandle> blob_data_handle = SetupBasicBlob(kId); | |
| 310 | |
| 311 // Do an extra increment to keep it around after we kill the handle. | |
| 312 IncrementRefCount(kId); | |
| 313 IncrementRefCount(kId); | |
| 314 DecrementRefCount(kId); | |
| 315 blob_data_handle = context_->GetBlobDataFromUUID(kId); | |
| 316 EXPECT_TRUE(blob_data_handle); | |
| 317 blob_data_handle.reset(); | |
| 318 base::RunLoop().RunUntilIdle(); | |
| 319 | |
| 320 EXPECT_TRUE(context_->registry().HasEntry(kId)); | |
| 321 DecrementRefCount(kId); | |
| 322 EXPECT_FALSE(context_->registry().HasEntry(kId)); | |
| 323 | |
| 324 // Make sure it goes away in the end. | |
| 325 blob_data_handle = context_->GetBlobDataFromUUID(kId); | |
| 326 EXPECT_FALSE(blob_data_handle); | |
| 327 } | |
| 328 | |
| 329 TEST_F(BlobStorageContextTest, BlobDataHandle) { | |
| 330 // Build up a basic blob. | |
| 331 const std::string kId("id"); | |
| 332 std::unique_ptr<BlobDataHandle> blob_data_handle = SetupBasicBlob(kId); | |
| 333 EXPECT_TRUE(blob_data_handle); | |
| 334 | |
| 335 // Get another handle | |
| 336 std::unique_ptr<BlobDataHandle> another_handle = | |
| 337 context_->GetBlobDataFromUUID(kId); | |
| 338 EXPECT_TRUE(another_handle); | |
| 339 | |
| 340 // Should disappear after dropping both handles. | |
| 341 blob_data_handle.reset(); | |
| 342 base::RunLoop().RunUntilIdle(); | |
| 343 | |
| 344 EXPECT_TRUE(context_->registry().HasEntry(kId)); | |
| 345 | |
| 346 another_handle.reset(); | |
| 347 base::RunLoop().RunUntilIdle(); | |
| 348 | |
| 349 blob_data_handle = context_->GetBlobDataFromUUID(kId); | |
| 350 EXPECT_FALSE(blob_data_handle); | |
| 351 } | |
| 352 | |
| 353 TEST_F(BlobStorageContextTest, MemoryUsage) { | |
| 354 const std::string kId1("id1"); | |
| 355 const std::string kId2("id2"); | |
| 356 | |
| 357 BlobDataBuilder builder1(kId1); | |
| 358 BlobDataBuilder builder2(kId2); | |
| 359 builder1.AppendData("Data1Data2"); | |
| 360 builder2.AppendBlob(kId1); | |
| 361 builder2.AppendBlob(kId1); | |
| 362 builder2.AppendBlob(kId1); | |
| 363 builder2.AppendBlob(kId1); | |
| 364 builder2.AppendBlob(kId1); | |
| 365 builder2.AppendBlob(kId1); | |
| 366 builder2.AppendBlob(kId1); | |
| 367 | |
| 368 EXPECT_EQ(0lu, context_->memory_controller().memory_usage()); | |
| 369 | |
| 370 std::unique_ptr<BlobDataHandle> blob_data_handle = | |
| 371 context_->AddFinishedBlob(&builder1); | |
| 372 EXPECT_EQ(10lu, context_->memory_controller().memory_usage()); | |
| 373 std::unique_ptr<BlobDataHandle> blob_data_handle2 = | |
| 374 context_->AddFinishedBlob(&builder2); | |
| 375 EXPECT_EQ(10lu, context_->memory_controller().memory_usage()); | |
| 376 | |
| 377 EXPECT_EQ(2u, context_->registry().blob_count()); | |
| 378 | |
| 379 blob_data_handle.reset(); | |
| 380 base::RunLoop().RunUntilIdle(); | |
| 381 | |
| 382 EXPECT_EQ(10lu, context_->memory_controller().memory_usage()); | |
| 383 EXPECT_EQ(1u, context_->registry().blob_count()); | |
| 384 blob_data_handle2.reset(); | |
| 385 base::RunLoop().RunUntilIdle(); | |
| 386 | |
| 387 EXPECT_EQ(0lu, context_->memory_controller().memory_usage()); | |
| 388 EXPECT_EQ(0u, context_->registry().blob_count()); | |
| 389 } | |
| 390 | |
| 391 TEST_F(BlobStorageContextTest, AddFinishedBlob) { | |
| 392 const std::string kId1("id1"); | |
| 393 const std::string kId2("id12"); | |
| 394 const std::string kId3("id3"); | |
| 395 | |
| 396 BlobDataBuilder builder1(kId1); | |
| 397 BlobDataBuilder builder2(kId2); | |
| 398 BlobDataBuilder canonicalized_blob_data2(kId2); | |
| 399 builder1.AppendData("Data1Data2"); | |
| 400 builder2.AppendBlob(kId1, 5, 5); | |
| 401 builder2.AppendData(" is the best"); | |
| 402 canonicalized_blob_data2.AppendData("Data2"); | |
| 403 canonicalized_blob_data2.AppendData(" is the best"); | |
| 404 | |
| 405 BlobStorageContext context; | |
| 406 | |
| 407 std::unique_ptr<BlobDataHandle> blob_data_handle = | |
| 408 context_->AddFinishedBlob(&builder1); | |
| 409 std::unique_ptr<BlobDataHandle> blob_data_handle2 = | |
| 410 context_->AddFinishedBlob(&builder2); | |
| 411 | |
| 412 EXPECT_EQ(10u + 12u + 5u, context_->memory_controller().memory_usage()); | |
| 413 | |
| 414 ASSERT_TRUE(blob_data_handle); | |
| 415 ASSERT_TRUE(blob_data_handle2); | |
| 416 std::unique_ptr<BlobDataSnapshot> data1 = blob_data_handle->CreateSnapshot(); | |
| 417 std::unique_ptr<BlobDataSnapshot> data2 = blob_data_handle2->CreateSnapshot(); | |
| 418 EXPECT_EQ(*data1, builder1); | |
| 419 EXPECT_EQ(*data2, canonicalized_blob_data2); | |
| 420 blob_data_handle.reset(); | |
| 421 data2.reset(); | |
| 422 | |
| 423 base::RunLoop().RunUntilIdle(); | |
| 424 | |
| 425 EXPECT_EQ(12u + 5u, context_->memory_controller().memory_usage()); | |
| 426 | |
| 427 blob_data_handle = context_->GetBlobDataFromUUID(kId1); | |
| 428 EXPECT_FALSE(blob_data_handle); | |
| 429 EXPECT_TRUE(blob_data_handle2); | |
| 430 data2 = blob_data_handle2->CreateSnapshot(); | |
| 431 EXPECT_EQ(*data2, canonicalized_blob_data2); | |
| 432 | |
| 433 // Test shared elements stick around. | |
| 434 BlobDataBuilder builder3(kId3); | |
| 435 builder3.AppendBlob(kId2); | |
| 436 builder3.AppendBlob(kId2); | |
| 437 std::unique_ptr<BlobDataHandle> blob_data_handle3 = | |
| 438 context_->AddFinishedBlob(&builder3); | |
| 439 EXPECT_FALSE(blob_data_handle3->IsBeingBuilt()); | |
| 440 blob_data_handle2.reset(); | |
| 441 base::RunLoop().RunUntilIdle(); | |
| 442 | |
| 443 EXPECT_EQ(12u + 5u, context_->memory_controller().memory_usage()); | |
| 444 | |
| 445 blob_data_handle2 = context_->GetBlobDataFromUUID(kId2); | |
| 446 EXPECT_FALSE(blob_data_handle2); | |
| 447 EXPECT_TRUE(blob_data_handle3); | |
| 448 std::unique_ptr<BlobDataSnapshot> data3 = blob_data_handle3->CreateSnapshot(); | |
| 449 | |
| 450 BlobDataBuilder canonicalized_blob_data3(kId3); | |
| 451 canonicalized_blob_data3.AppendData("Data2"); | |
| 452 canonicalized_blob_data3.AppendData(" is the best"); | |
| 453 canonicalized_blob_data3.AppendData("Data2"); | |
| 454 canonicalized_blob_data3.AppendData(" is the best"); | |
| 455 EXPECT_EQ(*data3, canonicalized_blob_data3); | |
| 456 | |
| 457 blob_data_handle.reset(); | |
| 458 blob_data_handle2.reset(); | |
| 459 blob_data_handle3.reset(); | |
| 460 base::RunLoop().RunUntilIdle(); | |
| 461 } | |
| 462 | |
| 463 TEST_F(BlobStorageContextTest, AddFinishedBlob_LargeOffset) { | |
| 464 // A value which does not fit in a 4-byte data type. Used to confirm that | |
| 465 // large values are supported on 32-bit Chromium builds. Regression test for: | |
| 466 // crbug.com/458122. | |
| 467 const uint64_t kLargeSize = std::numeric_limits<uint64_t>::max() - 1; | |
| 468 | |
| 469 const uint64_t kBlobLength = 5; | |
| 470 const std::string kId1("id1"); | |
| 471 const std::string kId2("id2"); | |
| 472 | |
| 473 BlobDataBuilder builder1(kId1); | |
| 474 builder1.AppendFileSystemFile(GURL(), 0, kLargeSize, base::Time::Now()); | |
| 475 | |
| 476 BlobDataBuilder builder2(kId2); | |
| 477 builder2.AppendBlob(kId1, kLargeSize - kBlobLength, kBlobLength); | |
| 478 | |
| 479 std::unique_ptr<BlobDataHandle> blob_data_handle1 = | |
| 480 context_->AddFinishedBlob(&builder1); | |
| 481 std::unique_ptr<BlobDataHandle> blob_data_handle2 = | |
| 482 context_->AddFinishedBlob(&builder2); | |
| 483 | |
| 484 ASSERT_TRUE(blob_data_handle1); | |
| 485 ASSERT_TRUE(blob_data_handle2); | |
| 486 std::unique_ptr<BlobDataSnapshot> data = blob_data_handle2->CreateSnapshot(); | |
| 487 ASSERT_EQ(1u, data->items().size()); | |
| 488 const scoped_refptr<BlobDataItem> item = data->items()[0]; | |
| 489 EXPECT_EQ(kLargeSize - kBlobLength, item->offset()); | |
| 490 EXPECT_EQ(kBlobLength, item->length()); | |
| 491 | |
| 492 blob_data_handle1.reset(); | |
| 493 blob_data_handle2.reset(); | |
| 494 base::RunLoop().RunUntilIdle(); | |
| 495 } | |
| 496 | |
| 497 TEST_F(BlobStorageContextTest, BuildDiskCacheBlob) { | |
| 498 scoped_refptr<BlobDataBuilder::DataHandle> | |
| 499 data_handle = new EmptyDataHandle(); | |
| 500 | |
| 501 { | |
| 502 BlobStorageContext context; | |
| 503 | |
| 504 std::unique_ptr<disk_cache::Backend> cache = CreateInMemoryDiskCache(); | |
| 505 ASSERT_TRUE(cache); | |
| 506 | |
| 507 const std::string kTestBlobData = "Test Blob Data"; | |
| 508 disk_cache::ScopedEntryPtr entry = | |
| 509 CreateDiskCacheEntry(cache.get(), "test entry", kTestBlobData); | |
| 510 | |
| 511 const std::string kId1Prime("id1.prime"); | |
| 512 BlobDataBuilder canonicalized_blob_data(kId1Prime); | |
| 513 canonicalized_blob_data.AppendData(kTestBlobData.c_str()); | |
| 514 | |
| 515 const std::string kId1("id1"); | |
| 516 BlobDataBuilder builder(kId1); | |
| 517 | |
| 518 builder.AppendDiskCacheEntry( | |
| 519 data_handle, entry.get(), kTestDiskCacheStreamIndex); | |
| 520 | |
| 521 std::unique_ptr<BlobDataHandle> blob_data_handle = | |
| 522 context.AddFinishedBlob(&builder); | |
| 523 std::unique_ptr<BlobDataSnapshot> data = blob_data_handle->CreateSnapshot(); | |
| 524 EXPECT_EQ(*data, builder); | |
| 525 EXPECT_FALSE(data_handle->HasOneRef()) | |
| 526 << "Data handle was destructed while context and builder still exist."; | |
| 527 } | |
| 528 EXPECT_TRUE(data_handle->HasOneRef()) | |
| 529 << "Data handle was not destructed along with blob storage context."; | |
| 530 base::RunLoop().RunUntilIdle(); | |
| 531 } | |
| 532 | |
| 533 TEST_F(BlobStorageContextTest, BuildFutureFileOnlyBlob) { | |
| 534 const std::string kId1("id1"); | |
| 535 context_ = | |
| 536 base::MakeUnique<BlobStorageContext>(temp_dir_.GetPath(), file_runner_); | |
| 537 SetTestMemoryLimits(); | |
| 538 | |
| 539 BlobDataBuilder builder(kId1); | |
| 540 builder.set_content_type("text/plain"); | |
| 541 builder.AppendFutureFile(0, 10, 0); | |
| 542 | |
| 543 BlobStatus status = BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
| 544 std::unique_ptr<BlobDataHandle> handle = context_->BuildBlob( | |
| 545 builder, base::Bind(&SaveBlobStatusAndFiles, &status, &files_)); | |
| 546 | |
| 547 size_t blobs_finished = 0; | |
| 548 EXPECT_EQ(BlobStatus::PENDING_QUOTA, handle->GetBlobStatus()); | |
| 549 EXPECT_EQ(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, status); | |
| 550 handle->RunOnConstructionComplete( | |
| 551 base::Bind(&IncrementNumber, &blobs_finished)); | |
| 552 EXPECT_EQ(0u, blobs_finished); | |
| 553 | |
| 554 EXPECT_TRUE(file_runner_->HasPendingTask()); | |
| 555 file_runner_->RunPendingTasks(); | |
| 556 EXPECT_EQ(0u, blobs_finished); | |
| 557 EXPECT_EQ(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, status); | |
| 558 EXPECT_EQ(BlobStatus::PENDING_QUOTA, handle->GetBlobStatus()); | |
| 559 base::RunLoop().RunUntilIdle(); | |
| 560 | |
| 561 EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, status); | |
| 562 EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, handle->GetBlobStatus()); | |
| 563 EXPECT_EQ(0u, blobs_finished); | |
| 564 | |
| 565 ASSERT_EQ(1u, files_.size()); | |
| 566 | |
| 567 builder.PopulateFutureFile(0, files_[0].file_reference, base::Time::Max()); | |
| 568 context_->NotifyTransportComplete(kId1); | |
| 569 | |
| 570 EXPECT_EQ(BlobStatus::DONE, handle->GetBlobStatus()); | |
| 571 EXPECT_EQ(0u, blobs_finished); | |
| 572 base::RunLoop().RunUntilIdle(); | |
| 573 EXPECT_EQ(1u, blobs_finished); | |
| 574 | |
| 575 builder.Clear(); | |
| 576 handle.reset(); | |
| 577 files_.clear(); | |
| 578 base::RunLoop().RunUntilIdle(); | |
| 579 // We should have file cleanup tasks. | |
| 580 EXPECT_TRUE(file_runner_->HasPendingTask()); | |
| 581 file_runner_->RunPendingTasks(); | |
| 582 base::RunLoop().RunUntilIdle(); | |
| 583 EXPECT_EQ(0lu, context_->memory_controller().memory_usage()); | |
| 584 EXPECT_EQ(0lu, context_->memory_controller().disk_usage()); | |
| 585 } | |
| 586 | |
| 587 TEST_F(BlobStorageContextTest, CompoundBlobs) { | |
| 588 const std::string kId1("id1"); | |
| 589 const std::string kId2("id2"); | |
| 590 const std::string kId3("id3"); | |
| 591 | |
| 592 // Setup a set of blob data for testing. | |
| 593 base::Time time1, time2; | |
| 594 ASSERT_TRUE(base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT", &time1)); | |
| 595 ASSERT_TRUE(base::Time::FromString("Mon, 14 Nov 1994, 11:30:49 GMT", &time2)); | |
| 596 | |
| 597 BlobDataBuilder blob_data1(kId1); | |
| 598 blob_data1.AppendData("Data1"); | |
| 599 blob_data1.AppendData("Data2"); | |
| 600 blob_data1.AppendFile(base::FilePath(FILE_PATH_LITERAL("File1.txt")), 10, | |
| 601 1024, time1); | |
| 602 | |
| 603 BlobDataBuilder blob_data2(kId2); | |
| 604 blob_data2.AppendData("Data3"); | |
| 605 blob_data2.AppendBlob(kId1, 8, 100); | |
| 606 blob_data2.AppendFile(base::FilePath(FILE_PATH_LITERAL("File2.txt")), 0, 20, | |
| 607 time2); | |
| 608 | |
| 609 BlobDataBuilder blob_data3(kId3); | |
| 610 blob_data3.AppendData("Data4"); | |
| 611 std::unique_ptr<disk_cache::Backend> cache = CreateInMemoryDiskCache(); | |
| 612 ASSERT_TRUE(cache); | |
| 613 disk_cache::ScopedEntryPtr disk_cache_entry = | |
| 614 CreateDiskCacheEntry(cache.get(), "another key", "Data5"); | |
| 615 blob_data3.AppendDiskCacheEntry(new EmptyDataHandle(), disk_cache_entry.get(), | |
| 616 kTestDiskCacheStreamIndex); | |
| 617 | |
| 618 BlobDataBuilder canonicalized_blob_data2(kId2); | |
| 619 canonicalized_blob_data2.AppendData("Data3"); | |
| 620 canonicalized_blob_data2.AppendData("a2___", 2); | |
| 621 canonicalized_blob_data2.AppendFile( | |
| 622 base::FilePath(FILE_PATH_LITERAL("File1.txt")), 10, 98, time1); | |
| 623 canonicalized_blob_data2.AppendFile( | |
| 624 base::FilePath(FILE_PATH_LITERAL("File2.txt")), 0, 20, time2); | |
| 625 | |
| 626 BlobStorageContext context; | |
| 627 std::unique_ptr<BlobDataHandle> blob_data_handle; | |
| 628 | |
| 629 // Test a blob referring to only data and a file. | |
| 630 blob_data_handle = context_->AddFinishedBlob(&blob_data1); | |
| 631 | |
| 632 ASSERT_TRUE(blob_data_handle); | |
| 633 std::unique_ptr<BlobDataSnapshot> data = blob_data_handle->CreateSnapshot(); | |
| 634 ASSERT_TRUE(blob_data_handle); | |
| 635 EXPECT_EQ(*data, blob_data1); | |
| 636 | |
| 637 // Test a blob composed in part with another blob. | |
| 638 blob_data_handle = context_->AddFinishedBlob(&blob_data2); | |
| 639 data = blob_data_handle->CreateSnapshot(); | |
| 640 ASSERT_TRUE(blob_data_handle); | |
| 641 ASSERT_TRUE(data); | |
| 642 EXPECT_EQ(*data, canonicalized_blob_data2); | |
| 643 | |
| 644 // Test a blob referring to only data and a disk cache entry. | |
| 645 blob_data_handle = context_->AddFinishedBlob(&blob_data3); | |
| 646 data = blob_data_handle->CreateSnapshot(); | |
| 647 ASSERT_TRUE(blob_data_handle); | |
| 648 EXPECT_EQ(*data, blob_data3); | |
| 649 | |
| 650 blob_data_handle.reset(); | |
| 651 base::RunLoop().RunUntilIdle(); | |
| 652 } | |
| 653 | |
| 654 TEST_F(BlobStorageContextTest, PublicBlobUrls) { | |
| 655 // Build up a basic blob. | |
| 656 const std::string kId("id"); | |
| 657 std::unique_ptr<BlobDataHandle> first_handle = SetupBasicBlob(kId); | |
| 658 | |
| 659 // Now register a url for that blob. | |
| 660 GURL kUrl("blob:id"); | |
| 661 context_->RegisterPublicBlobURL(kUrl, kId); | |
| 662 std::unique_ptr<BlobDataHandle> blob_data_handle = | |
| 663 context_->GetBlobDataFromPublicURL(kUrl); | |
| 664 ASSERT_TRUE(blob_data_handle.get()); | |
| 665 EXPECT_EQ(kId, blob_data_handle->uuid()); | |
| 666 std::unique_ptr<BlobDataSnapshot> data = blob_data_handle->CreateSnapshot(); | |
| 667 blob_data_handle.reset(); | |
| 668 first_handle.reset(); | |
| 669 base::RunLoop().RunUntilIdle(); | |
| 670 | |
| 671 // The url registration should keep the blob alive even after | |
| 672 // explicit references are dropped. | |
| 673 blob_data_handle = context_->GetBlobDataFromPublicURL(kUrl); | |
| 674 EXPECT_TRUE(blob_data_handle); | |
| 675 blob_data_handle.reset(); | |
| 676 | |
| 677 base::RunLoop().RunUntilIdle(); | |
| 678 // Finally get rid of the url registration and the blob. | |
| 679 context_->RevokePublicBlobURL(kUrl); | |
| 680 blob_data_handle = context_->GetBlobDataFromPublicURL(kUrl); | |
| 681 EXPECT_FALSE(blob_data_handle.get()); | |
| 682 EXPECT_FALSE(context_->registry().HasEntry(kId)); | |
| 683 } | |
| 684 | |
| 685 TEST_F(BlobStorageContextTest, TestUnknownBrokenAndBuildingBlobReference) { | |
| 686 const std::string kBrokenId("broken_id"); | |
| 687 const std::string kBuildingId("building_id"); | |
| 688 const std::string kReferencingId("referencing_id"); | |
| 689 const std::string kUnknownId("unknown_id"); | |
| 690 | |
| 691 // Create a broken blob. | |
| 692 std::unique_ptr<BlobDataHandle> broken_handle = | |
| 693 context_->AddBrokenBlob(kBrokenId, "", "", BlobStatus::ERR_OUT_OF_MEMORY); | |
| 694 EXPECT_TRUE(broken_handle->GetBlobStatus() == BlobStatus::ERR_OUT_OF_MEMORY); | |
| 695 EXPECT_TRUE(context_->registry().HasEntry(kBrokenId)); | |
| 696 | |
| 697 // Try to create a blob with a reference to an unknown blob. | |
| 698 BlobDataBuilder builder(kReferencingId); | |
| 699 builder.AppendData("data"); | |
| 700 builder.AppendBlob(kUnknownId); | |
| 701 std::unique_ptr<BlobDataHandle> handle = context_->AddFinishedBlob(builder); | |
| 702 EXPECT_TRUE(handle->IsBroken()); | |
| 703 EXPECT_TRUE(context_->registry().HasEntry(kReferencingId)); | |
| 704 handle.reset(); | |
| 705 base::RunLoop().RunUntilIdle(); | |
| 706 EXPECT_FALSE(context_->registry().HasEntry(kReferencingId)); | |
| 707 | |
| 708 // Try to create a blob with a reference to the broken blob. | |
| 709 BlobDataBuilder builder2(kReferencingId); | |
| 710 builder2.AppendData("data"); | |
| 711 builder2.AppendBlob(kBrokenId); | |
| 712 handle = context_->AddFinishedBlob(builder2); | |
| 713 EXPECT_TRUE(handle->IsBroken()); | |
| 714 EXPECT_TRUE(context_->registry().HasEntry(kReferencingId)); | |
| 715 handle.reset(); | |
| 716 base::RunLoop().RunUntilIdle(); | |
| 717 EXPECT_FALSE(context_->registry().HasEntry(kReferencingId)); | |
| 718 | |
| 719 // Try to create a blob with a reference to the building blob. | |
| 720 BlobDataBuilder builder3(kReferencingId); | |
| 721 builder3.AppendData("data"); | |
| 722 builder3.AppendBlob(kBuildingId); | |
| 723 handle = context_->AddFinishedBlob(builder3); | |
| 724 EXPECT_TRUE(handle->IsBroken()); | |
| 725 EXPECT_TRUE(context_->registry().HasEntry(kReferencingId)); | |
| 726 handle.reset(); | |
| 727 base::RunLoop().RunUntilIdle(); | |
| 728 EXPECT_FALSE(context_->registry().HasEntry(kReferencingId)); | |
| 729 } | |
| 730 | |
| 731 namespace { | |
| 732 constexpr size_t kTotalRawBlobs = 200; | |
| 733 constexpr size_t kTotalSlicedBlobs = 100; | |
| 734 constexpr char kTestDiskCacheData[] = "Test Blob Data"; | |
| 735 | |
| 736 // Appends data and data types that depend on the index. This is designed to | |
| 737 // exercise all types of combinations of data, future data, files, future files, | |
| 738 // and disk cache entries. | |
| 739 size_t AppendDataInBuilder(BlobDataBuilder* builder, | |
| 740 size_t index, | |
| 741 disk_cache::Entry* cache_entry) { | |
| 742 size_t size = 0; | |
| 743 // We can't have both future data and future files, so split those up. | |
| 744 if (index % 2 != 0) { | |
| 745 builder->AppendFutureData(5u); | |
| 746 size += 5u; | |
| 747 if (index % 3 == 1) { | |
| 748 builder->AppendData("abcdefghij", 4u); | |
| 749 size += 4u; | |
| 750 } | |
| 751 if (index % 3 == 0) { | |
| 752 builder->AppendFutureData(1u); | |
| 753 size += 1u; | |
| 754 } | |
| 755 } else if (index % 3 == 0) { | |
| 756 builder->AppendFutureFile(0lu, 3lu, 0); | |
| 757 size += 3u; | |
| 758 } | |
| 759 if (index % 5 != 0) { | |
| 760 builder->AppendFile( | |
| 761 base::FilePath::FromUTF8Unsafe(base::SizeTToString(index)), 0ul, 20ul, | |
| 762 base::Time::Max()); | |
| 763 size += 20u; | |
| 764 } | |
| 765 if (index % 3 != 0) { | |
| 766 scoped_refptr<BlobDataBuilder::DataHandle> disk_cache_data_handle = | |
| 767 new EmptyDataHandle(); | |
| 768 builder->AppendDiskCacheEntry(disk_cache_data_handle, cache_entry, | |
| 769 kTestDiskCacheStreamIndex); | |
| 770 size += strlen(kTestDiskCacheData); | |
| 771 } | |
| 772 return size; | |
| 773 } | |
| 774 | |
| 775 bool DoesBuilderHaveFutureData(size_t index) { | |
| 776 return index < kTotalRawBlobs && (index % 2 != 0 || index % 3 == 0); | |
| 777 } | |
| 778 | |
| 779 void PopulateDataInBuilder(BlobDataBuilder* builder, | |
| 780 size_t index, | |
| 781 base::TaskRunner* file_runner) { | |
| 782 if (index % 2 != 0) { | |
| 783 builder->PopulateFutureData(0, "abcde", 0, 5); | |
| 784 if (index % 3 == 0) { | |
| 785 builder->PopulateFutureData(1, "z", 0, 1); | |
| 786 } | |
| 787 } else if (index % 3 == 0) { | |
| 788 scoped_refptr<ShareableFileReference> file_ref = | |
| 789 ShareableFileReference::GetOrCreate( | |
| 790 base::FilePath::FromUTF8Unsafe( | |
| 791 base::SizeTToString(index + kTotalRawBlobs)), | |
| 792 ShareableFileReference::DONT_DELETE_ON_FINAL_RELEASE, file_runner); | |
| 793 builder->PopulateFutureFile(0, file_ref, base::Time::Max()); | |
| 794 } | |
| 795 } | |
| 796 } // namespace | |
| 797 | |
| 798 TEST_F(BlobStorageContextTest, BuildBlobCombinations) { | |
| 799 const std::string kId("id"); | |
| 800 | |
| 801 context_ = | |
| 802 base::MakeUnique<BlobStorageContext>(temp_dir_.GetPath(), file_runner_); | |
| 803 | |
| 804 SetTestMemoryLimits(); | |
| 805 std::unique_ptr<disk_cache::Backend> cache = CreateInMemoryDiskCache(); | |
| 806 ASSERT_TRUE(cache); | |
| 807 disk_cache::ScopedEntryPtr entry = | |
| 808 CreateDiskCacheEntry(cache.get(), "test entry", kTestDiskCacheData); | |
| 809 | |
| 810 // This tests mixed blob content with both synchronous and asynchronous | |
| 811 // construction. Blobs should also be paged to disk during execution. | |
| 812 std::vector<std::unique_ptr<BlobDataBuilder>> builders; | |
| 813 std::vector<size_t> sizes; | |
| 814 for (size_t i = 0; i < kTotalRawBlobs; i++) { | |
| 815 builders.emplace_back(new BlobDataBuilder(base::SizeTToString(i))); | |
| 816 auto& builder = *builders.back(); | |
| 817 size_t size = AppendDataInBuilder(&builder, i, entry.get()); | |
| 818 EXPECT_NE(0u, size); | |
| 819 sizes.push_back(size); | |
| 820 } | |
| 821 | |
| 822 for (size_t i = 0; i < kTotalSlicedBlobs; i++) { | |
| 823 builders.emplace_back( | |
| 824 new BlobDataBuilder(base::SizeTToString(i + kTotalRawBlobs))); | |
| 825 size_t source_size = sizes[i]; | |
| 826 size_t offset = source_size == 1 ? 0 : i % (source_size - 1); | |
| 827 size_t size = (i % (source_size - offset)) + 1; | |
| 828 builders.back()->AppendBlob(base::SizeTToString(i), offset, size); | |
| 829 } | |
| 830 | |
| 831 size_t total_finished_blobs = 0; | |
| 832 std::vector<std::unique_ptr<BlobDataHandle>> handles; | |
| 833 std::vector<BlobStatus> statuses; | |
| 834 std::vector<bool> populated; | |
| 835 statuses.resize(kTotalRawBlobs, | |
| 836 BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS); | |
| 837 populated.resize(kTotalRawBlobs, false); | |
| 838 for (size_t i = 0; i < builders.size(); i++) { | |
| 839 BlobDataBuilder& builder = *builders[i]; | |
| 840 builder.set_content_type("text/plain"); | |
| 841 bool has_pending_memory = DoesBuilderHaveFutureData(i); | |
| 842 std::unique_ptr<BlobDataHandle> handle = context_->BuildBlob( | |
| 843 builder, | |
| 844 has_pending_memory | |
| 845 ? base::Bind(&SaveBlobStatusAndFiles, &statuses[0] + i, &files_) | |
| 846 : BlobStorageContext::TransportAllowedCallback()); | |
| 847 handle->RunOnConstructionComplete( | |
| 848 base::Bind(&IncrementNumber, &total_finished_blobs)); | |
| 849 handles.push_back(std::move(handle)); | |
| 850 } | |
| 851 base::RunLoop().RunUntilIdle(); | |
| 852 | |
| 853 // We should be needing to send a page or two to disk. | |
| 854 EXPECT_TRUE(file_runner_->HasPendingTask()); | |
| 855 do { | |
| 856 file_runner_->RunPendingTasks(); | |
| 857 base::RunLoop().RunUntilIdle(); | |
| 858 // Continue populating data for items that can fit. | |
| 859 for (size_t i = 0; i < kTotalRawBlobs; i++) { | |
| 860 BlobDataBuilder* builder = builders[i].get(); | |
| 861 if (DoesBuilderHaveFutureData(i) && !populated[i] && | |
| 862 statuses[i] == BlobStatus::PENDING_TRANSPORT) { | |
| 863 PopulateDataInBuilder(builder, i, file_runner_.get()); | |
| 864 context_->NotifyTransportComplete(base::SizeTToString(i)); | |
| 865 populated[i] = true; | |
| 866 } | |
| 867 } | |
| 868 base::RunLoop().RunUntilIdle(); | |
| 869 } while (file_runner_->HasPendingTask()); | |
| 870 | |
| 871 // Check all builders with future items were signalled and populated. | |
| 872 for (size_t i = 0; i < populated.size(); i++) { | |
| 873 if (DoesBuilderHaveFutureData(i)) { | |
| 874 EXPECT_EQ(BlobStatus::PENDING_TRANSPORT, statuses[i]) << i; | |
| 875 EXPECT_TRUE(populated[i]) << i; | |
| 876 } | |
| 877 } | |
| 878 base::RunLoop().RunUntilIdle(); | |
| 879 | |
| 880 // We should be completely built now. | |
| 881 EXPECT_EQ(kTotalRawBlobs + kTotalSlicedBlobs, total_finished_blobs); | |
| 882 for (std::unique_ptr<BlobDataHandle>& handle : handles) { | |
| 883 EXPECT_EQ(BlobStatus::DONE, handle->GetBlobStatus()); | |
| 884 } | |
| 885 handles.clear(); | |
| 886 base::RunLoop().RunUntilIdle(); | |
| 887 files_.clear(); | |
| 888 // We should have file cleanup tasks. | |
| 889 EXPECT_TRUE(file_runner_->HasPendingTask()); | |
| 890 file_runner_->RunPendingTasks(); | |
| 891 base::RunLoop().RunUntilIdle(); | |
| 892 EXPECT_EQ(0lu, context_->memory_controller().memory_usage()); | |
| 893 EXPECT_EQ(0lu, context_->memory_controller().disk_usage()); | |
| 894 } | |
| 895 | |
| 896 // TODO(michaeln): tests for the deprecated url stuff | |
| 897 | |
| 898 } // namespace storage | |
| OLD | NEW |