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 "components/enhanced_bookmarks/image_store.h"
7 #include "base/files/file_path.h"
8 #include "base/files/scoped_temp_dir.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "components/enhanced_bookmarks/image_record.h"
11 #include "components/enhanced_bookmarks/image_store_util.h"
12 #include "components/enhanced_bookmarks/persistent_image_store.h"
13 #include "components/enhanced_bookmarks/test_image_store.h"
14 #include "sql/statement.h"
15 #include "testing/platform_test.h"
16 #include "third_party/skia/include/core/SkBitmap.h"
17 #include "third_party/skia/include/core/SkColor.h"
22 scoped_ptr
<gfx::Image
> CreateImage(
23 int width
, int height
, int a
, int r
, int g
, int b
) {
25 bitmap
.allocN32Pixels(width
, height
);
26 bitmap
.eraseARGB(a
, r
, g
, b
);
27 scoped_ptr
<gfx::Image
> image(
28 new gfx::Image(gfx::Image::CreateFrom1xBitmap(bitmap
)));
31 // Make sure the image has a kImageRepCocoaTouch.
33 #endif // defined(OS_IOS)
38 scoped_ptr
<gfx::Image
> GenerateWhiteImage() {
39 return CreateImage(42, 24, 255, 255, 255, 255);
42 scoped_ptr
<gfx::Image
> GenerateBlackImage(int width
, int height
) {
43 return CreateImage(width
, height
, 255, 0, 0, 0);
46 scoped_ptr
<gfx::Image
> GenerateBlackImage() {
47 return GenerateBlackImage(42, 24);
50 // Returns true if the two images are identical.
51 bool CompareImages(const gfx::Image
& image_1
, const gfx::Image
& image_2
) {
52 if (image_1
.IsEmpty() && image_2
.IsEmpty())
55 if (image_1
.IsEmpty() || image_2
.IsEmpty())
58 scoped_refptr
<base::RefCountedMemory
> image_1_bytes
=
59 enhanced_bookmarks::BytesForImage(image_1
);
60 scoped_refptr
<base::RefCountedMemory
> image_2_bytes
=
61 enhanced_bookmarks::BytesForImage(image_2
);
63 if (image_1_bytes
->size() != image_2_bytes
->size())
66 return !memcmp(image_1_bytes
->front(),
67 image_2_bytes
->front(),
68 image_1_bytes
->size());
71 bool CreateV1PersistentImageStoreDB(const base::FilePath
& path
) {
76 if (db
.DoesTableExist("images_by_url"))
79 const char kV1TableSql
[] =
80 "CREATE TABLE IF NOT EXISTS images_by_url ("
81 "page_url LONGVARCHAR NOT NULL,"
82 "image_url LONGVARCHAR NOT NULL,"
87 if (!db
.Execute(kV1TableSql
))
90 const char kV1IndexSql
[] =
91 "CREATE INDEX IF NOT EXISTS images_by_url_idx ON images_by_url(page_url)";
92 if (!db
.Execute(kV1IndexSql
))
95 sql::Statement
statement(db
.GetUniqueStatement(
96 "INSERT INTO images_by_url "
97 "(page_url, image_url, image_data, width, height) "
98 "VALUES (?, ?, ?, ?, ?)"));
99 statement
.BindString(0, "foo://bar");
100 statement
.BindString(1, "http://a.jpg");
101 scoped_refptr
<base::RefCountedMemory
> image_bytes
=
102 enhanced_bookmarks::BytesForImage(*GenerateWhiteImage());
103 statement
.BindBlob(2, image_bytes
->front(), (int)image_bytes
->size());
104 statement
.BindInt(3, 42);
105 statement
.BindInt(4, 24);
107 return statement
.Run();
110 // Factory functions for creating instances of the implementations.
112 ImageStore
* CreateStore(base::ScopedTempDir
& folder
);
115 ImageStore
* CreateStore
<TestImageStore
>(
116 base::ScopedTempDir
& folder
) {
117 return new TestImageStore();
121 ImageStore
* CreateStore
<PersistentImageStore
>(
122 base::ScopedTempDir
& folder
) {
123 return new PersistentImageStore(folder
.path());
126 // Methods to check if persistence is on or not.
127 template <class T
> bool ShouldPersist();
128 template <> bool ShouldPersist
<TestImageStore
>() { return false; }
129 template <> bool ShouldPersist
<PersistentImageStore
>() { return true; }
131 // Test fixture class template for the abstract API.
133 class ImageStoreUnitTest
: public PlatformTest
{
135 ImageStoreUnitTest() {}
136 ~ImageStoreUnitTest() override
{}
138 void SetUp() override
{
139 bool success
= tempDir_
.CreateUniqueTempDir();
140 ASSERT_TRUE(success
);
141 store_
.reset(CreateStore
<T
>(tempDir_
));
144 void TearDown() override
{
145 if (store_
&& use_persistent_store())
149 bool use_persistent_store() const { return ShouldPersist
<T
>(); }
150 void ResetStore() { store_
.reset(CreateStore
<T
>(tempDir_
)); }
152 // The directory the database is saved into.
153 base::ScopedTempDir tempDir_
;
154 // The object the fixture is testing, via its base interface.
155 scoped_ptr
<ImageStore
> store_
;
158 DISALLOW_COPY_AND_ASSIGN(ImageStoreUnitTest
);
161 // The list of implementations of the abstract API that are going to be tested.
162 typedef testing::Types
<TestImageStore
,
163 PersistentImageStore
> Implementations
;
165 TYPED_TEST_CASE(ImageStoreUnitTest
, Implementations
);
167 // All those tests are run on all the implementations.
168 TYPED_TEST(ImageStoreUnitTest
, StartsEmpty
) {
169 std::set
<GURL
> all_urls
;
170 this->store_
->GetAllPageUrls(&all_urls
);
171 EXPECT_EQ(0u, all_urls
.size());
174 TYPED_TEST(ImageStoreUnitTest
, StoreOne
) {
175 scoped_refptr
<enhanced_bookmarks::ImageRecord
> image(
176 new enhanced_bookmarks::ImageRecord(
177 GenerateBlackImage(), GURL("http://a.jpg"), SK_ColorBLACK
));
178 this->store_
->Insert(GURL("foo://bar"), image
);
180 std::set
<GURL
> all_urls
;
181 this->store_
->GetAllPageUrls(&all_urls
);
182 EXPECT_EQ(1u, all_urls
.size());
183 EXPECT_EQ(GURL("foo://bar"), *all_urls
.begin());
184 EXPECT_TRUE(this->store_
->HasKey(GURL("foo://bar")));
187 TYPED_TEST(ImageStoreUnitTest
, Retrieve
) {
188 const GURL
url("foo://bar");
189 scoped_refptr
<enhanced_bookmarks::ImageRecord
> image_in(
190 new enhanced_bookmarks::ImageRecord(
191 CreateImage(42, 24, 1, 0, 0, 1), GURL("http://a.jpg"), SK_ColorBLUE
));
192 this->store_
->Insert(url
, image_in
);
194 scoped_refptr
<enhanced_bookmarks::ImageRecord
> image_out
=
195 this->store_
->Get(url
);
196 const gfx::Size size
= this->store_
->GetSize(url
);
198 EXPECT_EQ(42, size
.width());
199 EXPECT_EQ(24, size
.height());
200 EXPECT_EQ(image_in
->url
, image_out
->url
);
201 EXPECT_TRUE(CompareImages(*image_in
->image
, *image_out
->image
));
202 EXPECT_EQ(SK_ColorBLUE
, image_out
->dominant_color
);
205 TYPED_TEST(ImageStoreUnitTest
, Erase
) {
206 const GURL
url("foo://bar");
207 scoped_refptr
<enhanced_bookmarks::ImageRecord
> image(
208 new enhanced_bookmarks::ImageRecord(
209 GenerateBlackImage(), GURL("http://a.jpg"), SK_ColorBLACK
));
210 this->store_
->Insert(url
, image
);
211 this->store_
->Erase(url
);
213 EXPECT_FALSE(this->store_
->HasKey(url
));
214 std::set
<GURL
> all_urls
;
215 this->store_
->GetAllPageUrls(&all_urls
);
216 EXPECT_EQ(0u, all_urls
.size());
219 TYPED_TEST(ImageStoreUnitTest
, ClearAll
) {
220 const GURL
url_foo("http://foo");
221 scoped_refptr
<enhanced_bookmarks::ImageRecord
> black_image(
222 new enhanced_bookmarks::ImageRecord(
223 GenerateBlackImage(), GURL("http://a.jpg"), SK_ColorBLACK
));
224 this->store_
->Insert(url_foo
, black_image
);
225 const GURL
url_bar("http://bar");
226 scoped_refptr
<enhanced_bookmarks::ImageRecord
> white_image(
227 new enhanced_bookmarks::ImageRecord(
228 GenerateWhiteImage(), GURL("http://a.jpg"), SK_ColorWHITE
));
229 this->store_
->Insert(url_bar
, white_image
);
231 this->store_
->ClearAll();
233 EXPECT_FALSE(this->store_
->HasKey(url_foo
));
234 EXPECT_FALSE(this->store_
->HasKey(url_bar
));
235 std::set
<GURL
> all_urls
;
236 this->store_
->GetAllPageUrls(&all_urls
);
237 EXPECT_EQ(0u, all_urls
.size());
240 TYPED_TEST(ImageStoreUnitTest
, Update
) {
241 const GURL
url("foo://bar");
243 scoped_refptr
<enhanced_bookmarks::ImageRecord
> image1(
244 new enhanced_bookmarks::ImageRecord(
245 GenerateWhiteImage(), GURL("1.jpg"), SK_ColorWHITE
));
246 this->store_
->Insert(url
, image1
);
248 scoped_refptr
<enhanced_bookmarks::ImageRecord
> image2(
249 new enhanced_bookmarks::ImageRecord(
250 GenerateBlackImage(), GURL("2.jpg"), SK_ColorBLACK
));
251 this->store_
->Insert(url
, image2
);
253 scoped_refptr
<enhanced_bookmarks::ImageRecord
> image_out
=
254 this->store_
->Get(url
);
256 EXPECT_TRUE(this->store_
->HasKey(url
));
257 std::set
<GURL
> all_urls
;
258 this->store_
->GetAllPageUrls(&all_urls
);
259 EXPECT_EQ(1u, all_urls
.size());
260 EXPECT_EQ(image2
->url
, image_out
->url
);
261 EXPECT_TRUE(CompareImages(*image2
->image
, *image_out
->image
));
262 EXPECT_EQ(SK_ColorBLACK
, image_out
->dominant_color
);
265 TYPED_TEST(ImageStoreUnitTest
, Persistence
) {
266 const GURL
url("foo://bar");
267 scoped_refptr
<enhanced_bookmarks::ImageRecord
> image_in(
268 new enhanced_bookmarks::ImageRecord(
269 GenerateBlackImage(), GURL("http://a.jpg"), SK_ColorBLACK
));
270 this->store_
->Insert(url
, image_in
);
273 if (this->use_persistent_store()) {
274 std::set
<GURL
> all_urls
;
275 this->store_
->GetAllPageUrls(&all_urls
);
276 EXPECT_EQ(1u, all_urls
.size());
277 EXPECT_EQ(url
, *all_urls
.begin());
278 EXPECT_TRUE(this->store_
->HasKey(url
));
279 scoped_refptr
<enhanced_bookmarks::ImageRecord
> image_out
=
280 this->store_
->Get(url
);
282 EXPECT_EQ(image_in
->url
, image_out
->url
);
283 EXPECT_TRUE(CompareImages(*image_in
->image
, *image_out
->image
));
284 EXPECT_EQ(image_in
->dominant_color
, image_out
->dominant_color
);
286 std::set
<GURL
> all_urls
;
287 this->store_
->GetAllPageUrls(&all_urls
);
288 EXPECT_EQ(0u, all_urls
.size());
289 EXPECT_FALSE(this->store_
->HasKey(url
));
293 TYPED_TEST(ImageStoreUnitTest
, MigrationToV2
) {
294 // Migration is available only with persistent stores.
295 if (!this->use_persistent_store())
299 EXPECT_TRUE(CreateV1PersistentImageStoreDB(this->tempDir_
.path().Append(
300 base::FilePath::FromUTF8Unsafe("BookmarkImageAndUrlStore.db"))));
302 scoped_refptr
<enhanced_bookmarks::ImageRecord
> image_out
=
303 this->store_
->Get(GURL("foo://bar"));
304 EXPECT_EQ(SK_ColorWHITE
, image_out
->dominant_color
);
307 TYPED_TEST(ImageStoreUnitTest
, GetSize
) {
308 const GURL
url("foo://bar");
309 scoped_refptr
<enhanced_bookmarks::ImageRecord
> image_in(
310 new enhanced_bookmarks::ImageRecord(
311 GenerateBlackImage(), GURL("http://a.jpg"), SK_ColorBLACK
));
314 if (this->use_persistent_store()) {
315 // File shouldn't exist before we actually start using it since we do lazy
317 EXPECT_EQ(this->store_
->GetStoreSizeInBytes(), -1);
319 EXPECT_LE(this->store_
->GetStoreSizeInBytes(), 1024);
321 for (int i
= 0; i
< 100; ++i
) {
322 this->store_
->Insert(GURL(url
.spec() + '/' + base::IntToString(i
)),
324 EXPECT_GE(this->store_
->GetStoreSizeInBytes(), size
);
325 size
= this->store_
->GetStoreSizeInBytes();
328 if (this->use_persistent_store()) {
329 EXPECT_GE(this->store_
->GetStoreSizeInBytes(), 80 * 1024); // 80kb
330 EXPECT_LE(this->store_
->GetStoreSizeInBytes(), 200 * 1024); // 200kb
332 EXPECT_GE(this->store_
->GetStoreSizeInBytes(), 400 * 1024); // 400kb
333 EXPECT_LE(this->store_
->GetStoreSizeInBytes(), 500 * 1024); // 500kb