blob: ff5c2914d2073d43dbb706a3906238c6da48ee7f [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/bookmarks/bookmark_merged_surface_service.h"
#include <memory>
#include "base/containers/to_vector.h"
#include "base/functional/bind.h"
#include "base/scoped_observation.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_ostream_operators.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "chrome/browser/bookmarks/bookmark_merged_surface_service_observer.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/bookmarks/bookmark_parent_folder_children.h"
#include "chrome/browser/bookmarks/bookmark_test_utils.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/browser/bookmark_node.h"
#include "components/bookmarks/managed/managed_bookmark_service.h"
#include "components/bookmarks/test/bookmark_test_helpers.h"
#include "components/bookmarks/test/test_bookmark_client.h"
#include "components/signin/public/base/signin_switches.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace {
using PermanentFolderType = BookmarkParentFolder::PermanentFolderType;
using bookmarks::BookmarkNode;
using bookmarks::test::AddNodesFromModelString;
using bookmarks::test::ModelStringFromNode;
using testing::_;
using testing::InSequence;
using testing::Mock;
using testing::Pair;
using testing::UnorderedElementsAre;
// Matcher for comparing `BookmarkParentFolderChildren` children to a vector of
// BookmarkNodes.
MATCHER_P(HasOrderedChildren, expected_children, "") {
if (arg.size() != expected_children.size()) {
*result_listener << "Expected " << expected_children.size() << " children, "
<< "but got " << arg.size();
return false;
}
for (size_t i = 0; i < arg.size(); ++i) {
const BookmarkNode* child = arg[i];
const BookmarkNode* expected_child = expected_children[i];
if (child != expected_child) {
*result_listener << "Child at index " << i << " does not match. Expected "
<< expected_child->GetTitledUrlNodeTitle()
<< ", but got " << child->GetTitledUrlNodeTitle();
return false;
}
}
return true;
}
MATCHER_P(HasParent, parent, "") {
const BookmarkNode* node = arg.as_non_permanent_folder();
if (!node) {
// `parent` can't be root.
return false;
}
CHECK(node->parent());
return BookmarkParentFolder::FromFolderNode(node->parent()) == parent;
}
class MockBookmarkMergedSurfaceServiceObserver
: public BookmarkMergedSurfaceServiceObserver {
public:
MOCK_METHOD(void, BookmarkMergedSurfaceServiceLoaded, ());
MOCK_METHOD(void, BookmarkMergedSurfaceServiceBeingDeleted, ());
MOCK_METHOD(void,
BookmarkNodeAdded,
(const BookmarkParentFolder& parent, size_t index));
MOCK_METHOD(void,
BookmarkNodesRemoved,
(const BookmarkParentFolder&,
(const base::flat_set<const BookmarkNode*>&)));
MOCK_METHOD(void,
BookmarkNodeMoved,
(const BookmarkParentFolder&,
size_t,
const BookmarkParentFolder&,
size_t));
MOCK_METHOD(void, BookmarkNodeChanged, (const bookmarks::BookmarkNode*));
MOCK_METHOD(void,
BookmarkNodeFaviconChanged,
(const bookmarks::BookmarkNode*));
MOCK_METHOD(void,
BookmarkParentFolderChildrenReordered,
(const BookmarkParentFolder&));
MOCK_METHOD(void, BookmarkAllUserNodesRemoved, ());
};
class BookmarkMergedSurfaceServiceTest : public testing::Test {
public:
void CreateBookmarkMergedSurfaceServiceWithManaged(
size_t managed_bookmarks_size) {
CreateBookmarkMergedSurfaceService(true, managed_bookmarks_size);
}
void CreateBookmarkMergedSurfaceService(bool with_managed_node = false,
size_t managed_bookmarks_size = 0) {
std::unique_ptr<bookmarks::TestBookmarkClient> bookmark_client;
if (with_managed_node) {
CHECK(managed_bookmarks_size);
managed_bookmark_service_ =
CreateManagedBookmarkService(&prefs_, managed_bookmarks_size);
bookmark_client = std::make_unique<TestBookmarkClientWithManagedService>(
managed_bookmark_service_.get());
} else {
bookmark_client = std::make_unique<bookmarks::TestBookmarkClient>();
}
model_ =
std::make_unique<bookmarks::BookmarkModel>(std::move(bookmark_client));
service_ = std::make_unique<BookmarkMergedSurfaceService>(
model_.get(), managed_bookmark_service_.get());
service_->AddObserver(&mock_service_observer_);
model_->LoadEmptyForTest();
service_->LoadForTesting({});
}
~BookmarkMergedSurfaceServiceTest() override {
service_->RemoveObserver(&mock_service_observer_);
}
BookmarkMergedSurfaceService& service() { return *service_; }
bookmarks::BookmarkModel& model() { return *model_; }
const BookmarkNode* managed_node() const {
return managed_bookmark_service_->managed_node();
}
void PerformMoveAction(Browser* browser,
const bookmarks::BookmarkNode* node,
const bookmarks::BookmarkNode* target_node,
size_t index) {
model_->Move(node, target_node, index);
}
MockBookmarkMergedSurfaceServiceObserver& mock_service_observer() {
return mock_service_observer_;
}
private:
base::test::ScopedFeatureList features_{
switches::kSyncEnableBookmarksInTransportMode};
sync_preferences::TestingPrefServiceSyncable prefs_;
std::unique_ptr<bookmarks::ManagedBookmarkService> managed_bookmark_service_;
std::unique_ptr<bookmarks::BookmarkModel> model_;
std::unique_ptr<BookmarkMergedSurfaceService> service_;
testing::NiceMock<MockBookmarkMergedSurfaceServiceObserver>
mock_service_observer_;
};
TEST_F(BookmarkMergedSurfaceServiceTest, IsNonDefaultOrderingTracked) {
const size_t kManagedBookmarksSize = 5;
CreateBookmarkMergedSurfaceServiceWithManaged(kManagedBookmarksSize);
EXPECT_FALSE(service().IsNonDefaultOrderingTracked(
BookmarkParentFolder::ManagedFolder()));
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
// Non permanent node.
BookmarkParentFolder non_permanent_folder(
BookmarkParentFolder::FromFolderNode(
model().bookmark_bar_node()->children()[3].get()));
EXPECT_FALSE(service().IsNonDefaultOrderingTracked(non_permanent_folder));
// No account nodes.
EXPECT_FALSE(service().IsNonDefaultOrderingTracked(
BookmarkParentFolder::BookmarkBarFolder()));
// Account nodes with empty child nodes.
model().CreateAccountPermanentFolders();
EXPECT_FALSE(service().IsNonDefaultOrderingTracked(
BookmarkParentFolder::BookmarkBarFolder()));
// Default order.
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"4 5 ");
EXPECT_FALSE(service().IsNonDefaultOrderingTracked(
BookmarkParentFolder::BookmarkBarFolder()));
// Custom order.
service().Move(model().bookmark_bar_node()->children()[0].get(),
BookmarkParentFolder::BookmarkBarFolder(), 0u,
/*browser=*/nullptr);
EXPECT_TRUE(service().IsNonDefaultOrderingTracked(
BookmarkParentFolder::BookmarkBarFolder()));
}
TEST_F(BookmarkMergedSurfaceServiceTest, GetChildrenCount) {
const size_t kManagedBookmarksSize = 5;
CreateBookmarkMergedSurfaceServiceWithManaged(kManagedBookmarksSize);
EXPECT_EQ(
service().GetChildrenCount(BookmarkParentFolder::BookmarkBarFolder()),
0u);
EXPECT_EQ(service().GetChildrenCount(BookmarkParentFolder::OtherFolder()),
0u);
EXPECT_EQ(service().GetChildrenCount(BookmarkParentFolder::MobileFolder()),
0u);
EXPECT_EQ(service().GetChildrenCount(BookmarkParentFolder::ManagedFolder()),
kManagedBookmarksSize);
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 f2:[ 6 ] ]");
EXPECT_EQ(
service().GetChildrenCount(BookmarkParentFolder::BookmarkBarFolder()),
4u);
const BookmarkNode* folder_f1 =
model().bookmark_bar_node()->children()[3].get();
EXPECT_EQ(service().GetChildrenCount(
BookmarkParentFolder::FromFolderNode(folder_f1)),
3u);
const BookmarkNode* folder_f2 = folder_f1->children()[2].get();
EXPECT_EQ(service().GetChildrenCount(
BookmarkParentFolder::FromFolderNode(folder_f2)),
1u);
AddNodesFromModelString(&model(), model().other_node(), "1 2 3 ");
EXPECT_EQ(service().GetChildrenCount(BookmarkParentFolder::OtherFolder()),
3u);
AddNodesFromModelString(&model(), model().mobile_node(), "4 5 6 ");
EXPECT_EQ(service().GetChildrenCount(BookmarkParentFolder::MobileFolder()),
3u);
}
TEST_F(BookmarkMergedSurfaceServiceTest, GetChildrenWithAccountNodes) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
const BookmarkNode* local_bb_node = model().bookmark_bar_node();
const BookmarkNode* account_bb_node = model().account_bookmark_bar_node();
ASSERT_TRUE(account_bb_node);
AddNodesFromModelString(&model(), local_bb_node, "1 2 3 f1:[ 4 5 f2:[ 6 ] ]");
AddNodesFromModelString(&model(), account_bb_node,
"7 8 9 f3:[ 10 11 f4:[ 12 ] ]");
ASSERT_FALSE(local_bb_node->children().empty());
ASSERT_FALSE(account_bb_node->children().empty());
BookmarkParentFolder bb_folder(BookmarkParentFolder::BookmarkBarFolder());
size_t expected_children_size =
local_bb_node->children().size() + account_bb_node->children().size();
EXPECT_EQ(service().GetChildrenCount(bb_folder), expected_children_size);
BookmarkParentFolderChildren children = service().GetChildren(bb_folder);
ASSERT_EQ(children.size(), expected_children_size);
size_t index = 0;
size_t account_bb_node_children_size = account_bb_node->children().size();
while (index < expected_children_size) {
const BookmarkNode* expected_bookmark_node =
index < account_bb_node_children_size
? account_bb_node->children()[index].get()
: local_bb_node->children()[index - account_bb_node_children_size]
.get();
EXPECT_EQ(children[index++], expected_bookmark_node);
}
// Tests `GetNodeAtIndex()`.
EXPECT_EQ(service().GetNodeAtIndex(bb_folder, 0u),
account_bb_node->children()[0].get());
EXPECT_EQ(
service().GetNodeAtIndex(bb_folder, account_bb_node_children_size - 1u),
account_bb_node->children().back().get());
EXPECT_EQ(service().GetNodeAtIndex(bb_folder, account_bb_node_children_size),
local_bb_node->children()[0].get());
EXPECT_EQ(service().GetNodeAtIndex(bb_folder, expected_children_size - 1u),
local_bb_node->children().back().get());
// Tests `GetIndexOf()`.
index = 0;
for (const auto& node : account_bb_node->children()) {
EXPECT_EQ(service().GetIndexOf(node.get()), index++);
}
for (const auto& node : local_bb_node->children()) {
EXPECT_EQ(service().GetIndexOf(node.get()), index++);
}
}
TEST_F(BookmarkMergedSurfaceServiceTest, ManagedNodeNull) {
CreateBookmarkMergedSurfaceService();
EXPECT_EQ(service().GetChildrenCount(BookmarkParentFolder::ManagedFolder()),
0u);
}
TEST_F(BookmarkMergedSurfaceServiceTest, GetIndexOf) {
CreateBookmarkMergedSurfaceServiceWithManaged(/*managed_bookmarks_size=*/3);
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
AddNodesFromModelString(&model(), model().other_node(), "6 7 8 ");
const BookmarkNode* f1 = model().bookmark_bar_node()->children()[3].get();
EXPECT_EQ(service().GetIndexOf(f1), 3u);
EXPECT_EQ(service().GetIndexOf(f1->children()[1].get()), 1u);
EXPECT_EQ(service().GetIndexOf(model().other_node()->children()[2].get()),
2u);
// Managed node.
EXPECT_EQ(service().GetIndexOf(managed_node()->children()[1].get()), 1u);
}
TEST_F(BookmarkMergedSurfaceServiceTest, GetNodeAtIndex) {
CreateBookmarkMergedSurfaceService();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
AddNodesFromModelString(&model(), model().other_node(), "6 7 8 ");
{
BookmarkParentFolder folder = BookmarkParentFolder::BookmarkBarFolder();
EXPECT_EQ(service().GetNodeAtIndex(folder, 0),
model().bookmark_bar_node()->children()[0].get());
EXPECT_EQ(service().GetNodeAtIndex(folder, 3),
model().bookmark_bar_node()->children()[3].get());
}
{
const BookmarkNode* f1 = model().bookmark_bar_node()->children()[3].get();
BookmarkParentFolder folder = BookmarkParentFolder::FromFolderNode(f1);
EXPECT_EQ(service().GetNodeAtIndex(folder, 0), f1->children()[0].get());
EXPECT_EQ(service().GetNodeAtIndex(folder, 1), f1->children()[1].get());
}
{
BookmarkParentFolder folder = BookmarkParentFolder::OtherFolder();
EXPECT_EQ(service().GetNodeAtIndex(folder, 0),
model().other_node()->children()[0].get());
EXPECT_EQ(service().GetNodeAtIndex(folder, 2),
model().other_node()->children()[2].get());
}
}
TEST_F(BookmarkMergedSurfaceServiceTest, IsParentFolderManaged) {
CreateBookmarkMergedSurfaceServiceWithManaged(2);
AddNodesFromModelString(&model(), model().bookmark_bar_node(), "f1:[ 4 5 ]");
EXPECT_FALSE(service().IsParentFolderManaged(
BookmarkParentFolder::BookmarkBarFolder()));
EXPECT_FALSE(
service().IsParentFolderManaged(BookmarkParentFolder::FromFolderNode(
model().bookmark_bar_node()->children()[0].get())));
EXPECT_TRUE(
service().IsParentFolderManaged(BookmarkParentFolder::ManagedFolder()));
const BookmarkNode* root_managed_node = managed_node();
EXPECT_TRUE(
service().IsParentFolderManaged(BookmarkParentFolder::FromFolderNode(
root_managed_node->children()[0].get())));
}
TEST_F(BookmarkMergedSurfaceServiceTest,
IsParentFolderManagedNoManagedService) {
CreateBookmarkMergedSurfaceService();
AddNodesFromModelString(&model(), model().bookmark_bar_node(), "f1:[ 4 5 ]");
EXPECT_FALSE(
service().IsParentFolderManaged(BookmarkParentFolder::FromFolderNode(
model().bookmark_bar_node()->children()[0].get())));
}
TEST_F(BookmarkMergedSurfaceServiceTest, MoveToPermanentFolder) {
CreateBookmarkMergedSurfaceService();
AddNodesFromModelString(&model(), model().bookmark_bar_node(), "1 2 3 ");
AddNodesFromModelString(&model(), model().other_node(), "4 5 6 ");
EXPECT_CALL(
mock_service_observer(),
BookmarkNodeMoved(
/*old_parent=*/BookmarkParentFolder::BookmarkBarFolder(),
/*old_index=*/1u, /*new_parent=*/BookmarkParentFolder::OtherFolder(),
/*new_index=*/1u));
// Move node "2" in bookmark bar to be after "4" in other node.
const BookmarkNode* node = model().bookmark_bar_node()->children()[1].get();
service().Move(node, BookmarkParentFolder::OtherFolder(), 1,
/*browser=*/nullptr);
EXPECT_EQ(ModelStringFromNode(model().bookmark_bar_node()), "1 3 ");
EXPECT_EQ(ModelStringFromNode(model().other_node()), "4 2 5 6 ");
}
TEST_F(BookmarkMergedSurfaceServiceTest,
MoveToPermanentFolderWithAccountNodes) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
AddNodesFromModelString(&model(), model().bookmark_bar_node(), "1 2 3 ");
AddNodesFromModelString(&model(), model().other_node(), "4 5 6 ");
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"A1 A2 A3 ");
AddNodesFromModelString(&model(), model().account_other_node(), "A4 A5 A6 ");
InSequence s;
// Move from local bookmark bar to local other node.
// Move node "2" in bookmark bar to be after "4" in other node.
const BookmarkNode* node = model().bookmark_bar_node()->children()[1].get();
EXPECT_CALL(
mock_service_observer(),
BookmarkNodeMoved(
/*old_parent=*/BookmarkParentFolder::BookmarkBarFolder(),
/*old_index=*/4u, /*new_parent=*/BookmarkParentFolder::OtherFolder(),
/*new_index=*/1u));
service().Move(node, BookmarkParentFolder::OtherFolder(), 1,
/*browser=*/nullptr);
EXPECT_EQ(ModelStringFromNode(model().bookmark_bar_node()), "1 3 ");
EXPECT_EQ(ModelStringFromNode(model().other_node()), "2 4 5 6 ");
EXPECT_EQ(service().GetIndexOf(node), 1u);
// Move within bookmark bar.
EXPECT_CALL(mock_service_observer(),
BookmarkNodeMoved(
/*old_parent=*/BookmarkParentFolder::BookmarkBarFolder(),
/*old_index=*/4u,
/*new_parent=*/BookmarkParentFolder::BookmarkBarFolder(),
/*new_index=*/1u));
node = model().bookmark_bar_node()->children()[1].get();
service().Move(node, BookmarkParentFolder::BookmarkBarFolder(), 1,
/*browser=*/nullptr);
EXPECT_EQ(ModelStringFromNode(model().bookmark_bar_node()), "3 1 ");
EXPECT_EQ(ModelStringFromNode(model().account_bookmark_bar_node()),
"A1 A2 A3 ");
// Expected order: "A1 3 A2 A3 1 "
EXPECT_EQ(service().GetIndexOf(node), 1u);
// Move from account other node to account bookmark bar.
EXPECT_CALL(
mock_service_observer(),
BookmarkNodeMoved(
/*old_parent=*/BookmarkParentFolder::OtherFolder(), /*old_index=*/0u,
/*new_parent=*/BookmarkParentFolder::BookmarkBarFolder(),
/*new_index=*/2u));
node = model().account_other_node()->children()[0].get();
service().Move(node, BookmarkParentFolder::BookmarkBarFolder(), 2,
/*browser=*/nullptr);
EXPECT_EQ(ModelStringFromNode(model().bookmark_bar_node()), "3 1 ");
EXPECT_EQ(ModelStringFromNode(model().account_bookmark_bar_node()),
"A1 A4 A2 A3 ");
EXPECT_EQ(ModelStringFromNode(model().account_other_node()), "A5 A6 ");
// Expected order: "A1 3 A4 A2 A3 1 "
EXPECT_EQ(service().GetIndexOf(node), 2u);
EXPECT_EQ(
service().GetNodeAtIndex(BookmarkParentFolder::BookmarkBarFolder(), 1u),
model().bookmark_bar_node()->children()[0].get());
}
TEST_F(BookmarkMergedSurfaceServiceTest, MoveIsNoOpWithAccountNodes) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
AddNodesFromModelString(&model(), model().bookmark_bar_node(), "1 2 3 ");
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"A1 A2 A3 ");
EXPECT_CALL(mock_service_observer(), BookmarkNodeMoved(_, _, _, _)).Times(0);
const BookmarkParentFolder bb_folder(
BookmarkParentFolder::BookmarkBarFolder());
service().Move(service().GetNodeAtIndex(bb_folder, 0), bb_folder, 0,
/*browser=*/nullptr);
service().Move(service().GetNodeAtIndex(bb_folder, 0), bb_folder, 1,
/*browser=*/nullptr);
}
TEST_F(BookmarkMergedSurfaceServiceTest, MoveIsNoOpWithoutAccountNodes) {
CreateBookmarkMergedSurfaceService();
AddNodesFromModelString(&model(), model().bookmark_bar_node(), "1 2 3 ");
EXPECT_CALL(mock_service_observer(), BookmarkNodeMoved(_, _, _, _)).Times(0);
const BookmarkParentFolder bb_folder(
BookmarkParentFolder::BookmarkBarFolder());
service().Move(service().GetNodeAtIndex(bb_folder, 1), bb_folder, 1,
/*browser=*/nullptr);
service().Move(service().GetNodeAtIndex(bb_folder, 1), bb_folder, 2,
/*browser=*/nullptr);
}
TEST_F(BookmarkMergedSurfaceServiceTest, MoveWithinBookmarkModelOnlyRequired) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
AddNodesFromModelString(&model(), model().bookmark_bar_node(), "1 2 3 ");
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"A1 A2 A3 ");
const BookmarkParentFolder bb_folder(
BookmarkParentFolder::BookmarkBarFolder());
EXPECT_CALL(mock_service_observer(),
BookmarkNodeMoved(/*old_parent=*/bb_folder, /*old_index=*/0,
/*new_parent=*/bb_folder, /*new_index=*/1));
EXPECT_CALL(mock_service_observer(),
BookmarkNodeMoved(/*old_parent=*/bb_folder, /*old_index=*/2,
/*new_parent=*/bb_folder, /*new_index=*/0));
service().Move(service().GetNodeAtIndex(bb_folder, 0), bb_folder, 2,
/*browser=*/nullptr);
service().Move(service().GetNodeAtIndex(bb_folder, 2), bb_folder, 0,
/*browser=*/nullptr);
}
TEST_F(BookmarkMergedSurfaceServiceTest, ScopedMoveChangeIsReset) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
AddNodesFromModelString(&model(), model().bookmark_bar_node(), "1 2 3 ");
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"A1 A2 A3 ");
const BookmarkParentFolder bb_folder(
BookmarkParentFolder::BookmarkBarFolder());
EXPECT_CALL(mock_service_observer(),
BookmarkNodeMoved(/*old_parent=*/bb_folder, /*old_index=*/0,
/*new_parent=*/bb_folder, /*new_index=*/1));
service().Move(service().GetNodeAtIndex(bb_folder, 0), bb_folder, 2,
/*browser=*/nullptr);
// Move in bookmark model.
EXPECT_CALL(mock_service_observer(),
BookmarkNodeMoved(/*old_parent=*/bb_folder, /*old_index=*/4,
/*new_parent=*/bb_folder, /*new_index=*/5));
model().Move(model().bookmark_bar_node()->children()[1].get(),
model().bookmark_bar_node(), 3u);
}
TEST_F(BookmarkMergedSurfaceServiceTest, MoveToBookmarkNode) {
CreateBookmarkMergedSurfaceService();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
AddNodesFromModelString(&model(), model().other_node(), "6 7 8 ");
// Move node "8" from other node to node "f1" after "4".
const BookmarkNode* node_to_move = model().other_node()->children()[2].get();
const BookmarkParentFolder destination = BookmarkParentFolder::FromFolderNode(
model().bookmark_bar_node()->children()[3].get());
EXPECT_CALL(
mock_service_observer(),
BookmarkNodeMoved(/*old_parent=*/BookmarkParentFolder::OtherFolder(),
/*old_index=*/2u,
/*new_parent=*/destination, /*new_index=*/1u));
service().Move(node_to_move, destination, 1,
/*browser=*/nullptr);
EXPECT_EQ(ModelStringFromNode(model().bookmark_bar_node()),
"1 2 3 f1:[ 4 8 5 ] ");
EXPECT_EQ(ModelStringFromNode(model().other_node()), "6 7 ");
}
TEST_F(BookmarkMergedSurfaceServiceTest, MoveFromAccountToLocalStorage) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
AddNodesFromModelString(&model(), model().bookmark_bar_node(), "f1:[ 1 2 ] ");
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"A1 A2 A3 ");
const BookmarkNode* node =
model().account_bookmark_bar_node()->children()[0].get();
const BookmarkNode* destination =
model().bookmark_bar_node()->children()[0].get();
const BookmarkParentFolder destination_folder(
BookmarkParentFolder::FromFolderNode(destination));
// Bypass the move storage dialog and directly move the bookmark.
base::MockCallback<
BookmarkMergedSurfaceService::ShowMoveStorageDialogCallback>
move_storage_callback;
service().SetShowMoveStorageDialogCallbackForTesting(
move_storage_callback.Get());
EXPECT_CALL(move_storage_callback, Run(testing::_, node, destination, 1))
.WillOnce(testing::Invoke(
this, &BookmarkMergedSurfaceServiceTest::PerformMoveAction));
EXPECT_CALL(mock_service_observer(),
BookmarkNodeMoved(
/*old_parent=*/BookmarkParentFolder::BookmarkBarFolder(),
/*old_index=*/0u,
/*new_parent=*/destination_folder, /*new_index=*/1u));
service().Move(node, destination_folder, 1, /*browser=*/nullptr);
EXPECT_EQ(ModelStringFromNode(model().bookmark_bar_node()), "f1:[ 1 A1 2 ] ");
EXPECT_EQ(ModelStringFromNode(model().account_bookmark_bar_node()), "A2 A3 ");
}
TEST_F(BookmarkMergedSurfaceServiceTest, MoveFromLocalToAccountStorage) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
AddNodesFromModelString(&model(), model().bookmark_bar_node(), "1 2 3 ");
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"f1:[ A1 A2 ] ");
const BookmarkNode* node = model().bookmark_bar_node()->children()[0].get();
const BookmarkNode* destination =
model().account_bookmark_bar_node()->children()[0].get();
const BookmarkParentFolder destination_folder(
BookmarkParentFolder::FromFolderNode(destination));
// Bypass the move storage dialog and directly move the bookmark.
base::MockCallback<
BookmarkMergedSurfaceService::ShowMoveStorageDialogCallback>
move_storage_callback;
service().SetShowMoveStorageDialogCallbackForTesting(
move_storage_callback.Get());
EXPECT_CALL(move_storage_callback, Run(testing::_, node, destination, 1))
.WillOnce(testing::Invoke(
this, &BookmarkMergedSurfaceServiceTest::PerformMoveAction));
EXPECT_CALL(mock_service_observer(),
BookmarkNodeMoved(
/*old_parent=*/BookmarkParentFolder::BookmarkBarFolder(),
/*old_index=*/1u,
/*new_parent=*/destination_folder, /*new_index=*/1u));
service().Move(node, destination_folder, 1, /*browser=*/nullptr);
EXPECT_EQ(ModelStringFromNode(model().bookmark_bar_node()), "2 3 ");
EXPECT_EQ(ModelStringFromNode(model().account_bookmark_bar_node()),
"f1:[ A1 1 A2 ] ");
}
TEST_F(BookmarkMergedSurfaceServiceTest, CopyBookmarkNodeData) {
CreateBookmarkMergedSurfaceService();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
AddNodesFromModelString(&model(), model().other_node(), "6 7 8 f2:[ 9 ] ");
// Copy node "f1" to "f2".
const BookmarkNode* node_to_copy =
model().bookmark_bar_node()->children()[3].get();
const bookmarks::BookmarkNodeData::Element node_data(node_to_copy);
const BookmarkParentFolder destination = BookmarkParentFolder::FromFolderNode(
model().other_node()->children()[3].get());
// Register second observer.
MockBookmarkMergedSurfaceServiceObserver mock_service_observer2;
base::ScopedObservation<BookmarkMergedSurfaceService,
BookmarkMergedSurfaceServiceObserver>
obs2_{&mock_service_observer2};
obs2_.Observe(&service());
InSequence sequence;
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(destination, 1u));
EXPECT_CALL(mock_service_observer2, BookmarkNodeAdded(destination, 1u));
EXPECT_CALL(mock_service_observer(),
BookmarkNodeAdded(HasParent(destination), 0u));
EXPECT_CALL(mock_service_observer2,
BookmarkNodeAdded(HasParent(destination), 0u));
EXPECT_CALL(mock_service_observer(),
BookmarkNodeAdded(HasParent(destination), 1u));
EXPECT_CALL(mock_service_observer2,
BookmarkNodeAdded(HasParent(destination), 1u));
service().AddNodesAsCopiesOfNodeData({node_data}, destination, 1);
EXPECT_EQ(ModelStringFromNode(model().bookmark_bar_node()),
"1 2 3 f1:[ 4 5 ] ");
EXPECT_EQ(ModelStringFromNode(model().other_node()),
"6 7 8 f2:[ 9 f1:[ 4 5 ] ] ");
}
TEST_F(BookmarkMergedSurfaceServiceTest, CopyBookmarkNodeDataMultipleNodes) {
CreateBookmarkMergedSurfaceService();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
AddNodesFromModelString(&model(), model().other_node(), "6 7 8 f2:[ 9 ] ");
// Copy nodes "2" and "3" to "f2".
const BookmarkNode* n1 = model().bookmark_bar_node()->children()[1].get();
const BookmarkNode* n2 = model().bookmark_bar_node()->children()[2].get();
std::vector<bookmarks::BookmarkNodeData::Element> nodes_data;
nodes_data.emplace_back(n1);
nodes_data.emplace_back(n2);
const BookmarkParentFolder destination = BookmarkParentFolder::FromFolderNode(
model().other_node()->children()[3].get());
InSequence sequence;
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(destination, 0u));
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(destination, 1u));
service().AddNodesAsCopiesOfNodeData(nodes_data, destination, 0);
EXPECT_EQ(ModelStringFromNode(model().bookmark_bar_node()),
"1 2 3 f1:[ 4 5 ] ");
EXPECT_EQ(ModelStringFromNode(model().other_node()), "6 7 8 f2:[ 2 3 9 ] ");
}
TEST_F(BookmarkMergedSurfaceServiceTest, CopyBookmarkNodeToPermanentFolder) {
CreateBookmarkMergedSurfaceService();
AddNodesFromModelString(&model(), model().bookmark_bar_node(), "1 2 3 ");
AddNodesFromModelString(&model(), model().other_node(),
"6 7 8 f2:[ 9 f3:[ 1 ] 2 ] ");
// Copy node "7" and "8" to bookmark bar.
std::vector<bookmarks::BookmarkNodeData::Element> nodes_data;
nodes_data.emplace_back(model().other_node()->children()[1].get());
nodes_data.emplace_back(model().other_node()->children()[2].get());
const BookmarkParentFolder bb_folder(
BookmarkParentFolder::BookmarkBarFolder());
{
InSequence sequence;
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(bb_folder, 1u));
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(bb_folder, 2u));
service().AddNodesAsCopiesOfNodeData(nodes_data, bb_folder, 1);
EXPECT_EQ(ModelStringFromNode(model().bookmark_bar_node()), "1 7 8 2 3 ");
EXPECT_EQ(service().GetNodeAtIndex(bb_folder, 1),
model().bookmark_bar_node()->children()[1].get());
EXPECT_EQ(service().GetNodeAtIndex(bb_folder, 2),
model().bookmark_bar_node()->children()[2].get());
}
// New nodes are added to the account node.
model().CreateAccountPermanentFolders();
// Copy node "6" and "F2" to bookmark bar.
nodes_data.clear();
nodes_data.emplace_back(model().other_node()->children()[0].get());
nodes_data.emplace_back(model().other_node()->children()[3].get());
{
InSequence sequence;
// Register second observer.
MockBookmarkMergedSurfaceServiceObserver mock_service_observer2;
base::ScopedObservation<BookmarkMergedSurfaceService,
BookmarkMergedSurfaceServiceObserver>
obs2_{&mock_service_observer2};
obs2_.Observe(&service());
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(bb_folder, 2u));
EXPECT_CALL(mock_service_observer2, BookmarkNodeAdded(bb_folder, 2u));
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(bb_folder, 3u));
EXPECT_CALL(mock_service_observer2, BookmarkNodeAdded(bb_folder, 3u));
EXPECT_CALL(mock_service_observer(),
BookmarkNodeAdded(HasParent(bb_folder), 0u));
EXPECT_CALL(mock_service_observer2,
BookmarkNodeAdded(HasParent(bb_folder), 0u));
EXPECT_CALL(mock_service_observer(),
BookmarkNodeAdded(HasParent(bb_folder), 1u));
EXPECT_CALL(mock_service_observer2,
BookmarkNodeAdded(HasParent(bb_folder), 1u));
BookmarkParentFolder obs1_expected_f3 = bb_folder;
BookmarkParentFolder obs2_expected_f3 = bb_folder;
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(_, 0u))
.WillOnce(testing::SaveArg<0>(&obs1_expected_f3));
EXPECT_CALL(mock_service_observer2, BookmarkNodeAdded(_, 0u))
.WillOnce(testing::SaveArg<0>(&obs2_expected_f3));
EXPECT_CALL(mock_service_observer(),
BookmarkNodeAdded(HasParent(bb_folder), 2u));
EXPECT_CALL(mock_service_observer2,
BookmarkNodeAdded(HasParent(bb_folder), 2u));
service().AddNodesAsCopiesOfNodeData(nodes_data, bb_folder, 2);
EXPECT_EQ(ModelStringFromNode(model().account_bookmark_bar_node()),
"6 f2:[ 9 f3:[ 1 ] 2 ] ");
EXPECT_EQ(service().GetNodeAtIndex(bb_folder, 2),
model().account_bookmark_bar_node()->children()[0].get());
EXPECT_EQ(service().GetNodeAtIndex(bb_folder, 3),
model().account_bookmark_bar_node()->children()[1].get());
EXPECT_EQ(obs1_expected_f3.as_non_permanent_folder(),
model()
.account_bookmark_bar_node()
->children()[1]
->children()[1]
.get());
EXPECT_EQ(obs1_expected_f3, obs2_expected_f3);
}
}
TEST_F(BookmarkMergedSurfaceServiceTest, ScopedAddNewNodesReset) {
CreateBookmarkMergedSurfaceService();
AddNodesFromModelString(&model(), model().bookmark_bar_node(), "1 2 3 ");
AddNodesFromModelString(&model(), model().other_node(), "6 7 8 f2:[ 9 ] ");
// Copy node "7" to bookmark bar.
bookmarks::BookmarkNodeData::Element node_data(
model().other_node()->children()[1].get());
const BookmarkParentFolder bb_folder(
BookmarkParentFolder::BookmarkBarFolder());
InSequence sequence;
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(bb_folder, 1u));
service().AddNodesAsCopiesOfNodeData({node_data}, bb_folder, 1);
EXPECT_EQ(ModelStringFromNode(model().bookmark_bar_node()), "1 7 2 3 ");
EXPECT_EQ(service().GetNodeAtIndex(bb_folder, 1),
model().bookmark_bar_node()->children()[1].get());
// Copy node "1" to "f2".
const BookmarkNode* node_to_copy =
model().bookmark_bar_node()->children()[0].get();
const BookmarkParentFolder destination = BookmarkParentFolder::FromFolderNode(
model().other_node()->children()[3].get());
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(destination, 1u));
service().AddNodesAsCopiesOfNodeData(
{bookmarks::BookmarkNodeData::Element(node_to_copy)}, destination, 1);
EXPECT_EQ(ModelStringFromNode(model().other_node()), "6 7 8 f2:[ 9 1 ] ");
}
TEST_F(BookmarkMergedSurfaceServiceTest,
GetUnderlyingNodesForNonPermanentNode) {
CreateBookmarkMergedSurfaceService();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
const BookmarkNode* node = model().bookmark_bar_node()->children()[3].get();
BookmarkParentFolder folder = BookmarkParentFolder::FromFolderNode(node);
EXPECT_THAT(service().GetUnderlyingNodes(folder), UnorderedElementsAre(node));
}
TEST_F(BookmarkMergedSurfaceServiceTest,
GetUnderlyingNodesManagedPermanentNode) {
CreateBookmarkMergedSurfaceServiceWithManaged(/*managed_bookmarks_size=*/2);
{
BookmarkParentFolder folder = BookmarkParentFolder::ManagedFolder();
EXPECT_THAT(service().GetUnderlyingNodes(folder),
UnorderedElementsAre(managed_node()));
}
{
// Non permanent managed node.
const BookmarkNode* node = managed_node()->children()[0].get();
BookmarkParentFolder folder = BookmarkParentFolder::FromFolderNode(node);
EXPECT_THAT(service().GetUnderlyingNodes(folder),
UnorderedElementsAre(node));
}
}
TEST_F(BookmarkMergedSurfaceServiceTest, GetUnderlyingNodesPermanentNode) {
CreateBookmarkMergedSurfaceService();
{
BookmarkParentFolder folder = BookmarkParentFolder::BookmarkBarFolder();
EXPECT_THAT(service().GetUnderlyingNodes(folder),
UnorderedElementsAre(model().bookmark_bar_node()));
}
{
BookmarkParentFolder folder = BookmarkParentFolder::OtherFolder();
EXPECT_THAT(service().GetUnderlyingNodes(folder),
UnorderedElementsAre(model().other_node()));
}
{
BookmarkParentFolder folder = BookmarkParentFolder::MobileFolder();
EXPECT_THAT(service().GetUnderlyingNodes(folder),
UnorderedElementsAre(model().mobile_node()));
}
}
TEST_F(BookmarkMergedSurfaceServiceTest,
GetDefaultParentForNewNodesForNonPermanentNode) {
CreateBookmarkMergedSurfaceService();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
const BookmarkNode* node = model().bookmark_bar_node()->children()[3].get();
BookmarkParentFolder folder = BookmarkParentFolder::FromFolderNode(node);
EXPECT_EQ(service().GetDefaultParentForNewNodes(folder), node);
}
TEST_F(BookmarkMergedSurfaceServiceTest,
GetDefaultParentForNewNodesForPermanentNode) {
CreateBookmarkMergedSurfaceService();
BookmarkParentFolder folder = BookmarkParentFolder::BookmarkBarFolder();
EXPECT_EQ(service().GetDefaultParentForNewNodes(folder),
model().bookmark_bar_node());
model().CreateAccountPermanentFolders();
EXPECT_EQ(service().GetDefaultParentForNewNodes(folder),
model().account_bookmark_bar_node());
}
TEST_F(BookmarkMergedSurfaceServiceTest,
GetParentForManagedNodeForPermanentManagedNode) {
CreateBookmarkMergedSurfaceServiceWithManaged(/*managed_bookmarks_size=*/3);
BookmarkParentFolder managed_folder = BookmarkParentFolder::ManagedFolder();
EXPECT_EQ(service().GetParentForManagedNode(managed_folder), managed_node());
}
TEST_F(BookmarkMergedSurfaceServiceTest,
GetParentForManagedNodeForNonPermanentNode) {
CreateBookmarkMergedSurfaceServiceWithManaged(/*managed_bookmarks_size=*/1);
AddNodesFromModelString(&model(), managed_node(), "f1:[ f11:[ 4 ] 5 ] ");
// Fetch second node to get the folder since a node was already added at
// creation.
const BookmarkNode* managed_folder =
managed_node()->children()[1].get()->children()[0].get();
ASSERT_TRUE(managed_folder->is_folder());
ASSERT_FALSE(managed_folder->parent()->is_permanent_node());
BookmarkParentFolder folder =
BookmarkParentFolder::FromFolderNode(managed_folder);
EXPECT_EQ(service().GetParentForManagedNode(folder), managed_folder);
}
TEST_F(BookmarkMergedSurfaceServiceTest, BookmarkNodeAdded) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
const BookmarkNode* local_bb_node = model().bookmark_bar_node();
const BookmarkNode* account_bb_node = model().account_bookmark_bar_node();
ASSERT_TRUE(account_bb_node);
BookmarkParentFolder bb_folder = BookmarkParentFolder::BookmarkBarFolder();
const GURL kUrl("http://foo.com");
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(bb_folder, 0));
const BookmarkNode* new_node = model().AddURL(local_bb_node, 0, u"L1", kUrl);
EXPECT_EQ(service().GetNodeAtIndex(bb_folder, 0u), new_node);
// Add node to the account.
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(bb_folder, 0));
new_node = model().AddURL(account_bb_node, 0, u"A1", kUrl);
EXPECT_EQ(service().GetNodeAtIndex(bb_folder, 0u), new_node);
// Add folder to `local_bb_node`.
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(bb_folder, 2u));
const BookmarkNode* folder_node =
model().AddFolder(local_bb_node, 1, u"title");
EXPECT_EQ(service().GetNodeAtIndex(bb_folder, 2u), folder_node);
// Add another account node.
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(bb_folder, 1));
new_node = model().AddURL(account_bb_node, 1, u"A2", kUrl);
EXPECT_EQ(service().GetNodeAtIndex(bb_folder, 1u), new_node);
EXPECT_THAT(service().GetChildren(bb_folder),
HasOrderedChildren(std::vector<const BookmarkNode*>{
account_bb_node->children()[0].get(),
account_bb_node->children()[1].get(),
local_bb_node->children()[0].get(),
local_bb_node->children()[1].get()}));
// Add new node to `folder_node`.
BookmarkParentFolder folder(
BookmarkParentFolder::FromFolderNode(folder_node));
EXPECT_CALL(mock_service_observer(), BookmarkNodeAdded(folder, 0));
new_node = model().AddURL(folder_node, 0, u"1", kUrl);
EXPECT_EQ(service().GetNodeAtIndex(folder, 0u), new_node);
}
TEST_F(BookmarkMergedSurfaceServiceTest, BookmarkNodeRemovedOrderingTracked) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
const BookmarkNode* local_bb_node = model().bookmark_bar_node();
const BookmarkNode* account_bb_node = model().account_bookmark_bar_node();
ASSERT_TRUE(account_bb_node);
BookmarkParentFolder bb_folder = BookmarkParentFolder::BookmarkBarFolder();
AddNodesFromModelString(&model(), local_bb_node, "1 2 3 f1:[ 4 5 ] ");
AddNodesFromModelString(&model(), account_bb_node, "7 8 9 f3:[ 10 11 ] ");
const auto& local_children = local_bb_node->children();
const auto& account_children = account_bb_node->children();
std::vector<const BookmarkNode*> expected_children{
account_children[0].get(), account_children[1].get(),
account_children[2].get(), account_children[3].get(),
local_children[0].get(), local_children[1].get(),
local_children[2].get(), local_children[3].get()};
EXPECT_THAT(service().GetChildren(bb_folder),
HasOrderedChildren(expected_children));
const BookmarkNode* node_to_remove = local_children[2].get();
size_t across_storage_index = service().GetIndexOf(node_to_remove);
ASSERT_EQ(across_storage_index, 6u);
EXPECT_CALL(
mock_service_observer(),
BookmarkNodesRemoved(bb_folder, UnorderedElementsAre(node_to_remove)));
expected_children.erase(expected_children.cbegin() + across_storage_index);
model().Remove(node_to_remove, bookmarks::metrics::BookmarkEditSource::kOther,
FROM_HERE);
Mock::VerifyAndClearExpectations(&mock_service_observer());
EXPECT_THAT(service().GetChildren(bb_folder),
HasOrderedChildren(expected_children));
node_to_remove = account_children[1].get();
across_storage_index = service().GetIndexOf(node_to_remove);
ASSERT_EQ(across_storage_index, 1u);
EXPECT_CALL(
mock_service_observer(),
BookmarkNodesRemoved(bb_folder, UnorderedElementsAre(node_to_remove)));
expected_children.erase(expected_children.cbegin() + across_storage_index);
model().Remove(node_to_remove, bookmarks::metrics::BookmarkEditSource::kOther,
FROM_HERE);
EXPECT_THAT(service().GetChildren(bb_folder),
HasOrderedChildren(expected_children));
}
TEST_F(BookmarkMergedSurfaceServiceTest, BookmarkNodeRemovedCustomOrder) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
const BookmarkNode* local_bb_node = model().bookmark_bar_node();
const BookmarkNode* account_bb_node = model().account_bookmark_bar_node();
ASSERT_TRUE(account_bb_node);
BookmarkParentFolder bb_folder = BookmarkParentFolder::BookmarkBarFolder();
AddNodesFromModelString(&model(), local_bb_node, "1 2 3 f1:[ 4 5 ] ");
AddNodesFromModelString(&model(), account_bb_node, "7 8 9 f3:[ 10 11 ] ");
const auto& local_children = local_bb_node->children();
const auto& account_children = account_bb_node->children();
std::vector<const BookmarkNode*> expected_children{
account_children[0].get(), account_children[1].get(),
local_children[0].get(), local_children[1].get(),
account_children[2].get(), local_children[2].get(),
account_children[3].get(), local_children[3].get()};
for (size_t i = 0; i < expected_children.size(); i++) {
service().Move(expected_children[i], bb_folder, i, /*browser=*/nullptr);
}
EXPECT_THAT(service().GetChildren(bb_folder),
HasOrderedChildren(expected_children));
const size_t index_node_to_remove = 1u;
EXPECT_CALL(mock_service_observer(),
BookmarkNodesRemoved(
bb_folder, UnorderedElementsAre(account_children[1].get())));
expected_children.erase(expected_children.cbegin() + index_node_to_remove);
model().Remove(account_children[1].get(),
bookmarks::metrics::BookmarkEditSource::kOther, FROM_HERE);
EXPECT_THAT(service().GetChildren(bb_folder),
HasOrderedChildren(expected_children));
}
TEST_F(BookmarkMergedSurfaceServiceTest, BookmarkNodeRemovedNonTrackedNode) {
CreateBookmarkMergedSurfaceService();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
// Remove node "4".
const size_t index = 0;
const BookmarkNode* parent_node =
model().bookmark_bar_node()->children()[3].get();
const BookmarkNode* node_to_remove = parent_node->children()[index].get();
EXPECT_CALL(
mock_service_observer(),
BookmarkNodesRemoved(BookmarkParentFolder::FromFolderNode(parent_node),
UnorderedElementsAre(node_to_remove)));
model().Remove(node_to_remove, bookmarks::metrics::BookmarkEditSource::kOther,
FROM_HERE);
}
TEST_F(BookmarkMergedSurfaceServiceTest,
BookmarkNodeRemovedAccountNodeWithChildNodes) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
BookmarkParentFolder bb_folder = BookmarkParentFolder::BookmarkBarFolder();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"7 8 9 f3:[ 10 11 ] ");
const auto& account_child_nodes =
model().account_bookmark_bar_node()->children();
EXPECT_CALL(
mock_service_observer(),
BookmarkNodesRemoved(bb_folder,
UnorderedElementsAre(account_child_nodes[0].get(),
account_child_nodes[1].get(),
account_child_nodes[2].get(),
account_child_nodes[3].get())));
model().RemoveAccountPermanentFolders();
}
TEST_F(BookmarkMergedSurfaceServiceTest,
BookmarkNodeRemovedAccountNodeCustomOrder) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
BookmarkParentFolder bb_folder = BookmarkParentFolder::BookmarkBarFolder();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"7 8 9 f3:[ 10 11 ] ");
const auto& local_children = model().bookmark_bar_node()->children();
const auto& account_children =
model().account_bookmark_bar_node()->children();
std::vector<const BookmarkNode*> expected_children{
account_children[0].get(), account_children[1].get(),
local_children[0].get(), local_children[1].get(),
account_children[2].get(), local_children[2].get(),
account_children[3].get(), local_children[3].get()};
for (size_t i = 0; i < expected_children.size(); i++) {
service().Move(expected_children[i], bb_folder, i, /*browser=*/nullptr);
}
EXPECT_THAT(service().GetChildren(bb_folder),
HasOrderedChildren(expected_children));
EXPECT_CALL(mock_service_observer(),
BookmarkNodesRemoved(
bb_folder, UnorderedElementsAre(
expected_children[0], expected_children[1],
expected_children[4], expected_children[6])));
expected_children.clear();
model().RemoveAccountPermanentFolders();
EXPECT_THAT(service().GetChildren(bb_folder),
HasOrderedChildren(std::vector<const BookmarkNode*>{
local_children[0].get(), local_children[1].get(),
local_children[2].get(), local_children[3].get()}));
}
TEST_F(BookmarkMergedSurfaceServiceTest,
BookmarkNodeRemovedAccountNodeWithNoChildren) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
BookmarkParentFolder bb_folder = BookmarkParentFolder::BookmarkBarFolder();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
EXPECT_CALL(mock_service_observer(),
BookmarkNodesRemoved(testing::_, testing::_))
.Times(0);
model().RemoveAccountPermanentFolders();
}
TEST_F(BookmarkMergedSurfaceServiceTest,
BookmarkNodeRemovedAccountNodeOrderingNotTracked) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
BookmarkParentFolder bb_folder = BookmarkParentFolder::BookmarkBarFolder();
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"1 2 3 ");
const auto& account_child_nodes =
model().account_bookmark_bar_node()->children();
EXPECT_CALL(
mock_service_observer(),
BookmarkNodesRemoved(bb_folder,
UnorderedElementsAre(account_child_nodes[0].get(),
account_child_nodes[1].get(),
account_child_nodes[2].get())));
model().RemoveAccountPermanentFolders();
EXPECT_FALSE(service().GetChildrenCount(bb_folder));
}
TEST_F(BookmarkMergedSurfaceServiceTest,
BookmarkNodeMovedBetweenPermanentFolders) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"7 8 9 f3:[ 10 11 ] ");
AddNodesFromModelString(&model(), model().other_node(), "A B C ");
AddNodesFromModelString(&model(), model().account_other_node(), "D E F ");
BookmarkParentFolder bb_folder = BookmarkParentFolder::BookmarkBarFolder();
const size_t bb_folder_size = service().GetChildrenCount(bb_folder);
BookmarkParentFolder other_folder = BookmarkParentFolder::OtherFolder();
const size_t other_folder_size = service().GetChildrenCount(other_folder);
// Move from bookmark bar `f1` to other node after `B`.
const BookmarkNode* f1 = model().bookmark_bar_node()->children()[3].get();
const size_t old_index = service().GetIndexOf(f1);
ASSERT_EQ(old_index, 7u);
const size_t expected_new_index = 5u;
ASSERT_EQ(service().GetIndexOf(model().other_node()->children()[1].get()),
expected_new_index - 1);
EXPECT_CALL(
mock_service_observer(),
BookmarkNodeMoved(/*old_parent=*/bb_folder, old_index,
/*new_parent=*/other_folder, expected_new_index));
model().Move(f1, model().other_node(), /*index*/ 2u);
EXPECT_EQ(service().GetChildrenCount(bb_folder), bb_folder_size - 1);
EXPECT_EQ(service().GetChildrenCount(other_folder), other_folder_size + 1);
EXPECT_EQ(service().GetIndexOf(f1), expected_new_index);
}
TEST_F(BookmarkMergedSurfaceServiceTest,
BookmarkNodeMovedWithinSamePermanentFolder) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"7 8 9 f3:[ 10 11 ] ");
BookmarkParentFolder bb_folder = BookmarkParentFolder::BookmarkBarFolder();
// Move `f1` to `account_bookmark_bar_node()` at the end.
const BookmarkNode* f1 = model().bookmark_bar_node()->children()[3].get();
size_t old_index = service().GetIndexOf(f1);
ASSERT_EQ(old_index, 7u);
size_t expected_new_index = 4u;
ASSERT_EQ(service().GetIndexOf(
model().account_bookmark_bar_node()->children()[3].get()),
expected_new_index - 1);
EXPECT_CALL(mock_service_observer(),
BookmarkNodeMoved(/*old_parent=*/bb_folder, old_index,
/*new_parent=*/bb_folder, expected_new_index));
model().Move(f1, model().account_bookmark_bar_node(),
/*index*/ 4u);
Mock::VerifyAndClearExpectations(&mock_service_observer());
EXPECT_EQ(service().GetIndexOf(f1), expected_new_index);
// Move within same parent folder.
const BookmarkNode* node =
model().account_bookmark_bar_node()->children()[1].get();
old_index = service().GetIndexOf(node);
expected_new_index = 2u;
EXPECT_CALL(mock_service_observer(),
BookmarkNodeMoved(/*old_parent=*/bb_folder, old_index,
/*new_parent=*/bb_folder, expected_new_index));
model().Move(node, model().account_bookmark_bar_node(), 3u);
EXPECT_EQ(service().GetIndexOf(node), expected_new_index);
}
TEST_F(BookmarkMergedSurfaceServiceTest,
BookmarkNodeMovedFromPermanentFolderToAFolder) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"7 8 9 f3:[ 10 11 ] ");
BookmarkParentFolder bb_folder = BookmarkParentFolder::BookmarkBarFolder();
const size_t bb_folder_size = service().GetChildrenCount(bb_folder);
// Move `3` to `account_bookmark_bar_node()` at the end.
const BookmarkNode* node = model().bookmark_bar_node()->children()[2].get();
const BookmarkNode* f3 =
model().account_bookmark_bar_node()->children()[3].get();
const size_t old_index = service().GetIndexOf(node);
ASSERT_EQ(old_index, 6u);
const size_t expected_new_index = 1u;
EXPECT_CALL(
mock_service_observer(),
BookmarkNodeMoved(/*old_parent=*/bb_folder, old_index,
/*new_parent=*/BookmarkParentFolder::FromFolderNode(f3),
expected_new_index));
model().Move(node, f3, expected_new_index);
EXPECT_EQ(service().GetChildrenCount(bb_folder), bb_folder_size - 1);
EXPECT_EQ(service().GetIndexOf(node), expected_new_index);
}
TEST_F(BookmarkMergedSurfaceServiceTest,
BookmarkNodeMovedFromFolderToPermanentFolder) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"7 8 9 f3:[ 10 11 ] ");
BookmarkParentFolder bb_folder = BookmarkParentFolder::BookmarkBarFolder();
const size_t bb_folder_size = service().GetChildrenCount(bb_folder);
// Move `4` from f1 to `account_bookmark_bar_node()` at index 1.
const BookmarkNode* old_parent =
model().bookmark_bar_node()->children()[3].get();
const BookmarkNode* node = old_parent->children()[0].get();
const size_t old_index = service().GetIndexOf(node);
ASSERT_EQ(old_index, 0u);
const size_t expected_new_index = 4u;
EXPECT_CALL(
mock_service_observer(),
BookmarkNodeMoved(
/*old_parent=*/BookmarkParentFolder::FromFolderNode(old_parent),
old_index, /*new_parent=*/bb_folder, expected_new_index));
model().Move(node, model().account_bookmark_bar_node(), expected_new_index);
EXPECT_EQ(service().GetChildrenCount(bb_folder), bb_folder_size + 1);
EXPECT_EQ(service().GetIndexOf(node), expected_new_index);
}
TEST_F(BookmarkMergedSurfaceServiceTest, BookmarkNodeChanged) {
CreateBookmarkMergedSurfaceService();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
const std::u16string kOriginalTitle(u"foo");
const GURL kUrl("http://url.com");
const BookmarkNode* node = model().bookmark_bar_node()->children()[0].get();
const std::u16string kNewTitle(u"goo");
EXPECT_CALL(mock_service_observer(), BookmarkNodeChanged(node));
model().SetTitle(node, kNewTitle,
bookmarks::metrics::BookmarkEditSource::kOther);
EXPECT_EQ(node->GetTitle(), kNewTitle);
}
TEST_F(BookmarkMergedSurfaceServiceTest, BookmarkNodeFaviconChanged) {
CreateBookmarkMergedSurfaceService();
const BookmarkNode* bookmark_bar_node = model().bookmark_bar_node();
const std::u16string kTitle(u"foo");
const GURL kPageURL("http://www.google.com");
const BookmarkNode* node =
model().AddURL(bookmark_bar_node, 0, kTitle, kPageURL);
std::set<GURL> changed_page_urls;
changed_page_urls.insert(kPageURL);
EXPECT_CALL(mock_service_observer(), BookmarkNodeFaviconChanged(node));
model().OnFaviconsChanged(changed_page_urls, GURL());
}
TEST_F(BookmarkMergedSurfaceServiceTest,
BookmarkParentFolderChildrenReordered) {
CreateBookmarkMergedSurfaceService();
AddNodesFromModelString(&model(), model().bookmark_bar_node(), "A B C D ");
const BookmarkNode* parent = model().bookmark_bar_node();
// Reorder bar node's bookmarks in reverse order.
std::vector<const BookmarkNode*> new_order = {
parent->children()[3].get(),
parent->children()[2].get(),
parent->children()[1].get(),
parent->children()[0].get(),
};
EXPECT_CALL(mock_service_observer(),
BookmarkParentFolderChildrenReordered(
BookmarkParentFolder::BookmarkBarFolder()));
model().ReorderChildren(parent, new_order);
}
TEST_F(BookmarkMergedSurfaceServiceTest, BookmarkAllUserNodesRemoved) {
CreateBookmarkMergedSurfaceService();
model().CreateAccountPermanentFolders();
AddNodesFromModelString(&model(), model().bookmark_bar_node(),
"1 2 3 f1:[ 4 5 ] ");
AddNodesFromModelString(&model(), model().account_bookmark_bar_node(),
"7 8 9 f3:[ 10 11 ] ");
ASSERT_EQ(
service().GetChildrenCount(BookmarkParentFolder::BookmarkBarFolder()),
8u);
EXPECT_CALL(mock_service_observer(), BookmarkAllUserNodesRemoved);
model().RemoveAllUserBookmarks(FROM_HERE);
EXPECT_EQ(
service().GetChildrenCount(BookmarkParentFolder::BookmarkBarFolder()),
0u);
}
TEST(BookmarkMergedSurfaceServiceLoadingTest, ModelLoadedFirst) {
std::unique_ptr<bookmarks::BookmarkModel> model =
std::make_unique<bookmarks::BookmarkModel>(
std::make_unique<bookmarks::TestBookmarkClient>());
BookmarkMergedSurfaceService service(model.get(),
/*managed_bookmark_service=*/nullptr);
MockBookmarkMergedSurfaceServiceObserver mock_observer;
base::ScopedObservation<BookmarkMergedSurfaceService,
BookmarkMergedSurfaceServiceObserver>
scoped_mock_observer{&mock_observer};
scoped_mock_observer.Observe(&service);
EXPECT_CALL(mock_observer, BookmarkMergedSurfaceServiceLoaded()).Times(0);
EXPECT_CALL(mock_observer, BookmarkNodeAdded).Times(0);
model->LoadEmptyForTest();
// Notifications ignored while service is not loaded yet.
AddNodesFromModelString(model.get(), model->other_node(), "1 2 3 ");
Mock::VerifyAndClearExpectations(&mock_observer);
EXPECT_CALL(mock_observer, BookmarkMergedSurfaceServiceLoaded()).Times(1);
service.LoadForTesting({});
// Verify service is observing the bookmark model.
EXPECT_CALL(mock_observer,
BookmarkNodeAdded(BookmarkParentFolder::BookmarkBarFolder(), _))
.Times(3);
AddNodesFromModelString(model.get(), model->bookmark_bar_node(), "1 2 3 ");
}
TEST(BookmarkMergedSurfaceServiceLoadingTest, ServiceLoadedFirst) {
std::unique_ptr<bookmarks::BookmarkModel> model =
std::make_unique<bookmarks::BookmarkModel>(
std::make_unique<bookmarks::TestBookmarkClient>());
BookmarkMergedSurfaceService service(model.get(),
/*managed_bookmark_service=*/nullptr);
MockBookmarkMergedSurfaceServiceObserver mock_observer;
base::ScopedObservation<BookmarkMergedSurfaceService,
BookmarkMergedSurfaceServiceObserver>
scoped_mock_observer{&mock_observer};
scoped_mock_observer.Observe(&service);
EXPECT_CALL(mock_observer, BookmarkMergedSurfaceServiceLoaded()).Times(0);
service.LoadForTesting({});
Mock::VerifyAndClearExpectations(&mock_observer);
EXPECT_CALL(mock_observer, BookmarkMergedSurfaceServiceLoaded()).Times(1);
model->LoadEmptyForTest();
// Verify service is observing the bookmark model.
EXPECT_CALL(mock_observer,
BookmarkNodeAdded(BookmarkParentFolder::BookmarkBarFolder(), _))
.Times(3);
AddNodesFromModelString(model.get(), model->bookmark_bar_node(), "1 2 3 ");
}
TEST(BookmarkMergedSurfaceServiceLoadingTest, IdsReassigned) {
std::unique_ptr<bookmarks::BookmarkModel> model =
std::make_unique<bookmarks::BookmarkModel>(
std::make_unique<bookmarks::TestBookmarkClient>());
BookmarkMergedSurfaceService service(model.get(),
/*managed_bookmark_service=*/nullptr);
MockBookmarkMergedSurfaceServiceObserver mock_observer;
base::ScopedObservation<BookmarkMergedSurfaceService,
BookmarkMergedSurfaceServiceObserver>
scoped_mock_observer{&mock_observer};
scoped_mock_observer.Observe(&service);
EXPECT_CALL(mock_observer, BookmarkMergedSurfaceServiceLoaded()).Times(1);
service.BookmarkModelLoaded(/*ids_reassigned=*/true);
model->LoadEmptyForTest();
}
TEST(BookmarkMergedSurfaceServiceLoadingTest, CustomOrder) {
base::test::ScopedFeatureList features{
switches::kSyncEnableBookmarksInTransportMode};
std::unique_ptr<bookmarks::BookmarkModel> model =
std::make_unique<bookmarks::BookmarkModel>(
std::make_unique<bookmarks::TestBookmarkClient>());
BookmarkMergedSurfaceService service(model.get(),
/*managed_bookmark_service=*/nullptr);
MockBookmarkMergedSurfaceServiceObserver mock_observer;
base::ScopedObservation<BookmarkMergedSurfaceService,
BookmarkMergedSurfaceServiceObserver>
scoped_mock_observer{&mock_observer};
scoped_mock_observer.Observe(&service);
model->LoadEmptyForTest();
model->CreateAccountPermanentFolders();
AddNodesFromModelString(model.get(), model->bookmark_bar_node(), "1 ");
AddNodesFromModelString(model.get(), model->account_bookmark_bar_node(),
"4 5 ");
EXPECT_CALL(mock_observer, BookmarkMergedSurfaceServiceLoaded()).Times(1);
service.LoadForTesting(
{{PermanentFolderType::kBookmarkBarNode,
{model->bookmark_bar_node()->children()[0]->id(),
model->account_bookmark_bar_node()->children()[0]->id(),
model->account_bookmark_bar_node()->children()[1]->id()}}});
EXPECT_EQ(
service.GetNodeAtIndex(BookmarkParentFolder::BookmarkBarFolder(), 0),
model->bookmark_bar_node()->children()[0].get());
EXPECT_EQ(
service.GetNodeAtIndex(BookmarkParentFolder::BookmarkBarFolder(), 1),
model->account_bookmark_bar_node()->children()[0].get());
}
} // namespace