blob: 3dcfebbd6777d1ece72803a2367e8e0eecf96831 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/browser_sync/sync_internals_message_handler.h"
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/gmock_move_support.h"
#include "base/test/task_environment.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/sync/model/type_entities_count.h"
#include "components/sync/service/sync_internals_util.h"
#include "components/sync/service/sync_service.h"
#include "components/sync/test/mock_sync_invalidations_service.h"
#include "components/sync/test/mock_sync_service.h"
#include "components/sync_user_events/fake_user_event_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using syncer::sync_ui_util::kGetAllNodes;
using syncer::sync_ui_util::kOnAboutInfoUpdated;
using syncer::sync_ui_util::kOnEntityCountsUpdated;
using syncer::sync_ui_util::kRequestDataAndRegisterForUpdates;
using syncer::sync_ui_util::kRequestStart;
using syncer::sync_ui_util::kWriteUserEvent;
using testing::_;
using testing::ElementsAre;
using testing::Return;
namespace browser_sync {
namespace {
const char kChannel[] = "canary";
// Matches a given base::ValueView against `dict`.
MATCHER_P(ValueViewMatchesDict, dict, "") {
base::Value value = arg.ToValue();
return value.is_dict() && value.GetDict() == dict;
}
class MockDelegate : public SyncInternalsMessageHandler::Delegate {
public:
MockDelegate() = default;
~MockDelegate() override = default;
MOCK_METHOD(void,
SendEventToPage,
(std::string_view, base::span<const base::ValueView>),
(override));
MOCK_METHOD(void,
ResolvePageCallback,
(const base::ValueView, const base::ValueView),
(override));
};
class SyncInternalsMessageHandlerTest : public testing::Test {
public:
SyncInternalsMessageHandlerTest() {
ON_CALL(mock_sync_service_, GetEntityCountsForDebugging)
.WillByDefault(base::test::RunCallback<0>(
syncer::TypeEntitiesCount(syncer::PASSWORDS)));
}
SyncInternalsMessageHandlerTest(const SyncInternalsMessageHandlerTest&) =
delete;
SyncInternalsMessageHandlerTest& operator=(
const SyncInternalsMessageHandlerTest&) = delete;
~SyncInternalsMessageHandlerTest() override = default;
MockDelegate* mock_delegate() { return &mock_delegate_; }
signin::IdentityTestEnvironment* identity_test_environment() {
return &identity_test_environment_;
}
syncer::MockSyncService* mock_sync_service() { return &mock_sync_service_; }
syncer::MockSyncInvalidationsService* mock_sync_invalidations_service() {
return &mock_sync_invalidations_service_;
}
syncer::FakeUserEventService* fake_user_event_service() {
return &fake_user_event_service_;
}
SyncInternalsMessageHandler* handler() { return handler_.get(); }
int get_about_sync_data_call_count() const {
return get_about_sync_data_dall_count_;
}
void ResetHandler() { handler_.reset(); }
// Fake return value for sync_ui_util::ConstructAboutInformation().
const base::Value::Dict kAboutInformation =
base::Value::Dict().Set("some_sync_state", "some_value");
private:
// Returns copies of the same constant dictionary, |kAboutInformation|.
base::Value::Dict ConstructFakeAboutInformation(syncer::SyncService* service,
const std::string& channel) {
++get_about_sync_data_dall_count_;
return kAboutInformation.Clone();
}
// SingleThreadTaskEnvironment is needed for IdentityTestEnvironment.
base::test::SingleThreadTaskEnvironment task_environment_;
MockDelegate mock_delegate_;
signin::IdentityTestEnvironment identity_test_environment_;
syncer::MockSyncService mock_sync_service_;
syncer::MockSyncInvalidationsService mock_sync_invalidations_service_;
syncer::FakeUserEventService fake_user_event_service_;
std::unique_ptr<SyncInternalsMessageHandler> handler_ =
std::make_unique<SyncInternalsMessageHandler>(
&mock_delegate_,
base::BindRepeating(
&SyncInternalsMessageHandlerTest::ConstructFakeAboutInformation,
base::Unretained(this)),
identity_test_environment_.identity_manager(),
&mock_sync_service_,
&mock_sync_invalidations_service_,
&fake_user_event_service_,
kChannel);
int get_about_sync_data_dall_count_ = 0;
};
TEST_F(SyncInternalsMessageHandlerTest, AddRemoveObservers) {
EXPECT_CALL(*mock_sync_service(), AddObserver);
EXPECT_CALL(*mock_sync_service(), RemoveObserver).Times(0);
handler()
->GetMessageHandlerMap()
.at(kRequestDataAndRegisterForUpdates)
.Run(base::Value::List());
testing::Mock::VerifyAndClearExpectations(mock_sync_service());
EXPECT_CALL(*mock_sync_service(), AddObserver).Times(0);
EXPECT_CALL(*mock_sync_service(), RemoveObserver);
ResetHandler();
}
TEST_F(SyncInternalsMessageHandlerTest, AddRemoveObserversDisableMessages) {
EXPECT_CALL(*mock_sync_service(), AddObserver);
EXPECT_CALL(*mock_sync_service(), RemoveObserver).Times(0);
handler()
->GetMessageHandlerMap()
.at(kRequestDataAndRegisterForUpdates)
.Run(base::Value::List());
testing::Mock::VerifyAndClearExpectations(mock_sync_service());
EXPECT_CALL(*mock_sync_service(), AddObserver).Times(0);
EXPECT_CALL(*mock_sync_service(), RemoveObserver);
handler()->DisableMessagesToPage();
testing::Mock::VerifyAndClearExpectations(mock_sync_service());
// Deregistration should not repeat, no counts should increase.
EXPECT_CALL(*mock_sync_service(), AddObserver).Times(0);
EXPECT_CALL(*mock_sync_service(), RemoveObserver).Times(0);
ResetHandler();
}
TEST_F(SyncInternalsMessageHandlerTest, AddRemoveObserversSyncDisabled) {
// Simulate completely disabling sync by flag or other mechanism.
auto handler = std::make_unique<SyncInternalsMessageHandler>(
mock_delegate(),
base::BindLambdaForTesting([&](syncer::SyncService*, const std::string&) {
return kAboutInformation.Clone();
}),
identity_test_environment()->identity_manager(),
/*sync_service=*/nullptr, mock_sync_invalidations_service(),
fake_user_event_service(), kChannel);
handler->GetMessageHandlerMap()
.at(kRequestDataAndRegisterForUpdates)
.Run(base::Value::List());
handler->DisableMessagesToPage();
// Cannot verify observer methods on sync services were not called, because
// there is no sync service. Rather, we're just making sure the handler hasn't
// performed any invalid operations when the sync service is missing.
}
TEST_F(SyncInternalsMessageHandlerTest, HandleGetAllNodes) {
base::OnceCallback<void(base::Value::List)> get_all_nodes_callback;
ON_CALL(*mock_sync_service(), GetAllNodesForDebugging)
.WillByDefault(MoveArg<0>(&get_all_nodes_callback));
handler()
->GetMessageHandlerMap()
.at(kGetAllNodes)
.Run(base::Value::List().Append("getAllNodes_0"));
EXPECT_CALL(*mock_delegate(), ResolvePageCallback);
std::move(get_all_nodes_callback).Run(base::Value::List());
testing::Mock::VerifyAndClearExpectations(mock_delegate());
handler()
->GetMessageHandlerMap()
.at(kGetAllNodes)
.Run(base::Value::List().Append("getAllNodes_1"));
// This breaks the weak ref the callback is hanging onto. Which results in
// the call count not incrementing.
handler()->DisableMessagesToPage();
EXPECT_CALL(*mock_delegate(), ResolvePageCallback).Times(0);
std::move(get_all_nodes_callback).Run(base::Value::List());
testing::Mock::VerifyAndClearExpectations(mock_delegate());
handler()
->GetMessageHandlerMap()
.at(kGetAllNodes)
.Run(base::Value::List().Append("getAllNodes_2"));
EXPECT_CALL(*mock_delegate(), ResolvePageCallback);
std::move(get_all_nodes_callback).Run(base::Value::List());
}
TEST_F(SyncInternalsMessageHandlerTest, SendAboutInfo) {
EXPECT_CALL(
*mock_delegate(),
SendEventToPage(kOnAboutInfoUpdated, ElementsAre(ValueViewMatchesDict(
std::cref(kAboutInformation)))));
EXPECT_CALL(*mock_delegate(), SendEventToPage(kOnEntityCountsUpdated, _));
static_cast<syncer::SyncServiceObserver*>(handler())->OnStateChanged(
mock_sync_service());
EXPECT_EQ(1, get_about_sync_data_call_count());
}
TEST_F(SyncInternalsMessageHandlerTest, WriteUserEvent) {
handler()
->GetMessageHandlerMap()
.at(kWriteUserEvent)
.Run(base::Value::List().Append("1000000000000000000").Append("-1"));
ASSERT_EQ(1u, fake_user_event_service()->GetRecordedUserEvents().size());
const sync_pb::UserEventSpecifics& event =
*fake_user_event_service()->GetRecordedUserEvents().begin();
EXPECT_EQ(sync_pb::UserEventSpecifics::kTestEvent, event.event_case());
EXPECT_EQ(1000000000000000000, event.event_time_usec());
EXPECT_EQ(-1, event.navigation_id());
}
TEST_F(SyncInternalsMessageHandlerTest, WriteUserEventBadParse) {
handler()
->GetMessageHandlerMap()
.at(kWriteUserEvent)
.Run(base::Value::List().Append("123abc").Append("abcde"));
ASSERT_EQ(1u, fake_user_event_service()->GetRecordedUserEvents().size());
const sync_pb::UserEventSpecifics& event =
*fake_user_event_service()->GetRecordedUserEvents().begin();
EXPECT_EQ(sync_pb::UserEventSpecifics::kTestEvent, event.event_case());
EXPECT_EQ(0, event.event_time_usec());
EXPECT_EQ(0, event.navigation_id());
}
TEST_F(SyncInternalsMessageHandlerTest, WriteUserEventBlank) {
handler()
->GetMessageHandlerMap()
.at(kWriteUserEvent)
.Run(base::Value::List().Append("").Append(""));
ASSERT_EQ(1u, fake_user_event_service()->GetRecordedUserEvents().size());
const sync_pb::UserEventSpecifics& event =
*fake_user_event_service()->GetRecordedUserEvents().begin();
EXPECT_EQ(sync_pb::UserEventSpecifics::kTestEvent, event.event_case());
EXPECT_TRUE(event.has_event_time_usec());
EXPECT_EQ(0, event.event_time_usec());
// Should not have a navigation_id because that means something different to
// the UserEvents logic.
EXPECT_FALSE(event.has_navigation_id());
}
TEST_F(SyncInternalsMessageHandlerTest, WriteUserEventZero) {
handler()
->GetMessageHandlerMap()
.at(kWriteUserEvent)
.Run(base::Value::List().Append("0").Append("0"));
ASSERT_EQ(1u, fake_user_event_service()->GetRecordedUserEvents().size());
const sync_pb::UserEventSpecifics& event =
*fake_user_event_service()->GetRecordedUserEvents().begin();
EXPECT_EQ(sync_pb::UserEventSpecifics::kTestEvent, event.event_case());
EXPECT_TRUE(event.has_event_time_usec());
EXPECT_EQ(0, event.event_time_usec());
// Should have a navigation_id, even though the value is 0.
EXPECT_TRUE(event.has_navigation_id());
EXPECT_EQ(0, event.navigation_id());
}
#if !BUILDFLAG(IS_CHROMEOS)
TEST_F(SyncInternalsMessageHandlerTest, RequestStart) {
identity_test_environment()->MakePrimaryAccountAvailable(
"foo@gmail.com", signin::ConsentLevel::kSignin);
EXPECT_CALL(*mock_sync_service()->GetMockUserSettings(),
SetInitialSyncFeatureSetupComplete);
handler()->GetMessageHandlerMap().at(kRequestStart).Run(base::Value::List());
CoreAccountInfo account_info =
identity_test_environment()->identity_manager()->GetPrimaryAccountInfo(
signin::ConsentLevel::kSync);
EXPECT_FALSE(account_info.IsEmpty());
EXPECT_EQ(account_info.email, "foo@gmail.com");
}
#endif // !BUILDFLAG(IS_CHROMEOS)
} // namespace
} // namespace browser_sync