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/search_provider_logos/logo_cache.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/file_util.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/run_loop.h"
14 #include "base/time/time.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace search_provider_logos {
18
GetExampleMetadata()19 LogoMetadata GetExampleMetadata() {
20 LogoMetadata metadata;
21 metadata.source_url = "http://google.com/mylogo";
22 metadata.fingerprint = "LC4JVIZ5HVITQFKH0V70";
23 EXPECT_TRUE(base::Time::FromString("98-05-05 05:05:06 GMT",
24 &metadata.expiration_time));
25 metadata.can_show_after_expiration = true;
26 metadata.on_click_url = "https://www.google.com/search?q=chicken";
27 metadata.alt_text = "A logo about chickens";
28 metadata.mime_type = "image/jpeg";
29 return metadata;
30 }
31
GetExampleMetadata2()32 LogoMetadata GetExampleMetadata2() {
33 LogoMetadata metadata;
34 metadata.source_url = "https://www.example.com/thebestlogo?size=large";
35 metadata.fingerprint = "bh4PLHdnEaQAPxNGRyMao1rOmVFTXuOdVhdrMmPV";
36 EXPECT_TRUE(base::Time::FromString("17-04-04 07:10:58 GMT",
37 &metadata.expiration_time));
38 metadata.can_show_after_expiration = false;
39 metadata.on_click_url = "http://www.example.co.uk/welcome.php#top";
40 metadata.alt_text = "This is a logo";
41 metadata.mime_type = "image/png";
42 return metadata;
43 }
44
CreateExampleImage(size_t num_bytes)45 base::RefCountedString* CreateExampleImage(size_t num_bytes) {
46 base::RefCountedString* encoded_image_str = new base::RefCountedString();
47 std::string& str = encoded_image_str->data();
48 str.resize(num_bytes);
49 for (size_t i = 0; i < num_bytes; ++i)
50 str[i] = static_cast<char>(i);
51 return encoded_image_str;
52 }
53
GetExampleLogo()54 EncodedLogo GetExampleLogo() {
55 EncodedLogo logo;
56 logo.encoded_image = CreateExampleImage(837);
57 logo.metadata = GetExampleMetadata();
58 return logo;
59 }
60
GetExampleLogo2()61 EncodedLogo GetExampleLogo2() {
62 EncodedLogo logo;
63 logo.encoded_image = CreateExampleImage(345);
64 logo.metadata = GetExampleMetadata2();
65 return logo;
66 }
67
ExpectMetadataEqual(const LogoMetadata & expected_metadata,const LogoMetadata & actual_metadata)68 void ExpectMetadataEqual(const LogoMetadata& expected_metadata,
69 const LogoMetadata& actual_metadata) {
70 EXPECT_EQ(expected_metadata.source_url, actual_metadata.source_url);
71 EXPECT_EQ(expected_metadata.fingerprint, actual_metadata.fingerprint);
72 EXPECT_EQ(expected_metadata.can_show_after_expiration,
73 actual_metadata.can_show_after_expiration);
74 EXPECT_EQ(expected_metadata.expiration_time, actual_metadata.expiration_time);
75 EXPECT_EQ(expected_metadata.on_click_url, actual_metadata.on_click_url);
76 EXPECT_EQ(expected_metadata.alt_text, actual_metadata.alt_text);
77 EXPECT_EQ(expected_metadata.mime_type, actual_metadata.mime_type);
78 }
79
ExpectLogosEqual(const EncodedLogo & expected_logo,const EncodedLogo & actual_logo)80 void ExpectLogosEqual(const EncodedLogo& expected_logo,
81 const EncodedLogo& actual_logo) {
82 ASSERT_TRUE(expected_logo.encoded_image);
83 ASSERT_TRUE(actual_logo.encoded_image);
84 EXPECT_TRUE(expected_logo.encoded_image->Equals(actual_logo.encoded_image));
85 ExpectMetadataEqual(expected_logo.metadata, actual_logo.metadata);
86 }
87
88 // Removes 1 byte from the end of the file at |path|.
ShortenFile(base::FilePath path)89 void ShortenFile(base::FilePath path) {
90 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
91 int64 file_length = file.GetLength();
92 ASSERT_NE(file_length, 0);
93 file.SetLength(file_length - 1);
94 }
95
96 class LogoCacheTest : public ::testing::Test {
97 protected:
SetUp()98 virtual void SetUp() OVERRIDE {
99 ASSERT_TRUE(cache_parent_dir_.CreateUniqueTempDir());
100 InitCache();
101 }
102
InitCache()103 void InitCache() {
104 cache_.reset(new LogoCache(
105 cache_parent_dir_.path().Append(FILE_PATH_LITERAL("cache"))));
106 }
107
ExpectMetadata(const LogoMetadata * expected_metadata)108 void ExpectMetadata(const LogoMetadata* expected_metadata) {
109 const LogoMetadata* retrieved_metadata = cache_->GetCachedLogoMetadata();
110 if (expected_metadata) {
111 ASSERT_TRUE(retrieved_metadata != NULL);
112 ExpectMetadataEqual(*expected_metadata, *retrieved_metadata);
113 } else {
114 ASSERT_TRUE(retrieved_metadata == NULL);
115 }
116 }
117
ExpectLogo(const EncodedLogo * expected_logo)118 void ExpectLogo(const EncodedLogo* expected_logo) {
119 scoped_ptr<EncodedLogo> retrieved_logo(cache_->GetCachedLogo());
120 if (expected_logo) {
121 ASSERT_TRUE(retrieved_logo.get() != NULL);
122 ExpectLogosEqual(*expected_logo, *retrieved_logo);
123 } else {
124 ASSERT_TRUE(retrieved_logo.get() == NULL);
125 }
126 }
127
128 // Deletes the existing LogoCache and creates a new one. This clears any
129 // logo or metadata cached in memory to simulate restarting Chrome.
SimulateRestart()130 void SimulateRestart() {
131 InitCache();
132 }
133
134 scoped_ptr<LogoCache> cache_;
135 base::ScopedTempDir cache_parent_dir_;
136 };
137
138 // Tests -----------------------------------------------------------------------
139
TEST(LogoCacheSerializationTest,SerializeMetadata)140 TEST(LogoCacheSerializationTest, SerializeMetadata) {
141 LogoMetadata metadata = GetExampleMetadata();
142 std::string metadata_str;
143 int logo_num_bytes = 33;
144 LogoCache::LogoMetadataToString(metadata, logo_num_bytes, &metadata_str);
145 scoped_ptr<LogoMetadata> metadata2 =
146 LogoCache::LogoMetadataFromString(metadata_str, &logo_num_bytes);
147 ASSERT_TRUE(metadata2);
148 ExpectMetadataEqual(metadata, *metadata2);
149 }
150
TEST(LogoCacheSerializationTest,DeserializeCorruptMetadata)151 TEST(LogoCacheSerializationTest, DeserializeCorruptMetadata) {
152 int logo_num_bytes = 33;
153 scoped_ptr<LogoMetadata> metadata =
154 LogoCache::LogoMetadataFromString("", &logo_num_bytes);
155 ASSERT_TRUE(metadata.get() == NULL);
156
157 LogoMetadata example_metadata = GetExampleMetadata2();
158 std::string corrupt_str;
159 LogoCache::LogoMetadataToString(
160 example_metadata, logo_num_bytes, &corrupt_str);
161 corrupt_str.append("@");
162 metadata = LogoCache::LogoMetadataFromString(corrupt_str, &logo_num_bytes);
163 ASSERT_TRUE(metadata.get() == NULL);
164 }
165
TEST_F(LogoCacheTest,StoreAndRetrieveMetadata)166 TEST_F(LogoCacheTest, StoreAndRetrieveMetadata) {
167 // Expect no metadata at first.
168 ExpectMetadata(NULL);
169
170 // Set initial metadata.
171 EncodedLogo logo = GetExampleLogo();
172 LogoMetadata& metadata = logo.metadata;
173 cache_->SetCachedLogo(&logo);
174 ExpectMetadata(&metadata);
175
176 // Update metadata.
177 metadata.on_click_url = "http://anotherwebsite.com";
178 cache_->UpdateCachedLogoMetadata(metadata);
179 ExpectMetadata(&metadata);
180
181 // Read metadata back from disk.
182 SimulateRestart();
183 ExpectMetadata(&metadata);
184
185 // Ensure metadata is cached in memory.
186 base::DeleteFile(cache_->GetMetadataPath(), false);
187 ExpectMetadata(&metadata);
188 }
189
TEST_F(LogoCacheTest,StoreAndRetrieveLogo)190 TEST_F(LogoCacheTest, StoreAndRetrieveLogo) {
191 // Expect no metadata at first.
192 ExpectLogo(NULL);
193
194 // Set initial logo.
195 EncodedLogo logo = GetExampleLogo();
196 cache_->SetCachedLogo(&logo);
197 ExpectLogo(&logo);
198
199 // Update logo to NULL.
200 cache_->SetCachedLogo(NULL);
201 ExpectLogo(NULL);
202
203 // Read logo back from disk.
204 SimulateRestart();
205 ExpectLogo(NULL);
206
207 // Update logo.
208 logo = GetExampleLogo2();
209 cache_->SetCachedLogo(&logo);
210 ExpectLogo(&logo);
211
212 // Read logo back from disk.
213 SimulateRestart();
214 ExpectLogo(&logo);
215 }
216
TEST_F(LogoCacheTest,RetrieveCorruptMetadata)217 TEST_F(LogoCacheTest, RetrieveCorruptMetadata) {
218 // Set initial logo.
219 EncodedLogo logo = GetExampleLogo2();
220 cache_->SetCachedLogo(&logo);
221 ExpectLogo(&logo);
222
223 // Corrupt metadata and expect NULL for both logo and metadata.
224 SimulateRestart();
225 ShortenFile(cache_->GetMetadataPath());
226 ExpectMetadata(NULL);
227 ExpectLogo(NULL);
228
229 // Ensure corrupt cache files are deleted.
230 EXPECT_FALSE(base::PathExists(cache_->GetMetadataPath()));
231 EXPECT_FALSE(base::PathExists(cache_->GetLogoPath()));
232 }
233
TEST_F(LogoCacheTest,RetrieveCorruptLogo)234 TEST_F(LogoCacheTest, RetrieveCorruptLogo) {
235 // Set initial logo.
236 EncodedLogo logo = GetExampleLogo();
237 cache_->SetCachedLogo(&logo);
238 ExpectLogo(&logo);
239
240 // Corrupt logo and expect NULL.
241 SimulateRestart();
242 ShortenFile(cache_->GetLogoPath());
243 ExpectLogo(NULL);
244 // Once the logo is noticed to be NULL, the metadata should also be cleared.
245 ExpectMetadata(NULL);
246
247 // Ensure corrupt cache files are deleted.
248 EXPECT_FALSE(base::PathExists(cache_->GetMetadataPath()));
249 EXPECT_FALSE(base::PathExists(cache_->GetLogoPath()));
250 }
251
252 } // namespace search_provider_logos
253