query_clusters_state_unittest.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. // Copyright 2022 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. #include "components/history_clusters/core/query_clusters_state.h"
  5. #include "base/run_loop.h"
  6. #include "base/test/bind.h"
  7. #include "base/test/metrics/histogram_tester.h"
  8. #include "base/test/task_environment.h"
  9. #include "components/history/core/browser/history_types.h"
  10. #include "components/history_clusters/core/history_clusters_service_test_api.h"
  11. #include "components/history_clusters/core/history_clusters_types.h"
  12. #include "testing/gmock/include/gmock/gmock.h"
  13. #include "testing/gtest/include/gtest/gtest.h"
  14. #include "url/gurl.h"
  15. using ::testing::ElementsAre;
  16. namespace history_clusters {
  17. namespace {
  18. // A struct containing all the params in `QueryClustersState::ResultCallback`.
  19. struct OnGotClustersResult {
  20. std::string query;
  21. std::vector<history::Cluster> cluster_batch;
  22. bool can_load_more;
  23. bool is_continuation;
  24. };
  25. } // namespace
  26. class QueryClustersStateTest : public testing::Test {
  27. public:
  28. QueryClustersStateTest()
  29. : task_environment_(
  30. base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME) {}
  31. QueryClustersStateTest(const QueryClustersStateTest&) = delete;
  32. QueryClustersStateTest& operator=(const QueryClustersStateTest&) = delete;
  33. protected:
  34. OnGotClustersResult InjectRawClustersAndAwaitPostProcessing(
  35. QueryClustersState* state,
  36. const std::vector<history::Cluster>& raw_clusters,
  37. QueryClustersContinuationParams continuation_params) {
  38. // This block injects the fake `raw_clusters` data for post-processing and
  39. // spins the message loop until we finish post-processing.
  40. OnGotClustersResult result;
  41. base::RunLoop loop;
  42. state->OnGotRawClusters(
  43. base::TimeTicks(),
  44. base::BindLambdaForTesting(
  45. [&](const std::string& query,
  46. std::vector<history::Cluster> cluster_batch, bool can_load_more,
  47. bool is_continuation) {
  48. result = {query, cluster_batch, can_load_more, is_continuation};
  49. loop.Quit();
  50. }),
  51. raw_clusters, continuation_params);
  52. loop.Run();
  53. return result;
  54. }
  55. void InjectRawClustersAndExpectNoCallback(
  56. QueryClustersState* state,
  57. const std::vector<history::Cluster>& raw_clusters,
  58. QueryClustersContinuationParams continuation_params) {
  59. state->OnGotRawClusters(
  60. base::TimeTicks(),
  61. base::BindLambdaForTesting(
  62. [&](const std::string& query,
  63. std::vector<history::Cluster> cluster_batch, bool can_load_more,
  64. bool is_continuation) {
  65. FAIL() << "Callback should not have been called.";
  66. }),
  67. raw_clusters, continuation_params);
  68. }
  69. base::test::TaskEnvironment task_environment_;
  70. };
  71. TEST_F(QueryClustersStateTest, PostProcessingOccursAndLogsHistograms) {
  72. base::HistogramTester histogram_tester;
  73. QueryClustersState state(nullptr, "");
  74. std::vector<history::Cluster> raw_clusters;
  75. raw_clusters.push_back(
  76. history::Cluster(1, {}, {{u"keyword_one", history::ClusterKeywordData()}},
  77. /*should_show_on_prominent_ui_surfaces=*/false));
  78. raw_clusters.push_back(
  79. history::Cluster(2, {}, {{u"keyword_two", history::ClusterKeywordData()}},
  80. /*should_show_on_prominent_ui_surfaces=*/true));
  81. auto result =
  82. InjectRawClustersAndAwaitPostProcessing(&state, raw_clusters, {});
  83. // Just a basic test to verify that post-processing did indeed occur.
  84. // Detailed tests for the behavior of the filtering are in
  85. // `HistoryClustersUtil`.
  86. ASSERT_EQ(result.cluster_batch.size(), 1U);
  87. EXPECT_EQ(result.cluster_batch[0].cluster_id, 2);
  88. EXPECT_EQ(result.query, "");
  89. EXPECT_EQ(result.can_load_more, true);
  90. EXPECT_EQ(result.is_continuation, false);
  91. histogram_tester.ExpectBucketCount(
  92. "History.Clusters.PercentClustersFilteredByQuery", 50, 1);
  93. histogram_tester.ExpectTotalCount("History.Clusters.ServiceLatency", 1);
  94. }
  95. TEST_F(QueryClustersStateTest, CrossBatchDeduplication) {
  96. QueryClustersState state(nullptr, "myquery");
  97. {
  98. std::vector<history::Cluster> raw_clusters;
  99. // Verify that non-matching prominent clusters are filtered out.
  100. raw_clusters.push_back(history::Cluster(
  101. 1, {}, {{u"keyword_one", history::ClusterKeywordData()}},
  102. /*should_show_on_prominent_ui_surfaces=*/true));
  103. // Verify that matching non-prominent clusters still are shown.
  104. raw_clusters.push_back(
  105. history::Cluster(2, {GetHardcodedClusterVisit(1)},
  106. {{u"myquery", history::ClusterKeywordData()}},
  107. /*should_show_on_prominent_ui_surfaces=*/false));
  108. auto result =
  109. InjectRawClustersAndAwaitPostProcessing(&state, raw_clusters, {});
  110. ASSERT_EQ(result.cluster_batch.size(), 1U);
  111. EXPECT_EQ(result.cluster_batch[0].cluster_id, 2);
  112. ASSERT_EQ(result.cluster_batch[0].visits.size(), 1U);
  113. EXPECT_EQ(
  114. result.cluster_batch[0].visits[0].annotated_visit.visit_row.visit_id,
  115. 1);
  116. EXPECT_EQ(result.query, "myquery");
  117. EXPECT_EQ(result.can_load_more, true);
  118. EXPECT_EQ(result.is_continuation, false);
  119. }
  120. // Send through a second batch of raw clusters. This verifies the stateful
  121. // cross-batch de-duplication.
  122. {
  123. std::vector<history::Cluster> raw_clusters;
  124. // Verify that a matching non-prominent non-duplicate cluster is still
  125. // allowed.
  126. raw_clusters.push_back(
  127. history::Cluster(3, {GetHardcodedClusterVisit(2)},
  128. {{u"myquery", history::ClusterKeywordData()}},
  129. /*should_show_on_prominent_ui_surfaces=*/false));
  130. // Verify that a matching non-prominent duplicate cluster is filtered out.
  131. raw_clusters.push_back(
  132. history::Cluster(4, {GetHardcodedClusterVisit(1)},
  133. {{u"myquery", history::ClusterKeywordData()}},
  134. /*should_show_on_prominent_ui_surfaces=*/false));
  135. // Verify that a matching prominent duplicate cluster is still allowed.
  136. raw_clusters.push_back(
  137. history::Cluster(5, {GetHardcodedClusterVisit(1)},
  138. {{u"myquery", history::ClusterKeywordData()}},
  139. /*should_show_on_prominent_ui_surfaces=*/true));
  140. auto result =
  141. InjectRawClustersAndAwaitPostProcessing(&state, raw_clusters, {});
  142. ASSERT_EQ(result.cluster_batch.size(), 2U);
  143. EXPECT_EQ(result.cluster_batch[0].cluster_id, 3);
  144. EXPECT_EQ(result.cluster_batch[1].cluster_id, 5);
  145. EXPECT_EQ(result.query, "myquery");
  146. EXPECT_EQ(result.can_load_more, true);
  147. EXPECT_EQ(result.is_continuation, true);
  148. }
  149. }
  150. TEST_F(QueryClustersStateTest, OnGotClusters) {
  151. const history::Cluster hidden_cluster = {1, {}, {}, false};
  152. const history::Cluster visible_cluster = {2, {}, {}, true};
  153. {
  154. QueryClustersState state(nullptr, "");
  155. // If the response clusters is empty, the callback should not be invoked.
  156. InjectRawClustersAndExpectNoCallback(
  157. &state, {},
  158. {base::Time(),
  159. /*is_continuation=*/false,
  160. /*is_partial_day=*/false,
  161. /*exhausted_unclustered_visits=*/false,
  162. /*exhausted_all_visits=*/false});
  163. // Likewise if the response clusters is filtered.
  164. InjectRawClustersAndExpectNoCallback(
  165. &state, {hidden_cluster},
  166. {base::Time(),
  167. /*is_continuation=*/false,
  168. /*is_partial_day=*/false,
  169. /*exhausted_unclustered_visits=*/false,
  170. /*exhausted_all_visits=*/false});
  171. // Even if the clusters is empty, the callback should be called when history
  172. // is exhausted.
  173. // Also, `is_continuation` should be false since this is the first time the
  174. // callbacks invoked even though there's been 2 earlier
  175. // `HistoryClusterService` responses.
  176. auto result = InjectRawClustersAndAwaitPostProcessing(
  177. &state, {},
  178. {base::Time(),
  179. /*is_continuation=*/false,
  180. /*is_partial_day=*/false,
  181. /*exhausted_unclustered_visits=*/true,
  182. /*exhausted_all_visits=*/true});
  183. EXPECT_EQ(result.cluster_batch.size(), 0u);
  184. EXPECT_EQ(result.query, "");
  185. EXPECT_EQ(result.can_load_more, false);
  186. EXPECT_EQ(result.is_continuation, false);
  187. }
  188. {
  189. QueryClustersState state(nullptr, "");
  190. // `is_continuation` should be false on the first callback.
  191. // `can_load_more` should be true on non-last callback.
  192. auto result = InjectRawClustersAndAwaitPostProcessing(
  193. &state, {visible_cluster},
  194. {base::Time(),
  195. /*is_continuation=*/false,
  196. /*is_partial_day=*/false,
  197. /*exhausted_unclustered_visits=*/false,
  198. /*exhausted_all_visits=*/false});
  199. EXPECT_EQ(result.cluster_batch.size(), 1u);
  200. EXPECT_EQ(result.query, "");
  201. EXPECT_EQ(result.can_load_more, true);
  202. EXPECT_EQ(result.is_continuation, false);
  203. // `is_continuation` should be true on non-first callback.
  204. // `can_load_more` should be true on non-last callback.
  205. result = InjectRawClustersAndAwaitPostProcessing(
  206. &state, {visible_cluster},
  207. {base::Time(),
  208. /*is_continuation=*/true,
  209. /*is_partial_day=*/false,
  210. /*exhausted_unclustered_visits=*/false,
  211. /*exhausted_all_visits=*/false});
  212. EXPECT_EQ(result.cluster_batch.size(), 1u);
  213. EXPECT_EQ(result.query, "");
  214. EXPECT_EQ(result.can_load_more, true);
  215. EXPECT_EQ(result.is_continuation, true);
  216. // `can_load_more` should be false on the last callback.
  217. result = InjectRawClustersAndAwaitPostProcessing(
  218. &state, {visible_cluster},
  219. {base::Time(),
  220. /*is_continuation=*/true,
  221. /*is_partial_day=*/false,
  222. /*exhausted_unclustered_visits=*/true,
  223. /*exhausted_all_visits=*/true});
  224. EXPECT_EQ(result.cluster_batch.size(), 1u);
  225. EXPECT_EQ(result.query, "");
  226. EXPECT_EQ(result.can_load_more, false);
  227. EXPECT_EQ(result.is_continuation, true);
  228. }
  229. }
  230. TEST_F(QueryClustersStateTest, UniqueRawLabels) {
  231. QueryClustersState state(nullptr, "");
  232. auto cluster1 = history::Cluster(1, {}, {});
  233. cluster1.raw_label = u"rawlabel1";
  234. auto cluster2 = history::Cluster(2, {}, {});
  235. cluster2.raw_label = u"rawlabel2";
  236. auto cluster3 = history::Cluster(3, {}, {});
  237. cluster3.raw_label = u"rawlabel3";
  238. // Now make some clusters with repeated raw labels.
  239. auto cluster4 = history::Cluster(4, {}, {});
  240. cluster4.raw_label = u"rawlabel1";
  241. auto cluster5 = history::Cluster(5, {}, {});
  242. cluster5.raw_label = u"rawlabel2";
  243. auto result = InjectRawClustersAndAwaitPostProcessing(
  244. &state, {cluster1, cluster2, cluster4}, {});
  245. ASSERT_EQ(result.cluster_batch.size(), 3U);
  246. EXPECT_THAT(state.raw_label_counts_so_far(),
  247. ElementsAre(std::make_pair(u"rawlabel1", 2),
  248. std::make_pair(u"rawlabel2", 1)));
  249. // Test updating an existing count, and adding new ones after that.
  250. result =
  251. InjectRawClustersAndAwaitPostProcessing(&state, {cluster5, cluster3}, {});
  252. ASSERT_EQ(result.cluster_batch.size(), 2U);
  253. EXPECT_THAT(state.raw_label_counts_so_far(),
  254. ElementsAre(std::make_pair(u"rawlabel1", 2),
  255. std::make_pair(u"rawlabel2", 2),
  256. std::make_pair(u"rawlabel3", 1)));
  257. }
  258. } // namespace history_clusters