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 gfx::Image
CreateImage(int width
, int height
, int a
, int r
, int g
, int b
) {
24 bitmap
.allocN32Pixels(width
, height
);
25 bitmap
.eraseARGB(a
, r
, g
, b
);
26 gfx::Image
image(gfx::Image::CreateFrom1xBitmap(bitmap
));
29 // Make sure the image has a kImageRepCocoaTouch.
31 #endif // defined(OS_IOS)
36 gfx::Image
GenerateWhiteImage() {
37 return CreateImage(42, 24, 255, 255, 255, 255);
40 gfx::Image
GenerateBlackImage(int width
, int height
) {
41 return CreateImage(width
, height
, 255, 0, 0, 0);
44 gfx::Image
GenerateBlackImage() {
45 return GenerateBlackImage(42, 24);
48 // Returns true if the two images are identical.
49 bool CompareImages(const gfx::Image
& image_1
, const gfx::Image
& image_2
) {
50 if (image_1
.IsEmpty() && image_2
.IsEmpty())
53 if (image_1
.IsEmpty() || image_2
.IsEmpty())
56 scoped_refptr
<base::RefCountedMemory
> image_1_bytes
=
57 enhanced_bookmarks::BytesForImage(image_1
);
58 scoped_refptr
<base::RefCountedMemory
> image_2_bytes
=
59 enhanced_bookmarks::BytesForImage(image_2
);
61 if (image_1_bytes
->size() != image_2_bytes
->size())
64 return !memcmp(image_1_bytes
->front(),
65 image_2_bytes
->front(),
66 image_1_bytes
->size());
69 bool CreateV1PersistentImageStoreDB(const base::FilePath
& path
) {
74 if (db
.DoesTableExist("images_by_url"))
77 const char kV1TableSql
[] =
78 "CREATE TABLE IF NOT EXISTS images_by_url ("
79 "page_url LONGVARCHAR NOT NULL,"
80 "image_url LONGVARCHAR NOT NULL,"
85 if (!db
.Execute(kV1TableSql
))
88 const char kV1IndexSql
[] =
89 "CREATE INDEX IF NOT EXISTS images_by_url_idx ON images_by_url(page_url)";
90 if (!db
.Execute(kV1IndexSql
))
93 sql::Statement
statement(db
.GetUniqueStatement(
94 "INSERT INTO images_by_url "
95 "(page_url, image_url, image_data, width, height) "
96 "VALUES (?, ?, ?, ?, ?)"));
97 statement
.BindString(0, "foo://bar");
98 statement
.BindString(1, "http://a.jpg");
99 scoped_refptr
<base::RefCountedMemory
> image_bytes
=
100 enhanced_bookmarks::BytesForImage(GenerateWhiteImage());
101 statement
.BindBlob(2, image_bytes
->front(), (int)image_bytes
->size());
102 statement
.BindInt(3, 42);
103 statement
.BindInt(4, 24);
105 return statement
.Run();
108 // Factory functions for creating instances of the implementations.
110 ImageStore
* CreateStore(base::ScopedTempDir
& folder
);
113 ImageStore
* CreateStore
<TestImageStore
>(
114 base::ScopedTempDir
& folder
) {
115 return new TestImageStore();
119 ImageStore
* CreateStore
<PersistentImageStore
>(
120 base::ScopedTempDir
& folder
) {
121 return new PersistentImageStore(folder
.path());
124 // Methods to check if persistence is on or not.
125 template <class T
> bool ShouldPersist();
126 template <> bool ShouldPersist
<TestImageStore
>() { return false; }
127 template <> bool ShouldPersist
<PersistentImageStore
>() { return true; }
129 // Test fixture class template for the abstract API.
131 class ImageStoreUnitTest
: public PlatformTest
{
133 ImageStoreUnitTest() {}
134 virtual ~ImageStoreUnitTest() {}
136 virtual void SetUp() override
{
137 bool success
= tempDir_
.CreateUniqueTempDir();
138 ASSERT_TRUE(success
);
139 store_
.reset(CreateStore
<T
>(tempDir_
));
142 virtual void TearDown() override
{
143 if (store_
&& use_persistent_store())
147 bool use_persistent_store() const { return ShouldPersist
<T
>(); }
148 void ResetStore() { store_
.reset(CreateStore
<T
>(tempDir_
)); }
150 // The directory the database is saved into.
151 base::ScopedTempDir tempDir_
;
152 // The object the fixture is testing, via its base interface.
153 scoped_ptr
<ImageStore
> store_
;
156 DISALLOW_COPY_AND_ASSIGN(ImageStoreUnitTest
);
159 // The list of implementations of the abstract API that are going to be tested.
160 typedef testing::Types
<TestImageStore
,
161 PersistentImageStore
> Implementations
;
163 TYPED_TEST_CASE(ImageStoreUnitTest
, Implementations
);
165 // All those tests are run on all the implementations.
166 TYPED_TEST(ImageStoreUnitTest
, StartsEmpty
) {
167 std::set
<GURL
> all_urls
;
168 this->store_
->GetAllPageUrls(&all_urls
);
169 EXPECT_EQ(0u, all_urls
.size());
172 TYPED_TEST(ImageStoreUnitTest
, StoreOne
) {
173 const enhanced_bookmarks::ImageRecord
image(
174 GenerateBlackImage(), GURL("http://a.jpg"), SK_ColorBLACK
);
175 this->store_
->Insert(GURL("foo://bar"), image
);
177 std::set
<GURL
> all_urls
;
178 this->store_
->GetAllPageUrls(&all_urls
);
179 EXPECT_EQ(1u, all_urls
.size());
180 EXPECT_EQ(GURL("foo://bar"), *all_urls
.begin());
181 EXPECT_TRUE(this->store_
->HasKey(GURL("foo://bar")));
184 TYPED_TEST(ImageStoreUnitTest
, Retrieve
) {
185 const GURL
url("foo://bar");
186 const enhanced_bookmarks::ImageRecord
image_in(
187 CreateImage(42, 24, 1, 0, 0, 1), GURL("http://a.jpg"), SK_ColorBLUE
);
188 this->store_
->Insert(url
, image_in
);
190 const enhanced_bookmarks::ImageRecord image_out
= this->store_
->Get(url
);
191 const gfx::Size size
= this->store_
->GetSize(url
);
193 EXPECT_EQ(42, size
.width());
194 EXPECT_EQ(24, size
.height());
195 EXPECT_EQ(image_in
.url
, image_out
.url
);
196 EXPECT_TRUE(CompareImages(image_in
.image
, image_out
.image
));
197 EXPECT_EQ(SK_ColorBLUE
, image_out
.dominant_color
);
200 TYPED_TEST(ImageStoreUnitTest
, Erase
) {
201 const GURL
url("foo://bar");
202 const enhanced_bookmarks::ImageRecord
image(
203 GenerateBlackImage(), GURL("http://a.jpg"), SK_ColorBLACK
);
204 this->store_
->Insert(url
, image
);
205 this->store_
->Erase(url
);
207 EXPECT_FALSE(this->store_
->HasKey(url
));
208 std::set
<GURL
> all_urls
;
209 this->store_
->GetAllPageUrls(&all_urls
);
210 EXPECT_EQ(0u, all_urls
.size());
213 TYPED_TEST(ImageStoreUnitTest
, ClearAll
) {
214 const GURL
url_foo("http://foo");
215 const enhanced_bookmarks::ImageRecord
black_image(
216 GenerateBlackImage(), GURL("http://a.jpg"), SK_ColorBLACK
);
217 this->store_
->Insert(url_foo
, black_image
);
218 const GURL
url_bar("http://bar");
219 const enhanced_bookmarks::ImageRecord
white_image(
220 GenerateWhiteImage(), GURL("http://a.jpg"), SK_ColorWHITE
);
221 this->store_
->Insert(url_bar
, white_image
);
223 this->store_
->ClearAll();
225 EXPECT_FALSE(this->store_
->HasKey(url_foo
));
226 EXPECT_FALSE(this->store_
->HasKey(url_bar
));
227 std::set
<GURL
> all_urls
;
228 this->store_
->GetAllPageUrls(&all_urls
);
229 EXPECT_EQ(0u, all_urls
.size());
232 TYPED_TEST(ImageStoreUnitTest
, Update
) {
233 const GURL
url("foo://bar");
234 const enhanced_bookmarks::ImageRecord
image1(GenerateWhiteImage(),
235 GURL("1.jpg"), SK_ColorWHITE
);
236 this->store_
->Insert(url
, image1
);
238 const enhanced_bookmarks::ImageRecord
image2(GenerateBlackImage(),
239 GURL("2.jpg"), SK_ColorBLACK
);
240 this->store_
->Insert(url
, image2
);
242 const enhanced_bookmarks::ImageRecord image_out
= this->store_
->Get(url
);
244 EXPECT_TRUE(this->store_
->HasKey(url
));
245 std::set
<GURL
> all_urls
;
246 this->store_
->GetAllPageUrls(&all_urls
);
247 EXPECT_EQ(1u, all_urls
.size());
248 EXPECT_EQ(image2
.url
, image_out
.url
);
249 EXPECT_TRUE(CompareImages(image2
.image
, image_out
.image
));
250 EXPECT_EQ(SK_ColorBLACK
, image_out
.dominant_color
);
253 TYPED_TEST(ImageStoreUnitTest
, Persistence
) {
254 const GURL
url("foo://bar");
255 const enhanced_bookmarks::ImageRecord
image_in(
256 GenerateBlackImage(), GURL("http://a.jpg"), SK_ColorBLACK
);
257 this->store_
->Insert(url
, image_in
);
260 if (this->use_persistent_store()) {
261 std::set
<GURL
> all_urls
;
262 this->store_
->GetAllPageUrls(&all_urls
);
263 EXPECT_EQ(1u, all_urls
.size());
264 EXPECT_EQ(url
, *all_urls
.begin());
265 EXPECT_TRUE(this->store_
->HasKey(url
));
266 const enhanced_bookmarks::ImageRecord image_out
= this->store_
->Get(url
);
268 EXPECT_EQ(image_in
.url
, image_out
.url
);
269 EXPECT_TRUE(CompareImages(image_in
.image
, image_out
.image
));
270 EXPECT_EQ(image_in
.dominant_color
, image_out
.dominant_color
);
272 std::set
<GURL
> all_urls
;
273 this->store_
->GetAllPageUrls(&all_urls
);
274 EXPECT_EQ(0u, all_urls
.size());
275 EXPECT_FALSE(this->store_
->HasKey(url
));
279 TYPED_TEST(ImageStoreUnitTest
, MigrationToV2
) {
280 // Migration is available only with persistent stores.
281 if (!this->use_persistent_store())
285 EXPECT_TRUE(CreateV1PersistentImageStoreDB(this->tempDir_
.path().Append(
286 base::FilePath::FromUTF8Unsafe("BookmarkImageAndUrlStore.db"))));
288 const enhanced_bookmarks::ImageRecord image_out
=
289 this->store_
->Get(GURL("foo://bar"));
290 EXPECT_EQ(SK_ColorWHITE
, image_out
.dominant_color
);
293 TYPED_TEST(ImageStoreUnitTest
, GetSize
) {
294 const GURL
url("foo://bar");
295 const enhanced_bookmarks::ImageRecord
image_in(
296 GenerateBlackImage(), GURL("http://a.jpg"), SK_ColorBLACK
);
299 if (this->use_persistent_store()) {
300 // File shouldn't exist before we actually start using it since we do lazy
302 EXPECT_EQ(this->store_
->GetStoreSizeInBytes(), -1);
304 EXPECT_LE(this->store_
->GetStoreSizeInBytes(), 1024);
306 for (int i
= 0; i
< 100; ++i
) {
307 this->store_
->Insert(GURL(url
.spec() + '/' + base::IntToString(i
)),
309 EXPECT_GE(this->store_
->GetStoreSizeInBytes(), size
);
310 size
= this->store_
->GetStoreSizeInBytes();
313 if (this->use_persistent_store()) {
314 EXPECT_GE(this->store_
->GetStoreSizeInBytes(), 80 * 1024); // 80kb
315 EXPECT_LE(this->store_
->GetStoreSizeInBytes(), 200 * 1024); // 200kb
317 EXPECT_GE(this->store_
->GetStoreSizeInBytes(), 400 * 1024); // 400kb
318 EXPECT_LE(this->store_
->GetStoreSizeInBytes(), 500 * 1024); // 500kb