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/files/file.h" | |
14 #include "base/files/file_path.h" | |
15 #include "base/memory/ref_counted.h" | |
16 #include "base/message_loop/message_loop.h" | |
17 #include "base/run_loop.h" | |
18 #include "base/time/time.h" | |
19 #include "content/browser/blob_storage/blob_dispatcher_host.h" | |
20 #include "content/browser/fileapi/chrome_blob_storage_context.h" | |
21 #include "content/public/test/test_browser_context.h" | |
22 #include "net/base/io_buffer.h" | |
23 #include "net/base/test_completion_callback.h" | |
24 #include "net/disk_cache/disk_cache.h" | |
25 #include "storage/browser/blob/blob_async_builder_host.h" | |
26 #include "storage/browser/blob/blob_data_builder.h" | |
27 #include "storage/browser/blob/blob_data_handle.h" | |
28 #include "storage/browser/blob/blob_data_item.h" | |
29 #include "storage/browser/blob/blob_data_snapshot.h" | |
30 #include "storage/browser/blob/blob_transport_result.h" | |
31 #include "storage/common/blob_storage/blob_item_bytes_request.h" | |
32 #include "storage/common/blob_storage/blob_item_bytes_response.h" | |
33 #include "testing/gtest/include/gtest/gtest.h" | |
34 | |
35 using RequestMemoryCallback = | |
36 storage::BlobAsyncBuilderHost::RequestMemoryCallback; | |
37 | |
38 namespace storage { | |
39 namespace { | |
40 | |
41 const char kContentType[] = "text/plain"; | |
42 const char kContentDisposition[] = "content_disposition"; | |
43 const int kTestDiskCacheStreamIndex = 0; | |
44 | |
45 // Our disk cache tests don't need a real data handle since the tests themselves | |
46 // scope the disk cache and entries. | |
47 class EmptyDataHandle : public storage::BlobDataBuilder::DataHandle { | |
48 private: | |
49 ~EmptyDataHandle() override {} | |
50 }; | |
51 | |
52 std::unique_ptr<disk_cache::Backend> CreateInMemoryDiskCache() { | |
53 std::unique_ptr<disk_cache::Backend> cache; | |
54 net::TestCompletionCallback callback; | |
55 int rv = disk_cache::CreateCacheBackend(net::MEMORY_CACHE, | |
56 net::CACHE_BACKEND_DEFAULT, | |
57 base::FilePath(), 0, | |
58 false, nullptr, nullptr, &cache, | |
59 callback.callback()); | |
60 EXPECT_EQ(net::OK, callback.GetResult(rv)); | |
61 | |
62 return cache; | |
63 } | |
64 | |
65 disk_cache::ScopedEntryPtr CreateDiskCacheEntry(disk_cache::Backend* cache, | |
66 const char* key, | |
67 const std::string& data) { | |
68 disk_cache::Entry* temp_entry = nullptr; | |
69 net::TestCompletionCallback callback; | |
70 int rv = cache->CreateEntry(key, &temp_entry, callback.callback()); | |
71 if (callback.GetResult(rv) != net::OK) | |
72 return nullptr; | |
73 disk_cache::ScopedEntryPtr entry(temp_entry); | |
74 | |
75 scoped_refptr<net::StringIOBuffer> iobuffer = new net::StringIOBuffer(data); | |
76 rv = entry->WriteData(kTestDiskCacheStreamIndex, 0, iobuffer.get(), | |
77 iobuffer->size(), callback.callback(), false); | |
78 EXPECT_EQ(static_cast<int>(data.size()), callback.GetResult(rv)); | |
79 return entry; | |
80 } | |
81 | |
82 | |
83 } // namespace | |
84 | |
85 class BlobStorageContextTest : public testing::Test { | |
86 protected: | |
87 BlobStorageContextTest() {} | |
88 ~BlobStorageContextTest() override {} | |
89 | |
90 std::unique_ptr<BlobDataHandle> SetupBasicBlob(const std::string& id) { | |
91 BlobDataBuilder builder(id); | |
92 builder.AppendData("1", 1); | |
93 builder.set_content_type("text/plain"); | |
94 return context_.AddFinishedBlob(builder); | |
95 } | |
96 | |
97 BlobStorageContext context_; | |
98 }; | |
99 | |
100 TEST_F(BlobStorageContextTest, IncrementDecrementRef) { | |
101 base::MessageLoop fake_io_message_loop; | |
102 | |
103 // Build up a basic blob. | |
104 const std::string kId("id"); | |
105 std::unique_ptr<BlobDataHandle> blob_data_handle = SetupBasicBlob(kId); | |
106 | |
107 // Do an extra increment to keep it around after we kill the handle. | |
108 context_.IncrementBlobRefCount(kId); | |
109 context_.IncrementBlobRefCount(kId); | |
110 context_.DecrementBlobRefCount(kId); | |
111 blob_data_handle = context_.GetBlobDataFromUUID(kId); | |
112 EXPECT_TRUE(blob_data_handle); | |
113 blob_data_handle.reset(); | |
114 base::RunLoop().RunUntilIdle(); | |
115 | |
116 EXPECT_TRUE(context_.registry().HasEntry(kId)); | |
117 context_.DecrementBlobRefCount(kId); | |
118 EXPECT_FALSE(context_.registry().HasEntry(kId)); | |
119 | |
120 // Make sure it goes away in the end. | |
121 blob_data_handle = context_.GetBlobDataFromUUID(kId); | |
122 EXPECT_FALSE(blob_data_handle); | |
123 } | |
124 | |
125 TEST_F(BlobStorageContextTest, OnCancelBuildingBlob) { | |
126 base::MessageLoop fake_io_message_loop; | |
127 | |
128 // Build up a basic blob. | |
129 const std::string kId("id"); | |
130 context_.CreatePendingBlob(kId, std::string(kContentType), | |
131 std::string(kContentDisposition)); | |
132 EXPECT_TRUE(context_.IsBeingBuilt(kId)); | |
133 context_.CancelPendingBlob(kId, IPCBlobCreationCancelCode::OUT_OF_MEMORY); | |
134 EXPECT_TRUE(context_.registry().HasEntry(kId)); | |
135 EXPECT_FALSE(context_.IsBeingBuilt(kId)); | |
136 EXPECT_TRUE(context_.IsBroken(kId)); | |
137 } | |
138 | |
139 TEST_F(BlobStorageContextTest, BlobDataHandle) { | |
140 base::MessageLoop fake_io_message_loop; | |
141 | |
142 // Build up a basic blob. | |
143 const std::string kId("id"); | |
144 std::unique_ptr<BlobDataHandle> blob_data_handle = SetupBasicBlob(kId); | |
145 EXPECT_TRUE(blob_data_handle); | |
146 | |
147 // Get another handle | |
148 std::unique_ptr<BlobDataHandle> another_handle = | |
149 context_.GetBlobDataFromUUID(kId); | |
150 EXPECT_TRUE(another_handle); | |
151 | |
152 // Should disappear after dropping both handles. | |
153 blob_data_handle.reset(); | |
154 base::RunLoop().RunUntilIdle(); | |
155 | |
156 EXPECT_TRUE(context_.registry().HasEntry(kId)); | |
157 | |
158 another_handle.reset(); | |
159 base::RunLoop().RunUntilIdle(); | |
160 | |
161 blob_data_handle = context_.GetBlobDataFromUUID(kId); | |
162 EXPECT_FALSE(blob_data_handle); | |
163 } | |
164 | |
165 TEST_F(BlobStorageContextTest, MemoryUsage) { | |
166 const std::string kId1("id1"); | |
167 const std::string kId2("id2"); | |
168 | |
169 base::MessageLoop fake_io_message_loop; | |
170 | |
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); | |
181 | |
182 EXPECT_EQ(0lu, context_.memory_usage()); | |
183 | |
184 std::unique_ptr<BlobDataHandle> blob_data_handle = | |
185 context_.AddFinishedBlob(&builder1); | |
186 EXPECT_EQ(10lu, context_.memory_usage()); | |
187 std::unique_ptr<BlobDataHandle> blob_data_handle2 = | |
188 context_.AddFinishedBlob(&builder2); | |
189 EXPECT_EQ(10lu, context_.memory_usage()); | |
190 | |
191 EXPECT_EQ(2u, context_.registry().blob_count()); | |
192 | |
193 blob_data_handle.reset(); | |
194 base::RunLoop().RunUntilIdle(); | |
195 | |
196 EXPECT_EQ(10lu, context_.memory_usage()); | |
197 EXPECT_EQ(1u, context_.registry().blob_count()); | |
198 blob_data_handle2.reset(); | |
199 base::RunLoop().RunUntilIdle(); | |
200 | |
201 EXPECT_EQ(0lu, context_.memory_usage()); | |
202 EXPECT_EQ(0u, context_.registry().blob_count()); | |
203 } | |
204 | |
205 TEST_F(BlobStorageContextTest, AddFinishedBlob) { | |
206 const std::string kId1("id1"); | |
207 const std::string kId2("id12"); | |
208 const std::string kId2Prime("id2.prime"); | |
209 const std::string kId3("id3"); | |
210 const std::string kId3Prime("id3.prime"); | |
211 | |
212 base::MessageLoop fake_io_message_loop; | |
213 | |
214 BlobDataBuilder builder1(kId1); | |
215 BlobDataBuilder builder2(kId2); | |
216 BlobDataBuilder canonicalized_blob_data2(kId2Prime); | |
217 builder1.AppendData("Data1Data2"); | |
218 builder2.AppendBlob(kId1, 5, 5); | |
219 builder2.AppendData(" is the best"); | |
220 canonicalized_blob_data2.AppendData("Data2"); | |
221 canonicalized_blob_data2.AppendData(" is the best"); | |
222 | |
223 BlobStorageContext context; | |
224 | |
225 std::unique_ptr<BlobDataHandle> blob_data_handle = | |
226 context_.AddFinishedBlob(&builder1); | |
227 std::unique_ptr<BlobDataHandle> blob_data_handle2 = | |
228 context_.AddFinishedBlob(&builder2); | |
229 | |
230 ASSERT_TRUE(blob_data_handle); | |
231 ASSERT_TRUE(blob_data_handle2); | |
232 std::unique_ptr<BlobDataSnapshot> data1 = blob_data_handle->CreateSnapshot(); | |
233 std::unique_ptr<BlobDataSnapshot> data2 = blob_data_handle2->CreateSnapshot(); | |
234 EXPECT_EQ(*data1, builder1); | |
235 EXPECT_EQ(*data2, canonicalized_blob_data2); | |
236 blob_data_handle.reset(); | |
237 data2.reset(); | |
238 | |
239 base::RunLoop().RunUntilIdle(); | |
240 | |
241 blob_data_handle = context_.GetBlobDataFromUUID(kId1); | |
242 EXPECT_FALSE(blob_data_handle); | |
243 EXPECT_TRUE(blob_data_handle2); | |
244 data2 = blob_data_handle2->CreateSnapshot(); | |
245 EXPECT_EQ(*data2, canonicalized_blob_data2); | |
246 | |
247 // Test shared elements stick around. | |
248 BlobDataBuilder builder3(kId3); | |
249 builder3.AppendBlob(kId2); | |
250 builder3.AppendBlob(kId2); | |
251 std::unique_ptr<BlobDataHandle> blob_data_handle3 = | |
252 context_.AddFinishedBlob(&builder3); | |
253 blob_data_handle2.reset(); | |
254 base::RunLoop().RunUntilIdle(); | |
255 | |
256 blob_data_handle2 = context_.GetBlobDataFromUUID(kId2); | |
257 EXPECT_FALSE(blob_data_handle2); | |
258 EXPECT_TRUE(blob_data_handle3); | |
259 std::unique_ptr<BlobDataSnapshot> data3 = blob_data_handle3->CreateSnapshot(); | |
260 | |
261 BlobDataBuilder canonicalized_blob_data3(kId3Prime); | |
262 canonicalized_blob_data3.AppendData("Data2"); | |
263 canonicalized_blob_data3.AppendData(" is the best"); | |
264 canonicalized_blob_data3.AppendData("Data2"); | |
265 canonicalized_blob_data3.AppendData(" is the best"); | |
266 EXPECT_EQ(*data3, canonicalized_blob_data3); | |
267 | |
268 blob_data_handle.reset(); | |
269 blob_data_handle2.reset(); | |
270 blob_data_handle3.reset(); | |
271 base::RunLoop().RunUntilIdle(); | |
272 } | |
273 | |
274 TEST_F(BlobStorageContextTest, AddFinishedBlob_LargeOffset) { | |
275 // A value which does not fit in a 4-byte data type. Used to confirm that | |
276 // large values are supported on 32-bit Chromium builds. Regression test for: | |
277 // crbug.com/458122. | |
278 const uint64_t kLargeSize = std::numeric_limits<uint64_t>::max(); | |
279 | |
280 const uint64_t kBlobLength = 5; | |
281 const std::string kId1("id1"); | |
282 const std::string kId2("id2"); | |
283 base::MessageLoop fake_io_message_loop; | |
284 | |
285 BlobDataBuilder builder1(kId1); | |
286 builder1.AppendFileSystemFile(GURL(), 0, kLargeSize, base::Time::Now()); | |
287 | |
288 BlobDataBuilder builder2(kId2); | |
289 builder2.AppendBlob(kId1, kLargeSize - kBlobLength, kBlobLength); | |
290 | |
291 std::unique_ptr<BlobDataHandle> blob_data_handle1 = | |
292 context_.AddFinishedBlob(&builder1); | |
293 std::unique_ptr<BlobDataHandle> blob_data_handle2 = | |
294 context_.AddFinishedBlob(&builder2); | |
295 | |
296 ASSERT_TRUE(blob_data_handle1); | |
297 ASSERT_TRUE(blob_data_handle2); | |
298 std::unique_ptr<BlobDataSnapshot> data = blob_data_handle2->CreateSnapshot(); | |
299 ASSERT_EQ(1u, data->items().size()); | |
300 const scoped_refptr<BlobDataItem> item = data->items()[0]; | |
301 EXPECT_EQ(kLargeSize - kBlobLength, item->offset()); | |
302 EXPECT_EQ(kBlobLength, item->length()); | |
303 | |
304 blob_data_handle1.reset(); | |
305 blob_data_handle2.reset(); | |
306 base::RunLoop().RunUntilIdle(); | |
307 } | |
308 | |
309 TEST_F(BlobStorageContextTest, BuildDiskCacheBlob) { | |
310 base::MessageLoop fake_io_message_loop; | |
311 scoped_refptr<BlobDataBuilder::DataHandle> | |
312 data_handle = new EmptyDataHandle(); | |
313 | |
314 { | |
315 BlobStorageContext context; | |
316 | |
317 std::unique_ptr<disk_cache::Backend> cache = CreateInMemoryDiskCache(); | |
318 ASSERT_TRUE(cache); | |
319 | |
320 const std::string kTestBlobData = "Test Blob Data"; | |
321 disk_cache::ScopedEntryPtr entry = | |
322 CreateDiskCacheEntry(cache.get(), "test entry", kTestBlobData); | |
323 | |
324 const std::string kId1Prime("id1.prime"); | |
325 BlobDataBuilder canonicalized_blob_data(kId1Prime); | |
326 canonicalized_blob_data.AppendData(kTestBlobData.c_str()); | |
327 | |
328 const std::string kId1("id1"); | |
329 BlobDataBuilder builder(kId1); | |
330 | |
331 builder.AppendDiskCacheEntry( | |
332 data_handle, entry.get(), kTestDiskCacheStreamIndex); | |
333 | |
334 std::unique_ptr<BlobDataHandle> blob_data_handle = | |
335 context.AddFinishedBlob(&builder); | |
336 std::unique_ptr<BlobDataSnapshot> data = blob_data_handle->CreateSnapshot(); | |
337 EXPECT_EQ(*data, builder); | |
338 EXPECT_FALSE(data_handle->HasOneRef()) | |
339 << "Data handle was destructed while context and builder still exist."; | |
340 } | |
341 EXPECT_TRUE(data_handle->HasOneRef()) | |
342 << "Data handle was not destructed along with blob storage context."; | |
343 base::RunLoop().RunUntilIdle(); | |
344 } | |
345 | |
346 TEST_F(BlobStorageContextTest, CompoundBlobs) { | |
347 const std::string kId1("id1"); | |
348 const std::string kId2("id2"); | |
349 const std::string kId3("id3"); | |
350 const std::string kId2Prime("id2.prime"); | |
351 | |
352 base::MessageLoop fake_io_message_loop; | |
353 | |
354 // Setup a set of blob data for testing. | |
355 base::Time time1, time2; | |
356 base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT", &time1); | |
357 base::Time::FromString("Mon, 14 Nov 1994, 11:30:49 GMT", &time2); | |
358 | |
359 BlobDataBuilder blob_data1(kId1); | |
360 blob_data1.AppendData("Data1"); | |
361 blob_data1.AppendData("Data2"); | |
362 blob_data1.AppendFile(base::FilePath(FILE_PATH_LITERAL("File1.txt")), 10, | |
363 1024, time1); | |
364 | |
365 BlobDataBuilder blob_data2(kId2); | |
366 blob_data2.AppendData("Data3"); | |
367 blob_data2.AppendBlob(kId1, 8, 100); | |
368 blob_data2.AppendFile(base::FilePath(FILE_PATH_LITERAL("File2.txt")), 0, 20, | |
369 time2); | |
370 | |
371 BlobDataBuilder blob_data3(kId3); | |
372 blob_data3.AppendData("Data4"); | |
373 std::unique_ptr<disk_cache::Backend> cache = CreateInMemoryDiskCache(); | |
374 ASSERT_TRUE(cache); | |
375 disk_cache::ScopedEntryPtr disk_cache_entry = | |
376 CreateDiskCacheEntry(cache.get(), "another key", "Data5"); | |
377 blob_data3.AppendDiskCacheEntry(new EmptyDataHandle(), disk_cache_entry.get(), | |
378 kTestDiskCacheStreamIndex); | |
379 | |
380 BlobDataBuilder canonicalized_blob_data2(kId2Prime); | |
381 canonicalized_blob_data2.AppendData("Data3"); | |
382 canonicalized_blob_data2.AppendData("a2___", 2); | |
383 canonicalized_blob_data2.AppendFile( | |
384 base::FilePath(FILE_PATH_LITERAL("File1.txt")), 10, 98, time1); | |
385 canonicalized_blob_data2.AppendFile( | |
386 base::FilePath(FILE_PATH_LITERAL("File2.txt")), 0, 20, time2); | |
387 | |
388 BlobStorageContext context; | |
389 std::unique_ptr<BlobDataHandle> blob_data_handle; | |
390 | |
391 // Test a blob referring to only data and a file. | |
392 blob_data_handle = context_.AddFinishedBlob(&blob_data1); | |
393 | |
394 ASSERT_TRUE(blob_data_handle); | |
395 std::unique_ptr<BlobDataSnapshot> data = blob_data_handle->CreateSnapshot(); | |
396 ASSERT_TRUE(blob_data_handle); | |
397 EXPECT_EQ(*data, blob_data1); | |
398 | |
399 // Test a blob composed in part with another blob. | |
400 blob_data_handle = context_.AddFinishedBlob(&blob_data2); | |
401 data = blob_data_handle->CreateSnapshot(); | |
402 ASSERT_TRUE(blob_data_handle); | |
403 ASSERT_TRUE(data); | |
404 EXPECT_EQ(*data, canonicalized_blob_data2); | |
405 | |
406 // Test a blob referring to only data and a disk cache entry. | |
407 blob_data_handle = context_.AddFinishedBlob(&blob_data3); | |
408 data = blob_data_handle->CreateSnapshot(); | |
409 ASSERT_TRUE(blob_data_handle); | |
410 EXPECT_EQ(*data, blob_data3); | |
411 | |
412 blob_data_handle.reset(); | |
413 base::RunLoop().RunUntilIdle(); | |
414 } | |
415 | |
416 TEST_F(BlobStorageContextTest, PublicBlobUrls) { | |
417 base::MessageLoop fake_io_message_loop; | |
418 | |
419 // Build up a basic blob. | |
420 const std::string kId("id"); | |
421 std::unique_ptr<BlobDataHandle> first_handle = SetupBasicBlob(kId); | |
422 | |
423 // Now register a url for that blob. | |
424 GURL kUrl("blob:id"); | |
425 context_.RegisterPublicBlobURL(kUrl, kId); | |
426 std::unique_ptr<BlobDataHandle> blob_data_handle = | |
427 context_.GetBlobDataFromPublicURL(kUrl); | |
428 ASSERT_TRUE(blob_data_handle.get()); | |
429 EXPECT_EQ(kId, blob_data_handle->uuid()); | |
430 std::unique_ptr<BlobDataSnapshot> data = blob_data_handle->CreateSnapshot(); | |
431 blob_data_handle.reset(); | |
432 first_handle.reset(); | |
433 base::RunLoop().RunUntilIdle(); | |
434 | |
435 // The url registration should keep the blob alive even after | |
436 // explicit references are dropped. | |
437 blob_data_handle = context_.GetBlobDataFromPublicURL(kUrl); | |
438 EXPECT_TRUE(blob_data_handle); | |
439 blob_data_handle.reset(); | |
440 base::RunLoop().RunUntilIdle(); | |
441 | |
442 // Finally get rid of the url registration and the blob. | |
443 context_.RevokePublicBlobURL(kUrl); | |
444 blob_data_handle = context_.GetBlobDataFromPublicURL(kUrl); | |
445 EXPECT_FALSE(blob_data_handle.get()); | |
446 EXPECT_FALSE(context_.registry().HasEntry(kId)); | |
447 } | |
448 | |
449 TEST_F(BlobStorageContextTest, TestUnknownBrokenAndBuildingBlobReference) { | |
450 base::MessageLoop fake_io_message_loop; | |
451 const std::string kBrokenId("broken_id"); | |
452 const std::string kBuildingId("building_id"); | |
453 const std::string kReferencingId("referencing_id"); | |
454 const std::string kUnknownId("unknown_id"); | |
455 | |
456 // Create a broken blob and a building blob. | |
457 context_.CreatePendingBlob(kBuildingId, "", ""); | |
458 context_.CreatePendingBlob(kBrokenId, "", ""); | |
459 context_.CancelPendingBlob(kBrokenId, IPCBlobCreationCancelCode::UNKNOWN); | |
460 EXPECT_TRUE(context_.IsBroken(kBrokenId)); | |
461 EXPECT_TRUE(context_.registry().HasEntry(kBrokenId)); | |
462 | |
463 // Try to create a blob with a reference to an unknown blob. | |
464 BlobDataBuilder builder(kReferencingId); | |
465 builder.AppendData("data"); | |
466 builder.AppendBlob(kUnknownId); | |
467 std::unique_ptr<BlobDataHandle> handle = context_.AddFinishedBlob(builder); | |
468 EXPECT_TRUE(handle->IsBroken()); | |
469 EXPECT_TRUE(context_.registry().HasEntry(kReferencingId)); | |
470 handle.reset(); | |
471 base::RunLoop().RunUntilIdle(); | |
472 EXPECT_FALSE(context_.registry().HasEntry(kReferencingId)); | |
473 | |
474 // Try to create a blob with a reference to the broken blob. | |
475 BlobDataBuilder builder2(kReferencingId); | |
476 builder2.AppendData("data"); | |
477 builder2.AppendBlob(kBrokenId); | |
478 handle = context_.AddFinishedBlob(builder2); | |
479 EXPECT_TRUE(handle->IsBroken()); | |
480 EXPECT_TRUE(context_.registry().HasEntry(kReferencingId)); | |
481 handle.reset(); | |
482 base::RunLoop().RunUntilIdle(); | |
483 EXPECT_FALSE(context_.registry().HasEntry(kReferencingId)); | |
484 | |
485 // Try to create a blob with a reference to the building blob. | |
486 BlobDataBuilder builder3(kReferencingId); | |
487 builder3.AppendData("data"); | |
488 builder3.AppendBlob(kBuildingId); | |
489 handle = context_.AddFinishedBlob(builder3); | |
490 EXPECT_TRUE(handle->IsBroken()); | |
491 EXPECT_TRUE(context_.registry().HasEntry(kReferencingId)); | |
492 handle.reset(); | |
493 base::RunLoop().RunUntilIdle(); | |
494 EXPECT_FALSE(context_.registry().HasEntry(kReferencingId)); | |
495 } | |
496 | |
497 // TODO(michaeln): tests for the depcrecated url stuff | |
498 | |
499 } // namespace content | |
OLD | NEW |