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 "storage/browser/blob/blob_data_builder.h"
18 #include "storage/browser/blob/blob_data_handle.h"
19 #include "storage/browser/blob/blob_data_item.h"
20 #include "storage/browser/blob/blob_data_snapshot.h"
21 #include "testing/gtest/include/gtest/gtest.h"
23 using storage::BlobDataBuilder
;
24 using storage::BlobDataHandle
;
25 using storage::BlobDataItem
;
26 using storage::BlobDataSnapshot
;
27 using storage::BlobStorageContext
;
28 using storage::DataElement
;
33 void SetupBasicBlob(BlobStorageHost
* host
, const std::string
& id
) {
34 EXPECT_TRUE(host
->StartBuildingBlob(id
));
36 item
.SetToBytes("1", 1);
37 EXPECT_TRUE(host
->AppendBlobDataItem(id
, item
));
38 EXPECT_TRUE(host
->FinishBuildingBlob(id
, "text/plain"));
39 EXPECT_FALSE(host
->StartBuildingBlob(id
));
44 TEST(BlobStorageContextTest
, IncrementDecrementRef
) {
45 BlobStorageContext context
;
46 BlobStorageHost
host(&context
);
47 base::MessageLoop fake_io_message_loop
;
49 // Build up a basic blob.
50 const std::string
kId("id");
51 SetupBasicBlob(&host
, kId
);
53 // Make sure it's there, finish building implies a ref of one.
54 scoped_ptr
<BlobDataHandle
> blob_data_handle
;
55 blob_data_handle
= context
.GetBlobDataFromUUID(kId
);
56 EXPECT_TRUE(blob_data_handle
);
57 blob_data_handle
.reset();
58 base::RunLoop().RunUntilIdle();
60 // Make sure its still there after inc/dec.
61 EXPECT_TRUE(host
.IncrementBlobRefCount(kId
));
62 EXPECT_TRUE(host
.DecrementBlobRefCount(kId
));
63 blob_data_handle
= context
.GetBlobDataFromUUID(kId
);
64 EXPECT_TRUE(blob_data_handle
);
65 blob_data_handle
.reset();
66 base::RunLoop().RunUntilIdle();
68 // Make sure it goes away in the end.
69 EXPECT_TRUE(host
.DecrementBlobRefCount(kId
));
70 blob_data_handle
= context
.GetBlobDataFromUUID(kId
);
71 EXPECT_FALSE(blob_data_handle
);
72 EXPECT_FALSE(host
.DecrementBlobRefCount(kId
));
73 EXPECT_FALSE(host
.IncrementBlobRefCount(kId
));
76 TEST(BlobStorageContextTest
, CancelBuildingBlob
) {
77 BlobStorageContext context
;
78 BlobStorageHost
host(&context
);
79 base::MessageLoop fake_io_message_loop
;
81 // Build up a basic blob.
82 const std::string
kId("id");
83 EXPECT_TRUE(host
.StartBuildingBlob(kId
));
85 item
.SetToBytes("1", 1);
86 EXPECT_TRUE(host
.AppendBlobDataItem(kId
, item
));
87 EXPECT_TRUE(host
.CancelBuildingBlob(kId
));
88 EXPECT_FALSE(host
.FinishBuildingBlob(kId
, "text/plain"));
89 EXPECT_TRUE(host
.StartBuildingBlob(kId
));
92 TEST(BlobStorageContextTest
, BlobDataHandle
) {
93 BlobStorageContext context
;
94 BlobStorageHost
host(&context
);
95 base::MessageLoop fake_io_message_loop
;
97 // Build up a basic blob.
98 const std::string
kId("id");
99 SetupBasicBlob(&host
, kId
);
101 // Get a handle to it.
102 scoped_ptr
<BlobDataHandle
> blob_data_handle
=
103 context
.GetBlobDataFromUUID(kId
);
104 EXPECT_TRUE(blob_data_handle
);
106 // Drop the host's ref to it.
107 EXPECT_TRUE(host
.DecrementBlobRefCount(kId
));
109 // Should still be there due to the handle.
110 scoped_ptr
<BlobDataHandle
> another_handle
=
111 context
.GetBlobDataFromUUID(kId
);
112 EXPECT_TRUE(another_handle
);
114 // Should disappear after dropping both handles.
115 blob_data_handle
.reset();
116 another_handle
.reset();
117 base::RunLoop().RunUntilIdle();
119 blob_data_handle
= context
.GetBlobDataFromUUID(kId
);
120 EXPECT_FALSE(blob_data_handle
);
123 TEST(BlobStorageContextTest
, MemoryUsage
) {
124 const std::string
kId1("id1");
125 const std::string
kId2("id2");
127 base::MessageLoop fake_io_message_loop
;
129 BlobDataBuilder
builder1(kId1
);
130 BlobDataBuilder
builder2(kId2
);
131 builder1
.AppendData("Data1Data2");
132 builder2
.AppendBlob(kId1
);
133 builder2
.AppendBlob(kId1
);
134 builder2
.AppendBlob(kId1
);
135 builder2
.AppendBlob(kId1
);
136 builder2
.AppendBlob(kId1
);
137 builder2
.AppendBlob(kId1
);
138 builder2
.AppendBlob(kId1
);
140 BlobStorageContext context
;
141 EXPECT_EQ(0lu, context
.memory_usage());
143 scoped_ptr
<BlobDataHandle
> blob_data_handle
=
144 context
.AddFinishedBlob(&builder1
);
145 EXPECT_EQ(10lu, context
.memory_usage());
146 scoped_ptr
<BlobDataHandle
> blob_data_handle2
=
147 context
.AddFinishedBlob(&builder2
);
148 EXPECT_EQ(10lu, context
.memory_usage());
150 blob_data_handle
.reset();
151 base::RunLoop().RunUntilIdle();
153 EXPECT_EQ(10lu, context
.memory_usage());
154 blob_data_handle2
.reset();
155 base::RunLoop().RunUntilIdle();
157 EXPECT_EQ(0lu, context
.memory_usage());
160 TEST(BlobStorageContextTest
, AddFinishedBlob
) {
161 const std::string
kId1("id1");
162 const std::string
kId2("id12");
163 const std::string
kId2Prime("id2.prime");
164 const std::string
kId3("id3");
165 const std::string
kId3Prime("id3.prime");
167 base::MessageLoop fake_io_message_loop
;
169 BlobDataBuilder
builder1(kId1
);
170 BlobDataBuilder
builder2(kId2
);
171 BlobDataBuilder
canonicalized_blob_data2(kId2Prime
);
172 builder1
.AppendData("Data1Data2");
173 builder2
.AppendBlob(kId1
, 5, 5);
174 builder2
.AppendData(" is the best");
175 canonicalized_blob_data2
.AppendData("Data2");
176 canonicalized_blob_data2
.AppendData(" is the best");
178 BlobStorageContext context
;
180 scoped_ptr
<BlobDataHandle
> blob_data_handle
=
181 context
.AddFinishedBlob(&builder1
);
182 scoped_ptr
<BlobDataHandle
> blob_data_handle2
=
183 context
.AddFinishedBlob(&builder2
);
185 ASSERT_TRUE(blob_data_handle
);
186 ASSERT_TRUE(blob_data_handle2
);
187 scoped_ptr
<BlobDataSnapshot
> data1
= blob_data_handle
->CreateSnapshot();
188 scoped_ptr
<BlobDataSnapshot
> data2
= blob_data_handle2
->CreateSnapshot();
189 EXPECT_EQ(*data1
, builder1
);
190 EXPECT_EQ(*data2
, canonicalized_blob_data2
);
191 blob_data_handle
.reset();
194 base::RunLoop().RunUntilIdle();
196 blob_data_handle
= context
.GetBlobDataFromUUID(kId1
);
197 EXPECT_FALSE(blob_data_handle
);
198 EXPECT_TRUE(blob_data_handle2
);
199 data2
= blob_data_handle2
->CreateSnapshot();
200 EXPECT_EQ(*data2
, canonicalized_blob_data2
);
202 // Test shared elements stick around.
203 BlobDataBuilder
builder3(kId3
);
204 builder3
.AppendBlob(kId2
);
205 builder3
.AppendBlob(kId2
);
206 scoped_ptr
<BlobDataHandle
> blob_data_handle3
=
207 context
.AddFinishedBlob(&builder3
);
208 blob_data_handle2
.reset();
209 base::RunLoop().RunUntilIdle();
211 blob_data_handle2
= context
.GetBlobDataFromUUID(kId2
);
212 EXPECT_FALSE(blob_data_handle2
);
213 EXPECT_TRUE(blob_data_handle3
);
214 scoped_ptr
<BlobDataSnapshot
> data3
= blob_data_handle3
->CreateSnapshot();
216 BlobDataBuilder
canonicalized_blob_data3(kId3Prime
);
217 canonicalized_blob_data3
.AppendData("Data2");
218 canonicalized_blob_data3
.AppendData(" is the best");
219 canonicalized_blob_data3
.AppendData("Data2");
220 canonicalized_blob_data3
.AppendData(" is the best");
221 EXPECT_EQ(*data3
, canonicalized_blob_data3
);
223 blob_data_handle
.reset();
224 blob_data_handle2
.reset();
225 blob_data_handle3
.reset();
226 base::RunLoop().RunUntilIdle();
229 TEST(BlobStorageContextTest
, AddFinishedBlob_LargeOffset
) {
230 // A value which does not fit in a 4-byte data type. Used to confirm that
231 // large values are supported on 32-bit Chromium builds. Regression test for:
233 const uint64_t kLargeSize
= std::numeric_limits
<uint64_t>::max();
235 const uint64_t kBlobLength
= 5;
236 const std::string
kId1("id1");
237 const std::string
kId2("id2");
238 base::MessageLoop fake_io_message_loop
;
240 BlobDataBuilder
builder1(kId1
);
241 builder1
.AppendFileSystemFile(GURL(), 0, kLargeSize
, base::Time::Now());
243 BlobDataBuilder
builder2(kId2
);
244 builder2
.AppendBlob(kId1
, kLargeSize
- kBlobLength
, kBlobLength
);
246 BlobStorageContext context
;
247 scoped_ptr
<BlobDataHandle
> blob_data_handle1
=
248 context
.AddFinishedBlob(&builder1
);
249 scoped_ptr
<BlobDataHandle
> blob_data_handle2
=
250 context
.AddFinishedBlob(&builder2
);
252 ASSERT_TRUE(blob_data_handle1
);
253 ASSERT_TRUE(blob_data_handle2
);
254 scoped_ptr
<BlobDataSnapshot
> data
= blob_data_handle2
->CreateSnapshot();
255 ASSERT_EQ(1u, data
->items().size());
256 const scoped_refptr
<BlobDataItem
> item
= data
->items()[0];
257 EXPECT_EQ(kLargeSize
- kBlobLength
, item
->offset());
258 EXPECT_EQ(kBlobLength
, item
->length());
260 blob_data_handle1
.reset();
261 blob_data_handle2
.reset();
262 base::RunLoop().RunUntilIdle();
265 TEST(BlobStorageContextTest
, CompoundBlobs
) {
266 const std::string
kId1("id1");
267 const std::string
kId2("id2");
268 const std::string
kId2Prime("id2.prime");
270 base::MessageLoop fake_io_message_loop
;
272 // Setup a set of blob data for testing.
273 base::Time time1
, time2
;
274 base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT", &time1
);
275 base::Time::FromString("Mon, 14 Nov 1994, 11:30:49 GMT", &time2
);
277 BlobDataBuilder
blob_data1(kId1
);
278 blob_data1
.AppendData("Data1");
279 blob_data1
.AppendData("Data2");
280 blob_data1
.AppendFile(base::FilePath(FILE_PATH_LITERAL("File1.txt")), 10,
283 BlobDataBuilder
blob_data2(kId2
);
284 blob_data2
.AppendData("Data3");
285 blob_data2
.AppendBlob(kId1
, 8, 100);
286 blob_data2
.AppendFile(base::FilePath(FILE_PATH_LITERAL("File2.txt")), 0, 20,
289 BlobDataBuilder
canonicalized_blob_data2(kId2Prime
);
290 canonicalized_blob_data2
.AppendData("Data3");
291 canonicalized_blob_data2
.AppendData("a2___", 2);
292 canonicalized_blob_data2
.AppendFile(
293 base::FilePath(FILE_PATH_LITERAL("File1.txt")), 10, 98, time1
);
294 canonicalized_blob_data2
.AppendFile(
295 base::FilePath(FILE_PATH_LITERAL("File2.txt")), 0, 20, time2
);
297 BlobStorageContext context
;
298 scoped_ptr
<BlobDataHandle
> blob_data_handle
;
300 // Test a blob referring to only data and a file.
301 blob_data_handle
= context
.AddFinishedBlob(&blob_data1
);
303 ASSERT_TRUE(blob_data_handle
);
304 scoped_ptr
<BlobDataSnapshot
> data
= blob_data_handle
->CreateSnapshot();
305 ASSERT_TRUE(blob_data_handle
);
306 EXPECT_EQ(*data
, blob_data1
);
308 // Test a blob composed in part with another blob.
309 blob_data_handle
= context
.AddFinishedBlob(&blob_data2
);
310 data
= blob_data_handle
->CreateSnapshot();
311 ASSERT_TRUE(blob_data_handle
);
313 EXPECT_EQ(*data
, canonicalized_blob_data2
);
315 blob_data_handle
.reset();
316 base::RunLoop().RunUntilIdle();
319 TEST(BlobStorageContextTest
, PublicBlobUrls
) {
320 BlobStorageContext context
;
321 BlobStorageHost
host(&context
);
322 base::MessageLoop fake_io_message_loop
;
324 // Build up a basic blob.
325 const std::string
kId("id");
326 SetupBasicBlob(&host
, kId
);
328 // Now register a url for that blob.
329 GURL
kUrl("blob:id");
330 EXPECT_TRUE(host
.RegisterPublicBlobURL(kUrl
, kId
));
331 scoped_ptr
<BlobDataHandle
> blob_data_handle
=
332 context
.GetBlobDataFromPublicURL(kUrl
);
333 ASSERT_TRUE(blob_data_handle
.get());
334 EXPECT_EQ(kId
, blob_data_handle
->uuid());
335 scoped_ptr
<BlobDataSnapshot
> data
= blob_data_handle
->CreateSnapshot();
336 blob_data_handle
.reset();
337 base::RunLoop().RunUntilIdle();
339 // The url registration should keep the blob alive even after
340 // explicit references are dropped.
341 EXPECT_TRUE(host
.DecrementBlobRefCount(kId
));
342 blob_data_handle
= context
.GetBlobDataFromPublicURL(kUrl
);
343 EXPECT_TRUE(blob_data_handle
);
344 blob_data_handle
.reset();
345 base::RunLoop().RunUntilIdle();
347 // Finally get rid of the url registration and the blob.
348 EXPECT_TRUE(host
.RevokePublicBlobURL(kUrl
));
349 blob_data_handle
= context
.GetBlobDataFromPublicURL(kUrl
);
350 EXPECT_TRUE(!blob_data_handle
.get());
351 EXPECT_FALSE(host
.RevokePublicBlobURL(kUrl
));
354 TEST(BlobStorageContextTest
, HostCleanup
) {
355 BlobStorageContext context
;
356 scoped_ptr
<BlobStorageHost
> host(new BlobStorageHost(&context
));
357 base::MessageLoop fake_io_message_loop
;
359 // Build up a basic blob and register a url
360 const std::string
kId("id");
361 GURL
kUrl("blob:id");
362 SetupBasicBlob(host
.get(), kId
);
363 EXPECT_TRUE(host
->RegisterPublicBlobURL(kUrl
, kId
));
365 // All should disappear upon host deletion.
367 scoped_ptr
<BlobDataHandle
> handle
= context
.GetBlobDataFromPublicURL(kUrl
);
368 EXPECT_TRUE(!handle
.get());
369 handle
= context
.GetBlobDataFromUUID(kId
);
370 EXPECT_TRUE(!handle
.get());
373 TEST(BlobStorageContextTest
, EarlyContextDeletion
) {
374 scoped_ptr
<BlobStorageContext
> context(new BlobStorageContext
);
375 BlobStorageHost
host(context
.get());
376 base::MessageLoop fake_io_message_loop
;
378 // Deleting the context should not induce crashes.
381 const std::string
kId("id");
382 GURL
kUrl("blob:id");
383 EXPECT_FALSE(host
.StartBuildingBlob(kId
));
385 item
.SetToBytes("1", 1);
386 EXPECT_FALSE(host
.AppendBlobDataItem(kId
, item
));
387 EXPECT_FALSE(host
.FinishBuildingBlob(kId
, "text/plain"));
388 EXPECT_FALSE(host
.RegisterPublicBlobURL(kUrl
, kId
));
389 EXPECT_FALSE(host
.IncrementBlobRefCount(kId
));
390 EXPECT_FALSE(host
.DecrementBlobRefCount(kId
));
391 EXPECT_FALSE(host
.RevokePublicBlobURL(kUrl
));
394 // TODO(michaeln): tests for the depcrecated url stuff
396 } // namespace content