blob: d7502361fc8a4256c0c69a165ac56b36e6c077f1 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/bookmarks/browser/bookmark_utils.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_task_environment.h"
#include "build/build_config.h"
#include "components/bookmarks/browser/base_bookmark_model_observer.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/browser/bookmark_node_data.h"
#include "components/bookmarks/test/test_bookmark_client.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
using base::ASCIIToUTF16;
using std::string;
namespace bookmarks {
namespace {
class BookmarkUtilsTest : public testing::Test,
public BaseBookmarkModelObserver {
public:
BookmarkUtilsTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::UI),
grouped_changes_beginning_count_(0),
grouped_changes_ended_count_(0) {}
~BookmarkUtilsTest() override {}
// Copy and paste is not yet supported on iOS. http://crbug.com/228147
#if !defined(OS_IOS)
void TearDown() override {
ui::Clipboard::DestroyClipboardForCurrentThread();
}
#endif // !defined(OS_IOS)
// Certain user actions require multiple changes to the bookmark model,
// however these modifications need to be atomic for the undo framework. The
// BaseBookmarkModelObserver is used to inform the boundaries of the user
// action. For example, when multiple bookmarks are cut to the clipboard we
// expect one call each to GroupedBookmarkChangesBeginning/Ended.
void ExpectGroupedChangeCount(int expected_beginning_count,
int expected_ended_count) {
// The undo framework is not used under Android. Thus the group change
// events will not be fired and so should not be tested for Android.
#if !defined(OS_ANDROID)
EXPECT_EQ(grouped_changes_beginning_count_, expected_beginning_count);
EXPECT_EQ(grouped_changes_ended_count_, expected_ended_count);
#endif
}
private:
// BaseBookmarkModelObserver:
void BookmarkModelChanged() override {}
void GroupedBookmarkChangesBeginning(BookmarkModel* model) override {
++grouped_changes_beginning_count_;
}
void GroupedBookmarkChangesEnded(BookmarkModel* model) override {
++grouped_changes_ended_count_;
}
// Clipboard requires a message loop.
base::test::ScopedTaskEnvironment scoped_task_environment_;
int grouped_changes_beginning_count_;
int grouped_changes_ended_count_;
DISALLOW_COPY_AND_ASSIGN(BookmarkUtilsTest);
};
TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesWordPhraseQuery) {
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
const BookmarkNode* node1 = model->AddURL(model->other_node(),
0,
ASCIIToUTF16("foo bar"),
GURL("http://www.google.com"));
const BookmarkNode* node2 = model->AddURL(model->other_node(),
0,
ASCIIToUTF16("baz buz"),
GURL("http://www.cnn.com"));
const BookmarkNode* folder1 =
model->AddFolder(model->other_node(), 0, ASCIIToUTF16("foo"));
std::vector<const BookmarkNode*> nodes;
QueryFields query;
query.word_phrase_query.reset(new base::string16);
// No nodes are returned for empty string.
*query.word_phrase_query = ASCIIToUTF16("");
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
EXPECT_TRUE(nodes.empty());
nodes.clear();
// No nodes are returned for space-only string.
*query.word_phrase_query = ASCIIToUTF16(" ");
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
EXPECT_TRUE(nodes.empty());
nodes.clear();
// Node "foo bar" and folder "foo" are returned in search results.
*query.word_phrase_query = ASCIIToUTF16("foo");
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
ASSERT_EQ(2U, nodes.size());
EXPECT_TRUE(nodes[0] == folder1);
EXPECT_TRUE(nodes[1] == node1);
nodes.clear();
// Ensure url matches return in search results.
*query.word_phrase_query = ASCIIToUTF16("cnn");
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
ASSERT_EQ(1U, nodes.size());
EXPECT_TRUE(nodes[0] == node2);
nodes.clear();
// Ensure folder "foo" is not returned in more specific search.
*query.word_phrase_query = ASCIIToUTF16("foo bar");
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
ASSERT_EQ(1U, nodes.size());
EXPECT_TRUE(nodes[0] == node1);
nodes.clear();
// Bookmark Bar and Other Bookmarks are not returned in search results.
*query.word_phrase_query = ASCIIToUTF16("Bookmark");
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
ASSERT_EQ(0U, nodes.size());
nodes.clear();
}
// Check exact matching against a URL query.
TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesUrl) {
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
const BookmarkNode* node1 = model->AddURL(model->other_node(),
0,
ASCIIToUTF16("Google"),
GURL("https://www.google.com/"));
model->AddURL(model->other_node(),
0,
ASCIIToUTF16("Google Calendar"),
GURL("https://www.google.com/calendar"));
model->AddFolder(model->other_node(), 0, ASCIIToUTF16("Folder"));
std::vector<const BookmarkNode*> nodes;
QueryFields query;
query.url.reset(new base::string16);
*query.url = ASCIIToUTF16("https://www.google.com/");
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
ASSERT_EQ(1U, nodes.size());
EXPECT_TRUE(nodes[0] == node1);
nodes.clear();
*query.url = ASCIIToUTF16("calendar");
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
ASSERT_EQ(0U, nodes.size());
nodes.clear();
// Empty URL should not match folders.
*query.url = ASCIIToUTF16("");
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
ASSERT_EQ(0U, nodes.size());
nodes.clear();
}
// Check exact matching against a title query.
TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesTitle) {
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
const BookmarkNode* node1 = model->AddURL(model->other_node(),
0,
ASCIIToUTF16("Google"),
GURL("https://www.google.com/"));
model->AddURL(model->other_node(),
0,
ASCIIToUTF16("Google Calendar"),
GURL("https://www.google.com/calendar"));
const BookmarkNode* folder1 =
model->AddFolder(model->other_node(), 0, ASCIIToUTF16("Folder"));
std::vector<const BookmarkNode*> nodes;
QueryFields query;
query.title.reset(new base::string16);
*query.title = ASCIIToUTF16("Google");
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
ASSERT_EQ(1U, nodes.size());
EXPECT_TRUE(nodes[0] == node1);
nodes.clear();
*query.title = ASCIIToUTF16("Calendar");
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
ASSERT_EQ(0U, nodes.size());
nodes.clear();
// Title should match folders.
*query.title = ASCIIToUTF16("Folder");
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
ASSERT_EQ(1U, nodes.size());
EXPECT_TRUE(nodes[0] == folder1);
nodes.clear();
}
// Check matching against a query with multiple predicates.
TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesConjunction) {
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
const BookmarkNode* node1 = model->AddURL(model->other_node(),
0,
ASCIIToUTF16("Google"),
GURL("https://www.google.com/"));
model->AddURL(model->other_node(),
0,
ASCIIToUTF16("Google Calendar"),
GURL("https://www.google.com/calendar"));
model->AddFolder(model->other_node(), 0, ASCIIToUTF16("Folder"));
std::vector<const BookmarkNode*> nodes;
QueryFields query;
// Test all fields matching.
query.word_phrase_query.reset(new base::string16(ASCIIToUTF16("www")));
query.url.reset(new base::string16(ASCIIToUTF16("https://www.google.com/")));
query.title.reset(new base::string16(ASCIIToUTF16("Google")));
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
ASSERT_EQ(1U, nodes.size());
EXPECT_TRUE(nodes[0] == node1);
nodes.clear();
std::unique_ptr<base::string16>* fields[] = {&query.word_phrase_query,
&query.url, &query.title};
// Test two fields matching.
for (size_t i = 0; i < base::size(fields); i++) {
std::unique_ptr<base::string16> original_value(fields[i]->release());
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
ASSERT_EQ(1U, nodes.size());
EXPECT_TRUE(nodes[0] == node1);
nodes.clear();
*fields[i] = std::move(original_value);
}
// Test two fields matching with one non-matching field.
for (size_t i = 0; i < base::size(fields); i++) {
std::unique_ptr<base::string16> original_value(fields[i]->release());
fields[i]->reset(new base::string16(ASCIIToUTF16("fjdkslafjkldsa")));
GetBookmarksMatchingProperties(model.get(), query, 100, &nodes);
ASSERT_EQ(0U, nodes.size());
nodes.clear();
*fields[i] = std::move(original_value);
}
}
// Copy and paste is not yet supported on iOS. http://crbug.com/228147
#if !defined(OS_IOS)
TEST_F(BookmarkUtilsTest, PasteBookmarkFromURL) {
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
const base::string16 url_text = ASCIIToUTF16("http://www.google.com/");
const BookmarkNode* new_folder = model->AddFolder(
model->bookmark_bar_node(), 0, ASCIIToUTF16("New_Folder"));
// Write blank text to clipboard.
{
ui::ScopedClipboardWriter clipboard_writer(ui::CLIPBOARD_TYPE_COPY_PASTE);
clipboard_writer.WriteText(base::string16());
}
// Now we shouldn't be able to paste from the clipboard.
EXPECT_FALSE(CanPasteFromClipboard(model.get(), new_folder));
// Write some valid url to the clipboard.
{
ui::ScopedClipboardWriter clipboard_writer(ui::CLIPBOARD_TYPE_COPY_PASTE);
clipboard_writer.WriteText(url_text);
}
// Now we should be able to paste from the clipboard.
EXPECT_TRUE(CanPasteFromClipboard(model.get(), new_folder));
PasteFromClipboard(model.get(), new_folder, 0);
ASSERT_EQ(1, new_folder->child_count());
// Url for added node should be same as url_text.
EXPECT_EQ(url_text, ASCIIToUTF16(new_folder->GetChild(0)->url().spec()));
}
TEST_F(BookmarkUtilsTest, CopyPaste) {
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
const BookmarkNode* node = model->AddURL(model->other_node(),
0,
ASCIIToUTF16("foo bar"),
GURL("http://www.google.com"));
// Copy a node to the clipboard.
std::vector<const BookmarkNode*> nodes;
nodes.push_back(node);
CopyToClipboard(model.get(), nodes, false);
// And make sure we can paste a bookmark from the clipboard.
EXPECT_TRUE(CanPasteFromClipboard(model.get(), model->bookmark_bar_node()));
// Write some text to the clipboard.
{
ui::ScopedClipboardWriter clipboard_writer(
ui::CLIPBOARD_TYPE_COPY_PASTE);
clipboard_writer.WriteText(ASCIIToUTF16("foo"));
}
// Now we shouldn't be able to paste from the clipboard.
EXPECT_FALSE(CanPasteFromClipboard(model.get(), model->bookmark_bar_node()));
}
// Test for updating title such that url and title pair are unique among the
// children of parent.
TEST_F(BookmarkUtilsTest, MakeTitleUnique) {
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
const base::string16 url_text = ASCIIToUTF16("http://www.google.com/");
const base::string16 title_text = ASCIIToUTF16("foobar");
const BookmarkNode* bookmark_bar_node = model->bookmark_bar_node();
const BookmarkNode* node =
model->AddURL(bookmark_bar_node, 0, title_text, GURL(url_text));
EXPECT_EQ(url_text,
ASCIIToUTF16(bookmark_bar_node->GetChild(0)->url().spec()));
EXPECT_EQ(title_text, bookmark_bar_node->GetChild(0)->GetTitle());
// Copy a node to the clipboard.
std::vector<const BookmarkNode*> nodes;
nodes.push_back(node);
CopyToClipboard(model.get(), nodes, false);
// Now we should be able to paste from the clipboard.
EXPECT_TRUE(CanPasteFromClipboard(model.get(), bookmark_bar_node));
PasteFromClipboard(model.get(), bookmark_bar_node, 1);
ASSERT_EQ(2, bookmark_bar_node->child_count());
// Url for added node should be same as url_text.
EXPECT_EQ(url_text,
ASCIIToUTF16(bookmark_bar_node->GetChild(1)->url().spec()));
// Title for added node should be numeric subscript suffix with copied node
// title.
EXPECT_EQ(ASCIIToUTF16("foobar (1)"),
bookmark_bar_node->GetChild(1)->GetTitle());
}
TEST_F(BookmarkUtilsTest, CopyPasteMetaInfo) {
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
const BookmarkNode* node = model->AddURL(model->other_node(),
0,
ASCIIToUTF16("foo bar"),
GURL("http://www.google.com"));
model->SetNodeMetaInfo(node, "somekey", "somevalue");
model->SetNodeMetaInfo(node, "someotherkey", "someothervalue");
// Copy a node to the clipboard.
std::vector<const BookmarkNode*> nodes;
nodes.push_back(node);
CopyToClipboard(model.get(), nodes, false);
// Paste node to a different folder.
const BookmarkNode* folder =
model->AddFolder(model->bookmark_bar_node(), 0, ASCIIToUTF16("Folder"));
EXPECT_EQ(0, folder->child_count());
// And make sure we can paste a bookmark from the clipboard.
EXPECT_TRUE(CanPasteFromClipboard(model.get(), folder));
PasteFromClipboard(model.get(), folder, 0);
ASSERT_EQ(1, folder->child_count());
// Verify that the pasted node contains the same meta info.
const BookmarkNode* pasted = folder->GetChild(0);
ASSERT_TRUE(pasted->GetMetaInfoMap());
EXPECT_EQ(2u, pasted->GetMetaInfoMap()->size());
std::string value;
EXPECT_TRUE(pasted->GetMetaInfo("somekey", &value));
EXPECT_EQ("somevalue", value);
EXPECT_TRUE(pasted->GetMetaInfo("someotherkey", &value));
EXPECT_EQ("someothervalue", value);
}
#if defined(OS_LINUX) || defined(OS_MACOSX)
// http://crbug.com/396472
#define MAYBE_CutToClipboard DISABLED_CutToClipboard
#else
#define MAYBE_CutToClipboard CutToClipboard
#endif
TEST_F(BookmarkUtilsTest, MAYBE_CutToClipboard) {
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
model->AddObserver(this);
base::string16 title(ASCIIToUTF16("foo"));
GURL url("http://foo.com");
const BookmarkNode* n1 = model->AddURL(model->other_node(), 0, title, url);
const BookmarkNode* n2 = model->AddURL(model->other_node(), 1, title, url);
// Cut the nodes to the clipboard.
std::vector<const BookmarkNode*> nodes;
nodes.push_back(n1);
nodes.push_back(n2);
CopyToClipboard(model.get(), nodes, true);
// Make sure the nodes were removed.
EXPECT_EQ(0, model->other_node()->child_count());
// Make sure observers were notified the set of changes should be grouped.
ExpectGroupedChangeCount(1, 1);
// And make sure we can paste from the clipboard.
EXPECT_TRUE(CanPasteFromClipboard(model.get(), model->other_node()));
}
TEST_F(BookmarkUtilsTest, PasteNonEditableNodes) {
// Load a model with an extra node that is not editable.
std::unique_ptr<TestBookmarkClient> client(new TestBookmarkClient());
BookmarkPermanentNodeList extra_nodes;
extra_nodes.push_back(std::make_unique<BookmarkPermanentNode>(100));
BookmarkPermanentNode* extra_node = extra_nodes.back().get();
client->SetExtraNodesToLoad(std::move(extra_nodes));
std::unique_ptr<BookmarkModel> model(
TestBookmarkClient::CreateModelWithClient(std::move(client)));
const BookmarkNode* node = model->AddURL(model->other_node(),
0,
ASCIIToUTF16("foo bar"),
GURL("http://www.google.com"));
// Copy a node to the clipboard.
std::vector<const BookmarkNode*> nodes;
nodes.push_back(node);
CopyToClipboard(model.get(), nodes, false);
// And make sure we can paste a bookmark from the clipboard.
EXPECT_TRUE(CanPasteFromClipboard(model.get(), model->bookmark_bar_node()));
// But it can't be pasted into a non-editable folder.
BookmarkClient* upcast = model->client();
EXPECT_FALSE(upcast->CanBeEditedByUser(extra_node));
EXPECT_FALSE(CanPasteFromClipboard(model.get(), extra_node));
}
#endif // !defined(OS_IOS)
TEST_F(BookmarkUtilsTest, GetParentForNewNodes) {
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
// This tests the case where selection contains one item and that item is a
// folder.
std::vector<const BookmarkNode*> nodes;
nodes.push_back(model->bookmark_bar_node());
int index = -1;
const BookmarkNode* real_parent =
GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
EXPECT_EQ(real_parent, model->bookmark_bar_node());
EXPECT_EQ(0, index);
nodes.clear();
// This tests the case where selection contains one item and that item is an
// url.
const BookmarkNode* page1 = model->AddURL(model->bookmark_bar_node(),
0,
ASCIIToUTF16("Google"),
GURL("http://google.com"));
nodes.push_back(page1);
real_parent = GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
EXPECT_EQ(real_parent, model->bookmark_bar_node());
EXPECT_EQ(1, index);
// This tests the case where selection has more than one item.
const BookmarkNode* folder1 =
model->AddFolder(model->bookmark_bar_node(), 1, ASCIIToUTF16("Folder 1"));
nodes.push_back(folder1);
real_parent = GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
EXPECT_EQ(real_parent, model->bookmark_bar_node());
EXPECT_EQ(2, index);
// This tests the case where selection doesn't contain any items.
nodes.clear();
real_parent = GetParentForNewNodes(model->bookmark_bar_node(), nodes, &index);
EXPECT_EQ(real_parent, model->bookmark_bar_node());
EXPECT_EQ(2, index);
}
// Verifies that meta info is copied when nodes are cloned.
TEST_F(BookmarkUtilsTest, CloneMetaInfo) {
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
// Add a node containing meta info.
const BookmarkNode* node = model->AddURL(model->other_node(),
0,
ASCIIToUTF16("foo bar"),
GURL("http://www.google.com"));
model->SetNodeMetaInfo(node, "somekey", "somevalue");
model->SetNodeMetaInfo(node, "someotherkey", "someothervalue");
// Clone node to a different folder.
const BookmarkNode* folder =
model->AddFolder(model->bookmark_bar_node(), 0, ASCIIToUTF16("Folder"));
std::vector<BookmarkNodeData::Element> elements;
BookmarkNodeData::Element node_data(node);
elements.push_back(node_data);
EXPECT_EQ(0, folder->child_count());
CloneBookmarkNode(model.get(), elements, folder, 0, false);
ASSERT_EQ(1, folder->child_count());
// Verify that the cloned node contains the same meta info.
const BookmarkNode* clone = folder->GetChild(0);
ASSERT_TRUE(clone->GetMetaInfoMap());
EXPECT_EQ(2u, clone->GetMetaInfoMap()->size());
std::string value;
EXPECT_TRUE(clone->GetMetaInfo("somekey", &value));
EXPECT_EQ("somevalue", value);
EXPECT_TRUE(clone->GetMetaInfo("someotherkey", &value));
EXPECT_EQ("someothervalue", value);
}
// Verifies that meta info fields in the non cloned set are not copied when
// cloning a bookmark.
TEST_F(BookmarkUtilsTest, CloneBookmarkResetsNonClonedKey) {
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
model->AddNonClonedKey("foo");
const BookmarkNode* parent = model->other_node();
const BookmarkNode* node = model->AddURL(
parent, 0, ASCIIToUTF16("title"), GURL("http://www.google.com"));
model->SetNodeMetaInfo(node, "foo", "ignored value");
model->SetNodeMetaInfo(node, "bar", "kept value");
std::vector<BookmarkNodeData::Element> elements;
BookmarkNodeData::Element node_data(node);
elements.push_back(node_data);
// Cloning a bookmark should clear the non cloned key.
CloneBookmarkNode(model.get(), elements, parent, 0, true);
ASSERT_EQ(2, parent->child_count());
std::string value;
EXPECT_FALSE(parent->GetChild(0)->GetMetaInfo("foo", &value));
// Other keys should still be cloned.
EXPECT_TRUE(parent->GetChild(0)->GetMetaInfo("bar", &value));
EXPECT_EQ("kept value", value);
}
// Verifies that meta info fields in the non cloned set are not copied when
// cloning a folder.
TEST_F(BookmarkUtilsTest, CloneFolderResetsNonClonedKey) {
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
model->AddNonClonedKey("foo");
const BookmarkNode* parent = model->other_node();
const BookmarkNode* node = model->AddFolder(parent, 0, ASCIIToUTF16("title"));
model->SetNodeMetaInfo(node, "foo", "ignored value");
model->SetNodeMetaInfo(node, "bar", "kept value");
std::vector<BookmarkNodeData::Element> elements;
BookmarkNodeData::Element node_data(node);
elements.push_back(node_data);
// Cloning a folder should clear the non cloned key.
CloneBookmarkNode(model.get(), elements, parent, 0, true);
ASSERT_EQ(2, parent->child_count());
std::string value;
EXPECT_FALSE(parent->GetChild(0)->GetMetaInfo("foo", &value));
// Other keys should still be cloned.
EXPECT_TRUE(parent->GetChild(0)->GetMetaInfo("bar", &value));
EXPECT_EQ("kept value", value);
}
TEST_F(BookmarkUtilsTest, RemoveAllBookmarks) {
// Load a model with an extra node that is not editable.
std::unique_ptr<TestBookmarkClient> client(new TestBookmarkClient());
BookmarkPermanentNodeList extra_nodes;
extra_nodes.push_back(std::make_unique<BookmarkPermanentNode>(100));
BookmarkPermanentNode* extra_node = extra_nodes.back().get();
client->SetExtraNodesToLoad(std::move(extra_nodes));
std::unique_ptr<BookmarkModel> model(
TestBookmarkClient::CreateModelWithClient(std::move(client)));
EXPECT_TRUE(model->bookmark_bar_node()->children().empty());
EXPECT_TRUE(model->other_node()->children().empty());
EXPECT_TRUE(model->mobile_node()->children().empty());
EXPECT_TRUE(extra_node->children().empty());
const base::string16 title = base::ASCIIToUTF16("Title");
const GURL url("http://google.com");
model->AddURL(model->bookmark_bar_node(), 0, title, url);
model->AddURL(model->other_node(), 0, title, url);
model->AddURL(model->mobile_node(), 0, title, url);
model->AddURL(extra_node, 0, title, url);
std::vector<const BookmarkNode*> nodes;
model->GetNodesByURL(url, &nodes);
ASSERT_EQ(4u, nodes.size());
RemoveAllBookmarks(model.get(), url);
nodes.clear();
model->GetNodesByURL(url, &nodes);
ASSERT_EQ(1u, nodes.size());
EXPECT_TRUE(model->bookmark_bar_node()->children().empty());
EXPECT_TRUE(model->other_node()->children().empty());
EXPECT_TRUE(model->mobile_node()->children().empty());
EXPECT_EQ(1, extra_node->child_count());
}
} // namespace
} // namespace bookmarks