synced_bookmark_tracker_unittest.cc 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/sync_bookmarks/synced_bookmark_tracker.h"
  5. #include "base/base64.h"
  6. #include "base/guid.h"
  7. #include "base/strings/utf_string_conversions.h"
  8. #include "base/test/metrics/histogram_tester.h"
  9. #include "base/test/scoped_feature_list.h"
  10. #include "components/bookmarks/browser/bookmark_model.h"
  11. #include "components/bookmarks/browser/bookmark_node.h"
  12. #include "components/bookmarks/browser/bookmark_utils.h"
  13. #include "components/bookmarks/test/test_bookmark_client.h"
  14. #include "components/sync/base/client_tag_hash.h"
  15. #include "components/sync/base/time.h"
  16. #include "components/sync/base/unique_position.h"
  17. #include "components/sync/protocol/bookmark_model_metadata.pb.h"
  18. #include "components/sync/protocol/entity_data.h"
  19. #include "components/sync/protocol/entity_specifics.pb.h"
  20. #include "components/sync/protocol/model_type_state.pb.h"
  21. #include "components/sync_bookmarks/switches.h"
  22. #include "components/sync_bookmarks/synced_bookmark_tracker_entity.h"
  23. #include "testing/gmock/include/gmock/gmock.h"
  24. #include "testing/gtest/include/gtest/gtest.h"
  25. using testing::Eq;
  26. using testing::IsNull;
  27. using testing::NotNull;
  28. namespace sync_bookmarks {
  29. namespace {
  30. constexpr int kNumPermanentNodes = 3;
  31. const char kBookmarkBarId[] = "bookmark_bar_id";
  32. const char kMobileBookmarksId[] = "synced_bookmarks_id";
  33. const char kOtherBookmarksId[] = "other_bookmarks_id";
  34. // Redefinition of |enum CorruptionReason| in synced_bookmark_tracker.cc to be
  35. // used in tests.
  36. enum class ExpectedCorruptionReason {
  37. NO_CORRUPTION = 0,
  38. MISSING_SERVER_ID = 1,
  39. BOOKMARK_ID_IN_TOMBSTONE = 2,
  40. MISSING_BOOKMARK_ID = 3,
  41. DEPRECATED_COUNT_MISMATCH = 4,
  42. DEPRECATED_IDS_MISMATCH = 5,
  43. DUPLICATED_SERVER_ID = 6,
  44. UNKNOWN_BOOKMARK_ID = 7,
  45. UNTRACKED_BOOKMARK = 8,
  46. BOOKMARK_GUID_MISMATCH = 9,
  47. DUPLICATED_CLIENT_TAG_HASH = 10,
  48. TRACKED_MANAGED_NODE = 11,
  49. MISSING_CLIENT_TAG_HASH = 12,
  50. MISSING_FAVICON_HASH = 13,
  51. kMaxValue = MISSING_FAVICON_HASH
  52. };
  53. sync_pb::EntitySpecifics GenerateSpecifics(const std::string& title,
  54. const std::string& url) {
  55. sync_pb::EntitySpecifics specifics;
  56. specifics.mutable_bookmark()->set_legacy_canonicalized_title(title);
  57. specifics.mutable_bookmark()->set_url(url);
  58. *specifics.mutable_bookmark()->mutable_unique_position() =
  59. syncer::UniquePosition::InitialPosition(
  60. syncer::UniquePosition::RandomSuffix())
  61. .ToProto();
  62. return specifics;
  63. }
  64. // |node| must not be nullptr.
  65. sync_pb::BookmarkMetadata CreateNodeMetadata(
  66. const bookmarks::BookmarkNode* node,
  67. const std::string& server_id) {
  68. sync_pb::BookmarkMetadata bookmark_metadata;
  69. bookmark_metadata.set_id(node->id());
  70. bookmark_metadata.mutable_metadata()->set_server_id(server_id);
  71. bookmark_metadata.mutable_metadata()->set_client_tag_hash(
  72. syncer::ClientTagHash::FromUnhashed(syncer::BOOKMARKS,
  73. node->guid().AsLowercaseString())
  74. .value());
  75. // Required by the validation logic.
  76. if (!node->is_folder()) {
  77. bookmark_metadata.mutable_metadata()->set_bookmark_favicon_hash(123);
  78. }
  79. return bookmark_metadata;
  80. }
  81. sync_pb::BookmarkMetadata CreateTombstoneMetadata(
  82. const std::string& server_id,
  83. const syncer::ClientTagHash& client_tag_hash) {
  84. sync_pb::BookmarkMetadata bookmark_metadata;
  85. bookmark_metadata.mutable_metadata()->set_server_id(server_id);
  86. bookmark_metadata.mutable_metadata()->set_is_deleted(true);
  87. bookmark_metadata.mutable_metadata()->set_sequence_number(1);
  88. bookmark_metadata.mutable_metadata()->set_client_tag_hash(
  89. client_tag_hash.value());
  90. return bookmark_metadata;
  91. }
  92. sync_pb::BookmarkModelMetadata CreateMetadataForPermanentNodes(
  93. const bookmarks::BookmarkModel* bookmark_model) {
  94. sync_pb::BookmarkModelMetadata model_metadata;
  95. model_metadata.mutable_model_type_state()->set_initial_sync_done(true);
  96. *model_metadata.add_bookmarks_metadata() =
  97. CreateNodeMetadata(bookmark_model->bookmark_bar_node(),
  98. /*server_id=*/kBookmarkBarId);
  99. *model_metadata.add_bookmarks_metadata() =
  100. CreateNodeMetadata(bookmark_model->mobile_node(),
  101. /*server_id=*/kMobileBookmarksId);
  102. *model_metadata.add_bookmarks_metadata() =
  103. CreateNodeMetadata(bookmark_model->other_node(),
  104. /*server_id=*/kOtherBookmarksId);
  105. CHECK_EQ(kNumPermanentNodes, model_metadata.bookmarks_metadata_size());
  106. return model_metadata;
  107. }
  108. TEST(SyncedBookmarkTrackerTest, ShouldAddEntity) {
  109. std::unique_ptr<SyncedBookmarkTracker> tracker =
  110. SyncedBookmarkTracker::CreateEmpty(sync_pb::ModelTypeState());
  111. const std::string kSyncId = "SYNC_ID";
  112. const std::string kTitle = "Title";
  113. const GURL kUrl("http://www.foo.com");
  114. const int64_t kId = 1;
  115. const base::GUID kGuid = base::GUID::GenerateRandomV4();
  116. const int64_t kServerVersion = 1000;
  117. const base::Time kCreationTime(base::Time::Now() - base::Seconds(1));
  118. const sync_pb::EntitySpecifics specifics =
  119. GenerateSpecifics(/*title=*/std::string(), /*url=*/std::string());
  120. bookmarks::BookmarkNode node(kId, kGuid, kUrl);
  121. const SyncedBookmarkTrackerEntity* entity =
  122. tracker->Add(&node, kSyncId, kServerVersion, kCreationTime, specifics);
  123. ASSERT_THAT(entity, NotNull());
  124. EXPECT_THAT(entity->bookmark_node(), Eq(&node));
  125. EXPECT_THAT(entity->GetClientTagHash(),
  126. Eq(syncer::ClientTagHash::FromUnhashed(
  127. syncer::BOOKMARKS, kGuid.AsLowercaseString())));
  128. EXPECT_THAT(entity->metadata().server_id(), Eq(kSyncId));
  129. EXPECT_THAT(entity->metadata().server_version(), Eq(kServerVersion));
  130. EXPECT_THAT(entity->metadata().creation_time(),
  131. Eq(syncer::TimeToProtoTime(kCreationTime)));
  132. EXPECT_TRUE(
  133. syncer::UniquePosition::FromProto(entity->metadata().unique_position())
  134. .Equals(syncer::UniquePosition::FromProto(
  135. specifics.bookmark().unique_position())));
  136. EXPECT_THAT(tracker->GetEntityForSyncId(kSyncId), Eq(entity));
  137. EXPECT_THAT(tracker->GetEntityForBookmarkNode(&node), Eq(entity));
  138. EXPECT_THAT(
  139. tracker->GetEntityForClientTagHash(syncer::ClientTagHash::FromUnhashed(
  140. syncer::BOOKMARKS, kGuid.AsLowercaseString())),
  141. Eq(entity));
  142. syncer::EntityData data;
  143. *data.specifics.mutable_bookmark() = specifics.bookmark();
  144. EXPECT_TRUE(entity->MatchesData(data));
  145. EXPECT_THAT(tracker->GetEntityForSyncId("unknown id"), IsNull());
  146. }
  147. TEST(SyncedBookmarkTrackerTest, ShouldRemoveEntity) {
  148. std::unique_ptr<SyncedBookmarkTracker> tracker =
  149. SyncedBookmarkTracker::CreateEmpty(sync_pb::ModelTypeState());
  150. const std::string kSyncId = "SYNC_ID";
  151. const int64_t kId = 1;
  152. const base::GUID kGuid = base::GUID::GenerateRandomV4();
  153. const int64_t kServerVersion = 1000;
  154. const base::Time kModificationTime(base::Time::Now() - base::Seconds(1));
  155. const sync_pb::EntitySpecifics specifics =
  156. GenerateSpecifics(/*title=*/std::string(), /*url=*/std::string());
  157. bookmarks::BookmarkNode node(kId, kGuid, GURL());
  158. const SyncedBookmarkTrackerEntity* entity = tracker->Add(
  159. &node, kSyncId, kServerVersion, kModificationTime, specifics);
  160. ASSERT_THAT(entity, NotNull());
  161. ASSERT_THAT(tracker->GetEntityForSyncId(kSyncId), Eq(entity));
  162. ASSERT_THAT(tracker->GetEntityForBookmarkNode(&node), Eq(entity));
  163. ASSERT_THAT(
  164. tracker->GetEntityForClientTagHash(syncer::ClientTagHash::FromUnhashed(
  165. syncer::BOOKMARKS, kGuid.AsLowercaseString())),
  166. Eq(entity));
  167. tracker->Remove(entity);
  168. EXPECT_THAT(tracker->GetEntityForSyncId(kSyncId), IsNull());
  169. EXPECT_THAT(tracker->GetEntityForBookmarkNode(&node), IsNull());
  170. EXPECT_THAT(
  171. tracker->GetEntityForClientTagHash(syncer::ClientTagHash::FromUnhashed(
  172. syncer::BOOKMARKS, kGuid.AsLowercaseString())),
  173. IsNull());
  174. }
  175. TEST(SyncedBookmarkTrackerTest, ShouldBuildBookmarkModelMetadata) {
  176. std::unique_ptr<SyncedBookmarkTracker> tracker =
  177. SyncedBookmarkTracker::CreateEmpty(sync_pb::ModelTypeState());
  178. const std::string kSyncId = "SYNC_ID";
  179. const std::string kTitle = "Title";
  180. const GURL kUrl("http://www.foo.com");
  181. const int64_t kId = 1;
  182. const int64_t kServerVersion = 1000;
  183. const base::Time kCreationTime(base::Time::Now() - base::Seconds(1));
  184. const sync_pb::EntitySpecifics specifics =
  185. GenerateSpecifics(/*title=*/std::string(), /*url=*/std::string());
  186. bookmarks::BookmarkNode node(kId, base::GUID::GenerateRandomV4(), kUrl);
  187. tracker->Add(&node, kSyncId, kServerVersion, kCreationTime, specifics);
  188. sync_pb::BookmarkModelMetadata bookmark_model_metadata =
  189. tracker->BuildBookmarkModelMetadata();
  190. ASSERT_THAT(bookmark_model_metadata.bookmarks_metadata().size(), Eq(1));
  191. EXPECT_THAT(
  192. bookmark_model_metadata.bookmarks_metadata(0).metadata().server_id(),
  193. Eq(kSyncId));
  194. }
  195. TEST(SyncedBookmarkTrackerTest,
  196. ShouldRequireCommitRequestWhenSequenceNumberIsIncremented) {
  197. std::unique_ptr<SyncedBookmarkTracker> tracker =
  198. SyncedBookmarkTracker::CreateEmpty(sync_pb::ModelTypeState());
  199. const std::string kSyncId = "SYNC_ID";
  200. const int64_t kId = 1;
  201. const int64_t kServerVersion = 1000;
  202. const base::Time kModificationTime(base::Time::Now() - base::Seconds(1));
  203. const sync_pb::EntitySpecifics specifics =
  204. GenerateSpecifics(/*title=*/std::string(), /*url=*/std::string());
  205. bookmarks::BookmarkNode node(kId, base::GUID::GenerateRandomV4(), GURL());
  206. const SyncedBookmarkTrackerEntity* entity = tracker->Add(
  207. &node, kSyncId, kServerVersion, kModificationTime, specifics);
  208. EXPECT_THAT(tracker->HasLocalChanges(), Eq(false));
  209. tracker->IncrementSequenceNumber(entity);
  210. EXPECT_THAT(tracker->HasLocalChanges(), Eq(true));
  211. }
  212. TEST(SyncedBookmarkTrackerTest, ShouldAckSequenceNumber) {
  213. std::unique_ptr<SyncedBookmarkTracker> tracker =
  214. SyncedBookmarkTracker::CreateEmpty(sync_pb::ModelTypeState());
  215. const std::string kSyncId = "SYNC_ID";
  216. const int64_t kId = 1;
  217. const int64_t kServerVersion = 1000;
  218. const base::Time kModificationTime(base::Time::Now() - base::Seconds(1));
  219. const sync_pb::EntitySpecifics specifics =
  220. GenerateSpecifics(/*title=*/std::string(), /*url=*/std::string());
  221. bookmarks::BookmarkNode node(kId, base::GUID::GenerateRandomV4(), GURL());
  222. const SyncedBookmarkTrackerEntity* entity = tracker->Add(
  223. &node, kSyncId, kServerVersion, kModificationTime, specifics);
  224. // Test simple scenario of ack'ing an incrememented sequence number.
  225. EXPECT_THAT(tracker->HasLocalChanges(), Eq(false));
  226. tracker->IncrementSequenceNumber(entity);
  227. EXPECT_THAT(tracker->HasLocalChanges(), Eq(true));
  228. tracker->AckSequenceNumber(entity);
  229. EXPECT_THAT(tracker->HasLocalChanges(), Eq(false));
  230. // Test ack'ing of a multiple times incremented sequence number.
  231. tracker->IncrementSequenceNumber(entity);
  232. EXPECT_THAT(tracker->HasLocalChanges(), Eq(true));
  233. tracker->IncrementSequenceNumber(entity);
  234. tracker->IncrementSequenceNumber(entity);
  235. EXPECT_THAT(tracker->HasLocalChanges(), Eq(true));
  236. tracker->AckSequenceNumber(entity);
  237. EXPECT_THAT(tracker->HasLocalChanges(), Eq(false));
  238. }
  239. TEST(SyncedBookmarkTrackerTest, ShouldUpdateUponCommitResponseWithNewId) {
  240. std::unique_ptr<SyncedBookmarkTracker> tracker =
  241. SyncedBookmarkTracker::CreateEmpty(sync_pb::ModelTypeState());
  242. const std::string kSyncId = "SYNC_ID";
  243. const std::string kNewSyncId = "NEW_SYNC_ID";
  244. const int64_t kId = 1;
  245. const int64_t kServerVersion = 1000;
  246. const int64_t kNewServerVersion = 1001;
  247. const base::Time kModificationTime(base::Time::Now() - base::Seconds(1));
  248. const sync_pb::EntitySpecifics specifics =
  249. GenerateSpecifics(/*title=*/std::string(), /*url=*/std::string());
  250. bookmarks::BookmarkNode node(kId, base::GUID::GenerateRandomV4(), GURL());
  251. const SyncedBookmarkTrackerEntity* entity = tracker->Add(
  252. &node, kSyncId, kServerVersion, kModificationTime, specifics);
  253. ASSERT_THAT(entity, NotNull());
  254. // Initially only the old ID should be tracked.
  255. ASSERT_THAT(tracker->GetEntityForSyncId(kSyncId), Eq(entity));
  256. ASSERT_THAT(tracker->GetEntityForSyncId(kNewSyncId), IsNull());
  257. // Receive a commit response with a changed id.
  258. tracker->UpdateUponCommitResponse(entity, kNewSyncId, kNewServerVersion,
  259. /*acked_sequence_number=*/1);
  260. // Old id shouldn't be there, but the new one should.
  261. EXPECT_THAT(tracker->GetEntityForSyncId(kSyncId), IsNull());
  262. EXPECT_THAT(tracker->GetEntityForSyncId(kNewSyncId), Eq(entity));
  263. EXPECT_THAT(entity->metadata().server_id(), Eq(kNewSyncId));
  264. EXPECT_THAT(entity->bookmark_node(), Eq(&node));
  265. EXPECT_THAT(entity->metadata().server_version(), Eq(kNewServerVersion));
  266. }
  267. TEST(SyncedBookmarkTrackerTest, ShouldUpdateId) {
  268. std::unique_ptr<SyncedBookmarkTracker> tracker =
  269. SyncedBookmarkTracker::CreateEmpty(sync_pb::ModelTypeState());
  270. const std::string kSyncId = "SYNC_ID";
  271. const std::string kNewSyncId = "NEW_SYNC_ID";
  272. const int64_t kServerVersion = 1000;
  273. const base::Time kModificationTime(base::Time::Now() - base::Seconds(1));
  274. const sync_pb::EntitySpecifics specifics =
  275. GenerateSpecifics(/*title=*/std::string(), /*url=*/std::string());
  276. bookmarks::BookmarkNode node(/*id=*/1, base::GUID::GenerateRandomV4(),
  277. GURL());
  278. // Track a sync entity.
  279. const SyncedBookmarkTrackerEntity* entity = tracker->Add(
  280. &node, kSyncId, kServerVersion, kModificationTime, specifics);
  281. ASSERT_THAT(entity, NotNull());
  282. // Update the sync id.
  283. tracker->UpdateSyncIdIfNeeded(entity, kNewSyncId);
  284. // Old id shouldn't be there, but the new one should.
  285. EXPECT_THAT(tracker->GetEntityForSyncId(kSyncId), IsNull());
  286. EXPECT_THAT(tracker->GetEntityForSyncId(kNewSyncId), Eq(entity));
  287. EXPECT_THAT(entity->metadata().server_id(), Eq(kNewSyncId));
  288. EXPECT_THAT(entity->bookmark_node(), Eq(&node));
  289. EXPECT_THAT(entity->metadata().server_version(), Eq(kServerVersion));
  290. }
  291. TEST(SyncedBookmarkTrackerTest,
  292. ShouldMaintainTombstoneOrderBetweenCtorAndBuildBookmarkModelMetadata) {
  293. // Feed a metadata batch of 5 entries to the constructor of the tracker.
  294. // First 2 are for node, and the last 4 are for tombstones.
  295. // Server ids.
  296. const std::string kId0 = "id0";
  297. const std::string kId1 = "id1";
  298. const std::string kId2 = "id2";
  299. const std::string kId3 = "id3";
  300. const std::string kId4 = "id4";
  301. std::unique_ptr<bookmarks::BookmarkModel> bookmark_model =
  302. bookmarks::TestBookmarkClient::CreateModel();
  303. const bookmarks::BookmarkNode* bookmark_bar_node =
  304. bookmark_model->bookmark_bar_node();
  305. const bookmarks::BookmarkNode* node0 = bookmark_model->AddFolder(
  306. /*parent=*/bookmark_bar_node, /*index=*/0, u"node0");
  307. const bookmarks::BookmarkNode* node1 = bookmark_model->AddFolder(
  308. /*parent=*/bookmark_bar_node, /*index=*/0, u"node1");
  309. sync_pb::BookmarkModelMetadata initial_model_metadata =
  310. CreateMetadataForPermanentNodes(bookmark_model.get());
  311. *initial_model_metadata.add_bookmarks_metadata() =
  312. CreateNodeMetadata(node0, /*server_id=*/kId0);
  313. *initial_model_metadata.add_bookmarks_metadata() =
  314. CreateNodeMetadata(node1, /*server_id=*/kId1);
  315. *initial_model_metadata.add_bookmarks_metadata() = CreateTombstoneMetadata(
  316. /*server_id=*/kId2, syncer::ClientTagHash::FromHashed("clienttaghash2"));
  317. *initial_model_metadata.add_bookmarks_metadata() = CreateTombstoneMetadata(
  318. /*server_id=*/kId3, syncer::ClientTagHash::FromHashed("clienttaghash3"));
  319. *initial_model_metadata.add_bookmarks_metadata() = CreateTombstoneMetadata(
  320. /*server_id=*/kId4, syncer::ClientTagHash::FromHashed("clienttaghash4"));
  321. std::unique_ptr<SyncedBookmarkTracker> tracker =
  322. SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  323. bookmark_model.get(), std::move(initial_model_metadata));
  324. ASSERT_THAT(tracker, NotNull());
  325. const sync_pb::BookmarkModelMetadata output_model_metadata =
  326. tracker->BuildBookmarkModelMetadata();
  327. // Tombstones should be the last 3 entries in the metadata and in the same
  328. // order as given to the constructor.
  329. ASSERT_THAT(output_model_metadata.bookmarks_metadata().size(),
  330. Eq(kNumPermanentNodes + 5));
  331. EXPECT_THAT(output_model_metadata.bookmarks_metadata(kNumPermanentNodes + 2)
  332. .metadata()
  333. .server_id(),
  334. Eq(kId2));
  335. EXPECT_THAT(output_model_metadata.bookmarks_metadata(kNumPermanentNodes + 3)
  336. .metadata()
  337. .server_id(),
  338. Eq(kId3));
  339. EXPECT_THAT(output_model_metadata.bookmarks_metadata(kNumPermanentNodes + 4)
  340. .metadata()
  341. .server_id(),
  342. Eq(kId4));
  343. }
  344. TEST(SyncedBookmarkTrackerTest,
  345. ShouldMaintainOrderOfMarkDeletedCallsWhenBuildBookmarkModelMetadata) {
  346. // Server ids.
  347. const std::string kId0 = "id0";
  348. const std::string kId1 = "id1";
  349. const std::string kId2 = "id2";
  350. const std::string kId3 = "id3";
  351. const std::string kId4 = "id4";
  352. std::unique_ptr<bookmarks::BookmarkModel> bookmark_model =
  353. bookmarks::TestBookmarkClient::CreateModel();
  354. const bookmarks::BookmarkNode* bookmark_bar_node =
  355. bookmark_model->bookmark_bar_node();
  356. const bookmarks::BookmarkNode* node0 = bookmark_model->AddFolder(
  357. /*parent=*/bookmark_bar_node, /*index=*/0, u"node0");
  358. const bookmarks::BookmarkNode* node1 = bookmark_model->AddFolder(
  359. /*parent=*/bookmark_bar_node, /*index=*/0, u"node1");
  360. const bookmarks::BookmarkNode* node2 = bookmark_model->AddFolder(
  361. /*parent=*/bookmark_bar_node, /*index=*/0, u"node2");
  362. const bookmarks::BookmarkNode* node3 = bookmark_model->AddFolder(
  363. /*parent=*/bookmark_bar_node, /*index=*/0, u"node3");
  364. const bookmarks::BookmarkNode* node4 = bookmark_model->AddFolder(
  365. /*parent=*/bookmark_bar_node, /*index=*/0, u"node4");
  366. sync_pb::BookmarkModelMetadata initial_model_metadata =
  367. CreateMetadataForPermanentNodes(bookmark_model.get());
  368. *initial_model_metadata.add_bookmarks_metadata() =
  369. CreateNodeMetadata(node0, /*server_id=*/kId0);
  370. *initial_model_metadata.add_bookmarks_metadata() =
  371. CreateNodeMetadata(node1, /*server_id=*/kId1);
  372. *initial_model_metadata.add_bookmarks_metadata() =
  373. CreateNodeMetadata(node2, /*server_id=*/kId2);
  374. *initial_model_metadata.add_bookmarks_metadata() =
  375. CreateNodeMetadata(node3, /*server_id=*/kId3);
  376. *initial_model_metadata.add_bookmarks_metadata() =
  377. CreateNodeMetadata(node4, /*server_id=*/kId4);
  378. std::unique_ptr<SyncedBookmarkTracker> tracker =
  379. SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  380. bookmark_model.get(), std::move(initial_model_metadata));
  381. ASSERT_THAT(tracker, NotNull());
  382. // Mark entities deleted in that order kId2, kId4, kId1
  383. tracker->MarkDeleted(tracker->GetEntityForSyncId(kId2));
  384. tracker->MarkDeleted(tracker->GetEntityForSyncId(kId4));
  385. tracker->MarkDeleted(tracker->GetEntityForSyncId(kId1));
  386. const sync_pb::BookmarkModelMetadata output_model_metadata =
  387. tracker->BuildBookmarkModelMetadata();
  388. // Tombstones should be the last 3 entries in the metadata and in the same as
  389. // calling MarkDeleted().
  390. ASSERT_THAT(output_model_metadata.bookmarks_metadata().size(),
  391. Eq(kNumPermanentNodes + 5));
  392. EXPECT_THAT(output_model_metadata.bookmarks_metadata(kNumPermanentNodes + 2)
  393. .metadata()
  394. .server_id(),
  395. Eq(kId2));
  396. EXPECT_THAT(output_model_metadata.bookmarks_metadata(kNumPermanentNodes + 3)
  397. .metadata()
  398. .server_id(),
  399. Eq(kId4));
  400. EXPECT_THAT(output_model_metadata.bookmarks_metadata(kNumPermanentNodes + 4)
  401. .metadata()
  402. .server_id(),
  403. Eq(kId1));
  404. }
  405. TEST(SyncedBookmarkTrackerTest, ShouldMarkDeleted) {
  406. std::unique_ptr<SyncedBookmarkTracker> tracker =
  407. SyncedBookmarkTracker::CreateEmpty(sync_pb::ModelTypeState());
  408. const std::string kSyncId = "SYNC_ID";
  409. const int64_t kId = 1;
  410. const base::GUID kGuid = base::GUID::GenerateRandomV4();
  411. const int64_t kServerVersion = 1000;
  412. const base::Time kModificationTime(base::Time::Now() - base::Seconds(1));
  413. const sync_pb::EntitySpecifics specifics =
  414. GenerateSpecifics(/*title=*/std::string(), /*url=*/std::string());
  415. bookmarks::BookmarkNode node(kId, kGuid, GURL());
  416. const SyncedBookmarkTrackerEntity* entity = tracker->Add(
  417. &node, kSyncId, kServerVersion, kModificationTime, specifics);
  418. ASSERT_THAT(tracker->TrackedUncommittedTombstonesCount(), Eq(0U));
  419. ASSERT_THAT(tracker->GetEntityForSyncId(kSyncId), Eq(entity));
  420. ASSERT_THAT(tracker->GetEntityForBookmarkNode(&node), Eq(entity));
  421. ASSERT_THAT(
  422. tracker->GetEntityForClientTagHash(syncer::ClientTagHash::FromUnhashed(
  423. syncer::BOOKMARKS, kGuid.AsLowercaseString())),
  424. Eq(entity));
  425. ASSERT_FALSE(entity->metadata().is_deleted());
  426. ASSERT_THAT(entity->bookmark_node(), Eq(&node));
  427. // Delete the bookmark, leading to a pending deletion (local tombstone).
  428. tracker->MarkDeleted(entity);
  429. EXPECT_THAT(tracker->TrackedUncommittedTombstonesCount(), Eq(1U));
  430. EXPECT_THAT(tracker->GetEntityForSyncId(kSyncId), Eq(entity));
  431. EXPECT_THAT(tracker->GetEntityForBookmarkNode(&node), IsNull());
  432. EXPECT_THAT(
  433. tracker->GetEntityForClientTagHash(syncer::ClientTagHash::FromUnhashed(
  434. syncer::BOOKMARKS, kGuid.AsLowercaseString())),
  435. Eq(entity));
  436. EXPECT_TRUE(entity->metadata().is_deleted());
  437. EXPECT_THAT(entity->bookmark_node(), IsNull());
  438. }
  439. TEST(SyncedBookmarkTrackerTest, ShouldUndeleteTombstone) {
  440. std::unique_ptr<SyncedBookmarkTracker> tracker =
  441. SyncedBookmarkTracker::CreateEmpty(sync_pb::ModelTypeState());
  442. const std::string kSyncId = "SYNC_ID";
  443. const int64_t kId = 1;
  444. const base::GUID kGuid = base::GUID::GenerateRandomV4();
  445. const int64_t kServerVersion = 1000;
  446. const base::Time kModificationTime(base::Time::Now() - base::Seconds(1));
  447. const sync_pb::EntitySpecifics specifics =
  448. GenerateSpecifics(/*title=*/std::string(), /*url=*/std::string());
  449. bookmarks::BookmarkNode node(kId, kGuid, GURL());
  450. const SyncedBookmarkTrackerEntity* entity = tracker->Add(
  451. &node, kSyncId, kServerVersion, kModificationTime, specifics);
  452. ASSERT_THAT(tracker->TrackedUncommittedTombstonesCount(), Eq(0U));
  453. ASSERT_THAT(tracker->GetEntityForSyncId(kSyncId), Eq(entity));
  454. // Delete the bookmark, leading to a pending deletion (local tombstone).
  455. tracker->MarkDeleted(entity);
  456. ASSERT_THAT(entity->bookmark_node(), IsNull());
  457. ASSERT_TRUE(entity->metadata().is_deleted());
  458. ASSERT_THAT(tracker->TrackedUncommittedTombstonesCount(), Eq(1U));
  459. ASSERT_THAT(tracker->GetEntityForBookmarkNode(&node), IsNull());
  460. ASSERT_THAT(
  461. tracker->GetEntityForClientTagHash(syncer::ClientTagHash::FromUnhashed(
  462. syncer::BOOKMARKS, kGuid.AsLowercaseString())),
  463. Eq(entity));
  464. // Undelete it.
  465. tracker->UndeleteTombstoneForBookmarkNode(entity, &node);
  466. EXPECT_THAT(entity->bookmark_node(), NotNull());
  467. EXPECT_FALSE(entity->metadata().is_deleted());
  468. EXPECT_THAT(tracker->TrackedUncommittedTombstonesCount(), Eq(0U));
  469. ASSERT_THAT(tracker->GetEntityForBookmarkNode(&node), Eq(entity));
  470. EXPECT_THAT(
  471. tracker->GetEntityForClientTagHash(syncer::ClientTagHash::FromUnhashed(
  472. syncer::BOOKMARKS, kGuid.AsLowercaseString())),
  473. Eq(entity));
  474. }
  475. TEST(SyncedBookmarkTrackerTest,
  476. ShouldOrderParentUpdatesBeforeChildUpdatesAndDeletionsComeLast) {
  477. // Construct this structure:
  478. // bookmark_bar
  479. // |- node0
  480. // |- node1
  481. // |- node2
  482. std::unique_ptr<bookmarks::BookmarkModel> bookmark_model =
  483. bookmarks::TestBookmarkClient::CreateModel();
  484. const bookmarks::BookmarkNode* bookmark_bar_node =
  485. bookmark_model->bookmark_bar_node();
  486. const bookmarks::BookmarkNode* node0 = bookmark_model->AddFolder(
  487. /*parent=*/bookmark_bar_node, /*index=*/0, u"node0");
  488. const bookmarks::BookmarkNode* node1 = bookmark_model->AddFolder(
  489. /*parent=*/node0, /*index=*/0, u"node1");
  490. const bookmarks::BookmarkNode* node2 = bookmark_model->AddFolder(
  491. /*parent=*/node1, /*index=*/0, u"node2");
  492. // Server ids.
  493. const std::string kId0 = "id0";
  494. const std::string kId1 = "id1";
  495. const std::string kId2 = "id2";
  496. const std::string kId3 = "id3";
  497. // Prepare the metadata with shuffled order.
  498. sync_pb::BookmarkModelMetadata model_metadata =
  499. CreateMetadataForPermanentNodes(bookmark_model.get());
  500. *model_metadata.add_bookmarks_metadata() =
  501. CreateNodeMetadata(node1, /*server_id=*/kId1);
  502. *model_metadata.add_bookmarks_metadata() = CreateTombstoneMetadata(
  503. /*server_id=*/kId3, syncer::ClientTagHash::FromHashed("clienttaghash3"));
  504. *model_metadata.add_bookmarks_metadata() =
  505. CreateNodeMetadata(node2, /*server_id=*/kId2);
  506. *model_metadata.add_bookmarks_metadata() =
  507. CreateNodeMetadata(node0, /*server_id=*/kId0);
  508. std::unique_ptr<SyncedBookmarkTracker> tracker =
  509. SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  510. bookmark_model.get(), std::move(model_metadata));
  511. ASSERT_THAT(tracker, NotNull());
  512. // Mark the entities that they have local changes. (in shuffled order just to
  513. // verify the tracker doesn't simply maintain the order of updates similar to
  514. // with deletions).
  515. tracker->IncrementSequenceNumber(tracker->GetEntityForSyncId(kId3));
  516. tracker->IncrementSequenceNumber(tracker->GetEntityForSyncId(kId1));
  517. tracker->IncrementSequenceNumber(tracker->GetEntityForSyncId(kId2));
  518. tracker->IncrementSequenceNumber(tracker->GetEntityForSyncId(kId0));
  519. std::vector<const SyncedBookmarkTrackerEntity*> entities_with_local_change =
  520. tracker->GetEntitiesWithLocalChanges();
  521. ASSERT_THAT(entities_with_local_change.size(), Eq(4U));
  522. // Verify updates are in parent before child order node0 --> node1 --> node2.
  523. EXPECT_THAT(entities_with_local_change[0]->metadata().server_id(), Eq(kId0));
  524. EXPECT_THAT(entities_with_local_change[1]->metadata().server_id(), Eq(kId1));
  525. EXPECT_THAT(entities_with_local_change[2]->metadata().server_id(), Eq(kId2));
  526. // Verify that deletion is the last entry.
  527. EXPECT_THAT(entities_with_local_change[3]->metadata().server_id(), Eq(kId3));
  528. }
  529. TEST(SyncedBookmarkTrackerTest, ShouldNotInvalidateMetadata) {
  530. std::unique_ptr<bookmarks::BookmarkModel> model =
  531. bookmarks::TestBookmarkClient::CreateModel();
  532. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  533. const bookmarks::BookmarkNode* node = model->AddFolder(
  534. /*parent=*/bookmark_bar_node, /*index=*/0, u"node0");
  535. sync_pb::BookmarkModelMetadata model_metadata =
  536. CreateMetadataForPermanentNodes(model.get());
  537. // Add entry for the managed node.
  538. *model_metadata.add_bookmarks_metadata() =
  539. CreateNodeMetadata(node, /*server_id=*/"NodeId");
  540. // Add a tombstone entry.
  541. *model_metadata.add_bookmarks_metadata() = CreateTombstoneMetadata(
  542. /*server_id=*/"tombstoneId",
  543. syncer::ClientTagHash::FromHashed("clienttaghash"));
  544. base::HistogramTester histogram_tester;
  545. EXPECT_THAT(SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  546. model.get(), std::move(model_metadata)),
  547. NotNull());
  548. histogram_tester.ExpectUniqueSample(
  549. "Sync.BookmarksModelMetadataCorruptionReason",
  550. /*sample=*/ExpectedCorruptionReason::NO_CORRUPTION,
  551. /*expected_bucket_count=*/1);
  552. }
  553. TEST(SyncedBookmarkTrackerTest, ShouldNotRequireClientTagsForPermanentNodes) {
  554. std::unique_ptr<bookmarks::BookmarkModel> model =
  555. bookmarks::TestBookmarkClient::CreateModel();
  556. sync_pb::BookmarkModelMetadata model_metadata =
  557. CreateMetadataForPermanentNodes(model.get());
  558. // Clear the client tag hash field in metadata, which is irrelevant for
  559. // permanent nodes (and some older versions of the browser didn't populate).
  560. for (sync_pb::BookmarkMetadata& bookmark_metadata :
  561. *model_metadata.mutable_bookmarks_metadata()) {
  562. bookmark_metadata.mutable_metadata()->clear_client_tag_hash();
  563. }
  564. std::unique_ptr<SyncedBookmarkTracker> tracker =
  565. SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  566. model.get(), std::move(model_metadata));
  567. ASSERT_THAT(tracker, NotNull());
  568. EXPECT_THAT(tracker->GetEntityForSyncId(kBookmarkBarId), NotNull());
  569. EXPECT_THAT(tracker->GetEntityForSyncId(kMobileBookmarksId), NotNull());
  570. EXPECT_THAT(tracker->GetEntityForSyncId(kOtherBookmarksId), NotNull());
  571. }
  572. TEST(SyncedBookmarkTrackerTest, ShouldInvalidateMetadataIfMissingMobileFolder) {
  573. std::unique_ptr<bookmarks::BookmarkModel> model =
  574. bookmarks::TestBookmarkClient::CreateModel();
  575. sync_pb::BookmarkModelMetadata model_metadata;
  576. model_metadata.mutable_model_type_state()->set_initial_sync_done(true);
  577. // Add entries for all the permanent nodes except for the Mobile bookmarks
  578. // folder.
  579. *model_metadata.add_bookmarks_metadata() =
  580. CreateNodeMetadata(model->bookmark_bar_node(),
  581. /*server_id=*/kBookmarkBarId);
  582. *model_metadata.add_bookmarks_metadata() =
  583. CreateNodeMetadata(model->other_node(),
  584. /*server_id=*/kOtherBookmarksId);
  585. base::HistogramTester histogram_tester;
  586. EXPECT_THAT(SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  587. model.get(), std::move(model_metadata)),
  588. IsNull());
  589. histogram_tester.ExpectUniqueSample(
  590. "Sync.BookmarksModelMetadataCorruptionReason",
  591. /*sample=*/ExpectedCorruptionReason::UNTRACKED_BOOKMARK,
  592. /*expected_bucket_count=*/1);
  593. }
  594. TEST(SyncedBookmarkTrackerTest, ShouldInvalidateMetadataIfMissingServerId) {
  595. std::unique_ptr<bookmarks::BookmarkModel> model =
  596. bookmarks::TestBookmarkClient::CreateModel();
  597. sync_pb::BookmarkModelMetadata model_metadata =
  598. CreateMetadataForPermanentNodes(model.get());
  599. // Remove a server ID to a permanent node.
  600. model_metadata.mutable_bookmarks_metadata(0)
  601. ->mutable_metadata()
  602. ->clear_server_id();
  603. base::HistogramTester histogram_tester;
  604. EXPECT_THAT(SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  605. model.get(), std::move(model_metadata)),
  606. IsNull());
  607. histogram_tester.ExpectUniqueSample(
  608. "Sync.BookmarksModelMetadataCorruptionReason",
  609. /*sample=*/ExpectedCorruptionReason::MISSING_SERVER_ID,
  610. /*expected_bucket_count=*/1);
  611. }
  612. TEST(SyncedBookmarkTrackerTest,
  613. ShouldInvalidateMetadataIfMissingLocalBookmarkId) {
  614. std::unique_ptr<bookmarks::BookmarkModel> model =
  615. bookmarks::TestBookmarkClient::CreateModel();
  616. sync_pb::BookmarkModelMetadata model_metadata =
  617. CreateMetadataForPermanentNodes(model.get());
  618. const bookmarks::BookmarkNode* node = model->AddFolder(
  619. /*parent=*/model->bookmark_bar_node(), /*index=*/0, u"node");
  620. *model_metadata.add_bookmarks_metadata() =
  621. CreateNodeMetadata(node, /*server_id=*/"serverid");
  622. // Remove the local bookmark ID.
  623. model_metadata.mutable_bookmarks_metadata()->rbegin()->clear_id();
  624. base::HistogramTester histogram_tester;
  625. EXPECT_THAT(SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  626. model.get(), std::move(model_metadata)),
  627. IsNull());
  628. histogram_tester.ExpectUniqueSample(
  629. "Sync.BookmarksModelMetadataCorruptionReason",
  630. /*sample=*/ExpectedCorruptionReason::MISSING_BOOKMARK_ID,
  631. /*expected_bucket_count=*/1);
  632. }
  633. TEST(SyncedBookmarkTrackerTest,
  634. ShouldInvalidateMetadataIfTombstoneHasBookmarkId) {
  635. std::unique_ptr<bookmarks::BookmarkModel> model =
  636. bookmarks::TestBookmarkClient::CreateModel();
  637. sync_pb::BookmarkModelMetadata model_metadata =
  638. CreateMetadataForPermanentNodes(model.get());
  639. *model_metadata.add_bookmarks_metadata() = CreateTombstoneMetadata(
  640. /*server_id=*/"serverid",
  641. syncer::ClientTagHash::FromHashed("clienttaghash"));
  642. // Add a node ID to the tombstone.
  643. model_metadata.mutable_bookmarks_metadata()->rbegin()->set_id(1234);
  644. base::HistogramTester histogram_tester;
  645. EXPECT_THAT(SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  646. model.get(), std::move(model_metadata)),
  647. IsNull());
  648. histogram_tester.ExpectUniqueSample(
  649. "Sync.BookmarksModelMetadataCorruptionReason",
  650. /*sample=*/ExpectedCorruptionReason::BOOKMARK_ID_IN_TOMBSTONE,
  651. /*expected_bucket_count=*/1);
  652. }
  653. TEST(SyncedBookmarkTrackerTest,
  654. ShouldInvalidateMetadataIfUnknownLocalBookmarkId) {
  655. std::unique_ptr<bookmarks::BookmarkModel> model =
  656. bookmarks::TestBookmarkClient::CreateModel();
  657. sync_pb::BookmarkModelMetadata model_metadata =
  658. CreateMetadataForPermanentNodes(model.get());
  659. const bookmarks::BookmarkNode* node = model->AddFolder(
  660. /*parent=*/model->bookmark_bar_node(), /*index=*/0, u"node");
  661. *model_metadata.add_bookmarks_metadata() =
  662. CreateNodeMetadata(node, /*server_id=*/"serverid");
  663. // Set an arbitrary local node ID that won't match anything in BookmarkModel.
  664. model_metadata.mutable_bookmarks_metadata()->rbegin()->set_id(123456);
  665. base::HistogramTester histogram_tester;
  666. EXPECT_THAT(SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  667. model.get(), std::move(model_metadata)),
  668. IsNull());
  669. histogram_tester.ExpectUniqueSample(
  670. "Sync.BookmarksModelMetadataCorruptionReason",
  671. /*sample=*/ExpectedCorruptionReason::UNKNOWN_BOOKMARK_ID,
  672. /*expected_bucket_count=*/1);
  673. }
  674. TEST(SyncedBookmarkTrackerTest, ShouldInvalidateMetadataIfGuidMismatch) {
  675. std::unique_ptr<bookmarks::BookmarkModel> model =
  676. bookmarks::TestBookmarkClient::CreateModel();
  677. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  678. const bookmarks::BookmarkNode* node0 = model->AddFolder(
  679. /*parent=*/bookmark_bar_node, /*index=*/0, u"node0");
  680. sync_pb::BookmarkModelMetadata model_metadata =
  681. CreateMetadataForPermanentNodes(model.get());
  682. sync_pb::BookmarkMetadata* node0_metadata =
  683. model_metadata.add_bookmarks_metadata();
  684. *node0_metadata = CreateNodeMetadata(node0, /*server_id=*/"id0");
  685. // Set a mismatching client tag hash.
  686. node0_metadata->mutable_metadata()->set_client_tag_hash("corrupthash");
  687. base::HistogramTester histogram_tester;
  688. EXPECT_THAT(SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  689. model.get(), std::move(model_metadata)),
  690. IsNull());
  691. histogram_tester.ExpectUniqueSample(
  692. "Sync.BookmarksModelMetadataCorruptionReason",
  693. /*sample=*/ExpectedCorruptionReason::BOOKMARK_GUID_MISMATCH,
  694. /*expected_bucket_count=*/1);
  695. }
  696. TEST(SyncedBookmarkTrackerTest,
  697. ShouldInvalidateMetadataIfTombstoneHasDuplicatedClientTagHash) {
  698. std::unique_ptr<bookmarks::BookmarkModel> model =
  699. bookmarks::TestBookmarkClient::CreateModel();
  700. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  701. const bookmarks::BookmarkNode* node0 = model->AddFolder(
  702. /*parent=*/bookmark_bar_node, /*index=*/0, u"node0");
  703. sync_pb::BookmarkModelMetadata model_metadata =
  704. CreateMetadataForPermanentNodes(model.get());
  705. sync_pb::BookmarkMetadata* node0_metadata =
  706. model_metadata.add_bookmarks_metadata();
  707. *node0_metadata = CreateNodeMetadata(node0, /*server_id=*/"id0");
  708. const syncer::ClientTagHash client_tag_hash =
  709. syncer::ClientTagHash::FromUnhashed(syncer::BOOKMARKS,
  710. node0->guid().AsLowercaseString());
  711. node0_metadata->mutable_metadata()->set_client_tag_hash(
  712. client_tag_hash.value());
  713. // Add the duplicate tombstone with a different server id but same client tag
  714. // hash.
  715. sync_pb::BookmarkMetadata* tombstone_metadata =
  716. model_metadata.add_bookmarks_metadata();
  717. *tombstone_metadata = CreateTombstoneMetadata(
  718. "id1", syncer::ClientTagHash::FromHashed("clienttaghash1"));
  719. tombstone_metadata->mutable_metadata()->set_client_tag_hash(
  720. client_tag_hash.value());
  721. base::HistogramTester histogram_tester;
  722. EXPECT_THAT(SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  723. model.get(), std::move(model_metadata)),
  724. IsNull());
  725. histogram_tester.ExpectUniqueSample(
  726. "Sync.BookmarksModelMetadataCorruptionReason",
  727. /*sample=*/ExpectedCorruptionReason::DUPLICATED_CLIENT_TAG_HASH,
  728. /*expected_bucket_count=*/1);
  729. }
  730. TEST(SyncedBookmarkTrackerTest,
  731. ShouldInvalidateMetadataIfMissingClientTagHash) {
  732. std::unique_ptr<bookmarks::BookmarkModel> model =
  733. bookmarks::TestBookmarkClient::CreateModel();
  734. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  735. const bookmarks::BookmarkNode* node0 = model->AddFolder(
  736. /*parent=*/bookmark_bar_node, /*index=*/0, u"node0");
  737. sync_pb::BookmarkModelMetadata model_metadata =
  738. CreateMetadataForPermanentNodes(model.get());
  739. sync_pb::BookmarkMetadata* node0_metadata =
  740. model_metadata.add_bookmarks_metadata();
  741. *node0_metadata = CreateNodeMetadata(node0, /*server_id=*/"id0");
  742. node0_metadata->mutable_metadata()->clear_client_tag_hash();
  743. base::HistogramTester histogram_tester;
  744. EXPECT_THAT(SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  745. model.get(), std::move(model_metadata)),
  746. IsNull());
  747. histogram_tester.ExpectUniqueSample(
  748. "Sync.BookmarksModelMetadataCorruptionReason",
  749. /*sample=*/ExpectedCorruptionReason::MISSING_CLIENT_TAG_HASH,
  750. /*expected_bucket_count=*/1);
  751. }
  752. TEST(SyncedBookmarkTrackerTest,
  753. ShouldInvalidateMetadataIfUnsyncableNodeIsTracked) {
  754. auto client = std::make_unique<bookmarks::TestBookmarkClient>();
  755. bookmarks::BookmarkNode* managed_node = client->EnableManagedNode();
  756. std::unique_ptr<bookmarks::BookmarkModel> model =
  757. bookmarks::TestBookmarkClient::CreateModelWithClient(std::move(client));
  758. // The model should contain the managed node now.
  759. ASSERT_THAT(GetBookmarkNodeByID(model.get(), managed_node->id()),
  760. Eq(managed_node));
  761. // Add entries for all the permanent nodes. TestBookmarkClient creates all the
  762. // 3 permanent nodes.
  763. sync_pb::BookmarkModelMetadata model_metadata =
  764. CreateMetadataForPermanentNodes(model.get());
  765. // Add unsyncable node to metadata.
  766. *model_metadata.add_bookmarks_metadata() =
  767. CreateNodeMetadata(managed_node,
  768. /*server_id=*/"server_id");
  769. base::HistogramTester histogram_tester;
  770. EXPECT_THAT(SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  771. model.get(), std::move(model_metadata)),
  772. IsNull());
  773. histogram_tester.ExpectUniqueSample(
  774. "Sync.BookmarksModelMetadataCorruptionReason",
  775. /*sample=*/ExpectedCorruptionReason::TRACKED_MANAGED_NODE,
  776. /*expected_bucket_count=*/1);
  777. }
  778. TEST(SyncedBookmarkTrackerTest, ShouldInvalidateMetadataIfMissingFaviconHash) {
  779. std::unique_ptr<bookmarks::BookmarkModel> model =
  780. bookmarks::TestBookmarkClient::CreateModel();
  781. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  782. const bookmarks::BookmarkNode* node0 =
  783. model->AddURL(/*parent=*/bookmark_bar_node, /*index=*/0, u"Title",
  784. GURL("http://www.url.com"));
  785. sync_pb::BookmarkModelMetadata model_metadata =
  786. CreateMetadataForPermanentNodes(model.get());
  787. sync_pb::BookmarkMetadata* node0_metadata =
  788. model_metadata.add_bookmarks_metadata();
  789. *node0_metadata = CreateNodeMetadata(node0, /*server_id=*/"id0");
  790. node0_metadata->mutable_metadata()->clear_bookmark_favicon_hash();
  791. base::HistogramTester histogram_tester;
  792. EXPECT_THAT(SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  793. model.get(), std::move(model_metadata)),
  794. IsNull());
  795. histogram_tester.ExpectUniqueSample(
  796. "Sync.BookmarksModelMetadataCorruptionReason",
  797. /*sample=*/ExpectedCorruptionReason::MISSING_FAVICON_HASH,
  798. /*expected_bucket_count=*/1);
  799. }
  800. TEST(SyncedBookmarkTrackerTest,
  801. ShouldMatchModelWithUnsyncableNodesAndMetadata) {
  802. auto client = std::make_unique<bookmarks::TestBookmarkClient>();
  803. bookmarks::BookmarkNode* managed_node = client->EnableManagedNode();
  804. std::unique_ptr<bookmarks::BookmarkModel> model =
  805. bookmarks::TestBookmarkClient::CreateModelWithClient(std::move(client));
  806. // The model should contain the managed node now.
  807. ASSERT_THAT(GetBookmarkNodeByID(model.get(), managed_node->id()),
  808. Eq(managed_node));
  809. // Add entries for all the permanent nodes. TestBookmarkClient creates all the
  810. // 3 permanent nodes.
  811. sync_pb::BookmarkModelMetadata model_metadata =
  812. CreateMetadataForPermanentNodes(model.get());
  813. base::HistogramTester histogram_tester;
  814. EXPECT_THAT(SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  815. model.get(), std::move(model_metadata)),
  816. NotNull());
  817. histogram_tester.ExpectUniqueSample(
  818. "Sync.BookmarksModelMetadataCorruptionReason",
  819. /*sample=*/ExpectedCorruptionReason::NO_CORRUPTION,
  820. /*expected_bucket_count=*/1);
  821. }
  822. TEST(SyncedBookmarkTrackerTest,
  823. ShouldPopulateFaviconHashForNewlyAddedEntities) {
  824. std::unique_ptr<SyncedBookmarkTracker> tracker =
  825. SyncedBookmarkTracker::CreateEmpty(sync_pb::ModelTypeState());
  826. const std::string kSyncId = "SYNC_ID";
  827. const std::string kTitle = "Title";
  828. const GURL kUrl("http://www.foo.com");
  829. const int64_t kId = 1;
  830. const int64_t kServerVersion = 1000;
  831. const base::Time kCreationTime = base::Time::Now();
  832. const std::string kFaviconPngBytes = "fakefaviconbytes";
  833. sync_pb::EntitySpecifics specifics = GenerateSpecifics(kTitle, kUrl.spec());
  834. specifics.mutable_bookmark()->set_favicon(kFaviconPngBytes);
  835. bookmarks::BookmarkNode node(kId, base::GUID::GenerateRandomV4(), kUrl);
  836. const SyncedBookmarkTrackerEntity* entity =
  837. tracker->Add(&node, kSyncId, kServerVersion, kCreationTime, specifics);
  838. EXPECT_TRUE(entity->metadata().has_bookmark_favicon_hash());
  839. EXPECT_TRUE(entity->MatchesFaviconHash(kFaviconPngBytes));
  840. EXPECT_FALSE(entity->MatchesFaviconHash("otherhash"));
  841. }
  842. TEST(SyncedBookmarkTrackerTest, ShouldPopulateFaviconHashUponUpdate) {
  843. const std::string kSyncId = "SYNC_ID";
  844. const std::string kTitle = "Title";
  845. const GURL kUrl("http://www.foo.com");
  846. const int64_t kServerVersion = 1000;
  847. const base::Time kModificationTime = base::Time::Now();
  848. const std::string kFaviconPngBytes = "fakefaviconbytes";
  849. std::unique_ptr<bookmarks::BookmarkModel> model =
  850. bookmarks::TestBookmarkClient::CreateModel();
  851. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  852. const bookmarks::BookmarkNode* node =
  853. model->AddURL(/*parent=*/bookmark_bar_node, /*index=*/0, u"Title",
  854. GURL("http://www.url.com"));
  855. sync_pb::BookmarkModelMetadata model_metadata =
  856. CreateMetadataForPermanentNodes(model.get());
  857. // Add entry for the URL node.
  858. *model_metadata.add_bookmarks_metadata() = CreateNodeMetadata(node, kSyncId);
  859. std::unique_ptr<SyncedBookmarkTracker> tracker =
  860. SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  861. model.get(), std::move(model_metadata));
  862. ASSERT_THAT(tracker, NotNull());
  863. const SyncedBookmarkTrackerEntity* entity =
  864. tracker->GetEntityForSyncId(kSyncId);
  865. ASSERT_THAT(entity, NotNull());
  866. ASSERT_FALSE(entity->MatchesFaviconHash(kFaviconPngBytes));
  867. sync_pb::EntitySpecifics specifics = GenerateSpecifics(kTitle, kUrl.spec());
  868. specifics.mutable_bookmark()->set_favicon(kFaviconPngBytes);
  869. tracker->Update(entity, kServerVersion, kModificationTime, specifics);
  870. EXPECT_TRUE(entity->metadata().has_bookmark_favicon_hash());
  871. EXPECT_TRUE(entity->MatchesFaviconHash(kFaviconPngBytes));
  872. EXPECT_FALSE(entity->MatchesFaviconHash("otherhash"));
  873. }
  874. TEST(SyncedBookmarkTrackerTest, ShouldNotReuploadEntitiesAfterMergeAndRestart) {
  875. base::test::ScopedFeatureList override_features;
  876. override_features.InitAndEnableFeature(switches::kSyncReuploadBookmarks);
  877. const std::string kTitle = "Title";
  878. const GURL kUrl("http://www.foo.com");
  879. sync_pb::ModelTypeState model_type_state;
  880. model_type_state.set_initial_sync_done(true);
  881. std::unique_ptr<SyncedBookmarkTracker> tracker =
  882. SyncedBookmarkTracker::CreateEmpty(model_type_state);
  883. tracker->SetBookmarksReuploaded();
  884. std::unique_ptr<bookmarks::BookmarkModel> model =
  885. bookmarks::TestBookmarkClient::CreateModel();
  886. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  887. const bookmarks::BookmarkNode* node =
  888. model->AddURL(/*parent=*/bookmark_bar_node, /*index=*/0,
  889. base::UTF8ToUTF16(kTitle), kUrl);
  890. const sync_pb::EntitySpecifics specifics =
  891. GenerateSpecifics(kTitle, kUrl.spec());
  892. tracker->Add(node, /*sync_id=*/"id", /*server_version=*/0,
  893. /*creation_time=*/base::Time::Now(), specifics);
  894. sync_pb::EntitySpecifics permanent_specifics;
  895. permanent_specifics.mutable_bookmark();
  896. // Add permanent nodes to tracker.
  897. tracker->Add(model->bookmark_bar_node(), kBookmarkBarId, /*server_version=*/0,
  898. /*creation_time=*/base::Time::Now(), permanent_specifics);
  899. tracker->Add(model->other_node(), kOtherBookmarksId, /*server_version=*/0,
  900. /*creation_time=*/base::Time::Now(), permanent_specifics);
  901. tracker->Add(model->mobile_node(), kMobileBookmarksId, /*server_version=*/0,
  902. /*creation_time=*/base::Time::Now(), permanent_specifics);
  903. ASSERT_FALSE(tracker->HasLocalChanges());
  904. // Simulate browser restart.
  905. tracker = SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  906. model.get(), tracker->BuildBookmarkModelMetadata());
  907. ASSERT_THAT(tracker, NotNull());
  908. EXPECT_FALSE(tracker->HasLocalChanges());
  909. EXPECT_EQ(4u, tracker->TrackedEntitiesCountForTest());
  910. }
  911. TEST(SyncedBookmarkTrackerTest,
  912. ShouldReportZeroIgnoredUpdateDueToMissingParentForNewTracker) {
  913. std::unique_ptr<SyncedBookmarkTracker> tracker =
  914. SyncedBookmarkTracker::CreateEmpty(sync_pb::ModelTypeState());
  915. EXPECT_THAT(tracker->GetNumIgnoredUpdatesDueToMissingParentForTest(), Eq(0));
  916. EXPECT_THAT(
  917. tracker->GetMaxVersionAmongIgnoredUpdatesDueToMissingParentForTest(),
  918. Eq(absl::nullopt));
  919. const sync_pb::BookmarkModelMetadata bookmark_model_metadata =
  920. tracker->BuildBookmarkModelMetadata();
  921. EXPECT_TRUE(
  922. bookmark_model_metadata.has_num_ignored_updates_due_to_missing_parent());
  923. EXPECT_THAT(
  924. bookmark_model_metadata.num_ignored_updates_due_to_missing_parent(),
  925. Eq(0));
  926. EXPECT_FALSE(
  927. bookmark_model_metadata
  928. .has_max_version_among_ignored_updates_due_to_missing_parent());
  929. }
  930. TEST(SyncedBookmarkTrackerTest,
  931. ShouldResetReuploadFlagOnDisabledFeatureToggle) {
  932. base::test::ScopedFeatureList override_features;
  933. override_features.InitAndDisableFeature(switches::kSyncReuploadBookmarks);
  934. const std::string kTitle = "Title";
  935. const GURL kUrl("http://www.foo.com");
  936. std::unique_ptr<bookmarks::BookmarkModel> bookmark_model =
  937. bookmarks::TestBookmarkClient::CreateModel();
  938. sync_pb::ModelTypeState model_type_state;
  939. model_type_state.set_initial_sync_done(true);
  940. sync_pb::BookmarkModelMetadata initial_model_metadata =
  941. CreateMetadataForPermanentNodes(bookmark_model.get());
  942. initial_model_metadata.set_bookmarks_hierarchy_fields_reuploaded(true);
  943. std::unique_ptr<SyncedBookmarkTracker> tracker =
  944. SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  945. bookmark_model.get(), std::move(initial_model_metadata));
  946. ASSERT_THAT(tracker, NotNull());
  947. EXPECT_FALSE(tracker->BuildBookmarkModelMetadata()
  948. .bookmarks_hierarchy_fields_reuploaded());
  949. }
  950. TEST(SyncedBookmarkTrackerTest,
  951. ShouldRestoreZeroIgnoredUpdateDueToMissingParent) {
  952. std::unique_ptr<bookmarks::BookmarkModel> bookmark_model =
  953. bookmarks::TestBookmarkClient::CreateModel();
  954. sync_pb::BookmarkModelMetadata bookmark_model_metadata =
  955. CreateMetadataForPermanentNodes(bookmark_model.get());
  956. bookmark_model_metadata.set_num_ignored_updates_due_to_missing_parent(0);
  957. std::unique_ptr<SyncedBookmarkTracker> tracker =
  958. SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  959. bookmark_model.get(), std::move(bookmark_model_metadata));
  960. ASSERT_THAT(tracker, NotNull());
  961. EXPECT_THAT(tracker->GetNumIgnoredUpdatesDueToMissingParentForTest(), Eq(0));
  962. EXPECT_THAT(
  963. tracker->GetMaxVersionAmongIgnoredUpdatesDueToMissingParentForTest(),
  964. Eq(absl::nullopt));
  965. }
  966. TEST(SyncedBookmarkTrackerTest,
  967. ShouldRestoreUnknownIgnoredUpdateDueToMissingParent) {
  968. std::unique_ptr<bookmarks::BookmarkModel> bookmark_model =
  969. bookmarks::TestBookmarkClient::CreateModel();
  970. sync_pb::BookmarkModelMetadata bookmark_model_metadata =
  971. CreateMetadataForPermanentNodes(bookmark_model.get());
  972. std::unique_ptr<SyncedBookmarkTracker> tracker =
  973. SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  974. bookmark_model.get(), std::move(bookmark_model_metadata));
  975. ASSERT_THAT(tracker, NotNull());
  976. EXPECT_THAT(tracker->GetNumIgnoredUpdatesDueToMissingParentForTest(),
  977. Eq(absl::nullopt));
  978. EXPECT_THAT(
  979. tracker->GetMaxVersionAmongIgnoredUpdatesDueToMissingParentForTest(),
  980. Eq(absl::nullopt));
  981. }
  982. TEST(SyncedBookmarkTrackerTest,
  983. ShouldRestoreNonZeroIgnoredUpdateDueToMissingParent) {
  984. const int64_t kIgnoredUpdates = 7;
  985. const int64_t kServerVersion = 123;
  986. std::unique_ptr<bookmarks::BookmarkModel> bookmark_model =
  987. bookmarks::TestBookmarkClient::CreateModel();
  988. sync_pb::BookmarkModelMetadata bookmark_model_metadata =
  989. CreateMetadataForPermanentNodes(bookmark_model.get());
  990. bookmark_model_metadata.set_num_ignored_updates_due_to_missing_parent(
  991. kIgnoredUpdates);
  992. bookmark_model_metadata
  993. .set_max_version_among_ignored_updates_due_to_missing_parent(
  994. kServerVersion);
  995. std::unique_ptr<SyncedBookmarkTracker> tracker =
  996. SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  997. bookmark_model.get(), std::move(bookmark_model_metadata));
  998. ASSERT_THAT(tracker, NotNull());
  999. EXPECT_THAT(tracker->GetNumIgnoredUpdatesDueToMissingParentForTest(),
  1000. Eq(kIgnoredUpdates));
  1001. EXPECT_THAT(
  1002. tracker->GetMaxVersionAmongIgnoredUpdatesDueToMissingParentForTest(),
  1003. Eq(kServerVersion));
  1004. }
  1005. TEST(SyncedBookmarkTrackerTest, ShouldRecordIgnoredUpdateDueToMissingParent) {
  1006. const int64_t kServerVersion = 123;
  1007. std::unique_ptr<SyncedBookmarkTracker> tracker =
  1008. SyncedBookmarkTracker::CreateEmpty(sync_pb::ModelTypeState());
  1009. ASSERT_THAT(tracker->GetNumIgnoredUpdatesDueToMissingParentForTest(), Eq(0));
  1010. ASSERT_THAT(
  1011. tracker->GetMaxVersionAmongIgnoredUpdatesDueToMissingParentForTest(),
  1012. Eq(absl::nullopt));
  1013. tracker->RecordIgnoredServerUpdateDueToMissingParent(kServerVersion);
  1014. EXPECT_THAT(tracker->GetNumIgnoredUpdatesDueToMissingParentForTest(), Eq(1));
  1015. EXPECT_THAT(
  1016. tracker->GetMaxVersionAmongIgnoredUpdatesDueToMissingParentForTest(),
  1017. Eq(kServerVersion));
  1018. const sync_pb::BookmarkModelMetadata bookmark_model_metadata =
  1019. tracker->BuildBookmarkModelMetadata();
  1020. EXPECT_THAT(
  1021. bookmark_model_metadata.num_ignored_updates_due_to_missing_parent(),
  1022. Eq(1));
  1023. EXPECT_THAT(bookmark_model_metadata
  1024. .max_version_among_ignored_updates_due_to_missing_parent(),
  1025. Eq(kServerVersion));
  1026. }
  1027. TEST(SyncedBookmarkTrackerTest,
  1028. ShouldPartiallyRecordIgnoredUpdateDueToMissingParentIfCounterUnknown) {
  1029. const int64_t kServerVersion = 123;
  1030. std::unique_ptr<bookmarks::BookmarkModel> bookmark_model =
  1031. bookmarks::TestBookmarkClient::CreateModel();
  1032. sync_pb::BookmarkModelMetadata bookmark_model_metadata =
  1033. CreateMetadataForPermanentNodes(bookmark_model.get());
  1034. std::unique_ptr<SyncedBookmarkTracker> tracker =
  1035. SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  1036. bookmark_model.get(), std::move(bookmark_model_metadata));
  1037. ASSERT_THAT(tracker, NotNull());
  1038. ASSERT_THAT(tracker->GetNumIgnoredUpdatesDueToMissingParentForTest(),
  1039. Eq(absl::nullopt));
  1040. ASSERT_THAT(
  1041. tracker->GetMaxVersionAmongIgnoredUpdatesDueToMissingParentForTest(),
  1042. Eq(absl::nullopt));
  1043. tracker->RecordIgnoredServerUpdateDueToMissingParent(kServerVersion);
  1044. EXPECT_THAT(tracker->GetNumIgnoredUpdatesDueToMissingParentForTest(),
  1045. Eq(absl::nullopt));
  1046. EXPECT_THAT(
  1047. tracker->GetMaxVersionAmongIgnoredUpdatesDueToMissingParentForTest(),
  1048. Eq(kServerVersion));
  1049. }
  1050. } // namespace
  1051. } // namespace sync_bookmarks