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.
5 #include "storage/browser/blob/blob_storage_context.h"
10 #include "base/files/file_path.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/run_loop.h"
15 #include "base/time/time.h"
16 #include "content/browser/fileapi/blob_storage_host.h"
17 #include "net/base/io_buffer.h"
18 #include "net/base/test_completion_callback.h"
19 #include "net/disk_cache/disk_cache.h"
20 #include "storage/browser/blob/blob_data_builder.h"
21 #include "storage/browser/blob/blob_data_handle.h"
22 #include "storage/browser/blob/blob_data_item.h"
23 #include "storage/browser/blob/blob_data_snapshot.h"
24 #include "testing/gtest/include/gtest/gtest.h"
26 using storage::BlobDataBuilder
;
27 using storage::BlobDataHandle
;
28 using storage::BlobDataItem
;
29 using storage::BlobDataSnapshot
;
30 using storage::BlobStorageContext
;
31 using storage::DataElement
;
36 const int kTestDiskCacheStreamIndex
= 0;
38 // Our disk cache tests don't need a real data handle since the tests themselves
39 // scope the disk cache and entries.
40 class EmptyDataHandle
: public storage::BlobDataBuilder::DataHandle
{
42 ~EmptyDataHandle() override
{}
45 scoped_ptr
<disk_cache::Backend
> CreateInMemoryDiskCache() {
46 scoped_ptr
<disk_cache::Backend
> cache
;
47 net::TestCompletionCallback callback
;
48 int rv
= disk_cache::CreateCacheBackend(net::MEMORY_CACHE
,
49 net::CACHE_BACKEND_DEFAULT
,
51 false, nullptr, nullptr, &cache
,
53 EXPECT_EQ(net::OK
, callback
.GetResult(rv
));
58 disk_cache::ScopedEntryPtr
CreateDiskCacheEntry(disk_cache::Backend
* cache
,
60 const std::string
& data
) {
61 disk_cache::Entry
* temp_entry
= nullptr;
62 net::TestCompletionCallback callback
;
63 int rv
= cache
->CreateEntry(key
, &temp_entry
, callback
.callback());
64 if (callback
.GetResult(rv
) != net::OK
)
66 disk_cache::ScopedEntryPtr
entry(temp_entry
);
68 scoped_refptr
<net::StringIOBuffer
> iobuffer
= new net::StringIOBuffer(data
);
69 rv
= entry
->WriteData(kTestDiskCacheStreamIndex
, 0, iobuffer
.get(),
70 iobuffer
->size(), callback
.callback(), false);
71 EXPECT_EQ(static_cast<int>(data
.size()), callback
.GetResult(rv
));
75 void SetupBasicBlob(BlobStorageHost
* host
, const std::string
& id
) {
76 EXPECT_TRUE(host
->StartBuildingBlob(id
));
78 item
.SetToBytes("1", 1);
79 EXPECT_TRUE(host
->AppendBlobDataItem(id
, item
));
80 EXPECT_TRUE(host
->FinishBuildingBlob(id
, "text/plain"));
81 EXPECT_FALSE(host
->StartBuildingBlob(id
));
86 TEST(BlobStorageContextTest
, IncrementDecrementRef
) {
87 BlobStorageContext context
;
88 BlobStorageHost
host(&context
);
89 base::MessageLoop fake_io_message_loop
;
91 // Build up a basic blob.
92 const std::string
kId("id");
93 SetupBasicBlob(&host
, kId
);
95 // Make sure it's there, finish building implies a ref of one.
96 scoped_ptr
<BlobDataHandle
> blob_data_handle
;
97 blob_data_handle
= context
.GetBlobDataFromUUID(kId
);
98 EXPECT_TRUE(blob_data_handle
);
99 blob_data_handle
.reset();
100 base::RunLoop().RunUntilIdle();
102 // Make sure its still there after inc/dec.
103 EXPECT_TRUE(host
.IncrementBlobRefCount(kId
));
104 EXPECT_TRUE(host
.DecrementBlobRefCount(kId
));
105 blob_data_handle
= context
.GetBlobDataFromUUID(kId
);
106 EXPECT_TRUE(blob_data_handle
);
107 blob_data_handle
.reset();
108 base::RunLoop().RunUntilIdle();
110 // Make sure it goes away in the end.
111 EXPECT_TRUE(host
.DecrementBlobRefCount(kId
));
112 blob_data_handle
= context
.GetBlobDataFromUUID(kId
);
113 EXPECT_FALSE(blob_data_handle
);
114 EXPECT_FALSE(host
.DecrementBlobRefCount(kId
));
115 EXPECT_FALSE(host
.IncrementBlobRefCount(kId
));
118 TEST(BlobStorageContextTest
, CancelBuildingBlob
) {
119 BlobStorageContext context
;
120 BlobStorageHost
host(&context
);
121 base::MessageLoop fake_io_message_loop
;
123 // Build up a basic blob.
124 const std::string
kId("id");
125 EXPECT_TRUE(host
.StartBuildingBlob(kId
));
127 item
.SetToBytes("1", 1);
128 EXPECT_TRUE(host
.AppendBlobDataItem(kId
, item
));
129 EXPECT_TRUE(host
.CancelBuildingBlob(kId
));
130 EXPECT_FALSE(host
.FinishBuildingBlob(kId
, "text/plain"));
131 EXPECT_TRUE(host
.StartBuildingBlob(kId
));
134 TEST(BlobStorageContextTest
, BlobDataHandle
) {
135 BlobStorageContext context
;
136 BlobStorageHost
host(&context
);
137 base::MessageLoop fake_io_message_loop
;
139 // Build up a basic blob.
140 const std::string
kId("id");
141 SetupBasicBlob(&host
, kId
);
143 // Get a handle to it.
144 scoped_ptr
<BlobDataHandle
> blob_data_handle
=
145 context
.GetBlobDataFromUUID(kId
);
146 EXPECT_TRUE(blob_data_handle
);
148 // Drop the host's ref to it.
149 EXPECT_TRUE(host
.DecrementBlobRefCount(kId
));
151 // Should still be there due to the handle.
152 scoped_ptr
<BlobDataHandle
> another_handle
=
153 context
.GetBlobDataFromUUID(kId
);
154 EXPECT_TRUE(another_handle
);
156 // Should disappear after dropping both handles.
157 blob_data_handle
.reset();
158 another_handle
.reset();
159 base::RunLoop().RunUntilIdle();
161 blob_data_handle
= context
.GetBlobDataFromUUID(kId
);
162 EXPECT_FALSE(blob_data_handle
);
165 TEST(BlobStorageContextTest
, MemoryUsage
) {
166 const std::string
kId1("id1");
167 const std::string
kId2("id2");
169 base::MessageLoop fake_io_message_loop
;
171 BlobDataBuilder
builder1(kId1
);
172 BlobDataBuilder
builder2(kId2
);
173 builder1
.AppendData("Data1Data2");
174 builder2
.AppendBlob(kId1
);
175 builder2
.AppendBlob(kId1
);
176 builder2
.AppendBlob(kId1
);
177 builder2
.AppendBlob(kId1
);
178 builder2
.AppendBlob(kId1
);
179 builder2
.AppendBlob(kId1
);
180 builder2
.AppendBlob(kId1
);
182 BlobStorageContext context
;
183 EXPECT_EQ(0lu, context
.memory_usage());
185 scoped_ptr
<BlobDataHandle
> blob_data_handle
=
186 context
.AddFinishedBlob(&builder1
);
187 EXPECT_EQ(10lu, context
.memory_usage());
188 scoped_ptr
<BlobDataHandle
> blob_data_handle2
=
189 context
.AddFinishedBlob(&builder2
);
190 EXPECT_EQ(10lu, context
.memory_usage());
192 blob_data_handle
.reset();
193 base::RunLoop().RunUntilIdle();
195 EXPECT_EQ(10lu, context
.memory_usage());
196 blob_data_handle2
.reset();
197 base::RunLoop().RunUntilIdle();
199 EXPECT_EQ(0lu, context
.memory_usage());
202 TEST(BlobStorageContextTest
, AddFinishedBlob
) {
203 const std::string
kId1("id1");
204 const std::string
kId2("id12");
205 const std::string
kId2Prime("id2.prime");
206 const std::string
kId3("id3");
207 const std::string
kId3Prime("id3.prime");
209 base::MessageLoop fake_io_message_loop
;
211 BlobDataBuilder
builder1(kId1
);
212 BlobDataBuilder
builder2(kId2
);
213 BlobDataBuilder
canonicalized_blob_data2(kId2Prime
);
214 builder1
.AppendData("Data1Data2");
215 builder2
.AppendBlob(kId1
, 5, 5);
216 builder2
.AppendData(" is the best");
217 canonicalized_blob_data2
.AppendData("Data2");
218 canonicalized_blob_data2
.AppendData(" is the best");
220 BlobStorageContext context
;
222 scoped_ptr
<BlobDataHandle
> blob_data_handle
=
223 context
.AddFinishedBlob(&builder1
);
224 scoped_ptr
<BlobDataHandle
> blob_data_handle2
=
225 context
.AddFinishedBlob(&builder2
);
227 ASSERT_TRUE(blob_data_handle
);
228 ASSERT_TRUE(blob_data_handle2
);
229 scoped_ptr
<BlobDataSnapshot
> data1
= blob_data_handle
->CreateSnapshot();
230 scoped_ptr
<BlobDataSnapshot
> data2
= blob_data_handle2
->CreateSnapshot();
231 EXPECT_EQ(*data1
, builder1
);
232 EXPECT_EQ(*data2
, canonicalized_blob_data2
);
233 blob_data_handle
.reset();
236 base::RunLoop().RunUntilIdle();
238 blob_data_handle
= context
.GetBlobDataFromUUID(kId1
);
239 EXPECT_FALSE(blob_data_handle
);
240 EXPECT_TRUE(blob_data_handle2
);
241 data2
= blob_data_handle2
->CreateSnapshot();
242 EXPECT_EQ(*data2
, canonicalized_blob_data2
);
244 // Test shared elements stick around.
245 BlobDataBuilder
builder3(kId3
);
246 builder3
.AppendBlob(kId2
);
247 builder3
.AppendBlob(kId2
);
248 scoped_ptr
<BlobDataHandle
> blob_data_handle3
=
249 context
.AddFinishedBlob(&builder3
);
250 blob_data_handle2
.reset();
251 base::RunLoop().RunUntilIdle();
253 blob_data_handle2
= context
.GetBlobDataFromUUID(kId2
);
254 EXPECT_FALSE(blob_data_handle2
);
255 EXPECT_TRUE(blob_data_handle3
);
256 scoped_ptr
<BlobDataSnapshot
> data3
= blob_data_handle3
->CreateSnapshot();
258 BlobDataBuilder
canonicalized_blob_data3(kId3Prime
);
259 canonicalized_blob_data3
.AppendData("Data2");
260 canonicalized_blob_data3
.AppendData(" is the best");
261 canonicalized_blob_data3
.AppendData("Data2");
262 canonicalized_blob_data3
.AppendData(" is the best");
263 EXPECT_EQ(*data3
, canonicalized_blob_data3
);
265 blob_data_handle
.reset();
266 blob_data_handle2
.reset();
267 blob_data_handle3
.reset();
268 base::RunLoop().RunUntilIdle();
271 TEST(BlobStorageContextTest
, AddFinishedBlob_LargeOffset
) {
272 // A value which does not fit in a 4-byte data type. Used to confirm that
273 // large values are supported on 32-bit Chromium builds. Regression test for:
275 const uint64_t kLargeSize
= std::numeric_limits
<uint64_t>::max();
277 const uint64_t kBlobLength
= 5;
278 const std::string
kId1("id1");
279 const std::string
kId2("id2");
280 base::MessageLoop fake_io_message_loop
;
282 BlobDataBuilder
builder1(kId1
);
283 builder1
.AppendFileSystemFile(GURL(), 0, kLargeSize
, base::Time::Now());
285 BlobDataBuilder
builder2(kId2
);
286 builder2
.AppendBlob(kId1
, kLargeSize
- kBlobLength
, kBlobLength
);
288 BlobStorageContext context
;
289 scoped_ptr
<BlobDataHandle
> blob_data_handle1
=
290 context
.AddFinishedBlob(&builder1
);
291 scoped_ptr
<BlobDataHandle
> blob_data_handle2
=
292 context
.AddFinishedBlob(&builder2
);
294 ASSERT_TRUE(blob_data_handle1
);
295 ASSERT_TRUE(blob_data_handle2
);
296 scoped_ptr
<BlobDataSnapshot
> data
= blob_data_handle2
->CreateSnapshot();
297 ASSERT_EQ(1u, data
->items().size());
298 const scoped_refptr
<BlobDataItem
> item
= data
->items()[0];
299 EXPECT_EQ(kLargeSize
- kBlobLength
, item
->offset());
300 EXPECT_EQ(kBlobLength
, item
->length());
302 blob_data_handle1
.reset();
303 blob_data_handle2
.reset();
304 base::RunLoop().RunUntilIdle();
307 TEST(BlobStorageContextTest
, BuildDiskCacheBlob
) {
308 base::MessageLoop fake_io_message_loop
;
309 scoped_refptr
<BlobDataBuilder::DataHandle
>
310 data_handle
= new EmptyDataHandle();
313 scoped_ptr
<BlobStorageContext
> context(new BlobStorageContext
);
314 BlobStorageHost
host(context
.get());
316 scoped_ptr
<disk_cache::Backend
> cache
= CreateInMemoryDiskCache();
319 const std::string kTestBlobData
= "Test Blob Data";
320 disk_cache::ScopedEntryPtr entry
=
321 CreateDiskCacheEntry(cache
.get(), "test entry", kTestBlobData
);
323 const std::string
kId1Prime("id1.prime");
324 BlobDataBuilder
canonicalized_blob_data(kId1Prime
);
325 canonicalized_blob_data
.AppendData(kTestBlobData
.c_str());
327 const std::string
kId1("id1");
328 BlobDataBuilder
builder(kId1
);
330 builder
.AppendDiskCacheEntry(
331 data_handle
, entry
.get(), kTestDiskCacheStreamIndex
);
333 scoped_ptr
<BlobDataHandle
> blob_data_handle
=
334 context
->AddFinishedBlob(&builder
);
335 scoped_ptr
<BlobDataSnapshot
> data
= blob_data_handle
->CreateSnapshot();
336 EXPECT_EQ(*data
, builder
);
337 EXPECT_FALSE(data_handle
->HasOneRef())
338 << "Data handle was destructed while context and builder still exist.";
340 EXPECT_TRUE(data_handle
->HasOneRef())
341 << "Data handle was not destructed along with blob storage context.";
342 base::RunLoop().RunUntilIdle();
345 TEST(BlobStorageContextTest
, CompoundBlobs
) {
346 const std::string
kId1("id1");
347 const std::string
kId2("id2");
348 const std::string
kId3("id3");
349 const std::string
kId2Prime("id2.prime");
351 base::MessageLoop fake_io_message_loop
;
353 // Setup a set of blob data for testing.
354 base::Time time1
, time2
;
355 base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT", &time1
);
356 base::Time::FromString("Mon, 14 Nov 1994, 11:30:49 GMT", &time2
);
358 BlobDataBuilder
blob_data1(kId1
);
359 blob_data1
.AppendData("Data1");
360 blob_data1
.AppendData("Data2");
361 blob_data1
.AppendFile(base::FilePath(FILE_PATH_LITERAL("File1.txt")), 10,
364 BlobDataBuilder
blob_data2(kId2
);
365 blob_data2
.AppendData("Data3");
366 blob_data2
.AppendBlob(kId1
, 8, 100);
367 blob_data2
.AppendFile(base::FilePath(FILE_PATH_LITERAL("File2.txt")), 0, 20,
370 BlobDataBuilder
blob_data3(kId3
);
371 blob_data3
.AppendData("Data4");
372 scoped_ptr
<disk_cache::Backend
> cache
= CreateInMemoryDiskCache();
374 disk_cache::ScopedEntryPtr disk_cache_entry
=
375 CreateDiskCacheEntry(cache
.get(), "another key", "Data5");
376 blob_data3
.AppendDiskCacheEntry(new EmptyDataHandle(), disk_cache_entry
.get(),
377 kTestDiskCacheStreamIndex
);
379 BlobDataBuilder
canonicalized_blob_data2(kId2Prime
);
380 canonicalized_blob_data2
.AppendData("Data3");
381 canonicalized_blob_data2
.AppendData("a2___", 2);
382 canonicalized_blob_data2
.AppendFile(
383 base::FilePath(FILE_PATH_LITERAL("File1.txt")), 10, 98, time1
);
384 canonicalized_blob_data2
.AppendFile(
385 base::FilePath(FILE_PATH_LITERAL("File2.txt")), 0, 20, time2
);
387 BlobStorageContext context
;
388 scoped_ptr
<BlobDataHandle
> blob_data_handle
;
390 // Test a blob referring to only data and a file.
391 blob_data_handle
= context
.AddFinishedBlob(&blob_data1
);
393 ASSERT_TRUE(blob_data_handle
);
394 scoped_ptr
<BlobDataSnapshot
> data
= blob_data_handle
->CreateSnapshot();
395 ASSERT_TRUE(blob_data_handle
);
396 EXPECT_EQ(*data
, blob_data1
);
398 // Test a blob composed in part with another blob.
399 blob_data_handle
= context
.AddFinishedBlob(&blob_data2
);
400 data
= blob_data_handle
->CreateSnapshot();
401 ASSERT_TRUE(blob_data_handle
);
403 EXPECT_EQ(*data
, canonicalized_blob_data2
);
405 // Test a blob referring to only data and a disk cache entry.
406 blob_data_handle
= context
.AddFinishedBlob(&blob_data3
);
407 data
= blob_data_handle
->CreateSnapshot();
408 ASSERT_TRUE(blob_data_handle
);
409 EXPECT_EQ(*data
, blob_data3
);
411 blob_data_handle
.reset();
412 base::RunLoop().RunUntilIdle();
415 TEST(BlobStorageContextTest
, PublicBlobUrls
) {
416 BlobStorageContext context
;
417 BlobStorageHost
host(&context
);
418 base::MessageLoop fake_io_message_loop
;
420 // Build up a basic blob.
421 const std::string
kId("id");
422 SetupBasicBlob(&host
, kId
);
424 // Now register a url for that blob.
425 GURL
kUrl("blob:id");
426 EXPECT_TRUE(host
.RegisterPublicBlobURL(kUrl
, kId
));
427 scoped_ptr
<BlobDataHandle
> blob_data_handle
=
428 context
.GetBlobDataFromPublicURL(kUrl
);
429 ASSERT_TRUE(blob_data_handle
.get());
430 EXPECT_EQ(kId
, blob_data_handle
->uuid());
431 scoped_ptr
<BlobDataSnapshot
> data
= blob_data_handle
->CreateSnapshot();
432 blob_data_handle
.reset();
433 base::RunLoop().RunUntilIdle();
435 // The url registration should keep the blob alive even after
436 // explicit references are dropped.
437 EXPECT_TRUE(host
.DecrementBlobRefCount(kId
));
438 blob_data_handle
= context
.GetBlobDataFromPublicURL(kUrl
);
439 EXPECT_TRUE(blob_data_handle
);
440 blob_data_handle
.reset();
441 base::RunLoop().RunUntilIdle();
443 // Finally get rid of the url registration and the blob.
444 EXPECT_TRUE(host
.RevokePublicBlobURL(kUrl
));
445 blob_data_handle
= context
.GetBlobDataFromPublicURL(kUrl
);
446 EXPECT_TRUE(!blob_data_handle
.get());
447 EXPECT_FALSE(host
.RevokePublicBlobURL(kUrl
));
450 TEST(BlobStorageContextTest
, HostCleanup
) {
451 BlobStorageContext context
;
452 scoped_ptr
<BlobStorageHost
> host(new BlobStorageHost(&context
));
453 base::MessageLoop fake_io_message_loop
;
455 // Build up a basic blob and register a url
456 const std::string
kId("id");
457 GURL
kUrl("blob:id");
458 SetupBasicBlob(host
.get(), kId
);
459 EXPECT_TRUE(host
->RegisterPublicBlobURL(kUrl
, kId
));
461 // All should disappear upon host deletion.
463 scoped_ptr
<BlobDataHandle
> handle
= context
.GetBlobDataFromPublicURL(kUrl
);
464 EXPECT_TRUE(!handle
.get());
465 handle
= context
.GetBlobDataFromUUID(kId
);
466 EXPECT_TRUE(!handle
.get());
469 TEST(BlobStorageContextTest
, EarlyContextDeletion
) {
470 scoped_ptr
<BlobStorageContext
> context(new BlobStorageContext
);
471 BlobStorageHost
host(context
.get());
472 base::MessageLoop fake_io_message_loop
;
474 // Deleting the context should not induce crashes.
477 const std::string
kId("id");
478 GURL
kUrl("blob:id");
479 EXPECT_FALSE(host
.StartBuildingBlob(kId
));
481 item
.SetToBytes("1", 1);
482 EXPECT_FALSE(host
.AppendBlobDataItem(kId
, item
));
483 EXPECT_FALSE(host
.FinishBuildingBlob(kId
, "text/plain"));
484 EXPECT_FALSE(host
.RegisterPublicBlobURL(kUrl
, kId
));
485 EXPECT_FALSE(host
.IncrementBlobRefCount(kId
));
486 EXPECT_FALSE(host
.DecrementBlobRefCount(kId
));
487 EXPECT_FALSE(host
.RevokePublicBlobURL(kUrl
));
490 // TODO(michaeln): tests for the depcrecated url stuff
492 } // namespace content