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 |