optimization_guide_store_unittest.cc 100 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648
  1. // Copyright 2019 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/optimization_guide/core/optimization_guide_store.h"
  5. #include <string>
  6. #include <vector>
  7. #include "base/bind.h"
  8. #include "base/files/file_util.h"
  9. #include "base/files/scoped_temp_dir.h"
  10. #include "base/memory/raw_ptr.h"
  11. #include "base/strings/string_number_conversions.h"
  12. #include "base/test/metrics/histogram_tester.h"
  13. #include "base/test/task_environment.h"
  14. #include "components/leveldb_proto/testing/fake_db.h"
  15. #include "components/optimization_guide/core/model_util.h"
  16. #include "components/optimization_guide/core/optimization_guide_features.h"
  17. #include "components/optimization_guide/core/optimization_guide_prefs.h"
  18. #include "components/optimization_guide/core/optimization_guide_util.h"
  19. #include "components/optimization_guide/core/store_update_data.h"
  20. #include "components/optimization_guide/proto/hint_cache.pb.h"
  21. #include "components/optimization_guide/proto/models.pb.h"
  22. #include "components/prefs/pref_registry_simple.h"
  23. #include "components/prefs/scoped_user_pref_update.h"
  24. #include "components/sync_preferences/testing_pref_service_syncable.h"
  25. #include "testing/gmock/include/gmock/gmock.h"
  26. #include "testing/gtest/include/gtest/gtest.h"
  27. #include "third_party/abseil-cpp/absl/types/optional.h"
  28. #include "url/gurl.h"
  29. using leveldb_proto::test::FakeDB;
  30. using testing::Mock;
  31. namespace optimization_guide {
  32. namespace {
  33. constexpr char kDefaultComponentVersion[] = "1.0.0";
  34. constexpr char kUpdateComponentVersion[] = "2.0.0";
  35. std::string GetHost(size_t id) {
  36. // Host alternates between two different domain types depending on
  37. // whether the id is odd or even.
  38. if (id % 2 == 0) {
  39. return "domain" + base::NumberToString(id) + ".org";
  40. } else {
  41. return "different.domain" + base::NumberToString(id) + ".co.in";
  42. }
  43. }
  44. enum class MetadataSchemaState {
  45. kMissing,
  46. kInvalid,
  47. kValid,
  48. };
  49. std::unique_ptr<proto::PredictionModel> CreatePredictionModel() {
  50. std::unique_ptr<optimization_guide::proto::PredictionModel> prediction_model =
  51. std::make_unique<optimization_guide::proto::PredictionModel>();
  52. optimization_guide::proto::ModelInfo* model_info =
  53. prediction_model->mutable_model_info();
  54. model_info->set_version(1);
  55. model_info->set_optimization_target(
  56. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
  57. model_info->add_supported_model_engine_versions(
  58. proto::ModelEngineVersion::MODEL_ENGINE_VERSION_DECISION_TREE);
  59. return prediction_model;
  60. }
  61. } // namespace
  62. class OptimizationGuideStoreTest : public testing::Test {
  63. public:
  64. using StoreEntry = proto::StoreEntry;
  65. using StoreEntryMap = std::map<OptimizationGuideStore::EntryKey, StoreEntry>;
  66. OptimizationGuideStoreTest() = default;
  67. OptimizationGuideStoreTest(const OptimizationGuideStoreTest&) = delete;
  68. OptimizationGuideStoreTest& operator=(const OptimizationGuideStoreTest&) =
  69. delete;
  70. void SetUp() override {
  71. ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
  72. pref_service_ = std::make_unique<TestingPrefServiceSimple>();
  73. prefs::RegisterProfilePrefs(pref_service_->registry());
  74. }
  75. void TearDown() override {
  76. last_loaded_hint_.reset();
  77. pref_service_.reset();
  78. }
  79. // Initializes the entries contained within the database on startup.
  80. void SeedInitialData(
  81. MetadataSchemaState state,
  82. absl::optional<size_t> component_hint_count = absl::optional<size_t>(),
  83. absl::optional<base::Time> fetched_hints_update =
  84. absl::optional<base::Time>()) {
  85. db_store_.clear();
  86. // Add a metadata schema entry if its state isn't kMissing. The version
  87. // entry version is set to the store's current version if the state is
  88. // kValid; otherwise, it's set to the invalid version of "0".
  89. if (state == MetadataSchemaState::kValid) {
  90. db_store_[OptimizationGuideStore::GetMetadataTypeEntryKey(
  91. OptimizationGuideStore::MetadataType::kSchema)]
  92. .set_version(OptimizationGuideStore::kStoreSchemaVersion);
  93. } else if (state == MetadataSchemaState::kInvalid) {
  94. db_store_[OptimizationGuideStore::GetMetadataTypeEntryKey(
  95. OptimizationGuideStore::MetadataType::kSchema)]
  96. .set_version("0");
  97. }
  98. // If the database is being seeded with component hints, it is indicated
  99. // with a provided count. Add the component metadata with the default
  100. // component version and then add the indicated number of component hints.
  101. // if (component_hint_count && component_hint_count >
  102. // static_cast<size_t>(0)) {
  103. if (component_hint_count && component_hint_count > 0u) {
  104. db_store_[OptimizationGuideStore::GetMetadataTypeEntryKey(
  105. OptimizationGuideStore::MetadataType::kComponent)]
  106. .set_version(kDefaultComponentVersion);
  107. OptimizationGuideStore::EntryKeyPrefix component_hint_key_prefix =
  108. OptimizationGuideStore::GetComponentHintEntryKeyPrefix(
  109. base::Version(kDefaultComponentVersion));
  110. for (size_t i = 0; i < component_hint_count.value(); ++i) {
  111. std::string host = GetHost(i);
  112. StoreEntry& entry = db_store_[component_hint_key_prefix + host];
  113. entry.set_entry_type(static_cast<proto::StoreEntryType>(
  114. OptimizationGuideStore::StoreEntryType::kComponentHint));
  115. proto::Hint* hint = entry.mutable_hint();
  116. hint->set_key(host);
  117. hint->set_key_representation(proto::HOST);
  118. proto::PageHint* page_hint = hint->add_page_hints();
  119. page_hint->set_page_pattern("page pattern " + base::NumberToString(i));
  120. }
  121. }
  122. if (fetched_hints_update) {
  123. db_store_[OptimizationGuideStore::GetMetadataTypeEntryKey(
  124. OptimizationGuideStore::MetadataType::kFetched)]
  125. .set_update_time_secs(
  126. fetched_hints_update->ToDeltaSinceWindowsEpoch().InSeconds());
  127. }
  128. }
  129. // Moves the specified number of component hints into the update data.
  130. void SeedComponentUpdateData(StoreUpdateData* update_data,
  131. size_t component_hint_count) {
  132. for (size_t i = 0; i < component_hint_count; ++i) {
  133. std::string host = GetHost(i);
  134. proto::Hint hint;
  135. hint.set_key(host);
  136. hint.set_key_representation(proto::HOST);
  137. proto::PageHint* page_hint = hint.add_page_hints();
  138. page_hint->set_page_pattern("page pattern " + base::NumberToString(i));
  139. update_data->MoveHintIntoUpdateData(std::move(hint));
  140. }
  141. }
  142. // Moves the specified number of fetched hints into the update data.
  143. void SeedFetchedUpdateData(StoreUpdateData* update_data,
  144. size_t fetched_hint_count) {
  145. for (size_t i = 0; i < fetched_hint_count; ++i) {
  146. std::string host = GetHost(i);
  147. proto::Hint hint;
  148. hint.set_key(host);
  149. hint.set_key_representation(proto::HOST);
  150. proto::PageHint* page_hint = hint.add_page_hints();
  151. page_hint->set_page_pattern("page pattern " + base::NumberToString(i));
  152. update_data->MoveHintIntoUpdateData(std::move(hint));
  153. }
  154. }
  155. // Moves a prediction model with |optimization_target| into the update data.
  156. void SeedPredictionModelUpdateData(
  157. StoreUpdateData* update_data,
  158. optimization_guide::proto::OptimizationTarget optimization_target,
  159. absl::optional<base::FilePath> model_file_path = absl::nullopt,
  160. absl::optional<proto::ModelInfo> info = absl::nullopt,
  161. absl::optional<base::Time> expiry_time = absl::nullopt) {
  162. std::unique_ptr<optimization_guide::proto::PredictionModel>
  163. prediction_model = CreatePredictionModel();
  164. if (info)
  165. prediction_model->mutable_model_info()->MergeFrom(*info);
  166. prediction_model->mutable_model_info()->set_optimization_target(
  167. optimization_target);
  168. if (expiry_time) {
  169. auto diff = expiry_time.value() - base::Time::Now();
  170. prediction_model->mutable_model_info()
  171. ->mutable_valid_duration()
  172. ->set_seconds(diff.InSeconds());
  173. }
  174. if (model_file_path) {
  175. prediction_model->mutable_model()->set_download_url(
  176. FilePathToString(*model_file_path));
  177. }
  178. update_data->CopyPredictionModelIntoUpdateData(*prediction_model);
  179. }
  180. void CreateDatabase() {
  181. // Reset everything.
  182. db_ = nullptr;
  183. guide_store_.reset();
  184. // Setup the fake db and the class under test.
  185. auto db = std::make_unique<FakeDB<StoreEntry>>(&db_store_);
  186. db_ = db.get();
  187. guide_store_ = std::make_unique<OptimizationGuideStore>(
  188. std::move(db), task_environment_.GetMainThreadTaskRunner(),
  189. pref_service_.get());
  190. }
  191. void InitializeDatabase(bool success, bool purge_existing_data = false) {
  192. EXPECT_CALL(*this, OnInitialized());
  193. guide_store()->Initialize(
  194. purge_existing_data,
  195. base::BindOnce(&OptimizationGuideStoreTest::OnInitialized,
  196. base::Unretained(this)));
  197. // OnDatabaseInitialized callback
  198. db()->InitStatusCallback(success ? leveldb_proto::Enums::kOK
  199. : leveldb_proto::Enums::kError);
  200. }
  201. void InitializeStore(MetadataSchemaState state,
  202. bool purge_existing_data = false) {
  203. InitializeDatabase(true /*=success*/, purge_existing_data);
  204. if (purge_existing_data) {
  205. // OnPurgeDatabase callback
  206. db()->UpdateCallback(true);
  207. return;
  208. }
  209. // OnLoadMetadata callback
  210. db()->LoadCallback(true);
  211. if (state == MetadataSchemaState::kValid) {
  212. // OnLoadEntryKeys callback
  213. db()->LoadCallback(true);
  214. } else {
  215. // OnPurgeDatabase callback
  216. db()->UpdateCallback(true);
  217. }
  218. }
  219. void UpdateComponentHints(std::unique_ptr<StoreUpdateData> component_data,
  220. bool update_success = true,
  221. bool load_hint_entry_keys_success = true) {
  222. EXPECT_CALL(*this, OnUpdateStore());
  223. guide_store()->UpdateComponentHints(
  224. std::move(component_data),
  225. base::BindOnce(&OptimizationGuideStoreTest::OnUpdateStore,
  226. base::Unretained(this)));
  227. // OnUpdateStore callback
  228. db()->UpdateCallback(update_success);
  229. if (update_success) {
  230. // OnLoadEntryKeys callback
  231. db()->LoadCallback(load_hint_entry_keys_success);
  232. }
  233. }
  234. void UpdateFetchedHints(std::unique_ptr<StoreUpdateData> fetched_data,
  235. bool update_success = true,
  236. bool load_hint_entry_keys_success = true) {
  237. EXPECT_CALL(*this, OnUpdateStore());
  238. guide_store()->UpdateFetchedHints(
  239. std::move(fetched_data),
  240. base::BindOnce(&OptimizationGuideStoreTest::OnUpdateStore,
  241. base::Unretained(this)));
  242. // OnUpdateStore callback
  243. db()->UpdateCallback(update_success);
  244. if (update_success) {
  245. // OnLoadEntryKeys callback
  246. db()->LoadCallback(load_hint_entry_keys_success);
  247. }
  248. }
  249. void UpdatePredictionModels(
  250. std::unique_ptr<StoreUpdateData> prediction_models_data,
  251. bool update_success = true,
  252. bool load_prediction_models_entry_keys_success = true) {
  253. EXPECT_CALL(*this, OnUpdateStore());
  254. guide_store()->UpdatePredictionModels(
  255. std::move(prediction_models_data),
  256. base::BindOnce(&OptimizationGuideStoreTest::OnUpdateStore,
  257. base::Unretained(this)));
  258. // OnLoadModelsToBeUpdated callback
  259. db()->LoadCallback(true);
  260. // OnUpdateStore callback
  261. db()->UpdateCallback(update_success);
  262. if (update_success) {
  263. // OnLoadEntryKeys callback
  264. db()->LoadCallback(load_prediction_models_entry_keys_success);
  265. }
  266. }
  267. void ClearFetchedHintsFromDatabase() {
  268. guide_store()->ClearFetchedHintsFromDatabase();
  269. db()->UpdateCallback(true);
  270. db()->LoadCallback(true);
  271. }
  272. void PurgeExpiredFetchedHints() {
  273. guide_store()->PurgeExpiredFetchedHints();
  274. // OnLoadExpiredEntriesToPurge
  275. db()->LoadCallback(true);
  276. // OnUpdateStore
  277. db()->UpdateCallback(true);
  278. // OnLoadEntryKeys callback
  279. db()->LoadCallback(true);
  280. }
  281. void PurgeInactiveModels() {
  282. guide_store()->PurgeInactiveModels();
  283. // OnLoadModelsToBeUpdated
  284. db()->LoadCallback(true);
  285. // OnUpdateStore
  286. db()->UpdateCallback(true);
  287. // OnLoadEntryKeys
  288. db()->LoadCallback(true);
  289. }
  290. bool IsMetadataSchemaEntryKeyPresent() const {
  291. return IsKeyPresent(OptimizationGuideStore::GetMetadataTypeEntryKey(
  292. OptimizationGuideStore::MetadataType::kSchema));
  293. }
  294. // Verifies that the fetched metadata has the expected next update time.
  295. void ExpectFetchedMetadata(base::Time update_time) const {
  296. const auto& metadata_entry =
  297. db_store_.find(OptimizationGuideStore::GetMetadataTypeEntryKey(
  298. OptimizationGuideStore::MetadataType::kFetched));
  299. if (metadata_entry != db_store_.end()) {
  300. // The next update time should have same time up to the second as the
  301. // metadata entry is stored in seconds.
  302. EXPECT_TRUE(base::Time::FromDeltaSinceWindowsEpoch(base::Seconds(
  303. metadata_entry->second.update_time_secs())) -
  304. update_time <
  305. base::Seconds(1));
  306. } else {
  307. FAIL() << "No fetched metadata found";
  308. }
  309. }
  310. // Verifies that the component metadata has the expected version and all
  311. // expected component hints are present.
  312. void ExpectComponentHintsPresent(const std::string& version,
  313. int count) const {
  314. const auto& metadata_entry =
  315. db_store_.find(OptimizationGuideStore::GetMetadataTypeEntryKey(
  316. OptimizationGuideStore::MetadataType::kComponent));
  317. if (metadata_entry != db_store_.end()) {
  318. EXPECT_EQ(metadata_entry->second.version(), version);
  319. } else {
  320. FAIL() << "No component metadata found";
  321. }
  322. OptimizationGuideStore::EntryKeyPrefix component_hint_entry_key_prefix =
  323. OptimizationGuideStore::GetComponentHintEntryKeyPrefix(
  324. base::Version(version));
  325. for (int i = 0; i < count; ++i) {
  326. std::string host = GetHost(i);
  327. OptimizationGuideStore::EntryKey hint_entry_key =
  328. component_hint_entry_key_prefix + host;
  329. const auto& hint_entry = db_store_.find(hint_entry_key);
  330. if (hint_entry == db_store_.end()) {
  331. FAIL() << "No entry found for component hint: " << hint_entry_key;
  332. }
  333. if (!hint_entry->second.has_hint()) {
  334. FAIL() << "Component hint entry does not have hint: " << hint_entry_key;
  335. }
  336. EXPECT_EQ(hint_entry->second.hint().key(), host);
  337. }
  338. }
  339. // Returns true if the data is present for the given key.
  340. bool IsKeyPresent(const OptimizationGuideStore::EntryKey& entry_key) const {
  341. return db_store_.find(entry_key) != db_store_.end();
  342. }
  343. size_t GetDBStoreEntryCount() const { return db_store_.size(); }
  344. size_t GetStoreEntryKeyCount() const {
  345. return guide_store_->GetEntryKeyCount();
  346. }
  347. OptimizationGuideStore* guide_store() { return guide_store_.get(); }
  348. FakeDB<proto::StoreEntry>* db() { return db_; }
  349. base::FilePath temp_dir() const { return temp_dir_.GetPath(); }
  350. const OptimizationGuideStore::EntryKey& last_loaded_entry_key() const {
  351. return last_loaded_entry_key_;
  352. }
  353. MemoryHint* last_loaded_hint() { return last_loaded_hint_.get(); }
  354. proto::PredictionModel* last_loaded_prediction_model() {
  355. return last_loaded_prediction_model_.get();
  356. }
  357. void OnHintLoaded(const OptimizationGuideStore::EntryKey& hint_entry_key,
  358. std::unique_ptr<MemoryHint> loaded_hint) {
  359. last_loaded_entry_key_ = hint_entry_key;
  360. last_loaded_hint_ = std::move(loaded_hint);
  361. }
  362. void OnPredictionModelLoaded(
  363. std::unique_ptr<proto::PredictionModel> loaded_prediction_model) {
  364. last_loaded_prediction_model_ = std::move(loaded_prediction_model);
  365. }
  366. bool IsStoreFilesToDeletePrefEmpty() {
  367. DictionaryPrefUpdate pref_update(pref_service_.get(),
  368. prefs::kStoreFilePathsToDelete);
  369. return pref_update.Get()->DictEmpty();
  370. }
  371. void RunUntilIdle() { task_environment_.RunUntilIdle(); }
  372. MOCK_METHOD0(OnInitialized, void());
  373. MOCK_METHOD0(OnUpdateStore, void());
  374. private:
  375. base::test::TaskEnvironment task_environment_;
  376. std::unique_ptr<TestingPrefServiceSimple> pref_service_;
  377. raw_ptr<FakeDB<proto::StoreEntry>> db_;
  378. StoreEntryMap db_store_;
  379. std::unique_ptr<OptimizationGuideStore> guide_store_;
  380. base::ScopedTempDir temp_dir_;
  381. OptimizationGuideStore::EntryKey last_loaded_entry_key_;
  382. std::unique_ptr<MemoryHint> last_loaded_hint_;
  383. std::unique_ptr<proto::PredictionModel> last_loaded_prediction_model_;
  384. };
  385. TEST_F(OptimizationGuideStoreTest, NoInitialization) {
  386. base::HistogramTester histogram_tester;
  387. SeedInitialData(MetadataSchemaState::kMissing);
  388. CreateDatabase();
  389. histogram_tester.ExpectTotalCount(
  390. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult", 0);
  391. histogram_tester.ExpectBucketCount(
  392. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  393. 1);
  394. histogram_tester.ExpectBucketCount(
  395. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  396. 0);
  397. histogram_tester.ExpectBucketCount(
  398. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 0);
  399. histogram_tester.ExpectBucketCount(
  400. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 0);
  401. }
  402. TEST_F(OptimizationGuideStoreTest,
  403. InitializeFailedOnInitializeWithNoInitialData) {
  404. base::HistogramTester histogram_tester;
  405. SeedInitialData(MetadataSchemaState::kMissing);
  406. CreateDatabase();
  407. InitializeDatabase(false /*=success*/);
  408. // In the case where initialization fails, the store should be fully purged.
  409. EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
  410. EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
  411. histogram_tester.ExpectTotalCount(
  412. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult", 0);
  413. histogram_tester.ExpectBucketCount(
  414. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  415. 1);
  416. histogram_tester.ExpectBucketCount(
  417. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  418. 1);
  419. histogram_tester.ExpectBucketCount(
  420. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 0);
  421. histogram_tester.ExpectBucketCount(
  422. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 1);
  423. }
  424. TEST_F(OptimizationGuideStoreTest,
  425. InitializeFailedOnLoadMetadataWithNoInitialData) {
  426. base::HistogramTester histogram_tester;
  427. SeedInitialData(MetadataSchemaState::kMissing);
  428. CreateDatabase();
  429. InitializeDatabase(true /*=success*/);
  430. // OnLoadMetadata callback
  431. db()->LoadCallback(false);
  432. // In the case where initialization fails, the store should be fully purged.
  433. EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
  434. EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
  435. histogram_tester.ExpectBucketCount(
  436. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  437. 1 /* kLoadMetadataFailed */, 1);
  438. histogram_tester.ExpectBucketCount(
  439. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  440. 1);
  441. histogram_tester.ExpectBucketCount(
  442. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  443. 1);
  444. histogram_tester.ExpectBucketCount(
  445. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 0);
  446. histogram_tester.ExpectBucketCount(
  447. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 1);
  448. }
  449. TEST_F(OptimizationGuideStoreTest,
  450. InitializeFailedOnUpdateMetadataNoInitialData) {
  451. base::HistogramTester histogram_tester;
  452. SeedInitialData(MetadataSchemaState::kMissing);
  453. CreateDatabase();
  454. InitializeDatabase(true /*=success*/);
  455. // OnLoadMetadata callback
  456. db()->LoadCallback(true);
  457. // OnPurgeDatabase callback
  458. db()->UpdateCallback(false);
  459. // In the case where initialization fails, the store should be fully purged.
  460. EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
  461. EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
  462. histogram_tester.ExpectBucketCount(
  463. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  464. 2 /* kSchemaMetadataMissing */, 1);
  465. histogram_tester.ExpectBucketCount(
  466. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  467. 1);
  468. histogram_tester.ExpectBucketCount(
  469. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  470. 1);
  471. histogram_tester.ExpectBucketCount(
  472. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 0);
  473. histogram_tester.ExpectBucketCount(
  474. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 1);
  475. }
  476. TEST_F(OptimizationGuideStoreTest,
  477. InitializeFailedOnInitializeWithInitialData) {
  478. base::HistogramTester histogram_tester;
  479. SeedInitialData(MetadataSchemaState::kValid, 10);
  480. CreateDatabase();
  481. InitializeDatabase(false /*=success*/);
  482. // In the case where initialization fails, the store should be fully purged.
  483. EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
  484. EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
  485. histogram_tester.ExpectTotalCount(
  486. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult", 0);
  487. histogram_tester.ExpectBucketCount(
  488. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  489. 1);
  490. histogram_tester.ExpectBucketCount(
  491. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  492. 1);
  493. histogram_tester.ExpectBucketCount(
  494. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 0);
  495. histogram_tester.ExpectBucketCount(
  496. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 1);
  497. }
  498. TEST_F(OptimizationGuideStoreTest,
  499. InitializeFailedOnLoadMetadataWithInitialData) {
  500. base::HistogramTester histogram_tester;
  501. SeedInitialData(MetadataSchemaState::kValid, 10);
  502. CreateDatabase();
  503. InitializeDatabase(true /*=success*/);
  504. // OnLoadMetadata callback
  505. db()->LoadCallback(false);
  506. // In the case where initialization fails, the store should be fully purged.
  507. EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
  508. EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
  509. histogram_tester.ExpectBucketCount(
  510. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  511. 1 /* kLoadMetadataFailed */, 1);
  512. histogram_tester.ExpectBucketCount(
  513. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  514. 1);
  515. histogram_tester.ExpectBucketCount(
  516. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  517. 1);
  518. histogram_tester.ExpectBucketCount(
  519. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 0);
  520. histogram_tester.ExpectBucketCount(
  521. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 1);
  522. }
  523. TEST_F(OptimizationGuideStoreTest,
  524. InitializeFailedOnUpdateMetadataWithInvalidSchemaEntry) {
  525. base::HistogramTester histogram_tester;
  526. SeedInitialData(MetadataSchemaState::kInvalid, 10);
  527. CreateDatabase();
  528. InitializeDatabase(true /*=success*/);
  529. // OnLoadMetadata callback
  530. db()->LoadCallback(true);
  531. // OnPurgeDatabase callback
  532. db()->UpdateCallback(false);
  533. // In the case where initialization fails, the store should be fully purged.
  534. EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
  535. EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
  536. histogram_tester.ExpectBucketCount(
  537. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  538. 3 /* kSchemaMetadataWrongVersion */, 1);
  539. histogram_tester.ExpectBucketCount(
  540. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  541. 1);
  542. histogram_tester.ExpectBucketCount(
  543. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  544. 1);
  545. histogram_tester.ExpectBucketCount(
  546. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 0);
  547. histogram_tester.ExpectBucketCount(
  548. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 1);
  549. }
  550. TEST_F(OptimizationGuideStoreTest,
  551. InitializeFailedOnLoadHintEntryKeysWithInitialData) {
  552. base::HistogramTester histogram_tester;
  553. SeedInitialData(MetadataSchemaState::kValid, 10, base::Time().Now());
  554. CreateDatabase();
  555. InitializeDatabase(true /*=success*/);
  556. // OnLoadMetadata callback
  557. db()->LoadCallback(true);
  558. // OnLoadEntryKeys callback
  559. db()->LoadCallback(false);
  560. // In the case where initialization fails, the store should be fully purged.
  561. EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
  562. EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
  563. histogram_tester.ExpectBucketCount(
  564. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  565. 0 /* kSuccess */, 1);
  566. histogram_tester.ExpectBucketCount(
  567. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  568. 1);
  569. histogram_tester.ExpectBucketCount(
  570. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  571. 1);
  572. histogram_tester.ExpectBucketCount(
  573. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 1);
  574. histogram_tester.ExpectBucketCount(
  575. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 1);
  576. }
  577. TEST_F(OptimizationGuideStoreTest, InitializeSucceededWithoutSchemaEntry) {
  578. base::HistogramTester histogram_tester;
  579. MetadataSchemaState schema_state = MetadataSchemaState::kMissing;
  580. SeedInitialData(schema_state);
  581. CreateDatabase();
  582. InitializeStore(schema_state);
  583. // The store should contain the schema metadata entry and nothing else.
  584. EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
  585. EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
  586. EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
  587. histogram_tester.ExpectBucketCount(
  588. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  589. 2 /* kSchemaMetadataMissing */, 1);
  590. histogram_tester.ExpectBucketCount(
  591. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  592. 1);
  593. histogram_tester.ExpectBucketCount(
  594. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  595. 1);
  596. histogram_tester.ExpectBucketCount(
  597. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 1);
  598. histogram_tester.ExpectBucketCount(
  599. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 0);
  600. }
  601. TEST_F(OptimizationGuideStoreTest, InitializeSucceededWithInvalidSchemaEntry) {
  602. base::HistogramTester histogram_tester;
  603. MetadataSchemaState schema_state = MetadataSchemaState::kInvalid;
  604. SeedInitialData(schema_state);
  605. CreateDatabase();
  606. InitializeStore(schema_state);
  607. // The store should contain the schema metadata entry and nothing else.
  608. EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
  609. EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
  610. EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
  611. histogram_tester.ExpectBucketCount(
  612. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  613. 3 /* kSchemaMetadataWrongVersion */, 1);
  614. histogram_tester.ExpectBucketCount(
  615. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  616. 1);
  617. histogram_tester.ExpectBucketCount(
  618. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  619. 1);
  620. histogram_tester.ExpectBucketCount(
  621. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 1);
  622. histogram_tester.ExpectBucketCount(
  623. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 0);
  624. }
  625. TEST_F(OptimizationGuideStoreTest, InitializeSucceededWithValidSchemaEntry) {
  626. base::HistogramTester histogram_tester;
  627. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  628. SeedInitialData(schema_state);
  629. CreateDatabase();
  630. InitializeStore(schema_state);
  631. // The store should contain the schema metadata entry and nothing else.
  632. EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
  633. EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
  634. EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
  635. histogram_tester.ExpectBucketCount(
  636. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  637. 4 /* kComponentMetadataMissing*/, 0);
  638. histogram_tester.ExpectBucketCount(
  639. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  640. 5 /* kFetchedMetadataMissing*/, 0);
  641. histogram_tester.ExpectBucketCount(
  642. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  643. 6 /* kComponentAndFetchedMetadataMissing*/, 1);
  644. histogram_tester.ExpectBucketCount(
  645. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  646. 1);
  647. histogram_tester.ExpectBucketCount(
  648. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  649. 1);
  650. histogram_tester.ExpectBucketCount(
  651. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 1);
  652. histogram_tester.ExpectBucketCount(
  653. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 0);
  654. }
  655. TEST_F(OptimizationGuideStoreTest,
  656. InitializeSucceededWithInvalidSchemaEntryAndInitialData) {
  657. base::HistogramTester histogram_tester;
  658. MetadataSchemaState schema_state = MetadataSchemaState::kInvalid;
  659. SeedInitialData(schema_state, 10);
  660. CreateDatabase();
  661. InitializeStore(schema_state);
  662. // The store should contain the schema metadata entry and nothing else, as
  663. // the initial component hints are all purged.
  664. EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
  665. EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
  666. EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
  667. histogram_tester.ExpectBucketCount(
  668. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  669. 3 /* kSchemaMetadataWrongVersion */, 1);
  670. histogram_tester.ExpectBucketCount(
  671. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  672. 1);
  673. histogram_tester.ExpectBucketCount(
  674. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  675. 1);
  676. histogram_tester.ExpectBucketCount(
  677. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 1);
  678. histogram_tester.ExpectBucketCount(
  679. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 0);
  680. }
  681. TEST_F(OptimizationGuideStoreTest, InitializeSucceededWithPurgeExistingData) {
  682. base::HistogramTester histogram_tester;
  683. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  684. SeedInitialData(schema_state, 10);
  685. CreateDatabase();
  686. InitializeStore(schema_state, true /*=purge_existing_data*/);
  687. // The store should contain the schema metadata entry and nothing else.
  688. EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
  689. EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
  690. EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
  691. histogram_tester.ExpectBucketCount(
  692. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  693. 1);
  694. histogram_tester.ExpectBucketCount(
  695. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  696. 1);
  697. histogram_tester.ExpectBucketCount(
  698. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 1);
  699. histogram_tester.ExpectBucketCount(
  700. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 0);
  701. }
  702. TEST_F(OptimizationGuideStoreTest,
  703. InitializeSucceededWithValidSchemaEntryAndInitialData) {
  704. base::HistogramTester histogram_tester;
  705. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  706. size_t component_hint_count = 10;
  707. SeedInitialData(schema_state, component_hint_count,
  708. base::Time().Now() /* fetch_update_time */);
  709. CreateDatabase();
  710. InitializeStore(schema_state);
  711. // The store should contain the schema metadata entry, the component metadata
  712. // entry, and all of the initial component hints.
  713. EXPECT_EQ(GetDBStoreEntryCount(),
  714. static_cast<size_t>(component_hint_count + 3));
  715. EXPECT_EQ(GetStoreEntryKeyCount(), component_hint_count);
  716. EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
  717. ExpectComponentHintsPresent(kDefaultComponentVersion, component_hint_count);
  718. histogram_tester.ExpectBucketCount(
  719. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  720. 0 /* kSuccess */, 1);
  721. histogram_tester.ExpectBucketCount(
  722. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  723. 1);
  724. histogram_tester.ExpectBucketCount(
  725. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  726. 1);
  727. histogram_tester.ExpectBucketCount(
  728. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 1);
  729. histogram_tester.ExpectBucketCount(
  730. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 0);
  731. }
  732. TEST_F(OptimizationGuideStoreTest,
  733. InitializeSucceededWithValidSchemaEntryAndComponentDataOnly) {
  734. base::HistogramTester histogram_tester;
  735. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  736. size_t component_hint_count = 10;
  737. SeedInitialData(schema_state, component_hint_count);
  738. CreateDatabase();
  739. InitializeStore(schema_state);
  740. // The store should contain the schema metadata entry, the component metadata
  741. // entry, and all of the initial component hints.
  742. EXPECT_EQ(GetDBStoreEntryCount(),
  743. static_cast<size_t>(component_hint_count + 2));
  744. EXPECT_EQ(GetStoreEntryKeyCount(), component_hint_count);
  745. EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
  746. ExpectComponentHintsPresent(kDefaultComponentVersion, component_hint_count);
  747. histogram_tester.ExpectBucketCount(
  748. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  749. 4 /* kComponentMetadataMissing*/, 0);
  750. histogram_tester.ExpectBucketCount(
  751. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  752. 5 /* kFetchedMetadataMissing*/, 1);
  753. histogram_tester.ExpectBucketCount(
  754. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  755. 6 /* kComponentAndFetchedMetadataMissing*/, 0);
  756. histogram_tester.ExpectBucketCount(
  757. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  758. 1);
  759. histogram_tester.ExpectBucketCount(
  760. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  761. 1);
  762. histogram_tester.ExpectBucketCount(
  763. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 1);
  764. histogram_tester.ExpectBucketCount(
  765. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 0);
  766. }
  767. TEST_F(OptimizationGuideStoreTest,
  768. InitializeSucceededWithValidSchemaEntryAndFetchedMetaData) {
  769. base::HistogramTester histogram_tester;
  770. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  771. size_t component_hint_count = 0;
  772. SeedInitialData(schema_state, component_hint_count, base::Time().Now());
  773. CreateDatabase();
  774. InitializeStore(schema_state);
  775. // The store should contain the schema metadata entry, the component metadata
  776. // entry, and all of the initial component hints.
  777. EXPECT_EQ(GetDBStoreEntryCount(),
  778. static_cast<size_t>(component_hint_count + 2));
  779. EXPECT_EQ(GetStoreEntryKeyCount(), component_hint_count);
  780. EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
  781. histogram_tester.ExpectBucketCount(
  782. "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
  783. 4 /* kComponentMetadataMissing*/, 1);
  784. histogram_tester.ExpectBucketCount(
  785. "OptimizationGuide.HintCacheLevelDBStore.Status", 0 /* kUninitialized */,
  786. 1);
  787. histogram_tester.ExpectBucketCount(
  788. "OptimizationGuide.HintCacheLevelDBStore.Status", 1 /* kInitializing */,
  789. 1);
  790. histogram_tester.ExpectBucketCount(
  791. "OptimizationGuide.HintCacheLevelDBStore.Status", 2 /* kAvailable */, 1);
  792. histogram_tester.ExpectBucketCount(
  793. "OptimizationGuide.HintCacheLevelDBStore.Status", 3 /* kFailed */, 0);
  794. }
  795. TEST_F(OptimizationGuideStoreTest,
  796. CreateComponentUpdateDataFailsForUninitializedStore) {
  797. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  798. SeedInitialData(schema_state, 10);
  799. CreateDatabase();
  800. // StoreUpdateData for a component update should only be created if the store
  801. // is initialized.
  802. EXPECT_FALSE(guide_store()->MaybeCreateUpdateDataForComponentHints(
  803. base::Version(kUpdateComponentVersion)));
  804. }
  805. TEST_F(OptimizationGuideStoreTest,
  806. CreateComponentUpdateDataFailsForEarlierVersion) {
  807. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  808. SeedInitialData(schema_state, 10);
  809. CreateDatabase();
  810. InitializeStore(schema_state);
  811. // No StoreUpdateData for a component update should be created when the
  812. // component version of the update is older than the store's component
  813. // version.
  814. EXPECT_FALSE(guide_store()->MaybeCreateUpdateDataForComponentHints(
  815. base::Version("0.0.0")));
  816. }
  817. TEST_F(OptimizationGuideStoreTest,
  818. CreateComponentUpdateDataFailsForCurrentVersion) {
  819. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  820. SeedInitialData(schema_state, 10);
  821. CreateDatabase();
  822. InitializeStore(schema_state);
  823. // No StoreUpdateData should be created when the component version of the
  824. // update is the same as the store's component version.
  825. EXPECT_FALSE(guide_store()->MaybeCreateUpdateDataForComponentHints(
  826. base::Version(kDefaultComponentVersion)));
  827. }
  828. TEST_F(OptimizationGuideStoreTest,
  829. CreateComponentUpdateDataSucceedsWithNoPreexistingVersion) {
  830. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  831. SeedInitialData(schema_state);
  832. CreateDatabase();
  833. InitializeStore(schema_state);
  834. // StoreUpdateData for a component update should be created when there is no
  835. // pre-existing component in the store.
  836. EXPECT_TRUE(guide_store()->MaybeCreateUpdateDataForComponentHints(
  837. base::Version(kDefaultComponentVersion)));
  838. }
  839. TEST_F(OptimizationGuideStoreTest,
  840. CreateComponentUpdateDataSucceedsForNewerVersion) {
  841. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  842. SeedInitialData(schema_state, 10);
  843. CreateDatabase();
  844. InitializeStore(schema_state);
  845. // StoreUpdateData for a component update should be created when the component
  846. // version of the update is newer than the store's component version.
  847. EXPECT_TRUE(guide_store()->MaybeCreateUpdateDataForComponentHints(
  848. base::Version(kUpdateComponentVersion)));
  849. }
  850. TEST_F(OptimizationGuideStoreTest, UpdateComponentHintsUpdateEntriesFails) {
  851. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  852. SeedInitialData(schema_state, 10);
  853. CreateDatabase();
  854. InitializeStore(schema_state);
  855. std::unique_ptr<StoreUpdateData> update_data =
  856. guide_store()->MaybeCreateUpdateDataForComponentHints(
  857. base::Version(kUpdateComponentVersion));
  858. ASSERT_TRUE(update_data);
  859. SeedComponentUpdateData(update_data.get(), 5);
  860. UpdateComponentHints(std::move(update_data), false /*update_success*/);
  861. // The store should be purged if the component data update fails.
  862. EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
  863. EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
  864. }
  865. TEST_F(OptimizationGuideStoreTest, UpdateComponentHintsGetKeysFails) {
  866. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  867. SeedInitialData(schema_state, 10);
  868. CreateDatabase();
  869. InitializeStore(schema_state);
  870. std::unique_ptr<StoreUpdateData> update_data =
  871. guide_store()->MaybeCreateUpdateDataForComponentHints(
  872. base::Version(kUpdateComponentVersion));
  873. ASSERT_TRUE(update_data);
  874. SeedComponentUpdateData(update_data.get(), 5);
  875. UpdateComponentHints(std::move(update_data), true /*update_success*/,
  876. false /*load_hints_keys_success*/);
  877. // The store should be purged if loading the keys after the component update
  878. // fails.
  879. EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
  880. EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
  881. }
  882. TEST_F(OptimizationGuideStoreTest, UpdateComponentHints) {
  883. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  884. size_t initial_hint_count = 10;
  885. size_t update_hint_count = 5;
  886. SeedInitialData(schema_state, initial_hint_count);
  887. CreateDatabase();
  888. InitializeStore(schema_state);
  889. std::unique_ptr<StoreUpdateData> update_data =
  890. guide_store()->MaybeCreateUpdateDataForComponentHints(
  891. base::Version(kUpdateComponentVersion));
  892. ASSERT_TRUE(update_data);
  893. SeedComponentUpdateData(update_data.get(), update_hint_count);
  894. UpdateComponentHints(std::move(update_data));
  895. // When the component update succeeds, the store should contain the schema
  896. // metadata entry, the component metadata entry, and all of the update's
  897. // component hints.
  898. EXPECT_EQ(GetDBStoreEntryCount(), update_hint_count + 2);
  899. EXPECT_EQ(GetStoreEntryKeyCount(), update_hint_count);
  900. ExpectComponentHintsPresent(kUpdateComponentVersion, update_hint_count);
  901. }
  902. TEST_F(OptimizationGuideStoreTest,
  903. UpdateComponentHintsAfterInitializationDataPurge) {
  904. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  905. size_t initial_hint_count = 10;
  906. size_t update_hint_count = 5;
  907. SeedInitialData(schema_state, initial_hint_count);
  908. CreateDatabase();
  909. InitializeStore(schema_state, true /*=purge_existing_data*/);
  910. std::unique_ptr<StoreUpdateData> update_data =
  911. guide_store()->MaybeCreateUpdateDataForComponentHints(
  912. base::Version(kUpdateComponentVersion));
  913. ASSERT_TRUE(update_data);
  914. SeedComponentUpdateData(update_data.get(), update_hint_count);
  915. UpdateComponentHints(std::move(update_data));
  916. // When the component update succeeds, the store should contain the schema
  917. // metadata entry, the component metadata entry, and all of the update's
  918. // component hints.
  919. EXPECT_EQ(GetDBStoreEntryCount(), update_hint_count + 2);
  920. EXPECT_EQ(GetStoreEntryKeyCount(), update_hint_count);
  921. ExpectComponentHintsPresent(kUpdateComponentVersion, update_hint_count);
  922. }
  923. TEST_F(OptimizationGuideStoreTest,
  924. CreateComponentDataWithAlreadyUpdatedVersionFails) {
  925. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  926. size_t initial_hint_count = 10;
  927. size_t update_hint_count = 5;
  928. SeedInitialData(schema_state, initial_hint_count);
  929. CreateDatabase();
  930. InitializeStore(schema_state);
  931. std::unique_ptr<StoreUpdateData> update_data =
  932. guide_store()->MaybeCreateUpdateDataForComponentHints(
  933. base::Version(kUpdateComponentVersion));
  934. ASSERT_TRUE(update_data);
  935. SeedComponentUpdateData(update_data.get(), update_hint_count);
  936. UpdateComponentHints(std::move(update_data));
  937. // StoreUpdateData for the component update should not be created for a second
  938. // component update with the same version as the first component update.
  939. EXPECT_FALSE(guide_store()->MaybeCreateUpdateDataForComponentHints(
  940. base::Version(kUpdateComponentVersion)));
  941. }
  942. TEST_F(OptimizationGuideStoreTest,
  943. UpdateComponentHintsWithUpdatedVersionFails) {
  944. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  945. size_t initial_hint_count = 10;
  946. size_t update_hint_count_1 = 5;
  947. size_t update_hint_count_2 = 15;
  948. SeedInitialData(schema_state, initial_hint_count);
  949. CreateDatabase();
  950. InitializeStore(schema_state);
  951. // Create two updates for the same component version with different counts.
  952. std::unique_ptr<StoreUpdateData> update_data_1 =
  953. guide_store()->MaybeCreateUpdateDataForComponentHints(
  954. base::Version(kUpdateComponentVersion));
  955. std::unique_ptr<StoreUpdateData> update_data_2 =
  956. guide_store()->MaybeCreateUpdateDataForComponentHints(
  957. base::Version(kUpdateComponentVersion));
  958. ASSERT_TRUE(update_data_1);
  959. SeedComponentUpdateData(update_data_1.get(), update_hint_count_1);
  960. ASSERT_TRUE(update_data_2);
  961. SeedComponentUpdateData(update_data_2.get(), update_hint_count_2);
  962. // Update the component data with the same component version twice:
  963. // first with |update_data_1| and then with |update_data_2|.
  964. UpdateComponentHints(std::move(update_data_1));
  965. EXPECT_CALL(*this, OnUpdateStore());
  966. guide_store()->UpdateComponentHints(
  967. std::move(update_data_2),
  968. base::BindOnce(&OptimizationGuideStoreTest::OnUpdateStore,
  969. base::Unretained(this)));
  970. // Verify that the store is populated with the component data from
  971. // |update_data_1| and not |update_data_2|.
  972. EXPECT_EQ(GetDBStoreEntryCount(), update_hint_count_1 + 2);
  973. EXPECT_EQ(GetStoreEntryKeyCount(), update_hint_count_1);
  974. ExpectComponentHintsPresent(kUpdateComponentVersion, update_hint_count_1);
  975. }
  976. TEST_F(OptimizationGuideStoreTest, LoadHintOnUnavailableStore) {
  977. size_t initial_hint_count = 10;
  978. SeedInitialData(MetadataSchemaState::kValid, initial_hint_count);
  979. CreateDatabase();
  980. const OptimizationGuideStore::EntryKey kInvalidEntryKey = "invalid";
  981. guide_store()->LoadHint(
  982. kInvalidEntryKey,
  983. base::BindOnce(&OptimizationGuideStoreTest::OnHintLoaded,
  984. base::Unretained(this)));
  985. // Verify that the OnHintLoaded callback runs when the store is unavailable
  986. // and that both the key and the hint were correctly set in it.
  987. EXPECT_EQ(last_loaded_entry_key(), kInvalidEntryKey);
  988. EXPECT_FALSE(last_loaded_hint());
  989. }
  990. TEST_F(OptimizationGuideStoreTest, LoadHintFailure) {
  991. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  992. size_t hint_count = 10;
  993. SeedInitialData(schema_state, hint_count);
  994. CreateDatabase();
  995. InitializeStore(schema_state);
  996. const OptimizationGuideStore::EntryKey kInvalidEntryKey = "invalid";
  997. guide_store()->LoadHint(
  998. kInvalidEntryKey,
  999. base::BindOnce(&OptimizationGuideStoreTest::OnHintLoaded,
  1000. base::Unretained(this)));
  1001. // OnLoadHint callback
  1002. db()->GetCallback(false);
  1003. // Verify that the OnHintLoaded callback runs when the store is unavailable
  1004. // and that both the key and the hint were correctly set in it.
  1005. EXPECT_EQ(last_loaded_entry_key(), kInvalidEntryKey);
  1006. EXPECT_FALSE(last_loaded_hint());
  1007. }
  1008. TEST_F(OptimizationGuideStoreTest, LoadHintSuccessInitialData) {
  1009. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1010. size_t hint_count = 10;
  1011. SeedInitialData(schema_state, hint_count);
  1012. CreateDatabase();
  1013. InitializeStore(schema_state);
  1014. // Verify that all component hints in the initial data can successfully be
  1015. // loaded from the store.
  1016. for (size_t i = 0; i < hint_count; ++i) {
  1017. std::string host = GetHost(i);
  1018. OptimizationGuideStore::EntryKey hint_entry_key;
  1019. if (!guide_store()->FindHintEntryKey(host, &hint_entry_key)) {
  1020. FAIL() << "Hint entry not found for host: " << host;
  1021. }
  1022. guide_store()->LoadHint(
  1023. hint_entry_key,
  1024. base::BindOnce(&OptimizationGuideStoreTest::OnHintLoaded,
  1025. base::Unretained(this)));
  1026. // OnLoadHint callback
  1027. db()->GetCallback(true);
  1028. EXPECT_EQ(last_loaded_entry_key(), hint_entry_key);
  1029. if (!last_loaded_hint()) {
  1030. FAIL() << "Loaded hint was null for entry key: " << hint_entry_key;
  1031. }
  1032. EXPECT_EQ(last_loaded_hint()->hint()->key(), host);
  1033. EXPECT_FALSE(last_loaded_hint()->expiry_time().has_value());
  1034. }
  1035. }
  1036. TEST_F(OptimizationGuideStoreTest, LoadHintSuccessUpdateData) {
  1037. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1038. size_t initial_hint_count = 10;
  1039. size_t update_hint_count = 5;
  1040. SeedInitialData(schema_state, initial_hint_count);
  1041. CreateDatabase();
  1042. InitializeStore(schema_state);
  1043. std::unique_ptr<StoreUpdateData> update_data =
  1044. guide_store()->MaybeCreateUpdateDataForComponentHints(
  1045. base::Version(kUpdateComponentVersion));
  1046. ASSERT_TRUE(update_data);
  1047. SeedComponentUpdateData(update_data.get(), update_hint_count);
  1048. UpdateComponentHints(std::move(update_data));
  1049. // Verify that all component hints within a successful component update can
  1050. // be loaded from the store.
  1051. for (size_t i = 0; i < update_hint_count; ++i) {
  1052. std::string host = GetHost(i);
  1053. OptimizationGuideStore::EntryKey hint_entry_key;
  1054. if (!guide_store()->FindHintEntryKey(host, &hint_entry_key)) {
  1055. FAIL() << "Hint entry not found for host: " << host;
  1056. }
  1057. guide_store()->LoadHint(
  1058. hint_entry_key,
  1059. base::BindOnce(&OptimizationGuideStoreTest::OnHintLoaded,
  1060. base::Unretained(this)));
  1061. // OnLoadHint callback
  1062. db()->GetCallback(true);
  1063. EXPECT_EQ(last_loaded_entry_key(), hint_entry_key);
  1064. if (!last_loaded_hint()) {
  1065. FAIL() << "Loaded hint was null for entry key: " << hint_entry_key;
  1066. }
  1067. EXPECT_EQ(last_loaded_hint()->hint()->key(), host);
  1068. EXPECT_FALSE(last_loaded_hint()->expiry_time().has_value());
  1069. }
  1070. }
  1071. TEST_F(OptimizationGuideStoreTest, FindHintEntryKeyOnUnavailableStore) {
  1072. size_t initial_hint_count = 10;
  1073. SeedInitialData(MetadataSchemaState::kValid, initial_hint_count);
  1074. CreateDatabase();
  1075. std::string host = GetHost(0);
  1076. OptimizationGuideStore::EntryKey hint_entry_key;
  1077. // Verify that hint entry keys can't be found when the store is unavailable.
  1078. EXPECT_FALSE(guide_store()->FindHintEntryKey(host, &hint_entry_key));
  1079. }
  1080. TEST_F(OptimizationGuideStoreTest, FindHintEntryKeyInitialData) {
  1081. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1082. size_t hint_count = 10;
  1083. SeedInitialData(schema_state, hint_count);
  1084. CreateDatabase();
  1085. InitializeStore(schema_state);
  1086. // Verify that all hints contained within the initial store data are reported
  1087. // as being found and hints that are not containd within the initial data are
  1088. // properly reported as not being found.
  1089. for (size_t i = 0; i < hint_count * 2; ++i) {
  1090. std::string host = GetHost(i);
  1091. OptimizationGuideStore::EntryKey hint_entry_key;
  1092. bool success = guide_store()->FindHintEntryKey(host, &hint_entry_key);
  1093. EXPECT_EQ(success, i < hint_count);
  1094. }
  1095. }
  1096. TEST_F(OptimizationGuideStoreTest, FindHintEntryKeyUpdateData) {
  1097. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1098. size_t initial_hint_count = 10;
  1099. size_t update_hint_count = 5;
  1100. SeedInitialData(schema_state, initial_hint_count);
  1101. CreateDatabase();
  1102. InitializeStore(schema_state);
  1103. std::unique_ptr<StoreUpdateData> update_data =
  1104. guide_store()->MaybeCreateUpdateDataForComponentHints(
  1105. base::Version(kUpdateComponentVersion));
  1106. ASSERT_TRUE(update_data);
  1107. SeedComponentUpdateData(update_data.get(), update_hint_count);
  1108. UpdateComponentHints(std::move(update_data));
  1109. // Verify that all hints contained within the component update are reported
  1110. // by the store as being found and hints that are not containd within the
  1111. // component update are properly reported as not being found.
  1112. for (size_t i = 0; i < update_hint_count * 2; ++i) {
  1113. std::string host = GetHost(i);
  1114. OptimizationGuideStore::EntryKey hint_entry_key;
  1115. bool success = guide_store()->FindHintEntryKey(host, &hint_entry_key);
  1116. EXPECT_EQ(success, i < update_hint_count);
  1117. }
  1118. }
  1119. TEST_F(OptimizationGuideStoreTest, FetchedHintsMetadataStored) {
  1120. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1121. base::Time update_time = base::Time().Now();
  1122. SeedInitialData(schema_state, 10, update_time);
  1123. CreateDatabase();
  1124. InitializeStore(schema_state);
  1125. ExpectFetchedMetadata(update_time);
  1126. }
  1127. TEST_F(OptimizationGuideStoreTest, FindHintEntryKeyForFetchedHints) {
  1128. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1129. size_t update_hint_count = 5;
  1130. base::Time update_time = base::Time().Now();
  1131. SeedInitialData(schema_state, 0);
  1132. CreateDatabase();
  1133. InitializeStore(schema_state);
  1134. std::unique_ptr<StoreUpdateData> update_data =
  1135. guide_store()->CreateUpdateDataForFetchedHints(update_time);
  1136. ASSERT_TRUE(update_data);
  1137. SeedFetchedUpdateData(update_data.get(), update_hint_count);
  1138. UpdateFetchedHints(std::move(update_data));
  1139. for (size_t i = 0; i < update_hint_count; ++i) {
  1140. std::string host = GetHost(i);
  1141. OptimizationGuideStore::EntryKey hint_entry_key;
  1142. bool success = guide_store()->FindHintEntryKey(host, &hint_entry_key);
  1143. EXPECT_EQ(success, i < update_hint_count);
  1144. }
  1145. }
  1146. TEST_F(OptimizationGuideStoreTest,
  1147. FindHintEntryKeyCheckFetchedBeforeComponentHints) {
  1148. base::HistogramTester histogram_tester;
  1149. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1150. size_t initial_hint_count = 10;
  1151. base::Time update_time = base::Time().Now();
  1152. SeedInitialData(schema_state, initial_hint_count);
  1153. CreateDatabase();
  1154. InitializeStore(schema_state);
  1155. base::Version version("2.0.0");
  1156. std::unique_ptr<StoreUpdateData> update_data =
  1157. guide_store()->MaybeCreateUpdateDataForComponentHints(
  1158. base::Version(kUpdateComponentVersion));
  1159. ASSERT_TRUE(update_data);
  1160. proto::Hint hint1;
  1161. hint1.set_key("domain1.org");
  1162. hint1.set_key_representation(proto::HOST);
  1163. update_data->MoveHintIntoUpdateData(std::move(hint1));
  1164. proto::Hint hint2;
  1165. hint2.set_key("host.domain2.org");
  1166. hint2.set_key_representation(proto::HOST);
  1167. update_data->MoveHintIntoUpdateData(std::move(hint2));
  1168. UpdateComponentHints(std::move(update_data));
  1169. // Add fetched hints to the store that overlap with the same hosts as the
  1170. // initial set.
  1171. update_data = guide_store()->CreateUpdateDataForFetchedHints(update_time);
  1172. proto::Hint hint;
  1173. hint.set_key("host.domain2.org");
  1174. hint.set_key_representation(proto::HOST);
  1175. update_data->MoveHintIntoUpdateData(std::move(hint));
  1176. UpdateFetchedHints(std::move(update_data));
  1177. // Hint for host.domain2.org should be a fetched hint ("3_" prefix)
  1178. // as fetched hints take priority.
  1179. std::string host = "host.domain2.org";
  1180. OptimizationGuideStore::EntryKey hint_entry_key;
  1181. if (!guide_store()->FindHintEntryKey(host, &hint_entry_key)) {
  1182. FAIL() << "Hint entry not found for host: " << host;
  1183. }
  1184. EXPECT_EQ(hint_entry_key, "3_host.domain2.org");
  1185. host = "domain1.org";
  1186. if (!guide_store()->FindHintEntryKey(host, &hint_entry_key)) {
  1187. FAIL() << "Hint entry not found for host: " << host;
  1188. }
  1189. EXPECT_EQ(hint_entry_key, "2_2.0.0_domain1.org");
  1190. }
  1191. TEST_F(OptimizationGuideStoreTest, SuccessfulRemovedFetchedHintsByKey) {
  1192. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1193. SeedInitialData(schema_state, 10);
  1194. CreateDatabase();
  1195. InitializeStore(schema_state);
  1196. std::unique_ptr<StoreUpdateData> component_update_data =
  1197. guide_store()->MaybeCreateUpdateDataForComponentHints(
  1198. base::Version(kUpdateComponentVersion));
  1199. ASSERT_TRUE(component_update_data);
  1200. proto::Hint hint1;
  1201. hint1.set_key("domain1.org");
  1202. hint1.set_key_representation(proto::HOST);
  1203. component_update_data->MoveHintIntoUpdateData(std::move(hint1));
  1204. proto::Hint hint2;
  1205. hint2.set_key("domain2.org");
  1206. hint2.set_key_representation(proto::HOST);
  1207. component_update_data->MoveHintIntoUpdateData(std::move(hint2));
  1208. UpdateComponentHints(std::move(component_update_data));
  1209. std::unique_ptr<StoreUpdateData> fetched_update_data =
  1210. guide_store()->CreateUpdateDataForFetchedHints(base::Time().Now());
  1211. proto::Hint fetched_hint1;
  1212. fetched_hint1.set_key("domain2.org");
  1213. fetched_hint1.set_key_representation(proto::HOST);
  1214. fetched_update_data->MoveHintIntoUpdateData(std::move(fetched_hint1));
  1215. proto::Hint fetched_hint2;
  1216. fetched_hint2.set_key("domain3.org");
  1217. fetched_hint2.set_key_representation(proto::HOST);
  1218. fetched_update_data->MoveHintIntoUpdateData(std::move(fetched_hint2));
  1219. UpdateFetchedHints(std::move(fetched_update_data));
  1220. base::RunLoop run_loop;
  1221. guide_store()->RemoveFetchedHintsByKey(run_loop.QuitClosure(),
  1222. {
  1223. "domain1.org",
  1224. "domain2.org",
  1225. "domain3.org",
  1226. "domain4.org",
  1227. });
  1228. db()->UpdateCallback(/*success=*/true);
  1229. run_loop.Run();
  1230. OptimizationGuideStore::EntryKey hint_entry_key;
  1231. // Check for keys that should exist.
  1232. EXPECT_TRUE(guide_store()->FindHintEntryKey("domain1.org", &hint_entry_key));
  1233. EXPECT_EQ("2_2.0.0_domain1.org", hint_entry_key);
  1234. EXPECT_TRUE(guide_store()->FindHintEntryKey("domain2.org", &hint_entry_key));
  1235. EXPECT_EQ("2_2.0.0_domain2.org", hint_entry_key);
  1236. // Check for keys that should not exist.
  1237. EXPECT_FALSE(guide_store()->FindHintEntryKey("domain3.org", &hint_entry_key));
  1238. }
  1239. TEST_F(OptimizationGuideStoreTest, FailedRemovedFetchedHintsByKey) {
  1240. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1241. SeedInitialData(schema_state, 10);
  1242. CreateDatabase();
  1243. InitializeStore(schema_state);
  1244. std::unique_ptr<StoreUpdateData> component_update_data =
  1245. guide_store()->MaybeCreateUpdateDataForComponentHints(
  1246. base::Version(kUpdateComponentVersion));
  1247. ASSERT_TRUE(component_update_data);
  1248. proto::Hint hint1;
  1249. hint1.set_key("domain1.org");
  1250. hint1.set_key_representation(proto::HOST);
  1251. component_update_data->MoveHintIntoUpdateData(std::move(hint1));
  1252. proto::Hint hint2;
  1253. hint2.set_key("domain2.org");
  1254. hint2.set_key_representation(proto::HOST);
  1255. component_update_data->MoveHintIntoUpdateData(std::move(hint2));
  1256. UpdateComponentHints(std::move(component_update_data));
  1257. std::unique_ptr<StoreUpdateData> fetched_update_data =
  1258. guide_store()->CreateUpdateDataForFetchedHints(base::Time().Now());
  1259. ASSERT_TRUE(fetched_update_data);
  1260. proto::Hint fetched_hint1;
  1261. fetched_hint1.set_key("domain2.org");
  1262. fetched_hint1.set_key_representation(proto::HOST);
  1263. fetched_update_data->MoveHintIntoUpdateData(std::move(fetched_hint1));
  1264. proto::Hint fetched_hint2;
  1265. fetched_hint2.set_key("domain3.org");
  1266. fetched_hint2.set_key_representation(proto::HOST);
  1267. fetched_update_data->MoveHintIntoUpdateData(std::move(fetched_hint2));
  1268. UpdateFetchedHints(std::move(fetched_update_data));
  1269. bool did_callback_run = false;
  1270. base::OnceClosure callback = base::BindOnce(
  1271. [](bool* set_when_run) { *set_when_run = true; }, &did_callback_run);
  1272. guide_store()->RemoveFetchedHintsByKey(std::move(callback), {
  1273. "domain1.org",
  1274. "domain2.org",
  1275. "domain3.org",
  1276. "domain4.org",
  1277. });
  1278. RunUntilIdle();
  1279. db()->UpdateCallback(/*success=*/false);
  1280. RunUntilIdle();
  1281. EXPECT_FALSE(did_callback_run);
  1282. // The callback did not succeed, so the store should no longer be available.
  1283. EXPECT_EQ(0U, GetStoreEntryKeyCount());
  1284. EXPECT_FALSE(guide_store()->IsAvailable());
  1285. }
  1286. TEST_F(OptimizationGuideStoreTest, ClearFetchedHints) {
  1287. base::HistogramTester histogram_tester;
  1288. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1289. size_t initial_hint_count = 10;
  1290. base::Time update_time = base::Time().Now();
  1291. SeedInitialData(schema_state, initial_hint_count);
  1292. CreateDatabase();
  1293. InitializeStore(schema_state);
  1294. base::Version version("2.0.0");
  1295. std::unique_ptr<StoreUpdateData> update_data =
  1296. guide_store()->MaybeCreateUpdateDataForComponentHints(
  1297. base::Version(kUpdateComponentVersion));
  1298. ASSERT_TRUE(update_data);
  1299. proto::Hint hint1;
  1300. hint1.set_key("domain1.org");
  1301. hint1.set_key_representation(proto::HOST);
  1302. update_data->MoveHintIntoUpdateData(std::move(hint1));
  1303. proto::Hint hint2;
  1304. hint2.set_key("host.domain2.org");
  1305. hint2.set_key_representation(proto::HOST);
  1306. update_data->MoveHintIntoUpdateData(std::move(hint2));
  1307. UpdateComponentHints(std::move(update_data));
  1308. // Add fetched hints to the store that overlap with the same hosts as the
  1309. // initial set.
  1310. update_data = guide_store()->CreateUpdateDataForFetchedHints(update_time);
  1311. proto::Hint fetched_hint1;
  1312. fetched_hint1.set_key("host.domain2.org");
  1313. fetched_hint1.set_key_representation(proto::HOST);
  1314. update_data->MoveHintIntoUpdateData(std::move(fetched_hint1));
  1315. proto::Hint fetched_hint2;
  1316. fetched_hint2.set_key("domain3.org");
  1317. fetched_hint2.set_key_representation(proto::HOST);
  1318. update_data->MoveHintIntoUpdateData(std::move(fetched_hint2));
  1319. UpdateFetchedHints(std::move(update_data));
  1320. // Hint for host.domain2.org should be a fetched hint ("3_" prefix)
  1321. // as fetched hints take priority.
  1322. std::string host = "host.domain2.org";
  1323. OptimizationGuideStore::EntryKey hint_entry_key;
  1324. if (!guide_store()->FindHintEntryKey(host, &hint_entry_key)) {
  1325. FAIL() << "Hint entry not found for host: " << host;
  1326. }
  1327. EXPECT_EQ(hint_entry_key, "3_host.domain2.org");
  1328. host = "domain1.org";
  1329. if (!guide_store()->FindHintEntryKey(host, &hint_entry_key)) {
  1330. FAIL() << "Hint entry not found for host: " << host;
  1331. }
  1332. EXPECT_EQ(hint_entry_key, "2_2.0.0_domain1.org");
  1333. // Remove the fetched hints from the OptimizationGuideStore.
  1334. ClearFetchedHintsFromDatabase();
  1335. histogram_tester.ExpectBucketCount(
  1336. "OptimizationGuide.ClearFetchedHints.StoreAvailable", true, 1);
  1337. host = "domain1.org";
  1338. // Component hint should still exist.
  1339. EXPECT_TRUE(guide_store()->FindHintEntryKey(host, &hint_entry_key));
  1340. host = "domain3.org";
  1341. // Fetched hint should not still exist.
  1342. EXPECT_FALSE(guide_store()->FindHintEntryKey(host, &hint_entry_key));
  1343. // Add Components back - newer version.
  1344. base::Version version3("3.0.0");
  1345. std::unique_ptr<StoreUpdateData> update_data2 =
  1346. guide_store()->MaybeCreateUpdateDataForComponentHints(version3);
  1347. ASSERT_TRUE(update_data2);
  1348. proto::Hint new_hint2;
  1349. new_hint2.set_key("host.domain2.org");
  1350. new_hint2.set_key_representation(proto::HOST);
  1351. update_data2->MoveHintIntoUpdateData(std::move(new_hint2));
  1352. UpdateComponentHints(std::move(update_data2));
  1353. host = "host.domain2.org";
  1354. EXPECT_TRUE(guide_store()->FindHintEntryKey(host, &hint_entry_key));
  1355. update_data = guide_store()->CreateUpdateDataForFetchedHints(update_time);
  1356. proto::Hint new_hint;
  1357. new_hint.set_key("domain1.org");
  1358. new_hint.set_key_representation(proto::HOST);
  1359. update_data->MoveHintIntoUpdateData(std::move(new_hint));
  1360. UpdateFetchedHints(std::move(update_data));
  1361. // Add fetched hints to the store that overlap with the same hosts as the
  1362. // initial set.
  1363. host = "domain1.org";
  1364. if (!guide_store()->FindHintEntryKey(host, &hint_entry_key)) {
  1365. FAIL() << "Hint entry not found for host: " << host;
  1366. }
  1367. EXPECT_EQ(hint_entry_key, "3_domain1.org");
  1368. }
  1369. TEST_F(OptimizationGuideStoreTest, FetchHintsPurgeExpiredFetchedHints) {
  1370. base::HistogramTester histogram_tester;
  1371. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1372. size_t initial_hint_count = 10;
  1373. base::Time update_time = base::Time().Now();
  1374. SeedInitialData(schema_state, initial_hint_count);
  1375. CreateDatabase();
  1376. InitializeStore(schema_state);
  1377. base::Version version("2.0.0");
  1378. std::unique_ptr<StoreUpdateData> update_data =
  1379. guide_store()->MaybeCreateUpdateDataForComponentHints(
  1380. base::Version(kUpdateComponentVersion));
  1381. ASSERT_TRUE(update_data);
  1382. proto::Hint hint1;
  1383. hint1.set_key("domain1.org");
  1384. hint1.set_key_representation(proto::HOST);
  1385. update_data->MoveHintIntoUpdateData(std::move(hint1));
  1386. proto::Hint hint2;
  1387. hint2.set_key("host.domain2.org");
  1388. hint2.set_key_representation(proto::HOST);
  1389. update_data->MoveHintIntoUpdateData(std::move(hint2));
  1390. UpdateComponentHints(std::move(update_data));
  1391. // Add fetched hints to the store that overlap with the same hosts as the
  1392. // initial set.
  1393. update_data = guide_store()->CreateUpdateDataForFetchedHints(update_time);
  1394. proto::Hint fetched_hint1;
  1395. fetched_hint1.set_key("domain2.org");
  1396. fetched_hint1.set_key_representation(proto::HOST);
  1397. fetched_hint1.mutable_max_cache_duration()->set_seconds(
  1398. base::Days(7).InSeconds());
  1399. update_data->MoveHintIntoUpdateData(std::move(fetched_hint1));
  1400. proto::Hint fetched_hint2;
  1401. fetched_hint2.set_key("domain3.org");
  1402. fetched_hint2.set_key_representation(proto::HOST);
  1403. fetched_hint2.mutable_max_cache_duration()->set_seconds(
  1404. base::Days(7).InSeconds());
  1405. update_data->MoveHintIntoUpdateData(std::move(fetched_hint2));
  1406. UpdateFetchedHints(std::move(update_data));
  1407. // Add expired fetched hints to the store.
  1408. update_data = guide_store()->CreateUpdateDataForFetchedHints(update_time);
  1409. proto::Hint fetched_hint3;
  1410. fetched_hint1.set_key("domain4.org");
  1411. fetched_hint1.set_key_representation(proto::HOST);
  1412. fetched_hint1.mutable_max_cache_duration()->set_seconds(
  1413. base::Days(-7).InSeconds());
  1414. update_data->MoveHintIntoUpdateData(std::move(fetched_hint1));
  1415. proto::Hint fetched_hint4;
  1416. fetched_hint2.set_key("domain5.org");
  1417. fetched_hint2.set_key_representation(proto::HOST);
  1418. fetched_hint2.mutable_max_cache_duration()->set_seconds(
  1419. base::Days(-7).InSeconds());
  1420. update_data->MoveHintIntoUpdateData(std::move(fetched_hint2));
  1421. UpdateFetchedHints(std::move(update_data));
  1422. PurgeExpiredFetchedHints();
  1423. OptimizationGuideStore::EntryKey hint_entry_key;
  1424. EXPECT_FALSE(guide_store()->FindHintEntryKey("domain4.org", &hint_entry_key));
  1425. EXPECT_FALSE(guide_store()->FindHintEntryKey("domain5.org", &hint_entry_key));
  1426. EXPECT_TRUE(guide_store()->FindHintEntryKey("domain2.org", &hint_entry_key));
  1427. EXPECT_TRUE(guide_store()->FindHintEntryKey("domain3.org", &hint_entry_key));
  1428. }
  1429. TEST_F(OptimizationGuideStoreTest, FetchedHintsLoadExpiredHint) {
  1430. base::HistogramTester histogram_tester;
  1431. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1432. size_t initial_hint_count = 10;
  1433. base::Time update_time = base::Time().Now();
  1434. SeedInitialData(schema_state, initial_hint_count);
  1435. CreateDatabase();
  1436. InitializeStore(schema_state);
  1437. base::Version version("2.0.0");
  1438. std::unique_ptr<StoreUpdateData> update_data =
  1439. guide_store()->MaybeCreateUpdateDataForComponentHints(
  1440. base::Version(kUpdateComponentVersion));
  1441. ASSERT_TRUE(update_data);
  1442. proto::Hint hint1;
  1443. hint1.set_key("domain1.org");
  1444. hint1.set_key_representation(proto::HOST);
  1445. update_data->MoveHintIntoUpdateData(std::move(hint1));
  1446. proto::Hint hint2;
  1447. hint2.set_key("host.domain2.org");
  1448. hint2.set_key_representation(proto::HOST);
  1449. update_data->MoveHintIntoUpdateData(std::move(hint2));
  1450. UpdateComponentHints(std::move(update_data));
  1451. // Add fetched hints to the store that expired.
  1452. update_data = guide_store()->CreateUpdateDataForFetchedHints(update_time);
  1453. proto::Hint fetched_hint1;
  1454. fetched_hint1.set_key("host.domain2.org");
  1455. fetched_hint1.set_key_representation(proto::HOST);
  1456. fetched_hint1.mutable_max_cache_duration()->set_seconds(
  1457. base::Days(-10).InSeconds());
  1458. update_data->MoveHintIntoUpdateData(std::move(fetched_hint1));
  1459. proto::Hint fetched_hint2;
  1460. fetched_hint2.set_key("domain3.org");
  1461. fetched_hint2.set_key_representation(proto::HOST);
  1462. update_data->MoveHintIntoUpdateData(std::move(fetched_hint2));
  1463. UpdateFetchedHints(std::move(update_data));
  1464. // Hint for host.domain2.org should be a fetched hint ("3_" prefix)
  1465. // as fetched hints take priority.
  1466. std::string host = "host.domain2.org";
  1467. OptimizationGuideStore::EntryKey hint_entry_key;
  1468. if (!guide_store()->FindHintEntryKey(host, &hint_entry_key)) {
  1469. FAIL() << "Hint entry not found for host: " << host;
  1470. }
  1471. EXPECT_EQ(hint_entry_key, "3_host.domain2.org");
  1472. guide_store()->LoadHint(
  1473. hint_entry_key, base::BindOnce(&OptimizationGuideStoreTest::OnHintLoaded,
  1474. base::Unretained(this)));
  1475. // OnLoadHint callback
  1476. db()->GetCallback(true);
  1477. // |hint_entry_key| will be a fetched hint but the entry will be empty.
  1478. EXPECT_EQ(last_loaded_entry_key(), hint_entry_key);
  1479. EXPECT_FALSE(last_loaded_hint());
  1480. histogram_tester.ExpectBucketCount(
  1481. "OptimizationGuide.HintCacheStore.OnLoadHint.FetchedHintExpired", true,
  1482. 1);
  1483. }
  1484. TEST_F(OptimizationGuideStoreTest, FetchedHintsLoadPopulatesExpiryTime) {
  1485. base::HistogramTester histogram_tester;
  1486. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1487. size_t initial_hint_count = 10;
  1488. base::Time update_time = base::Time().Now();
  1489. SeedInitialData(schema_state, initial_hint_count);
  1490. CreateDatabase();
  1491. InitializeStore(schema_state);
  1492. base::Version version("2.0.0");
  1493. std::unique_ptr<StoreUpdateData> update_data =
  1494. guide_store()->MaybeCreateUpdateDataForComponentHints(
  1495. base::Version(kUpdateComponentVersion));
  1496. ASSERT_TRUE(update_data);
  1497. proto::Hint hint1;
  1498. hint1.set_key("domain1.org");
  1499. hint1.set_key_representation(proto::HOST);
  1500. update_data->MoveHintIntoUpdateData(std::move(hint1));
  1501. proto::Hint hint2;
  1502. hint2.set_key("host.domain2.org");
  1503. hint2.set_key_representation(proto::HOST);
  1504. update_data->MoveHintIntoUpdateData(std::move(hint2));
  1505. UpdateComponentHints(std::move(update_data));
  1506. // Add fetched hints to the store that expired.
  1507. update_data = guide_store()->CreateUpdateDataForFetchedHints(update_time);
  1508. proto::Hint fetched_hint1;
  1509. fetched_hint1.set_key("host.domain2.org");
  1510. fetched_hint1.set_key_representation(proto::HOST);
  1511. fetched_hint1.mutable_max_cache_duration()->set_seconds(
  1512. base::Days(10).InSeconds());
  1513. update_data->MoveHintIntoUpdateData(std::move(fetched_hint1));
  1514. proto::Hint fetched_hint2;
  1515. fetched_hint2.set_key("domain3.org");
  1516. fetched_hint2.set_key_representation(proto::HOST);
  1517. update_data->MoveHintIntoUpdateData(std::move(fetched_hint2));
  1518. UpdateFetchedHints(std::move(update_data));
  1519. // Hint for host.domain2.org should be a fetched hint ("3_" prefix)
  1520. // as fetched hints take priority.
  1521. std::string host = "host.domain2.org";
  1522. OptimizationGuideStore::EntryKey hint_entry_key;
  1523. if (!guide_store()->FindHintEntryKey(host, &hint_entry_key)) {
  1524. FAIL() << "Hint entry not found for host: " << host;
  1525. }
  1526. EXPECT_EQ(hint_entry_key, "3_host.domain2.org");
  1527. guide_store()->LoadHint(
  1528. hint_entry_key, base::BindOnce(&OptimizationGuideStoreTest::OnHintLoaded,
  1529. base::Unretained(this)));
  1530. // OnLoadHint callback
  1531. db()->GetCallback(true);
  1532. // |hint_entry_key| will be a fetched hint but the entry will be empty.
  1533. EXPECT_EQ(last_loaded_entry_key(), hint_entry_key);
  1534. EXPECT_TRUE(last_loaded_hint());
  1535. EXPECT_TRUE(last_loaded_hint()->expiry_time().has_value());
  1536. }
  1537. TEST_F(OptimizationGuideStoreTest, FindPredictionModelEntryKey) {
  1538. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1539. SeedInitialData(schema_state, 0);
  1540. CreateDatabase();
  1541. InitializeStore(schema_state);
  1542. base::Time update_time = base::Time().Now();
  1543. std::unique_ptr<StoreUpdateData> update_data =
  1544. guide_store()->CreateUpdateDataForPredictionModels(
  1545. update_time +
  1546. optimization_guide::features::StoredModelsValidDuration());
  1547. ASSERT_TRUE(update_data);
  1548. SeedPredictionModelUpdateData(update_data.get(),
  1549. proto::OPTIMIZATION_TARGET_UNKNOWN);
  1550. SeedPredictionModelUpdateData(update_data.get(),
  1551. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
  1552. UpdatePredictionModels(std::move(update_data));
  1553. OptimizationGuideStore::EntryKey entry_key;
  1554. bool success = guide_store()->FindPredictionModelEntryKey(
  1555. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  1556. EXPECT_TRUE(success);
  1557. EXPECT_EQ(entry_key, "4_1");
  1558. }
  1559. TEST_F(OptimizationGuideStoreTest,
  1560. FindEntryKeyMissingForMissingPredictionModel) {
  1561. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1562. SeedInitialData(schema_state, 0);
  1563. CreateDatabase();
  1564. InitializeStore(schema_state);
  1565. base::Time update_time = base::Time().Now();
  1566. std::unique_ptr<StoreUpdateData> update_data =
  1567. guide_store()->CreateUpdateDataForPredictionModels(
  1568. update_time +
  1569. optimization_guide::features::StoredModelsValidDuration());
  1570. ASSERT_TRUE(update_data);
  1571. SeedPredictionModelUpdateData(update_data.get(),
  1572. proto::OPTIMIZATION_TARGET_UNKNOWN);
  1573. UpdatePredictionModels(std::move(update_data));
  1574. OptimizationGuideStore::EntryKey entry_key;
  1575. bool success = guide_store()->FindPredictionModelEntryKey(
  1576. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  1577. EXPECT_FALSE(success);
  1578. EXPECT_EQ(entry_key, "4_1");
  1579. }
  1580. TEST_F(OptimizationGuideStoreTest, FindAndRemovePredictionModelEntryKey) {
  1581. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1582. SeedInitialData(schema_state, 0);
  1583. CreateDatabase();
  1584. InitializeStore(schema_state);
  1585. base::Time update_time = base::Time().Now();
  1586. std::unique_ptr<StoreUpdateData> update_data =
  1587. guide_store()->CreateUpdateDataForPredictionModels(
  1588. update_time +
  1589. optimization_guide::features::StoredModelsValidDuration());
  1590. ASSERT_TRUE(update_data);
  1591. SeedPredictionModelUpdateData(update_data.get(),
  1592. proto::OPTIMIZATION_TARGET_UNKNOWN);
  1593. SeedPredictionModelUpdateData(update_data.get(),
  1594. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
  1595. UpdatePredictionModels(std::move(update_data));
  1596. const OptimizationGuideStore::EntryKey kInvalidEntryKey = "4_2";
  1597. EXPECT_FALSE(
  1598. guide_store()->RemovePredictionModelFromEntryKey(kInvalidEntryKey));
  1599. OptimizationGuideStore::EntryKey entry_key;
  1600. bool success = guide_store()->FindPredictionModelEntryKey(
  1601. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  1602. ASSERT_TRUE(success);
  1603. EXPECT_TRUE(guide_store()->RemovePredictionModelFromEntryKey(entry_key));
  1604. // OnLoadModelsToBeUpdated
  1605. db()->LoadCallback(true);
  1606. // OnUpdateStore
  1607. db()->UpdateCallback(true);
  1608. // OnLoadEntryKeys
  1609. db()->LoadCallback(true);
  1610. EXPECT_FALSE(guide_store()->FindPredictionModelEntryKey(
  1611. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key));
  1612. }
  1613. TEST_F(OptimizationGuideStoreTest, LoadPredictionModel) {
  1614. base::HistogramTester histogram_tester;
  1615. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1616. SeedInitialData(schema_state, 0);
  1617. CreateDatabase();
  1618. InitializeStore(schema_state);
  1619. base::Time update_time = base::Time().Now();
  1620. std::unique_ptr<StoreUpdateData> update_data =
  1621. guide_store()->CreateUpdateDataForPredictionModels(
  1622. update_time +
  1623. optimization_guide::features::StoredModelsValidDuration());
  1624. ASSERT_TRUE(update_data);
  1625. SeedPredictionModelUpdateData(update_data.get(),
  1626. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
  1627. UpdatePredictionModels(std::move(update_data));
  1628. OptimizationGuideStore::EntryKey entry_key;
  1629. bool success = guide_store()->FindPredictionModelEntryKey(
  1630. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  1631. EXPECT_TRUE(success);
  1632. guide_store()->LoadPredictionModel(
  1633. entry_key,
  1634. base::BindOnce(&OptimizationGuideStoreTest::OnPredictionModelLoaded,
  1635. base::Unretained(this)));
  1636. // OnPredictionModelLoaded callback
  1637. db()->GetCallback(true);
  1638. EXPECT_TRUE(last_loaded_prediction_model());
  1639. }
  1640. TEST_F(OptimizationGuideStoreTest, LoadPredictionModelOnUnavailableStore) {
  1641. base::HistogramTester histogram_tester;
  1642. size_t initial_hint_count = 10;
  1643. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1644. SeedInitialData(schema_state, initial_hint_count);
  1645. CreateDatabase();
  1646. InitializeStore(schema_state);
  1647. const OptimizationGuideStore::EntryKey kInvalidEntryKey = "4_2";
  1648. guide_store()->LoadPredictionModel(
  1649. kInvalidEntryKey,
  1650. base::BindOnce(&OptimizationGuideStoreTest::OnPredictionModelLoaded,
  1651. base::Unretained(this)));
  1652. // OnPredictionModelLoaded callback
  1653. db()->GetCallback(true);
  1654. // Verify that the OnPredictionModelLoaded callback runs when the store is
  1655. // unavailable and that the prediction model was correctly set.
  1656. EXPECT_FALSE(last_loaded_prediction_model());
  1657. }
  1658. TEST_F(OptimizationGuideStoreTest, LoadPredictionModelOnExpiredModel) {
  1659. base::HistogramTester histogram_tester;
  1660. size_t initial_hint_count = 10;
  1661. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1662. SeedInitialData(schema_state, initial_hint_count);
  1663. CreateDatabase();
  1664. InitializeStore(schema_state);
  1665. // Add an update with models that are "inactive".
  1666. base::Time update_time = base::Time().Now();
  1667. std::unique_ptr<StoreUpdateData> update_data =
  1668. guide_store()->CreateUpdateDataForPredictionModels(
  1669. update_time -
  1670. optimization_guide::features::StoredModelsValidDuration());
  1671. ASSERT_TRUE(update_data);
  1672. SeedPredictionModelUpdateData(
  1673. update_data.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
  1674. /*model_file_path=*/absl::nullopt,
  1675. /*info=*/{},
  1676. update_time - optimization_guide::features::StoredModelsValidDuration());
  1677. UpdatePredictionModels(std::move(update_data));
  1678. // Since models haven't been purged yet it will initially show up as valid.
  1679. OptimizationGuideStore::EntryKey entry_key;
  1680. EXPECT_TRUE(guide_store()->FindPredictionModelEntryKey(
  1681. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key));
  1682. guide_store()->LoadPredictionModel(
  1683. entry_key,
  1684. base::BindOnce(&OptimizationGuideStoreTest::OnPredictionModelLoaded,
  1685. base::Unretained(this)));
  1686. // OnPredictionModelLoaded callback
  1687. db()->GetCallback(true);
  1688. // After load completes, the key will still exist until after purge.
  1689. EXPECT_TRUE(guide_store()->FindPredictionModelEntryKey(
  1690. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key));
  1691. // Verify that the OnPredictionModelLoaded callback runs when the store is
  1692. // unavailable and that the prediction model was correctly not available.
  1693. EXPECT_FALSE(last_loaded_prediction_model());
  1694. }
  1695. TEST_F(OptimizationGuideStoreTest, LoadPredictionModelWithUpdateInFlight) {
  1696. base::HistogramTester histogram_tester;
  1697. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1698. SeedInitialData(schema_state, 0);
  1699. CreateDatabase();
  1700. InitializeStore(schema_state);
  1701. base::Time update_time = base::Time().Now();
  1702. std::unique_ptr<StoreUpdateData> update_data =
  1703. guide_store()->CreateUpdateDataForPredictionModels(
  1704. update_time +
  1705. optimization_guide::features::StoredModelsValidDuration());
  1706. ASSERT_TRUE(update_data);
  1707. SeedPredictionModelUpdateData(update_data.get(),
  1708. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
  1709. UpdatePredictionModels(std::move(update_data));
  1710. const OptimizationGuideStore::EntryKey kEntryKey = "4_1";
  1711. guide_store()->LoadPredictionModel(
  1712. kEntryKey,
  1713. base::BindOnce(&OptimizationGuideStoreTest::OnPredictionModelLoaded,
  1714. base::Unretained(this)));
  1715. db()->GetCallback(true);
  1716. // Verify that the OnPredictionModelLoaded callback eventually runs with the
  1717. // prediction model being correctly set.
  1718. EXPECT_TRUE(last_loaded_prediction_model());
  1719. }
  1720. TEST_F(OptimizationGuideStoreTest,
  1721. LoadPredictionModelWithDownloadUrlDoesntExist) {
  1722. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1723. SeedInitialData(schema_state, 0);
  1724. CreateDatabase();
  1725. InitializeStore(schema_state);
  1726. base::Time update_time = base::Time().Now();
  1727. std::unique_ptr<StoreUpdateData> update_data =
  1728. guide_store()->CreateUpdateDataForPredictionModels(
  1729. update_time +
  1730. optimization_guide::features::StoredModelsValidDuration());
  1731. ASSERT_TRUE(update_data);
  1732. SeedPredictionModelUpdateData(update_data.get(),
  1733. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
  1734. temp_dir().AppendASCII("doesntexist"));
  1735. UpdatePredictionModels(std::move(update_data));
  1736. OptimizationGuideStore::EntryKey entry_key;
  1737. bool success = guide_store()->FindPredictionModelEntryKey(
  1738. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  1739. EXPECT_TRUE(success);
  1740. guide_store()->LoadPredictionModel(
  1741. entry_key,
  1742. base::BindOnce(&OptimizationGuideStoreTest::OnPredictionModelLoaded,
  1743. base::Unretained(this)));
  1744. // OnPredictionModelLoaded callback
  1745. db()->GetCallback(true);
  1746. // Wait for file to be verified.
  1747. RunUntilIdle();
  1748. // OnLoadModelsToBeUpdated callback
  1749. db()->LoadCallback(true);
  1750. // OnUpdateStore callback
  1751. db()->UpdateCallback(true);
  1752. // OnLoadEntryKeys callback
  1753. db()->LoadCallback(true);
  1754. EXPECT_FALSE(last_loaded_prediction_model());
  1755. // Make sure key is deleted.
  1756. success = guide_store()->FindPredictionModelEntryKey(
  1757. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  1758. EXPECT_FALSE(success);
  1759. }
  1760. TEST_F(OptimizationGuideStoreTest,
  1761. LoadPredictionModelWithAdditionalFileDoesntExist) {
  1762. base::HistogramTester histogram_tester;
  1763. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1764. SeedInitialData(schema_state, 0);
  1765. CreateDatabase();
  1766. InitializeStore(schema_state);
  1767. base::Time update_time = base::Time().Now();
  1768. std::unique_ptr<StoreUpdateData> update_data =
  1769. guide_store()->CreateUpdateDataForPredictionModels(
  1770. update_time +
  1771. optimization_guide::features::StoredModelsValidDuration());
  1772. ASSERT_TRUE(update_data);
  1773. base::FilePath model_file_path = temp_dir().AppendASCII("model.tflite");
  1774. ASSERT_EQ(static_cast<int32_t>(3),
  1775. base::WriteFile(model_file_path, "boo", 3));
  1776. base::FilePath additional_file_path = temp_dir().AppendASCII("doesntexist");
  1777. proto::ModelInfo info;
  1778. info.add_additional_files()->set_file_path(
  1779. FilePathToString(additional_file_path));
  1780. SeedPredictionModelUpdateData(update_data.get(),
  1781. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
  1782. temp_dir().AppendASCII("doesntexist"), info);
  1783. UpdatePredictionModels(std::move(update_data));
  1784. OptimizationGuideStore::EntryKey entry_key;
  1785. bool success = guide_store()->FindPredictionModelEntryKey(
  1786. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  1787. EXPECT_TRUE(success);
  1788. guide_store()->LoadPredictionModel(
  1789. entry_key,
  1790. base::BindOnce(&OptimizationGuideStoreTest::OnPredictionModelLoaded,
  1791. base::Unretained(this)));
  1792. // OnPredictionModelLoaded callback
  1793. db()->GetCallback(true);
  1794. // Wait for file to be verified.
  1795. RunUntilIdle();
  1796. // OnLoadModelsToBeUpdated callback
  1797. db()->LoadCallback(true);
  1798. // OnUpdateStore callback
  1799. db()->UpdateCallback(true);
  1800. // OnLoadEntryKeys callback
  1801. db()->LoadCallback(true);
  1802. EXPECT_FALSE(last_loaded_prediction_model());
  1803. // Make sure key is deleted.
  1804. success = guide_store()->FindPredictionModelEntryKey(
  1805. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  1806. EXPECT_FALSE(success);
  1807. histogram_tester.ExpectUniqueSample(
  1808. "OptimizationGuide.ModelFilesVerified.PainfulPageLoad", false, 1);
  1809. }
  1810. TEST_F(OptimizationGuideStoreTest,
  1811. LoadPredictionModelWithAdditionalFileThatExists) {
  1812. base::HistogramTester histogram_tester;
  1813. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1814. SeedInitialData(schema_state, 0);
  1815. CreateDatabase();
  1816. InitializeStore(schema_state);
  1817. base::Time update_time = base::Time().Now();
  1818. std::unique_ptr<StoreUpdateData> update_data =
  1819. guide_store()->CreateUpdateDataForPredictionModels(
  1820. update_time +
  1821. optimization_guide::features::StoredModelsValidDuration());
  1822. ASSERT_TRUE(update_data);
  1823. base::FilePath model_file_path = temp_dir().AppendASCII("model.tflite");
  1824. ASSERT_EQ(static_cast<int32_t>(3),
  1825. base::WriteFile(model_file_path, "boo", 3));
  1826. base::FilePath additional_file_path =
  1827. temp_dir().AppendASCII("additional_file.txt");
  1828. ASSERT_EQ(static_cast<int32_t>(3),
  1829. base::WriteFile(additional_file_path, "ah!", 3));
  1830. proto::ModelInfo info;
  1831. info.add_additional_files()->set_file_path(
  1832. FilePathToString(additional_file_path));
  1833. SeedPredictionModelUpdateData(update_data.get(),
  1834. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
  1835. model_file_path, info);
  1836. UpdatePredictionModels(std::move(update_data));
  1837. OptimizationGuideStore::EntryKey entry_key;
  1838. bool success = guide_store()->FindPredictionModelEntryKey(
  1839. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  1840. EXPECT_TRUE(success);
  1841. guide_store()->LoadPredictionModel(
  1842. entry_key,
  1843. base::BindOnce(&OptimizationGuideStoreTest::OnPredictionModelLoaded,
  1844. base::Unretained(this)));
  1845. // OnPredictionModelLoaded callback
  1846. db()->GetCallback(true);
  1847. // Wait for file to be verified.
  1848. RunUntilIdle();
  1849. proto::PredictionModel* loaded_model = last_loaded_prediction_model();
  1850. ASSERT_TRUE(loaded_model);
  1851. EXPECT_EQ(StringToFilePath(loaded_model->model().download_url()).value(),
  1852. model_file_path);
  1853. ASSERT_EQ(loaded_model->model_info().additional_files().size(), 1);
  1854. EXPECT_EQ(StringToFilePath(
  1855. loaded_model->model_info().additional_files(0).file_path()),
  1856. additional_file_path);
  1857. histogram_tester.ExpectUniqueSample(
  1858. "OptimizationGuide.ModelFilesVerified.PainfulPageLoad", true, 1);
  1859. }
  1860. TEST_F(OptimizationGuideStoreTest,
  1861. LoadPredictionModelWithDownloadUrlThatExists) {
  1862. base::HistogramTester histogram_tester;
  1863. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1864. SeedInitialData(schema_state, 0);
  1865. CreateDatabase();
  1866. InitializeStore(schema_state);
  1867. base::Time update_time = base::Time().Now();
  1868. std::unique_ptr<StoreUpdateData> update_data =
  1869. guide_store()->CreateUpdateDataForPredictionModels(
  1870. update_time +
  1871. optimization_guide::features::StoredModelsValidDuration());
  1872. ASSERT_TRUE(update_data);
  1873. base::FilePath file_path = temp_dir().AppendASCII("file");
  1874. ASSERT_EQ(static_cast<int32_t>(3), base::WriteFile(file_path, "boo", 3));
  1875. SeedPredictionModelUpdateData(update_data.get(),
  1876. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
  1877. file_path);
  1878. UpdatePredictionModels(std::move(update_data));
  1879. OptimizationGuideStore::EntryKey entry_key;
  1880. bool success = guide_store()->FindPredictionModelEntryKey(
  1881. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  1882. EXPECT_TRUE(success);
  1883. guide_store()->LoadPredictionModel(
  1884. entry_key,
  1885. base::BindOnce(&OptimizationGuideStoreTest::OnPredictionModelLoaded,
  1886. base::Unretained(this)));
  1887. // OnPredictionModelLoaded callback
  1888. db()->GetCallback(true);
  1889. // Wait for file to be verified.
  1890. RunUntilIdle();
  1891. proto::PredictionModel* loaded_model = last_loaded_prediction_model();
  1892. EXPECT_TRUE(loaded_model);
  1893. EXPECT_EQ(StringToFilePath(loaded_model->model().download_url()).value(),
  1894. file_path);
  1895. histogram_tester.ExpectUniqueSample(
  1896. "OptimizationGuide.ModelFilesVerified.PainfulPageLoad", true, 1);
  1897. }
  1898. TEST_F(OptimizationGuideStoreTest, UpdatePredictionModelsDeletesOldFile) {
  1899. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1900. SeedInitialData(schema_state, 0);
  1901. CreateDatabase();
  1902. InitializeStore(schema_state);
  1903. base::FilePath old_dir = temp_dir().AppendASCII("model_v1");
  1904. base::FilePath new_dir = temp_dir().AppendASCII("model_v2");
  1905. ASSERT_TRUE(base::CreateDirectory(old_dir));
  1906. ASSERT_TRUE(base::CreateDirectory(new_dir));
  1907. base::Time update_time = base::Time().Now();
  1908. std::unique_ptr<StoreUpdateData> update_data =
  1909. guide_store()->CreateUpdateDataForPredictionModels(
  1910. update_time +
  1911. optimization_guide::features::StoredModelsValidDuration());
  1912. ASSERT_TRUE(update_data);
  1913. base::FilePath old_file_path = old_dir.AppendASCII("model.tflite");
  1914. ASSERT_EQ(static_cast<int32_t>(3), base::WriteFile(old_file_path, "boo", 3));
  1915. SeedPredictionModelUpdateData(update_data.get(),
  1916. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
  1917. old_file_path);
  1918. UpdatePredictionModels(std::move(update_data));
  1919. OptimizationGuideStore::EntryKey entry_key;
  1920. bool success = guide_store()->FindPredictionModelEntryKey(
  1921. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  1922. EXPECT_TRUE(success);
  1923. std::unique_ptr<StoreUpdateData> update_data2 =
  1924. guide_store()->CreateUpdateDataForPredictionModels(
  1925. update_time +
  1926. optimization_guide::features::StoredModelsValidDuration());
  1927. ASSERT_TRUE(update_data2);
  1928. base::FilePath new_file_path = new_dir.AppendASCII("model.tflite");
  1929. ASSERT_EQ(static_cast<int32_t>(3), base::WriteFile(new_file_path, "boo", 3));
  1930. SeedPredictionModelUpdateData(update_data2.get(),
  1931. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
  1932. new_file_path);
  1933. UpdatePredictionModels(std::move(update_data2));
  1934. RunUntilIdle();
  1935. // Make sure new file exists.
  1936. EXPECT_TRUE(base::PathExists(new_file_path));
  1937. EXPECT_TRUE(base::PathExists(new_dir));
  1938. // Make sure the path to delete gets stored in the pref.
  1939. EXPECT_FALSE(IsStoreFilesToDeletePrefEmpty());
  1940. // Re-create the database so the old file gets deleted.
  1941. CreateDatabase();
  1942. RunUntilIdle();
  1943. // Make sure old file is deleted when store is deleted.
  1944. EXPECT_FALSE(base::PathExists(old_file_path));
  1945. EXPECT_FALSE(base::PathExists(old_dir));
  1946. // Make sure the pref gets cleared out.
  1947. EXPECT_TRUE(IsStoreFilesToDeletePrefEmpty());
  1948. }
  1949. TEST_F(OptimizationGuideStoreTest,
  1950. BackwardCompatibilityForParentDirectoryStorage) {
  1951. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  1952. SeedInitialData(schema_state, 0);
  1953. CreateDatabase();
  1954. InitializeStore(schema_state);
  1955. base::FilePath old_dir = temp_dir().AppendASCII("OptGuideModels");
  1956. base::FilePath new_dir = old_dir.AppendASCII("foo_target_v2");
  1957. ASSERT_TRUE(base::CreateDirectory(old_dir));
  1958. ASSERT_TRUE(base::CreateDirectory(new_dir));
  1959. base::FilePath old_unassociated_model_path =
  1960. old_dir.AppendASCII("other_model.tflite");
  1961. ASSERT_EQ(static_cast<int32_t>(3),
  1962. base::WriteFile(old_unassociated_model_path, "boo", 3));
  1963. base::Time update_time = base::Time().Now();
  1964. std::unique_ptr<StoreUpdateData> update_data =
  1965. guide_store()->CreateUpdateDataForPredictionModels(
  1966. update_time +
  1967. optimization_guide::features::StoredModelsValidDuration());
  1968. ASSERT_TRUE(update_data);
  1969. base::FilePath old_file_path = old_dir.AppendASCII("model_v1.tflite");
  1970. ASSERT_EQ(static_cast<int32_t>(3), base::WriteFile(old_file_path, "boo", 3));
  1971. SeedPredictionModelUpdateData(update_data.get(),
  1972. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
  1973. old_file_path);
  1974. UpdatePredictionModels(std::move(update_data));
  1975. OptimizationGuideStore::EntryKey entry_key;
  1976. bool success = guide_store()->FindPredictionModelEntryKey(
  1977. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  1978. EXPECT_TRUE(success);
  1979. std::unique_ptr<StoreUpdateData> update_data2 =
  1980. guide_store()->CreateUpdateDataForPredictionModels(
  1981. update_time +
  1982. optimization_guide::features::StoredModelsValidDuration());
  1983. ASSERT_TRUE(update_data2);
  1984. base::FilePath new_file_path = new_dir.Append(GetBaseFileNameForModels());
  1985. ASSERT_EQ(static_cast<int32_t>(3), base::WriteFile(new_file_path, "boo", 3));
  1986. SeedPredictionModelUpdateData(update_data2.get(),
  1987. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
  1988. new_file_path);
  1989. UpdatePredictionModels(std::move(update_data2));
  1990. RunUntilIdle();
  1991. EXPECT_FALSE(IsStoreFilesToDeletePrefEmpty());
  1992. // Re-create database so files get deleted.
  1993. CreateDatabase();
  1994. RunUntilIdle();
  1995. // The old file should have been deleted, but not its containing directory
  1996. // because that is recognized as the OptGuideModel parent storage directory.
  1997. EXPECT_FALSE(base::PathExists(old_file_path));
  1998. EXPECT_TRUE(base::PathExists(old_dir));
  1999. // The other unassociated old-code model should not have been touched.
  2000. EXPECT_TRUE(base::PathExists(old_unassociated_model_path));
  2001. EXPECT_TRUE(base::PathExists(new_file_path));
  2002. EXPECT_TRUE(base::PathExists(new_dir));
  2003. EXPECT_TRUE(IsStoreFilesToDeletePrefEmpty());
  2004. }
  2005. TEST_F(OptimizationGuideStoreTest, RemovePredictionModelEntryKeyDeletesFile) {
  2006. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  2007. SeedInitialData(schema_state, 0);
  2008. CreateDatabase();
  2009. InitializeStore(schema_state);
  2010. base::Time update_time = base::Time().Now();
  2011. std::unique_ptr<StoreUpdateData> update_data =
  2012. guide_store()->CreateUpdateDataForPredictionModels(
  2013. update_time +
  2014. optimization_guide::features::StoredModelsValidDuration());
  2015. ASSERT_TRUE(update_data);
  2016. base::FilePath file_path = temp_dir().AppendASCII("file");
  2017. ASSERT_EQ(static_cast<int32_t>(3), base::WriteFile(file_path, "boo", 3));
  2018. SeedPredictionModelUpdateData(update_data.get(),
  2019. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
  2020. file_path);
  2021. UpdatePredictionModels(std::move(update_data));
  2022. OptimizationGuideStore::EntryKey entry_key;
  2023. bool success = guide_store()->FindPredictionModelEntryKey(
  2024. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  2025. ASSERT_TRUE(success);
  2026. EXPECT_TRUE(guide_store()->RemovePredictionModelFromEntryKey(entry_key));
  2027. // OnLoadModelsToBeUpdated
  2028. db()->LoadCallback(true);
  2029. RunUntilIdle();
  2030. // OnUpdateStore
  2031. db()->UpdateCallback(true);
  2032. // OnLoadEntryKeys
  2033. db()->LoadCallback(true);
  2034. EXPECT_FALSE(guide_store()->FindPredictionModelEntryKey(
  2035. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key));
  2036. // Ensure that file to delete gets persisted so it can be deleted later.
  2037. EXPECT_FALSE(IsStoreFilesToDeletePrefEmpty());
  2038. // Re-create the database so files get deleted.
  2039. CreateDatabase();
  2040. RunUntilIdle();
  2041. // Make sure file is deleted when model is removed and store is getting
  2042. // cleaned up.
  2043. EXPECT_FALSE(base::PathExists(file_path));
  2044. // Make sure pref gets cleaned up when path gets deleted.
  2045. EXPECT_TRUE(IsStoreFilesToDeletePrefEmpty());
  2046. }
  2047. TEST_F(OptimizationGuideStoreTest, PurgeInactiveModels) {
  2048. base::HistogramTester histogram_tester;
  2049. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  2050. SeedInitialData(schema_state, 0);
  2051. CreateDatabase();
  2052. InitializeStore(schema_state);
  2053. // Add an update with models that are "inactive".
  2054. base::Time update_time = base::Time().Now();
  2055. std::unique_ptr<StoreUpdateData> update_data =
  2056. guide_store()->CreateUpdateDataForPredictionModels(
  2057. update_time -
  2058. optimization_guide::features::StoredModelsValidDuration());
  2059. ASSERT_TRUE(update_data);
  2060. base::FilePath old_file_path = temp_dir().AppendASCII("model_v1.tflite");
  2061. ASSERT_EQ(static_cast<int32_t>(3), base::WriteFile(old_file_path, "boo", 3));
  2062. proto::ModelInfo model_info;
  2063. model_info.set_version(123);
  2064. SeedPredictionModelUpdateData(
  2065. update_data.get(), proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
  2066. old_file_path, model_info,
  2067. update_time - optimization_guide::features::StoredModelsValidDuration());
  2068. UpdatePredictionModels(std::move(update_data));
  2069. // Add an update with models that are "active".
  2070. std::unique_ptr<StoreUpdateData> update_data2 =
  2071. guide_store()->CreateUpdateDataForPredictionModels(
  2072. update_time +
  2073. optimization_guide::features::StoredModelsValidDuration());
  2074. ASSERT_TRUE(update_data2);
  2075. SeedPredictionModelUpdateData(update_data2.get(),
  2076. proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION);
  2077. UpdatePredictionModels(std::move(update_data2));
  2078. // Make sure both models are in the store.
  2079. OptimizationGuideStore::EntryKey entry_key;
  2080. bool success = guide_store()->FindPredictionModelEntryKey(
  2081. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  2082. ASSERT_TRUE(success);
  2083. success = guide_store()->FindPredictionModelEntryKey(
  2084. proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION, &entry_key);
  2085. ASSERT_TRUE(success);
  2086. PurgeInactiveModels();
  2087. RunUntilIdle();
  2088. // The expired model should be removed.
  2089. EXPECT_FALSE(guide_store()->FindPredictionModelEntryKey(
  2090. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key));
  2091. // Should not purge models that are still active.
  2092. EXPECT_TRUE(guide_store()->FindPredictionModelEntryKey(
  2093. proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION, &entry_key));
  2094. histogram_tester.ExpectUniqueSample(
  2095. "OptimizationGuide.PredictionModelExpired.PainfulPageLoad", true, 1);
  2096. histogram_tester.ExpectUniqueSample(
  2097. "OptimizationGuide.PredictionModelExpiredVersion.PainfulPageLoad", 123,
  2098. 1);
  2099. histogram_tester.ExpectTotalCount(
  2100. "OptimizationGuide.PredictionModelExpired.LanguageDetection", 0);
  2101. histogram_tester.ExpectTotalCount(
  2102. "OptimizationGuide.PredictionModelExpiredVersion.LanguageDetection", 0);
  2103. // Make sure old file path gets persisted to be deleted later.
  2104. EXPECT_FALSE(IsStoreFilesToDeletePrefEmpty());
  2105. // Reset database to clean up old files.
  2106. CreateDatabase();
  2107. RunUntilIdle();
  2108. EXPECT_FALSE(base::PathExists(old_file_path));
  2109. // Make sure pref gets cleaned up when file path gets deleted.
  2110. EXPECT_TRUE(IsStoreFilesToDeletePrefEmpty());
  2111. }
  2112. struct ValidityTestCase {
  2113. std::string test_name;
  2114. bool keep_beyond_valid_duration;
  2115. bool initially_expired;
  2116. bool expect_kept;
  2117. };
  2118. class OptimizationGuideStoreValidityTest
  2119. : public OptimizationGuideStoreTest,
  2120. public ::testing::WithParamInterface<ValidityTestCase> {};
  2121. TEST_P(OptimizationGuideStoreValidityTest, PurgeInactiveModels) {
  2122. const ValidityTestCase& test_case = GetParam();
  2123. base::HistogramTester histogram_tester;
  2124. MetadataSchemaState schema_state = MetadataSchemaState::kValid;
  2125. SeedInitialData(schema_state, 0);
  2126. CreateDatabase();
  2127. InitializeStore(schema_state);
  2128. // Add an update with one model according to ValidityTestCase settings.
  2129. base::Time update_time = base::Time().Now();
  2130. if (test_case.initially_expired) {
  2131. update_time -= optimization_guide::features::StoredModelsValidDuration();
  2132. } else {
  2133. update_time += optimization_guide::features::StoredModelsValidDuration();
  2134. }
  2135. std::unique_ptr<StoreUpdateData> update_data =
  2136. guide_store()->CreateUpdateDataForPredictionModels(update_time);
  2137. ASSERT_TRUE(update_data);
  2138. proto::ModelInfo info;
  2139. info.set_version(123);
  2140. info.set_keep_beyond_valid_duration(test_case.keep_beyond_valid_duration);
  2141. base::FilePath old_file_path = temp_dir().AppendASCII("model_v1.tflite");
  2142. ASSERT_EQ(static_cast<int32_t>(3), base::WriteFile(old_file_path, "boo", 3));
  2143. SeedPredictionModelUpdateData(update_data.get(),
  2144. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
  2145. old_file_path, info, update_time);
  2146. UpdatePredictionModels(std::move(update_data));
  2147. // Add an update with models that are "active" and should be unaffected.
  2148. std::unique_ptr<StoreUpdateData> update_data2 =
  2149. guide_store()->CreateUpdateDataForPredictionModels(
  2150. base::Time().Now() +
  2151. optimization_guide::features::StoredModelsValidDuration());
  2152. ASSERT_TRUE(update_data2);
  2153. SeedPredictionModelUpdateData(update_data2.get(),
  2154. proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION);
  2155. UpdatePredictionModels(std::move(update_data2));
  2156. // Make sure both models are in the store.
  2157. OptimizationGuideStore::EntryKey entry_key;
  2158. bool success = guide_store()->FindPredictionModelEntryKey(
  2159. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
  2160. ASSERT_TRUE(success);
  2161. EXPECT_TRUE(base::PathExists(old_file_path));
  2162. success = guide_store()->FindPredictionModelEntryKey(
  2163. proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION, &entry_key);
  2164. ASSERT_TRUE(success);
  2165. PurgeInactiveModels();
  2166. RunUntilIdle();
  2167. // Verify that the model file, entry key and histogram match expectations for
  2168. // PageLoad.
  2169. EXPECT_EQ(test_case.expect_kept,
  2170. guide_store()->FindPredictionModelEntryKey(
  2171. proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key));
  2172. if (test_case.expect_kept) {
  2173. histogram_tester.ExpectTotalCount(
  2174. "OptimizationGuide.PredictionModelExpired.PainfulPageLoad", 0);
  2175. histogram_tester.ExpectTotalCount(
  2176. "OptimizationGuide.PredictionModelExpiredVersion.PainfulPageLoad", 0);
  2177. } else {
  2178. histogram_tester.ExpectUniqueSample(
  2179. "OptimizationGuide.PredictionModelExpired.PainfulPageLoad", true, 1);
  2180. histogram_tester.ExpectUniqueSample(
  2181. "OptimizationGuide.PredictionModelExpiredVersion.PainfulPageLoad", 123,
  2182. 1);
  2183. }
  2184. // Verify that the other model is not deleted.
  2185. EXPECT_TRUE(guide_store()->FindPredictionModelEntryKey(
  2186. proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION, &entry_key));
  2187. histogram_tester.ExpectTotalCount(
  2188. "OptimizationGuide.PredictionModelExpired.LanguageDetection", 0);
  2189. histogram_tester.ExpectTotalCount(
  2190. "OptimizationGuide.PredictionModelExpiredVersion.LanguageDetection", 0);
  2191. // Make sure file to delete gets put in pref to delete later, if applicable.
  2192. EXPECT_EQ(test_case.expect_kept, IsStoreFilesToDeletePrefEmpty());
  2193. // Reset database to delete files.
  2194. CreateDatabase();
  2195. RunUntilIdle();
  2196. // Make sure old file is deleted if we expect it to.
  2197. EXPECT_EQ(test_case.expect_kept, base::PathExists(old_file_path));
  2198. // Make sure pref doesn't have any items in it.
  2199. EXPECT_TRUE(IsStoreFilesToDeletePrefEmpty());
  2200. }
  2201. INSTANTIATE_TEST_SUITE_P(
  2202. OptimizationGuideStoreValidityTests,
  2203. OptimizationGuideStoreValidityTest,
  2204. testing::ValuesIn<ValidityTestCase>({
  2205. {
  2206. "KeepDespiteInvalidModel",
  2207. /*keep_beyond_valid_duration=*/true,
  2208. /*initially_expired=*/true,
  2209. /*expect_kept=*/true,
  2210. },
  2211. {
  2212. "KeepAndInitiallyValid",
  2213. /*keep_beyond_valid_duration=*/true,
  2214. /*initially_expired=*/false,
  2215. /*expect_kept=*/true,
  2216. },
  2217. {
  2218. "DeleteAndInitiallyValid",
  2219. /*keep_beyond_valid_duration=*/false,
  2220. /*initially_expired=*/false,
  2221. /*expect_kept=*/true,
  2222. },
  2223. // Only in this case should the model be removed.
  2224. {
  2225. "DeleteAndInvalidModel",
  2226. /*keep_beyond_valid_duration=*/false,
  2227. /*initially_expired=*/true,
  2228. /*expect_kept=*/false,
  2229. },
  2230. }),
  2231. [](const testing::TestParamInfo<
  2232. OptimizationGuideStoreValidityTest::ParamType>& info) {
  2233. return info.param.test_name;
  2234. });
  2235. } // namespace optimization_guide