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 "base/files/file_path.h"
6#include "base/memory/ref_counted.h"
7#include "base/memory/scoped_ptr.h"
8#include "base/message_loop/message_loop.h"
9#include "base/run_loop.h"
10#include "base/time/time.h"
11#include "content/browser/fileapi/blob_storage_host.h"
12#include "storage/browser/blob/blob_data_handle.h"
13#include "storage/browser/blob/blob_storage_context.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16using storage::BlobDataHandle;
17
18namespace content {
19
20namespace {
21void SetupBasicBlob(BlobStorageHost* host, const std::string& id) {
22  EXPECT_TRUE(host->StartBuildingBlob(id));
23  BlobData::Item item;
24  item.SetToBytes("1", 1);
25  EXPECT_TRUE(host->AppendBlobDataItem(id, item));
26  EXPECT_TRUE(host->FinishBuildingBlob(id, "text/plain"));
27  EXPECT_FALSE(host->StartBuildingBlob(id));
28}
29}  // namespace
30
31TEST(BlobStorageContextTest, IncrementDecrementRef) {
32  BlobStorageContext context;
33  BlobStorageHost host(&context);
34  base::MessageLoop fake_io_message_loop;
35
36  // Build up a basic blob.
37  const std::string kId("id");
38  SetupBasicBlob(&host, kId);
39
40  // Make sure it's there, finish building implies a ref of one.
41  scoped_ptr<BlobDataHandle> blob_data_handle;
42  blob_data_handle = context.GetBlobDataFromUUID(kId);
43  EXPECT_TRUE(blob_data_handle);
44  blob_data_handle.reset();
45  {  // Clean up for ASAN
46    base::RunLoop run_loop;
47    run_loop.RunUntilIdle();
48  }
49
50  // Make sure its still there after inc/dec.
51  EXPECT_TRUE(host.IncrementBlobRefCount(kId));
52  EXPECT_TRUE(host.DecrementBlobRefCount(kId));
53  blob_data_handle = context.GetBlobDataFromUUID(kId);
54  EXPECT_TRUE(blob_data_handle);
55  blob_data_handle.reset();
56  {  // Clean up for ASAN
57    base::RunLoop run_loop;
58    run_loop.RunUntilIdle();
59  }
60
61  // Make sure it goes away in the end.
62  EXPECT_TRUE(host.DecrementBlobRefCount(kId));
63  blob_data_handle = context.GetBlobDataFromUUID(kId);
64  EXPECT_FALSE(blob_data_handle);
65  EXPECT_FALSE(host.DecrementBlobRefCount(kId));
66  EXPECT_FALSE(host.IncrementBlobRefCount(kId));
67}
68
69TEST(BlobStorageContextTest, BlobDataHandle) {
70  BlobStorageContext context;
71  BlobStorageHost host(&context);
72  base::MessageLoop fake_io_message_loop;
73
74  // Build up a basic blob.
75  const std::string kId("id");
76  SetupBasicBlob(&host, kId);
77
78  // Get a handle to it.
79  scoped_ptr<BlobDataHandle> blob_data_handle =
80      context.GetBlobDataFromUUID(kId);
81  EXPECT_TRUE(blob_data_handle);
82
83  // Drop the host's ref to it.
84  EXPECT_TRUE(host.DecrementBlobRefCount(kId));
85
86  // Should still be there due to the handle.
87  scoped_ptr<BlobDataHandle> another_handle =
88      context.GetBlobDataFromUUID(kId);
89  EXPECT_TRUE(another_handle);
90
91  // Should disappear after dropping both handles.
92  blob_data_handle.reset();
93  another_handle.reset();
94  {  // Clean up for ASAN
95    base::RunLoop run_loop;
96    run_loop.RunUntilIdle();
97  }
98  blob_data_handle = context.GetBlobDataFromUUID(kId);
99  EXPECT_FALSE(blob_data_handle);
100}
101
102TEST(BlobStorageContextTest, CompoundBlobs) {
103  const std::string kId1("id1");
104  const std::string kId2("id2");
105  const std::string kId2Prime("id2.prime");
106
107  base::MessageLoop fake_io_message_loop;
108
109  // Setup a set of blob data for testing.
110  base::Time time1, time2;
111  base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT", &time1);
112  base::Time::FromString("Mon, 14 Nov 1994, 11:30:49 GMT", &time2);
113
114  scoped_refptr<BlobData> blob_data1(new BlobData(kId1));
115  blob_data1->AppendData("Data1");
116  blob_data1->AppendData("Data2");
117  blob_data1->AppendFile(base::FilePath(FILE_PATH_LITERAL("File1.txt")),
118    10, 1024, time1);
119
120  scoped_refptr<BlobData> blob_data2(new BlobData(kId2));
121  blob_data2->AppendData("Data3");
122  blob_data2->AppendBlob(kId1, 8, 100);
123  blob_data2->AppendFile(base::FilePath(FILE_PATH_LITERAL("File2.txt")),
124      0, 20, time2);
125
126  scoped_refptr<BlobData> canonicalized_blob_data2(new BlobData(kId2Prime));
127  canonicalized_blob_data2->AppendData("Data3");
128  canonicalized_blob_data2->AppendData("a2___", 2);
129  canonicalized_blob_data2->AppendFile(
130      base::FilePath(FILE_PATH_LITERAL("File1.txt")),
131      10, 98, time1);
132  canonicalized_blob_data2->AppendFile(
133      base::FilePath(FILE_PATH_LITERAL("File2.txt")), 0, 20, time2);
134
135  BlobStorageContext context;
136  scoped_ptr<BlobDataHandle> blob_data_handle;
137
138  // Test a blob referring to only data and a file.
139  blob_data_handle = context.AddFinishedBlob(blob_data1.get());
140  ASSERT_TRUE(blob_data_handle.get());
141  EXPECT_TRUE(*(blob_data_handle->data()) == *blob_data1.get());
142
143  // Test a blob composed in part with another blob.
144  blob_data_handle = context.AddFinishedBlob(blob_data2.get());
145  ASSERT_TRUE(blob_data_handle.get());
146  EXPECT_TRUE(*(blob_data_handle->data()) == *canonicalized_blob_data2.get());
147
148  blob_data_handle.reset();
149  {  // Clean up for ASAN
150    base::RunLoop run_loop;
151    run_loop.RunUntilIdle();
152  }
153}
154
155TEST(BlobStorageContextTest, PublicBlobUrls) {
156  BlobStorageContext context;
157  BlobStorageHost host(&context);
158  base::MessageLoop fake_io_message_loop;
159
160  // Build up a basic blob.
161  const std::string kId("id");
162  SetupBasicBlob(&host, kId);
163
164  // Now register a url for that blob.
165  GURL kUrl("blob:id");
166  EXPECT_TRUE(host.RegisterPublicBlobURL(kUrl, kId));
167  scoped_ptr<BlobDataHandle> blob_data_handle =
168      context.GetBlobDataFromPublicURL(kUrl);
169  ASSERT_TRUE(blob_data_handle.get());
170  EXPECT_EQ(kId, blob_data_handle->data()->uuid());
171  blob_data_handle.reset();
172  {  // Clean up for ASAN
173    base::RunLoop run_loop;
174    run_loop.RunUntilIdle();
175  }
176
177  // The url registration should keep the blob alive even after
178  // explicit references are dropped.
179  EXPECT_TRUE(host.DecrementBlobRefCount(kId));
180  blob_data_handle = context.GetBlobDataFromPublicURL(kUrl);
181  EXPECT_TRUE(blob_data_handle);
182  blob_data_handle.reset();
183  {  // Clean up for ASAN
184    base::RunLoop run_loop;
185    run_loop.RunUntilIdle();
186  }
187
188  // Finally get rid of the url registration and the blob.
189  EXPECT_TRUE(host.RevokePublicBlobURL(kUrl));
190  blob_data_handle = context.GetBlobDataFromPublicURL(kUrl);
191  EXPECT_TRUE(!blob_data_handle.get());
192  EXPECT_FALSE(host.RevokePublicBlobURL(kUrl));
193}
194
195TEST(BlobStorageContextTest, HostCleanup) {
196  BlobStorageContext context;
197  scoped_ptr<BlobStorageHost> host(new BlobStorageHost(&context));
198  base::MessageLoop fake_io_message_loop;
199
200  // Build up a basic blob and register a url
201  const std::string kId("id");
202  GURL kUrl("blob:id");
203  SetupBasicBlob(host.get(), kId);
204  EXPECT_TRUE(host->RegisterPublicBlobURL(kUrl, kId));
205
206  // All should disappear upon host deletion.
207  host.reset();
208  scoped_ptr<BlobDataHandle> handle = context.GetBlobDataFromPublicURL(kUrl);
209  EXPECT_TRUE(!handle.get());
210  handle = context.GetBlobDataFromUUID(kId);
211  EXPECT_TRUE(!handle.get());
212}
213
214TEST(BlobStorageContextTest, EarlyContextDeletion) {
215  scoped_ptr<BlobStorageContext> context(new BlobStorageContext);
216  BlobStorageHost host(context.get());
217  base::MessageLoop fake_io_message_loop;
218
219  // Deleting the context should not induce crashes.
220  context.reset();
221
222  const std::string kId("id");
223  GURL kUrl("blob:id");
224  EXPECT_FALSE(host.StartBuildingBlob(kId));
225  BlobData::Item item;
226  item.SetToBytes("1", 1);
227  EXPECT_FALSE(host.AppendBlobDataItem(kId, item));
228  EXPECT_FALSE(host.FinishBuildingBlob(kId, "text/plain"));
229  EXPECT_FALSE(host.RegisterPublicBlobURL(kUrl, kId));
230  EXPECT_FALSE(host.IncrementBlobRefCount(kId));
231  EXPECT_FALSE(host.DecrementBlobRefCount(kId));
232  EXPECT_FALSE(host.RevokePublicBlobURL(kUrl));
233}
234
235// TODO(michaeln): tests for the depcrecated url stuff
236
237}  // namespace content
238