browsing_topics_state_unittest.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  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_state.h"
  5. #include "base/base64.h"
  6. #include "base/callback_helpers.h"
  7. #include "base/files/file_util.h"
  8. #include "base/files/scoped_temp_dir.h"
  9. #include "base/json/json_file_value_serializer.h"
  10. #include "base/json/values_util.h"
  11. #include "base/strings/strcat.h"
  12. #include "base/test/metrics/histogram_tester.h"
  13. #include "base/test/scoped_feature_list.h"
  14. #include "base/test/task_environment.h"
  15. #include "components/browsing_topics/util.h"
  16. #include "testing/gtest/include/gtest/gtest.h"
  17. #include "third_party/blink/public/common/features.h"
  18. namespace browsing_topics {
  19. namespace {
  20. constexpr base::Time kTime1 =
  21. base::Time::FromDeltaSinceWindowsEpoch(base::Days(1));
  22. constexpr base::Time kTime2 =
  23. base::Time::FromDeltaSinceWindowsEpoch(base::Days(2));
  24. constexpr base::Time kTime3 =
  25. base::Time::FromDeltaSinceWindowsEpoch(base::Days(3));
  26. constexpr base::Time kTime4 =
  27. base::Time::FromDeltaSinceWindowsEpoch(base::Days(4));
  28. constexpr base::Time kTime5 =
  29. base::Time::FromDeltaSinceWindowsEpoch(base::Days(5));
  30. constexpr browsing_topics::HmacKey kZeroKey = {};
  31. constexpr browsing_topics::HmacKey kTestKey = {1};
  32. constexpr browsing_topics::HmacKey kTestKey2 = {2};
  33. constexpr size_t kTaxonomySize = 349;
  34. constexpr int kTaxonomyVersion = 1;
  35. constexpr int64_t kModelVersion = 2;
  36. constexpr size_t kPaddedTopTopicsStartIndex = 3;
  37. EpochTopics CreateTestEpochTopics(base::Time calculation_time) {
  38. std::vector<TopicAndDomains> top_topics_and_observing_domains;
  39. top_topics_and_observing_domains.emplace_back(
  40. TopicAndDomains(Topic(1), {HashedDomain(1)}));
  41. top_topics_and_observing_domains.emplace_back(
  42. TopicAndDomains(Topic(2), {HashedDomain(1), HashedDomain(2)}));
  43. top_topics_and_observing_domains.emplace_back(
  44. TopicAndDomains(Topic(3), {HashedDomain(1), HashedDomain(3)}));
  45. top_topics_and_observing_domains.emplace_back(
  46. TopicAndDomains(Topic(4), {HashedDomain(2), HashedDomain(3)}));
  47. top_topics_and_observing_domains.emplace_back(
  48. TopicAndDomains(Topic(5), {HashedDomain(1)}));
  49. EpochTopics epoch_topics(std::move(top_topics_and_observing_domains),
  50. kPaddedTopTopicsStartIndex, kTaxonomySize,
  51. kTaxonomyVersion, kModelVersion, calculation_time);
  52. return epoch_topics;
  53. }
  54. } // namespace
  55. class BrowsingTopicsStateTest : public testing::Test {
  56. public:
  57. BrowsingTopicsStateTest()
  58. : task_environment_(new base::test::TaskEnvironment(
  59. base::test::TaskEnvironment::TimeSource::MOCK_TIME)) {
  60. feature_list_.InitAndEnableFeatureWithParameters(
  61. blink::features::kBrowsingTopics, {{"config_version", "123"}});
  62. OverrideHmacKeyForTesting(kTestKey);
  63. EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
  64. }
  65. base::FilePath TestFilePath() {
  66. return temp_dir_.GetPath().Append(FILE_PATH_LITERAL("BrowsingTopicsState"));
  67. }
  68. std::string GetTestFileContent() {
  69. JSONFileValueDeserializer deserializer(TestFilePath());
  70. std::unique_ptr<base::Value> value = deserializer.Deserialize(
  71. /*error_code=*/nullptr,
  72. /*error_message=*/nullptr);
  73. EXPECT_TRUE(value);
  74. return base::CollapseWhitespaceASCII(value->DebugString(), true);
  75. }
  76. void CreateOrOverrideTestFile(std::vector<EpochTopics> epochs,
  77. base::Time next_scheduled_calculation_time,
  78. std::string hex_encoded_hmac_key,
  79. int config_version) {
  80. base::Value::List epochs_list;
  81. for (const EpochTopics& epoch : epochs) {
  82. epochs_list.Append(epoch.ToDictValue());
  83. }
  84. base::Value::Dict dict;
  85. dict.Set("epochs", std::move(epochs_list));
  86. dict.Set("next_scheduled_calculation_time",
  87. base::TimeToValue(next_scheduled_calculation_time));
  88. dict.Set("hex_encoded_hmac_key", std::move(hex_encoded_hmac_key));
  89. dict.Set("config_version", config_version);
  90. JSONFileValueSerializer(TestFilePath()).Serialize(dict);
  91. }
  92. void OnBrowsingTopicsStateLoaded() { observed_state_loaded_ = true; }
  93. bool observed_state_loaded() const { return observed_state_loaded_; }
  94. protected:
  95. base::test::ScopedFeatureList feature_list_;
  96. std::unique_ptr<base::test::TaskEnvironment> task_environment_;
  97. base::ScopedTempDir temp_dir_;
  98. bool observed_state_loaded_ = false;
  99. };
  100. TEST_F(BrowsingTopicsStateTest, InitFromNoFile_SaveToDiskAfterDelay) {
  101. base::HistogramTester histograms;
  102. BrowsingTopicsState state(
  103. temp_dir_.GetPath(),
  104. base::BindOnce(&BrowsingTopicsStateTest::OnBrowsingTopicsStateLoaded,
  105. base::Unretained(this)));
  106. EXPECT_FALSE(state.HasScheduledSaveForTesting());
  107. EXPECT_FALSE(observed_state_loaded());
  108. // UMA should not be recorded yet.
  109. histograms.ExpectTotalCount(
  110. "BrowsingTopics.BrowsingTopicsState.LoadFinishStatus", 0);
  111. // Let the backend file read task finish.
  112. task_environment_->RunUntilIdle();
  113. histograms.ExpectUniqueSample(
  114. "BrowsingTopics.BrowsingTopicsState.LoadFinishStatus", true,
  115. /*expected_bucket_count=*/1);
  116. EXPECT_TRUE(state.epochs().empty());
  117. EXPECT_TRUE(state.next_scheduled_calculation_time().is_null());
  118. EXPECT_TRUE(std::equal(state.hmac_key().begin(), state.hmac_key().end(),
  119. kTestKey.begin()));
  120. EXPECT_TRUE(state.HasScheduledSaveForTesting());
  121. EXPECT_TRUE(observed_state_loaded());
  122. // Advance clock until immediately before saving takes place.
  123. task_environment_->FastForwardBy(base::Milliseconds(2499));
  124. EXPECT_TRUE(state.HasScheduledSaveForTesting());
  125. EXPECT_FALSE(base::PathExists(TestFilePath()));
  126. // Advance clock past the saving moment.
  127. task_environment_->FastForwardBy(base::Milliseconds(1));
  128. EXPECT_FALSE(state.HasScheduledSaveForTesting());
  129. EXPECT_TRUE(base::PathExists(TestFilePath()));
  130. EXPECT_EQ(
  131. GetTestFileContent(),
  132. "{\"config_version\": 123,\"epochs\": [ ],\"hex_encoded_hmac_key\": "
  133. "\"0100000000000000000000000000000000000000000000000000000000000000\","
  134. "\"next_scheduled_calculation_time\": \"0\"}");
  135. }
  136. TEST_F(BrowsingTopicsStateTest,
  137. UpdateNextScheduledCalculationTime_SaveToDiskAfterDelay) {
  138. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  139. task_environment_->FastForwardBy(base::Milliseconds(3000));
  140. EXPECT_FALSE(state.HasScheduledSaveForTesting());
  141. state.UpdateNextScheduledCalculationTime();
  142. EXPECT_TRUE(state.epochs().empty());
  143. EXPECT_EQ(state.next_scheduled_calculation_time(),
  144. base::Time::Now() + base::Days(7));
  145. EXPECT_TRUE(std::equal(state.hmac_key().begin(), state.hmac_key().end(),
  146. kTestKey.begin()));
  147. EXPECT_TRUE(state.HasScheduledSaveForTesting());
  148. task_environment_->FastForwardBy(base::Milliseconds(2499));
  149. EXPECT_TRUE(state.HasScheduledSaveForTesting());
  150. task_environment_->FastForwardBy(base::Milliseconds(1));
  151. EXPECT_FALSE(state.HasScheduledSaveForTesting());
  152. std::string expected_content = base::StrCat(
  153. {"{\"config_version\": 123,\"epochs\": [ ],\"hex_encoded_hmac_key\": "
  154. "\"0100000000000000000000000000000000000000000000000000000000000000"
  155. "\",\"next_scheduled_calculation_time\": \"",
  156. base::NumberToString(state.next_scheduled_calculation_time()
  157. .ToDeltaSinceWindowsEpoch()
  158. .InMicroseconds()),
  159. "\"}"});
  160. EXPECT_EQ(GetTestFileContent(), expected_content);
  161. }
  162. TEST_F(BrowsingTopicsStateTest, AddEpoch) {
  163. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  164. task_environment_->RunUntilIdle();
  165. // Successful topics calculation at `kTime1`.
  166. state.AddEpoch(CreateTestEpochTopics(kTime1));
  167. EXPECT_EQ(state.epochs().size(), 1u);
  168. EXPECT_FALSE(state.epochs()[0].empty());
  169. EXPECT_EQ(state.epochs()[0].calculation_time(), kTime1);
  170. // Successful topics calculation at `kTime2`.
  171. state.AddEpoch(CreateTestEpochTopics(kTime2));
  172. EXPECT_EQ(state.epochs().size(), 2u);
  173. EXPECT_FALSE(state.epochs()[0].empty());
  174. EXPECT_EQ(state.epochs()[0].calculation_time(), kTime1);
  175. EXPECT_FALSE(state.epochs()[1].empty());
  176. EXPECT_EQ(state.epochs()[1].calculation_time(), kTime2);
  177. // Failed topics calculation.
  178. state.AddEpoch(EpochTopics(kTime3));
  179. EXPECT_EQ(state.epochs().size(), 3u);
  180. EXPECT_FALSE(state.epochs()[0].empty());
  181. EXPECT_EQ(state.epochs()[0].calculation_time(), kTime1);
  182. EXPECT_FALSE(state.epochs()[1].empty());
  183. EXPECT_EQ(state.epochs()[1].calculation_time(), kTime2);
  184. EXPECT_TRUE(state.epochs()[2].empty());
  185. EXPECT_EQ(state.epochs()[2].calculation_time(), kTime3);
  186. // Successful topics calculation at `kTime4`.
  187. state.AddEpoch(CreateTestEpochTopics(kTime4));
  188. EXPECT_EQ(state.epochs().size(), 4u);
  189. EXPECT_FALSE(state.epochs()[0].empty());
  190. EXPECT_EQ(state.epochs()[0].calculation_time(), kTime1);
  191. EXPECT_FALSE(state.epochs()[1].empty());
  192. EXPECT_EQ(state.epochs()[1].calculation_time(), kTime2);
  193. EXPECT_TRUE(state.epochs()[2].empty());
  194. EXPECT_FALSE(state.epochs()[3].empty());
  195. EXPECT_EQ(state.epochs()[3].calculation_time(), kTime4);
  196. // Successful topics calculation at `kTime5`. When this epoch is added, the
  197. // first one should be evicted.
  198. state.AddEpoch(CreateTestEpochTopics(kTime5));
  199. EXPECT_EQ(state.epochs().size(), 4u);
  200. EXPECT_FALSE(state.epochs()[0].empty());
  201. EXPECT_EQ(state.epochs()[0].calculation_time(), kTime2);
  202. EXPECT_TRUE(state.epochs()[1].empty());
  203. EXPECT_FALSE(state.epochs()[2].empty());
  204. EXPECT_EQ(state.epochs()[2].calculation_time(), kTime4);
  205. EXPECT_FALSE(state.epochs()[3].empty());
  206. EXPECT_EQ(state.epochs()[3].calculation_time(), kTime5);
  207. // The `next_scheduled_calculation_time` and `hmac_key` are unaffected.
  208. EXPECT_EQ(state.next_scheduled_calculation_time(), base::Time());
  209. EXPECT_TRUE(std::equal(state.hmac_key().begin(), state.hmac_key().end(),
  210. kTestKey.begin()));
  211. }
  212. TEST_F(BrowsingTopicsStateTest, EpochsForSite_Empty) {
  213. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  214. task_environment_->RunUntilIdle();
  215. EXPECT_TRUE(state.EpochsForSite(/*top_domain=*/"foo.com").empty());
  216. }
  217. TEST_F(BrowsingTopicsStateTest, EpochsForSite_OneEpoch_SwitchTimeNotArrived) {
  218. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  219. task_environment_->RunUntilIdle();
  220. state.AddEpoch(CreateTestEpochTopics(kTime1));
  221. state.UpdateNextScheduledCalculationTime();
  222. ASSERT_LT(state.CalculateSiteStickyTimeDelta("foo.com") + base::Hours(1),
  223. base::Days(7));
  224. task_environment_->FastForwardBy(base::Hours(1));
  225. EXPECT_TRUE(state.EpochsForSite(/*top_domain=*/"foo.com").empty());
  226. }
  227. TEST_F(BrowsingTopicsStateTest, EpochsForSite_OneEpoch_SwitchTimeArrived) {
  228. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  229. task_environment_->RunUntilIdle();
  230. state.AddEpoch(CreateTestEpochTopics(kTime1));
  231. state.UpdateNextScheduledCalculationTime();
  232. ASSERT_GT(state.CalculateSiteStickyTimeDelta("foo.com") + base::Days(1),
  233. base::Days(7));
  234. task_environment_->FastForwardBy(base::Days(1));
  235. std::vector<const EpochTopics*> epochs_for_site =
  236. state.EpochsForSite(/*top_domain=*/"foo.com");
  237. EXPECT_EQ(epochs_for_site.size(), 1u);
  238. EXPECT_EQ(epochs_for_site[0], &state.epochs()[0]);
  239. }
  240. TEST_F(BrowsingTopicsStateTest,
  241. EpochsForSite_ThreeEpochs_SwitchTimeNotArrived) {
  242. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  243. task_environment_->RunUntilIdle();
  244. state.AddEpoch(CreateTestEpochTopics(kTime1));
  245. state.AddEpoch(CreateTestEpochTopics(kTime2));
  246. state.AddEpoch(CreateTestEpochTopics(kTime3));
  247. state.UpdateNextScheduledCalculationTime();
  248. task_environment_->FastForwardBy(base::Hours(1));
  249. std::vector<const EpochTopics*> epochs_for_site =
  250. state.EpochsForSite(/*top_domain=*/"foo.com");
  251. EXPECT_EQ(epochs_for_site.size(), 2u);
  252. EXPECT_EQ(epochs_for_site[0], &state.epochs()[0]);
  253. EXPECT_EQ(epochs_for_site[1], &state.epochs()[1]);
  254. }
  255. TEST_F(BrowsingTopicsStateTest, EpochsForSite_ThreeEpochs_SwitchTimeArrived) {
  256. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  257. task_environment_->RunUntilIdle();
  258. state.AddEpoch(CreateTestEpochTopics(kTime1));
  259. state.AddEpoch(CreateTestEpochTopics(kTime2));
  260. state.AddEpoch(CreateTestEpochTopics(kTime3));
  261. state.UpdateNextScheduledCalculationTime();
  262. task_environment_->FastForwardBy(base::Days(1));
  263. std::vector<const EpochTopics*> epochs_for_site =
  264. state.EpochsForSite(/*top_domain=*/"foo.com");
  265. EXPECT_EQ(epochs_for_site.size(), 3u);
  266. EXPECT_EQ(epochs_for_site[0], &state.epochs()[0]);
  267. EXPECT_EQ(epochs_for_site[1], &state.epochs()[1]);
  268. EXPECT_EQ(epochs_for_site[2], &state.epochs()[2]);
  269. }
  270. TEST_F(BrowsingTopicsStateTest, EpochsForSite_FourEpochs_SwitchTimeNotArrived) {
  271. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  272. task_environment_->RunUntilIdle();
  273. state.AddEpoch(CreateTestEpochTopics(kTime1));
  274. state.AddEpoch(CreateTestEpochTopics(kTime2));
  275. state.AddEpoch(CreateTestEpochTopics(kTime3));
  276. state.AddEpoch(CreateTestEpochTopics(kTime4));
  277. state.UpdateNextScheduledCalculationTime();
  278. task_environment_->FastForwardBy(base::Hours(1));
  279. std::vector<const EpochTopics*> epochs_for_site =
  280. state.EpochsForSite(/*top_domain=*/"foo.com");
  281. EXPECT_EQ(epochs_for_site.size(), 3u);
  282. EXPECT_EQ(epochs_for_site[0], &state.epochs()[0]);
  283. EXPECT_EQ(epochs_for_site[1], &state.epochs()[1]);
  284. EXPECT_EQ(epochs_for_site[2], &state.epochs()[2]);
  285. }
  286. TEST_F(BrowsingTopicsStateTest, EpochsForSite_FourEpochs_SwitchTimeArrived) {
  287. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  288. task_environment_->RunUntilIdle();
  289. state.AddEpoch(CreateTestEpochTopics(kTime1));
  290. state.AddEpoch(CreateTestEpochTopics(kTime2));
  291. state.AddEpoch(CreateTestEpochTopics(kTime3));
  292. state.AddEpoch(CreateTestEpochTopics(kTime4));
  293. state.UpdateNextScheduledCalculationTime();
  294. task_environment_->FastForwardBy(base::Days(1));
  295. std::vector<const EpochTopics*> epochs_for_site =
  296. state.EpochsForSite(/*top_domain=*/"foo.com");
  297. EXPECT_EQ(epochs_for_site.size(), 3u);
  298. EXPECT_EQ(epochs_for_site[0], &state.epochs()[1]);
  299. EXPECT_EQ(epochs_for_site[1], &state.epochs()[2]);
  300. EXPECT_EQ(epochs_for_site[2], &state.epochs()[3]);
  301. }
  302. TEST_F(BrowsingTopicsStateTest, InitFromPreexistingFile_CorruptedHmacKey) {
  303. base::HistogramTester histograms;
  304. std::vector<EpochTopics> epochs;
  305. epochs.emplace_back(CreateTestEpochTopics(kTime1));
  306. CreateOrOverrideTestFile(std::move(epochs),
  307. /*next_scheduled_calculation_time=*/kTime2,
  308. /*hex_encoded_hmac_key=*/"123",
  309. /*config_version=*/123);
  310. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  311. task_environment_->RunUntilIdle();
  312. EXPECT_EQ(state.epochs().size(), 0u);
  313. EXPECT_TRUE(state.next_scheduled_calculation_time().is_null());
  314. EXPECT_TRUE(std::equal(state.hmac_key().begin(), state.hmac_key().end(),
  315. kZeroKey.begin()));
  316. histograms.ExpectUniqueSample(
  317. "BrowsingTopics.BrowsingTopicsState.LoadFinishStatus", false,
  318. /*expected_bucket_count=*/1);
  319. }
  320. TEST_F(BrowsingTopicsStateTest, InitFromPreexistingFile_SameConfigVersion) {
  321. base::HistogramTester histograms;
  322. std::vector<EpochTopics> epochs;
  323. epochs.emplace_back(CreateTestEpochTopics(kTime1));
  324. CreateOrOverrideTestFile(std::move(epochs),
  325. /*next_scheduled_calculation_time=*/kTime2,
  326. /*hex_encoded_hmac_key=*/base::HexEncode(kTestKey2),
  327. /*config_version=*/123);
  328. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  329. task_environment_->RunUntilIdle();
  330. EXPECT_EQ(state.epochs().size(), 1u);
  331. EXPECT_FALSE(state.epochs()[0].empty());
  332. EXPECT_EQ(state.epochs()[0].model_version(), kModelVersion);
  333. EXPECT_EQ(state.next_scheduled_calculation_time(), kTime2);
  334. EXPECT_TRUE(std::equal(state.hmac_key().begin(), state.hmac_key().end(),
  335. kTestKey2.begin()));
  336. histograms.ExpectUniqueSample(
  337. "BrowsingTopics.BrowsingTopicsState.LoadFinishStatus", true,
  338. /*expected_bucket_count=*/1);
  339. }
  340. TEST_F(BrowsingTopicsStateTest,
  341. InitFromPreexistingFile_DifferentConfigVersion) {
  342. base::HistogramTester histograms;
  343. std::vector<EpochTopics> epochs;
  344. epochs.emplace_back(CreateTestEpochTopics(kTime1));
  345. CreateOrOverrideTestFile(std::move(epochs),
  346. /*next_scheduled_calculation_time=*/kTime2,
  347. /*hex_encoded_hmac_key=*/base::HexEncode(kTestKey2),
  348. /*config_version=*/100);
  349. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  350. task_environment_->RunUntilIdle();
  351. EXPECT_TRUE(state.epochs().empty());
  352. EXPECT_TRUE(state.next_scheduled_calculation_time().is_null());
  353. EXPECT_TRUE(std::equal(state.hmac_key().begin(), state.hmac_key().end(),
  354. kTestKey2.begin()));
  355. histograms.ExpectUniqueSample(
  356. "BrowsingTopics.BrowsingTopicsState.LoadFinishStatus", true,
  357. /*expected_bucket_count=*/1);
  358. }
  359. TEST_F(BrowsingTopicsStateTest, ClearOneEpoch) {
  360. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  361. task_environment_->RunUntilIdle();
  362. state.AddEpoch(CreateTestEpochTopics(kTime1));
  363. EXPECT_EQ(state.epochs().size(), 1u);
  364. EXPECT_FALSE(state.epochs()[0].empty());
  365. EXPECT_EQ(state.epochs()[0].calculation_time(), kTime1);
  366. state.AddEpoch(CreateTestEpochTopics(kTime2));
  367. EXPECT_EQ(state.epochs().size(), 2u);
  368. EXPECT_FALSE(state.epochs()[0].empty());
  369. EXPECT_EQ(state.epochs()[0].calculation_time(), kTime1);
  370. EXPECT_FALSE(state.epochs()[1].empty());
  371. EXPECT_EQ(state.epochs()[1].calculation_time(), kTime2);
  372. state.ClearOneEpoch(/*epoch_index=*/0);
  373. EXPECT_EQ(state.epochs().size(), 2u);
  374. EXPECT_TRUE(state.epochs()[0].empty());
  375. EXPECT_FALSE(state.epochs()[1].empty());
  376. EXPECT_EQ(state.epochs()[1].calculation_time(), kTime2);
  377. state.UpdateNextScheduledCalculationTime();
  378. EXPECT_EQ(state.next_scheduled_calculation_time(),
  379. base::Time::Now() + base::Days(7));
  380. EXPECT_TRUE(std::equal(state.hmac_key().begin(), state.hmac_key().end(),
  381. kTestKey.begin()));
  382. }
  383. TEST_F(BrowsingTopicsStateTest, ClearAllTopics) {
  384. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  385. task_environment_->RunUntilIdle();
  386. state.AddEpoch(CreateTestEpochTopics(kTime1));
  387. EXPECT_EQ(state.epochs().size(), 1u);
  388. EXPECT_FALSE(state.epochs()[0].empty());
  389. EXPECT_EQ(state.epochs()[0].calculation_time(), kTime1);
  390. state.AddEpoch(CreateTestEpochTopics(kTime2));
  391. EXPECT_EQ(state.epochs().size(), 2u);
  392. EXPECT_FALSE(state.epochs()[0].empty());
  393. EXPECT_EQ(state.epochs()[0].calculation_time(), kTime1);
  394. EXPECT_FALSE(state.epochs()[1].empty());
  395. EXPECT_EQ(state.epochs()[1].calculation_time(), kTime2);
  396. state.UpdateNextScheduledCalculationTime();
  397. state.ClearAllTopics();
  398. EXPECT_EQ(state.epochs().size(), 0u);
  399. EXPECT_EQ(state.next_scheduled_calculation_time(),
  400. base::Time::Now() + base::Days(7));
  401. EXPECT_TRUE(std::equal(state.hmac_key().begin(), state.hmac_key().end(),
  402. kTestKey.begin()));
  403. }
  404. TEST_F(BrowsingTopicsStateTest, ClearTopic) {
  405. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  406. task_environment_->RunUntilIdle();
  407. state.AddEpoch(CreateTestEpochTopics(kTime1));
  408. state.AddEpoch(CreateTestEpochTopics(kTime2));
  409. state.UpdateNextScheduledCalculationTime();
  410. state.ClearTopic(Topic(3), kTaxonomyVersion);
  411. EXPECT_EQ(state.epochs().size(), 2u);
  412. EXPECT_EQ(state.epochs()[0].top_topics_and_observing_domains()[0].topic(),
  413. Topic(1));
  414. EXPECT_EQ(state.epochs()[0].top_topics_and_observing_domains()[1].topic(),
  415. Topic(2));
  416. EXPECT_FALSE(
  417. state.epochs()[0].top_topics_and_observing_domains()[2].IsValid());
  418. EXPECT_EQ(state.epochs()[0].top_topics_and_observing_domains()[3].topic(),
  419. Topic(4));
  420. EXPECT_EQ(state.epochs()[0].top_topics_and_observing_domains()[4].topic(),
  421. Topic(5));
  422. EXPECT_EQ(state.epochs()[1].top_topics_and_observing_domains()[0].topic(),
  423. Topic(1));
  424. EXPECT_EQ(state.epochs()[1].top_topics_and_observing_domains()[1].topic(),
  425. Topic(2));
  426. EXPECT_FALSE(
  427. state.epochs()[1].top_topics_and_observing_domains()[2].IsValid());
  428. EXPECT_EQ(state.epochs()[1].top_topics_and_observing_domains()[3].topic(),
  429. Topic(4));
  430. EXPECT_EQ(state.epochs()[1].top_topics_and_observing_domains()[4].topic(),
  431. Topic(5));
  432. }
  433. TEST_F(BrowsingTopicsStateTest, ClearContextDomain) {
  434. BrowsingTopicsState state(temp_dir_.GetPath(), base::DoNothing());
  435. task_environment_->RunUntilIdle();
  436. state.AddEpoch(CreateTestEpochTopics(kTime1));
  437. state.AddEpoch(CreateTestEpochTopics(kTime2));
  438. state.UpdateNextScheduledCalculationTime();
  439. state.ClearContextDomain(HashedDomain(1));
  440. EXPECT_EQ(
  441. state.epochs()[0].top_topics_and_observing_domains()[0].hashed_domains(),
  442. std::set<HashedDomain>{});
  443. EXPECT_EQ(
  444. state.epochs()[0].top_topics_and_observing_domains()[1].hashed_domains(),
  445. std::set<HashedDomain>({HashedDomain(2)}));
  446. EXPECT_EQ(
  447. state.epochs()[0].top_topics_and_observing_domains()[2].hashed_domains(),
  448. std::set<HashedDomain>({HashedDomain(3)}));
  449. EXPECT_EQ(
  450. state.epochs()[0].top_topics_and_observing_domains()[3].hashed_domains(),
  451. std::set<HashedDomain>({HashedDomain(2), HashedDomain(3)}));
  452. EXPECT_EQ(
  453. state.epochs()[0].top_topics_and_observing_domains()[4].hashed_domains(),
  454. std::set<HashedDomain>{});
  455. EXPECT_EQ(
  456. state.epochs()[1].top_topics_and_observing_domains()[0].hashed_domains(),
  457. std::set<HashedDomain>{});
  458. EXPECT_EQ(
  459. state.epochs()[1].top_topics_and_observing_domains()[1].hashed_domains(),
  460. std::set<HashedDomain>({HashedDomain(2)}));
  461. EXPECT_EQ(
  462. state.epochs()[1].top_topics_and_observing_domains()[2].hashed_domains(),
  463. std::set<HashedDomain>({HashedDomain(3)}));
  464. EXPECT_EQ(
  465. state.epochs()[1].top_topics_and_observing_domains()[3].hashed_domains(),
  466. std::set<HashedDomain>({HashedDomain(2), HashedDomain(3)}));
  467. EXPECT_EQ(
  468. state.epochs()[1].top_topics_and_observing_domains()[4].hashed_domains(),
  469. std::set<HashedDomain>{});
  470. }
  471. TEST_F(BrowsingTopicsStateTest, ShouldSaveFileDespiteShutdownWhileScheduled) {
  472. auto state = std::make_unique<BrowsingTopicsState>(temp_dir_.GetPath(),
  473. base::DoNothing());
  474. task_environment_->RunUntilIdle();
  475. ASSERT_TRUE(state->HasScheduledSaveForTesting());
  476. EXPECT_FALSE(base::PathExists(TestFilePath()));
  477. state.reset();
  478. task_environment_.reset();
  479. // TaskEnvironment and BrowsingTopicsState both have been destroyed, mimic-ing
  480. // a browser shutdown.
  481. EXPECT_TRUE(base::PathExists(TestFilePath()));
  482. EXPECT_EQ(
  483. GetTestFileContent(),
  484. "{\"config_version\": 123,\"epochs\": [ ],\"hex_encoded_hmac_key\": "
  485. "\"0100000000000000000000000000000000000000000000000000000000000000\","
  486. "\"next_scheduled_calculation_time\": \"0\"}");
  487. }
  488. } // namespace browsing_topics