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 "components/enhanced_bookmarks/image_store.h"
6
7#include "base/files/scoped_temp_dir.h"
8#include "base/strings/string_number_conversions.h"
9#include "components/enhanced_bookmarks/image_store_util.h"
10#include "components/enhanced_bookmarks/persistent_image_store.h"
11#include "components/enhanced_bookmarks/test_image_store.h"
12#include "testing/platform_test.h"
13#include "third_party/skia/include/core/SkBitmap.h"
14#include "url/gurl.h"
15
16namespace {
17
18gfx::Image CreateImage(int width, int height, int a, int r, int g, int b) {
19  SkBitmap bitmap;
20  bitmap.allocN32Pixels(width, height);
21  bitmap.eraseARGB(a, r, g, b);
22  gfx::Image image(gfx::Image::CreateFrom1xBitmap(bitmap));
23
24#if defined(OS_IOS)
25  // Make sure the image has a kImageRepCocoaTouch.
26  image.ToUIImage();
27#endif  // defined(OS_IOS)
28
29  return image;
30}
31
32gfx::Image GenerateWhiteImage() {
33  return CreateImage(42, 24, 255, 255, 255, 255);
34}
35
36gfx::Image GenerateBlackImage(int width, int height) {
37  return CreateImage(width, height, 255, 0, 0, 0);
38}
39
40gfx::Image GenerateBlackImage() {
41  return GenerateBlackImage(42, 24);
42}
43
44// Returns true if the two images are identical.
45bool CompareImages(const gfx::Image& image_1, const gfx::Image& image_2) {
46  if (image_1.IsEmpty() && image_2.IsEmpty())
47    return true;
48
49  if (image_1.IsEmpty() || image_2.IsEmpty())
50    return false;
51
52  scoped_refptr<base::RefCountedMemory> image_1_bytes =
53      enhanced_bookmarks::BytesForImage(image_1);
54  scoped_refptr<base::RefCountedMemory> image_2_bytes =
55      enhanced_bookmarks::BytesForImage(image_2);
56
57  if (image_1_bytes->size() != image_2_bytes->size())
58    return false;
59
60  return !memcmp(image_1_bytes->front(),
61                 image_2_bytes->front(),
62                 image_1_bytes->size());
63}
64
65// Factory functions for creating instances of the implementations.
66template <class T>
67ImageStore* CreateStore(base::ScopedTempDir& folder);
68
69template <>
70ImageStore* CreateStore<TestImageStore>(
71    base::ScopedTempDir& folder) {
72  return new TestImageStore();
73}
74
75template <>
76ImageStore* CreateStore<PersistentImageStore>(
77    base::ScopedTempDir& folder) {
78  return new PersistentImageStore(folder.path());
79}
80
81// Methods to check if persistence is on or not.
82template <class T> bool ShouldPersist();
83template <> bool ShouldPersist<TestImageStore>() { return false; }
84template <> bool ShouldPersist<PersistentImageStore>() { return true; }
85
86// Test fixture class template for the abstract API.
87template <class T>
88class ImageStoreUnitTest : public PlatformTest {
89 protected:
90  ImageStoreUnitTest() {}
91  virtual ~ImageStoreUnitTest() {}
92
93  virtual void SetUp() OVERRIDE {
94    bool success = tempDir_.CreateUniqueTempDir();
95    ASSERT_TRUE(success);
96    store_.reset(CreateStore<T>(tempDir_));
97  }
98
99  virtual void TearDown() OVERRIDE {
100    if (store_ && use_persistent_store())
101      store_->ClearAll();
102  }
103
104  bool use_persistent_store() const { return ShouldPersist<T>(); }
105  void ResetStore() { store_.reset(CreateStore<T>(tempDir_)); }
106
107  // The directory the database is saved into.
108  base::ScopedTempDir tempDir_;
109  // The object the fixture is testing, via its base interface.
110  scoped_ptr<ImageStore> store_;
111
112 private:
113  DISALLOW_COPY_AND_ASSIGN(ImageStoreUnitTest);
114};
115
116// The list of implementations of the abstract API that are going to be tested.
117typedef testing::Types<TestImageStore,
118                       PersistentImageStore> Implementations;
119
120TYPED_TEST_CASE(ImageStoreUnitTest, Implementations);
121
122// All those tests are run on all the implementations.
123TYPED_TEST(ImageStoreUnitTest, StartsEmpty) {
124  std::set<GURL> all_urls;
125  this->store_->GetAllPageUrls(&all_urls);
126  EXPECT_EQ(0u, all_urls.size());
127}
128
129TYPED_TEST(ImageStoreUnitTest, StoreOne) {
130  this->store_->Insert(GURL("foo://bar"), GURL("a.jpg"), GenerateBlackImage());
131
132  std::set<GURL> all_urls;
133  this->store_->GetAllPageUrls(&all_urls);
134  EXPECT_EQ(1u, all_urls.size());
135  EXPECT_EQ(GURL("foo://bar"), *all_urls.begin());
136  EXPECT_TRUE(this->store_->HasKey(GURL("foo://bar")));
137}
138
139TYPED_TEST(ImageStoreUnitTest, Retrieve) {
140  gfx::Image src_image = GenerateBlackImage(42, 24);
141  const GURL url("foo://bar");
142  const GURL image_url("a.jpg");
143  this->store_->Insert(url, image_url, src_image);
144
145  std::pair<gfx::Image, GURL> image_info = this->store_->Get(url);
146  gfx::Size size = this->store_->GetSize(url);
147
148  EXPECT_EQ(size.width(), 42);
149  EXPECT_EQ(size.height(), 24);
150  EXPECT_EQ(image_url, image_info.second);
151  EXPECT_TRUE(CompareImages(src_image, image_info.first));
152}
153
154TYPED_TEST(ImageStoreUnitTest, Erase) {
155  gfx::Image src_image = GenerateBlackImage();
156  const GURL url("foo://bar");
157  const GURL image_url("a.jpg");
158  this->store_->Insert(url, image_url, src_image);
159  this->store_->Erase(url);
160
161  EXPECT_FALSE(this->store_->HasKey(url));
162  std::set<GURL> all_urls;
163  this->store_->GetAllPageUrls(&all_urls);
164  EXPECT_EQ(0u, all_urls.size());
165}
166
167TYPED_TEST(ImageStoreUnitTest, ClearAll) {
168  const GURL url_foo("http://foo");
169  this->store_->Insert(url_foo, GURL("foo.jpg"), GenerateBlackImage());
170  const GURL url_bar("http://bar");
171  this->store_->Insert(url_foo, GURL("bar.jpg"), GenerateWhiteImage());
172
173  this->store_->ClearAll();
174
175  EXPECT_FALSE(this->store_->HasKey(url_foo));
176  EXPECT_FALSE(this->store_->HasKey(url_bar));
177  std::set<GURL> all_urls;
178  this->store_->GetAllPageUrls(&all_urls);
179  EXPECT_EQ(0u, all_urls.size());
180}
181
182TYPED_TEST(ImageStoreUnitTest, Update) {
183  gfx::Image src_image1 = GenerateWhiteImage();
184  gfx::Image src_image2 = GenerateBlackImage();
185  const GURL url("foo://bar");
186  const GURL image_url1("1.jpg");
187  this->store_->Insert(url, image_url1, src_image1);
188
189  const GURL image_url2("2.jpg");
190  this->store_->Insert(url, image_url2, src_image2);
191
192  std::pair<gfx::Image, GURL> image_info = this->store_->Get(url);
193
194  EXPECT_TRUE(this->store_->HasKey(url));
195  std::set<GURL> all_urls;
196  this->store_->GetAllPageUrls(&all_urls);
197  EXPECT_EQ(1u, all_urls.size());
198  EXPECT_EQ(image_url2, image_info.second);
199  EXPECT_TRUE(CompareImages(src_image2, image_info.first));
200}
201
202TYPED_TEST(ImageStoreUnitTest, Persistence) {
203  gfx::Image src_image = GenerateBlackImage();
204  const GURL url("foo://bar");
205  const GURL image_url("a.jpg");
206  this->store_->Insert(url, image_url, src_image);
207
208  this->ResetStore();
209  if (this->use_persistent_store()) {
210    std::set<GURL> all_urls;
211    this->store_->GetAllPageUrls(&all_urls);
212    EXPECT_EQ(1u, all_urls.size());
213    EXPECT_EQ(GURL("foo://bar"), *all_urls.begin());
214    EXPECT_TRUE(this->store_->HasKey(GURL("foo://bar")));
215    std::pair<gfx::Image, GURL> image_info = this->store_->Get(url);
216
217    EXPECT_EQ(image_url, image_info.second);
218    EXPECT_TRUE(CompareImages(src_image, image_info.first));
219  } else {
220    std::set<GURL> all_urls;
221    this->store_->GetAllPageUrls(&all_urls);
222    EXPECT_EQ(0u, all_urls.size());
223    EXPECT_FALSE(this->store_->HasKey(GURL("foo://bar")));
224  }
225}
226
227TYPED_TEST(ImageStoreUnitTest, GetSize) {
228  gfx::Image src_image = GenerateBlackImage();
229  const GURL url("foo://bar");
230  const GURL image_url("a.jpg");
231
232  int64 size = 0;
233  if (this->use_persistent_store()) {
234    // File shouldn't exist before we actually start using it since we do lazy
235    // initialization.
236    EXPECT_EQ(this->store_->GetStoreSizeInBytes(), -1);
237  } else {
238    EXPECT_LE(this->store_->GetStoreSizeInBytes(), 1024);
239  }
240  for (int i = 0; i < 100; ++i) {
241    this->store_->Insert(
242        GURL(url.spec() + '/' + base::IntToString(i)), image_url, src_image);
243    EXPECT_GE(this->store_->GetStoreSizeInBytes(), size);
244    size = this->store_->GetStoreSizeInBytes();
245  }
246
247  if (this->use_persistent_store()) {
248    EXPECT_GE(this->store_->GetStoreSizeInBytes(),  90 * 1024); //  90kb
249    EXPECT_LE(this->store_->GetStoreSizeInBytes(), 200 * 1024); // 200kb
250  } else {
251    EXPECT_GE(this->store_->GetStoreSizeInBytes(), 400 * 1024); // 400kb
252    EXPECT_LE(this->store_->GetStoreSizeInBytes(), 500 * 1024); // 500kb
253  }
254}
255
256}  // namespace
257