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