Home | History | Annotate | Download | only in enhanced_bookmarks
      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/enhanced_bookmarks/enhanced_bookmark_model.h"
      6 
      7 #include "base/base64.h"
      8 #include "base/macros.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/run_loop.h"
     12 #include "base/strings/string_util.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "components/bookmarks/browser/bookmark_model.h"
     15 #include "components/bookmarks/browser/bookmark_node.h"
     16 #include "components/bookmarks/test/test_bookmark_client.h"
     17 #include "components/enhanced_bookmarks/enhanced_bookmark_model_observer.h"
     18 #include "components/enhanced_bookmarks/proto/metadata.pb.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 #include "url/gurl.h"
     21 
     22 using enhanced_bookmarks::EnhancedBookmarkModel;
     23 
     24 namespace {
     25 const std::string BOOKMARK_URL("http://example.com/index.html");
     26 }  // namespace
     27 
     28 class EnhancedBookmarkModelTest
     29     : public testing::Test,
     30       public enhanced_bookmarks::EnhancedBookmarkModelObserver {
     31  public:
     32   EnhancedBookmarkModelTest()
     33       : loaded_calls_(0),
     34         shutting_down_calls_(0),
     35         added_calls_(0),
     36         removed_calls_(0),
     37         all_user_nodes_removed_calls_(0),
     38         remote_id_changed_calls_(0),
     39         last_added_(NULL),
     40         last_removed_(NULL),
     41         last_remote_id_node_(NULL) {}
     42   virtual ~EnhancedBookmarkModelTest() {}
     43 
     44   virtual void SetUp() OVERRIDE {
     45     message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_DEFAULT));
     46     bookmark_client_.reset(new bookmarks::TestBookmarkClient());
     47     bookmark_model_.reset(bookmark_client_->CreateModel().release());
     48     model_.reset(new EnhancedBookmarkModel(bookmark_model_.get(), "v1.0"));
     49     model_->AddObserver(this);
     50   }
     51 
     52   virtual void TearDown() OVERRIDE {
     53     if (model_)
     54       model_->Shutdown();
     55     model_.reset();
     56     bookmark_model_.reset();
     57     bookmark_client_.reset();
     58     message_loop_.reset();
     59   }
     60 
     61  protected:
     62   const BookmarkNode* AddBookmark() {
     63     return AddBookmark("Some title", bookmark_model_->other_node());
     64   }
     65 
     66   const BookmarkNode* AddFolder() {
     67     return AddFolder("Some title", bookmark_model_->other_node());
     68   }
     69 
     70   const BookmarkNode* AddBookmark(const std::string& name,
     71                                   const BookmarkNode* parent) {
     72     return model_->AddURL(parent,
     73                           0,  // index.
     74                           base::ASCIIToUTF16(name),
     75                           GURL(BOOKMARK_URL),
     76                           base::Time::Now());
     77   }
     78 
     79   const BookmarkNode* AddFolder(const std::string& name,
     80                                 const BookmarkNode* parent) {
     81     return model_->AddFolder(parent, 0, base::ASCIIToUTF16(name));
     82   }
     83 
     84   std::string GetVersion(const BookmarkNode* node) {
     85     return GetMetaInfoField(node, "stars.version");
     86   }
     87 
     88   std::string GetId(const BookmarkNode* node) {
     89     return GetMetaInfoField(node, "stars.id");
     90   }
     91 
     92   std::string GetOldId(const BookmarkNode* node) {
     93     return GetMetaInfoField(node, "stars.oldId");
     94   }
     95 
     96   std::string GetMetaInfoField(const BookmarkNode* node,
     97                                const std::string& name) {
     98     std::string value;
     99     if (!node->GetMetaInfo(name, &value))
    100       return std::string();
    101     return value;
    102   }
    103 
    104   scoped_ptr<base::MessageLoop> message_loop_;
    105   scoped_ptr<bookmarks::TestBookmarkClient> bookmark_client_;
    106   scoped_ptr<BookmarkModel> bookmark_model_;
    107   scoped_ptr<EnhancedBookmarkModel> model_;
    108 
    109   // EnhancedBookmarkModelObserver implementation:
    110   virtual void EnhancedBookmarkModelLoaded() OVERRIDE { loaded_calls_++; }
    111   virtual void EnhancedBookmarkModelShuttingDown() OVERRIDE {
    112     shutting_down_calls_++;
    113   }
    114   virtual void EnhancedBookmarkAdded(const BookmarkNode* node) OVERRIDE {
    115     added_calls_++;
    116     last_added_ = node;
    117   }
    118   virtual void EnhancedBookmarkRemoved(const BookmarkNode* node) OVERRIDE {
    119     removed_calls_++;
    120     last_removed_ = node;
    121   }
    122   virtual void EnhancedBookmarkAllUserNodesRemoved() OVERRIDE {
    123     all_user_nodes_removed_calls_++;
    124   }
    125   virtual void EnhancedBookmarkRemoteIdChanged(
    126       const BookmarkNode* node,
    127       const std::string& old_remote_id,
    128       const std::string& remote_id) OVERRIDE {
    129     remote_id_changed_calls_++;
    130     last_remote_id_node_ = node;
    131     last_old_remote_id_ = old_remote_id;
    132     last_remote_id_ = remote_id;
    133   }
    134 
    135   // Observer call counters:
    136   int loaded_calls_;
    137   int shutting_down_calls_;
    138   int added_calls_;
    139   int removed_calls_;
    140   int all_user_nodes_removed_calls_;
    141   int remote_id_changed_calls_;
    142 
    143   // Observer parameter cache:
    144   const BookmarkNode* last_added_;
    145   const BookmarkNode* last_removed_;
    146   const BookmarkNode* last_remote_id_node_;
    147   std::string last_old_remote_id_;
    148   std::string last_remote_id_;
    149 
    150  private:
    151   DISALLOW_COPY_AND_ASSIGN(EnhancedBookmarkModelTest);
    152 };
    153 
    154 TEST_F(EnhancedBookmarkModelTest, TestEmptySnippet) {
    155   const BookmarkNode* node = AddBookmark();
    156 
    157   std::string snippet(model_->GetSnippet(node));
    158   EXPECT_EQ(snippet, "");
    159 };
    160 
    161 TEST_F(EnhancedBookmarkModelTest, TestSnippet) {
    162   const BookmarkNode* node = AddBookmark();
    163 
    164   // Binary serialize the protobuf.
    165   image::collections::PageData data;
    166   data.set_snippet("I'm happy!");
    167   ASSERT_TRUE(data.IsInitialized());
    168   std::string output;
    169   bool result = data.SerializeToString(&output);
    170   ASSERT_TRUE(result);
    171 
    172   // base64 encode the output.
    173   std::string encoded;
    174   base::Base64Encode(output, &encoded);
    175   bookmark_model_->SetNodeMetaInfo(node, "stars.pageData", encoded);
    176 
    177   std::string snippet(model_->GetSnippet(node));
    178   EXPECT_EQ(snippet, "I'm happy!");
    179 }
    180 
    181 TEST_F(EnhancedBookmarkModelTest, TestBadEncodingSnippet) {
    182   const BookmarkNode* node = AddBookmark();
    183 
    184   // Binary serialize the protobuf.
    185   image::collections::PageData data;
    186   data.set_snippet("You are happy!");
    187   ASSERT_TRUE(data.IsInitialized());
    188   std::string output;
    189   bool result = data.SerializeToString(&output);
    190   ASSERT_TRUE(result);
    191 
    192   // don't base 64 encode the output.
    193   bookmark_model_->SetNodeMetaInfo(node, "stars.pageData", output);
    194 
    195   std::string snippet(model_->GetSnippet(node));
    196   EXPECT_EQ(snippet, "");
    197 }
    198 
    199 TEST_F(EnhancedBookmarkModelTest, TestOriginalImage) {
    200   const BookmarkNode* node = AddBookmark();
    201 
    202   image::collections::ImageData data;
    203   // Intentionally make raw pointer.
    204   image::collections::ImageData_ImageInfo* info =
    205       new image::collections::ImageData_ImageInfo;
    206   info->set_url("http://example.com/foobar");
    207   info->set_width(15);
    208   info->set_height(55);
    209   // This method consumes the pointer.
    210   data.set_allocated_original_info(info);
    211 
    212   std::string output;
    213   bool result = data.SerializePartialToString(&output);
    214   ASSERT_TRUE(result);
    215 
    216   // base64 encode the output.
    217   std::string encoded;
    218   base::Base64Encode(output, &encoded);
    219   bookmark_model_->SetNodeMetaInfo(node, "stars.imageData", encoded);
    220 
    221   GURL url;
    222   int width;
    223   int height;
    224   result = model_->GetOriginalImage(node, &url, &width, &height);
    225   ASSERT_TRUE(result);
    226   EXPECT_EQ(url, GURL("http://example.com/foobar"));
    227   EXPECT_EQ(width, 15);
    228   EXPECT_EQ(height, 55);
    229 }
    230 
    231 TEST_F(EnhancedBookmarkModelTest, TestThumbnailImage) {
    232   const BookmarkNode* node = AddBookmark();
    233 
    234   image::collections::ImageData data;
    235   // Intentionally make raw pointer.
    236   image::collections::ImageData_ImageInfo* info =
    237       new image::collections::ImageData_ImageInfo;
    238   info->set_url("http://example.com/foobar");
    239   info->set_width(15);
    240   info->set_height(55);
    241   // This method consumes the pointer.
    242   data.set_allocated_thumbnail_info(info);
    243 
    244   std::string output;
    245   bool result = data.SerializePartialToString(&output);
    246   ASSERT_TRUE(result);
    247 
    248   // base64 encode the output.
    249   std::string encoded;
    250   base::Base64Encode(output, &encoded);
    251   bookmark_model_->SetNodeMetaInfo(node, "stars.imageData", encoded);
    252 
    253   GURL url;
    254   int width;
    255   int height;
    256   result = model_->GetThumbnailImage(node, &url, &width, &height);
    257   ASSERT_TRUE(result);
    258   EXPECT_EQ(url, GURL("http://example.com/foobar"));
    259   EXPECT_EQ(width, 15);
    260   EXPECT_EQ(height, 55);
    261 }
    262 
    263 TEST_F(EnhancedBookmarkModelTest, TestOriginalImageMissingDimensions) {
    264   const BookmarkNode* node = AddBookmark();
    265 
    266   image::collections::ImageData data;
    267   // Intentionally make raw pointer.
    268   image::collections::ImageData_ImageInfo* info =
    269       new image::collections::ImageData_ImageInfo;
    270   info->set_url("http://example.com/foobar");
    271   // This method consumes the pointer.
    272   data.set_allocated_original_info(info);
    273 
    274   std::string output;
    275   bool result = data.SerializePartialToString(&output);
    276   ASSERT_TRUE(result);
    277 
    278   // base64 encode the output.
    279   std::string encoded;
    280   base::Base64Encode(output, &encoded);
    281   bookmark_model_->SetNodeMetaInfo(node, "stars.imageData", encoded);
    282 
    283   GURL url;
    284   int width;
    285   int height;
    286   result = model_->GetOriginalImage(node, &url, &width, &height);
    287   ASSERT_FALSE(result);
    288 }
    289 
    290 TEST_F(EnhancedBookmarkModelTest, TestOriginalImageBadUrl) {
    291   const BookmarkNode* node = AddBookmark();
    292 
    293   image::collections::ImageData data;
    294   // Intentionally make raw pointer.
    295   image::collections::ImageData_ImageInfo* info =
    296       new image::collections::ImageData_ImageInfo;
    297   info->set_url("asdf. 13r");
    298   info->set_width(15);
    299   info->set_height(55);
    300   // This method consumes the pointer.
    301   data.set_allocated_original_info(info);
    302 
    303   std::string output;
    304   bool result = data.SerializePartialToString(&output);
    305   ASSERT_TRUE(result);
    306 
    307   // base64 encode the output.
    308   std::string encoded;
    309   base::Base64Encode(output, &encoded);
    310   bookmark_model_->SetNodeMetaInfo(node, "stars.imageData", encoded);
    311 
    312   GURL url;
    313   int width;
    314   int height;
    315   result = model_->GetOriginalImage(node, &url, &width, &height);
    316   ASSERT_FALSE(result);
    317 }
    318 
    319 TEST_F(EnhancedBookmarkModelTest, TestEncodeDecode) {
    320   const BookmarkNode* node = AddBookmark();
    321 
    322   bool result =
    323       model_->SetOriginalImage(node, GURL("http://example.com/i.jpg"), 22, 33);
    324   ASSERT_TRUE(result);
    325 
    326   GURL url;
    327   int width;
    328   int height;
    329   result = model_->GetOriginalImage(node, &url, &width, &height);
    330   ASSERT_TRUE(result);
    331   EXPECT_EQ(url, GURL("http://example.com/i.jpg"));
    332   EXPECT_EQ(width, 22);
    333   EXPECT_EQ(height, 33);
    334   EXPECT_EQ("v1.0", GetVersion(node));
    335 }
    336 
    337 TEST_F(EnhancedBookmarkModelTest, TestDoubleEncodeDecode) {
    338   const BookmarkNode* node = AddBookmark();
    339 
    340   // Encode some information.
    341   bool result =
    342       model_->SetOriginalImage(node, GURL("http://example.com/i.jpg"), 22, 33);
    343   ASSERT_TRUE(result);
    344   // Encode some different information.
    345   result =
    346       model_->SetOriginalImage(node, GURL("http://example.com/i.jpg"), 33, 44);
    347   ASSERT_TRUE(result);
    348 
    349   GURL url;
    350   int width;
    351   int height;
    352   result = model_->GetOriginalImage(node, &url, &width, &height);
    353   ASSERT_TRUE(result);
    354   EXPECT_EQ(url, GURL("http://example.com/i.jpg"));
    355   EXPECT_EQ(width, 33);
    356   EXPECT_EQ(height, 44);
    357   EXPECT_EQ("v1.0", GetVersion(node));
    358 }
    359 
    360 TEST_F(EnhancedBookmarkModelTest, TestRemoteId) {
    361   const BookmarkNode* node = AddBookmark();
    362   // Verify that the remote id starts with the correct prefix.
    363   EXPECT_TRUE(StartsWithASCII(model_->GetRemoteId(node), "ebc_", true));
    364 
    365   // Getting the remote id for nodes that don't have them should return the
    366   // empty string.
    367   const BookmarkNode* existing_node =
    368       bookmark_model_->AddURL(bookmark_model_->other_node(),
    369                               0,
    370                               base::ASCIIToUTF16("Title"),
    371                               GURL(GURL(BOOKMARK_URL)));
    372   EXPECT_TRUE(model_->GetRemoteId(existing_node).empty());
    373 
    374   // Folder nodes should not have a remote id set on creation.
    375   const BookmarkNode* folder_node = AddFolder();
    376   EXPECT_TRUE(model_->GetRemoteId(folder_node).empty());
    377 }
    378 
    379 TEST_F(EnhancedBookmarkModelTest, TestEmptyDescription) {
    380   const BookmarkNode* node = AddBookmark();
    381 
    382   std::string description(model_->GetDescription(node));
    383   EXPECT_EQ(description, "");
    384 }
    385 
    386 TEST_F(EnhancedBookmarkModelTest, TestDescription) {
    387   const BookmarkNode* node = AddBookmark();
    388   const std::string description("This is the most useful description of all.");
    389 
    390   // Set the description.
    391   model_->SetDescription(node, description);
    392 
    393   // Check the description is the one that was set.
    394   EXPECT_EQ(model_->GetDescription(node), description);
    395   EXPECT_EQ("v1.0", GetVersion(node));
    396 }
    397 
    398 // If there is no notes field, the description should fall back on the snippet.
    399 TEST_F(EnhancedBookmarkModelTest, TestDescriptionFallback) {
    400   const BookmarkNode* node = AddBookmark();
    401 
    402   // Binary serialize the protobuf.
    403   image::collections::PageData data;
    404   data.set_snippet("Joe Bar Team");
    405   ASSERT_TRUE(data.IsInitialized());
    406   std::string output;
    407   bool result = data.SerializeToString(&output);
    408   ASSERT_TRUE(result);
    409 
    410   // base64 encode the output.
    411   std::string encoded;
    412   base::Base64Encode(output, &encoded);
    413   bookmark_model_->SetNodeMetaInfo(node, "stars.pageData", encoded);
    414 
    415   // The snippet is used as the description.
    416   std::string snippet(model_->GetSnippet(node));
    417   EXPECT_EQ("Joe Bar Team", model_->GetDescription(node));
    418 
    419   // Set the description.
    420   const std::string description("This is the most useful description of all.");
    421   model_->SetDescription(node, description);
    422 
    423   // Check the description is the one that was set.
    424   EXPECT_EQ(model_->GetDescription(node), description);
    425 }
    426 
    427 // Makes sure that the stars.version field is set every time
    428 // EnhancedBookmarkModel makes a change to a node.
    429 TEST_F(EnhancedBookmarkModelTest, TestVersionField) {
    430   const BookmarkNode* node = AddBookmark();
    431   EXPECT_EQ("", GetVersion(node));
    432 
    433   model_->SetDescription(node, "foo");
    434   EXPECT_EQ("v1.0", GetVersion(node));
    435 
    436   // Add a suffix to the version to set.
    437   model_->SetVersionSuffix("alpha");
    438 
    439   model_->SetDescription(node, "foo");
    440   // Since the description didn't actually change, the version field should
    441   // not either.
    442   EXPECT_EQ("v1.0", GetVersion(node));
    443 
    444   model_->SetDescription(node, "bar");
    445   EXPECT_EQ("v1.0/alpha", GetVersion(node));
    446 }
    447 
    448 // Verifies that duplicate nodes are reset when the model is created.
    449 TEST_F(EnhancedBookmarkModelTest, ResetDuplicateNodesOnInitialization) {
    450   model_->Shutdown();
    451 
    452   const BookmarkNode* parent = bookmark_model_->other_node();
    453   const BookmarkNode* node1 = bookmark_model_->AddURL(
    454       parent, 0, base::ASCIIToUTF16("Some title"), GURL(BOOKMARK_URL));
    455   const BookmarkNode* node2 = bookmark_model_->AddURL(
    456       parent, 0, base::ASCIIToUTF16("Some title"), GURL(BOOKMARK_URL));
    457   const BookmarkNode* node3 = bookmark_model_->AddURL(
    458       parent, 0, base::ASCIIToUTF16("Some title"), GURL(BOOKMARK_URL));
    459   const BookmarkNode* node4 = bookmark_model_->AddURL(
    460       parent, 0, base::ASCIIToUTF16("Some title"), GURL(BOOKMARK_URL));
    461 
    462   bookmark_model_->SetNodeMetaInfo(node1, "stars.id", "c_1");
    463   bookmark_model_->SetNodeMetaInfo(node2, "stars.id", "c_2");
    464   bookmark_model_->SetNodeMetaInfo(node3, "stars.id", "c_1");
    465   bookmark_model_->SetNodeMetaInfo(node4, "stars.id", "c_1");
    466   EXPECT_EQ("c_1", GetId(node1));
    467   EXPECT_EQ("c_2", GetId(node2));
    468   EXPECT_EQ("c_1", GetId(node3));
    469   EXPECT_EQ("c_1", GetId(node4));
    470 
    471   model_.reset(new EnhancedBookmarkModel(bookmark_model_.get(), "v2.0"));
    472   base::RunLoop().RunUntilIdle();
    473   EXPECT_EQ("c_2", GetId(node2));
    474   EXPECT_EQ("", GetId(node1));
    475   EXPECT_EQ("", GetId(node3));
    476   EXPECT_EQ("", GetId(node4));
    477   EXPECT_EQ("c_1", GetOldId(node1));
    478   EXPECT_EQ("c_1", GetOldId(node3));
    479   EXPECT_EQ("c_1", GetOldId(node4));
    480   EXPECT_EQ("v2.0", GetVersion(node1));
    481   EXPECT_EQ("v2.0", GetVersion(node3));
    482   EXPECT_EQ("v2.0", GetVersion(node4));
    483 }
    484 
    485 // Verifies that duplicate nodes are reset if one is created.
    486 TEST_F(EnhancedBookmarkModelTest, ResetDuplicateAddedNodes) {
    487   BookmarkNode::MetaInfoMap meta_info;
    488   meta_info["stars.id"] = "c_1";
    489   const BookmarkNode* parent = bookmark_model_->other_node();
    490 
    491   const BookmarkNode* node1 =
    492       bookmark_model_->AddURLWithCreationTimeAndMetaInfo(
    493           parent,
    494           0,
    495           base::ASCIIToUTF16("Some title"),
    496           GURL(BOOKMARK_URL),
    497           base::Time::Now(),
    498           &meta_info);
    499   EXPECT_EQ("c_1", GetId(node1));
    500 
    501   const BookmarkNode* node2 =
    502       bookmark_model_->AddURLWithCreationTimeAndMetaInfo(
    503           parent,
    504           0,
    505           base::ASCIIToUTF16("Some title"),
    506           GURL(BOOKMARK_URL),
    507           base::Time::Now(),
    508           &meta_info);
    509 
    510   base::RunLoop().RunUntilIdle();
    511   EXPECT_EQ("", GetId(node1));
    512   EXPECT_EQ("", GetId(node2));
    513   EXPECT_EQ("c_1", GetOldId(node1));
    514   EXPECT_EQ("c_1", GetOldId(node2));
    515   EXPECT_EQ("v1.0", GetVersion(node1));
    516   EXPECT_EQ("v1.0", GetVersion(node2));
    517 }
    518 
    519 // Verifies that duplicate nodes are reset if an id is changed to a duplicate
    520 // value.
    521 TEST_F(EnhancedBookmarkModelTest, ResetDuplicateChangedNodes) {
    522   const BookmarkNode* node1 = AddBookmark();
    523   const BookmarkNode* node2 = AddBookmark();
    524 
    525   bookmark_model_->SetNodeMetaInfo(node1, "stars.id", "c_1");
    526   EXPECT_EQ("c_1", GetId(node1));
    527 
    528   bookmark_model_->SetNodeMetaInfo(node2, "stars.id", "c_1");
    529   base::RunLoop().RunUntilIdle();
    530   EXPECT_EQ("", GetId(node1));
    531   EXPECT_EQ("", GetId(node2));
    532   EXPECT_EQ("c_1", GetOldId(node1));
    533   EXPECT_EQ("c_1", GetOldId(node2));
    534   EXPECT_EQ("v1.0", GetVersion(node1));
    535   EXPECT_EQ("v1.0", GetVersion(node2));
    536 }
    537 
    538 TEST_F(EnhancedBookmarkModelTest, SetMultipleMetaInfo) {
    539   const BookmarkNode* node = AddBookmark();
    540   BookmarkNode::MetaInfoMap meta_info;
    541   meta_info["a"] = "aa";
    542   meta_info["b"] = "bb";
    543 
    544   model_->SetVersionSuffix("1");
    545   model_->SetMultipleMetaInfo(node, meta_info);
    546   EXPECT_EQ("aa", GetMetaInfoField(node, "a"));
    547   EXPECT_EQ("bb", GetMetaInfoField(node, "b"));
    548   EXPECT_EQ("v1.0/1", GetVersion(node));
    549 
    550   // Not present fields does not erase the fields already set on the node.
    551   meta_info["a"] = "aaa";
    552   model_->SetVersionSuffix("2");
    553   model_->SetMultipleMetaInfo(node, meta_info);
    554   EXPECT_EQ("aaa", GetMetaInfoField(node, "a"));
    555   EXPECT_EQ("bb", GetMetaInfoField(node, "b"));
    556   EXPECT_EQ("v1.0/2", GetVersion(node));
    557 
    558   // Not actually changing any values should not set the version field.
    559   model_->SetVersionSuffix("3");
    560   model_->SetMultipleMetaInfo(node, meta_info);
    561   EXPECT_EQ("v1.0/2", GetVersion(node));
    562 }
    563 
    564 TEST_F(EnhancedBookmarkModelTest, ObserverShuttingDownEvent) {
    565   EXPECT_EQ(0, shutting_down_calls_);
    566   model_->Shutdown();
    567   EXPECT_EQ(1, shutting_down_calls_);
    568   model_.reset();
    569 }
    570 
    571 TEST_F(EnhancedBookmarkModelTest, ObserverNodeAddedEvent) {
    572   EXPECT_EQ(0, added_calls_);
    573   const BookmarkNode* node = AddBookmark();
    574   EXPECT_EQ(1, added_calls_);
    575   EXPECT_EQ(node, last_added_);
    576 
    577   const BookmarkNode* folder = AddFolder();
    578   EXPECT_EQ(2, added_calls_);
    579   EXPECT_EQ(folder, last_added_);
    580 }
    581 
    582 TEST_F(EnhancedBookmarkModelTest, ObserverNodeRemovedEvent) {
    583   const BookmarkNode* node = AddBookmark();
    584   const BookmarkNode* folder = AddFolder();
    585 
    586   EXPECT_EQ(0, removed_calls_);
    587   bookmark_model_->Remove(node->parent(), node->parent()->GetIndexOf(node));
    588   EXPECT_EQ(1, removed_calls_);
    589   EXPECT_EQ(node, last_removed_);
    590 
    591   bookmark_model_->Remove(folder->parent(),
    592                           folder->parent()->GetIndexOf(folder));
    593   EXPECT_EQ(2, removed_calls_);
    594   EXPECT_EQ(folder, last_removed_);
    595 }
    596 
    597 TEST_F(EnhancedBookmarkModelTest, ObserverAllUserNodesRemovedEvent) {
    598   AddBookmark();
    599   AddFolder();
    600   EXPECT_EQ(0, all_user_nodes_removed_calls_);
    601   bookmark_model_->RemoveAllUserBookmarks();
    602   EXPECT_EQ(0, removed_calls_);
    603   EXPECT_EQ(1, all_user_nodes_removed_calls_);
    604 }
    605 
    606 TEST_F(EnhancedBookmarkModelTest, ObserverRemoteIdChangedEvent) {
    607   const BookmarkNode* node1 = AddFolder();
    608   const BookmarkNode* node2 = AddFolder();
    609 
    610   EXPECT_EQ(0, remote_id_changed_calls_);
    611   bookmark_model_->SetNodeMetaInfo(node1, "stars.id", "c_1");
    612   base::RunLoop().RunUntilIdle();
    613   EXPECT_EQ(1, remote_id_changed_calls_);
    614   EXPECT_EQ(node1, last_remote_id_node_);
    615   EXPECT_EQ("", last_old_remote_id_);
    616   EXPECT_EQ("c_1", last_remote_id_);
    617 
    618   bookmark_model_->SetNodeMetaInfo(node2, "stars.id", "c_2");
    619   base::RunLoop().RunUntilIdle();
    620   EXPECT_EQ(2, remote_id_changed_calls_);
    621   EXPECT_EQ(node2, last_remote_id_node_);
    622   EXPECT_EQ("", last_old_remote_id_);
    623   EXPECT_EQ("c_2", last_remote_id_);
    624 
    625   bookmark_model_->SetNodeMetaInfo(node1, "stars.id", "c_3");
    626   base::RunLoop().RunUntilIdle();
    627   EXPECT_EQ(3, remote_id_changed_calls_);
    628   EXPECT_EQ(node1, last_remote_id_node_);
    629   EXPECT_EQ("c_1", last_old_remote_id_);
    630   EXPECT_EQ("c_3", last_remote_id_);
    631 
    632   // Set to duplicate ids.
    633   bookmark_model_->SetNodeMetaInfo(node2, "stars.id", "c_3");
    634   EXPECT_EQ(4, remote_id_changed_calls_);
    635   EXPECT_EQ(node2, last_remote_id_node_);
    636   EXPECT_EQ("c_2", last_old_remote_id_);
    637   EXPECT_EQ("c_3", last_remote_id_);
    638   base::RunLoop().RunUntilIdle();
    639   EXPECT_EQ(6, remote_id_changed_calls_);
    640   EXPECT_EQ("", last_remote_id_);
    641 }
    642 
    643 TEST_F(EnhancedBookmarkModelTest, ShutDownWhileResetDuplicationScheduled) {
    644   const BookmarkNode* node1 = AddBookmark();
    645   const BookmarkNode* node2 = AddBookmark();
    646   bookmark_model_->SetNodeMetaInfo(node1, "stars.id", "c_1");
    647   bookmark_model_->SetNodeMetaInfo(node2, "stars.id", "c_1");
    648   model_->Shutdown();
    649   model_.reset();
    650   base::RunLoop().RunUntilIdle();
    651 }
    652