blob: efd3bf73cf2c0d9c122be66ce132b99368645b4e [file] [log] [blame]
// Copyright (c) 2012 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 <utility>
#include "base/guid.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/test/integration/bookmarks_helper.h"
#include "chrome/browser/sync/test/integration/feature_toggler.h"
#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
#include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
#include "chrome/browser/sync/test/integration/sync_test.h"
#include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/browser/url_and_title.h"
#include "components/sync/driver/profile_sync_service.h"
#include "components/sync/driver/sync_driver_switches.h"
#include "components/sync/engine_impl/loopback_server/loopback_server_entity.h"
#include "components/sync/test/fake_server/bookmark_entity_builder.h"
#include "components/sync/test/fake_server/entity_builder_factory.h"
#include "components/sync/test/fake_server/fake_server_verifier.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/layout.h"
namespace {
using bookmarks::BookmarkModel;
using bookmarks::BookmarkNode;
using bookmarks::UrlAndTitle;
using bookmarks_helper::AddFolder;
using bookmarks_helper::AddURL;
using bookmarks_helper::CheckHasNoFavicon;
using bookmarks_helper::ContainsBookmarkNodeWithGUID;
using bookmarks_helper::CountBookmarksWithTitlesMatching;
using bookmarks_helper::CountBookmarksWithUrlsMatching;
using bookmarks_helper::CountFoldersWithTitlesMatching;
using bookmarks_helper::Create1xFaviconFromPNGFile;
using bookmarks_helper::CreateFavicon;
using bookmarks_helper::GetBookmarkBarNode;
using bookmarks_helper::GetBookmarkModel;
using bookmarks_helper::GetOtherNode;
using bookmarks_helper::ModelMatchesVerifier;
using bookmarks_helper::Move;
using bookmarks_helper::Remove;
using bookmarks_helper::RemoveAll;
using bookmarks_helper::SetFavicon;
using bookmarks_helper::SetTitle;
// All tests in this file utilize a single profile.
// TODO(pvalenzuela): Standardize this pattern by moving this constant to
// SyncTest and using it in all single client tests.
const int kSingleProfileIndex = 0;
// An arbitrary GUID, to be used for injecting the same bookmark entity to the
// fake server across PRE_MyTest and MyTest.
const char kBookmarkGuid[] = "e397ed62-9532-4dbf-ae55-200236eba15c";
class SingleClientBookmarksSyncTest : public FeatureToggler, public SyncTest {
public:
SingleClientBookmarksSyncTest()
: FeatureToggler(switches::kSyncUSSBookmarks), SyncTest(SINGLE_CLIENT) {}
~SingleClientBookmarksSyncTest() override {}
// Verify that the local bookmark model (for the Profile corresponding to
// |index|) matches the data on the FakeServer. It is assumed that FakeServer
// is being used and each bookmark has a unique title. Folders are not
// verified.
void VerifyBookmarkModelMatchesFakeServer(int index);
private:
DISALLOW_COPY_AND_ASSIGN(SingleClientBookmarksSyncTest);
};
void SingleClientBookmarksSyncTest::VerifyBookmarkModelMatchesFakeServer(
int index) {
fake_server::FakeServerVerifier fake_server_verifier(GetFakeServer());
std::vector<UrlAndTitle> local_bookmarks;
GetBookmarkModel(index)->GetBookmarks(&local_bookmarks);
// Verify that all local bookmark titles exist once on the server.
std::vector<UrlAndTitle>::const_iterator it;
for (it = local_bookmarks.begin(); it != local_bookmarks.end(); ++it) {
ASSERT_TRUE(fake_server_verifier.VerifyEntityCountByTypeAndName(
1,
syncer::BOOKMARKS,
base::UTF16ToUTF8(it->title)));
}
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, Sanity) {
ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
// Starting state:
// other_node
// -> top
// -> tier1_a
// -> http://mail.google.com "tier1_a_url0"
// -> http://www.pandora.com "tier1_a_url1"
// -> http://www.facebook.com "tier1_a_url2"
// -> tier1_b
// -> http://www.nhl.com "tier1_b_url0"
const BookmarkNode* top = AddFolder(
kSingleProfileIndex, GetOtherNode(kSingleProfileIndex), 0, "top");
const BookmarkNode* tier1_a = AddFolder(
kSingleProfileIndex, top, 0, "tier1_a");
const BookmarkNode* tier1_b = AddFolder(
kSingleProfileIndex, top, 1, "tier1_b");
const BookmarkNode* tier1_a_url0 = AddURL(
kSingleProfileIndex, tier1_a, 0, "tier1_a_url0",
GURL("http://mail.google.com"));
const BookmarkNode* tier1_a_url1 = AddURL(
kSingleProfileIndex, tier1_a, 1, "tier1_a_url1",
GURL("http://www.pandora.com"));
const BookmarkNode* tier1_a_url2 = AddURL(
kSingleProfileIndex, tier1_a, 2, "tier1_a_url2",
GURL("http://www.facebook.com"));
const BookmarkNode* tier1_b_url0 = AddURL(
kSingleProfileIndex, tier1_b, 0, "tier1_b_url0",
GURL("http://www.nhl.com"));
// Setup sync, wait for its completion, and make sure changes were synced.
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(
UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait());
ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
// Ultimately we want to end up with the following model; but this test is
// more about the journey than the destination.
//
// bookmark_bar
// -> CNN (www.cnn.com)
// -> tier1_a
// -> tier1_a_url2 (www.facebook.com)
// -> tier1_a_url1 (www.pandora.com)
// -> Porsche (www.porsche.com)
// -> Bank of America (www.bankofamerica.com)
// -> Seattle Bubble
// other_node
// -> top
// -> tier1_b
// -> Wired News (www.wired.com)
// -> tier2_b
// -> tier1_b_url0
// -> tier3_b
// -> Toronto Maple Leafs (mapleleafs.nhl.com)
// -> Wynn (www.wynnlasvegas.com)
// -> tier1_a_url0
const BookmarkNode* bar = GetBookmarkBarNode(kSingleProfileIndex);
const BookmarkNode* cnn = AddURL(
kSingleProfileIndex, bar, 0, "CNN", GURL("http://www.cnn.com"));
ASSERT_NE(nullptr, cnn);
Move(kSingleProfileIndex, tier1_a, bar, 1);
// Wait for the bookmark position change to sync.
ASSERT_TRUE(
UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait());
ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
const BookmarkNode* porsche = AddURL(
kSingleProfileIndex, bar, 2, "Porsche", GURL("http://www.porsche.com"));
// Rearrange stuff in tier1_a.
ASSERT_EQ(tier1_a, tier1_a_url2->parent());
ASSERT_EQ(tier1_a, tier1_a_url1->parent());
Move(kSingleProfileIndex, tier1_a_url2, tier1_a, 0);
Move(kSingleProfileIndex, tier1_a_url1, tier1_a, 2);
// Wait for the rearranged hierarchy to sync.
ASSERT_TRUE(
UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait());
ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
ASSERT_EQ(1, tier1_a_url0->parent()->GetIndexOf(tier1_a_url0));
Move(kSingleProfileIndex, tier1_a_url0, bar, bar->children().size());
const BookmarkNode* boa =
AddURL(kSingleProfileIndex, bar, bar->children().size(),
"Bank of America", GURL("https://www.bankofamerica.com"));
ASSERT_NE(nullptr, boa);
Move(kSingleProfileIndex, tier1_a_url0, top, top->children().size());
const BookmarkNode* bubble =
AddURL(kSingleProfileIndex, bar, bar->children().size(), "Seattle Bubble",
GURL("http://seattlebubble.com"));
ASSERT_NE(nullptr, bubble);
const BookmarkNode* wired = AddURL(
kSingleProfileIndex, bar, 2, "Wired News", GURL("http://www.wired.com"));
const BookmarkNode* tier2_b = AddFolder(
kSingleProfileIndex, tier1_b, 0, "tier2_b");
Move(kSingleProfileIndex, tier1_b_url0, tier2_b, 0);
Move(kSingleProfileIndex, porsche, bar, 0);
SetTitle(kSingleProfileIndex, wired, "News Wired");
SetTitle(kSingleProfileIndex, porsche, "ICanHazPorsche?");
// Wait for the title change to sync.
ASSERT_TRUE(
UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait());
ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
ASSERT_EQ(tier1_a_url0->id(), top->children().back()->id());
Remove(kSingleProfileIndex, top, top->children().size() - 1);
Move(kSingleProfileIndex, wired, tier1_b, 0);
Move(kSingleProfileIndex, porsche, bar, 3);
const BookmarkNode* tier3_b = AddFolder(
kSingleProfileIndex, tier2_b, 1, "tier3_b");
const BookmarkNode* leafs = AddURL(
kSingleProfileIndex, tier1_a, 0, "Toronto Maple Leafs",
GURL("http://mapleleafs.nhl.com"));
const BookmarkNode* wynn = AddURL(
kSingleProfileIndex, bar, 1, "Wynn", GURL("http://www.wynnlasvegas.com"));
Move(kSingleProfileIndex, wynn, tier3_b, 0);
Move(kSingleProfileIndex, leafs, tier3_b, 0);
// Wait for newly added bookmarks to sync.
ASSERT_TRUE(
UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait());
ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
// Only verify FakeServer data if FakeServer is being used.
// TODO(pvalenzuela): Use this style of verification in more tests once it is
// proven stable.
if (GetFakeServer())
VerifyBookmarkModelMatchesFakeServer(kSingleProfileIndex);
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, CommitLocalCreations) {
ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
// Starting state:
// other_node
// -> top
// -> tier1_a
// -> http://mail.google.com "tier1_a_url0"
// -> http://www.pandora.com "tier1_a_url1"
// -> http://www.facebook.com "tier1_a_url2"
// -> tier1_b
// -> http://www.nhl.com "tier1_b_url0"
const BookmarkNode* top = AddFolder(
kSingleProfileIndex, GetOtherNode(kSingleProfileIndex), 0, "top");
const BookmarkNode* tier1_a =
AddFolder(kSingleProfileIndex, top, 0, "tier1_a");
const BookmarkNode* tier1_b =
AddFolder(kSingleProfileIndex, top, 1, "tier1_b");
const BookmarkNode* tier1_a_url0 =
AddURL(kSingleProfileIndex, tier1_a, 0, "tier1_a_url0",
GURL("http://mail.google.com"));
const BookmarkNode* tier1_a_url1 =
AddURL(kSingleProfileIndex, tier1_a, 1, "tier1_a_url1",
GURL("http://www.pandora.com"));
const BookmarkNode* tier1_a_url2 =
AddURL(kSingleProfileIndex, tier1_a, 2, "tier1_a_url2",
GURL("http://www.facebook.com"));
const BookmarkNode* tier1_b_url0 =
AddURL(kSingleProfileIndex, tier1_b, 0, "tier1_b_url0",
GURL("http://www.nhl.com"));
EXPECT_TRUE(tier1_a_url0);
EXPECT_TRUE(tier1_a_url1);
EXPECT_TRUE(tier1_a_url2);
EXPECT_TRUE(tier1_b_url0);
// Setup sync, wait for its completion, and make sure changes were synced.
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(
UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait());
EXPECT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, InjectedBookmark) {
std::string title = "Montreal Canadiens";
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(title);
fake_server_->InjectEntity(bookmark_builder.BuildBookmark(
GURL("http://canadiens.nhl.com")));
DisableVerifier();
ASSERT_TRUE(SetupClients());
ASSERT_TRUE(SetupSync());
EXPECT_EQ(1u, CountBookmarksWithTitlesMatching(kSingleProfileIndex, title));
}
// Test that a client doesn't mutate the favicon data in the process
// of storing the favicon data from sync to the database or in the process
// of requesting data from the database for sync.
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
SetFaviconHiDPIDifferentCodec) {
// Set the supported scale factors to 1x and 2x such that
// BookmarkModel::GetFavicon() requests both 1x and 2x.
// 1x -> for sync, 2x -> for the UI.
std::vector<ui::ScaleFactor> supported_scale_factors;
supported_scale_factors.push_back(ui::SCALE_FACTOR_100P);
supported_scale_factors.push_back(ui::SCALE_FACTOR_200P);
ui::SetSupportedScaleFactors(supported_scale_factors);
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
const GURL page_url("http://www.google.com");
const GURL icon_url("http://www.google.com/favicon.ico");
const BookmarkNode* bookmark = AddURL(kSingleProfileIndex, "title", page_url);
// Simulate receiving a favicon from sync encoded by a different PNG encoder
// than the one native to the OS. This tests the PNG data is not decoded to
// SkBitmap (or any other image format) then encoded back to PNG on the path
// between sync and the database.
gfx::Image original_favicon = Create1xFaviconFromPNGFile(
"favicon_cocoa_png_codec.png");
ASSERT_FALSE(original_favicon.IsEmpty());
SetFavicon(kSingleProfileIndex, bookmark, icon_url, original_favicon,
bookmarks_helper::FROM_SYNC);
ASSERT_TRUE(
UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait());
ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
scoped_refptr<base::RefCountedMemory> original_favicon_bytes =
original_favicon.As1xPNGBytes();
gfx::Image final_favicon =
GetBookmarkModel(kSingleProfileIndex)->GetFavicon(bookmark);
scoped_refptr<base::RefCountedMemory> final_favicon_bytes =
final_favicon.As1xPNGBytes();
// Check that the data was not mutated from the original.
EXPECT_TRUE(original_favicon_bytes.get());
EXPECT_TRUE(original_favicon_bytes->Equals(final_favicon_bytes));
}
// Test that a client deletes favicons from sync when they have been removed
// from the local database.
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, DeleteFaviconFromSync) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
const GURL page_url("http://www.google.com");
const GURL icon_url("http://www.google.com/favicon.ico");
const BookmarkNode* bookmark = AddURL(kSingleProfileIndex, "title", page_url);
SetFavicon(0, bookmark, icon_url, CreateFavicon(SK_ColorWHITE),
bookmarks_helper::FROM_UI);
ASSERT_TRUE(
UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait());
ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
// Simulate receiving a favicon deletion from sync.
DeleteFaviconMappings(kSingleProfileIndex, bookmark,
bookmarks_helper::FROM_SYNC);
ASSERT_TRUE(
UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait());
ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
CheckHasNoFavicon(kSingleProfileIndex, page_url);
EXPECT_TRUE(
GetBookmarkModel(kSingleProfileIndex)->GetFavicon(bookmark).IsEmpty());
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
BookmarkAllNodesRemovedEvent) {
ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
// Starting state:
// other_node
// -> folder0
// -> tier1_a
// -> http://mail.google.com
// -> http://www.google.com
// -> http://news.google.com
// -> http://yahoo.com
// -> http://www.cnn.com
// bookmark_bar
// -> empty_folder
// -> folder1
// -> http://yahoo.com
// -> http://gmail.com
const BookmarkNode* folder0 = AddFolder(
kSingleProfileIndex, GetOtherNode(kSingleProfileIndex), 0, "folder0");
const BookmarkNode* tier1_a =
AddFolder(kSingleProfileIndex, folder0, 0, "tier1_a");
ASSERT_TRUE(AddURL(kSingleProfileIndex, folder0, 1, "News",
GURL("http://news.google.com")));
ASSERT_TRUE(AddURL(kSingleProfileIndex, folder0, 2, "Yahoo",
GURL("http://www.yahoo.com")));
ASSERT_TRUE(AddURL(kSingleProfileIndex, tier1_a, 0, "Gmai",
GURL("http://mail.google.com")));
ASSERT_TRUE(AddURL(kSingleProfileIndex, tier1_a, 1, "Google",
GURL("http://www.google.com")));
ASSERT_TRUE(AddURL(kSingleProfileIndex, GetOtherNode(kSingleProfileIndex), 1,
"CNN", GURL("http://www.cnn.com")));
ASSERT_TRUE(AddFolder(kSingleProfileIndex,
GetBookmarkBarNode(kSingleProfileIndex), 0,
"empty_folder"));
const BookmarkNode* folder1 =
AddFolder(kSingleProfileIndex, GetBookmarkBarNode(kSingleProfileIndex), 1,
"folder1");
ASSERT_TRUE(AddURL(kSingleProfileIndex, folder1, 0, "Yahoo",
GURL("http://www.yahoo.com")));
ASSERT_TRUE(AddURL(kSingleProfileIndex, GetBookmarkBarNode(0), 2, "Gmai",
GURL("http://gmail.com")));
// Set up sync, wait for its completion and verify that changes propagated.
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(
UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait());
ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
// Remove all bookmarks and wait for sync completion.
RemoveAll(kSingleProfileIndex);
ASSERT_TRUE(
UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait());
// Verify other node has no children now.
EXPECT_TRUE(GetOtherNode(kSingleProfileIndex)->children().empty());
EXPECT_TRUE(GetBookmarkBarNode(kSingleProfileIndex)->children().empty());
// Verify model matches verifier.
ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, DownloadDeletedBookmark) {
std::string title = "Patrick Star";
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(title);
fake_server_->InjectEntity(bookmark_builder.BuildBookmark(
GURL("http://en.wikipedia.org/wiki/Patrick_Star")));
DisableVerifier();
ASSERT_TRUE(SetupSync());
ASSERT_EQ(1u, CountBookmarksWithTitlesMatching(kSingleProfileIndex, title));
std::vector<sync_pb::SyncEntity> server_bookmarks =
GetFakeServer()->GetSyncEntitiesByModelType(syncer::BOOKMARKS);
ASSERT_EQ(1ul, server_bookmarks.size());
std::string entity_id = server_bookmarks[0].id_string();
std::unique_ptr<syncer::LoopbackServerEntity> tombstone(
syncer::PersistentTombstoneEntity::CreateNew(entity_id, std::string()));
GetFakeServer()->InjectEntity(std::move(tombstone));
const syncer::ModelTypeSet kBookmarksType(syncer::BOOKMARKS);
TriggerSyncForModelTypes(kSingleProfileIndex, kBookmarksType);
const int kExpectedCountAfterDeletion = 0;
ASSERT_TRUE(BookmarksTitleChecker(kSingleProfileIndex, title,
kExpectedCountAfterDeletion)
.Wait());
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
DownloadModifiedBookmark) {
std::string title = "Syrup";
GURL original_url = GURL("https://en.wikipedia.org/?title=Maple_syrup");
GURL updated_url = GURL("https://en.wikipedia.org/wiki/Xylem");
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(title);
fake_server_->InjectEntity(bookmark_builder.BuildBookmark(original_url));
DisableVerifier();
ASSERT_TRUE(SetupSync());
ASSERT_EQ(1u, CountBookmarksWithTitlesMatching(kSingleProfileIndex, title));
ASSERT_EQ(1u,
CountBookmarksWithUrlsMatching(kSingleProfileIndex, original_url));
ASSERT_EQ(0u,
CountBookmarksWithUrlsMatching(kSingleProfileIndex, updated_url));
std::vector<sync_pb::SyncEntity> server_bookmarks =
GetFakeServer()->GetSyncEntitiesByModelType(syncer::BOOKMARKS);
ASSERT_EQ(1ul, server_bookmarks.size());
std::string entity_id = server_bookmarks[0].id_string();
sync_pb::EntitySpecifics specifics = server_bookmarks[0].specifics();
sync_pb::BookmarkSpecifics* bookmark_specifics = specifics.mutable_bookmark();
bookmark_specifics->set_url(updated_url.spec());
ASSERT_TRUE(GetFakeServer()->ModifyEntitySpecifics(entity_id, specifics));
const syncer::ModelTypeSet kBookmarksType(syncer::BOOKMARKS);
TriggerSyncForModelTypes(kSingleProfileIndex, kBookmarksType);
ASSERT_TRUE(BookmarksUrlChecker(kSingleProfileIndex, updated_url, 1).Wait());
ASSERT_EQ(0u,
CountBookmarksWithUrlsMatching(kSingleProfileIndex, original_url));
ASSERT_EQ(1u, CountBookmarksWithTitlesMatching(kSingleProfileIndex, title));
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, DownloadBookmarkFolder) {
const std::string title = "Seattle Sounders FC";
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(title);
fake_server_->InjectEntity(bookmark_builder.BuildFolder());
DisableVerifier();
ASSERT_TRUE(SetupClients());
ASSERT_EQ(0u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
ASSERT_TRUE(SetupSync());
ASSERT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
DownloadLegacyBookmarkFolder) {
const std::string title = "Seattle Sounders FC";
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(title);
fake_server_->InjectEntity(bookmark_builder.BuildFolder(/*is_legacy=*/true));
DisableVerifier();
ASSERT_TRUE(SetupClients());
ASSERT_EQ(0u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
ASSERT_TRUE(SetupSync());
ASSERT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
}
// Legacy bookmark clients append a blank space to empty titles, ".", ".." tiles
// before committing them because historically they were illegal server titles.
// This test makes sure that this functionality is implemented for backward
// compatibility with legacy clients.
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
ShouldCommitBookmarksWithIllegalServerNames) {
ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
const std::vector<std::string> illegal_titles = {"", ".", ".."};
// Create 3 bookmarks under the bookmark bar with illegal titles.
for (const std::string& illegal_title : illegal_titles) {
ASSERT_TRUE(AddURL(kSingleProfileIndex, illegal_title,
GURL("http://www.google.com")));
}
// Wait till all entities are committed.
ASSERT_TRUE(
UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait());
// Collect the titles committed on the server.
std::vector<sync_pb::SyncEntity> entities =
fake_server_->GetSyncEntitiesByModelType(syncer::BOOKMARKS);
std::vector<std::string> committed_titles;
for (const sync_pb::SyncEntity& entity : entities) {
committed_titles.push_back(entity.specifics().bookmark().title());
}
// A space should have been appended to each illegal title before committing.
EXPECT_THAT(committed_titles,
testing::UnorderedElementsAre(" ", ". ", ".. "));
}
// This test the opposite functionality in the test above. Legacy bookmark
// clients omit a blank space from blank space title, ". ", ".. " tiles upon
// receiving the remote updates. An extra space has been appended during a
// commit because historically they were considered illegal server titles. This
// test makes sure that this functionality is implemented for backward
// compatibility with legacy clients.
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
ShouldCreateLocalBookmarksWithIllegalServerNames) {
const std::vector<std::string> illegal_titles = {"", ".", ".."};
// Create 3 bookmarks on the server under BookmarkBar with illegal server
// titles with a blank space appended to simulate a commit from a legacy
// client.
fake_server::EntityBuilderFactory entity_builder_factory;
for (const std::string& illegal_title : illegal_titles) {
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(illegal_title + " ");
fake_server_->InjectEntity(
bookmark_builder.BuildBookmark(GURL("http://www.google.com")));
}
ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
DisableVerifier();
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
// There should be bookmark with illegal title (without the appended space).
for (const std::string& illegal_title : illegal_titles) {
EXPECT_EQ(1u, CountBookmarksWithTitlesMatching(kSingleProfileIndex,
illegal_title));
}
}
// Legacy bookmark clients append a blank space to empty titles. This tests that
// this is respected when merging local and remote hierarchies.
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
ShouldTruncateBlanksWhenMatchingTitles) {
const std::string remote_blank_title = " ";
const std::string local_empty_title = "";
// Create a folder on the server under BookmarkBar with a title with a blank
// space.
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(remote_blank_title);
fake_server_->InjectEntity(bookmark_builder.BuildFolder());
DisableVerifier();
ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
// Create a folder on the client under BookmarkBar with an empty title.
const BookmarkNode* node =
AddFolder(kSingleProfileIndex, GetBookmarkBarNode(kSingleProfileIndex), 0,
local_empty_title);
ASSERT_TRUE(node);
ASSERT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex,
local_empty_title));
ASSERT_TRUE(SetupSync());
// There should be only one bookmark on the client. The remote node should
// have been merged with the local node and either the local or remote titles
// is picked.
EXPECT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex,
local_empty_title) +
CountFoldersWithTitlesMatching(kSingleProfileIndex,
remote_blank_title));
}
// Legacy bookmark clients truncate long titles up to 255 bytes. This tests that
// this is respected when merging local and remote hierarchies.
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
ShouldTruncateLongTitles) {
const std::string remote_truncated_title =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrst"
"uvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN"
"OPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefgh"
"ijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU";
const std::string local_full_title =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrst"
"uvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN"
"OPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefgh"
"ijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB"
"CDEFGHIJKLMNOPQRSTUVWXYZ";
// Create a folder on the server under BookmarkBar with a truncated title.
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(remote_truncated_title);
fake_server_->InjectEntity(bookmark_builder.BuildFolder());
DisableVerifier();
ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
// Create a folder on the client under BookmarkBar with a long title.
const BookmarkNode* node =
AddFolder(kSingleProfileIndex, GetBookmarkBarNode(kSingleProfileIndex), 0,
local_full_title);
ASSERT_TRUE(node);
ASSERT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex,
local_full_title));
ASSERT_TRUE(SetupSync());
// There should be only one bookmark on the client. The remote node should
// have been merged with the local node and either the local or remote title
// is picked.
EXPECT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex,
local_full_title) +
CountFoldersWithTitlesMatching(kSingleProfileIndex,
remote_truncated_title));
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
DownloadBookmarkFoldersWithPositions) {
const std::string title0 = "Folder left";
const std::string title1 = "Folder middle";
const std::string title2 = "Folder right";
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark0_builder =
entity_builder_factory.NewBookmarkEntityBuilder(title0);
bookmark0_builder.SetIndex(0);
fake_server::BookmarkEntityBuilder bookmark1_builder =
entity_builder_factory.NewBookmarkEntityBuilder(title1);
bookmark1_builder.SetIndex(1);
fake_server::BookmarkEntityBuilder bookmark2_builder =
entity_builder_factory.NewBookmarkEntityBuilder(title2);
bookmark2_builder.SetIndex(2);
fake_server_->InjectEntity(bookmark0_builder.BuildFolder());
fake_server_->InjectEntity(bookmark2_builder.BuildFolder());
fake_server_->InjectEntity(bookmark1_builder.BuildFolder());
DisableVerifier();
ASSERT_TRUE(SetupClients());
ASSERT_TRUE(SetupSync());
EXPECT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title0));
EXPECT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title1));
EXPECT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title2));
const BookmarkNode* bar = GetBookmarkBarNode(kSingleProfileIndex);
ASSERT_EQ(3u, bar->children().size());
EXPECT_EQ(base::ASCIIToUTF16(title0), bar->children()[0]->GetTitle());
EXPECT_EQ(base::ASCIIToUTF16(title1), bar->children()[1]->GetTitle());
EXPECT_EQ(base::ASCIIToUTF16(title2), bar->children()[2]->GetTitle());
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, E2E_ONLY(SanitySetup)) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
}
IN_PROC_BROWSER_TEST_P(
SingleClientBookmarksSyncTest,
RemoveRightAfterAddShouldNotSendCommitRequestsOrTombstones) {
base::HistogramTester histogram_tester;
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
// Add a folder and directly remove it.
ASSERT_NE(nullptr,
AddFolder(kSingleProfileIndex,
/*parent=*/GetBookmarkBarNode(kSingleProfileIndex),
/*index=*/0, "folder name"));
Remove(kSingleProfileIndex,
/*parent=*/GetBookmarkBarNode(kSingleProfileIndex), 0);
// Add another bookmark to make sure a full sync cycle completion.
ASSERT_NE(nullptr, AddURL(kSingleProfileIndex,
/*parent=*/GetOtherNode(kSingleProfileIndex),
/*index=*/0, "title", GURL("http://www.url.com")));
ASSERT_TRUE(
UpdatedProgressMarkerChecker(GetSyncService(kSingleProfileIndex)).Wait());
EXPECT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
// There should have been one creation and no deletions.
EXPECT_EQ(
1, histogram_tester.GetBucketCount("Sync.ModelTypeEntityChange3.BOOKMARK",
/*LOCAL_CREATION=*/1));
EXPECT_EQ(
0, histogram_tester.GetBucketCount("Sync.ModelTypeEntityChange3.BOOKMARK",
/*LOCAL_DELETION=*/0));
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
PRE_PersistProgressMarkerOnRestart) {
const std::string title = "Seattle Sounders FC";
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(title);
bookmark_builder.SetId(
syncer::LoopbackServerEntity::CreateId(syncer::BOOKMARKS, kBookmarkGuid));
fake_server_->InjectEntity(bookmark_builder.BuildFolder());
base::HistogramTester histogram_tester;
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_EQ(1u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
EXPECT_NE(
0, histogram_tester.GetBucketCount("Sync.ModelTypeEntityChange3.BOOKMARK",
/*REMOTE_INITIAL_UPDATE=*/5));
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
PersistProgressMarkerOnRestart) {
const std::string title = "Seattle Sounders FC";
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(title);
bookmark_builder.SetId(
syncer::LoopbackServerEntity::CreateId(syncer::BOOKMARKS, kBookmarkGuid));
fake_server_->InjectEntity(bookmark_builder.BuildFolder());
base::HistogramTester histogram_tester;
ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
ASSERT_EQ(1u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
#if defined(CHROMEOS)
// signin::SetRefreshTokenForPrimaryAccount() is needed on ChromeOS in order
// to get a non-empty refresh token on startup.
GetClient(0)->SignInPrimaryAccount();
#endif // defined(CHROMEOS)
ASSERT_TRUE(GetClient(kSingleProfileIndex)->AwaitEngineInitialization());
// After restart, the last sync cycle snapshot should be empty.
// Once a sync request happened (e.g. by a poll), that snapshot is populated.
// We use the following checker to simply wait for an non-empty snapshot.
EXPECT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
ASSERT_EQ(1u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
EXPECT_EQ(
0, histogram_tester.GetBucketCount("Sync.ModelTypeEntityChange3.BOOKMARK",
/*REMOTE_INITIAL_UPDATE=*/5));
}
// TODO(crbug.com/1012222): re-enable this test on all builders once flakiness
// is addressed.
#if defined(THREAD_SANITIZER)
#define MAYBE_ApplyRemoteCreationWithValidGUID \
DISABLED_ApplyRemoteCreationWithValidGUID
#else
#define MAYBE_ApplyRemoteCreationWithValidGUID ApplyRemoteCreationWithValidGUID
#endif
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
MAYBE_ApplyRemoteCreationWithValidGUID) {
// This test is only relevant for USS code path.
if (!base::FeatureList::IsEnabled(switches::kSyncUSSBookmarks))
return;
// Start syncing.
DisableVerifier();
ASSERT_TRUE(SetupSync());
ASSERT_EQ(0u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
// Create a bookmark folder with a valid GUID.
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder("Seattle Sounders FC");
// Issue remote creation with a valid GUID.
std::unique_ptr<syncer::LoopbackServerEntity> folder =
bookmark_builder.BuildFolder(/*is_legacy=*/false);
const std::string guid = folder.get()->GetSpecifics().bookmark().guid();
ASSERT_TRUE(base::IsValidGUID(guid));
ASSERT_FALSE(ContainsBookmarkNodeWithGUID(kSingleProfileIndex, guid));
fake_server_->InjectEntity(std::move(folder));
// A folder should have been added with the corresponding GUID.
EXPECT_TRUE(BookmarksGUIDChecker(kSingleProfileIndex, guid).Wait());
EXPECT_EQ(1u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
EXPECT_EQ(
guid,
GetBookmarkBarNode(kSingleProfileIndex)->children()[0].get()->guid());
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
ApplyRemoteCreationWithoutValidGUID) {
// This test is only relevant for USS code path.
if (!base::FeatureList::IsEnabled(switches::kSyncUSSBookmarks))
return;
// Start syncing.
DisableVerifier();
ASSERT_TRUE(SetupSync());
ASSERT_EQ(0u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
const std::string originator_client_item_id = base::GenerateGUID();
ASSERT_FALSE(ContainsBookmarkNodeWithGUID(kSingleProfileIndex,
originator_client_item_id));
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(
"Seattle Sounders FC", originator_client_item_id);
// Issue remote creation without a valid GUID but with a valid
// originator_client_item_id.
std::unique_ptr<syncer::LoopbackServerEntity> folder =
bookmark_builder.BuildFolder(/*is_legacy=*/true);
ASSERT_TRUE(folder.get()->GetSpecifics().bookmark().guid().empty());
fake_server_->InjectEntity(std::move(folder));
// A bookmark folder should have been added with the originator_client_item_id
// as the GUID.
EXPECT_TRUE(
BookmarksGUIDChecker(kSingleProfileIndex, originator_client_item_id)
.Wait());
EXPECT_EQ(1u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
EXPECT_EQ(
originator_client_item_id,
GetBookmarkBarNode(kSingleProfileIndex)->children()[0].get()->guid());
}
#if defined(THREAD_SANITIZER) || defined(ADDRESS_SANITIZER)
#define MAYBE_ApplyRemoteCreationWithoutValidGUIDOrOCII \
DISABLED_ApplyRemoteCreationWithoutValidGUIDOrOCII
#else
#define MAYBE_ApplyRemoteCreationWithoutValidGUIDOrOCII \
ApplyRemoteCreationWithoutValidGUIDOrOCII
#endif
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
MAYBE_ApplyRemoteCreationWithoutValidGUIDOrOCII) {
// This test is only relevant for USS code path.
if (!base::FeatureList::IsEnabled(switches::kSyncUSSBookmarks))
return;
// Start syncing.
DisableVerifier();
ASSERT_TRUE(SetupSync());
ASSERT_EQ(0u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
GURL url = GURL("http://foo.com");
const std::string originator_client_item_id = "INVALID OCII";
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(
"Seattle Sounders FC", originator_client_item_id);
// Issue remote creation without a valid GUID or a valid
// originator_client_item_id.
std::unique_ptr<syncer::LoopbackServerEntity> bookmark =
bookmark_builder.BuildBookmark(url, /*is_legacy=*/true);
ASSERT_TRUE(bookmark.get()->GetSpecifics().bookmark().guid().empty());
fake_server_->InjectEntity(std::move(bookmark));
// A bookmark should have been added with a newly assigned valid GUID.
EXPECT_TRUE(BookmarksUrlChecker(kSingleProfileIndex, url, 1).Wait());
EXPECT_EQ(1u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
EXPECT_TRUE(base::IsValidGUID(
GetBookmarkBarNode(kSingleProfileIndex)->children()[0].get()->guid()));
EXPECT_FALSE(ContainsBookmarkNodeWithGUID(kSingleProfileIndex,
originator_client_item_id));
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
MergeRemoteCreationWithValidGUID) {
// This test is only relevant for USS code path.
if (!base::FeatureList::IsEnabled(switches::kSyncUSSBookmarks))
return;
const GURL url = GURL("http://www.foo.com");
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder("Seattle Sounders FC");
// Create bookmark in server with a valid GUID.
std::unique_ptr<syncer::LoopbackServerEntity> bookmark =
bookmark_builder.BuildBookmark(url, /*is_legacy=*/false);
const std::string guid = bookmark.get()->GetSpecifics().bookmark().guid();
ASSERT_TRUE(base::IsValidGUID(guid));
fake_server_->InjectEntity(std::move(bookmark));
// Start syncing.
DisableVerifier();
ASSERT_TRUE(SetupClients());
ASSERT_EQ(0u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
ASSERT_TRUE(SetupSync());
// A bookmark should have been added with the corresponding GUID.
EXPECT_TRUE(BookmarksUrlChecker(kSingleProfileIndex, url, 1).Wait());
EXPECT_EQ(1u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
EXPECT_TRUE(ContainsBookmarkNodeWithGUID(kSingleProfileIndex, guid));
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
MergeRemoteCreationWithoutValidGUID) {
// This test is only relevant for USS code path.
if (!base::FeatureList::IsEnabled(switches::kSyncUSSBookmarks))
return;
const GURL url = GURL("http://www.foo.com");
const std::string originator_client_item_id = base::GenerateGUID();
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(
"Seattle Sounders FC", originator_client_item_id);
// Create bookmark in server without a valid GUID but with a valid
// originator_client_item_id.
std::unique_ptr<syncer::LoopbackServerEntity> bookmark =
bookmark_builder.BuildBookmark(url, /*is_legacy=*/true);
ASSERT_TRUE(bookmark.get()->GetSpecifics().bookmark().guid().empty());
fake_server_->InjectEntity(std::move(bookmark));
// Start syncing.
DisableVerifier();
ASSERT_TRUE(SetupClients());
ASSERT_EQ(0u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
ASSERT_TRUE(SetupSync());
// A bookmark should have been added with the originator_client_item_id as the
// GUID.
EXPECT_TRUE(BookmarksUrlChecker(kSingleProfileIndex, url, 1).Wait());
EXPECT_EQ(1u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
EXPECT_TRUE(ContainsBookmarkNodeWithGUID(kSingleProfileIndex,
originator_client_item_id));
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
MergeRemoteCreationWithoutValidGUIDOrOCII) {
// This test is only relevant for USS code path.
if (!base::FeatureList::IsEnabled(switches::kSyncUSSBookmarks))
return;
const GURL url = GURL("http://www.foo.com");
const std::string originator_client_item_id = "INVALID OCII";
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(
"Seattle Sounders FC", originator_client_item_id);
// Create bookmark in server without a valid GUID and without a valid
// originator_client_item_id.
std::unique_ptr<syncer::LoopbackServerEntity> bookmark =
bookmark_builder.BuildBookmark(url, /*is_legacy=*/true);
ASSERT_TRUE(bookmark.get()->GetSpecifics().bookmark().guid().empty());
fake_server_->InjectEntity(std::move(bookmark));
// Start syncing.
DisableVerifier();
ASSERT_TRUE(SetupClients());
ASSERT_EQ(0u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
ASSERT_TRUE(SetupSync());
// A bookmark should have been added with a newly assigned valid GUID.
EXPECT_TRUE(BookmarksUrlChecker(kSingleProfileIndex, url, 1).Wait());
EXPECT_EQ(1u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
EXPECT_TRUE(base::IsValidGUID(
GetBookmarkBarNode(kSingleProfileIndex)->children()[0].get()->guid()));
EXPECT_FALSE(ContainsBookmarkNodeWithGUID(kSingleProfileIndex,
originator_client_item_id));
}
// TODO(crbug.com/1012223): re-enable this test on all builders once flakiness
// is addressed.
#if defined(THREAD_SANITIZER)
#define MAYBE_ApplyRemoteUpdateWithValidGUID \
DISABLED_ApplyRemoteUpdateWithValidGUID
#else
#define MAYBE_ApplyRemoteUpdateWithValidGUID ApplyRemoteUpdateWithValidGUID
#endif
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
MAYBE_ApplyRemoteUpdateWithValidGUID) {
// This test is only relevant for USS code path and when BookmarkNode GUID
// replacement is enabled.
if (!base::FeatureList::IsEnabled(switches::kSyncUSSBookmarks))
return;
// Create a bookmark.
const GURL url = GURL("https://foo.com");
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder("Seattle Sounders FC");
// Issue remote creation.
std::unique_ptr<syncer::LoopbackServerEntity> bookmark =
bookmark_builder.BuildBookmark(url, /*is_legacy=*/false);
const std::string old_guid = bookmark.get()->GetSpecifics().bookmark().guid();
fake_server_->InjectEntity(std::move(bookmark));
// Start syncing.
DisableVerifier();
ASSERT_TRUE(SetupSync());
// Created bookmark should be in the model.
ASSERT_TRUE(BookmarksUrlChecker(kSingleProfileIndex, url, 1).Wait());
ASSERT_EQ(1u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
ASSERT_EQ(
old_guid,
GetBookmarkBarNode(kSingleProfileIndex)->children()[0].get()->guid());
// Change bookmark GUID in the server to simulate an update from another
// client that has a differente GUID assigned to this bookmark. This can
// happen because every client assigns GUIDs independently when the GUID is
// missing, and the values should eventually be consistent across clients.
std::vector<sync_pb::SyncEntity> server_bookmarks =
GetFakeServer()->GetSyncEntitiesByModelType(syncer::BOOKMARKS);
ASSERT_EQ(1ul, server_bookmarks.size());
sync_pb::EntitySpecifics specifics = server_bookmarks[0].specifics();
const std::string new_guid = base::GenerateGUID();
specifics.mutable_bookmark()->set_guid(new_guid);
ASSERT_TRUE(GetFakeServer()->ModifyEntitySpecifics(
/*entity_id=*/server_bookmarks[0].id_string(), specifics));
// The bookmark GUID should have been updated with the corresponding value.
EXPECT_TRUE(BookmarksGUIDChecker(kSingleProfileIndex, new_guid).Wait());
EXPECT_EQ(1u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
EXPECT_EQ(
new_guid,
GetBookmarkBarNode(kSingleProfileIndex)->children()[0].get()->guid());
}
IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
MergeRemoteUpdateWithValidGUID) {
// This test is only relevant for USS code path.
if (!base::FeatureList::IsEnabled(switches::kSyncUSSBookmarks))
return;
DisableVerifier();
ASSERT_TRUE(SetupClients());
// Create a local bookmark folder.
const std::string title = "Seattle Sounders FC";
const BookmarkNode* local_folder = AddFolder(
kSingleProfileIndex, GetBookmarkBarNode(kSingleProfileIndex), 0, title);
const std::string old_guid = local_folder->guid();
ASSERT_TRUE(local_folder);
ASSERT_TRUE(BookmarksGUIDChecker(kSingleProfileIndex, old_guid).Wait());
ASSERT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
// Create an equivalent remote folder.
fake_server::EntityBuilderFactory entity_builder_factory;
fake_server::BookmarkEntityBuilder bookmark_builder =
entity_builder_factory.NewBookmarkEntityBuilder(title);
std::unique_ptr<syncer::LoopbackServerEntity> remote_folder =
bookmark_builder.BuildFolder(/*is_legacy=*/false);
const std::string new_guid = remote_folder->GetSpecifics().bookmark().guid();
fake_server_->InjectEntity(std::move(remote_folder));
// Start syncing.
ASSERT_EQ(1u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
ASSERT_TRUE(SetupSync());
// The folder GUID should have been updated with the corresponding value.
EXPECT_TRUE(BookmarksGUIDChecker(kSingleProfileIndex, new_guid).Wait());
EXPECT_FALSE(ContainsBookmarkNodeWithGUID(kSingleProfileIndex, old_guid));
EXPECT_EQ(1u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
}
INSTANTIATE_TEST_SUITE_P(USS,
SingleClientBookmarksSyncTest,
::testing::Values(false, true));
} // namespace