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