Home | History | Annotate | Download | only in browser
      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
   1206