1 // Copyright 2018 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 "chrome/browser/android/explore_sites/get_images_task.h"
6
7 #include <memory>
8
9 #include "base/bind.h"
10 #include "base/test/bind.h"
11 #include "base/test/mock_callback.h"
12 #include "chrome/browser/android/explore_sites/explore_sites_schema.h"
13 #include "components/offline_pages/task/task.h"
14 #include "components/offline_pages/task/task_test_base.h"
15 #include "sql/database.h"
16 #include "sql/meta_table.h"
17 #include "sql/statement.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 using offline_pages::TaskTestBase;
22
23 namespace explore_sites {
24 using InitializationStatus = ExploreSitesStore::InitializationStatus;
25
26 class ExploreSitesGetImagesTaskTest : public TaskTestBase {
27 public:
28 ExploreSitesGetImagesTaskTest() = default;
29 ~ExploreSitesGetImagesTaskTest() override = default;
30
SetUp()31 void SetUp() override {
32 store_ = std::make_unique<ExploreSitesStore>(task_runner());
33 }
34
store()35 ExploreSitesStore* store() { return store_.get(); }
36
ExecuteSync(base::RepeatingCallback<bool (sql::Database *)> query)37 void ExecuteSync(base::RepeatingCallback<bool(sql::Database*)> query) {
38 store()->Execute(base::OnceCallback<bool(sql::Database*)>(query),
39 base::BindOnce([](bool result) { ASSERT_TRUE(result); }),
40 false);
41 RunUntilIdle();
42 }
43
44 void PopulateTestingCatalog();
45 void UpdateCatalogVersions(std::string current_version,
46 std::string downloading_version);
47 void PopulateInvalidRowsInCatalog();
48
ExpectEmptyImageList(EncodedImageList images)49 void ExpectEmptyImageList(EncodedImageList images) {
50 EXPECT_EQ(0U, images.size());
51 }
52
StoreResult()53 EncodedImageListCallback StoreResult() {
54 return base::BindLambdaForTesting(
55 [&](EncodedImageList result) { last_result = std::move(result); });
56 }
57
ExpectSummaryBitmapsInOrder()58 void ExpectSummaryBitmapsInOrder() {
59 std::vector<std::string> ordered_bitmaps = {"bytes1", "bytes3", "bytes2"};
60 EXPECT_EQ(3U, last_result.size());
61 for (int i = 0; i < 3; i++) {
62 std::vector<uint8_t>& result = *last_result[i];
63 EXPECT_EQ(ordered_bitmaps[i], std::string(result.begin(), result.end()));
64 }
65 }
66
67 EncodedImageList last_result;
68
69 private:
70 std::unique_ptr<ExploreSitesStore> store_;
71
72 DISALLOW_COPY_AND_ASSIGN(ExploreSitesGetImagesTaskTest);
73 };
74
PopulateTestingCatalog()75 void ExploreSitesGetImagesTaskTest::PopulateTestingCatalog() {
76 ExecuteSync(base::BindLambdaForTesting([](sql::Database* db) {
77 sql::MetaTable meta_table;
78 ExploreSitesSchema::InitMetaTable(db, &meta_table);
79 meta_table.SetValue("current_catalog", 5678);
80 meta_table.DeleteKey("downloading_catalog");
81 sql::Statement insert(db->GetUniqueStatement(R"(
82 INSERT INTO categories
83 (category_id, version_token, type, label)
84 VALUES
85 (3, "5678", 1, "label_1"),
86 (4, "5678", 2, "label_2");)"));
87 if (!insert.Run())
88 return false;
89
90 sql::Statement insert_sites(db->GetUniqueStatement(R"(
91 INSERT INTO sites
92 (site_id, url, category_id, title, favicon)
93 VALUES
94 (1, "https://www.example.com/1", 3, "example_1", "bytes1"),
95 (2, "https://www.example.com/2", 4, "example_2", "bytes2"),
96 (3, "https://www.example.com/3", 3, "example_3", "bytes3"),
97 (4, "https://www.example.com/4", 4, "example_4", "bytes4");
98 )"));
99 return insert_sites.Run();
100 }));
101 }
102
103 void ExploreSitesGetImagesTaskTest::UpdateCatalogVersions(
104 std::string current_version,
105 std::string downloading_version) {
106 ExecuteSync(base::BindLambdaForTesting([&](sql::Database* db) {
107 sql::MetaTable meta_table;
108 ExploreSitesSchema::InitMetaTable(db, &meta_table);
109 meta_table.SetValue(ExploreSitesSchema::kDownloadingCatalogKey,
110 downloading_version);
111 meta_table.SetValue(ExploreSitesSchema::kCurrentCatalogKey,
112 current_version);
113 return true;
114 }));
115 }
116
117 void ExploreSitesGetImagesTaskTest::PopulateInvalidRowsInCatalog() {
118 ExecuteSync(base::BindLambdaForTesting([](sql::Database* db) {
119 sql::MetaTable meta_table;
120 ExploreSitesSchema::InitMetaTable(db, &meta_table);
121 meta_table.SetValue(ExploreSitesSchema::kDownloadingCatalogKey, "5678");
122 meta_table.DeleteKey(ExploreSitesSchema::kCurrentCatalogKey);
123 sql::Statement insert(db->GetUniqueStatement(R"(
124 INSERT INTO categories
125 (category_id, version_token, type, label)
126 VALUES
127 (1, "XXXX", 1, "bad_1"),
128 (2, "XXXX", 2, "bad_2");)"));
129 if (!insert.Run())
130 return false;
131
132 sql::Statement insert_sites(db->GetUniqueStatement(R"(
133 INSERT INTO sites
134 (site_id, url, category_id, title, favicon)
135 VALUES
136 (5, "https://www.bad.com/1", 1, "bad", "bad - used unknown version"),
137 (6, "https://www.bad.com/2", 2, "bad", "bad - used unknown version");
138 )"));
139 return insert_sites.Run();
140 }));
141 }
142
143 TEST_F(ExploreSitesGetImagesTaskTest, StoreFailure) {
144 store()->SetInitializationStatusForTesting(InitializationStatus::kFailure,
145 false);
146
147 GetImagesTask task(store(), 1, StoreResult());
148 RunTask(&task);
149 EXPECT_EQ(0U, last_result.size());
150 }
151
152 TEST_F(ExploreSitesGetImagesTaskTest, SiteDoesNotExist) {
153 GetImagesTask task(store(), 43, StoreResult());
154 RunTask(&task);
155 EXPECT_EQ(0U, last_result.size());
156 }
157
158 TEST_F(ExploreSitesGetImagesTaskTest, CategoryDoesNotExist) {
159 GetImagesTask task(store(), 43 /* invalid id */, 4, StoreResult());
160 RunTask(&task);
161 EXPECT_EQ(0U, last_result.size());
162 }
163
164 TEST_F(ExploreSitesGetImagesTaskTest, SiteExistsAndHasFavicon) {
165 PopulateTestingCatalog();
166 GetImagesTask task(store(), 1, StoreResult());
167 RunTask(&task);
168
169 EXPECT_EQ(1U, last_result.size());
170 std::vector<uint8_t>& result = *last_result[0];
171 EXPECT_EQ("bytes1", std::string(result.begin(), result.end()));
172 last_result.clear();
173
174 GetImagesTask task2(store(), 3, StoreResult());
175 RunTask(&task2);
176
177 EXPECT_EQ(1U, last_result.size());
178 std::vector<uint8_t>& result3 = *last_result[0];
179 EXPECT_EQ("bytes3", std::string(result3.begin(), result3.end()));
180 }
181
182 TEST_F(ExploreSitesGetImagesTaskTest, SitesExistAndNotBlocked) {
183 PopulateTestingCatalog();
184 GetImagesTask task(store(), 3, 4, StoreResult());
185 RunTask(&task);
186
187 EXPECT_EQ(2U, last_result.size());
188 std::vector<uint8_t>& result = *last_result[0];
189 EXPECT_EQ("bytes1", std::string(result.begin(), result.end()));
190 std::vector<uint8_t>& result2 = *last_result[1];
191 EXPECT_EQ("bytes3", std::string(result2.begin(), result2.end()));
192 }
193
194 TEST_F(ExploreSitesGetImagesTaskTest, SitesExistAndBlocked) {
195 PopulateTestingCatalog();
196 ExecuteSync(base::BindLambdaForTesting([&](sql::Database* db) {
197 sql::Statement insert(db->GetUniqueStatement(R"(
198 INSERT INTO site_blocklist
199 (url, date_removed)
200 VALUES
201 ("https://www.example.com/1", 123);)"));
202 return insert.Run();
203 }));
204 GetImagesTask task(store(), 3, 4, StoreResult());
205 RunTask(&task);
206
207 EXPECT_EQ(1U, last_result.size());
208 std::vector<uint8_t>& result = *last_result[0];
209 EXPECT_EQ("bytes3", std::string(result.begin(), result.end()));
210 last_result.clear();
211 }
212
213 TEST_F(ExploreSitesGetImagesTaskTest, TooManySitesToReturn) {
214 PopulateTestingCatalog();
215 // Add 3 more sites to the interesting category, but limit the site max to 4.
216 ExecuteSync(base::BindLambdaForTesting([&](sql::Database* db) {
217 sql::Statement insert(db->GetUniqueStatement(R"(
218 INSERT INTO sites
219 (site_id, url, category_id, title, favicon)
220 VALUES
221 (5, "https://www.example.com/5", 3, "example_5", "bytes5"),
222 (6, "https://www.example.com/6", 3, "example_6", "bytes6"),
223 (7, "https://www.example.com/7", 3, "example_7", "bytes7");)"));
224 return insert.Run();
225 }));
226 GetImagesTask task(store(), 3, 4, StoreResult());
227 RunTask(&task);
228
229 EXPECT_EQ(4U, last_result.size());
230 }
231
232 TEST_F(ExploreSitesGetImagesTaskTest, SkipMissingFavicons) {
233 PopulateTestingCatalog();
234 ExecuteSync(base::BindLambdaForTesting([&](sql::Database* db) {
235 sql::Statement insert(db->GetUniqueStatement(R"(
236 INSERT INTO sites
237 (site_id, url, category_id, title, favicon)
238 VALUES
239 (5, "https://www.example.com/5", 3, "example_5", NULL),
240 (6, "https://www.example.com/6", 3, "example_6", ""),
241 (7, "https://www.example.com/7", 3, "example_7", "bytes7");
242 )"));
243 return insert.Run();
244 }));
245 GetImagesTask task(store(), 3, 3, StoreResult());
246 RunTask(&task);
247
248 EXPECT_EQ(3U, last_result.size());
249 std::vector<uint8_t>& result3 = *last_result[2];
250 EXPECT_EQ("bytes7", std::string(result3.begin(), result3.end()));
251 }
252
253 TEST_F(ExploreSitesGetImagesTaskTest, SummaryImage) {
254 PopulateTestingCatalog();
255 PopulateInvalidRowsInCatalog();
256 UpdateCatalogVersions("5678", "XXXX");
257 GetImagesTask task(store(), GetImagesTask::DataType::kSummary, 3,
258 StoreResult());
259 RunTask(&task);
260 ExpectSummaryBitmapsInOrder();
261 }
262
263 TEST_F(ExploreSitesGetImagesTaskTest,
264 SummaryImageUsesDownloadingCatalogIfNecessary) {
265 PopulateTestingCatalog();
266 PopulateInvalidRowsInCatalog();
267 UpdateCatalogVersions("", "5678");
268 GetImagesTask task(store(), GetImagesTask::DataType::kSummary, 3,
269 StoreResult());
270 RunTask(&task);
271 ExpectSummaryBitmapsInOrder();
272 }
273
274 TEST_F(ExploreSitesGetImagesTaskTest, SummaryImageNoResultsIfNoCatalogVersion) {
275 PopulateTestingCatalog();
276 PopulateInvalidRowsInCatalog();
277 UpdateCatalogVersions("", "");
278 GetImagesTask task(store(), GetImagesTask::DataType::kSummary, 3,
279 StoreResult());
280 RunTask(&task);
281 EXPECT_EQ(0U, last_result.size());
282 }
283
284 } // namespace explore_sites
285