bookmark_specifics_conversions_unittest.cc 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059
  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/bookmark_specifics_conversions.h"
  5. #include <memory>
  6. #include <string>
  7. #include <utility>
  8. #include <vector>
  9. #include "base/guid.h"
  10. #include "base/strings/strcat.h"
  11. #include "base/strings/utf_string_conversions.h"
  12. #include "base/test/metrics/histogram_tester.h"
  13. #include "base/time/time.h"
  14. #include "components/bookmarks/browser/bookmark_model.h"
  15. #include "components/bookmarks/browser/bookmark_node.h"
  16. #include "components/bookmarks/test/test_bookmark_client.h"
  17. #include "components/favicon/core/test/mock_favicon_service.h"
  18. #include "components/sync/base/client_tag_hash.h"
  19. #include "components/sync/base/unique_position.h"
  20. #include "components/sync/protocol/bookmark_specifics.pb.h"
  21. #include "components/sync/protocol/entity_data.h"
  22. #include "components/sync/protocol/entity_specifics.pb.h"
  23. #include "components/sync/protocol/model_type_state.pb.h"
  24. #include "components/sync_bookmarks/synced_bookmark_tracker.h"
  25. #include "components/sync_bookmarks/synced_bookmark_tracker_entity.h"
  26. #include "testing/gmock/include/gmock/gmock.h"
  27. #include "testing/gtest/include/gtest/gtest.h"
  28. #include "third_party/skia/include/core/SkBitmap.h"
  29. #include "ui/gfx/image/image.h"
  30. #include "url/gurl.h"
  31. namespace sync_bookmarks {
  32. namespace {
  33. using bookmarks::TestBookmarkClient;
  34. using testing::_;
  35. using testing::Eq;
  36. using testing::Ge;
  37. using testing::IsEmpty;
  38. using testing::Not;
  39. using testing::NotNull;
  40. // Fork of enum InvalidBookmarkSpecificsError.
  41. enum class InvalidBookmarkSpecificsError {
  42. kEmptySpecifics = 0,
  43. kInvalidURL = 1,
  44. kIconURLWithoutFavicon = 2,
  45. kInvalidIconURL = 3,
  46. kNonUniqueMetaInfoKeys = 4,
  47. kInvalidGUID = 5,
  48. kInvalidParentGUID = 6,
  49. kInvalidUniquePosition = 7,
  50. kBannedGUID = 8,
  51. kMaxValue = kBannedGUID,
  52. };
  53. sync_pb::UniquePosition RandomUniquePosition() {
  54. return syncer::UniquePosition::InitialPosition(
  55. syncer::UniquePosition::RandomSuffix())
  56. .ToProto();
  57. }
  58. // Returns a single-color 16x16 image using |color|.
  59. gfx::Image CreateTestImage(SkColor color) {
  60. SkBitmap bitmap;
  61. bitmap.allocN32Pixels(16, 16);
  62. bitmap.eraseColor(color);
  63. return gfx::Image::CreateFrom1xBitmap(bitmap);
  64. }
  65. TEST(BookmarkSpecificsConversionsTest, ShouldCreateSpecificsFromBookmarkNode) {
  66. const GURL kUrl("http://www.url.com");
  67. const std::string kTitle = "Title";
  68. const base::Time kTime = base::Time::Now();
  69. const std::string kKey1 = "key1";
  70. const std::string kValue1 = "value1";
  71. const std::string kKey2 = "key2";
  72. const std::string kValue2 = "value2";
  73. const syncer::UniquePosition kUniquePosition =
  74. syncer::UniquePosition::InitialPosition(
  75. syncer::UniquePosition::RandomSuffix());
  76. std::unique_ptr<bookmarks::BookmarkModel> model =
  77. bookmarks::TestBookmarkClient::CreateModel();
  78. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  79. const bookmarks::BookmarkNode* node = model->AddURL(
  80. /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16(kTitle),
  81. kUrl);
  82. ASSERT_THAT(node, NotNull());
  83. model->SetDateAdded(node, kTime);
  84. model->UpdateLastUsedTime(node, kTime);
  85. model->SetNodeMetaInfo(node, kKey1, kValue1);
  86. model->SetNodeMetaInfo(node, kKey2, kValue2);
  87. sync_pb::EntitySpecifics specifics = CreateSpecificsFromBookmarkNode(
  88. node, model.get(), kUniquePosition.ToProto(),
  89. /*force_favicon_load=*/false);
  90. const sync_pb::BookmarkSpecifics& bm_specifics = specifics.bookmark();
  91. EXPECT_THAT(bm_specifics.guid(), Eq(node->guid().AsLowercaseString()));
  92. EXPECT_THAT(bm_specifics.parent_guid(),
  93. Eq(bookmarks::BookmarkNode::kBookmarkBarNodeGuid));
  94. EXPECT_THAT(bm_specifics.type(), Eq(sync_pb::BookmarkSpecifics::URL));
  95. EXPECT_THAT(bm_specifics.legacy_canonicalized_title(), Eq(kTitle));
  96. EXPECT_THAT(GURL(bm_specifics.url()), Eq(kUrl));
  97. EXPECT_THAT(base::Time::FromDeltaSinceWindowsEpoch(
  98. base::Microseconds(bm_specifics.creation_time_us())),
  99. Eq(kTime));
  100. EXPECT_THAT(base::Time::FromDeltaSinceWindowsEpoch(
  101. base::Microseconds(bm_specifics.last_used_time_us())),
  102. Eq(kTime));
  103. EXPECT_TRUE(syncer::UniquePosition::FromProto(bm_specifics.unique_position())
  104. .Equals(kUniquePosition));
  105. for (const sync_pb::MetaInfo& meta_info : bm_specifics.meta_info()) {
  106. std::string value;
  107. node->GetMetaInfo(meta_info.key(), &value);
  108. EXPECT_THAT(meta_info.value(), Eq(value));
  109. }
  110. }
  111. TEST(BookmarkSpecificsConversionsTest,
  112. ShouldCreateSpecificsFromBookmarkNodeNoDateLastUsed) {
  113. const GURL kUrl("http://www.url.com");
  114. const std::string kTitle = "Title";
  115. const syncer::UniquePosition kUniquePosition =
  116. syncer::UniquePosition::InitialPosition(
  117. syncer::UniquePosition::RandomSuffix());
  118. std::unique_ptr<bookmarks::BookmarkModel> model =
  119. bookmarks::TestBookmarkClient::CreateModel();
  120. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  121. const bookmarks::BookmarkNode* node = model->AddURL(
  122. /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16(kTitle),
  123. kUrl);
  124. sync_pb::EntitySpecifics specifics = CreateSpecificsFromBookmarkNode(
  125. node, model.get(), kUniquePosition.ToProto(),
  126. /*force_favicon_load=*/false);
  127. const sync_pb::BookmarkSpecifics& bm_specifics = specifics.bookmark();
  128. EXPECT_THAT(bm_specifics.guid(), Eq(node->guid().AsLowercaseString()));
  129. EXPECT_THAT(bm_specifics.parent_guid(),
  130. Eq(bookmarks::BookmarkNode::kBookmarkBarNodeGuid));
  131. EXPECT_FALSE(bm_specifics.has_last_used_time_us());
  132. }
  133. TEST(BookmarkSpecificsConversionsTest,
  134. ShouldCreateSpecificsFromBookmarkNodeWithIllegalTitle) {
  135. std::unique_ptr<bookmarks::BookmarkModel> model =
  136. bookmarks::TestBookmarkClient::CreateModel();
  137. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  138. const std::vector<std::string> illegal_titles = {"", ".", ".."};
  139. size_t index = 0;
  140. for (const std::string& illegal_title : illegal_titles) {
  141. const bookmarks::BookmarkNode* node = model->AddURL(
  142. /*parent=*/bookmark_bar_node, index++, base::UTF8ToUTF16(illegal_title),
  143. GURL("http://www.url.com"));
  144. ASSERT_THAT(node, NotNull());
  145. sync_pb::EntitySpecifics specifics = CreateSpecificsFromBookmarkNode(
  146. node, model.get(), RandomUniquePosition(),
  147. /*force_favicon_load=*/false);
  148. // Legacy clients append a space to illegal titles.
  149. EXPECT_THAT(specifics.bookmark().legacy_canonicalized_title(),
  150. Eq(illegal_title + " "));
  151. }
  152. }
  153. TEST(BookmarkSpecificsConversionsTest,
  154. ShouldCreateSpecificsWithoutUrlFromFolderNode) {
  155. std::unique_ptr<bookmarks::BookmarkModel> model =
  156. bookmarks::TestBookmarkClient::CreateModel();
  157. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  158. const bookmarks::BookmarkNode* node = model->AddFolder(
  159. /*parent=*/bookmark_bar_node, /*index=*/0, u"Title");
  160. ASSERT_THAT(node, NotNull());
  161. sync_pb::EntitySpecifics specifics =
  162. CreateSpecificsFromBookmarkNode(node, model.get(), RandomUniquePosition(),
  163. /*force_favicon_load=*/false);
  164. const sync_pb::BookmarkSpecifics& bm_specifics = specifics.bookmark();
  165. EXPECT_FALSE(bm_specifics.has_url());
  166. EXPECT_THAT(bm_specifics.type(), Eq(sync_pb::BookmarkSpecifics::FOLDER));
  167. EXPECT_FALSE(bm_specifics.has_last_used_time_us());
  168. }
  169. TEST(BookmarkSpecificsConversionsTest,
  170. ShouldLoadFaviconWhenCreatingSpecificsFromBookmarkNode) {
  171. auto client = std::make_unique<TestBookmarkClient>();
  172. TestBookmarkClient* client_ptr = client.get();
  173. std::unique_ptr<bookmarks::BookmarkModel> model =
  174. TestBookmarkClient::CreateModelWithClient(std::move(client));
  175. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  176. const bookmarks::BookmarkNode* node = model->AddURL(
  177. /*parent=*/bookmark_bar_node, /*index=*/0, u"Title",
  178. GURL("http://www.url.com"));
  179. ASSERT_THAT(node, NotNull());
  180. ASSERT_FALSE(node->is_favicon_loaded());
  181. ASSERT_FALSE(client_ptr->HasFaviconLoadTasks());
  182. sync_pb::EntitySpecifics specifics =
  183. CreateSpecificsFromBookmarkNode(node, model.get(), RandomUniquePosition(),
  184. /*force_favicon_load=*/true);
  185. EXPECT_TRUE(client_ptr->HasFaviconLoadTasks());
  186. EXPECT_FALSE(specifics.bookmark().has_favicon());
  187. EXPECT_FALSE(specifics.bookmark().has_icon_url());
  188. }
  189. TEST(BookmarkSpecificsConversionsTest,
  190. ShouldNotLoadFaviconWhenCreatingSpecificsFromBookmarkNode) {
  191. auto client = std::make_unique<TestBookmarkClient>();
  192. TestBookmarkClient* client_ptr = client.get();
  193. std::unique_ptr<bookmarks::BookmarkModel> model =
  194. TestBookmarkClient::CreateModelWithClient(std::move(client));
  195. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  196. const bookmarks::BookmarkNode* node = model->AddURL(
  197. /*parent=*/bookmark_bar_node, /*index=*/0, u"Title",
  198. GURL("http://www.url.com"));
  199. ASSERT_THAT(node, NotNull());
  200. ASSERT_FALSE(node->is_favicon_loaded());
  201. ASSERT_FALSE(client_ptr->HasFaviconLoadTasks());
  202. sync_pb::EntitySpecifics specifics = CreateSpecificsFromBookmarkNode(
  203. node, model.get(), RandomUniquePosition(), /*force_favicon_load=*/false);
  204. EXPECT_FALSE(client_ptr->HasFaviconLoadTasks());
  205. EXPECT_FALSE(specifics.bookmark().has_favicon());
  206. EXPECT_FALSE(specifics.bookmark().has_icon_url());
  207. }
  208. TEST(BookmarkSpecificsConversionsTest,
  209. ShouldIncludeFaviconWhenCreatingSpecificsFromBookmarkNodeIfLoaded) {
  210. const GURL kBookmarkUrl("http://www.url.com");
  211. const GURL kIconUrl("http://www.icon-url.com");
  212. const SkColor kColor = SK_ColorRED;
  213. auto client = std::make_unique<TestBookmarkClient>();
  214. TestBookmarkClient* client_ptr = client.get();
  215. std::unique_ptr<bookmarks::BookmarkModel> model =
  216. TestBookmarkClient::CreateModelWithClient(std::move(client));
  217. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  218. const bookmarks::BookmarkNode* node = model->AddURL(
  219. /*parent=*/bookmark_bar_node, /*index=*/0, u"Title", kBookmarkUrl);
  220. ASSERT_THAT(node, NotNull());
  221. ASSERT_FALSE(node->is_favicon_loaded());
  222. // Complete the loading of the favicon as part of the test setup.
  223. model->GetFavicon(node);
  224. ASSERT_TRUE(client_ptr->HasFaviconLoadTasks());
  225. client_ptr->SimulateFaviconLoaded(kBookmarkUrl, kIconUrl,
  226. CreateTestImage(kColor));
  227. ASSERT_TRUE(node->is_favicon_loaded());
  228. sync_pb::EntitySpecifics specifics =
  229. CreateSpecificsFromBookmarkNode(node, model.get(), RandomUniquePosition(),
  230. /*force_favicon_load=*/false);
  231. EXPECT_THAT(specifics.bookmark().favicon(), Not(IsEmpty()));
  232. EXPECT_THAT(specifics.bookmark().icon_url(), Eq(kIconUrl));
  233. // Verify that the |favicon| field is properly encoded.
  234. const gfx::Image favicon = gfx::Image::CreateFrom1xPNGBytes(
  235. reinterpret_cast<const unsigned char*>(
  236. specifics.bookmark().favicon().data()),
  237. specifics.bookmark().favicon().size());
  238. EXPECT_THAT(favicon.Width(), Eq(16));
  239. EXPECT_THAT(favicon.Height(), Eq(16));
  240. EXPECT_THAT(favicon.AsBitmap().getColor(1, 1), Eq(kColor));
  241. }
  242. TEST(BookmarkSpecificsConversionsTest,
  243. ShouldOmitLargeFaviconUrlWhenCreatingSpecificsFromBookmarkNode) {
  244. const GURL kBookmarkUrl("http://www.url.com");
  245. const GURL kIconUrl(
  246. base::StrCat({"http://www.icon-url.com/", std::string(5000, 'a')}));
  247. const SkColor kColor = SK_ColorRED;
  248. // This test uses a valid but very long icon URL, larger than
  249. // |kMaxFaviconUrlSize|.
  250. ASSERT_TRUE(kIconUrl.is_valid());
  251. ASSERT_THAT(kIconUrl.spec().size(), Ge(5000u));
  252. auto client = std::make_unique<TestBookmarkClient>();
  253. TestBookmarkClient* client_ptr = client.get();
  254. std::unique_ptr<bookmarks::BookmarkModel> model =
  255. TestBookmarkClient::CreateModelWithClient(std::move(client));
  256. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  257. const bookmarks::BookmarkNode* node = model->AddURL(
  258. /*parent=*/bookmark_bar_node, /*index=*/0, u"Title", kBookmarkUrl);
  259. ASSERT_THAT(node, NotNull());
  260. ASSERT_FALSE(node->is_favicon_loaded());
  261. // Complete the loading of the favicon as part of the test setup.
  262. model->GetFavicon(node);
  263. ASSERT_TRUE(client_ptr->HasFaviconLoadTasks());
  264. client_ptr->SimulateFaviconLoaded(kBookmarkUrl, kIconUrl,
  265. CreateTestImage(kColor));
  266. ASSERT_TRUE(node->is_favicon_loaded());
  267. sync_pb::EntitySpecifics specifics =
  268. CreateSpecificsFromBookmarkNode(node, model.get(), RandomUniquePosition(),
  269. /*force_favicon_load=*/false);
  270. // The icon URL should be omitted (populated with the empty string).
  271. EXPECT_TRUE(specifics.bookmark().has_icon_url());
  272. EXPECT_THAT(specifics.bookmark().icon_url(), IsEmpty());
  273. // The favicon image itself should be synced.
  274. EXPECT_THAT(specifics.bookmark().favicon(), Not(IsEmpty()));
  275. }
  276. TEST(BookmarkSpecificsConversionsTest,
  277. ShouldCreateNonFolderBookmarkNodeFromSpecifics) {
  278. const GURL kUrl("http://www.url.com");
  279. const base::GUID kGuid = base::GUID::GenerateRandomV4();
  280. const std::string kTitle = "Title";
  281. const base::Time kTime = base::Time::Now();
  282. const GURL kIconUrl("http://www.icon-url.com");
  283. const std::string kKey1 = "key1";
  284. const std::string kValue1 = "value1";
  285. const std::string kKey2 = "key2";
  286. const std::string kValue2 = "value2";
  287. sync_pb::BookmarkSpecifics bm_specifics;
  288. bm_specifics.set_url(kUrl.spec());
  289. bm_specifics.set_guid(kGuid.AsLowercaseString());
  290. bm_specifics.set_icon_url(kIconUrl.spec());
  291. bm_specifics.set_favicon("PNG");
  292. bm_specifics.set_legacy_canonicalized_title(kTitle);
  293. bm_specifics.set_creation_time_us(
  294. kTime.ToDeltaSinceWindowsEpoch().InMicroseconds());
  295. bm_specifics.set_last_used_time_us(
  296. kTime.ToDeltaSinceWindowsEpoch().InMicroseconds());
  297. bm_specifics.set_type(sync_pb::BookmarkSpecifics::URL);
  298. // Parent GUID and unique position are ignored by
  299. // CreateBookmarkNodeFromSpecifics(), but are required here to pass DCHECKs.
  300. bm_specifics.set_parent_guid(bookmarks::BookmarkNode::kBookmarkBarNodeGuid);
  301. *bm_specifics.mutable_unique_position() = RandomUniquePosition();
  302. sync_pb::MetaInfo* meta_info1 = bm_specifics.add_meta_info();
  303. meta_info1->set_key(kKey1);
  304. meta_info1->set_value(kValue1);
  305. sync_pb::MetaInfo* meta_info2 = bm_specifics.add_meta_info();
  306. meta_info2->set_key(kKey2);
  307. meta_info2->set_value(kValue2);
  308. std::unique_ptr<bookmarks::BookmarkModel> model =
  309. bookmarks::TestBookmarkClient::CreateModel();
  310. testing::NiceMock<favicon::MockFaviconService> favicon_service;
  311. EXPECT_CALL(favicon_service,
  312. AddPageNoVisitForBookmark(kUrl, base::UTF8ToUTF16(kTitle)));
  313. EXPECT_CALL(favicon_service, MergeFavicon(kUrl, kIconUrl, _, _, _));
  314. base::HistogramTester histogram_tester;
  315. const bookmarks::BookmarkNode* node = CreateBookmarkNodeFromSpecifics(
  316. bm_specifics,
  317. /*parent=*/model->bookmark_bar_node(), /*index=*/0, model.get(),
  318. &favicon_service);
  319. ASSERT_THAT(node, NotNull());
  320. EXPECT_THAT(node->guid(), Eq(kGuid));
  321. EXPECT_THAT(node->GetTitle(), Eq(base::UTF8ToUTF16(kTitle)));
  322. EXPECT_FALSE(node->is_folder());
  323. EXPECT_THAT(node->url(), Eq(kUrl));
  324. EXPECT_THAT(node->date_added(), Eq(kTime));
  325. EXPECT_THAT(node->date_last_used(), Eq(kTime));
  326. std::string value1;
  327. node->GetMetaInfo(kKey1, &value1);
  328. EXPECT_THAT(value1, Eq(kValue1));
  329. std::string value2;
  330. node->GetMetaInfo(kKey2, &value2);
  331. EXPECT_THAT(value2, Eq(kValue2));
  332. histogram_tester.ExpectUniqueSample(
  333. "Sync.BookmarkSpecificsExcludingFoldersContainFavicon",
  334. /*sample=*/true,
  335. /*expected_bucket_count=*/1);
  336. }
  337. TEST(BookmarkSpecificsConversionsTest, ShouldCreateFolderFromSpecifics) {
  338. const base::GUID kGuid = base::GUID::GenerateRandomV4();
  339. const std::string kTitle = "Title";
  340. const base::Time kTime = base::Time::Now();
  341. const std::string kKey1 = "key1";
  342. const std::string kValue1 = "value1";
  343. const std::string kKey2 = "key2";
  344. const std::string kValue2 = "value2";
  345. sync_pb::BookmarkSpecifics bm_specifics;
  346. bm_specifics.set_guid(kGuid.AsLowercaseString());
  347. bm_specifics.set_legacy_canonicalized_title(kTitle);
  348. bm_specifics.set_creation_time_us(
  349. kTime.ToDeltaSinceWindowsEpoch().InMicroseconds());
  350. bm_specifics.set_type(sync_pb::BookmarkSpecifics::FOLDER);
  351. // Parent GUID and unique position are ignored by
  352. // CreateBookmarkNodeFromSpecifics(), but are required here to pass DCHECKs.
  353. bm_specifics.set_parent_guid(bookmarks::BookmarkNode::kBookmarkBarNodeGuid);
  354. *bm_specifics.mutable_unique_position() = RandomUniquePosition();
  355. sync_pb::MetaInfo* meta_info1 = bm_specifics.add_meta_info();
  356. meta_info1->set_key(kKey1);
  357. meta_info1->set_value(kValue1);
  358. sync_pb::MetaInfo* meta_info2 = bm_specifics.add_meta_info();
  359. meta_info2->set_key(kKey2);
  360. meta_info2->set_value(kValue2);
  361. std::unique_ptr<bookmarks::BookmarkModel> model =
  362. bookmarks::TestBookmarkClient::CreateModel();
  363. testing::NiceMock<favicon::MockFaviconService> favicon_service;
  364. EXPECT_CALL(favicon_service, AddPageNoVisitForBookmark).Times(0);
  365. EXPECT_CALL(favicon_service, MergeFavicon).Times(0);
  366. EXPECT_CALL(favicon_service, DeleteFaviconMappings).Times(0);
  367. base::HistogramTester histogram_tester;
  368. const bookmarks::BookmarkNode* node = CreateBookmarkNodeFromSpecifics(
  369. bm_specifics,
  370. /*parent=*/model->bookmark_bar_node(), /*index=*/0, model.get(),
  371. &favicon_service);
  372. ASSERT_THAT(node, NotNull());
  373. EXPECT_THAT(node->guid(), Eq(kGuid));
  374. EXPECT_THAT(node->GetTitle(), Eq(base::UTF8ToUTF16(kTitle)));
  375. EXPECT_TRUE(node->is_folder());
  376. // TODO(crbug.com/1214840): Folders should propagate the creation time into
  377. // BookmarkModel, just like non-folders.
  378. EXPECT_THAT(node->date_added(), Ge(kTime));
  379. std::string value1;
  380. node->GetMetaInfo(kKey1, &value1);
  381. EXPECT_THAT(value1, Eq(kValue1));
  382. std::string value2;
  383. node->GetMetaInfo(kKey2, &value2);
  384. EXPECT_THAT(value2, Eq(kValue2));
  385. // The histogram should not be recorded for folders.
  386. histogram_tester.ExpectTotalCount(
  387. "Sync.BookmarkSpecificsExcludingFoldersContainFavicon", 0);
  388. }
  389. TEST(BookmarkSpecificsConversionsTest,
  390. ShouldPreferFullTitleOnCreatingBookmarkNodeFromSpecifics) {
  391. const GURL kUrl("http://www.url.com");
  392. const base::GUID kGuid = base::GUID::GenerateRandomV4();
  393. const std::string kTitle = "Title";
  394. const std::string kFullTitle = "Title Long Version";
  395. const base::Time kTime = base::Time::Now();
  396. const GURL kIconUrl("http://www.icon-url.com");
  397. const std::string kKey1 = "key1";
  398. const std::string kValue1 = "value1";
  399. const std::string kKey2 = "key2";
  400. const std::string kValue2 = "value2";
  401. sync_pb::BookmarkSpecifics bm_specifics;
  402. bm_specifics.set_url(kUrl.spec());
  403. bm_specifics.set_guid(kGuid.AsLowercaseString());
  404. bm_specifics.set_icon_url(kIconUrl.spec());
  405. bm_specifics.set_favicon("PNG");
  406. bm_specifics.set_legacy_canonicalized_title(kTitle);
  407. bm_specifics.set_full_title(kFullTitle);
  408. bm_specifics.set_creation_time_us(
  409. kTime.ToDeltaSinceWindowsEpoch().InMicroseconds());
  410. bm_specifics.set_type(sync_pb::BookmarkSpecifics::URL);
  411. // Parent GUID and unique position are ignored by
  412. // CreateBookmarkNodeFromSpecifics(), but are required here to pass DCHECKs.
  413. bm_specifics.set_parent_guid(bookmarks::BookmarkNode::kBookmarkBarNodeGuid);
  414. *bm_specifics.mutable_unique_position() = RandomUniquePosition();
  415. sync_pb::MetaInfo* meta_info1 = bm_specifics.add_meta_info();
  416. meta_info1->set_key(kKey1);
  417. meta_info1->set_value(kValue1);
  418. sync_pb::MetaInfo* meta_info2 = bm_specifics.add_meta_info();
  419. meta_info2->set_key(kKey2);
  420. meta_info2->set_value(kValue2);
  421. std::unique_ptr<bookmarks::BookmarkModel> model =
  422. bookmarks::TestBookmarkClient::CreateModel();
  423. testing::NiceMock<favicon::MockFaviconService> favicon_service;
  424. EXPECT_CALL(favicon_service,
  425. AddPageNoVisitForBookmark(kUrl, base::UTF8ToUTF16(kFullTitle)));
  426. EXPECT_CALL(favicon_service, MergeFavicon(kUrl, kIconUrl, _, _, _));
  427. const bookmarks::BookmarkNode* node = CreateBookmarkNodeFromSpecifics(
  428. bm_specifics,
  429. /*parent=*/model->bookmark_bar_node(), /*index=*/0, model.get(),
  430. &favicon_service);
  431. ASSERT_THAT(node, NotNull());
  432. EXPECT_THAT(node->guid(), Eq(kGuid));
  433. EXPECT_THAT(node->GetTitle(), Eq(base::UTF8ToUTF16(kFullTitle)));
  434. EXPECT_THAT(node->url(), Eq(kUrl));
  435. EXPECT_THAT(node->date_added(), Eq(kTime));
  436. std::string value1;
  437. node->GetMetaInfo(kKey1, &value1);
  438. EXPECT_THAT(value1, Eq(kValue1));
  439. std::string value2;
  440. node->GetMetaInfo(kKey2, &value2);
  441. EXPECT_THAT(value2, Eq(kValue2));
  442. }
  443. TEST(BookmarkSpecificsConversionsTest,
  444. ShouldCreateBookmarkNodeFromSpecificsWithIllegalTitle) {
  445. std::unique_ptr<bookmarks::BookmarkModel> model =
  446. bookmarks::TestBookmarkClient::CreateModel();
  447. testing::NiceMock<favicon::MockFaviconService> favicon_service;
  448. const std::vector<std::string> illegal_titles = {"", ".", ".."};
  449. size_t index = 0;
  450. for (const std::string& illegal_title : illegal_titles) {
  451. sync_pb::BookmarkSpecifics bm_specifics;
  452. bm_specifics.set_url("http://www.url.com");
  453. bm_specifics.set_guid(base::GUID::GenerateRandomV4().AsLowercaseString());
  454. // Legacy clients append an extra space to illegal clients.
  455. bm_specifics.set_legacy_canonicalized_title(illegal_title + " ");
  456. bm_specifics.set_type(sync_pb::BookmarkSpecifics::URL);
  457. // Parent GUID and unique position are ignored by
  458. // CreateBookmarkNodeFromSpecifics(), but are required here to pass DCHECKs.
  459. bm_specifics.set_parent_guid(bookmarks::BookmarkNode::kBookmarkBarNodeGuid);
  460. *bm_specifics.mutable_unique_position() = RandomUniquePosition();
  461. const bookmarks::BookmarkNode* node =
  462. CreateBookmarkNodeFromSpecifics(bm_specifics,
  463. /*parent=*/model->bookmark_bar_node(),
  464. index++, model.get(), &favicon_service);
  465. ASSERT_THAT(node, NotNull());
  466. // The node should be created without the extra space.
  467. EXPECT_THAT(node->GetTitle(), Eq(base::UTF8ToUTF16(illegal_title)));
  468. }
  469. }
  470. TEST(BookmarkSpecificsConversionsTest,
  471. ShouldCreateBookmarkNodeFromSpecificsWithFaviconAndWithoutIconUrl) {
  472. const GURL kUrl("http://www.url.com");
  473. const base::GUID kGuid = base::GUID::GenerateRandomV4();
  474. const std::string kTitle = "Title";
  475. const GURL kIconUrl("http://www.icon-url.com");
  476. sync_pb::BookmarkSpecifics bm_specifics;
  477. bm_specifics.set_url(kUrl.spec());
  478. bm_specifics.set_guid(kGuid.AsLowercaseString());
  479. bm_specifics.set_favicon("PNG");
  480. bm_specifics.set_legacy_canonicalized_title(kTitle);
  481. bm_specifics.set_type(sync_pb::BookmarkSpecifics::URL);
  482. // Parent GUID and unique position are ignored by
  483. // CreateBookmarkNodeFromSpecifics(), but are required here to pass DCHECKs.
  484. bm_specifics.set_parent_guid(bookmarks::BookmarkNode::kBookmarkBarNodeGuid);
  485. *bm_specifics.mutable_unique_position() = RandomUniquePosition();
  486. std::unique_ptr<bookmarks::BookmarkModel> model =
  487. bookmarks::TestBookmarkClient::CreateModel();
  488. testing::NiceMock<favicon::MockFaviconService> favicon_service;
  489. // The favicon service should be called with page url since the icon url is
  490. // missing.
  491. EXPECT_CALL(favicon_service, MergeFavicon(kUrl, kUrl, _, _, _));
  492. const bookmarks::BookmarkNode* node = CreateBookmarkNodeFromSpecifics(
  493. bm_specifics,
  494. /*parent=*/model->bookmark_bar_node(), /*index=*/0, model.get(),
  495. &favicon_service);
  496. EXPECT_THAT(node, NotNull());
  497. }
  498. TEST(BookmarkSpecificsConversionsTest, ShouldUpdateBookmarkNodeFromSpecifics) {
  499. const GURL kUrl("http://www.url.com");
  500. const std::string kTitle = "Title";
  501. const base::Time kTime = base::Time::Now();
  502. const std::string kKey1 = "key1";
  503. const std::string kValue1 = "value1";
  504. const std::string kKey2 = "key2";
  505. const std::string kValue2 = "value2";
  506. std::unique_ptr<bookmarks::BookmarkModel> model =
  507. bookmarks::TestBookmarkClient::CreateModel();
  508. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  509. const bookmarks::BookmarkNode* node = model->AddURL(
  510. /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16(kTitle),
  511. GURL(kUrl));
  512. ASSERT_THAT(node, NotNull());
  513. model->SetNodeMetaInfo(node, kKey1, kValue1);
  514. model->SetNodeMetaInfo(node, kKey2, kValue2);
  515. const GURL kNewUrl("http://www.new-url.com");
  516. const std::string kNewTitle = "NewTitle";
  517. const GURL kNewIconUrl("http://www.new-icon-url.com");
  518. const std::string kNewValue1 = "new-value1";
  519. const std::string kNewValue2 = "new-value2";
  520. sync_pb::BookmarkSpecifics bm_specifics;
  521. bm_specifics.set_url(kNewUrl.spec());
  522. bm_specifics.set_guid(node->guid().AsLowercaseString());
  523. bm_specifics.set_icon_url(kNewIconUrl.spec());
  524. bm_specifics.set_favicon("PNG");
  525. bm_specifics.set_legacy_canonicalized_title(kNewTitle);
  526. bm_specifics.set_creation_time_us(
  527. kTime.ToDeltaSinceWindowsEpoch().InMicroseconds());
  528. bm_specifics.set_last_used_time_us(
  529. kTime.ToDeltaSinceWindowsEpoch().InMicroseconds());
  530. sync_pb::MetaInfo* meta_info1 = bm_specifics.add_meta_info();
  531. meta_info1->set_key(kKey1);
  532. meta_info1->set_value(kNewValue1);
  533. sync_pb::MetaInfo* meta_info2 = bm_specifics.add_meta_info();
  534. meta_info2->set_key(kKey2);
  535. meta_info2->set_value(kNewValue2);
  536. testing::NiceMock<favicon::MockFaviconService> favicon_service;
  537. EXPECT_CALL(favicon_service,
  538. AddPageNoVisitForBookmark(kNewUrl, base::UTF8ToUTF16(kNewTitle)));
  539. EXPECT_CALL(favicon_service, MergeFavicon(kNewUrl, kNewIconUrl, _, _, _));
  540. UpdateBookmarkNodeFromSpecifics(bm_specifics, node, model.get(),
  541. &favicon_service);
  542. EXPECT_THAT(node->GetTitle(), Eq(base::UTF8ToUTF16(kNewTitle)));
  543. EXPECT_THAT(node->url(), Eq(kNewUrl));
  544. std::string value1;
  545. node->GetMetaInfo(kKey1, &value1);
  546. EXPECT_THAT(value1, Eq(kNewValue1));
  547. std::string value2;
  548. node->GetMetaInfo(kKey2, &value2);
  549. EXPECT_THAT(value2, Eq(kNewValue2));
  550. }
  551. TEST(BookmarkSpecificsConversionsTest,
  552. ShouldPreferFullTitleOnUpdateBookmarkNodeFromSpecifics) {
  553. const GURL kUrl("http://www.url.com");
  554. const std::string kTitle = "Title";
  555. const base::Time kTime = base::Time::Now();
  556. const std::string kKey1 = "key1";
  557. const std::string kValue1 = "value1";
  558. const std::string kKey2 = "key2";
  559. const std::string kValue2 = "value2";
  560. std::unique_ptr<bookmarks::BookmarkModel> model =
  561. bookmarks::TestBookmarkClient::CreateModel();
  562. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  563. const bookmarks::BookmarkNode* node = model->AddURL(
  564. /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16(kTitle),
  565. GURL(kUrl));
  566. ASSERT_THAT(node, NotNull());
  567. model->SetNodeMetaInfo(node, kKey1, kValue1);
  568. model->SetNodeMetaInfo(node, kKey2, kValue2);
  569. const GURL kNewUrl("http://www.new-url.com");
  570. const std::string kNewTitle = "NewTitle";
  571. const std::string kNewFullTitle = "NewTitle Long Version";
  572. const GURL kNewIconUrl("http://www.new-icon-url.com");
  573. const std::string kNewValue1 = "new-value1";
  574. const std::string kNewValue2 = "new-value2";
  575. sync_pb::BookmarkSpecifics bm_specifics;
  576. bm_specifics.set_url(kNewUrl.spec());
  577. bm_specifics.set_guid(node->guid().AsLowercaseString());
  578. bm_specifics.set_icon_url(kNewIconUrl.spec());
  579. bm_specifics.set_favicon("PNG");
  580. bm_specifics.set_legacy_canonicalized_title(kNewTitle);
  581. bm_specifics.set_full_title(kNewFullTitle);
  582. bm_specifics.set_creation_time_us(
  583. kTime.ToDeltaSinceWindowsEpoch().InMicroseconds());
  584. sync_pb::MetaInfo* meta_info1 = bm_specifics.add_meta_info();
  585. meta_info1->set_key(kKey1);
  586. meta_info1->set_value(kNewValue1);
  587. sync_pb::MetaInfo* meta_info2 = bm_specifics.add_meta_info();
  588. meta_info2->set_key(kKey2);
  589. meta_info2->set_value(kNewValue2);
  590. testing::NiceMock<favicon::MockFaviconService> favicon_service;
  591. EXPECT_CALL(favicon_service, AddPageNoVisitForBookmark(
  592. kNewUrl, base::UTF8ToUTF16(kNewFullTitle)));
  593. EXPECT_CALL(favicon_service, MergeFavicon(kNewUrl, kNewIconUrl, _, _, _));
  594. UpdateBookmarkNodeFromSpecifics(bm_specifics, node, model.get(),
  595. &favicon_service);
  596. EXPECT_THAT(node->GetTitle(), Eq(base::UTF8ToUTF16(kNewFullTitle)));
  597. EXPECT_THAT(node->url(), Eq(kNewUrl));
  598. std::string value1;
  599. node->GetMetaInfo(kKey1, &value1);
  600. EXPECT_THAT(value1, Eq(kNewValue1));
  601. std::string value2;
  602. node->GetMetaInfo(kKey2, &value2);
  603. EXPECT_THAT(value2, Eq(kNewValue2));
  604. }
  605. TEST(BookmarkSpecificsConversionsTest,
  606. ShouldUpdateBookmarkNodeFromSpecificsWithFaviconAndWithoutIconUrl) {
  607. const GURL kUrl("http://www.url.com");
  608. const std::string kTitle = "Title";
  609. std::unique_ptr<bookmarks::BookmarkModel> model =
  610. bookmarks::TestBookmarkClient::CreateModel();
  611. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  612. const bookmarks::BookmarkNode* node = model->AddURL(
  613. /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16(kTitle),
  614. GURL(kUrl));
  615. ASSERT_THAT(node, NotNull());
  616. const GURL kNewUrl("http://www.new-url.com");
  617. sync_pb::BookmarkSpecifics bm_specifics;
  618. bm_specifics.set_url(kNewUrl.spec());
  619. bm_specifics.set_guid(node->guid().AsLowercaseString());
  620. bm_specifics.set_favicon("PNG");
  621. testing::NiceMock<favicon::MockFaviconService> favicon_service;
  622. // The favicon service should be called with page url since the icon url is
  623. // missing.
  624. EXPECT_CALL(favicon_service, MergeFavicon(kNewUrl, kNewUrl, _, _, _));
  625. UpdateBookmarkNodeFromSpecifics(bm_specifics, node, model.get(),
  626. &favicon_service);
  627. }
  628. TEST(BookmarkSpecificsConversionsTest, ShouldBeValidBookmarkSpecifics) {
  629. sync_pb::BookmarkSpecifics bm_specifics;
  630. // URL is irrelevant for a folder.
  631. bm_specifics.set_url("INVALID_URL");
  632. bm_specifics.set_guid(base::GUID::GenerateRandomV4().AsLowercaseString());
  633. bm_specifics.set_parent_guid(
  634. base::GUID::GenerateRandomV4().AsLowercaseString());
  635. bm_specifics.set_type(sync_pb::BookmarkSpecifics::FOLDER);
  636. *bm_specifics.mutable_unique_position() = RandomUniquePosition();
  637. EXPECT_TRUE(IsValidBookmarkSpecifics(bm_specifics));
  638. bm_specifics.set_type(sync_pb::BookmarkSpecifics::URL);
  639. ASSERT_FALSE(IsValidBookmarkSpecifics(bm_specifics));
  640. bm_specifics.set_url("http://www.valid-url.com");
  641. EXPECT_TRUE(IsValidBookmarkSpecifics(bm_specifics));
  642. }
  643. TEST(BookmarkSpecificsConversionsTest,
  644. ShouldBeValidBookmarkSpecificsWithFaviconAndWithoutIconUrl) {
  645. sync_pb::BookmarkSpecifics bm_specifics;
  646. bm_specifics.set_url("http://www.valid-url.com");
  647. bm_specifics.set_favicon("PNG");
  648. bm_specifics.set_guid(base::GUID::GenerateRandomV4().AsLowercaseString());
  649. bm_specifics.set_parent_guid(
  650. base::GUID::GenerateRandomV4().AsLowercaseString());
  651. bm_specifics.set_type(sync_pb::BookmarkSpecifics::URL);
  652. *bm_specifics.mutable_unique_position() = RandomUniquePosition();
  653. EXPECT_TRUE(IsValidBookmarkSpecifics(bm_specifics));
  654. }
  655. TEST(BookmarkSpecificsConversionsTest,
  656. ShouldBeInvalidBookmarkSpecificsWithoutFaviconAndWithIconUrl) {
  657. sync_pb::BookmarkSpecifics bm_specifics;
  658. bm_specifics.set_url("http://www.valid-url.com");
  659. bm_specifics.set_icon_url("http://www.valid-icon-url.com");
  660. bm_specifics.set_guid(base::GUID::GenerateRandomV4().AsLowercaseString());
  661. bm_specifics.set_type(sync_pb::BookmarkSpecifics::URL);
  662. base::HistogramTester histogram_tester;
  663. EXPECT_FALSE(IsValidBookmarkSpecifics(bm_specifics));
  664. histogram_tester.ExpectBucketCount(
  665. "Sync.InvalidBookmarkSpecifics",
  666. /*sample=*/InvalidBookmarkSpecificsError::kIconURLWithoutFavicon,
  667. /*expected_count=*/1);
  668. }
  669. TEST(BookmarkSpecificsConversionsTest,
  670. ShouldBeInvalidBookmarkSpecificsWithInvalidGUID) {
  671. base::HistogramTester histogram_tester;
  672. sync_pb::BookmarkSpecifics bm_specifics;
  673. bm_specifics.set_type(sync_pb::BookmarkSpecifics::FOLDER);
  674. bm_specifics.set_parent_guid(
  675. base::GUID::GenerateRandomV4().AsLowercaseString());
  676. *bm_specifics.mutable_unique_position() = RandomUniquePosition();
  677. // No GUID.
  678. bm_specifics.clear_guid();
  679. EXPECT_FALSE(IsValidBookmarkSpecifics(bm_specifics));
  680. histogram_tester.ExpectBucketCount(
  681. "Sync.InvalidBookmarkSpecifics",
  682. /*sample=*/InvalidBookmarkSpecificsError::kInvalidGUID,
  683. /*expected_count=*/1);
  684. // Add empty GUID.
  685. bm_specifics.set_guid("");
  686. EXPECT_FALSE(IsValidBookmarkSpecifics(bm_specifics));
  687. histogram_tester.ExpectBucketCount(
  688. "Sync.InvalidBookmarkSpecifics",
  689. /*sample=*/InvalidBookmarkSpecificsError::kInvalidGUID,
  690. /*expected_count=*/2);
  691. // Add invalid GUID.
  692. bm_specifics.set_guid("INVALID GUID");
  693. EXPECT_FALSE(IsValidBookmarkSpecifics(bm_specifics));
  694. histogram_tester.ExpectBucketCount(
  695. "Sync.InvalidBookmarkSpecifics",
  696. /*sample=*/InvalidBookmarkSpecificsError::kInvalidGUID,
  697. /*expected_count=*/3);
  698. // Add valid GUID.
  699. bm_specifics.set_guid(base::GUID::GenerateRandomV4().AsLowercaseString());
  700. ASSERT_TRUE(IsValidBookmarkSpecifics(bm_specifics));
  701. }
  702. TEST(BookmarkSpecificsConversionsTest,
  703. ShouldBeInvalidBookmarkSpecificsWithInvalidParentGUID) {
  704. base::HistogramTester histogram_tester;
  705. sync_pb::BookmarkSpecifics bm_specifics;
  706. bm_specifics.set_guid(base::GUID::GenerateRandomV4().AsLowercaseString());
  707. bm_specifics.set_type(sync_pb::BookmarkSpecifics::FOLDER);
  708. *bm_specifics.mutable_unique_position() = RandomUniquePosition();
  709. // No parent GUID.
  710. bm_specifics.clear_parent_guid();
  711. EXPECT_FALSE(IsValidBookmarkSpecifics(bm_specifics));
  712. histogram_tester.ExpectBucketCount(
  713. "Sync.InvalidBookmarkSpecifics",
  714. /*sample=*/InvalidBookmarkSpecificsError::kInvalidParentGUID,
  715. /*expected_count=*/1);
  716. // Add invalid parent GUID.
  717. bm_specifics.set_parent_guid("INVALID GUID");
  718. EXPECT_FALSE(IsValidBookmarkSpecifics(bm_specifics));
  719. histogram_tester.ExpectBucketCount(
  720. "Sync.InvalidBookmarkSpecifics",
  721. /*sample=*/InvalidBookmarkSpecificsError::kInvalidParentGUID,
  722. /*expected_count=*/2);
  723. // Add valid GUID.
  724. bm_specifics.set_parent_guid(
  725. base::GUID::GenerateRandomV4().AsLowercaseString());
  726. ASSERT_TRUE(IsValidBookmarkSpecifics(bm_specifics));
  727. }
  728. TEST(BookmarkSpecificsConversionsTest,
  729. ShouldBeInvalidBookmarkSpecificsDueToInvalidUniquePosition) {
  730. sync_pb::BookmarkSpecifics bm_specifics;
  731. bm_specifics.set_type(sync_pb::BookmarkSpecifics::FOLDER);
  732. // Leave |unique_position| field populated but empty.
  733. bm_specifics.mutable_unique_position();
  734. base::HistogramTester histogram_tester;
  735. EXPECT_FALSE(IsValidBookmarkSpecifics(bm_specifics));
  736. histogram_tester.ExpectBucketCount(
  737. "Sync.InvalidBookmarkSpecifics",
  738. /*sample=*/InvalidBookmarkSpecificsError::kInvalidUniquePosition,
  739. /*expected_count=*/1);
  740. }
  741. TEST(BookmarkSpecificsConversionsTest,
  742. ShouldBeInvalidBookmarkSpecificsWithBannedGUID) {
  743. ASSERT_THAT(bookmarks::BookmarkNode::kBannedGuidDueToPastSyncBug,
  744. Eq(InferGuidFromLegacyOriginatorId(
  745. /*originator_cache_guid=*/"",
  746. /*originator_client_item_id=*/"")
  747. .AsLowercaseString()));
  748. base::HistogramTester histogram_tester;
  749. sync_pb::EntitySpecifics specifics;
  750. sync_pb::BookmarkSpecifics* bm_specifics = specifics.mutable_bookmark();
  751. bm_specifics->set_type(sync_pb::BookmarkSpecifics::FOLDER);
  752. *bm_specifics->mutable_unique_position() = RandomUniquePosition();
  753. bm_specifics->set_guid(bookmarks::BookmarkNode::kBannedGuidDueToPastSyncBug);
  754. EXPECT_FALSE(IsValidBookmarkSpecifics(*bm_specifics));
  755. histogram_tester.ExpectBucketCount(
  756. "Sync.InvalidBookmarkSpecifics",
  757. /*sample=*/InvalidBookmarkSpecificsError::kBannedGUID,
  758. /*expected_count=*/1);
  759. }
  760. TEST(BookmarkSpecificsConversionsTest, ShouldBeInvalidBookmarkSpecifics) {
  761. sync_pb::BookmarkSpecifics bm_specifics;
  762. // Empty specifics.
  763. {
  764. base::HistogramTester histogram_tester;
  765. EXPECT_FALSE(IsValidBookmarkSpecifics(bm_specifics));
  766. histogram_tester.ExpectBucketCount(
  767. "Sync.InvalidBookmarkSpecifics",
  768. /*sample=*/InvalidBookmarkSpecificsError::kEmptySpecifics,
  769. /*expected_count=*/1);
  770. }
  771. {
  772. base::HistogramTester histogram_tester;
  773. bm_specifics.set_type(sync_pb::BookmarkSpecifics::FOLDER);
  774. EXPECT_FALSE(IsValidBookmarkSpecifics(bm_specifics));
  775. histogram_tester.ExpectBucketCount(
  776. "Sync.InvalidBookmarkSpecifics",
  777. /*sample=*/InvalidBookmarkSpecificsError::kInvalidGUID,
  778. /*expected_count=*/1);
  779. }
  780. // Populate the required fields.
  781. bm_specifics.set_guid(base::GUID::GenerateRandomV4().AsLowercaseString());
  782. bm_specifics.set_parent_guid(
  783. base::GUID::GenerateRandomV4().AsLowercaseString());
  784. *bm_specifics.mutable_unique_position() = RandomUniquePosition();
  785. ASSERT_TRUE(IsValidBookmarkSpecifics(bm_specifics));
  786. {
  787. base::HistogramTester histogram_tester;
  788. bm_specifics.set_type(sync_pb::BookmarkSpecifics::URL);
  789. EXPECT_FALSE(IsValidBookmarkSpecifics(bm_specifics));
  790. histogram_tester.ExpectBucketCount(
  791. "Sync.InvalidBookmarkSpecifics",
  792. /*sample=*/InvalidBookmarkSpecificsError::kInvalidURL,
  793. /*expected_count=*/1);
  794. }
  795. // Add invalid url.
  796. {
  797. base::HistogramTester histogram_tester;
  798. bm_specifics.set_url("INVALID_URL");
  799. EXPECT_FALSE(IsValidBookmarkSpecifics(bm_specifics));
  800. histogram_tester.ExpectBucketCount(
  801. "Sync.InvalidBookmarkSpecifics",
  802. /*sample=*/InvalidBookmarkSpecificsError::kInvalidURL,
  803. /*expected_count=*/1);
  804. }
  805. // Add a valid url.
  806. bm_specifics.set_url("http://www.valid-url.com");
  807. ASSERT_TRUE(IsValidBookmarkSpecifics(bm_specifics));
  808. sync_pb::MetaInfo* meta_info1 = bm_specifics.add_meta_info();
  809. meta_info1->set_key("key");
  810. meta_info1->set_value("value1");
  811. ASSERT_TRUE(IsValidBookmarkSpecifics(bm_specifics));
  812. // Add redudant keys in meta_info.
  813. base::HistogramTester histogram_tester;
  814. sync_pb::MetaInfo* meta_info2 = bm_specifics.add_meta_info();
  815. meta_info2->set_key("key");
  816. meta_info2->set_value("value2");
  817. EXPECT_FALSE(IsValidBookmarkSpecifics(bm_specifics));
  818. histogram_tester.ExpectBucketCount(
  819. "Sync.InvalidBookmarkSpecifics",
  820. /*sample=*/InvalidBookmarkSpecificsError::kNonUniqueMetaInfoKeys,
  821. /*expected_count=*/1);
  822. }
  823. TEST(BookmarkSpecificsConversionsTest, ReplaceUrlNodeWithUpdatedGUID) {
  824. std::unique_ptr<bookmarks::BookmarkModel> model =
  825. bookmarks::TestBookmarkClient::CreateModel();
  826. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  827. const base::GUID kGuid = base::GUID::GenerateRandomV4();
  828. const std::u16string kTitle = u"bar";
  829. const GURL kUrl = GURL("http://foo.com");
  830. const base::Time kCreationTime = base::Time::Now();
  831. auto meta_info_map = std::make_unique<bookmarks::BookmarkNode::MetaInfoMap>();
  832. const std::string kKey = "key";
  833. const std::string kValue = "value";
  834. (*meta_info_map)[kKey] = kValue;
  835. // Add a bookmark URL.
  836. const bookmarks::BookmarkNode* original_url = model->AddURL(
  837. bookmark_bar_node, 0, kTitle, kUrl, meta_info_map.get(), kCreationTime);
  838. // Replace url1.
  839. const bookmarks::BookmarkNode* new_url =
  840. ReplaceBookmarkNodeGUID(original_url, kGuid, model.get());
  841. // All data except for the GUID should be the same.
  842. EXPECT_EQ(kGuid, new_url->guid());
  843. EXPECT_EQ(kTitle, new_url->GetTitle());
  844. EXPECT_EQ(bookmark_bar_node, new_url->parent());
  845. EXPECT_EQ(0u, bookmark_bar_node->GetIndexOf(new_url));
  846. EXPECT_EQ(kUrl, new_url->url());
  847. EXPECT_EQ(kCreationTime, new_url->date_added());
  848. std::string out_value_url;
  849. EXPECT_TRUE(new_url->GetMetaInfo(kKey, &out_value_url));
  850. EXPECT_EQ(kValue, out_value_url);
  851. }
  852. TEST(BookmarkSpecificsConversionsTest, ReplaceFolderNodeWithUpdatedGUID) {
  853. std::unique_ptr<bookmarks::BookmarkModel> model =
  854. bookmarks::TestBookmarkClient::CreateModel();
  855. const bookmarks::BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
  856. const base::GUID kGuid = base::GUID::GenerateRandomV4();
  857. const std::u16string kTitle = u"foobar";
  858. auto meta_info_map = std::make_unique<bookmarks::BookmarkNode::MetaInfoMap>();
  859. const std::string kKey = "key";
  860. const std::string kValue = "value";
  861. (*meta_info_map)[kKey] = kValue;
  862. // Add a folder with child URLs.
  863. const bookmarks::BookmarkNode* original_folder =
  864. model->AddFolder(bookmark_bar_node, 0, kTitle, meta_info_map.get());
  865. const bookmarks::BookmarkNode* url1 =
  866. model->AddURL(original_folder, 0, u"bar", GURL("http://bar.com"));
  867. const bookmarks::BookmarkNode* url2 =
  868. model->AddURL(original_folder, 1, u"foo", GURL("http://foo.com"));
  869. // Replace folder1.
  870. const bookmarks::BookmarkNode* new_folder =
  871. ReplaceBookmarkNodeGUID(original_folder, kGuid, model.get());
  872. // All data except for the GUID should be the same.
  873. EXPECT_EQ(kGuid, new_folder->guid());
  874. EXPECT_EQ(kTitle, new_folder->GetTitle());
  875. EXPECT_EQ(bookmark_bar_node, new_folder->parent());
  876. EXPECT_EQ(0u, bookmark_bar_node->GetIndexOf(new_folder));
  877. std::string out_value_folder;
  878. EXPECT_TRUE(new_folder->GetMetaInfo(kKey, &out_value_folder));
  879. EXPECT_EQ(kValue, out_value_folder);
  880. EXPECT_EQ(2u, new_folder->children().size());
  881. EXPECT_EQ(0u, new_folder->GetIndexOf(url1));
  882. EXPECT_EQ(1u, new_folder->GetIndexOf(url2));
  883. }
  884. TEST(BookmarkSpecificsConversionsTest,
  885. ShouldConsiderValidBookmarkGuidIfMatchesClientTag) {
  886. const std::string kGuid = base::GUID::GenerateRandomV4().AsLowercaseString();
  887. sync_pb::BookmarkSpecifics specifics;
  888. specifics.set_guid(kGuid);
  889. EXPECT_TRUE(HasExpectedBookmarkGuid(
  890. specifics, syncer::ClientTagHash::FromUnhashed(syncer::BOOKMARKS, kGuid),
  891. /*originator_cache_guid=*/"",
  892. /*originator_client_item_id=*/""));
  893. }
  894. TEST(BookmarkSpecificsConversionsTest,
  895. ShouldConsiderValidBookmarkGuidIfMatchesOriginator) {
  896. const std::string kGuid = base::GUID::GenerateRandomV4().AsLowercaseString();
  897. sync_pb::BookmarkSpecifics specifics;
  898. specifics.set_guid(kGuid);
  899. EXPECT_TRUE(HasExpectedBookmarkGuid(specifics, syncer::ClientTagHash(),
  900. /*originator_cache_guid=*/"",
  901. /*originator_client_item_id=*/kGuid));
  902. }
  903. TEST(BookmarkSpecificsConversionsTest,
  904. ShouldConsiderInvalidBookmarkGuidIfEmptyOriginator) {
  905. const std::string kGuid = InferGuidFromLegacyOriginatorId(
  906. /*originator_cache_guid=*/"",
  907. /*=originator_client_item_id=*/"")
  908. .AsLowercaseString();
  909. sync_pb::BookmarkSpecifics specifics;
  910. specifics.set_guid(kGuid);
  911. EXPECT_FALSE(HasExpectedBookmarkGuid(specifics,
  912. syncer::ClientTagHash::FromHashed("foo"),
  913. /*originator_cache_guid=*/"",
  914. /*originator_client_item_id=*/""));
  915. }
  916. } // namespace
  917. } // namespace sync_bookmarks