browsing_topics_calculator_unittest.cc 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995
  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/browsing_topics/browsing_topics_calculator.h"
  5. #include "base/files/scoped_temp_dir.h"
  6. #include "base/logging.h"
  7. #include "base/test/bind.h"
  8. #include "base/test/gtest_util.h"
  9. #include "base/test/metrics/histogram_tester.h"
  10. #include "base/test/scoped_feature_list.h"
  11. #include "components/browsing_topics/test_util.h"
  12. #include "components/browsing_topics/util.h"
  13. #include "components/content_settings/core/browser/cookie_settings.h"
  14. #include "components/content_settings/core/browser/host_content_settings_map.h"
  15. #include "components/history/core/browser/history_database_params.h"
  16. #include "components/history/core/browser/history_service.h"
  17. #include "components/history/core/test/test_history_database.h"
  18. #include "components/optimization_guide/content/browser/page_content_annotations_service.h"
  19. #include "components/optimization_guide/content/browser/test_page_content_annotator.h"
  20. #include "components/optimization_guide/core/test_model_info_builder.h"
  21. #include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
  22. #include "components/privacy_sandbox/privacy_sandbox_prefs.h"
  23. #include "components/privacy_sandbox/privacy_sandbox_settings.h"
  24. #include "components/privacy_sandbox/privacy_sandbox_test_util.h"
  25. #include "components/sync_preferences/testing_pref_service_syncable.h"
  26. #include "components/ukm/test_ukm_recorder.h"
  27. #include "content/public/test/browser_task_environment.h"
  28. #include "content/public/test/browsing_topics_test_util.h"
  29. #include "services/metrics/public/cpp/ukm_builders.h"
  30. #include "third_party/blink/public/common/features.h"
  31. namespace browsing_topics {
  32. namespace {
  33. constexpr size_t kTaxonomySize = 349;
  34. constexpr int kTaxonomyVersion = 1;
  35. constexpr char kHost1[] = "www.foo1.com";
  36. constexpr char kHost2[] = "www.foo2.com";
  37. constexpr char kHost3[] = "www.foo3.com";
  38. constexpr char kHost4[] = "www.foo4.com";
  39. constexpr char kHost5[] = "www.foo5.com";
  40. constexpr char kHost6[] = "www.foo6.com";
  41. } // namespace
  42. class BrowsingTopicsCalculatorTest : public testing::Test {
  43. public:
  44. BrowsingTopicsCalculatorTest()
  45. : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
  46. EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
  47. content_settings::CookieSettings::RegisterProfilePrefs(prefs_.registry());
  48. HostContentSettingsMap::RegisterProfilePrefs(prefs_.registry());
  49. privacy_sandbox::RegisterProfilePrefs(prefs_.registry());
  50. host_content_settings_map_ = base::MakeRefCounted<HostContentSettingsMap>(
  51. &prefs_, /*is_off_the_record=*/false, /*store_last_modified=*/false,
  52. /*restore_session=*/false);
  53. cookie_settings_ = base::MakeRefCounted<content_settings::CookieSettings>(
  54. host_content_settings_map_.get(), &prefs_, false, "chrome-extension");
  55. auto privacy_sandbox_delegate = std::make_unique<
  56. privacy_sandbox_test_util::MockPrivacySandboxSettingsDelegate>();
  57. privacy_sandbox_delegate->SetUpDefaultResponse(/*restricted=*/false);
  58. privacy_sandbox_settings_ =
  59. std::make_unique<privacy_sandbox::PrivacySandboxSettings>(
  60. std::move(privacy_sandbox_delegate),
  61. host_content_settings_map_.get(), cookie_settings_, &prefs_,
  62. /*incognito_profile=*/false);
  63. topics_site_data_manager_ =
  64. std::make_unique<content::TesterBrowsingTopicsSiteDataManager>(
  65. temp_dir_.GetPath());
  66. history_service_ = std::make_unique<history::HistoryService>();
  67. history_service_->Init(
  68. history::TestHistoryDatabaseParamsForPath(temp_dir_.GetPath()));
  69. optimization_guide_model_provider_ = std::make_unique<
  70. optimization_guide::TestOptimizationGuideModelProvider>();
  71. page_content_annotations_service_ =
  72. std::make_unique<optimization_guide::PageContentAnnotationsService>(
  73. "en-US", optimization_guide_model_provider_.get(),
  74. history_service_.get(), nullptr, base::FilePath(), nullptr,
  75. nullptr);
  76. page_content_annotations_service_->OverridePageContentAnnotatorForTesting(
  77. &test_page_content_annotator_);
  78. task_environment_.RunUntilIdle();
  79. }
  80. ~BrowsingTopicsCalculatorTest() override {
  81. host_content_settings_map_->ShutdownOnUIThread();
  82. }
  83. EpochTopics CalculateTopics(base::circular_deque<EpochTopics> epochs = {}) {
  84. EpochTopics result = EpochTopics(base::Time());
  85. base::RunLoop run_loop;
  86. TesterBrowsingTopicsCalculator topics_calculator =
  87. TesterBrowsingTopicsCalculator(
  88. privacy_sandbox_settings_.get(), history_service_.get(),
  89. topics_site_data_manager_.get(),
  90. page_content_annotations_service_.get(), epochs,
  91. base::BindLambdaForTesting([&](EpochTopics epoch_topics) {
  92. result = std::move(epoch_topics);
  93. run_loop.Quit();
  94. }),
  95. /*rand_uint64_queue=*/
  96. base::queue<uint64_t>{{100, 101, 102, 103, 104}});
  97. run_loop.Run();
  98. return result;
  99. }
  100. void AddHistoryEntries(const std::vector<std::string>& hosts,
  101. base::Time time) {
  102. history::HistoryAddPageArgs add_page_args;
  103. add_page_args.time = time;
  104. add_page_args.context_id = reinterpret_cast<history::ContextID>(1);
  105. for (const std::string& host : hosts) {
  106. static int nav_entry_id = 0;
  107. ++nav_entry_id;
  108. add_page_args.url = GURL(base::StrCat({"https://", host}));
  109. add_page_args.nav_entry_id = nav_entry_id;
  110. history_service_->AddPage(add_page_args);
  111. history_service_->SetBrowsingTopicsAllowed(
  112. add_page_args.context_id, nav_entry_id, add_page_args.url);
  113. }
  114. task_environment_.RunUntilIdle();
  115. }
  116. void AddApiUsageContextEntries(
  117. const std::vector<std::pair<std::string, std::set<HashedDomain>>>&
  118. main_frame_hosts_with_context_domains) {
  119. for (auto& [main_frame_host, context_domains] :
  120. main_frame_hosts_with_context_domains) {
  121. topics_site_data_manager_->OnBrowsingTopicsApiUsed(
  122. HashMainFrameHostForStorage(main_frame_host),
  123. base::flat_set<HashedDomain>(context_domains.begin(),
  124. context_domains.end()),
  125. base::Time::Now());
  126. }
  127. task_environment_.RunUntilIdle();
  128. }
  129. std::vector<optimization_guide::WeightedIdentifier> TopicsAndWeight(
  130. const std::vector<int32_t>& topics,
  131. double weight) {
  132. std::vector<optimization_guide::WeightedIdentifier> result;
  133. for (int32_t topic : topics) {
  134. result.emplace_back(
  135. optimization_guide::WeightedIdentifier(topic, weight));
  136. }
  137. return result;
  138. }
  139. void ExpectResultTopicsEqual(
  140. const std::vector<TopicAndDomains>& result,
  141. std::vector<std::pair<Topic, std::set<HashedDomain>>> expected) {
  142. DCHECK_EQ(expected.size(), 5u);
  143. EXPECT_EQ(result.size(), 5u);
  144. for (int i = 0; i < 5; ++i) {
  145. EXPECT_EQ(result[i].topic(), expected[i].first);
  146. EXPECT_EQ(result[i].hashed_domains(), expected[i].second);
  147. }
  148. }
  149. protected:
  150. content::BrowserTaskEnvironment task_environment_;
  151. sync_preferences::TestingPrefServiceSyncable prefs_;
  152. scoped_refptr<HostContentSettingsMap> host_content_settings_map_;
  153. scoped_refptr<content_settings::CookieSettings> cookie_settings_;
  154. std::unique_ptr<privacy_sandbox::PrivacySandboxSettings>
  155. privacy_sandbox_settings_;
  156. std::unique_ptr<content::TesterBrowsingTopicsSiteDataManager>
  157. topics_site_data_manager_;
  158. std::unique_ptr<history::HistoryService> history_service_;
  159. std::unique_ptr<optimization_guide::TestOptimizationGuideModelProvider>
  160. optimization_guide_model_provider_;
  161. std::unique_ptr<optimization_guide::PageContentAnnotationsService>
  162. page_content_annotations_service_;
  163. optimization_guide::TestPageContentAnnotator test_page_content_annotator_;
  164. base::ScopedTempDir temp_dir_;
  165. };
  166. TEST_F(BrowsingTopicsCalculatorTest, PermissionDenied) {
  167. base::HistogramTester histograms;
  168. privacy_sandbox_settings_->SetPrivacySandboxEnabled(false);
  169. EpochTopics result = CalculateTopics();
  170. EXPECT_TRUE(result.empty());
  171. histograms.ExpectUniqueSample(
  172. "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus",
  173. /*kFailurePermissionDenied*/ 1,
  174. /*expected_bucket_count=*/1);
  175. }
  176. TEST_F(BrowsingTopicsCalculatorTest, ApiUsageContextQueryError) {
  177. base::HistogramTester histograms;
  178. topics_site_data_manager_->SetQueryFailureOverride();
  179. EpochTopics result = CalculateTopics();
  180. EXPECT_TRUE(result.empty());
  181. histograms.ExpectUniqueSample(
  182. "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus",
  183. /*kFailureApiUsageContextQueryError*/ 2,
  184. /*expected_bucket_count=*/1);
  185. }
  186. TEST_F(BrowsingTopicsCalculatorTest, AnnotationExecutionError) {
  187. base::HistogramTester histograms;
  188. EpochTopics result = CalculateTopics();
  189. EXPECT_TRUE(result.empty());
  190. histograms.ExpectUniqueSample(
  191. "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus",
  192. /*kFailureAnnotationExecutionError*/ 3,
  193. /*expected_bucket_count=*/1);
  194. }
  195. class BrowsingTopicsCalculatorUnsupporedTaxonomyVersionTest
  196. : public BrowsingTopicsCalculatorTest {
  197. public:
  198. BrowsingTopicsCalculatorUnsupporedTaxonomyVersionTest() {
  199. feature_list_.InitAndEnableFeatureWithParameters(
  200. blink::features::kBrowsingTopics, {{"taxonomy_version", "999"}});
  201. }
  202. private:
  203. base::test::ScopedFeatureList feature_list_;
  204. };
  205. TEST_F(BrowsingTopicsCalculatorUnsupporedTaxonomyVersionTest,
  206. TaxonomyVersionNotSupportedInBinary) {
  207. base::HistogramTester histograms;
  208. test_page_content_annotator_.UsePageTopics(
  209. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), {});
  210. EpochTopics result = CalculateTopics();
  211. EXPECT_TRUE(result.empty());
  212. histograms.ExpectUniqueSample(
  213. "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus",
  214. /*kFailureTaxonomyVersionNotSupportedInBinary*/ 4,
  215. /*expected_bucket_count=*/1);
  216. }
  217. TEST_F(BrowsingTopicsCalculatorTest, TopicsMetadata) {
  218. base::HistogramTester histograms;
  219. base::Time begin_time = base::Time::Now();
  220. test_page_content_annotator_.UsePageTopics(
  221. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), {});
  222. EpochTopics result1 = CalculateTopics();
  223. EXPECT_FALSE(result1.empty());
  224. EXPECT_EQ(result1.taxonomy_size(), kTaxonomySize);
  225. EXPECT_EQ(result1.taxonomy_version(), kTaxonomyVersion);
  226. EXPECT_EQ(result1.model_version(), 1);
  227. EXPECT_EQ(result1.calculation_time(), begin_time);
  228. histograms.ExpectUniqueSample(
  229. "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus",
  230. /*kSuccess*/ 0,
  231. /*expected_bucket_count=*/1);
  232. task_environment_.AdvanceClock(base::Seconds(2));
  233. test_page_content_annotator_.UsePageTopics(
  234. *optimization_guide::TestModelInfoBuilder().SetVersion(50).Build(), {});
  235. EpochTopics result2 = CalculateTopics();
  236. EXPECT_FALSE(result2.empty());
  237. EXPECT_EQ(result2.taxonomy_size(), kTaxonomySize);
  238. EXPECT_EQ(result2.taxonomy_version(), kTaxonomyVersion);
  239. EXPECT_EQ(result2.model_version(), 50);
  240. EXPECT_EQ(result2.calculation_time(), begin_time + base::Seconds(2));
  241. histograms.ExpectUniqueSample(
  242. "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus",
  243. /*kSuccess*/ 0,
  244. /*expected_bucket_count=*/2);
  245. }
  246. TEST_F(BrowsingTopicsCalculatorTest, TopTopicsRankedByFrequency) {
  247. base::Time begin_time = base::Time::Now();
  248. AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
  249. begin_time);
  250. test_page_content_annotator_.UsePageTopics(
  251. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  252. {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
  253. {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
  254. {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
  255. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  256. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  257. {kHost6, TopicsAndWeight({6}, 0.1)}});
  258. task_environment_.AdvanceClock(base::Seconds(1));
  259. EpochTopics result = CalculateTopics();
  260. ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
  261. {{Topic(6), {}},
  262. {Topic(5), {}},
  263. {Topic(4), {}},
  264. {Topic(3), {}},
  265. {Topic(2), {}}});
  266. EXPECT_EQ(result.padded_top_topics_start_index(), 5u);
  267. }
  268. TEST_F(BrowsingTopicsCalculatorTest, ModelHasNoTopicsForHost) {
  269. base::Time begin_time = base::Time::Now();
  270. AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
  271. begin_time);
  272. test_page_content_annotator_.UsePageTopics(
  273. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  274. {{kHost1, {}},
  275. {kHost2, {}},
  276. {kHost3, {}},
  277. {kHost4, {}},
  278. {kHost5, {}},
  279. {kHost6, {}}});
  280. task_environment_.AdvanceClock(base::Seconds(1));
  281. EpochTopics result = CalculateTopics();
  282. ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
  283. {{Topic(101), {}},
  284. {Topic(102), {}},
  285. {Topic(103), {}},
  286. {Topic(104), {}},
  287. {Topic(105), {}}});
  288. EXPECT_EQ(result.padded_top_topics_start_index(), 0u);
  289. }
  290. TEST_F(BrowsingTopicsCalculatorTest,
  291. TopTopicsRankedByFrequency_AlsoAffectedByHostsCount) {
  292. base::Time begin_time = base::Time::Now();
  293. AddHistoryEntries({kHost1, kHost1, kHost1, kHost1, kHost1, kHost1, kHost2,
  294. kHost3, kHost4, kHost5, kHost6},
  295. begin_time);
  296. test_page_content_annotator_.UsePageTopics(
  297. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  298. {{kHost1, TopicsAndWeight({1, 2}, 0.1)},
  299. {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
  300. {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
  301. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  302. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  303. {kHost6, TopicsAndWeight({6}, 0.1)}});
  304. task_environment_.AdvanceClock(base::Seconds(1));
  305. EpochTopics result = CalculateTopics();
  306. ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
  307. {{Topic(2), {}},
  308. {Topic(1), {}},
  309. {Topic(6), {}},
  310. {Topic(5), {}},
  311. {Topic(4), {}}});
  312. EXPECT_EQ(result.padded_top_topics_start_index(), 5u);
  313. }
  314. TEST_F(BrowsingTopicsCalculatorTest,
  315. TopTopicsRankingNotAffectedByAnnotationWeight) {
  316. base::Time begin_time = base::Time::Now();
  317. AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
  318. begin_time);
  319. // Setting the weight for Topic(1) and Topic(2) to 0.9. This weight shouldn't
  320. // affect the top topics ordering.
  321. test_page_content_annotator_.UsePageTopics(
  322. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  323. {{kHost1, TopicsAndWeight({1, 2}, 0.9)},
  324. {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
  325. {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
  326. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  327. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  328. {kHost6, TopicsAndWeight({6}, 0.1)}});
  329. task_environment_.AdvanceClock(base::Seconds(1));
  330. EpochTopics result = CalculateTopics();
  331. ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
  332. {{Topic(6), {}},
  333. {Topic(5), {}},
  334. {Topic(4), {}},
  335. {Topic(3), {}},
  336. {Topic(2), {}}});
  337. EXPECT_EQ(result.padded_top_topics_start_index(), 5u);
  338. }
  339. TEST_F(BrowsingTopicsCalculatorTest, AllTopTopicsRandomlyPadded) {
  340. test_page_content_annotator_.UsePageTopics(
  341. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  342. {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
  343. {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
  344. {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
  345. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  346. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  347. {kHost6, TopicsAndWeight({6}, 0.1)}});
  348. EpochTopics result = CalculateTopics();
  349. ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
  350. {{Topic(101), {}},
  351. {Topic(102), {}},
  352. {Topic(103), {}},
  353. {Topic(104), {}},
  354. {Topic(105), {}}});
  355. EXPECT_EQ(result.padded_top_topics_start_index(), 0u);
  356. }
  357. TEST_F(BrowsingTopicsCalculatorTest, TopTopicsPartiallyPadded) {
  358. base::HistogramTester histograms;
  359. base::Time begin_time = base::Time::Now();
  360. AddHistoryEntries({kHost4, kHost5, kHost6}, begin_time);
  361. test_page_content_annotator_.UsePageTopics(
  362. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  363. {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
  364. {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
  365. {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
  366. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  367. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  368. {kHost6, TopicsAndWeight({6}, 0.1)}});
  369. task_environment_.AdvanceClock(base::Seconds(1));
  370. EpochTopics result = CalculateTopics();
  371. ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
  372. {{Topic(6), {}},
  373. {Topic(5), {}},
  374. {Topic(4), {}},
  375. {Topic(101), {}},
  376. {Topic(102), {}}});
  377. EXPECT_EQ(result.padded_top_topics_start_index(), 3u);
  378. }
  379. TEST_F(BrowsingTopicsCalculatorTest, CalculationResultUkm_FailedCalculation) {
  380. ukm::TestAutoSetUkmRecorder ukm_recorder;
  381. privacy_sandbox_settings_->SetPrivacySandboxEnabled(false);
  382. CalculateTopics();
  383. auto entries = ukm_recorder.GetEntriesByName(
  384. ukm::builders::BrowsingTopics_EpochTopicsCalculationResult::kEntryName);
  385. EXPECT_EQ(1u, entries.size());
  386. EXPECT_FALSE(ukm_recorder.GetEntryMetric(
  387. entries.back(),
  388. ukm::builders::BrowsingTopics_EpochTopicsCalculationResult::
  389. kTopTopic0Name));
  390. }
  391. TEST_F(BrowsingTopicsCalculatorTest, CalculationResultUkm) {
  392. ukm::TestAutoSetUkmRecorder ukm_recorder;
  393. base::HistogramTester histograms;
  394. base::Time begin_time = base::Time::Now();
  395. AddHistoryEntries({kHost4, kHost5, kHost6}, begin_time);
  396. test_page_content_annotator_.UsePageTopics(
  397. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  398. {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
  399. {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
  400. {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
  401. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  402. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  403. {kHost6, TopicsAndWeight({6}, 0.1)}});
  404. task_environment_.AdvanceClock(base::Seconds(1));
  405. CalculateTopics();
  406. auto entries = ukm_recorder.GetEntriesByName(
  407. ukm::builders::BrowsingTopics_EpochTopicsCalculationResult::kEntryName);
  408. EXPECT_EQ(1u, entries.size());
  409. ukm_recorder.ExpectEntryMetric(
  410. entries.back(),
  411. ukm::builders::BrowsingTopics_EpochTopicsCalculationResult::
  412. kTopTopic0Name,
  413. 6);
  414. ukm_recorder.ExpectEntryMetric(
  415. entries.back(),
  416. ukm::builders::BrowsingTopics_EpochTopicsCalculationResult::
  417. kTopTopic1Name,
  418. 5);
  419. ukm_recorder.ExpectEntryMetric(
  420. entries.back(),
  421. ukm::builders::BrowsingTopics_EpochTopicsCalculationResult::
  422. kTopTopic2Name,
  423. 4);
  424. ukm_recorder.ExpectEntryMetric(
  425. entries.back(),
  426. ukm::builders::BrowsingTopics_EpochTopicsCalculationResult::
  427. kTopTopic3Name,
  428. 101);
  429. ukm_recorder.ExpectEntryMetric(
  430. entries.back(),
  431. ukm::builders::BrowsingTopics_EpochTopicsCalculationResult::
  432. kTopTopic4Name,
  433. 102);
  434. ukm_recorder.ExpectEntryMetric(
  435. entries.back(),
  436. ukm::builders::BrowsingTopics_EpochTopicsCalculationResult::
  437. kTaxonomyVersionName,
  438. 1);
  439. ukm_recorder.ExpectEntryMetric(
  440. entries.back(),
  441. ukm::builders::BrowsingTopics_EpochTopicsCalculationResult::
  442. kModelVersionName,
  443. 1);
  444. ukm_recorder.ExpectEntryMetric(
  445. entries.back(),
  446. ukm::builders::BrowsingTopics_EpochTopicsCalculationResult::
  447. kPaddedTopicsStartIndexName,
  448. 3);
  449. }
  450. TEST_F(BrowsingTopicsCalculatorTest, TopTopicsAndObservingDomains) {
  451. base::Time begin_time = base::Time::Now();
  452. AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
  453. begin_time);
  454. AddApiUsageContextEntries(
  455. {{kHost1, {}},
  456. {kHost2, {}},
  457. {kHost3, {HashedDomain(2)}},
  458. {kHost4, {HashedDomain(3)}},
  459. {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
  460. test_page_content_annotator_.UsePageTopics(
  461. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  462. {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
  463. {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
  464. {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
  465. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  466. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  467. {kHost6, TopicsAndWeight({6}, 0.1)}});
  468. task_environment_.AdvanceClock(base::Seconds(1));
  469. EpochTopics result = CalculateTopics();
  470. ExpectResultTopicsEqual(
  471. result.top_topics_and_observing_domains(),
  472. {{Topic(6), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
  473. {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
  474. {Topic(4), {HashedDomain(2), HashedDomain(3)}},
  475. {Topic(3), {HashedDomain(2)}},
  476. {Topic(2), {}}});
  477. EXPECT_EQ(result.padded_top_topics_start_index(), 5u);
  478. }
  479. TEST_F(
  480. BrowsingTopicsCalculatorTest,
  481. HistoryHostsBefore21DaysAgo_IgnoredForTopTopicsDecision_IgnoredForObservingDomainsDecision) {
  482. base::Time begin_time = base::Time::Now();
  483. AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
  484. begin_time - base::Days(21));
  485. AddApiUsageContextEntries(
  486. {{kHost1, {}},
  487. {kHost2, {}},
  488. {kHost3, {HashedDomain(2)}},
  489. {kHost4, {HashedDomain(3)}},
  490. {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
  491. test_page_content_annotator_.UsePageTopics(
  492. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  493. {{kHost1, TopicsAndWeight({1, 2, 103, 4, 5, 6}, 0.1)},
  494. {kHost2, TopicsAndWeight({2, 103, 4, 5, 6}, 0.1)},
  495. {kHost3, TopicsAndWeight({103, 4, 5, 6}, 0.1)},
  496. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  497. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  498. {kHost6, TopicsAndWeight({6}, 0.1)}});
  499. task_environment_.AdvanceClock(base::Seconds(1));
  500. EpochTopics result = CalculateTopics();
  501. ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
  502. {{Topic(101), {}},
  503. {Topic(102), {}},
  504. {Topic(103), {}},
  505. {Topic(104), {}},
  506. {Topic(105), {}}});
  507. EXPECT_EQ(result.padded_top_topics_start_index(), 0u);
  508. }
  509. TEST_F(
  510. BrowsingTopicsCalculatorTest,
  511. HistoryHostsBetween7And21Days_IgnoredForTopTopicsDecision_ConsideredForObservingDomainsDecision) {
  512. base::Time begin_time = base::Time::Now();
  513. AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
  514. begin_time - base::Days(20));
  515. AddApiUsageContextEntries(
  516. {{kHost1, {}},
  517. {kHost2, {}},
  518. {kHost3, {HashedDomain(2)}},
  519. {kHost4, {HashedDomain(3)}},
  520. {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
  521. test_page_content_annotator_.UsePageTopics(
  522. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  523. {{kHost1, TopicsAndWeight({1, 2, 103, 4, 5, 6}, 0.1)},
  524. {kHost2, TopicsAndWeight({2, 103, 4, 5, 6}, 0.1)},
  525. {kHost3, TopicsAndWeight({103, 4, 5, 6}, 0.1)},
  526. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  527. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  528. {kHost6, TopicsAndWeight({6}, 0.1)}});
  529. task_environment_.AdvanceClock(base::Seconds(1));
  530. EpochTopics result = CalculateTopics();
  531. ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
  532. {{Topic(101), {}},
  533. {Topic(102), {}},
  534. {Topic(103), {HashedDomain(2)}},
  535. {Topic(104), {}},
  536. {Topic(105), {}}});
  537. EXPECT_EQ(result.padded_top_topics_start_index(), 0u);
  538. }
  539. TEST_F(BrowsingTopicsCalculatorTest,
  540. DataQueryBoundedByTopicsDataAccessibleSince) {
  541. base::Time begin_time = base::Time::Now();
  542. prefs_.SetTime(prefs::kPrivacySandboxTopicsDataAccessibleSince,
  543. begin_time + base::Days(6));
  544. AddHistoryEntries({kHost1, kHost2}, begin_time);
  545. AddApiUsageContextEntries({{kHost1, {}}, {kHost2, {}}});
  546. task_environment_.AdvanceClock(base::Days(6));
  547. AddHistoryEntries({kHost3, kHost4, kHost5, kHost6},
  548. begin_time + base::Days(6));
  549. AddApiUsageContextEntries(
  550. {{kHost3, {HashedDomain(2)}},
  551. {kHost4, {HashedDomain(3)}},
  552. {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
  553. test_page_content_annotator_.UsePageTopics(
  554. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  555. {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
  556. {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
  557. {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
  558. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  559. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  560. {kHost6, TopicsAndWeight({6}, 0.1)}});
  561. task_environment_.AdvanceClock(base::Seconds(1));
  562. EpochTopics result = CalculateTopics();
  563. ExpectResultTopicsEqual(
  564. result.top_topics_and_observing_domains(),
  565. {{Topic(6), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
  566. {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
  567. {Topic(4), {HashedDomain(2), HashedDomain(3)}},
  568. {Topic(3), {HashedDomain(2)}},
  569. {Topic(101), {}}});
  570. EXPECT_EQ(result.padded_top_topics_start_index(), 4u);
  571. }
  572. TEST_F(BrowsingTopicsCalculatorTest,
  573. HistoryDataBoundedByLastEpochCalculationTime) {
  574. base::Time begin_time = base::Time::Now();
  575. AddHistoryEntries({kHost1, kHost2, kHost3}, begin_time);
  576. AddApiUsageContextEntries({{kHost3, {HashedDomain(5)}}});
  577. task_environment_.AdvanceClock(base::Days(4));
  578. AddHistoryEntries({kHost2, kHost3}, begin_time + base::Days(4));
  579. AddApiUsageContextEntries({{kHost3, {HashedDomain(2)}}});
  580. task_environment_.AdvanceClock(base::Days(2));
  581. AddHistoryEntries({kHost3, kHost4, kHost5, kHost6},
  582. begin_time + base::Days(6));
  583. AddApiUsageContextEntries(
  584. {{kHost4, {HashedDomain(3)}},
  585. {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
  586. test_page_content_annotator_.UsePageTopics(
  587. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  588. {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
  589. {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
  590. {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
  591. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  592. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  593. {kHost6, TopicsAndWeight({6}, 0.1)}});
  594. task_environment_.AdvanceClock(base::Seconds(1));
  595. base::circular_deque<EpochTopics> epochs;
  596. epochs.push_back(EpochTopics(begin_time + base::Days(6)));
  597. EpochTopics result = CalculateTopics(std::move(epochs));
  598. // The topics are only from hosts since `begin_time + base::Days(6)`. The
  599. // observing domains are from data since `begin_time`.
  600. ExpectResultTopicsEqual(
  601. result.top_topics_and_observing_domains(),
  602. {{Topic(6),
  603. {HashedDomain(1), HashedDomain(2), HashedDomain(3), HashedDomain(5)}},
  604. {Topic(5),
  605. {HashedDomain(1), HashedDomain(2), HashedDomain(3), HashedDomain(5)}},
  606. {Topic(4), {HashedDomain(2), HashedDomain(3), HashedDomain(5)}},
  607. {Topic(3), {HashedDomain(2), HashedDomain(5)}},
  608. {Topic(101), {}}});
  609. EXPECT_EQ(result.padded_top_topics_start_index(), 4u);
  610. }
  611. TEST_F(BrowsingTopicsCalculatorTest,
  612. HistoryDataAndApiUsageContextDataBoundedByPriorEpochsCalculationTime) {
  613. base::Time begin_time = base::Time::Now();
  614. AddHistoryEntries({kHost1, kHost2, kHost3}, begin_time);
  615. AddApiUsageContextEntries({{kHost3, {HashedDomain(5)}}});
  616. task_environment_.AdvanceClock(base::Days(4));
  617. AddHistoryEntries({kHost2, kHost3}, begin_time + base::Days(4));
  618. AddApiUsageContextEntries({{kHost3, {HashedDomain(2)}}});
  619. task_environment_.AdvanceClock(base::Days(2));
  620. AddHistoryEntries({kHost3, kHost4, kHost5, kHost6},
  621. begin_time + base::Days(6));
  622. AddApiUsageContextEntries(
  623. {{kHost4, {HashedDomain(3)}},
  624. {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
  625. test_page_content_annotator_.UsePageTopics(
  626. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  627. {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
  628. {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
  629. {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
  630. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  631. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  632. {kHost6, TopicsAndWeight({6}, 0.1)}});
  633. task_environment_.AdvanceClock(base::Seconds(1));
  634. base::circular_deque<EpochTopics> epochs;
  635. epochs.push_back(EpochTopics(begin_time + base::Days(4)));
  636. epochs.push_back(EpochTopics(begin_time + base::Days(5)));
  637. epochs.push_back(EpochTopics(begin_time + base::Days(6)));
  638. EpochTopics result = CalculateTopics(std::move(epochs));
  639. // The topics are only from hosts since `begin_time + base::Days(6)`. The
  640. // observing domains are from data since `begin_time + base::Days(4)`.
  641. ExpectResultTopicsEqual(
  642. result.top_topics_and_observing_domains(),
  643. {{Topic(6), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
  644. {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
  645. {Topic(4), {HashedDomain(2), HashedDomain(3)}},
  646. {Topic(3), {HashedDomain(2)}},
  647. {Topic(101), {}}});
  648. EXPECT_EQ(result.padded_top_topics_start_index(), 4u);
  649. }
  650. TEST_F(BrowsingTopicsCalculatorTest,
  651. TopTopicsAndObservingDomains_DomainsSizeExceedsLimit) {
  652. base::Time begin_time = base::Time::Now();
  653. std::set<HashedDomain> large_size_domains;
  654. for (int i = 1; i <= 1001; ++i) {
  655. large_size_domains.insert(HashedDomain(i));
  656. }
  657. AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
  658. begin_time);
  659. AddApiUsageContextEntries({{kHost1, {}},
  660. {kHost2, {}},
  661. {kHost3, {HashedDomain(2)}},
  662. {kHost4, {HashedDomain(3)}},
  663. {kHost5, large_size_domains}});
  664. test_page_content_annotator_.UsePageTopics(
  665. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  666. {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
  667. {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
  668. {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
  669. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  670. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  671. {kHost6, TopicsAndWeight({6}, 0.1)}});
  672. task_environment_.AdvanceClock(base::Seconds(1));
  673. std::set<HashedDomain> expected_domains_after_capping = large_size_domains;
  674. expected_domains_after_capping.erase(HashedDomain(1));
  675. EpochTopics result = CalculateTopics();
  676. ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
  677. {{Topic(6), expected_domains_after_capping},
  678. {Topic(5), expected_domains_after_capping},
  679. {Topic(4), {HashedDomain(2), HashedDomain(3)}},
  680. {Topic(3), {HashedDomain(2)}},
  681. {Topic(2), {}}});
  682. EXPECT_EQ(result.padded_top_topics_start_index(), 5u);
  683. }
  684. TEST_F(BrowsingTopicsCalculatorTest, TopicBlocked) {
  685. base::Time begin_time = base::Time::Now();
  686. AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
  687. begin_time);
  688. AddApiUsageContextEntries(
  689. {{kHost1, {}},
  690. {kHost2, {}},
  691. {kHost3, {HashedDomain(2)}},
  692. {kHost4, {HashedDomain(3)}},
  693. {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
  694. test_page_content_annotator_.UsePageTopics(
  695. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  696. {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
  697. {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
  698. {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
  699. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  700. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  701. {kHost6, TopicsAndWeight({6}, 0.1)}});
  702. task_environment_.AdvanceClock(base::Seconds(1));
  703. privacy_sandbox_settings_->SetTopicAllowed(
  704. privacy_sandbox::CanonicalTopic(Topic(6), kTaxonomyVersion),
  705. /*allowed=*/false);
  706. privacy_sandbox_settings_->SetTopicAllowed(
  707. privacy_sandbox::CanonicalTopic(Topic(4), kTaxonomyVersion),
  708. /*allowed=*/false);
  709. EpochTopics result = CalculateTopics();
  710. ExpectResultTopicsEqual(
  711. result.top_topics_and_observing_domains(),
  712. {{Topic(0), {}},
  713. {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
  714. {Topic(0), {}},
  715. {Topic(3), {HashedDomain(2)}},
  716. {Topic(2), {}}});
  717. EXPECT_EQ(result.padded_top_topics_start_index(), 5u);
  718. }
  719. TEST_F(BrowsingTopicsCalculatorTest, PaddedTopicsDoNotDuplicate) {
  720. base::Time begin_time = base::Time::Now();
  721. AddHistoryEntries({kHost4, kHost5, kHost6}, begin_time);
  722. AddApiUsageContextEntries(
  723. {{kHost1, {}},
  724. {kHost2, {}},
  725. {kHost3, {HashedDomain(2)}},
  726. {kHost4, {HashedDomain(3)}},
  727. {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
  728. test_page_content_annotator_.UsePageTopics(
  729. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  730. {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 102}, 0.1)},
  731. {kHost2, TopicsAndWeight({2, 3, 4, 5, 102}, 0.1)},
  732. {kHost3, TopicsAndWeight({3, 4, 5, 102}, 0.1)},
  733. {kHost4, TopicsAndWeight({4, 5, 102}, 0.1)},
  734. {kHost5, TopicsAndWeight({5, 102}, 0.1)},
  735. {kHost6, TopicsAndWeight({102}, 0.1)}});
  736. task_environment_.AdvanceClock(base::Seconds(1));
  737. EpochTopics result = CalculateTopics();
  738. ExpectResultTopicsEqual(
  739. result.top_topics_and_observing_domains(),
  740. {{Topic(102), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
  741. {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
  742. {Topic(4), {HashedDomain(3)}},
  743. {Topic(101), {}},
  744. {Topic(103), {}}});
  745. }
  746. TEST_F(BrowsingTopicsCalculatorTest, Metrics) {
  747. base::HistogramTester histograms;
  748. base::Time begin_time = base::Time::Now();
  749. AddHistoryEntries({kHost4, kHost5, kHost6}, begin_time);
  750. AddApiUsageContextEntries(
  751. {{kHost1, {}},
  752. {kHost2, {}},
  753. {kHost3, {HashedDomain(2)}},
  754. {kHost4, {HashedDomain(3)}},
  755. {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
  756. test_page_content_annotator_.UsePageTopics(
  757. *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
  758. {{kHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
  759. {kHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
  760. {kHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
  761. {kHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
  762. {kHost5, TopicsAndWeight({5, 6}, 0.1)},
  763. {kHost6, TopicsAndWeight({6}, 0.1)}});
  764. task_environment_.AdvanceClock(base::Seconds(1));
  765. EpochTopics result = CalculateTopics();
  766. ExpectResultTopicsEqual(
  767. result.top_topics_and_observing_domains(),
  768. {{Topic(6), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
  769. {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
  770. {Topic(4), {HashedDomain(3)}},
  771. {Topic(101), {}},
  772. {Topic(102), {}}});
  773. EXPECT_EQ(result.padded_top_topics_start_index(), 3u);
  774. histograms.ExpectUniqueSample(
  775. "BrowsingTopics.EpochTopicsCalculation.EligibleDistinctHistoryHostsCount",
  776. /*sample=*/3,
  777. /*expected_bucket_count=*/1);
  778. histograms.ExpectUniqueSample(
  779. "BrowsingTopics.EpochTopicsCalculation.TopTopicsCountBeforePadding",
  780. /*sample=*/3,
  781. /*expected_bucket_count=*/1);
  782. histograms.ExpectTotalCount(
  783. "BrowsingTopics.EpochTopicsCalculation."
  784. "ObservationContextDomainsCountPerTopTopic",
  785. /*count=*/5);
  786. histograms.ExpectBucketCount(
  787. "BrowsingTopics.EpochTopicsCalculation."
  788. "ObservationContextDomainsCountPerTopTopic",
  789. /*sample=*/0,
  790. /*expected_count=*/2);
  791. histograms.ExpectBucketCount(
  792. "BrowsingTopics.EpochTopicsCalculation."
  793. "ObservationContextDomainsCountPerTopTopic",
  794. /*sample=*/1,
  795. /*expected_count=*/1);
  796. histograms.ExpectBucketCount(
  797. "BrowsingTopics.EpochTopicsCalculation."
  798. "ObservationContextDomainsCountPerTopTopic",
  799. /*sample=*/3,
  800. /*expected_count=*/2);
  801. }
  802. } // namespace browsing_topics