Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / components / bookmarks / browser / bookmark_model_unittest.cc
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
5 #include "components/bookmarks/browser/bookmark_model.h"
6
7 #include <set>
8 #include <string>
9
10 #include "base/base_paths.h"
11 #include "base/basictypes.h"
12 #include "base/command_line.h"
13 #include "base/compiler_specific.h"
14 #include "base/containers/hash_tables.h"
15 #include "base/strings/string16.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_split.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/time/time.h"
21 #include "components/bookmarks/browser/bookmark_model_observer.h"
22 #include "components/bookmarks/browser/bookmark_utils.h"
23 #include "components/bookmarks/test/bookmark_test_helpers.h"
24 #include "components/bookmarks/test/test_bookmark_client.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "ui/base/models/tree_node_iterator.h"
27 #include "ui/base/models/tree_node_model.h"
28 #include "url/gurl.h"
29
30 using base::ASCIIToUTF16;
31 using base::Time;
32 using base::TimeDelta;
33
34 namespace bookmarks {
35 namespace {
36
37 // Test cases used to test the removal of extra whitespace when adding
38 // a new folder/bookmark or updating a title of a folder/bookmark.
39 static struct {
40   const std::string input_title;
41   const std::string expected_title;
42 } url_whitespace_test_cases[] = {
43   {"foobar", "foobar"},
44   // Newlines.
45   {"foo\nbar", "foo bar"},
46   {"foo\n\nbar", "foo bar"},
47   {"foo\n\n\nbar", "foo bar"},
48   {"foo\r\nbar", "foo bar"},
49   {"foo\r\n\r\nbar", "foo bar"},
50   {"\nfoo\nbar\n", "foo bar"},
51   // Spaces.
52   {"foo  bar", "foo bar"},
53   {" foo bar ", "foo bar"},
54   {"  foo  bar  ", "foo bar"},
55   // Tabs.
56   {"\tfoo\tbar\t", "foo bar"},
57   {"\tfoo bar\t", "foo bar"},
58   // Mixed cases.
59   {"\tfoo\nbar\t", "foo bar"},
60   {"\tfoo\r\nbar\t", "foo bar"},
61   {"  foo\tbar\n", "foo bar"},
62   {"\t foo \t  bar  \t", "foo bar"},
63   {"\n foo\r\n\tbar\n \t", "foo bar"},
64 };
65
66 // Test cases used to test the removal of extra whitespace when adding
67 // a new folder/bookmark or updating a title of a folder/bookmark.
68 static struct {
69   const std::string input_title;
70   const std::string expected_title;
71 } title_whitespace_test_cases[] = {
72   {"foobar", "foobar"},
73   // Newlines.
74   {"foo\nbar", "foo bar"},
75   {"foo\n\nbar", "foo  bar"},
76   {"foo\n\n\nbar", "foo   bar"},
77   {"foo\r\nbar", "foo  bar"},
78   {"foo\r\n\r\nbar", "foo    bar"},
79   {"\nfoo\nbar\n", " foo bar "},
80   // Spaces.
81   {"foo  bar", "foo  bar"},
82   {" foo bar ", " foo bar "},
83   {"  foo  bar  ", "  foo  bar  "},
84   // Tabs.
85   {"\tfoo\tbar\t", " foo bar "},
86   {"\tfoo bar\t", " foo bar "},
87   // Mixed cases.
88   {"\tfoo\nbar\t", " foo bar "},
89   {"\tfoo\r\nbar\t", " foo  bar "},
90   {"  foo\tbar\n", "  foo bar "},
91   {"\t foo \t  bar  \t", "  foo    bar   "},
92   {"\n foo\r\n\tbar\n \t", "  foo   bar   "},
93 };
94
95 // Helper to get a mutable bookmark node.
96 BookmarkNode* AsMutable(const BookmarkNode* node) {
97   return const_cast<BookmarkNode*>(node);
98 }
99
100 void SwapDateAdded(BookmarkNode* n1, BookmarkNode* n2) {
101   Time tmp = n1->date_added();
102   n1->set_date_added(n2->date_added());
103   n2->set_date_added(tmp);
104 }
105
106 class BookmarkModelTest : public testing::Test,
107                           public BookmarkModelObserver {
108  public:
109   struct ObserverDetails {
110     ObserverDetails() {
111       Set(NULL, NULL, -1, -1);
112     }
113
114     void Set(const BookmarkNode* node1,
115              const BookmarkNode* node2,
116              int index1,
117              int index2) {
118       node1_ = node1;
119       node2_ = node2;
120       index1_ = index1;
121       index2_ = index2;
122     }
123
124     void ExpectEquals(const BookmarkNode* node1,
125                       const BookmarkNode* node2,
126                       int index1,
127                       int index2) {
128       EXPECT_EQ(node1_, node1);
129       EXPECT_EQ(node2_, node2);
130       EXPECT_EQ(index1_, index1);
131       EXPECT_EQ(index2_, index2);
132     }
133
134    private:
135     const BookmarkNode* node1_;
136     const BookmarkNode* node2_;
137     int index1_;
138     int index2_;
139   };
140
141   BookmarkModelTest() : model_(client_.CreateModel()) {
142     model_->AddObserver(this);
143     ClearCounts();
144   }
145
146   void BookmarkModelLoaded(BookmarkModel* model, bool ids_reassigned) override {
147     // We never load from the db, so that this should never get invoked.
148     NOTREACHED();
149   }
150
151   void BookmarkNodeMoved(BookmarkModel* model,
152                          const BookmarkNode* old_parent,
153                          int old_index,
154                          const BookmarkNode* new_parent,
155                          int new_index) override {
156     ++moved_count_;
157     observer_details_.Set(old_parent, new_parent, old_index, new_index);
158   }
159
160   void BookmarkNodeAdded(BookmarkModel* model,
161                          const BookmarkNode* parent,
162                          int index) override {
163     ++added_count_;
164     observer_details_.Set(parent, NULL, index, -1);
165   }
166
167   void OnWillRemoveBookmarks(BookmarkModel* model,
168                              const BookmarkNode* parent,
169                              int old_index,
170                              const BookmarkNode* node) override {
171     ++before_remove_count_;
172   }
173
174   void BookmarkNodeRemoved(BookmarkModel* model,
175                            const BookmarkNode* parent,
176                            int old_index,
177                            const BookmarkNode* node,
178                            const std::set<GURL>& removed_urls) override {
179     ++removed_count_;
180     observer_details_.Set(parent, NULL, old_index, -1);
181   }
182
183   void BookmarkNodeChanged(BookmarkModel* model,
184                            const BookmarkNode* node) override {
185     ++changed_count_;
186     observer_details_.Set(node, NULL, -1, -1);
187   }
188
189   void OnWillChangeBookmarkNode(BookmarkModel* model,
190                                 const BookmarkNode* node) override {
191     ++before_change_count_;
192   }
193
194   void BookmarkNodeChildrenReordered(BookmarkModel* model,
195                                      const BookmarkNode* node) override {
196     ++reordered_count_;
197   }
198
199   void OnWillReorderBookmarkNode(BookmarkModel* model,
200                                  const BookmarkNode* node) override {
201     ++before_reorder_count_;
202   }
203
204   void BookmarkNodeFaviconChanged(BookmarkModel* model,
205                                   const BookmarkNode* node) override {
206     // We never attempt to load favicons, so that this method never
207     // gets invoked.
208   }
209
210   void ExtensiveBookmarkChangesBeginning(BookmarkModel* model) override {
211     ++extensive_changes_beginning_count_;
212   }
213
214   void ExtensiveBookmarkChangesEnded(BookmarkModel* model) override {
215     ++extensive_changes_ended_count_;
216   }
217
218   void BookmarkAllUserNodesRemoved(
219       BookmarkModel* model,
220       const std::set<GURL>& removed_urls) override {
221     ++all_bookmarks_removed_;
222   }
223
224   void OnWillRemoveAllUserBookmarks(BookmarkModel* model) override {
225     ++before_remove_all_count_;
226   }
227
228   void ClearCounts() {
229     added_count_ = moved_count_ = removed_count_ = changed_count_ =
230         reordered_count_ = extensive_changes_beginning_count_ =
231         extensive_changes_ended_count_ = all_bookmarks_removed_ =
232         before_remove_count_ = before_change_count_ = before_reorder_count_ =
233         before_remove_all_count_ = 0;
234   }
235
236   void AssertObserverCount(int added_count,
237                            int moved_count,
238                            int removed_count,
239                            int changed_count,
240                            int reordered_count,
241                            int before_remove_count,
242                            int before_change_count,
243                            int before_reorder_count,
244                            int before_remove_all_count) {
245     EXPECT_EQ(added_count_, added_count);
246     EXPECT_EQ(moved_count_, moved_count);
247     EXPECT_EQ(removed_count_, removed_count);
248     EXPECT_EQ(changed_count_, changed_count);
249     EXPECT_EQ(reordered_count_, reordered_count);
250     EXPECT_EQ(before_remove_count_, before_remove_count);
251     EXPECT_EQ(before_change_count_, before_change_count);
252     EXPECT_EQ(before_reorder_count_, before_reorder_count);
253     EXPECT_EQ(before_remove_all_count_, before_remove_all_count);
254   }
255
256   void AssertExtensiveChangesObserverCount(
257       int extensive_changes_beginning_count,
258       int extensive_changes_ended_count) {
259     EXPECT_EQ(extensive_changes_beginning_count_,
260               extensive_changes_beginning_count);
261     EXPECT_EQ(extensive_changes_ended_count_, extensive_changes_ended_count);
262   }
263
264   int AllNodesRemovedObserverCount() const { return all_bookmarks_removed_; }
265
266   BookmarkPermanentNode* ReloadModelWithExtraNode() {
267     BookmarkPermanentNode* extra_node = new BookmarkPermanentNode(100);
268     bookmarks::BookmarkPermanentNodeList extra_nodes;
269     extra_nodes.push_back(extra_node);
270     client_.SetExtraNodesToLoad(extra_nodes.Pass());
271
272     model_->RemoveObserver(this);
273     model_ = client_.CreateModel();
274     model_->AddObserver(this);
275     ClearCounts();
276
277     if (model_->root_node()->GetIndexOf(extra_node) == -1)
278       ADD_FAILURE();
279
280     return extra_node;
281   }
282
283  protected:
284   TestBookmarkClient client_;
285   scoped_ptr<BookmarkModel> model_;
286   ObserverDetails observer_details_;
287
288  private:
289   int added_count_;
290   int moved_count_;
291   int removed_count_;
292   int changed_count_;
293   int reordered_count_;
294   int extensive_changes_beginning_count_;
295   int extensive_changes_ended_count_;
296   int all_bookmarks_removed_;
297   int before_remove_count_;
298   int before_change_count_;
299   int before_reorder_count_;
300   int before_remove_all_count_;
301
302   DISALLOW_COPY_AND_ASSIGN(BookmarkModelTest);
303 };
304
305 TEST_F(BookmarkModelTest, InitialState) {
306   const BookmarkNode* bb_node = model_->bookmark_bar_node();
307   ASSERT_TRUE(bb_node != NULL);
308   EXPECT_EQ(0, bb_node->child_count());
309   EXPECT_EQ(BookmarkNode::BOOKMARK_BAR, bb_node->type());
310
311   const BookmarkNode* other_node = model_->other_node();
312   ASSERT_TRUE(other_node != NULL);
313   EXPECT_EQ(0, other_node->child_count());
314   EXPECT_EQ(BookmarkNode::OTHER_NODE, other_node->type());
315
316   const BookmarkNode* mobile_node = model_->mobile_node();
317   ASSERT_TRUE(mobile_node != NULL);
318   EXPECT_EQ(0, mobile_node->child_count());
319   EXPECT_EQ(BookmarkNode::MOBILE, mobile_node->type());
320
321   EXPECT_TRUE(bb_node->id() != other_node->id());
322   EXPECT_TRUE(bb_node->id() != mobile_node->id());
323   EXPECT_TRUE(other_node->id() != mobile_node->id());
324 }
325
326 TEST_F(BookmarkModelTest, AddURL) {
327   const BookmarkNode* root = model_->bookmark_bar_node();
328   const base::string16 title(ASCIIToUTF16("foo"));
329   const GURL url("http://foo.com");
330
331   const BookmarkNode* new_node = model_->AddURL(root, 0, title, url);
332   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
333   observer_details_.ExpectEquals(root, NULL, 0, -1);
334
335   ASSERT_EQ(1, root->child_count());
336   ASSERT_EQ(title, new_node->GetTitle());
337   ASSERT_TRUE(url == new_node->url());
338   ASSERT_EQ(BookmarkNode::URL, new_node->type());
339   ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url));
340
341   EXPECT_TRUE(new_node->id() != root->id() &&
342               new_node->id() != model_->other_node()->id() &&
343               new_node->id() != model_->mobile_node()->id());
344 }
345
346 TEST_F(BookmarkModelTest, AddURLWithUnicodeTitle) {
347   const BookmarkNode* root = model_->bookmark_bar_node();
348   const base::string16 title(base::WideToUTF16(
349       L"\u767e\u5ea6\u4e00\u4e0b\uff0c\u4f60\u5c31\u77e5\u9053"));
350   const GURL url("https://www.baidu.com/");
351
352   const BookmarkNode* new_node = model_->AddURL(root, 0, title, url);
353   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
354   observer_details_.ExpectEquals(root, NULL, 0, -1);
355
356   ASSERT_EQ(1, root->child_count());
357   ASSERT_EQ(title, new_node->GetTitle());
358   ASSERT_TRUE(url == new_node->url());
359   ASSERT_EQ(BookmarkNode::URL, new_node->type());
360   ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url));
361
362   EXPECT_TRUE(new_node->id() != root->id() &&
363               new_node->id() != model_->other_node()->id() &&
364               new_node->id() != model_->mobile_node()->id());
365 }
366
367 TEST_F(BookmarkModelTest, AddURLWithWhitespaceTitle) {
368   for (size_t i = 0; i < arraysize(url_whitespace_test_cases); ++i) {
369     const BookmarkNode* root = model_->bookmark_bar_node();
370     const base::string16 title(
371         ASCIIToUTF16(url_whitespace_test_cases[i].input_title));
372     const GURL url("http://foo.com");
373
374     const BookmarkNode* new_node = model_->AddURL(root, i, title, url);
375
376     int size = i + 1;
377     EXPECT_EQ(size, root->child_count());
378     EXPECT_EQ(ASCIIToUTF16(url_whitespace_test_cases[i].expected_title),
379               new_node->GetTitle());
380     EXPECT_EQ(BookmarkNode::URL, new_node->type());
381   }
382 }
383
384 TEST_F(BookmarkModelTest, AddURLWithCreationTimeAndMetaInfo) {
385   const BookmarkNode* root = model_->bookmark_bar_node();
386   const base::string16 title(ASCIIToUTF16("foo"));
387   const GURL url("http://foo.com");
388   const Time time = Time::Now() - TimeDelta::FromDays(1);
389   BookmarkNode::MetaInfoMap meta_info;
390   meta_info["foo"] = "bar";
391
392   const BookmarkNode* new_node = model_->AddURLWithCreationTimeAndMetaInfo(
393       root, 0, title, url, time, &meta_info);
394   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
395   observer_details_.ExpectEquals(root, NULL, 0, -1);
396
397   ASSERT_EQ(1, root->child_count());
398   ASSERT_EQ(title, new_node->GetTitle());
399   ASSERT_TRUE(url == new_node->url());
400   ASSERT_EQ(BookmarkNode::URL, new_node->type());
401   ASSERT_EQ(time, new_node->date_added());
402   ASSERT_TRUE(new_node->GetMetaInfoMap());
403   ASSERT_EQ(meta_info, *new_node->GetMetaInfoMap());
404   ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url));
405
406   EXPECT_TRUE(new_node->id() != root->id() &&
407               new_node->id() != model_->other_node()->id() &&
408               new_node->id() != model_->mobile_node()->id());
409 }
410
411 TEST_F(BookmarkModelTest, AddURLToMobileBookmarks) {
412   const BookmarkNode* root = model_->mobile_node();
413   const base::string16 title(ASCIIToUTF16("foo"));
414   const GURL url("http://foo.com");
415
416   const BookmarkNode* new_node = model_->AddURL(root, 0, title, url);
417   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
418   observer_details_.ExpectEquals(root, NULL, 0, -1);
419
420   ASSERT_EQ(1, root->child_count());
421   ASSERT_EQ(title, new_node->GetTitle());
422   ASSERT_TRUE(url == new_node->url());
423   ASSERT_EQ(BookmarkNode::URL, new_node->type());
424   ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url));
425
426   EXPECT_TRUE(new_node->id() != root->id() &&
427               new_node->id() != model_->other_node()->id() &&
428               new_node->id() != model_->mobile_node()->id());
429 }
430
431 TEST_F(BookmarkModelTest, AddFolder) {
432   const BookmarkNode* root = model_->bookmark_bar_node();
433   const base::string16 title(ASCIIToUTF16("foo"));
434
435   const BookmarkNode* new_node = model_->AddFolder(root, 0, title);
436   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
437   observer_details_.ExpectEquals(root, NULL, 0, -1);
438
439   ASSERT_EQ(1, root->child_count());
440   ASSERT_EQ(title, new_node->GetTitle());
441   ASSERT_EQ(BookmarkNode::FOLDER, new_node->type());
442
443   EXPECT_TRUE(new_node->id() != root->id() &&
444               new_node->id() != model_->other_node()->id() &&
445               new_node->id() != model_->mobile_node()->id());
446
447   // Add another folder, just to make sure folder_ids are incremented correctly.
448   ClearCounts();
449   model_->AddFolder(root, 0, title);
450   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
451   observer_details_.ExpectEquals(root, NULL, 0, -1);
452 }
453
454 TEST_F(BookmarkModelTest, AddFolderWithWhitespaceTitle) {
455   for (size_t i = 0; i < arraysize(title_whitespace_test_cases); ++i) {
456     const BookmarkNode* root = model_->bookmark_bar_node();
457     const base::string16 title(
458         ASCIIToUTF16(title_whitespace_test_cases[i].input_title));
459
460     const BookmarkNode* new_node = model_->AddFolder(root, i, title);
461
462     int size = i + 1;
463     EXPECT_EQ(size, root->child_count());
464     EXPECT_EQ(ASCIIToUTF16(title_whitespace_test_cases[i].expected_title),
465               new_node->GetTitle());
466     EXPECT_EQ(BookmarkNode::FOLDER, new_node->type());
467   }
468 }
469
470 TEST_F(BookmarkModelTest, RemoveURL) {
471   const BookmarkNode* root = model_->bookmark_bar_node();
472   const base::string16 title(ASCIIToUTF16("foo"));
473   const GURL url("http://foo.com");
474   model_->AddURL(root, 0, title, url);
475   ClearCounts();
476
477   model_->Remove(root, 0);
478   ASSERT_EQ(0, root->child_count());
479   AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0);
480   observer_details_.ExpectEquals(root, NULL, 0, -1);
481
482   // Make sure there is no mapping for the URL.
483   ASSERT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == NULL);
484 }
485
486 TEST_F(BookmarkModelTest, RemoveFolder) {
487   const BookmarkNode* root = model_->bookmark_bar_node();
488   const BookmarkNode* folder = model_->AddFolder(root, 0, ASCIIToUTF16("foo"));
489
490   ClearCounts();
491
492   // Add a URL as a child.
493   const base::string16 title(ASCIIToUTF16("foo"));
494   const GURL url("http://foo.com");
495   model_->AddURL(folder, 0, title, url);
496
497   ClearCounts();
498
499   // Now remove the folder.
500   model_->Remove(root, 0);
501   ASSERT_EQ(0, root->child_count());
502   AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0);
503   observer_details_.ExpectEquals(root, NULL, 0, -1);
504
505   // Make sure there is no mapping for the URL.
506   ASSERT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == NULL);
507 }
508
509 TEST_F(BookmarkModelTest, RemoveAllUserBookmarks) {
510   const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
511
512   ClearCounts();
513
514   // Add a url to bookmark bar.
515   base::string16 title(ASCIIToUTF16("foo"));
516   GURL url("http://foo.com");
517   model_->AddURL(bookmark_bar_node, 0, title, url);
518
519   // Add a folder with child URL.
520   const BookmarkNode* folder = model_->AddFolder(bookmark_bar_node, 0, title);
521   model_->AddURL(folder, 0, title, url);
522
523   AssertObserverCount(3, 0, 0, 0, 0, 0, 0, 0, 0);
524   ClearCounts();
525
526   model_->RemoveAllUserBookmarks();
527
528   EXPECT_EQ(0, bookmark_bar_node->child_count());
529   // No individual BookmarkNodeRemoved events are fired, so removed count
530   // should be 0.
531   AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 0, 1);
532   AssertExtensiveChangesObserverCount(1, 1);
533   EXPECT_EQ(1, AllNodesRemovedObserverCount());
534 }
535
536 TEST_F(BookmarkModelTest, SetTitle) {
537   const BookmarkNode* root = model_->bookmark_bar_node();
538   base::string16 title(ASCIIToUTF16("foo"));
539   const GURL url("http://foo.com");
540   const BookmarkNode* node = model_->AddURL(root, 0, title, url);
541
542   ClearCounts();
543
544   title = ASCIIToUTF16("foo2");
545   model_->SetTitle(node, title);
546   AssertObserverCount(0, 0, 0, 1, 0, 0, 1, 0, 0);
547   observer_details_.ExpectEquals(node, NULL, -1, -1);
548   EXPECT_EQ(title, node->GetTitle());
549 }
550
551 TEST_F(BookmarkModelTest, SetTitleWithWhitespace) {
552   for (size_t i = 0; i < arraysize(title_whitespace_test_cases); ++i) {
553     const BookmarkNode* root = model_->bookmark_bar_node();
554     base::string16 title(ASCIIToUTF16("dummy"));
555     const GURL url("http://foo.com");
556     const BookmarkNode* node = model_->AddURL(root, 0, title, url);
557
558     title = ASCIIToUTF16(title_whitespace_test_cases[i].input_title);
559     model_->SetTitle(node, title);
560     EXPECT_EQ(ASCIIToUTF16(title_whitespace_test_cases[i].expected_title),
561               node->GetTitle());
562   }
563 }
564
565 TEST_F(BookmarkModelTest, SetURL) {
566   const BookmarkNode* root = model_->bookmark_bar_node();
567   const base::string16 title(ASCIIToUTF16("foo"));
568   GURL url("http://foo.com");
569   const BookmarkNode* node = model_->AddURL(root, 0, title, url);
570
571   ClearCounts();
572
573   url = GURL("http://foo2.com");
574   model_->SetURL(node, url);
575   AssertObserverCount(0, 0, 0, 1, 0, 0, 1, 0, 0);
576   observer_details_.ExpectEquals(node, NULL, -1, -1);
577   EXPECT_EQ(url, node->url());
578 }
579
580 TEST_F(BookmarkModelTest, SetDateAdded) {
581   const BookmarkNode* root = model_->bookmark_bar_node();
582   const base::string16 title(ASCIIToUTF16("foo"));
583   GURL url("http://foo.com");
584   const BookmarkNode* node = model_->AddURL(root, 0, title, url);
585
586   ClearCounts();
587
588   base::Time new_time = base::Time::Now() + base::TimeDelta::FromMinutes(20);
589   model_->SetDateAdded(node, new_time);
590   AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 0, 0);
591   EXPECT_EQ(new_time, node->date_added());
592   EXPECT_EQ(new_time, model_->bookmark_bar_node()->date_folder_modified());
593 }
594
595 TEST_F(BookmarkModelTest, Move) {
596   const BookmarkNode* root = model_->bookmark_bar_node();
597   const base::string16 title(ASCIIToUTF16("foo"));
598   const GURL url("http://foo.com");
599   const BookmarkNode* node = model_->AddURL(root, 0, title, url);
600   const BookmarkNode* folder1 = model_->AddFolder(root, 0, ASCIIToUTF16("foo"));
601   ClearCounts();
602
603   model_->Move(node, folder1, 0);
604
605   AssertObserverCount(0, 1, 0, 0, 0, 0, 0, 0, 0);
606   observer_details_.ExpectEquals(root, folder1, 1, 0);
607   EXPECT_TRUE(folder1 == node->parent());
608   EXPECT_EQ(1, root->child_count());
609   EXPECT_EQ(folder1, root->GetChild(0));
610   EXPECT_EQ(1, folder1->child_count());
611   EXPECT_EQ(node, folder1->GetChild(0));
612
613   // And remove the folder.
614   ClearCounts();
615   model_->Remove(root, 0);
616   AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0);
617   observer_details_.ExpectEquals(root, NULL, 0, -1);
618   EXPECT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == NULL);
619   EXPECT_EQ(0, root->child_count());
620 }
621
622 TEST_F(BookmarkModelTest, NonMovingMoveCall) {
623   const BookmarkNode* root = model_->bookmark_bar_node();
624   const base::string16 title(ASCIIToUTF16("foo"));
625   const GURL url("http://foo.com");
626   const base::Time old_date(base::Time::Now() - base::TimeDelta::FromDays(1));
627
628   const BookmarkNode* node = model_->AddURL(root, 0, title, url);
629   model_->SetDateFolderModified(root, old_date);
630
631   // Since |node| is already at the index 0 of |root|, this is no-op.
632   model_->Move(node, root, 0);
633
634   // Check that the modification date is kept untouched.
635   EXPECT_EQ(old_date, root->date_folder_modified());
636 }
637
638 TEST_F(BookmarkModelTest, Copy) {
639   const BookmarkNode* root = model_->bookmark_bar_node();
640   static const std::string model_string("a 1:[ b c ] d 2:[ e f g ] h ");
641   test::AddNodesFromModelString(model_.get(), root, model_string);
642
643   // Validate initial model.
644   std::string actual_model_string = test::ModelStringFromNode(root);
645   EXPECT_EQ(model_string, actual_model_string);
646
647   // Copy 'd' to be after '1:b': URL item from bar to folder.
648   const BookmarkNode* node_to_copy = root->GetChild(2);
649   const BookmarkNode* destination = root->GetChild(1);
650   model_->Copy(node_to_copy, destination, 1);
651   actual_model_string = test::ModelStringFromNode(root);
652   EXPECT_EQ("a 1:[ b d c ] d 2:[ e f g ] h ", actual_model_string);
653
654   // Copy '1:d' to be after 'a': URL item from folder to bar.
655   const BookmarkNode* folder = root->GetChild(1);
656   node_to_copy = folder->GetChild(1);
657   model_->Copy(node_to_copy, root, 1);
658   actual_model_string = test::ModelStringFromNode(root);
659   EXPECT_EQ("a d 1:[ b d c ] d 2:[ e f g ] h ", actual_model_string);
660
661   // Copy '1' to be after '2:e': Folder from bar to folder.
662   node_to_copy = root->GetChild(2);
663   destination = root->GetChild(4);
664   model_->Copy(node_to_copy, destination, 1);
665   actual_model_string = test::ModelStringFromNode(root);
666   EXPECT_EQ("a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f g ] h ",
667             actual_model_string);
668
669   // Copy '2:1' to be after '2:f': Folder within same folder.
670   folder = root->GetChild(4);
671   node_to_copy = folder->GetChild(1);
672   model_->Copy(node_to_copy, folder, 3);
673   actual_model_string = test::ModelStringFromNode(root);
674   EXPECT_EQ("a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h ",
675             actual_model_string);
676
677   // Copy first 'd' to be after 'h': URL item within the bar.
678   node_to_copy = root->GetChild(1);
679   model_->Copy(node_to_copy, root, 6);
680   actual_model_string = test::ModelStringFromNode(root);
681   EXPECT_EQ("a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h d ",
682             actual_model_string);
683
684   // Copy '2' to be after 'a': Folder within the bar.
685   node_to_copy = root->GetChild(4);
686   model_->Copy(node_to_copy, root, 1);
687   actual_model_string = test::ModelStringFromNode(root);
688   EXPECT_EQ("a 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] d 1:[ b d c ] "
689             "d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h d ",
690             actual_model_string);
691 }
692
693 // Tests that adding a URL to a folder updates the last modified time.
694 TEST_F(BookmarkModelTest, ParentForNewNodes) {
695   ASSERT_EQ(model_->bookmark_bar_node(), model_->GetParentForNewNodes());
696
697   const base::string16 title(ASCIIToUTF16("foo"));
698   const GURL url("http://foo.com");
699
700   model_->AddURL(model_->other_node(), 0, title, url);
701   ASSERT_EQ(model_->other_node(), model_->GetParentForNewNodes());
702 }
703
704 // Tests that adding a URL to a folder updates the last modified time.
705 TEST_F(BookmarkModelTest, ParentForNewMobileNodes) {
706   ASSERT_EQ(model_->bookmark_bar_node(), model_->GetParentForNewNodes());
707
708   const base::string16 title(ASCIIToUTF16("foo"));
709   const GURL url("http://foo.com");
710
711   model_->AddURL(model_->mobile_node(), 0, title, url);
712   ASSERT_EQ(model_->mobile_node(), model_->GetParentForNewNodes());
713 }
714
715 // Make sure recently modified stays in sync when adding a URL.
716 TEST_F(BookmarkModelTest, MostRecentlyModifiedFolders) {
717   // Add a folder.
718   const BookmarkNode* folder =
719       model_->AddFolder(model_->other_node(), 0, ASCIIToUTF16("foo"));
720   // Add a URL to it.
721   model_->AddURL(folder, 0, ASCIIToUTF16("blah"), GURL("http://foo.com"));
722
723   // Make sure folder is in the most recently modified.
724   std::vector<const BookmarkNode*> most_recent_folders =
725       bookmarks::GetMostRecentlyModifiedUserFolders(model_.get(), 1);
726   ASSERT_EQ(1U, most_recent_folders.size());
727   ASSERT_EQ(folder, most_recent_folders[0]);
728
729   // Nuke the folder and do another fetch, making sure folder isn't in the
730   // returned list.
731   model_->Remove(folder->parent(), 0);
732   most_recent_folders =
733       bookmarks::GetMostRecentlyModifiedUserFolders(model_.get(), 1);
734   ASSERT_EQ(1U, most_recent_folders.size());
735   ASSERT_TRUE(most_recent_folders[0] != folder);
736 }
737
738 // Make sure MostRecentlyAddedEntries stays in sync.
739 TEST_F(BookmarkModelTest, MostRecentlyAddedEntries) {
740   // Add a couple of nodes such that the following holds for the time of the
741   // nodes: n1 > n2 > n3 > n4.
742   Time base_time = Time::Now();
743   BookmarkNode* n1 = AsMutable(model_->AddURL(model_->bookmark_bar_node(),
744                                               0,
745                                               ASCIIToUTF16("blah"),
746                                               GURL("http://foo.com/0")));
747   BookmarkNode* n2 = AsMutable(model_->AddURL(model_->bookmark_bar_node(),
748                                               1,
749                                               ASCIIToUTF16("blah"),
750                                               GURL("http://foo.com/1")));
751   BookmarkNode* n3 = AsMutable(model_->AddURL(model_->bookmark_bar_node(),
752                                               2,
753                                               ASCIIToUTF16("blah"),
754                                               GURL("http://foo.com/2")));
755   BookmarkNode* n4 = AsMutable(model_->AddURL(model_->bookmark_bar_node(),
756                                               3,
757                                               ASCIIToUTF16("blah"),
758                                               GURL("http://foo.com/3")));
759   n1->set_date_added(base_time + TimeDelta::FromDays(4));
760   n2->set_date_added(base_time + TimeDelta::FromDays(3));
761   n3->set_date_added(base_time + TimeDelta::FromDays(2));
762   n4->set_date_added(base_time + TimeDelta::FromDays(1));
763
764   // Make sure order is honored.
765   std::vector<const BookmarkNode*> recently_added;
766   bookmarks::GetMostRecentlyAddedEntries(model_.get(), 2, &recently_added);
767   ASSERT_EQ(2U, recently_added.size());
768   ASSERT_TRUE(n1 == recently_added[0]);
769   ASSERT_TRUE(n2 == recently_added[1]);
770
771   // swap 1 and 2, then check again.
772   recently_added.clear();
773   SwapDateAdded(n1, n2);
774   bookmarks::GetMostRecentlyAddedEntries(model_.get(), 4, &recently_added);
775   ASSERT_EQ(4U, recently_added.size());
776   ASSERT_TRUE(n2 == recently_added[0]);
777   ASSERT_TRUE(n1 == recently_added[1]);
778   ASSERT_TRUE(n3 == recently_added[2]);
779   ASSERT_TRUE(n4 == recently_added[3]);
780 }
781
782 // Makes sure GetMostRecentlyAddedUserNodeForURL stays in sync.
783 TEST_F(BookmarkModelTest, GetMostRecentlyAddedUserNodeForURL) {
784   // Add a couple of nodes such that the following holds for the time of the
785   // nodes: n1 > n2
786   Time base_time = Time::Now();
787   const GURL url("http://foo.com/0");
788   BookmarkNode* n1 = AsMutable(model_->AddURL(
789       model_->bookmark_bar_node(), 0, ASCIIToUTF16("blah"), url));
790   BookmarkNode* n2 = AsMutable(model_->AddURL(
791       model_->bookmark_bar_node(), 1, ASCIIToUTF16("blah"), url));
792   n1->set_date_added(base_time + TimeDelta::FromDays(4));
793   n2->set_date_added(base_time + TimeDelta::FromDays(3));
794
795   // Make sure order is honored.
796   ASSERT_EQ(n1, model_->GetMostRecentlyAddedUserNodeForURL(url));
797
798   // swap 1 and 2, then check again.
799   SwapDateAdded(n1, n2);
800   ASSERT_EQ(n2, model_->GetMostRecentlyAddedUserNodeForURL(url));
801 }
802
803 // Makes sure GetBookmarks removes duplicates.
804 TEST_F(BookmarkModelTest, GetBookmarksWithDups) {
805   const GURL url("http://foo.com/0");
806   const base::string16 title(ASCIIToUTF16("blah"));
807   model_->AddURL(model_->bookmark_bar_node(), 0, title, url);
808   model_->AddURL(model_->bookmark_bar_node(), 1, title, url);
809
810   std::vector<BookmarkModel::URLAndTitle> bookmarks;
811   model_->GetBookmarks(&bookmarks);
812   ASSERT_EQ(1U, bookmarks.size());
813   EXPECT_EQ(url, bookmarks[0].url);
814   EXPECT_EQ(title, bookmarks[0].title);
815
816   model_->AddURL(model_->bookmark_bar_node(), 2, ASCIIToUTF16("Title2"), url);
817   // Only one returned, even titles are different.
818   bookmarks.clear();
819   model_->GetBookmarks(&bookmarks);
820   EXPECT_EQ(1U, bookmarks.size());
821 }
822
823 TEST_F(BookmarkModelTest, HasBookmarks) {
824   const GURL url("http://foo.com/");
825   model_->AddURL(model_->bookmark_bar_node(), 0, ASCIIToUTF16("bar"), url);
826
827   EXPECT_TRUE(model_->HasBookmarks());
828 }
829
830 // See comment in PopulateNodeFromString.
831 typedef ui::TreeNodeWithValue<BookmarkNode::Type> TestNode;
832
833 // Does the work of PopulateNodeFromString. index gives the index of the current
834 // element in description to process.
835 void PopulateNodeImpl(const std::vector<std::string>& description,
836                       size_t* index,
837                       TestNode* parent) {
838   while (*index < description.size()) {
839     const std::string& element = description[*index];
840     (*index)++;
841     if (element == "[") {
842       // Create a new folder and recurse to add all the children.
843       // Folders are given a unique named by way of an ever increasing integer
844       // value. The folders need not have a name, but one is assigned to help
845       // in debugging.
846       static int next_folder_id = 1;
847       TestNode* new_node =
848           new TestNode(base::IntToString16(next_folder_id++),
849                        BookmarkNode::FOLDER);
850       parent->Add(new_node, parent->child_count());
851       PopulateNodeImpl(description, index, new_node);
852     } else if (element == "]") {
853       // End the current folder.
854       return;
855     } else {
856       // Add a new URL.
857
858       // All tokens must be space separated. If there is a [ or ] in the name it
859       // likely means a space was forgotten.
860       DCHECK(element.find('[') == std::string::npos);
861       DCHECK(element.find(']') == std::string::npos);
862       parent->Add(new TestNode(base::UTF8ToUTF16(element), BookmarkNode::URL),
863                   parent->child_count());
864     }
865   }
866 }
867
868 // Creates and adds nodes to parent based on description. description consists
869 // of the following tokens (all space separated):
870 //   [ : creates a new USER_FOLDER node. All elements following the [ until the
871 //       next balanced ] is encountered are added as children to the node.
872 //   ] : closes the last folder created by [ so that any further nodes are added
873 //       to the current folders parent.
874 //   text: creates a new URL node.
875 // For example, "a [b] c" creates the following nodes:
876 //   a 1 c
877 //     |
878 //     b
879 // In words: a node of type URL with the title a, followed by a folder node with
880 // the title 1 having the single child of type url with name b, followed by
881 // the url node with the title c.
882 //
883 // NOTE: each name must be unique, and folders are assigned a unique title by
884 // way of an increasing integer.
885 void PopulateNodeFromString(const std::string& description, TestNode* parent) {
886   std::vector<std::string> elements;
887   base::SplitStringAlongWhitespace(description, &elements);
888   size_t index = 0;
889   PopulateNodeImpl(elements, &index, parent);
890 }
891
892 // Populates the BookmarkNode with the children of parent.
893 void PopulateBookmarkNode(TestNode* parent,
894                           BookmarkModel* model,
895                           const BookmarkNode* bb_node) {
896   for (int i = 0; i < parent->child_count(); ++i) {
897     TestNode* child = parent->GetChild(i);
898     if (child->value == BookmarkNode::FOLDER) {
899       const BookmarkNode* new_bb_node =
900           model->AddFolder(bb_node, i, child->GetTitle());
901       PopulateBookmarkNode(child, model, new_bb_node);
902     } else {
903       model->AddURL(bb_node, i, child->GetTitle(),
904           GURL("http://" + base::UTF16ToASCII(child->GetTitle())));
905     }
906   }
907 }
908
909 // Test class that creates a BookmarkModel with a real history backend.
910 class BookmarkModelTestWithProfile : public testing::Test {
911  public:
912   BookmarkModelTestWithProfile() {}
913
914  protected:
915   // Verifies the contents of the bookmark bar node match the contents of the
916   // TestNode.
917   void VerifyModelMatchesNode(TestNode* expected, const BookmarkNode* actual) {
918     ASSERT_EQ(expected->child_count(), actual->child_count());
919     for (int i = 0; i < expected->child_count(); ++i) {
920       TestNode* expected_child = expected->GetChild(i);
921       const BookmarkNode* actual_child = actual->GetChild(i);
922       ASSERT_EQ(expected_child->GetTitle(), actual_child->GetTitle());
923       if (expected_child->value == BookmarkNode::FOLDER) {
924         ASSERT_TRUE(actual_child->type() == BookmarkNode::FOLDER);
925         // Recurse throught children.
926         VerifyModelMatchesNode(expected_child, actual_child);
927         if (HasFatalFailure())
928           return;
929       } else {
930         // No need to check the URL, just the title is enough.
931         ASSERT_TRUE(actual_child->is_url());
932       }
933     }
934   }
935
936   void VerifyNoDuplicateIDs(BookmarkModel* model) {
937     ui::TreeNodeIterator<const BookmarkNode> it(model->root_node());
938     base::hash_set<int64> ids;
939     while (it.has_next())
940       ASSERT_TRUE(ids.insert(it.Next()->id()).second);
941   }
942
943   TestBookmarkClient client_;
944   scoped_ptr<BookmarkModel> model_;
945 };
946
947 // Creates a set of nodes in the bookmark bar model, then recreates the
948 // bookmark bar model which triggers loading from the db and checks the loaded
949 // structure to make sure it is what we first created.
950 TEST_F(BookmarkModelTestWithProfile, CreateAndRestore) {
951   struct TestData {
952     // Structure of the children of the bookmark bar model node.
953     const std::string bbn_contents;
954     // Structure of the children of the other node.
955     const std::string other_contents;
956     // Structure of the children of the synced node.
957     const std::string mobile_contents;
958   } data[] = {
959     // See PopulateNodeFromString for a description of these strings.
960     { "", "" },
961     { "a", "b" },
962     { "a [ b ]", "" },
963     { "", "[ b ] a [ c [ d e [ f ] ] ]" },
964     { "a [ b ]", "" },
965     { "a b c [ d e [ f ] ]", "g h i [ j k [ l ] ]"},
966   };
967   for (size_t i = 0; i < arraysize(data); ++i) {
968     model_ = client_.CreateModel();
969
970     TestNode bbn;
971     PopulateNodeFromString(data[i].bbn_contents, &bbn);
972     PopulateBookmarkNode(&bbn, model_.get(), model_->bookmark_bar_node());
973
974     TestNode other;
975     PopulateNodeFromString(data[i].other_contents, &other);
976     PopulateBookmarkNode(&other, model_.get(), model_->other_node());
977
978     TestNode mobile;
979     PopulateNodeFromString(data[i].mobile_contents, &mobile);
980     PopulateBookmarkNode(&mobile, model_.get(), model_->mobile_node());
981
982     VerifyModelMatchesNode(&bbn, model_->bookmark_bar_node());
983     VerifyModelMatchesNode(&other, model_->other_node());
984     VerifyModelMatchesNode(&mobile, model_->mobile_node());
985     VerifyNoDuplicateIDs(model_.get());
986   }
987 }
988
989 TEST_F(BookmarkModelTest, Sort) {
990   // Populate the bookmark bar node with nodes for 'B', 'a', 'd' and 'C'.
991   // 'C' and 'a' are folders.
992   TestNode bbn;
993   PopulateNodeFromString("B [ a ] d [ a ]", &bbn);
994   const BookmarkNode* parent = model_->bookmark_bar_node();
995   PopulateBookmarkNode(&bbn, model_.get(), parent);
996
997   BookmarkNode* child1 = AsMutable(parent->GetChild(1));
998   child1->SetTitle(ASCIIToUTF16("a"));
999   delete child1->Remove(child1->GetChild(0));
1000   BookmarkNode* child3 = AsMutable(parent->GetChild(3));
1001   child3->SetTitle(ASCIIToUTF16("C"));
1002   delete child3->Remove(child3->GetChild(0));
1003
1004   ClearCounts();
1005
1006   // Sort the children of the bookmark bar node.
1007   model_->SortChildren(parent);
1008
1009   // Make sure we were notified.
1010   AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 1, 0);
1011
1012   // Make sure the order matches (remember, 'a' and 'C' are folders and
1013   // come first).
1014   EXPECT_EQ(parent->GetChild(0)->GetTitle(), ASCIIToUTF16("a"));
1015   EXPECT_EQ(parent->GetChild(1)->GetTitle(), ASCIIToUTF16("C"));
1016   EXPECT_EQ(parent->GetChild(2)->GetTitle(), ASCIIToUTF16("B"));
1017   EXPECT_EQ(parent->GetChild(3)->GetTitle(), ASCIIToUTF16("d"));
1018 }
1019
1020 TEST_F(BookmarkModelTest, Reorder) {
1021   // Populate the bookmark bar node with nodes 'A', 'B', 'C' and 'D'.
1022   TestNode bbn;
1023   PopulateNodeFromString("A B C D", &bbn);
1024   BookmarkNode* parent = AsMutable(model_->bookmark_bar_node());
1025   PopulateBookmarkNode(&bbn, model_.get(), parent);
1026
1027   ClearCounts();
1028
1029   // Reorder bar node's bookmarks in reverse order.
1030   std::vector<const BookmarkNode*> new_order;
1031   new_order.push_back(parent->GetChild(3));
1032   new_order.push_back(parent->GetChild(2));
1033   new_order.push_back(parent->GetChild(1));
1034   new_order.push_back(parent->GetChild(0));
1035   model_->ReorderChildren(parent, new_order);
1036
1037   // Make sure we were notified.
1038   AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 1, 0);
1039
1040   // Make sure the order matches is correct (it should be reversed).
1041   ASSERT_EQ(4, parent->child_count());
1042   EXPECT_EQ("D", base::UTF16ToASCII(parent->GetChild(0)->GetTitle()));
1043   EXPECT_EQ("C", base::UTF16ToASCII(parent->GetChild(1)->GetTitle()));
1044   EXPECT_EQ("B", base::UTF16ToASCII(parent->GetChild(2)->GetTitle()));
1045   EXPECT_EQ("A", base::UTF16ToASCII(parent->GetChild(3)->GetTitle()));
1046 }
1047
1048 TEST_F(BookmarkModelTest, NodeVisibility) {
1049   // Mobile node invisible by default
1050   EXPECT_TRUE(model_->bookmark_bar_node()->IsVisible());
1051   EXPECT_TRUE(model_->other_node()->IsVisible());
1052   EXPECT_FALSE(model_->mobile_node()->IsVisible());
1053
1054   // Visibility of permanent node can only be changed if they are not
1055   // forced to be visible by the client.
1056   model_->SetPermanentNodeVisible(BookmarkNode::BOOKMARK_BAR, false);
1057   EXPECT_TRUE(model_->bookmark_bar_node()->IsVisible());
1058   model_->SetPermanentNodeVisible(BookmarkNode::OTHER_NODE, false);
1059   EXPECT_TRUE(model_->other_node()->IsVisible());
1060   model_->SetPermanentNodeVisible(BookmarkNode::MOBILE, true);
1061   EXPECT_TRUE(model_->mobile_node()->IsVisible());
1062   model_->SetPermanentNodeVisible(BookmarkNode::MOBILE, false);
1063   EXPECT_FALSE(model_->mobile_node()->IsVisible());
1064
1065   // Arbitrary node should be visible
1066   TestNode bbn;
1067   PopulateNodeFromString("B", &bbn);
1068   const BookmarkNode* parent = model_->mobile_node();
1069   PopulateBookmarkNode(&bbn, model_.get(), parent);
1070   EXPECT_TRUE(parent->GetChild(0)->IsVisible());
1071
1072   // Mobile folder should be visible now that it has a child.
1073   EXPECT_TRUE(model_->mobile_node()->IsVisible());
1074 }
1075
1076 TEST_F(BookmarkModelTest, MobileNodeVisibileWithChildren) {
1077   const BookmarkNode* root = model_->mobile_node();
1078   const base::string16 title(ASCIIToUTF16("foo"));
1079   const GURL url("http://foo.com");
1080
1081   model_->AddURL(root, 0, title, url);
1082   EXPECT_TRUE(model_->mobile_node()->IsVisible());
1083 }
1084
1085 TEST_F(BookmarkModelTest, ExtensiveChangesObserver) {
1086   AssertExtensiveChangesObserverCount(0, 0);
1087   EXPECT_FALSE(model_->IsDoingExtensiveChanges());
1088   model_->BeginExtensiveChanges();
1089   EXPECT_TRUE(model_->IsDoingExtensiveChanges());
1090   AssertExtensiveChangesObserverCount(1, 0);
1091   model_->EndExtensiveChanges();
1092   EXPECT_FALSE(model_->IsDoingExtensiveChanges());
1093   AssertExtensiveChangesObserverCount(1, 1);
1094 }
1095
1096 TEST_F(BookmarkModelTest, MultipleExtensiveChangesObserver) {
1097   AssertExtensiveChangesObserverCount(0, 0);
1098   EXPECT_FALSE(model_->IsDoingExtensiveChanges());
1099   model_->BeginExtensiveChanges();
1100   EXPECT_TRUE(model_->IsDoingExtensiveChanges());
1101   AssertExtensiveChangesObserverCount(1, 0);
1102   model_->BeginExtensiveChanges();
1103   EXPECT_TRUE(model_->IsDoingExtensiveChanges());
1104   AssertExtensiveChangesObserverCount(1, 0);
1105   model_->EndExtensiveChanges();
1106   EXPECT_TRUE(model_->IsDoingExtensiveChanges());
1107   AssertExtensiveChangesObserverCount(1, 0);
1108   model_->EndExtensiveChanges();
1109   EXPECT_FALSE(model_->IsDoingExtensiveChanges());
1110   AssertExtensiveChangesObserverCount(1, 1);
1111 }
1112
1113 // Verifies that IsBookmarked is true if any bookmark matches the given URL,
1114 // and that IsBookmarkedByUser is true only if at least one of the matching
1115 // bookmarks can be edited by the user.
1116 TEST_F(BookmarkModelTest, IsBookmarked) {
1117   // Reload the model with an extra node that is not editable by the user.
1118   BookmarkPermanentNode* extra_node = ReloadModelWithExtraNode();
1119
1120   // "google.com" is a "user" bookmark.
1121   model_->AddURL(model_->other_node(), 0, base::ASCIIToUTF16("User"),
1122                  GURL("http://google.com"));
1123   // "youtube.com" is not.
1124   model_->AddURL(extra_node, 0, base::ASCIIToUTF16("Extra"),
1125                  GURL("http://youtube.com"));
1126
1127   EXPECT_TRUE(model_->IsBookmarked(GURL("http://google.com")));
1128   EXPECT_TRUE(model_->IsBookmarked(GURL("http://youtube.com")));
1129   EXPECT_FALSE(model_->IsBookmarked(GURL("http://reddit.com")));
1130
1131   EXPECT_TRUE(
1132       bookmarks::IsBookmarkedByUser(model_.get(), GURL("http://google.com")));
1133   EXPECT_FALSE(
1134       bookmarks::IsBookmarkedByUser(model_.get(), GURL("http://youtube.com")));
1135   EXPECT_FALSE(
1136       bookmarks::IsBookmarkedByUser(model_.get(), GURL("http://reddit.com")));
1137 }
1138
1139 // Verifies that GetMostRecentlyAddedUserNodeForURL skips bookmarks that
1140 // are not owned by the user.
1141 TEST_F(BookmarkModelTest, GetMostRecentlyAddedUserNodeForURLSkipsManagedNodes) {
1142   // Reload the model with an extra node that is not editable by the user.
1143   BookmarkPermanentNode* extra_node = ReloadModelWithExtraNode();
1144
1145   const base::string16 title = base::ASCIIToUTF16("Title");
1146   const BookmarkNode* user_parent = model_->other_node();
1147   const BookmarkNode* managed_parent = extra_node;
1148   const GURL url("http://google.com");
1149
1150   // |url| is not bookmarked yet.
1151   EXPECT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == NULL);
1152
1153   // Having a managed node doesn't count.
1154   model_->AddURL(managed_parent, 0, title, url);
1155   EXPECT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == NULL);
1156
1157   // Now add a user node.
1158   const BookmarkNode* user = model_->AddURL(user_parent, 0, title, url);
1159   EXPECT_EQ(user, model_->GetMostRecentlyAddedUserNodeForURL(url));
1160
1161   // Having a more recent managed node doesn't count either.
1162   const BookmarkNode* managed = model_->AddURL(managed_parent, 0, title, url);
1163   EXPECT_GE(managed->date_added(), user->date_added());
1164   EXPECT_EQ(user, model_->GetMostRecentlyAddedUserNodeForURL(url));
1165 }
1166
1167 TEST(BookmarkNodeTest, NodeMetaInfo) {
1168   GURL url;
1169   BookmarkNode node(url);
1170   EXPECT_FALSE(node.GetMetaInfoMap());
1171
1172   EXPECT_TRUE(node.SetMetaInfo("key1", "value1"));
1173   std::string out_value;
1174   EXPECT_TRUE(node.GetMetaInfo("key1", &out_value));
1175   EXPECT_EQ("value1", out_value);
1176   EXPECT_FALSE(node.SetMetaInfo("key1", "value1"));
1177
1178   EXPECT_FALSE(node.GetMetaInfo("key2.subkey1", &out_value));
1179   EXPECT_TRUE(node.SetMetaInfo("key2.subkey1", "value2"));
1180   EXPECT_TRUE(node.GetMetaInfo("key2.subkey1", &out_value));
1181   EXPECT_EQ("value2", out_value);
1182
1183   EXPECT_FALSE(node.GetMetaInfo("key2.subkey2.leaf", &out_value));
1184   EXPECT_TRUE(node.SetMetaInfo("key2.subkey2.leaf", ""));
1185   EXPECT_TRUE(node.GetMetaInfo("key2.subkey2.leaf", &out_value));
1186   EXPECT_EQ("", out_value);
1187
1188   EXPECT_TRUE(node.DeleteMetaInfo("key1"));
1189   EXPECT_TRUE(node.DeleteMetaInfo("key2.subkey1"));
1190   EXPECT_TRUE(node.DeleteMetaInfo("key2.subkey2.leaf"));
1191   EXPECT_FALSE(node.DeleteMetaInfo("key3"));
1192   EXPECT_FALSE(node.GetMetaInfo("key1", &out_value));
1193   EXPECT_FALSE(node.GetMetaInfo("key2.subkey1", &out_value));
1194   EXPECT_FALSE(node.GetMetaInfo("key2.subkey2", &out_value));
1195   EXPECT_FALSE(node.GetMetaInfo("key2.subkey2.leaf", &out_value));
1196   EXPECT_FALSE(node.GetMetaInfoMap());
1197 }
1198
1199 }  // namespace
1200 }  // namespace bookmarks