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