bookmark_model_unittest.cc 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854
  1. // Copyright 2014 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/bookmarks/browser/bookmark_model.h"
  5. #include <stddef.h>
  6. #include <stdint.h>
  7. #include <set>
  8. #include <string>
  9. #include <unordered_set>
  10. #include <utility>
  11. #include "base/base_paths.h"
  12. #include "base/command_line.h"
  13. #include "base/compiler_specific.h"
  14. #include "base/containers/contains.h"
  15. #include "base/files/scoped_temp_dir.h"
  16. #include "base/guid.h"
  17. #include "base/memory/raw_ptr.h"
  18. #include "base/run_loop.h"
  19. #include "base/strings/string_number_conversions.h"
  20. #include "base/strings/string_split.h"
  21. #include "base/strings/string_util.h"
  22. #include "base/strings/utf_string_conversions.h"
  23. #include "base/test/task_environment.h"
  24. #include "base/time/time.h"
  25. #include "build/build_config.h"
  26. #include "components/bookmarks/browser/bookmark_model_observer.h"
  27. #include "components/bookmarks/browser/bookmark_undo_delegate.h"
  28. #include "components/bookmarks/browser/bookmark_utils.h"
  29. #include "components/bookmarks/browser/titled_url_match.h"
  30. #include "components/bookmarks/browser/url_and_title.h"
  31. #include "components/bookmarks/test/bookmark_test_helpers.h"
  32. #include "components/bookmarks/test/test_bookmark_client.h"
  33. #include "components/favicon_base/favicon_callback.h"
  34. #include "components/favicon_base/favicon_types.h"
  35. #include "components/query_parser/query_parser.h"
  36. #include "testing/gtest/include/gtest/gtest.h"
  37. #include "third_party/skia/include/core/SkBitmap.h"
  38. #include "ui/base/models/tree_node_iterator.h"
  39. #include "ui/base/models/tree_node_model.h"
  40. #include "ui/gfx/image/image.h"
  41. #include "url/gurl.h"
  42. using base::ASCIIToUTF16;
  43. using base::Time;
  44. namespace bookmarks {
  45. namespace {
  46. // Test cases used to test the removal of extra whitespace when adding
  47. // a new folder/bookmark or updating a title of a folder/bookmark.
  48. // Note that whitespace characters are all replaced with spaces, but spaces are
  49. // not collapsed or trimmed.
  50. static struct {
  51. const std::string input_title;
  52. const std::string expected_title;
  53. } url_whitespace_test_cases[] = {
  54. {"foobar", "foobar"},
  55. // Newlines.
  56. {"foo\nbar", "foo bar"},
  57. {"foo\n\nbar", "foo bar"},
  58. {"foo\n\n\nbar", "foo bar"},
  59. {"foo\r\nbar", "foo bar"},
  60. {"foo\r\n\r\nbar", "foo bar"},
  61. {"\nfoo\nbar\n", " foo bar "},
  62. // Spaces should not collapse.
  63. {"foo bar", "foo bar"},
  64. {" foo bar ", " foo bar "},
  65. {" foo bar ", " foo bar "},
  66. // Tabs.
  67. {"\tfoo\tbar\t", " foo bar "},
  68. {"\tfoo bar\t", " foo bar "},
  69. // Mixed cases.
  70. {"\tfoo\nbar\t", " foo bar "},
  71. {"\tfoo\r\nbar\t", " foo bar "},
  72. {" foo\tbar\n", " foo bar "},
  73. {"\t foo \t bar \t", " foo bar "},
  74. {"\n foo\r\n\tbar\n \t", " foo bar "},
  75. };
  76. // Test cases used to test the removal of extra whitespace when adding
  77. // a new folder/bookmark or updating a title of a folder/bookmark.
  78. static struct {
  79. const std::string input_title;
  80. const std::string expected_title;
  81. } title_whitespace_test_cases[] = {
  82. {"foobar", "foobar"},
  83. // Newlines.
  84. {"foo\nbar", "foo bar"},
  85. {"foo\n\nbar", "foo bar"},
  86. {"foo\n\n\nbar", "foo bar"},
  87. {"foo\r\nbar", "foo bar"},
  88. {"foo\r\n\r\nbar", "foo bar"},
  89. {"\nfoo\nbar\n", " foo bar "},
  90. // Spaces.
  91. {"foo bar", "foo bar"},
  92. {" foo bar ", " foo bar "},
  93. {" foo bar ", " foo bar "},
  94. // Tabs.
  95. {"\tfoo\tbar\t", " foo bar "},
  96. {"\tfoo bar\t", " foo bar "},
  97. // Mixed cases.
  98. {"\tfoo\nbar\t", " foo bar "},
  99. {"\tfoo\r\nbar\t", " foo bar "},
  100. {" foo\tbar\n", " foo bar "},
  101. {"\t foo \t bar \t", " foo bar "},
  102. {"\n foo\r\n\tbar\n \t", " foo bar "},
  103. };
  104. class ScopedBookmarkUndoDelegate : public BookmarkUndoDelegate {
  105. public:
  106. explicit ScopedBookmarkUndoDelegate(BookmarkModel* model) : model_(model) {
  107. model_->SetUndoDelegate(this);
  108. }
  109. ~ScopedBookmarkUndoDelegate() override { model_->SetUndoDelegate(nullptr); }
  110. void RestoreLastRemovedBookmark() {
  111. DCHECK(undo_provider_);
  112. undo_provider_->RestoreRemovedNode(parent_, index_,
  113. std::move(last_removed_node_));
  114. parent_ = nullptr;
  115. index_ = 0;
  116. }
  117. // BookmarkUndoDelegate overrides.
  118. void SetUndoProvider(BookmarkUndoProvider* provider) override {
  119. undo_provider_ = provider;
  120. }
  121. void OnBookmarkNodeRemoved(BookmarkModel* model,
  122. const BookmarkNode* parent,
  123. size_t index,
  124. std::unique_ptr<BookmarkNode> node) override {
  125. parent_ = parent;
  126. index_ = index;
  127. last_removed_node_ = std::move(node);
  128. }
  129. private:
  130. raw_ptr<BookmarkModel> model_ = nullptr;
  131. raw_ptr<BookmarkUndoProvider> undo_provider_ = nullptr;
  132. raw_ptr<const BookmarkNode> parent_ = nullptr;
  133. size_t index_ = 0;
  134. std::unique_ptr<BookmarkNode> last_removed_node_;
  135. };
  136. // Helper to get a mutable bookmark node.
  137. BookmarkNode* AsMutable(const BookmarkNode* node) {
  138. return const_cast<BookmarkNode*>(node);
  139. }
  140. void SwapDateAdded(BookmarkNode* n1, BookmarkNode* n2) {
  141. Time tmp = n1->date_added();
  142. n1->set_date_added(n2->date_added());
  143. n2->set_date_added(tmp);
  144. }
  145. // See comment in PopulateNodeFromString.
  146. using TestNode = ui::TreeNodeWithValue<BookmarkNode::Type>;
  147. // Does the work of PopulateNodeFromString. index gives the index of the current
  148. // element in description to process.
  149. void PopulateNodeImpl(const std::vector<std::string>& description,
  150. size_t* index,
  151. TestNode* parent) {
  152. while (*index < description.size()) {
  153. const std::string& element = description[*index];
  154. (*index)++;
  155. if (element == "[") {
  156. // Create a new folder and recurse to add all the children.
  157. // Folders are given a unique named by way of an ever increasing integer
  158. // value. The folders need not have a name, but one is assigned to help
  159. // in debugging.
  160. static int next_folder_id = 1;
  161. TestNode* new_node = parent->Add(std::make_unique<TestNode>(
  162. base::NumberToString16(next_folder_id++), BookmarkNode::FOLDER));
  163. PopulateNodeImpl(description, index, new_node);
  164. } else if (element == "]") {
  165. // End the current folder.
  166. return;
  167. } else {
  168. // Add a new URL.
  169. // All tokens must be space separated. If there is a [ or ] in the name it
  170. // likely means a space was forgotten.
  171. DCHECK(element.find('[') == std::string::npos);
  172. DCHECK(element.find(']') == std::string::npos);
  173. parent->Add(std::make_unique<TestNode>(base::UTF8ToUTF16(element),
  174. BookmarkNode::URL));
  175. }
  176. }
  177. }
  178. // Creates and adds nodes to parent based on description. description consists
  179. // of the following tokens (all space separated):
  180. // [ : creates a new USER_FOLDER node. All elements following the [ until the
  181. // next balanced ] is encountered are added as children to the node.
  182. // ] : closes the last folder created by [ so that any further nodes are added
  183. // to the current folders parent.
  184. // text: creates a new URL node.
  185. // For example, "a [b] c" creates the following nodes:
  186. // a 1 c
  187. // |
  188. // b
  189. // In words: a node of type URL with the title a, followed by a folder node with
  190. // the title 1 having the single child of type url with name b, followed by
  191. // the url node with the title c.
  192. //
  193. // NOTE: each name must be unique, and folders are assigned a unique title by
  194. // way of an increasing integer.
  195. void PopulateNodeFromString(const std::string& description, TestNode* parent) {
  196. std::vector<std::string> elements = base::SplitString(
  197. description, base::kWhitespaceASCII,
  198. base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  199. size_t index = 0;
  200. PopulateNodeImpl(elements, &index, parent);
  201. }
  202. // Populates the BookmarkNode with the children of parent.
  203. void PopulateBookmarkNode(TestNode* parent,
  204. BookmarkModel* model,
  205. const BookmarkNode* bb_node) {
  206. for (size_t i = 0; i < parent->children().size(); ++i) {
  207. TestNode* child = parent->children()[i].get();
  208. if (child->value == BookmarkNode::FOLDER) {
  209. const BookmarkNode* new_bb_node =
  210. model->AddFolder(bb_node, i, child->GetTitle());
  211. PopulateBookmarkNode(child, model, new_bb_node);
  212. } else {
  213. model->AddURL(bb_node, i, child->GetTitle(),
  214. GURL("http://" + base::UTF16ToASCII(child->GetTitle())));
  215. }
  216. }
  217. }
  218. // Verifies the contents of the bookmark bar node match the contents of the
  219. // TestNode.
  220. void VerifyModelMatchesNode(TestNode* expected, const BookmarkNode* actual) {
  221. ASSERT_EQ(expected->children().size(), actual->children().size());
  222. for (size_t i = 0; i < expected->children().size(); ++i) {
  223. TestNode* expected_child = expected->children()[i].get();
  224. const BookmarkNode* actual_child = actual->children()[i].get();
  225. ASSERT_EQ(expected_child->GetTitle(), actual_child->GetTitle());
  226. if (expected_child->value == BookmarkNode::FOLDER) {
  227. ASSERT_TRUE(actual_child->type() == BookmarkNode::FOLDER);
  228. // Recurse throught children.
  229. VerifyModelMatchesNode(expected_child, actual_child);
  230. } else {
  231. // No need to check the URL, just the title is enough.
  232. ASSERT_TRUE(actual_child->is_url());
  233. }
  234. }
  235. }
  236. void VerifyNoDuplicateIDs(BookmarkModel* model) {
  237. ui::TreeNodeIterator<const BookmarkNode> it(model->root_node());
  238. std::unordered_set<int64_t> ids;
  239. while (it.has_next())
  240. ASSERT_TRUE(ids.insert(it.Next()->id()).second);
  241. }
  242. class BookmarkModelTest : public testing::Test,
  243. public BookmarkModelObserver,
  244. public BookmarkUndoDelegate {
  245. public:
  246. struct ObserverDetails {
  247. ObserverDetails() {
  248. Set(nullptr, nullptr, static_cast<size_t>(-1), static_cast<size_t>(-1));
  249. }
  250. void Set(const BookmarkNode* node1,
  251. const BookmarkNode* node2,
  252. size_t index1,
  253. size_t index2) {
  254. node1_ = node1;
  255. node2_ = node2;
  256. index1_ = index1;
  257. index2_ = index2;
  258. }
  259. void ExpectEquals(const BookmarkNode* node1,
  260. const BookmarkNode* node2,
  261. size_t index1,
  262. size_t index2) {
  263. EXPECT_EQ(node1_, node1);
  264. EXPECT_EQ(node2_, node2);
  265. EXPECT_EQ(index1_, index1);
  266. EXPECT_EQ(index2_, index2);
  267. }
  268. private:
  269. raw_ptr<const BookmarkNode> node1_;
  270. raw_ptr<const BookmarkNode> node2_;
  271. size_t index1_;
  272. size_t index2_;
  273. };
  274. struct NodeRemovalDetail {
  275. NodeRemovalDetail(const BookmarkNode* parent,
  276. size_t index,
  277. const BookmarkNode* node)
  278. : parent_node_id(parent->id()), index(index), node_id(node->id()) {}
  279. bool operator==(const NodeRemovalDetail& other) const {
  280. return parent_node_id == other.parent_node_id &&
  281. index == other.index &&
  282. node_id == other.node_id;
  283. }
  284. int64_t parent_node_id;
  285. size_t index;
  286. int64_t node_id;
  287. };
  288. BookmarkModelTest() : model_(TestBookmarkClient::CreateModel()) {
  289. model_->AddObserver(this);
  290. ClearCounts();
  291. }
  292. BookmarkModelTest(const BookmarkModelTest&) = delete;
  293. BookmarkModelTest& operator=(const BookmarkModelTest&) = delete;
  294. void BookmarkModelLoaded(BookmarkModel* model, bool ids_reassigned) override {
  295. // We never load from the db, so that this should never get invoked.
  296. NOTREACHED();
  297. }
  298. void BookmarkNodeMoved(BookmarkModel* model,
  299. const BookmarkNode* old_parent,
  300. size_t old_index,
  301. const BookmarkNode* new_parent,
  302. size_t new_index) override {
  303. ++moved_count_;
  304. observer_details_.Set(old_parent, new_parent, old_index, new_index);
  305. }
  306. void BookmarkNodeAdded(BookmarkModel* model,
  307. const BookmarkNode* parent,
  308. size_t index) override {
  309. ++added_count_;
  310. observer_details_.Set(parent, nullptr, index, static_cast<size_t>(-1));
  311. }
  312. void OnWillRemoveBookmarks(BookmarkModel* model,
  313. const BookmarkNode* parent,
  314. size_t old_index,
  315. const BookmarkNode* node) override {
  316. ++before_remove_count_;
  317. }
  318. void SetUndoProvider(BookmarkUndoProvider* provider) override {}
  319. void BookmarkNodeRemoved(BookmarkModel* model,
  320. const BookmarkNode* parent,
  321. size_t old_index,
  322. const BookmarkNode* node,
  323. const std::set<GURL>& removed_urls) override {
  324. ++removed_count_;
  325. observer_details_.Set(parent, nullptr, old_index, static_cast<size_t>(-1));
  326. }
  327. void BookmarkNodeChanged(BookmarkModel* model,
  328. const BookmarkNode* node) override {
  329. ++changed_count_;
  330. observer_details_.Set(node, nullptr, static_cast<size_t>(-1),
  331. static_cast<size_t>(-1));
  332. }
  333. void OnWillChangeBookmarkNode(BookmarkModel* model,
  334. const BookmarkNode* node) override {
  335. ++before_change_count_;
  336. }
  337. void BookmarkNodeChildrenReordered(BookmarkModel* model,
  338. const BookmarkNode* node) override {
  339. ++reordered_count_;
  340. }
  341. void OnWillReorderBookmarkNode(BookmarkModel* model,
  342. const BookmarkNode* node) override {
  343. ++before_reorder_count_;
  344. }
  345. void BookmarkNodeFaviconChanged(BookmarkModel* model,
  346. const BookmarkNode* node) override {
  347. // We never attempt to load favicons, so that this method never
  348. // gets invoked.
  349. }
  350. void ExtensiveBookmarkChangesBeginning(BookmarkModel* model) override {
  351. ++extensive_changes_beginning_count_;
  352. }
  353. void ExtensiveBookmarkChangesEnded(BookmarkModel* model) override {
  354. ++extensive_changes_ended_count_;
  355. }
  356. void BookmarkAllUserNodesRemoved(
  357. BookmarkModel* model,
  358. const std::set<GURL>& removed_urls) override {
  359. ++all_bookmarks_removed_;
  360. }
  361. void OnWillRemoveAllUserBookmarks(BookmarkModel* model) override {
  362. ++before_remove_all_count_;
  363. }
  364. void GroupedBookmarkChangesBeginning(BookmarkModel* model) override {
  365. ++grouped_changes_beginning_count_;
  366. }
  367. void GroupedBookmarkChangesEnded(BookmarkModel* model) override {
  368. ++grouped_changes_ended_count_;
  369. }
  370. void OnBookmarkNodeRemoved(BookmarkModel* model,
  371. const BookmarkNode* parent,
  372. size_t index,
  373. std::unique_ptr<BookmarkNode> node) override {
  374. node_removal_details_.push_back(
  375. NodeRemovalDetail(parent, index, node.get()));
  376. }
  377. void ClearCounts() {
  378. added_count_ = moved_count_ = removed_count_ = changed_count_ =
  379. reordered_count_ = extensive_changes_beginning_count_ =
  380. extensive_changes_ended_count_ = all_bookmarks_removed_ =
  381. before_remove_count_ = before_change_count_ = before_reorder_count_ =
  382. before_remove_all_count_ = grouped_changes_beginning_count_ =
  383. grouped_changes_ended_count_ = 0;
  384. }
  385. void AssertObserverCount(int added_count,
  386. int moved_count,
  387. int removed_count,
  388. int changed_count,
  389. int reordered_count,
  390. int before_remove_count,
  391. int before_change_count,
  392. int before_reorder_count,
  393. int before_remove_all_count) {
  394. EXPECT_EQ(added_count, added_count_);
  395. EXPECT_EQ(moved_count, moved_count_);
  396. EXPECT_EQ(removed_count, removed_count_);
  397. EXPECT_EQ(changed_count, changed_count_);
  398. EXPECT_EQ(reordered_count, reordered_count_);
  399. EXPECT_EQ(before_remove_count, before_remove_count_);
  400. EXPECT_EQ(before_change_count, before_change_count_);
  401. EXPECT_EQ(before_reorder_count, before_reorder_count_);
  402. EXPECT_EQ(before_remove_all_count, before_remove_all_count_);
  403. }
  404. void AssertExtensiveChangesObserverCount(
  405. int extensive_changes_beginning_count,
  406. int extensive_changes_ended_count) {
  407. EXPECT_EQ(extensive_changes_beginning_count,
  408. extensive_changes_beginning_count_);
  409. EXPECT_EQ(extensive_changes_ended_count, extensive_changes_ended_count_);
  410. }
  411. void AssertGroupedChangesObserverCount(
  412. int grouped_changes_beginning_count,
  413. int grouped_changes_ended_count) {
  414. EXPECT_EQ(grouped_changes_beginning_count,
  415. grouped_changes_beginning_count_);
  416. EXPECT_EQ(grouped_changes_ended_count, grouped_changes_ended_count_);
  417. }
  418. int AllNodesRemovedObserverCount() const { return all_bookmarks_removed_; }
  419. BookmarkPermanentNode* ReloadModelWithManagedNode() {
  420. model_->RemoveObserver(this);
  421. auto client = std::make_unique<TestBookmarkClient>();
  422. BookmarkPermanentNode* managed_node = client->EnableManagedNode();
  423. model_ = TestBookmarkClient::CreateModelWithClient(std::move(client));
  424. model_->AddObserver(this);
  425. ClearCounts();
  426. if (!model_->root_node()->GetIndexOf(managed_node).has_value())
  427. ADD_FAILURE();
  428. return managed_node;
  429. }
  430. protected:
  431. std::unique_ptr<BookmarkModel> model_;
  432. ObserverDetails observer_details_;
  433. std::vector<NodeRemovalDetail> node_removal_details_;
  434. private:
  435. int added_count_;
  436. int moved_count_;
  437. int removed_count_;
  438. int changed_count_;
  439. int reordered_count_;
  440. int extensive_changes_beginning_count_;
  441. int extensive_changes_ended_count_;
  442. int all_bookmarks_removed_;
  443. int before_remove_count_;
  444. int before_change_count_;
  445. int before_reorder_count_;
  446. int before_remove_all_count_;
  447. int grouped_changes_beginning_count_;
  448. int grouped_changes_ended_count_;
  449. };
  450. TEST_F(BookmarkModelTest, InitialState) {
  451. const BookmarkNode* bb_node = model_->bookmark_bar_node();
  452. ASSERT_TRUE(bb_node != nullptr);
  453. EXPECT_EQ(0u, bb_node->children().size());
  454. EXPECT_EQ(BookmarkNode::BOOKMARK_BAR, bb_node->type());
  455. const BookmarkNode* other_node = model_->other_node();
  456. ASSERT_TRUE(other_node != nullptr);
  457. EXPECT_EQ(0u, other_node->children().size());
  458. EXPECT_EQ(BookmarkNode::OTHER_NODE, other_node->type());
  459. const BookmarkNode* mobile_node = model_->mobile_node();
  460. ASSERT_TRUE(mobile_node != nullptr);
  461. EXPECT_EQ(0u, mobile_node->children().size());
  462. EXPECT_EQ(BookmarkNode::MOBILE, mobile_node->type());
  463. EXPECT_TRUE(bb_node->id() != other_node->id());
  464. EXPECT_TRUE(bb_node->id() != mobile_node->id());
  465. EXPECT_TRUE(other_node->id() != mobile_node->id());
  466. }
  467. TEST_F(BookmarkModelTest, AddURL) {
  468. const BookmarkNode* root = model_->bookmark_bar_node();
  469. const std::u16string title(u"foo");
  470. const GURL url("http://foo.com");
  471. const BookmarkNode* new_node = model_->AddURL(root, 0, title, url);
  472. AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
  473. observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1));
  474. ASSERT_EQ(1u, root->children().size());
  475. ASSERT_EQ(title, new_node->GetTitle());
  476. ASSERT_TRUE(url == new_node->url());
  477. ASSERT_TRUE(new_node->guid().is_valid());
  478. ASSERT_EQ(BookmarkNode::URL, new_node->type());
  479. ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url));
  480. EXPECT_TRUE(new_node->id() != root->id() &&
  481. new_node->id() != model_->other_node()->id() &&
  482. new_node->id() != model_->mobile_node()->id());
  483. }
  484. TEST_F(BookmarkModelTest, AddURLWithUnicodeTitle) {
  485. const BookmarkNode* root = model_->bookmark_bar_node();
  486. const std::u16string title(
  487. u"\u767e\u5ea6\u4e00\u4e0b\uff0c\u4f60\u5c31\u77e5\u9053");
  488. const GURL url("https://www.baidu.com/");
  489. const BookmarkNode* new_node = model_->AddURL(root, 0, title, url);
  490. AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
  491. observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1));
  492. ASSERT_EQ(1u, root->children().size());
  493. ASSERT_EQ(title, new_node->GetTitle());
  494. ASSERT_TRUE(url == new_node->url());
  495. ASSERT_EQ(BookmarkNode::URL, new_node->type());
  496. ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url));
  497. EXPECT_TRUE(new_node->id() != root->id() &&
  498. new_node->id() != model_->other_node()->id() &&
  499. new_node->id() != model_->mobile_node()->id());
  500. }
  501. TEST_F(BookmarkModelTest, AddURLWithWhitespaceTitle) {
  502. for (size_t i = 0; i < std::size(url_whitespace_test_cases); ++i) {
  503. const BookmarkNode* root = model_->bookmark_bar_node();
  504. const std::u16string title(
  505. ASCIIToUTF16(url_whitespace_test_cases[i].input_title));
  506. const GURL url("http://foo.com");
  507. const BookmarkNode* new_node = model_->AddURL(root, i, title, url);
  508. EXPECT_EQ(i + 1, root->children().size());
  509. EXPECT_EQ(ASCIIToUTF16(url_whitespace_test_cases[i].expected_title),
  510. new_node->GetTitle());
  511. EXPECT_EQ(BookmarkNode::URL, new_node->type());
  512. }
  513. }
  514. TEST_F(BookmarkModelTest, AddURLWithCreationTimeAndMetaInfo) {
  515. const BookmarkNode* root = model_->bookmark_bar_node();
  516. const std::u16string title(u"foo");
  517. const GURL url("http://foo.com");
  518. const Time time = Time::Now() - base::Days(1);
  519. BookmarkNode::MetaInfoMap meta_info;
  520. meta_info["foo"] = "bar";
  521. const BookmarkNode* new_node =
  522. model_->AddURL(root, 0, title, url, &meta_info, time);
  523. AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
  524. observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1));
  525. ASSERT_EQ(1u, root->children().size());
  526. ASSERT_EQ(title, new_node->GetTitle());
  527. ASSERT_TRUE(url == new_node->url());
  528. ASSERT_TRUE(new_node->guid().is_valid());
  529. ASSERT_EQ(BookmarkNode::URL, new_node->type());
  530. ASSERT_EQ(time, new_node->date_added());
  531. ASSERT_TRUE(new_node->GetMetaInfoMap());
  532. ASSERT_EQ(meta_info, *new_node->GetMetaInfoMap());
  533. ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url));
  534. EXPECT_TRUE(new_node->id() != root->id() &&
  535. new_node->id() != model_->other_node()->id() &&
  536. new_node->id() != model_->mobile_node()->id());
  537. }
  538. TEST_F(BookmarkModelTest, AddURLWithGUID) {
  539. const BookmarkNode* root = model_->bookmark_bar_node();
  540. const std::u16string title(u"foo");
  541. const GURL url("http://foo.com");
  542. const Time time = Time::Now() - base::Days(1);
  543. BookmarkNode::MetaInfoMap meta_info;
  544. const base::GUID guid = base::GUID::GenerateRandomV4();
  545. const BookmarkNode* new_node =
  546. model_->AddURL(root, /*index=*/0, title, url, &meta_info, time, guid);
  547. EXPECT_EQ(guid, new_node->guid());
  548. }
  549. TEST_F(BookmarkModelTest, AddURLToMobileBookmarks) {
  550. const BookmarkNode* root = model_->mobile_node();
  551. const std::u16string title(u"foo");
  552. const GURL url("http://foo.com");
  553. const BookmarkNode* new_node = model_->AddURL(root, 0, title, url);
  554. AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
  555. observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1));
  556. ASSERT_EQ(1u, root->children().size());
  557. ASSERT_EQ(title, new_node->GetTitle());
  558. ASSERT_TRUE(url == new_node->url());
  559. ASSERT_EQ(BookmarkNode::URL, new_node->type());
  560. ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url));
  561. EXPECT_TRUE(new_node->id() != root->id() &&
  562. new_node->id() != model_->other_node()->id() &&
  563. new_node->id() != model_->mobile_node()->id());
  564. }
  565. TEST_F(BookmarkModelTest, AddFolder) {
  566. const BookmarkNode* root = model_->bookmark_bar_node();
  567. const std::u16string title(u"foo");
  568. const BookmarkNode* new_node = model_->AddFolder(root, 0, title);
  569. AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
  570. observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1));
  571. ASSERT_EQ(1u, root->children().size());
  572. ASSERT_EQ(title, new_node->GetTitle());
  573. ASSERT_TRUE(new_node->guid().is_valid());
  574. ASSERT_EQ(BookmarkNode::FOLDER, new_node->type());
  575. EXPECT_TRUE(new_node->id() != root->id() &&
  576. new_node->id() != model_->other_node()->id() &&
  577. new_node->id() != model_->mobile_node()->id());
  578. // Add another folder, just to make sure folder_ids are incremented correctly.
  579. ClearCounts();
  580. model_->AddFolder(root, 0, title);
  581. AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
  582. observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1));
  583. }
  584. TEST_F(BookmarkModelTest, AddFolderWithCreationTime) {
  585. const BookmarkNode* root = model_->bookmark_bar_node();
  586. const std::u16string title(u"foo");
  587. BookmarkNode::MetaInfoMap meta_info;
  588. const base::Time creation_time(base::Time::Now() - base::Days(1));
  589. const BookmarkNode* new_node =
  590. model_->AddFolder(root, /*index=*/0, title, &meta_info, creation_time);
  591. EXPECT_EQ(creation_time, new_node->date_added());
  592. }
  593. TEST_F(BookmarkModelTest, AddFolderWithGUID) {
  594. const BookmarkNode* root = model_->bookmark_bar_node();
  595. const std::u16string title(u"foo");
  596. BookmarkNode::MetaInfoMap meta_info;
  597. const base::GUID guid = base::GUID::GenerateRandomV4();
  598. const BookmarkNode* new_node =
  599. model_->AddFolder(root, /*index=*/0, title, &meta_info,
  600. /*creation_time=*/Time::Now(), guid);
  601. EXPECT_EQ(guid, new_node->guid());
  602. }
  603. TEST_F(BookmarkModelTest, AddFolderWithWhitespaceTitle) {
  604. for (size_t i = 0; i < std::size(title_whitespace_test_cases); ++i) {
  605. const BookmarkNode* root = model_->bookmark_bar_node();
  606. const std::u16string title(
  607. ASCIIToUTF16(title_whitespace_test_cases[i].input_title));
  608. const BookmarkNode* new_node = model_->AddFolder(root, i, title);
  609. EXPECT_EQ(i + 1, root->children().size());
  610. EXPECT_EQ(ASCIIToUTF16(title_whitespace_test_cases[i].expected_title),
  611. new_node->GetTitle());
  612. EXPECT_EQ(BookmarkNode::FOLDER, new_node->type());
  613. }
  614. }
  615. TEST_F(BookmarkModelTest, RemoveURL) {
  616. const BookmarkNode* root = model_->bookmark_bar_node();
  617. const std::u16string title(u"foo");
  618. const GURL url("http://foo.com");
  619. model_->AddURL(root, 0, title, url);
  620. ClearCounts();
  621. model_->Remove(root->children().front().get());
  622. ASSERT_EQ(0u, root->children().size());
  623. AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0);
  624. observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1));
  625. // Make sure there is no mapping for the URL.
  626. ASSERT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == nullptr);
  627. }
  628. TEST_F(BookmarkModelTest, RemoveFolder) {
  629. const BookmarkNode* root = model_->bookmark_bar_node();
  630. const BookmarkNode* folder = model_->AddFolder(root, 0, u"foo");
  631. ClearCounts();
  632. // Add a URL as a child.
  633. const std::u16string title(u"foo");
  634. const GURL url("http://foo.com");
  635. model_->AddURL(folder, 0, title, url);
  636. ClearCounts();
  637. // Now remove the folder.
  638. model_->Remove(root->children().front().get());
  639. ASSERT_EQ(0u, root->children().size());
  640. AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0);
  641. observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1));
  642. // Make sure there is no mapping for the URL.
  643. ASSERT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == nullptr);
  644. }
  645. TEST_F(BookmarkModelTest, RemoveAllUserBookmarks) {
  646. const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
  647. ClearCounts();
  648. // Add a url to bookmark bar.
  649. std::u16string title(u"foo");
  650. GURL url("http://foo.com");
  651. const BookmarkNode* url_node =
  652. model_->AddURL(bookmark_bar_node, 0, title, url);
  653. // Add a folder with child URL.
  654. const BookmarkNode* folder = model_->AddFolder(bookmark_bar_node, 0, title);
  655. model_->AddURL(folder, 0, title, url);
  656. AssertObserverCount(3, 0, 0, 0, 0, 0, 0, 0, 0);
  657. ClearCounts();
  658. size_t permanent_node_count = model_->root_node()->children().size();
  659. NodeRemovalDetail expected_node_removal_details[] = {
  660. NodeRemovalDetail(bookmark_bar_node, 1, url_node),
  661. NodeRemovalDetail(bookmark_bar_node, 0, folder),
  662. };
  663. model_->SetUndoDelegate(this);
  664. model_->RemoveAllUserBookmarks();
  665. EXPECT_EQ(0u, bookmark_bar_node->children().size());
  666. // No permanent node should be removed.
  667. EXPECT_EQ(permanent_node_count, model_->root_node()->children().size());
  668. // No individual BookmarkNodeRemoved events are fired, so removed count
  669. // should be 0.
  670. AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 0, 1);
  671. AssertExtensiveChangesObserverCount(1, 1);
  672. AssertGroupedChangesObserverCount(1, 1);
  673. EXPECT_EQ(1, AllNodesRemovedObserverCount());
  674. EXPECT_EQ(1, AllNodesRemovedObserverCount());
  675. ASSERT_EQ(2u, node_removal_details_.size());
  676. EXPECT_EQ(expected_node_removal_details[0], node_removal_details_[0]);
  677. EXPECT_EQ(expected_node_removal_details[1], node_removal_details_[1]);
  678. }
  679. TEST_F(BookmarkModelTest, ClearLastUsedTimeInRange) {
  680. const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
  681. ClearCounts();
  682. base::Time time = base::Time::Now();
  683. // Add a url to bookmark bar.
  684. std::u16string title(u"foo");
  685. GURL url("http://foo.com");
  686. const BookmarkNode* url_node =
  687. model_->AddURL(bookmark_bar_node, 0, title, url);
  688. model_->UpdateLastUsedTime(url_node, time);
  689. // Add a folder with child URL.
  690. const BookmarkNode* folder = model_->AddFolder(bookmark_bar_node, 0, title);
  691. const BookmarkNode* folder_url_node = model_->AddURL(folder, 0, title, url);
  692. model_->UpdateLastUsedTime(folder_url_node, time);
  693. EXPECT_EQ(time, url_node->date_last_used());
  694. EXPECT_EQ(time, folder_url_node->date_last_used());
  695. model_->ClearLastUsedTimeInRange(time - base::Seconds(1),
  696. time + base::Seconds(1));
  697. EXPECT_EQ(base::Time(), url_node->date_last_used());
  698. EXPECT_EQ(base::Time(), folder_url_node->date_last_used());
  699. }
  700. TEST_F(BookmarkModelTest, ClearLastUsedTimeInRangeForAllTime) {
  701. const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
  702. ClearCounts();
  703. base::Time time = base::Time::Now();
  704. // Add a url to bookmark bar.
  705. std::u16string title(u"foo");
  706. GURL url("http://foo.com");
  707. const BookmarkNode* url_node =
  708. model_->AddURL(bookmark_bar_node, 0, title, url);
  709. model_->UpdateLastUsedTime(url_node, time);
  710. // Add a folder with child URL.
  711. const BookmarkNode* folder = model_->AddFolder(bookmark_bar_node, 0, title);
  712. const BookmarkNode* folder_url_node = model_->AddURL(folder, 0, title, url);
  713. model_->UpdateLastUsedTime(folder_url_node, time);
  714. EXPECT_EQ(time, url_node->date_last_used());
  715. EXPECT_EQ(time, folder_url_node->date_last_used());
  716. model_->ClearLastUsedTimeInRange(base::Time(), base::Time::Max());
  717. EXPECT_EQ(base::Time(), url_node->date_last_used());
  718. EXPECT_EQ(base::Time(), folder_url_node->date_last_used());
  719. }
  720. TEST_F(BookmarkModelTest, SetTitle) {
  721. const BookmarkNode* root = model_->bookmark_bar_node();
  722. std::u16string title(u"foo");
  723. const GURL url("http://url.com");
  724. const BookmarkNode* node = model_->AddURL(root, 0, title, url);
  725. ClearCounts();
  726. title = u"goo";
  727. model_->SetTitle(node, title);
  728. AssertObserverCount(0, 0, 0, 1, 0, 0, 1, 0, 0);
  729. observer_details_.ExpectEquals(node, nullptr, static_cast<size_t>(-1),
  730. static_cast<size_t>(-1));
  731. EXPECT_EQ(title, node->GetTitle());
  732. // Should update the index.
  733. auto matches = model_->GetBookmarksMatching(
  734. u"foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT);
  735. EXPECT_TRUE(matches.empty());
  736. matches = model_->GetBookmarksMatching(
  737. u"goo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT);
  738. ASSERT_EQ(1u, matches.size());
  739. EXPECT_EQ(url, matches[0].node->GetTitledUrlNodeUrl());
  740. }
  741. TEST_F(BookmarkModelTest, SetTitleWithWhitespace) {
  742. for (size_t i = 0; i < std::size(title_whitespace_test_cases); ++i) {
  743. const BookmarkNode* root = model_->bookmark_bar_node();
  744. std::u16string title(u"dummy");
  745. const GURL url("http://foo.com");
  746. const BookmarkNode* node = model_->AddURL(root, 0, title, url);
  747. title = ASCIIToUTF16(title_whitespace_test_cases[i].input_title);
  748. model_->SetTitle(node, title);
  749. EXPECT_EQ(ASCIIToUTF16(title_whitespace_test_cases[i].expected_title),
  750. node->GetTitle());
  751. }
  752. }
  753. TEST_F(BookmarkModelTest, SetFolderTitle) {
  754. const BookmarkNode* root = model_->bookmark_bar_node();
  755. const BookmarkNode* folder = model_->AddFolder(root, 0, u"folder");
  756. const std::u16string title(u"foo");
  757. const GURL url("http://foo.com");
  758. const BookmarkNode* node = model_->AddURL(folder, 0, title, url);
  759. ClearCounts();
  760. model_->SetTitle(folder, u"golder");
  761. // Should not change the hierarchy.
  762. EXPECT_EQ(root->children().size(), 1u);
  763. EXPECT_EQ(root->children().front().get(), folder);
  764. EXPECT_EQ(folder->children().size(), 1u);
  765. EXPECT_EQ(folder->children().front().get(), node);
  766. EXPECT_EQ(node->parent(), folder);
  767. // Should update the index.
  768. auto matches = model_->GetBookmarksMatching(
  769. u"folder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT,
  770. /*match_ancestor_titles= */ true);
  771. EXPECT_TRUE(matches.empty());
  772. matches = model_->GetBookmarksMatching(
  773. u"golder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT,
  774. /*match_ancestor_titles= */ true);
  775. ASSERT_EQ(matches.size(), 1u);
  776. EXPECT_EQ(matches[0].node, node);
  777. EXPECT_EQ(matches[0].node->GetTitledUrlNodeUrl(), url);
  778. }
  779. TEST_F(BookmarkModelTest, SetURL) {
  780. const BookmarkNode* root = model_->bookmark_bar_node();
  781. const std::u16string title(u"foo");
  782. GURL url("http://foo.com");
  783. const BookmarkNode* node = model_->AddURL(root, 0, title, url);
  784. ClearCounts();
  785. url = GURL("http://foo2.com");
  786. model_->SetURL(node, url);
  787. AssertObserverCount(0, 0, 0, 1, 0, 0, 1, 0, 0);
  788. observer_details_.ExpectEquals(node, nullptr, static_cast<size_t>(-1),
  789. static_cast<size_t>(-1));
  790. EXPECT_EQ(url, node->url());
  791. }
  792. TEST_F(BookmarkModelTest, SetDateAdded) {
  793. const BookmarkNode* root = model_->bookmark_bar_node();
  794. const std::u16string title(u"foo");
  795. GURL url("http://foo.com");
  796. const BookmarkNode* node = model_->AddURL(root, 0, title, url);
  797. ClearCounts();
  798. base::Time new_time = base::Time::Now() + base::Minutes(20);
  799. model_->SetDateAdded(node, new_time);
  800. AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 0, 0);
  801. EXPECT_EQ(new_time, node->date_added());
  802. EXPECT_EQ(new_time, model_->bookmark_bar_node()->date_folder_modified());
  803. }
  804. TEST_F(BookmarkModelTest, Move) {
  805. const BookmarkNode* root = model_->bookmark_bar_node();
  806. const std::u16string title(u"foo");
  807. const GURL url("http://foo.com");
  808. const BookmarkNode* node = model_->AddURL(root, 0, title, url);
  809. const BookmarkNode* folder1 = model_->AddFolder(root, 0, u"folder");
  810. ClearCounts();
  811. model_->Move(node, folder1, 0);
  812. AssertObserverCount(0, 1, 0, 0, 0, 0, 0, 0, 0);
  813. observer_details_.ExpectEquals(root, folder1, 1, 0);
  814. EXPECT_TRUE(folder1 == node->parent());
  815. EXPECT_EQ(1u, root->children().size());
  816. EXPECT_EQ(folder1, root->children().front().get());
  817. EXPECT_EQ(1u, folder1->children().size());
  818. EXPECT_EQ(node, folder1->children().front().get());
  819. auto matches = model_->GetBookmarksMatching(
  820. u"folder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT,
  821. /*match_ancestor_titles= */ true);
  822. EXPECT_EQ(matches[0].node, node);
  823. // And remove the folder.
  824. ClearCounts();
  825. model_->Remove(root->children().front().get());
  826. AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0);
  827. observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1));
  828. EXPECT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == nullptr);
  829. EXPECT_EQ(0u, root->children().size());
  830. matches = model_->GetBookmarksMatching(
  831. u"foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT);
  832. EXPECT_TRUE(matches.empty());
  833. }
  834. TEST_F(BookmarkModelTest, NonMovingMoveCall) {
  835. const BookmarkNode* root = model_->bookmark_bar_node();
  836. const std::u16string title(u"foo");
  837. const GURL url("http://foo.com");
  838. const base::Time old_date(base::Time::Now() - base::Days(1));
  839. const BookmarkNode* node = model_->AddURL(root, 0, title, url);
  840. model_->SetDateFolderModified(root, old_date);
  841. // Since |node| is already at the index 0 of |root|, this is no-op.
  842. model_->Move(node, root, 0);
  843. // Check that the modification date is kept untouched.
  844. EXPECT_EQ(old_date, root->date_folder_modified());
  845. }
  846. TEST_F(BookmarkModelTest, MoveURLFromFolder) {
  847. const BookmarkNode* root = model_->bookmark_bar_node();
  848. const BookmarkNode* folder1 = model_->AddFolder(root, 0, u"folder");
  849. const BookmarkNode* folder2 = model_->AddFolder(root, 0, u"golder");
  850. const std::u16string title(u"foo");
  851. const GURL url("http://foo.com");
  852. const BookmarkNode* node = model_->AddURL(folder1, 0, title, url);
  853. ClearCounts();
  854. model_->Move(node, folder2, 0);
  855. // Should update the hierarchy.
  856. AssertObserverCount(0, 1, 0, 0, 0, 0, 0, 0, 0);
  857. observer_details_.ExpectEquals(folder1, folder2, 0, 0);
  858. EXPECT_EQ(root->children().size(), 2u);
  859. EXPECT_EQ(folder1->children().size(), 0u);
  860. EXPECT_EQ(folder2->children().size(), 1u);
  861. EXPECT_EQ(folder2->children().front().get(), node);
  862. auto matches = model_->GetBookmarksMatching(
  863. u"folder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT,
  864. /*match_ancestor_titles= */ true);
  865. EXPECT_TRUE(matches.empty());
  866. matches = model_->GetBookmarksMatching(
  867. u"golder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT,
  868. /*match_ancestor_titles= */ true);
  869. EXPECT_EQ(matches[0].node, node);
  870. matches.clear();
  871. // Move back.
  872. ClearCounts();
  873. model_->Move(node, folder1, 0);
  874. // Should update the hierarchy.
  875. AssertObserverCount(0, 1, 0, 0, 0, 0, 0, 0, 0);
  876. observer_details_.ExpectEquals(folder2, folder1, 0, 0);
  877. EXPECT_EQ(root->children().size(), 2u);
  878. EXPECT_EQ(folder1->children().size(), 1u);
  879. EXPECT_EQ(folder2->children().size(), 0u);
  880. EXPECT_EQ(folder1->children().front().get(), node);
  881. matches = model_->GetBookmarksMatching(
  882. u"folder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT,
  883. /*match_ancestor_titles= */ true);
  884. EXPECT_EQ(matches[0].node, node);
  885. matches.clear();
  886. matches = model_->GetBookmarksMatching(
  887. u"golder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT,
  888. /*match_ancestor_titles= */ true);
  889. EXPECT_TRUE(matches.empty());
  890. }
  891. TEST_F(BookmarkModelTest, MoveFolder) {
  892. const BookmarkNode* root = model_->bookmark_bar_node();
  893. const BookmarkNode* folder1 = model_->AddFolder(root, 0, u"folder");
  894. const BookmarkNode* folder2 = model_->AddFolder(root, 1, u"golder");
  895. const BookmarkNode* folder3 = model_->AddFolder(folder1, 0, u"holder");
  896. const std::u16string title(u"foo");
  897. const GURL url("http://foo.com");
  898. const BookmarkNode* node = model_->AddURL(folder3, 0, title, url);
  899. ClearCounts();
  900. model_->Move(folder3, folder2, 0);
  901. // Should update the hierarchy.
  902. AssertObserverCount(0, 1, 0, 0, 0, 0, 0, 0, 0);
  903. observer_details_.ExpectEquals(folder1, folder2, 0, 0);
  904. EXPECT_EQ(root->children().size(), 2u);
  905. EXPECT_EQ(root->children()[0].get(), folder1);
  906. EXPECT_EQ(root->children()[1].get(), folder2);
  907. EXPECT_EQ(folder1->children().size(), 0u);
  908. EXPECT_EQ(folder2->children().size(), 1u);
  909. EXPECT_EQ(folder2->children()[0].get(), folder3);
  910. EXPECT_EQ(folder3->children().size(), 1u);
  911. EXPECT_EQ(folder3->children()[0].get(), node);
  912. // Should update the index.
  913. auto matches = model_->GetBookmarksMatching(
  914. u"folder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT,
  915. /*match_ancestor_titles= */ true);
  916. EXPECT_TRUE(matches.empty());
  917. matches = model_->GetBookmarksMatching(
  918. u"golder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT,
  919. /*match_ancestor_titles= */ true);
  920. EXPECT_EQ(matches[0].node, node);
  921. matches.clear();
  922. matches = model_->GetBookmarksMatching(
  923. u"holder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT,
  924. /*match_ancestor_titles= */ true);
  925. EXPECT_EQ(matches[0].node, node);
  926. matches.clear();
  927. }
  928. TEST_F(BookmarkModelTest, Copy) {
  929. const BookmarkNode* root = model_->bookmark_bar_node();
  930. static const std::string model_string("a 1:[ b c ] d 2:[ e f g ] h ");
  931. test::AddNodesFromModelString(model_.get(), root, model_string);
  932. // Validate initial model.
  933. std::string actual_model_string = test::ModelStringFromNode(root);
  934. EXPECT_EQ(model_string, actual_model_string);
  935. // Copy 'd' to be after '1:b': URL item from bar to folder.
  936. const BookmarkNode* node_to_copy = root->children()[2].get();
  937. const BookmarkNode* destination = root->children()[1].get();
  938. model_->Copy(node_to_copy, destination, 1);
  939. actual_model_string = test::ModelStringFromNode(root);
  940. EXPECT_EQ("a 1:[ b d c ] d 2:[ e f g ] h ", actual_model_string);
  941. // Copy '1:d' to be after 'a': URL item from folder to bar.
  942. const BookmarkNode* folder = root->children()[1].get();
  943. node_to_copy = folder->children()[1].get();
  944. model_->Copy(node_to_copy, root, 1);
  945. actual_model_string = test::ModelStringFromNode(root);
  946. EXPECT_EQ("a d 1:[ b d c ] d 2:[ e f g ] h ", actual_model_string);
  947. // Copy '1' to be after '2:e': Folder from bar to folder.
  948. node_to_copy = root->children()[2].get();
  949. destination = root->children()[4].get();
  950. model_->Copy(node_to_copy, destination, 1);
  951. actual_model_string = test::ModelStringFromNode(root);
  952. EXPECT_EQ("a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f g ] h ",
  953. actual_model_string);
  954. // Copy '2:1' to be after '2:f': Folder within same folder.
  955. folder = root->children()[4].get();
  956. node_to_copy = folder->children()[1].get();
  957. model_->Copy(node_to_copy, folder, 3);
  958. actual_model_string = test::ModelStringFromNode(root);
  959. EXPECT_EQ("a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h ",
  960. actual_model_string);
  961. // Copy first 'd' to be after 'h': URL item within the bar.
  962. node_to_copy = root->children()[1].get();
  963. model_->Copy(node_to_copy, root, 6);
  964. actual_model_string = test::ModelStringFromNode(root);
  965. EXPECT_EQ("a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h d ",
  966. actual_model_string);
  967. // Copy '2' to be after 'a': Folder within the bar.
  968. node_to_copy = root->children()[4].get();
  969. model_->Copy(node_to_copy, root, 1);
  970. actual_model_string = test::ModelStringFromNode(root);
  971. EXPECT_EQ("a 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] d 1:[ b d c ] "
  972. "d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h d ",
  973. actual_model_string);
  974. }
  975. // Tests the default node if no bookmarks have been added yet
  976. TEST_F(BookmarkModelTest, ParentForNewNodesWithEmptyModel) {
  977. #if BUILDFLAG(IS_ANDROID)
  978. ASSERT_EQ(model_->mobile_node(), GetParentForNewNodes(model_.get()));
  979. #else
  980. ASSERT_EQ(model_->bookmark_bar_node(), GetParentForNewNodes(model_.get()));
  981. #endif
  982. }
  983. #if BUILDFLAG(IS_ANDROID)
  984. // Tests that the bookmark_bar_node can still be returned even on Android in
  985. // case the last bookmark was added to it.
  986. TEST_F(BookmarkModelTest, ParentCanBeBookmarkBarOnAndroid) {
  987. const std::u16string title(u"foo");
  988. const GURL url("http://foo.com");
  989. model_->AddURL(model_->bookmark_bar_node(), 0, title, url);
  990. ASSERT_EQ(model_->bookmark_bar_node(), GetParentForNewNodes(model_.get()));
  991. }
  992. #endif
  993. // Tests that adding a URL to a folder updates the last modified time.
  994. TEST_F(BookmarkModelTest, ParentForNewNodes) {
  995. const std::u16string title(u"foo");
  996. const GURL url("http://foo.com");
  997. model_->AddURL(model_->other_node(), 0, title, url);
  998. ASSERT_EQ(model_->other_node(), GetParentForNewNodes(model_.get()));
  999. }
  1000. // Tests that adding a URL to a folder updates the last modified time.
  1001. TEST_F(BookmarkModelTest, ParentForNewMobileNodes) {
  1002. const std::u16string title(u"foo");
  1003. const GURL url("http://foo.com");
  1004. model_->AddURL(model_->mobile_node(), 0, title, url);
  1005. ASSERT_EQ(model_->mobile_node(), GetParentForNewNodes(model_.get()));
  1006. }
  1007. // Make sure recently modified stays in sync when adding a URL.
  1008. TEST_F(BookmarkModelTest, MostRecentlyModifiedFolders) {
  1009. // Add a folder.
  1010. const BookmarkNode* folder =
  1011. model_->AddFolder(model_->other_node(), 0, u"foo");
  1012. // Add a URL to it.
  1013. model_->AddURL(folder, 0, u"blah", GURL("http://foo.com"));
  1014. // Make sure folder is in the most recently modified.
  1015. std::vector<const BookmarkNode*> most_recent_folders =
  1016. GetMostRecentlyModifiedUserFolders(model_.get(), 1);
  1017. ASSERT_EQ(1U, most_recent_folders.size());
  1018. ASSERT_EQ(folder, most_recent_folders[0]);
  1019. // Nuke the folder and do another fetch, making sure folder isn't in the
  1020. // returned list.
  1021. model_->Remove(folder->parent()->children().front().get());
  1022. most_recent_folders = GetMostRecentlyModifiedUserFolders(model_.get(), 1);
  1023. ASSERT_EQ(1U, most_recent_folders.size());
  1024. ASSERT_TRUE(most_recent_folders[0] != folder);
  1025. }
  1026. // Make sure MostRecentlyAddedEntries stays in sync.
  1027. TEST_F(BookmarkModelTest, MostRecentlyAddedEntries) {
  1028. // Add a couple of nodes such that the following holds for the time of the
  1029. // nodes: n1 > n2 > n3 > n4.
  1030. Time base_time = Time::Now();
  1031. BookmarkNode* n1 = AsMutable(model_->AddURL(
  1032. model_->bookmark_bar_node(), 0, u"blah", GURL("http://foo.com/0")));
  1033. BookmarkNode* n2 = AsMutable(model_->AddURL(
  1034. model_->bookmark_bar_node(), 1, u"blah", GURL("http://foo.com/1")));
  1035. BookmarkNode* n3 = AsMutable(model_->AddURL(
  1036. model_->bookmark_bar_node(), 2, u"blah", GURL("http://foo.com/2")));
  1037. BookmarkNode* n4 = AsMutable(model_->AddURL(
  1038. model_->bookmark_bar_node(), 3, u"blah", GURL("http://foo.com/3")));
  1039. n1->set_date_added(base_time + base::Days(4));
  1040. n2->set_date_added(base_time + base::Days(3));
  1041. n3->set_date_added(base_time + base::Days(2));
  1042. n4->set_date_added(base_time + base::Days(1));
  1043. // Make sure order is honored.
  1044. std::vector<const BookmarkNode*> recently_added;
  1045. GetMostRecentlyAddedEntries(model_.get(), 2, &recently_added);
  1046. ASSERT_EQ(2U, recently_added.size());
  1047. ASSERT_TRUE(n1 == recently_added[0]);
  1048. ASSERT_TRUE(n2 == recently_added[1]);
  1049. // swap 1 and 2, then check again.
  1050. recently_added.clear();
  1051. SwapDateAdded(n1, n2);
  1052. GetMostRecentlyAddedEntries(model_.get(), 4, &recently_added);
  1053. ASSERT_EQ(4U, recently_added.size());
  1054. ASSERT_TRUE(n2 == recently_added[0]);
  1055. ASSERT_TRUE(n1 == recently_added[1]);
  1056. ASSERT_TRUE(n3 == recently_added[2]);
  1057. ASSERT_TRUE(n4 == recently_added[3]);
  1058. }
  1059. // Makes sure GetMostRecentlyAddedUserNodeForURL stays in sync.
  1060. TEST_F(BookmarkModelTest, GetMostRecentlyAddedUserNodeForURL) {
  1061. // Add a couple of nodes such that the following holds for the time of the
  1062. // nodes: n1 > n2
  1063. Time base_time = Time::Now();
  1064. const GURL url("http://foo.com/0");
  1065. BookmarkNode* n1 =
  1066. AsMutable(model_->AddURL(model_->bookmark_bar_node(), 0, u"blah", url));
  1067. BookmarkNode* n2 =
  1068. AsMutable(model_->AddURL(model_->bookmark_bar_node(), 1, u"blah", url));
  1069. n1->set_date_added(base_time + base::Days(4));
  1070. n2->set_date_added(base_time + base::Days(3));
  1071. // Make sure order is honored.
  1072. ASSERT_EQ(n1, model_->GetMostRecentlyAddedUserNodeForURL(url));
  1073. // swap 1 and 2, then check again.
  1074. SwapDateAdded(n1, n2);
  1075. ASSERT_EQ(n2, model_->GetMostRecentlyAddedUserNodeForURL(url));
  1076. }
  1077. // Makes sure GetBookmarks removes duplicates.
  1078. TEST_F(BookmarkModelTest, GetBookmarksWithDups) {
  1079. const GURL url("http://foo.com/0");
  1080. const std::u16string title(u"blah");
  1081. model_->AddURL(model_->bookmark_bar_node(), 0, title, url);
  1082. model_->AddURL(model_->bookmark_bar_node(), 1, title, url);
  1083. std::vector<UrlAndTitle> bookmarks;
  1084. model_->GetBookmarks(&bookmarks);
  1085. ASSERT_EQ(1U, bookmarks.size());
  1086. EXPECT_EQ(url, bookmarks[0].url);
  1087. EXPECT_EQ(title, bookmarks[0].title);
  1088. model_->AddURL(model_->bookmark_bar_node(), 2, u"Title2", url);
  1089. // Only one returned, even titles are different.
  1090. bookmarks.clear();
  1091. model_->GetBookmarks(&bookmarks);
  1092. EXPECT_EQ(1U, bookmarks.size());
  1093. }
  1094. TEST_F(BookmarkModelTest, HasBookmarks) {
  1095. const GURL url("http://foo.com/");
  1096. model_->AddURL(model_->bookmark_bar_node(), 0, u"bar", url);
  1097. EXPECT_TRUE(model_->HasBookmarks());
  1098. }
  1099. // http://crbug.com/450464
  1100. TEST_F(BookmarkModelTest, DISABLED_Sort) {
  1101. // Populate the bookmark bar node with nodes for 'B', 'a', 'd' and 'C'.
  1102. // 'C' and 'a' are folders.
  1103. TestNode bbn;
  1104. PopulateNodeFromString("B [ a ] d [ a ]", &bbn);
  1105. const BookmarkNode* parent = model_->bookmark_bar_node();
  1106. PopulateBookmarkNode(&bbn, model_.get(), parent);
  1107. BookmarkNode* child1 = parent->children()[1].get();
  1108. child1->SetTitle(u"a");
  1109. child1->Remove(0);
  1110. BookmarkNode* child3 = parent->children()[3].get();
  1111. child3->SetTitle(u"C");
  1112. child3->Remove(0);
  1113. ClearCounts();
  1114. // Sort the children of the bookmark bar node.
  1115. model_->SortChildren(parent);
  1116. // Make sure we were notified.
  1117. AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 1, 0);
  1118. // Make sure the order matches (remember, 'a' and 'C' are folders and
  1119. // come first).
  1120. EXPECT_EQ(parent->children()[0]->GetTitle(), u"a");
  1121. EXPECT_EQ(parent->children()[1]->GetTitle(), u"C");
  1122. EXPECT_EQ(parent->children()[2]->GetTitle(), u"B");
  1123. EXPECT_EQ(parent->children()[3]->GetTitle(), u"d");
  1124. }
  1125. TEST_F(BookmarkModelTest, Reorder) {
  1126. // Populate the bookmark bar node with nodes 'A', 'B', 'C' and 'D'.
  1127. TestNode bbn;
  1128. PopulateNodeFromString("A B C D", &bbn);
  1129. BookmarkNode* parent = AsMutable(model_->bookmark_bar_node());
  1130. PopulateBookmarkNode(&bbn, model_.get(), parent);
  1131. ClearCounts();
  1132. // Reorder bar node's bookmarks in reverse order.
  1133. std::vector<const BookmarkNode*> new_order = {
  1134. parent->children()[3].get(),
  1135. parent->children()[2].get(),
  1136. parent->children()[1].get(),
  1137. parent->children()[0].get(),
  1138. };
  1139. model_->ReorderChildren(parent, new_order);
  1140. // Make sure we were notified.
  1141. AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 1, 0);
  1142. // Make sure the order matches is correct (it should be reversed).
  1143. ASSERT_EQ(4u, parent->children().size());
  1144. EXPECT_EQ("D", base::UTF16ToASCII(parent->children()[0]->GetTitle()));
  1145. EXPECT_EQ("C", base::UTF16ToASCII(parent->children()[1]->GetTitle()));
  1146. EXPECT_EQ("B", base::UTF16ToASCII(parent->children()[2]->GetTitle()));
  1147. EXPECT_EQ("A", base::UTF16ToASCII(parent->children()[3]->GetTitle()));
  1148. }
  1149. TEST_F(BookmarkModelTest, NodeVisibility) {
  1150. // Mobile node invisible by default
  1151. EXPECT_TRUE(model_->bookmark_bar_node()->IsVisible());
  1152. EXPECT_TRUE(model_->other_node()->IsVisible());
  1153. EXPECT_FALSE(model_->mobile_node()->IsVisible());
  1154. // Arbitrary node should be visible
  1155. TestNode bbn;
  1156. PopulateNodeFromString("B", &bbn);
  1157. const BookmarkNode* parent = model_->mobile_node();
  1158. PopulateBookmarkNode(&bbn, model_.get(), parent);
  1159. EXPECT_TRUE(parent->children().front()->IsVisible());
  1160. // Mobile folder should be visible now that it has a child.
  1161. EXPECT_TRUE(model_->mobile_node()->IsVisible());
  1162. }
  1163. TEST_F(BookmarkModelTest, MobileNodeVisibleWithChildren) {
  1164. const BookmarkNode* root = model_->mobile_node();
  1165. const std::u16string title(u"foo");
  1166. const GURL url("http://foo.com");
  1167. model_->AddURL(root, 0, title, url);
  1168. EXPECT_TRUE(model_->mobile_node()->IsVisible());
  1169. }
  1170. TEST_F(BookmarkModelTest, ExtensiveChangesObserver) {
  1171. AssertExtensiveChangesObserverCount(0, 0);
  1172. EXPECT_FALSE(model_->IsDoingExtensiveChanges());
  1173. model_->BeginExtensiveChanges();
  1174. EXPECT_TRUE(model_->IsDoingExtensiveChanges());
  1175. AssertExtensiveChangesObserverCount(1, 0);
  1176. model_->EndExtensiveChanges();
  1177. EXPECT_FALSE(model_->IsDoingExtensiveChanges());
  1178. AssertExtensiveChangesObserverCount(1, 1);
  1179. }
  1180. TEST_F(BookmarkModelTest, MultipleExtensiveChangesObserver) {
  1181. AssertExtensiveChangesObserverCount(0, 0);
  1182. EXPECT_FALSE(model_->IsDoingExtensiveChanges());
  1183. model_->BeginExtensiveChanges();
  1184. EXPECT_TRUE(model_->IsDoingExtensiveChanges());
  1185. AssertExtensiveChangesObserverCount(1, 0);
  1186. model_->BeginExtensiveChanges();
  1187. EXPECT_TRUE(model_->IsDoingExtensiveChanges());
  1188. AssertExtensiveChangesObserverCount(1, 0);
  1189. model_->EndExtensiveChanges();
  1190. EXPECT_TRUE(model_->IsDoingExtensiveChanges());
  1191. AssertExtensiveChangesObserverCount(1, 0);
  1192. model_->EndExtensiveChanges();
  1193. EXPECT_FALSE(model_->IsDoingExtensiveChanges());
  1194. AssertExtensiveChangesObserverCount(1, 1);
  1195. }
  1196. // Verifies that IsBookmarked is true if any bookmark matches the given URL,
  1197. // and that IsBookmarkedByUser is true only if at least one of the matching
  1198. // bookmarks can be edited by the user.
  1199. TEST_F(BookmarkModelTest, IsBookmarked) {
  1200. // Reload the model with a managed node that is not editable by the user.
  1201. BookmarkPermanentNode* managed_node = ReloadModelWithManagedNode();
  1202. // "google.com" is a "user" bookmark.
  1203. model_->AddURL(model_->other_node(), 0, u"User", GURL("http://google.com"));
  1204. // "youtube.com" is not.
  1205. model_->AddURL(managed_node, 0, u"Managed", GURL("http://youtube.com"));
  1206. EXPECT_TRUE(model_->IsBookmarked(GURL("http://google.com")));
  1207. EXPECT_TRUE(model_->IsBookmarked(GURL("http://youtube.com")));
  1208. EXPECT_FALSE(model_->IsBookmarked(GURL("http://reddit.com")));
  1209. EXPECT_TRUE(IsBookmarkedByUser(model_.get(), GURL("http://google.com")));
  1210. EXPECT_FALSE(IsBookmarkedByUser(model_.get(), GURL("http://youtube.com")));
  1211. EXPECT_FALSE(IsBookmarkedByUser(model_.get(), GURL("http://reddit.com")));
  1212. }
  1213. // Verifies that GetMostRecentlyAddedUserNodeForURL skips bookmarks that
  1214. // are not owned by the user.
  1215. TEST_F(BookmarkModelTest, GetMostRecentlyAddedUserNodeForURLSkipsManagedNodes) {
  1216. // Reload the model with a managed node that is not editable by the user.
  1217. BookmarkPermanentNode* managed_node = ReloadModelWithManagedNode();
  1218. const std::u16string title = u"Title";
  1219. const BookmarkNode* user_parent = model_->other_node();
  1220. const BookmarkNode* managed_parent = managed_node;
  1221. const GURL url("http://google.com");
  1222. // |url| is not bookmarked yet.
  1223. EXPECT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == nullptr);
  1224. // Having a managed node doesn't count.
  1225. model_->AddURL(managed_parent, 0, title, url);
  1226. EXPECT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == nullptr);
  1227. // Now add a user node.
  1228. const BookmarkNode* user = model_->AddURL(user_parent, 0, title, url);
  1229. EXPECT_EQ(user, model_->GetMostRecentlyAddedUserNodeForURL(url));
  1230. // Having a more recent managed node doesn't count either.
  1231. const BookmarkNode* managed = model_->AddURL(managed_parent, 0, title, url);
  1232. EXPECT_GE(managed->date_added(), user->date_added());
  1233. EXPECT_EQ(user, model_->GetMostRecentlyAddedUserNodeForURL(url));
  1234. }
  1235. // Verifies that renaming a bookmark folder does not add the folder node to the
  1236. // autocomplete index. crbug.com/778266
  1237. TEST_F(BookmarkModelTest, RenamedFolderNodeExcludedFromIndex) {
  1238. // Add a folder.
  1239. const BookmarkNode* folder =
  1240. model_->AddFolder(model_->other_node(), 0, u"MyFavorites");
  1241. // Change the folder title.
  1242. model_->SetTitle(folder, u"MyBookmarks");
  1243. // There should be no matching bookmarks.
  1244. std::vector<TitledUrlMatch> matches = model_->GetBookmarksMatching(
  1245. u"MyB", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT);
  1246. EXPECT_TRUE(matches.empty());
  1247. }
  1248. TEST_F(BookmarkModelTest, GetBookmarksMatching) {
  1249. const BookmarkNode* root = model_->bookmark_bar_node();
  1250. const BookmarkNode* folder = model_->AddFolder(root, 0, u"folder");
  1251. const std::u16string title(u"foo");
  1252. const GURL url("http://foo.com");
  1253. const BookmarkNode* node = model_->AddURL(folder, 0, title, url);
  1254. // Should not match paths by default.
  1255. auto matches = model_->GetBookmarksMatching(
  1256. u"folder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT);
  1257. EXPECT_TRUE(matches.empty());
  1258. // Should not match incorrect paths.
  1259. matches = model_->GetBookmarksMatching(
  1260. u"golder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT,
  1261. /*match_ancestor_titles= */ true);
  1262. EXPECT_TRUE(matches.empty());
  1263. // Should match correct paths.
  1264. matches = model_->GetBookmarksMatching(
  1265. u"folder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT,
  1266. /*match_ancestor_titles= */ true);
  1267. EXPECT_EQ(matches[0].node, node);
  1268. }
  1269. // Verifies that TitledUrlIndex is updated when a bookmark is removed.
  1270. TEST_F(BookmarkModelTest, TitledUrlIndexUpdatedOnRemove) {
  1271. const std::u16string title = u"Title";
  1272. const GURL url("http://google.com");
  1273. const BookmarkNode* root = model_->bookmark_bar_node();
  1274. model_->AddURL(root, 0, title, url);
  1275. ASSERT_EQ(1U, model_
  1276. ->GetBookmarksMatching(
  1277. title, 1, query_parser::MatchingAlgorithm::DEFAULT)
  1278. .size());
  1279. // Remove the node and make sure we don't get back any results.
  1280. model_->Remove(root->children().front().get());
  1281. EXPECT_EQ(0U, model_
  1282. ->GetBookmarksMatching(
  1283. title, 1, query_parser::MatchingAlgorithm::DEFAULT)
  1284. .size());
  1285. }
  1286. // Verifies that TitledUrlIndex is updated when a bookmark's title changes.
  1287. TEST_F(BookmarkModelTest, TitledUrlIndexUpdatedOnChangeTitle) {
  1288. const std::u16string initial_title = u"Initial";
  1289. const std::u16string new_title = u"New";
  1290. const GURL url("http://google.com");
  1291. const BookmarkNode* root = model_->bookmark_bar_node();
  1292. model_->AddURL(root, 0, initial_title, url);
  1293. ASSERT_EQ(1U,
  1294. model_
  1295. ->GetBookmarksMatching(initial_title, 1,
  1296. query_parser::MatchingAlgorithm::DEFAULT)
  1297. .size());
  1298. ASSERT_EQ(0U, model_
  1299. ->GetBookmarksMatching(
  1300. new_title, 1, query_parser::MatchingAlgorithm::DEFAULT)
  1301. .size());
  1302. // Change the title.
  1303. model_->SetTitle(root->children().front().get(), new_title);
  1304. // Verify that we only get results for the new title.
  1305. EXPECT_EQ(0U,
  1306. model_
  1307. ->GetBookmarksMatching(initial_title, 1,
  1308. query_parser::MatchingAlgorithm::DEFAULT)
  1309. .size());
  1310. EXPECT_EQ(1U, model_
  1311. ->GetBookmarksMatching(
  1312. new_title, 1, query_parser::MatchingAlgorithm::DEFAULT)
  1313. .size());
  1314. }
  1315. // Verifies that TitledUrlIndex is updated when a bookmark's URL changes.
  1316. TEST_F(BookmarkModelTest, TitledUrlIndexUpdatedOnChangeURL) {
  1317. const std::u16string title = u"Title";
  1318. const GURL initial_url("http://initial");
  1319. const GURL new_url("http://new");
  1320. const BookmarkNode* root = model_->bookmark_bar_node();
  1321. model_->AddURL(root, 0, title, initial_url);
  1322. ASSERT_EQ(1U, model_
  1323. ->GetBookmarksMatching(
  1324. u"initial", 1, query_parser::MatchingAlgorithm::DEFAULT)
  1325. .size());
  1326. ASSERT_EQ(0U, model_
  1327. ->GetBookmarksMatching(
  1328. u"new", 1, query_parser::MatchingAlgorithm::DEFAULT)
  1329. .size());
  1330. // Change the URL.
  1331. model_->SetURL(root->children().front().get(), new_url);
  1332. // Verify that we only get results for the new URL.
  1333. EXPECT_EQ(0U, model_
  1334. ->GetBookmarksMatching(
  1335. u"initial", 1, query_parser::MatchingAlgorithm::DEFAULT)
  1336. .size());
  1337. EXPECT_EQ(1U, model_
  1338. ->GetBookmarksMatching(
  1339. u"new", 1, query_parser::MatchingAlgorithm::DEFAULT)
  1340. .size());
  1341. }
  1342. // Verifies the TitledUrlIndex is probably loaded.
  1343. TEST(BookmarkModelLoadTest, TitledUrlIndexPopulatedOnLoad) {
  1344. // Create a model with a single url.
  1345. base::ScopedTempDir tmp_dir;
  1346. ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
  1347. base::test::TaskEnvironment task_environment{
  1348. base::test::TaskEnvironment::TimeSource::MOCK_TIME};
  1349. std::unique_ptr<BookmarkModel> model =
  1350. std::make_unique<BookmarkModel>(std::make_unique<TestBookmarkClient>());
  1351. model->Load(nullptr, tmp_dir.GetPath());
  1352. test::WaitForBookmarkModelToLoad(model.get());
  1353. const GURL node_url("http://google.com");
  1354. model->AddURL(model->bookmark_bar_node(), 0, u"User", node_url);
  1355. // This is necessary to ensure the save completes.
  1356. task_environment.FastForwardUntilNoTasksRemain();
  1357. // Recreate the model and ensure GetBookmarksMatching() returns the url that
  1358. // was added.
  1359. model =
  1360. std::make_unique<BookmarkModel>(std::make_unique<TestBookmarkClient>());
  1361. model->Load(nullptr, tmp_dir.GetPath());
  1362. test::WaitForBookmarkModelToLoad(model.get());
  1363. std::vector<TitledUrlMatch> matches = model->GetBookmarksMatching(
  1364. u"user", 1, query_parser::MatchingAlgorithm::DEFAULT);
  1365. ASSERT_EQ(1u, matches.size());
  1366. EXPECT_EQ(node_url, matches[0].node->GetTitledUrlNodeUrl());
  1367. }
  1368. TEST(BookmarkNodeTest, NodeMetaInfo) {
  1369. GURL url;
  1370. BookmarkNode node(/*id=*/0, base::GUID::GenerateRandomV4(), url);
  1371. EXPECT_FALSE(node.GetMetaInfoMap());
  1372. EXPECT_TRUE(node.SetMetaInfo("key1", "value1"));
  1373. std::string out_value;
  1374. EXPECT_TRUE(node.GetMetaInfo("key1", &out_value));
  1375. EXPECT_EQ("value1", out_value);
  1376. EXPECT_FALSE(node.SetMetaInfo("key1", "value1"));
  1377. EXPECT_FALSE(node.GetMetaInfo("key2.subkey1", &out_value));
  1378. EXPECT_TRUE(node.SetMetaInfo("key2.subkey1", "value2"));
  1379. EXPECT_TRUE(node.GetMetaInfo("key2.subkey1", &out_value));
  1380. EXPECT_EQ("value2", out_value);
  1381. EXPECT_FALSE(node.GetMetaInfo("key2.subkey2.leaf", &out_value));
  1382. EXPECT_TRUE(node.SetMetaInfo("key2.subkey2.leaf", ""));
  1383. EXPECT_TRUE(node.GetMetaInfo("key2.subkey2.leaf", &out_value));
  1384. EXPECT_EQ("", out_value);
  1385. EXPECT_TRUE(node.DeleteMetaInfo("key1"));
  1386. EXPECT_TRUE(node.DeleteMetaInfo("key2.subkey1"));
  1387. EXPECT_TRUE(node.DeleteMetaInfo("key2.subkey2.leaf"));
  1388. EXPECT_FALSE(node.DeleteMetaInfo("key3"));
  1389. EXPECT_FALSE(node.GetMetaInfo("key1", &out_value));
  1390. EXPECT_FALSE(node.GetMetaInfo("key2.subkey1", &out_value));
  1391. EXPECT_FALSE(node.GetMetaInfo("key2.subkey2", &out_value));
  1392. EXPECT_FALSE(node.GetMetaInfo("key2.subkey2.leaf", &out_value));
  1393. EXPECT_FALSE(node.GetMetaInfoMap());
  1394. }
  1395. // Creates a set of nodes in the bookmark model, and checks that the loaded
  1396. // structure is what we first created.
  1397. TEST(BookmarkModelTest2, CreateAndRestore) {
  1398. struct TestData {
  1399. // Structure of the children of the bookmark model node.
  1400. const std::string bbn_contents;
  1401. // Structure of the children of the other node.
  1402. const std::string other_contents;
  1403. // Structure of the children of the synced node.
  1404. const std::string mobile_contents;
  1405. } data[] = {
  1406. // See PopulateNodeFromString for a description of these strings.
  1407. { "", "" },
  1408. { "a", "b" },
  1409. { "a [ b ]", "" },
  1410. { "", "[ b ] a [ c [ d e [ f ] ] ]" },
  1411. { "a [ b ]", "" },
  1412. { "a b c [ d e [ f ] ]", "g h i [ j k [ l ] ]"},
  1413. };
  1414. std::unique_ptr<BookmarkModel> model;
  1415. for (size_t i = 0; i < std::size(data); ++i) {
  1416. model = TestBookmarkClient::CreateModel();
  1417. TestNode bbn;
  1418. PopulateNodeFromString(data[i].bbn_contents, &bbn);
  1419. PopulateBookmarkNode(&bbn, model.get(), model->bookmark_bar_node());
  1420. TestNode other;
  1421. PopulateNodeFromString(data[i].other_contents, &other);
  1422. PopulateBookmarkNode(&other, model.get(), model->other_node());
  1423. TestNode mobile;
  1424. PopulateNodeFromString(data[i].mobile_contents, &mobile);
  1425. PopulateBookmarkNode(&mobile, model.get(), model->mobile_node());
  1426. VerifyModelMatchesNode(&bbn, model->bookmark_bar_node());
  1427. VerifyModelMatchesNode(&other, model->other_node());
  1428. VerifyModelMatchesNode(&mobile, model->mobile_node());
  1429. VerifyNoDuplicateIDs(model.get());
  1430. }
  1431. }
  1432. } // namespace
  1433. class BookmarkModelFaviconTest : public testing::Test,
  1434. public BookmarkModelObserver {
  1435. public:
  1436. BookmarkModelFaviconTest() : model_(TestBookmarkClient::CreateModel()) {
  1437. model_->AddObserver(this);
  1438. }
  1439. BookmarkModelFaviconTest(const BookmarkModelFaviconTest&) = delete;
  1440. BookmarkModelFaviconTest& operator=(const BookmarkModelFaviconTest&) = delete;
  1441. // Emulates the favicon getting asynchronously loaded. In production, the
  1442. // favicon is asynchronously loaded when BookmarkModel::GetFavicon() is
  1443. // called.
  1444. void OnFaviconLoaded(BookmarkNode* node, const GURL& icon_url) {
  1445. SkBitmap bitmap;
  1446. bitmap.allocN32Pixels(16, 16);
  1447. bitmap.eraseColor(SK_ColorBLUE);
  1448. gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmap);
  1449. favicon_base::FaviconImageResult image_result;
  1450. image_result.image = image;
  1451. image_result.icon_url = icon_url;
  1452. model_->OnFaviconDataAvailable(node, image_result);
  1453. }
  1454. bool WasNodeUpdated(const BookmarkNode* node) {
  1455. return base::Contains(updated_nodes_, node);
  1456. }
  1457. void ClearUpdatedNodes() {
  1458. updated_nodes_.clear();
  1459. }
  1460. protected:
  1461. void BookmarkModelLoaded(BookmarkModel* model, bool ids_reassigned) override {
  1462. }
  1463. void BookmarkNodeMoved(BookmarkModel* model,
  1464. const BookmarkNode* old_parent,
  1465. size_t old_index,
  1466. const BookmarkNode* new_parent,
  1467. size_t new_index) override {}
  1468. void BookmarkNodeAdded(BookmarkModel* model,
  1469. const BookmarkNode* parent,
  1470. size_t index) override {}
  1471. void BookmarkNodeRemoved(BookmarkModel* model,
  1472. const BookmarkNode* parent,
  1473. size_t old_index,
  1474. const BookmarkNode* node,
  1475. const std::set<GURL>& removed_urls) override {}
  1476. void BookmarkNodeChanged(BookmarkModel* model,
  1477. const BookmarkNode* node) override {}
  1478. void BookmarkNodeFaviconChanged(BookmarkModel* model,
  1479. const BookmarkNode* node) override {
  1480. updated_nodes_.push_back(node);
  1481. }
  1482. void BookmarkNodeChildrenReordered(BookmarkModel* model,
  1483. const BookmarkNode* node) override {}
  1484. void BookmarkAllUserNodesRemoved(
  1485. BookmarkModel* model,
  1486. const std::set<GURL>& removed_urls) override {
  1487. }
  1488. std::unique_ptr<BookmarkModel> model_;
  1489. std::vector<const BookmarkNode*> updated_nodes_;
  1490. };
  1491. // Test that BookmarkModel::OnFaviconsChanged() sends a notification that the
  1492. // favicon changed to each BookmarkNode which has either a matching page URL
  1493. // (e.g. http://www.google.com) or a matching icon URL
  1494. // (e.g. http://www.google.com/favicon.ico).
  1495. TEST_F(BookmarkModelFaviconTest, FaviconsChangedObserver) {
  1496. const BookmarkNode* root = model_->bookmark_bar_node();
  1497. std::u16string kTitle(u"foo");
  1498. GURL kPageURL1("http://www.google.com");
  1499. GURL kPageURL2("http://www.google.ca");
  1500. GURL kPageURL3("http://www.amazon.com");
  1501. GURL kFaviconURL12("http://www.google.com/favicon.ico");
  1502. GURL kFaviconURL3("http://www.amazon.com/favicon.ico");
  1503. const BookmarkNode* node1 = model_->AddURL(root, 0, kTitle, kPageURL1);
  1504. const BookmarkNode* node2 = model_->AddURL(root, 0, kTitle, kPageURL2);
  1505. const BookmarkNode* node3 = model_->AddURL(root, 0, kTitle, kPageURL3);
  1506. const BookmarkNode* node4 = model_->AddURL(root, 0, kTitle, kPageURL3);
  1507. {
  1508. OnFaviconLoaded(AsMutable(node1), kFaviconURL12);
  1509. OnFaviconLoaded(AsMutable(node2), kFaviconURL12);
  1510. OnFaviconLoaded(AsMutable(node3), kFaviconURL3);
  1511. OnFaviconLoaded(AsMutable(node4), kFaviconURL3);
  1512. ClearUpdatedNodes();
  1513. std::set<GURL> changed_page_urls;
  1514. changed_page_urls.insert(kPageURL2);
  1515. changed_page_urls.insert(kPageURL3);
  1516. model_->OnFaviconsChanged(changed_page_urls, GURL());
  1517. ASSERT_EQ(3u, updated_nodes_.size());
  1518. EXPECT_TRUE(WasNodeUpdated(node2));
  1519. EXPECT_TRUE(WasNodeUpdated(node3));
  1520. EXPECT_TRUE(WasNodeUpdated(node4));
  1521. }
  1522. {
  1523. // Reset the favicon data because BookmarkModel::OnFaviconsChanged() clears
  1524. // the BookmarkNode's favicon data for all of the BookmarkNodes whose
  1525. // favicon data changed.
  1526. OnFaviconLoaded(AsMutable(node1), kFaviconURL12);
  1527. OnFaviconLoaded(AsMutable(node2), kFaviconURL12);
  1528. OnFaviconLoaded(AsMutable(node3), kFaviconURL3);
  1529. OnFaviconLoaded(AsMutable(node4), kFaviconURL3);
  1530. ClearUpdatedNodes();
  1531. model_->OnFaviconsChanged(std::set<GURL>(), kFaviconURL12);
  1532. ASSERT_EQ(2u, updated_nodes_.size());
  1533. EXPECT_TRUE(WasNodeUpdated(node1));
  1534. EXPECT_TRUE(WasNodeUpdated(node2));
  1535. }
  1536. {
  1537. OnFaviconLoaded(AsMutable(node1), kFaviconURL12);
  1538. OnFaviconLoaded(AsMutable(node2), kFaviconURL12);
  1539. OnFaviconLoaded(AsMutable(node3), kFaviconURL3);
  1540. OnFaviconLoaded(AsMutable(node4), kFaviconURL3);
  1541. ClearUpdatedNodes();
  1542. std::set<GURL> changed_page_urls;
  1543. changed_page_urls.insert(kPageURL1);
  1544. model_->OnFaviconsChanged(changed_page_urls, kFaviconURL12);
  1545. ASSERT_EQ(2u, updated_nodes_.size());
  1546. EXPECT_TRUE(WasNodeUpdated(node1));
  1547. EXPECT_TRUE(WasNodeUpdated(node2));
  1548. }
  1549. }
  1550. TEST_F(BookmarkModelFaviconTest, ShouldResetFaviconStatusAfterRestore) {
  1551. const std::u16string kTitle(u"foo");
  1552. const GURL kPageURL("http://www.google.com");
  1553. const BookmarkNode* bookmark_bar = model_->bookmark_bar_node();
  1554. const BookmarkNode* node = model_->AddURL(bookmark_bar, 0, kTitle, kPageURL);
  1555. ASSERT_FALSE(node->is_favicon_loaded());
  1556. ASSERT_FALSE(node->is_favicon_loading());
  1557. // Initiate favicon loading.
  1558. model_->GetFavicon(node);
  1559. ASSERT_TRUE(node->is_favicon_loading());
  1560. ScopedBookmarkUndoDelegate undo_delegate(model_.get());
  1561. model_->Remove(node);
  1562. undo_delegate.RestoreLastRemovedBookmark();
  1563. EXPECT_FALSE(node->is_favicon_loading());
  1564. EXPECT_FALSE(node->is_favicon_loaded());
  1565. }
  1566. } // namespace bookmarks