// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/services/device_sync/cryptauth_device_registry_impl.h"
#include <map>
#include <memory>
#include <string>
#include <utility>
#include "base/no_destructor.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chromeos/ash/components/multidevice/software_feature.h"
#include "chromeos/ash/components/multidevice/software_feature_state.h"
#include "chromeos/ash/services/device_sync/cryptauth_device_registry.h"
#include "chromeos/ash/services/device_sync/pref_names.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_v2_test_util.h"
#include "chromeos/ash/services/device_sync/value_string_encoding.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash::device_sync {
namespace {
const char kInstanceId0[] = "instance_id_0";
const char kInstanceId1[] = "instance_id_1";
} // namespace
class DeviceSyncCryptAuthDeviceRegistryImplTest : public testing::Test {
public:
DeviceSyncCryptAuthDeviceRegistryImplTest(
const DeviceSyncCryptAuthDeviceRegistryImplTest&) = delete;
DeviceSyncCryptAuthDeviceRegistryImplTest& operator=(
const DeviceSyncCryptAuthDeviceRegistryImplTest&) = delete;
protected:
DeviceSyncCryptAuthDeviceRegistryImplTest() = default;
~DeviceSyncCryptAuthDeviceRegistryImplTest() override = default;
void SetUp() override {
CryptAuthDeviceRegistryImpl::RegisterPrefs(pref_service_.registry());
}
void CreateDeviceRegistry() {
device_registry_ =
CryptAuthDeviceRegistryImpl::Factory::Create(&pref_service_);
}
const CryptAuthDevice& GetDeviceForTest(size_t index) const {
static const base::NoDestructor<std::vector<CryptAuthDevice>> devices([] {
const char kDeviceName0[] = "device_name_0";
const char kDeviceName1[] = "device_name_1";
const char kDeviceBetterTogetherPublicKey0[] =
"device_better_together_public_key_0";
const char kDeviceBetterTogetherPublicKey1[] =
"device_better_together_public_key_1";
const base::Time kLastUpdateTime0 =
base::Time::FromSecondsSinceUnixEpoch(100);
const base::Time kLastUpdateTime1 =
base::Time::FromSecondsSinceUnixEpoch(200);
const std::map<multidevice::SoftwareFeature,
multidevice::SoftwareFeatureState>
kFakeFeatureStates0 = {
{multidevice::SoftwareFeature::kBetterTogetherClient,
multidevice::SoftwareFeatureState::kEnabled}};
const std::map<multidevice::SoftwareFeature,
multidevice::SoftwareFeatureState>
kFakeFeatureStates1 = {
{multidevice::SoftwareFeature::kBetterTogetherHost,
multidevice::SoftwareFeatureState::kEnabled}};
return std::vector<CryptAuthDevice>{
CryptAuthDevice(kInstanceId0, kDeviceName0,
kDeviceBetterTogetherPublicKey0, kLastUpdateTime0,
cryptauthv2::GetBetterTogetherDeviceMetadataForTest(),
kFakeFeatureStates0),
CryptAuthDevice(kInstanceId1, kDeviceName1,
kDeviceBetterTogetherPublicKey1, kLastUpdateTime1,
std::nullopt /* better_together_device_metadata */,
kFakeFeatureStates1)};
}());
EXPECT_LT(index, devices->size());
return devices->at(index);
}
base::Value::Dict AsDictionary(
const CryptAuthDeviceRegistry::InstanceIdToDeviceMap& devices) const {
base::Value::Dict dict;
for (const std::pair<std::string, CryptAuthDevice>& id_device_pair :
devices) {
dict.Set(util::EncodeAsString(id_device_pair.first),
id_device_pair.second.AsDictionary());
}
return dict;
}
void VerifyDeviceRegistry(
const CryptAuthDeviceRegistry::InstanceIdToDeviceMap& expected_devices) {
EXPECT_EQ(expected_devices, device_registry()->instance_id_to_device_map());
// Verify pref.
EXPECT_EQ(AsDictionary(expected_devices),
pref_service_.GetDict(prefs::kCryptAuthDeviceRegistry));
}
PrefService* pref_service() { return &pref_service_; }
CryptAuthDeviceRegistry* device_registry() { return device_registry_.get(); }
private:
TestingPrefServiceSimple pref_service_;
std::unique_ptr<CryptAuthDeviceRegistry> device_registry_;
};
TEST_F(DeviceSyncCryptAuthDeviceRegistryImplTest, AddAndGetDevices) {
CreateDeviceRegistry();
EXPECT_TRUE(device_registry()->AddDevice(GetDeviceForTest(0)));
EXPECT_FALSE(device_registry()->AddDevice(GetDeviceForTest(0)));
EXPECT_TRUE(device_registry()->AddDevice(GetDeviceForTest(1)));
VerifyDeviceRegistry(
{{kInstanceId0, GetDeviceForTest(0)},
{kInstanceId1, GetDeviceForTest(1)}} /* expected_devices */);
EXPECT_EQ(GetDeviceForTest(0), *device_registry()->GetDevice(kInstanceId0));
EXPECT_EQ(GetDeviceForTest(1), *device_registry()->GetDevice(kInstanceId1));
}
TEST_F(DeviceSyncCryptAuthDeviceRegistryImplTest, OverwriteDevice) {
CreateDeviceRegistry();
EXPECT_TRUE(device_registry()->AddDevice(GetDeviceForTest(0)));
EXPECT_EQ(GetDeviceForTest(0), *device_registry()->GetDevice(kInstanceId0));
CryptAuthDevice device_with_same_instance_id(
kInstanceId0, "name", "key", base::Time::FromSecondsSinceUnixEpoch(5000),
cryptauthv2::BetterTogetherDeviceMetadata(), {});
EXPECT_TRUE(device_registry()->AddDevice(device_with_same_instance_id));
EXPECT_EQ(device_with_same_instance_id,
*device_registry()->GetDevice(kInstanceId0));
}
TEST_F(DeviceSyncCryptAuthDeviceRegistryImplTest, OverwriteRegistry) {
CreateDeviceRegistry();
CryptAuthDeviceRegistry::InstanceIdToDeviceMap old_devices = {
{kInstanceId0, GetDeviceForTest(0)}};
EXPECT_TRUE(device_registry()->SetRegistry(old_devices));
VerifyDeviceRegistry(old_devices);
EXPECT_FALSE(device_registry()->SetRegistry(old_devices));
CryptAuthDeviceRegistry::InstanceIdToDeviceMap new_devices = {
{kInstanceId1, GetDeviceForTest(1)}};
EXPECT_TRUE(device_registry()->SetRegistry(new_devices));
VerifyDeviceRegistry(new_devices);
}
TEST_F(DeviceSyncCryptAuthDeviceRegistryImplTest, DeleteDevice) {
CreateDeviceRegistry();
EXPECT_TRUE(device_registry()->AddDevice(GetDeviceForTest(0)));
VerifyDeviceRegistry(
{{kInstanceId0, GetDeviceForTest(0)}} /* expected_devices */);
EXPECT_TRUE(device_registry()->DeleteDevice(kInstanceId0));
EXPECT_FALSE(device_registry()->GetDevice(kInstanceId0));
VerifyDeviceRegistry({} /* expected_devices */);
EXPECT_FALSE(device_registry()->DeleteDevice(kInstanceId0));
}
TEST_F(DeviceSyncCryptAuthDeviceRegistryImplTest, PopulateRegistryFromPref) {
CryptAuthDeviceRegistry::InstanceIdToDeviceMap expected_devices = {
{kInstanceId0, GetDeviceForTest(0)}, {kInstanceId1, GetDeviceForTest(1)}};
pref_service()->SetDict(prefs::kCryptAuthDeviceRegistry,
AsDictionary(expected_devices));
CreateDeviceRegistry();
VerifyDeviceRegistry(expected_devices);
}
} // namespace ash::device_sync