chromium/chrome/browser/ash/policy/core/device_policy_decoder_unittest.cc

// Copyright 2018 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/ash/policy/core/device_policy_decoder.h"

#include <memory>
#include <vector>

#include "base/functional/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/ash/policy/core/device_local_account.h"
#include "chromeos/ash/components/policy/weekly_time/weekly_time.h"
#include "chromeos/ash/components/policy/weekly_time/weekly_time_interval.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
#include "components/policy/core/common/device_local_account_type.h"
#include "components/policy/core/common/policy_bundle.h"
#include "components/policy/policy_constants.h"
#include "components/policy/proto/chrome_device_policy.pb.h"
#include "components/strings/grit/components_strings.h"
#include "policy_common_definitions.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"

namespace em = enterprise_management;

namespace policy {

namespace {

constexpr char kInvalidJson[] = R"({"foo": "bar")";
constexpr char16_t kInvalidJsonParsingError[] =
    u"Policy parsing error: Invalid JSON string: Line: 1, column: 14, Syntax "
    u"error.";

constexpr char kInvalidPolicyName[] = "invalid-policy-name";

constexpr char kWallpaperJson[] = R"({
      "url": "https://example.com/device_wallpaper.jpg",
      "hash": "examplewallpaperhash"
    })";

constexpr char kWallpaperJsonInvalidValue[] = R"({
      "url": 123,
      "hash": "examplewallpaperhash"
    })";

constexpr char kWallpaperJsonUnknownProperty[] = R"({
    "url": "https://example.com/device_wallpaper.jpg",
    "hash": "examplewallpaperhash",
    "unknown-field": "random-value"
  })";

constexpr char kWallpaperUrlPropertyName[] = "url";
constexpr char kWallpaperUrlPropertyValue[] =
    "https://example.com/device_wallpaper.jpg";
constexpr char kWallpaperHashPropertyName[] = "hash";
constexpr char kWallpaperHashPropertyValue[] = "examplewallpaperhash";
constexpr char kValidBluetoothServiceUUID4[] = "0x1124";
constexpr char kValidBluetoothServiceUUID8[] = "0000180F";
constexpr char kValidBluetoothServiceUUID32[] =
    "00002A00-0000-1000-8000-00805F9B34FB";
constexpr char kValidBluetoothServiceUUIDList[] =
    "[\"0x1124\", \"0000180F\", \"00002A00-0000-1000-8000-00805F9B34FB\"]";
constexpr char kInvalidBluetoothServiceUUIDList[] = "[\"wrong-uuid\"]";

constexpr char kDeviceLocalAccountKioskAccountId[] = "kiosk_account_id";

constexpr char kValidDeviceWeeklyScheduledSuspendList[] = R"([
    {
      "start": {
        "day_of_week": "MONDAY",
        "time": 64800000
      },
      "end": {
        "day_of_week": "TUESDAY",
        "time": 28800000
      }
    },
    {
      "start": {
        "day_of_week": "FRIDAY",
        "time": 75600000
      },
      "end": {
        "day_of_week": "MONDAY",
        "time": 25200000
      }
    }
])";

constexpr char kValidDeviceRestrictionScheduleJson[] = R"([
  {
    "start": {
        "day_of_week": "WEDNESDAY",
        "milliseconds_since_midnight": 43200000
    },
    "end": {
        "day_of_week": "WEDNESDAY",
        "milliseconds_since_midnight": 75600000
    }
  },
  {
    "start": {
        "day_of_week": "FRIDAY",
        "milliseconds_since_midnight": 64800000
    },
    "end": {
        "day_of_week": "MONDAY",
        "milliseconds_since_midnight": 21600000
    }
  }
])";

}  // namespace

class DevicePolicyDecoderTest : public testing::Test {
 public:
  DevicePolicyDecoderTest() = default;

  DevicePolicyDecoderTest(const DevicePolicyDecoderTest&) = delete;
  DevicePolicyDecoderTest& operator=(const DevicePolicyDecoderTest&) = delete;

  ~DevicePolicyDecoderTest() override = default;

 protected:
  base::Value GetWallpaperDict() const;
  base::Value GetBluetoothServiceAllowedList() const;
  std::vector<WeeklyTimeInterval> GetDeviceWeeklyScheduledSuspendList() const;
  void DecodeDevicePolicyTestHelper(
      const em::ChromeDeviceSettingsProto& device_policy,
      const std::string& policy_path,
      base::Value expected_value) const;
  void DecodeUnsetDevicePolicyTestHelper(
      const em::ChromeDeviceSettingsProto& device_policy,
      const std::string& policy_path) const;
};

base::Value DevicePolicyDecoderTest::GetWallpaperDict() const {
  return base::Value(
      base::Value::Dict()
          .Set(kWallpaperUrlPropertyName, kWallpaperUrlPropertyValue)
          .Set(kWallpaperHashPropertyName, kWallpaperHashPropertyValue));
}

base::Value DevicePolicyDecoderTest::GetBluetoothServiceAllowedList() const {
  return base::Value(base::Value::List()
                         .Append(kValidBluetoothServiceUUID4)
                         .Append(kValidBluetoothServiceUUID8)
                         .Append(kValidBluetoothServiceUUID32));
}

std::vector<WeeklyTimeInterval>
DevicePolicyDecoderTest::GetDeviceWeeklyScheduledSuspendList() const {
  using time_proto = em::WeeklyTimeProto;
  std::vector<WeeklyTimeInterval> ret;
  ret.emplace_back(
      WeeklyTime(time_proto::MONDAY, base::Hours(18).InMilliseconds(),
                 /*timezone_offset=*/std::nullopt),
      WeeklyTime(time_proto::TUESDAY, base::Hours(8).InMilliseconds(),
                 /*timezone_offset=*/std::nullopt));
  ret.emplace_back(
      WeeklyTime(time_proto::FRIDAY, base::Hours(21).InMilliseconds(),
                 /*timezone_offset=*/std::nullopt),
      WeeklyTime(time_proto::MONDAY, base::Hours(7).InMilliseconds(),
                 /*timezone_offset=*/std::nullopt));
  return ret;
}

void DevicePolicyDecoderTest::DecodeDevicePolicyTestHelper(
    const em::ChromeDeviceSettingsProto& device_policy,
    const std::string& policy_path,
    base::Value expected_value) const {
  PolicyBundle bundle;
  PolicyMap& policies = bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));

  base::WeakPtr<ExternalDataManager> external_data_manager;

  DecodeDevicePolicy(device_policy, external_data_manager, &policies);

  // It is safe to use `GetValueUnsafe()` as multiple policy types are handled.
  const base::Value* actual_value = policies.GetValueUnsafe(policy_path);
  ASSERT_NE(actual_value, nullptr);
  EXPECT_EQ(*actual_value, expected_value);
}

void DevicePolicyDecoderTest::DecodeUnsetDevicePolicyTestHelper(
    const em::ChromeDeviceSettingsProto& device_policy,
    const std::string& policy_path) const {
  PolicyBundle bundle;
  PolicyMap& policies = bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));

  base::WeakPtr<ExternalDataManager> external_data_manager;

  DecodeDevicePolicy(device_policy, external_data_manager, &policies);

  // It is safe to use `GetValueUnsafe()` as multiple policy types are handled.
  const base::Value* actual_value = policies.GetValueUnsafe(policy_path);
  EXPECT_EQ(actual_value, nullptr);
}

TEST_F(DevicePolicyDecoderTest, DecodeJsonStringAndNormalizeJSONParseError) {
  auto decoding_result =
      DecodeJsonStringAndNormalize(kInvalidJson, key::kDeviceWallpaperImage);
  ASSERT_FALSE(decoding_result.has_value());
  std::string localized_error =
      l10n_util::GetStringFUTF8(IDS_POLICY_PROTO_PARSING_ERROR,
                                base::UTF8ToUTF16(decoding_result.error()));
  EXPECT_NE(std::string::npos,
            localized_error.find("Policy parsing error: Invalid JSON string"));
}

#if GTEST_HAS_DEATH_TEST
TEST_F(DevicePolicyDecoderTest, DecodeJsonStringAndNormalizeInvalidSchema) {
  std::string error;
  EXPECT_DEATH(std::ignore = DecodeJsonStringAndNormalize(kWallpaperJson,
                                                          kInvalidPolicyName),
               "");
}
#endif

TEST_F(DevicePolicyDecoderTest, DecodeJsonStringAndNormalizeInvalidValue) {
  auto decoding_result = DecodeJsonStringAndNormalize(
      kWallpaperJsonInvalidValue, key::kDeviceWallpaperImage);
  ASSERT_FALSE(decoding_result.has_value());
  std::string localized_error =
      l10n_util::GetStringFUTF8(IDS_POLICY_PROTO_PARSING_ERROR,
                                base::UTF8ToUTF16(decoding_result.error()));
  EXPECT_EQ(
      "Policy parsing error: Invalid policy value: Policy type mismatch: "
      "expected: \"string\", actual: \"integer\". (at "
      "DeviceWallpaperImage.url)",
      localized_error);
}

TEST_F(DevicePolicyDecoderTest, DecodeJsonStringAndNormalizeUnknownProperty) {
  auto decoding_result = DecodeJsonStringAndNormalize(
      kWallpaperJsonUnknownProperty, key::kDeviceWallpaperImage);
  ASSERT_TRUE(decoding_result.has_value());
  ASSERT_TRUE(decoding_result->non_fatal_errors.has_value());

  std::string localized_error = l10n_util::GetStringFUTF8(
      IDS_POLICY_PROTO_PARSING_ERROR,
      base::UTF8ToUTF16(decoding_result->non_fatal_errors.value()));
  EXPECT_EQ(GetWallpaperDict(), decoding_result->decoded_json);
  EXPECT_EQ(
      "Policy parsing error: Dropped unknown properties: Unknown property: "
      "unknown-field (at DeviceWallpaperImage)",
      localized_error);
}

TEST_F(DevicePolicyDecoderTest, DecodeJsonStringAndNormalizeSuccess) {
  auto decoding_result =
      DecodeJsonStringAndNormalize(kWallpaperJson, key::kDeviceWallpaperImage);
  ASSERT_TRUE(decoding_result.has_value());
  EXPECT_EQ(GetWallpaperDict(), decoding_result->decoded_json);
  EXPECT_FALSE(decoding_result->non_fatal_errors.has_value());
}

TEST_F(DevicePolicyDecoderTest, DeviceActivityHeartbeatEnabled) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kDeviceActivityHeartbeatEnabled);

  base::Value device_activity_heartbeat_enabled_value(true);
  device_policy.mutable_device_reporting()
      ->set_device_activity_heartbeat_enabled(
          device_activity_heartbeat_enabled_value.GetBool());

  DecodeDevicePolicyTestHelper(
      device_policy, key::kDeviceActivityHeartbeatEnabled,
      std::move(device_activity_heartbeat_enabled_value));
}

TEST_F(DevicePolicyDecoderTest, DeviceActivityHeartbeatCollectionRateMs) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(
      device_policy, key::kDeviceActivityHeartbeatCollectionRateMs);

  base::Value device_activity_heartbeat_collection_rate_ms_value(120000);
  device_policy.mutable_device_reporting()
      ->set_device_activity_heartbeat_collection_rate_ms(
          device_activity_heartbeat_collection_rate_ms_value.GetInt());

  DecodeDevicePolicyTestHelper(
      device_policy, key::kDeviceActivityHeartbeatCollectionRateMs,
      std::move(device_activity_heartbeat_collection_rate_ms_value));
}

TEST_F(DevicePolicyDecoderTest, ReportDeviceLoginLogout) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kReportDeviceLoginLogout);

  base::Value report_login_logout_value(true);
  device_policy.mutable_device_reporting()->set_report_login_logout(
      report_login_logout_value.GetBool());

  DecodeDevicePolicyTestHelper(device_policy, key::kReportDeviceLoginLogout,
                               std::move(report_login_logout_value));
}

TEST_F(DevicePolicyDecoderTest, ReportDeviceCRDSessions) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy, key::kReportCRDSessions);

  base::Value report_crd_sessions_value(true);
  device_policy.mutable_device_reporting()->set_report_crd_sessions(
      report_crd_sessions_value.GetBool());

  DecodeDevicePolicyTestHelper(device_policy, key::kReportCRDSessions,
                               std::move(report_crd_sessions_value));
}

TEST_F(DevicePolicyDecoderTest, ReportDeviceNetworkTelemetryCollectionRateMs) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(
      device_policy, key::kReportDeviceNetworkTelemetryCollectionRateMs);

  base::Value collection_rate_ms_value(120000);
  device_policy.mutable_device_reporting()
      ->set_report_network_telemetry_collection_rate_ms(
          collection_rate_ms_value.GetInt());

  DecodeDevicePolicyTestHelper(
      device_policy, key::kReportDeviceNetworkTelemetryCollectionRateMs,
      std::move(collection_rate_ms_value));
}

TEST_F(DevicePolicyDecoderTest,
       ReportDeviceNetworkTelemetryEventCheckingRateMs) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(
      device_policy, key::kReportDeviceNetworkTelemetryEventCheckingRateMs);

  base::Value event_checking_rate_ms_value(80000);
  device_policy.mutable_device_reporting()
      ->set_report_network_telemetry_event_checking_rate_ms(
          event_checking_rate_ms_value.GetInt());

  DecodeDevicePolicyTestHelper(
      device_policy, key::kReportDeviceNetworkTelemetryEventCheckingRateMs,
      std::move(event_checking_rate_ms_value));
}

TEST_F(DevicePolicyDecoderTest, ReportDeviceAudioStatusCheckingRateMs) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(
      device_policy, key::kReportDeviceAudioStatusCheckingRateMs);

  base::Value event_checking_rate_ms_value(80000);
  device_policy.mutable_device_reporting()
      ->set_report_device_audio_status_checking_rate_ms(
          event_checking_rate_ms_value.GetInt());

  DecodeDevicePolicyTestHelper(device_policy,
                               key::kReportDeviceAudioStatusCheckingRateMs,
                               std::move(event_checking_rate_ms_value));
}

TEST_F(DevicePolicyDecoderTest, DeviceReportRuntimeCountersCheckingRateMs) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(
      device_policy, key::kDeviceReportRuntimeCountersCheckingRateMs);

  base::Value event_checking_rate_ms_value(90000000);
  device_policy.mutable_device_reporting()
      ->set_device_report_runtime_counters_checking_rate_ms(
          event_checking_rate_ms_value.GetInt());

  DecodeDevicePolicyTestHelper(device_policy,
                               key::kDeviceReportRuntimeCountersCheckingRateMs,
                               std::move(event_checking_rate_ms_value));
}

TEST_F(DevicePolicyDecoderTest, ReportDevicePeripherals) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kReportDevicePeripherals);

  base::Value report_peripherals_value(true);
  device_policy.mutable_device_reporting()->set_report_peripherals(
      report_peripherals_value.GetBool());

  DecodeDevicePolicyTestHelper(device_policy, key::kReportDevicePeripherals,
                               std::move(report_peripherals_value));
}

TEST_F(DevicePolicyDecoderTest, ReportDeviceAudioStatus) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kReportDeviceAudioStatus);

  base::Value report_audio_status_value(true);
  device_policy.mutable_device_reporting()->set_report_audio_status(
      report_audio_status_value.GetBool());

  DecodeDevicePolicyTestHelper(device_policy, key::kReportDeviceAudioStatus,
                               std::move(report_audio_status_value));
}

TEST_F(DevicePolicyDecoderTest, DeviceReportRuntimeCounters) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kDeviceReportRuntimeCounters);

  base::Value report_runtime_counters_value(true);
  device_policy.mutable_device_reporting()->set_report_runtime_counters(
      report_runtime_counters_value.GetBool());

  DecodeDevicePolicyTestHelper(device_policy, key::kDeviceReportRuntimeCounters,
                               std::move(report_runtime_counters_value));
}

TEST_F(DevicePolicyDecoderTest, ReportDeviceSecurityStatus) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kReportDeviceSecurityStatus);

  base::Value report_security_status_value(true);
  device_policy.mutable_device_reporting()->set_report_security_status(
      report_security_status_value.GetBool());

  DecodeDevicePolicyTestHelper(device_policy, key::kReportDeviceSecurityStatus,
                               std::move(report_security_status_value));
}

TEST_F(DevicePolicyDecoderTest, ReportDeviceNetworkConfiguration) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kReportDeviceNetworkConfiguration);

  base::Value report_network_configuration_value(true);
  device_policy.mutable_device_reporting()->set_report_network_configuration(
      report_network_configuration_value.GetBool());

  DecodeDevicePolicyTestHelper(device_policy,
                               key::kReportDeviceNetworkConfiguration,
                               std::move(report_network_configuration_value));
}

TEST_F(DevicePolicyDecoderTest, ReportDeviceNetworkStatus) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kReportDeviceNetworkStatus);

  base::Value report_network_status_value(true);
  device_policy.mutable_device_reporting()->set_report_network_status(
      report_network_status_value.GetBool());

  DecodeDevicePolicyTestHelper(device_policy, key::kReportDeviceNetworkStatus,
                               std::move(report_network_status_value));
}

TEST_F(DevicePolicyDecoderTest, kReportDeviceOsUpdateStatus) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kReportDeviceOsUpdateStatus);

  base::Value report_os_update_status_value(true);
  device_policy.mutable_device_reporting()->set_report_os_update_status(
      report_os_update_status_value.GetBool());

  DecodeDevicePolicyTestHelper(device_policy, key::kReportDeviceOsUpdateStatus,
                               std::move(report_os_update_status_value));
}

TEST_F(DevicePolicyDecoderTest,
       ReportDeviceSignalStrengthEventDrivenTelemetry) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(
      device_policy, key::kReportDeviceSignalStrengthEventDrivenTelemetry);

  auto signal_strength_telemetry_list =
      base::Value::List().Append("network_telemetry").Append("https_latency");
  device_policy.mutable_device_reporting()
      ->mutable_report_signal_strength_event_driven_telemetry()
      ->add_entries("network_telemetry");
  device_policy.mutable_device_reporting()
      ->mutable_report_signal_strength_event_driven_telemetry()
      ->add_entries("https_latency");

  DecodeDevicePolicyTestHelper(
      device_policy, key::kReportDeviceSignalStrengthEventDrivenTelemetry,
      base::Value(std::move(signal_strength_telemetry_list)));
}

TEST_F(DevicePolicyDecoderTest, DeviceReportNetworkEvents) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kDeviceReportNetworkEvents);

  base::Value report_network_events_value(true);
  device_policy.mutable_device_reporting()->set_report_network_events(
      report_network_events_value.GetBool());

  DecodeDevicePolicyTestHelper(device_policy, key::kDeviceReportNetworkEvents,
                               std::move(report_network_events_value));
}

TEST_F(DevicePolicyDecoderTest, DecodeServiceUUIDListSuccess) {
  auto decoding_result = DecodeJsonStringAndNormalize(
      kValidBluetoothServiceUUIDList, key::kDeviceAllowedBluetoothServices);
  ASSERT_TRUE(decoding_result.has_value());
  EXPECT_EQ(GetBluetoothServiceAllowedList(), decoding_result->decoded_json);
  EXPECT_FALSE(decoding_result->non_fatal_errors.has_value());
}

TEST_F(DevicePolicyDecoderTest, DecodeServiceUUIDListError) {
  auto decoding_result = DecodeJsonStringAndNormalize(
      kInvalidBluetoothServiceUUIDList, key::kDeviceAllowedBluetoothServices);
  ASSERT_FALSE(decoding_result.has_value());
  EXPECT_EQ(
      "Invalid policy value: Invalid value for string (at "
      "DeviceAllowedBluetoothServices[0])",
      decoding_result.error());
}

TEST_F(DevicePolicyDecoderTest,
       DecodeLoginScreenPromptOnMultipleMatchingCertificates) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(
      device_policy,
      key::kDeviceLoginScreenPromptOnMultipleMatchingCertificates);

  base::Value login_screen_prompt_value(true);
  device_policy.mutable_login_screen_prompt_on_multiple_matching_certificates()
      ->set_value(login_screen_prompt_value.GetBool());

  DecodeDevicePolicyTestHelper(
      device_policy,
      key::kDeviceLoginScreenPromptOnMultipleMatchingCertificates,
      std::move(login_screen_prompt_value));
}

TEST_F(DevicePolicyDecoderTest, DecodeDeviceEncryptedReportingPipelineEnabled) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(
      device_policy, key::kDeviceEncryptedReportingPipelineEnabled);

  base::Value prompt_value(true);
  device_policy.mutable_device_encrypted_reporting_pipeline_enabled()
      ->set_enabled(prompt_value.GetBool());

  DecodeDevicePolicyTestHelper(device_policy,
                               key::kDeviceEncryptedReportingPipelineEnabled,
                               std::move(prompt_value));
}

TEST_F(DevicePolicyDecoderTest, DecodeDeviceAutofillSAMLUsername) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kDeviceAutofillSAMLUsername);

  base::Value autofill_saml_username_value("login_hint");
  device_policy.mutable_saml_username()
      ->set_url_parameter_to_autofill_saml_username(
          autofill_saml_username_value.GetString());

  DecodeDevicePolicyTestHelper(device_policy, key::kDeviceAutofillSAMLUsername,
                               std::move(autofill_saml_username_value));
}

TEST_F(DevicePolicyDecoderTest, DeviceReportXDREvents) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy, key::kDeviceReportXDREvents);

  base::Value device_report_xdr_events_value(true);
  device_policy.mutable_device_report_xdr_events()->set_enabled(
      device_report_xdr_events_value.GetBool());

  DecodeDevicePolicyTestHelper(device_policy, key::kDeviceReportXDREvents,
                               std::move(device_report_xdr_events_value));
}

TEST_F(DevicePolicyDecoderTest, DeviceHindiInscriptLayoutEnabled) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kDeviceHindiInscriptLayoutEnabled);

  base::Value device_hindi_inscript_layout_enabled_value(true);
  device_policy.mutable_device_hindi_inscript_layout_enabled()->set_enabled(
      device_hindi_inscript_layout_enabled_value.GetBool());

  DecodeDevicePolicyTestHelper(
      device_policy, key::kDeviceHindiInscriptLayoutEnabled,
      std::move(device_hindi_inscript_layout_enabled_value));
}

TEST_F(DevicePolicyDecoderTest, DeviceSystemAecEnabled) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kDeviceSystemAecEnabled);

  base::Value device_system_aec_enabled_value(true);
  device_policy.mutable_device_system_aec_enabled()
      ->set_device_system_aec_enabled(
          device_system_aec_enabled_value.GetBool());

  DecodeDevicePolicyTestHelper(device_policy, key::kDeviceSystemAecEnabled,
                               std::move(device_system_aec_enabled_value));
}

TEST_F(DevicePolicyDecoderTest,
       DecodeDeviceLocalAccountsWithoutEphemeralModeField) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy, key::kDeviceLocalAccounts);

  em::DeviceLocalAccountInfoProto* account =
      device_policy.mutable_device_local_accounts()->add_account();
  account->set_account_id(kDeviceLocalAccountKioskAccountId);
  account->set_type(
      em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_WEB_KIOSK_APP);

  DecodeDevicePolicyTestHelper(
      device_policy, key::kDeviceLocalAccounts,
      base::Value(base::Value::List().Append(
          base::Value::Dict()
              .Set(ash::kAccountsPrefDeviceLocalAccountsKeyId,
                   kDeviceLocalAccountKioskAccountId)
              .Set(ash::kAccountsPrefDeviceLocalAccountsKeyType,
                   static_cast<int>(DeviceLocalAccountType::kWebKioskApp))
              .Set(ash::kAccountsPrefDeviceLocalAccountsKeyEphemeralMode,
                   static_cast<int>(
                       DeviceLocalAccount::EphemeralMode::kUnset)))));
}

TEST_F(DevicePolicyDecoderTest,
       DecodeDeviceLocalAccountsWithEphemeralModeField) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy, key::kDeviceLocalAccounts);

  em::DeviceLocalAccountInfoProto* account =
      device_policy.mutable_device_local_accounts()->add_account();
  account->set_account_id(kDeviceLocalAccountKioskAccountId);
  account->set_type(em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_KIOSK_APP);
  account->set_ephemeral_mode(
      em::DeviceLocalAccountInfoProto::EPHEMERAL_MODE_DISABLE);

  DecodeDevicePolicyTestHelper(
      device_policy, key::kDeviceLocalAccounts,
      base::Value(base::Value::List().Append(
          base::Value::Dict()
              .Set(ash::kAccountsPrefDeviceLocalAccountsKeyId,
                   kDeviceLocalAccountKioskAccountId)
              .Set(ash::kAccountsPrefDeviceLocalAccountsKeyType,
                   static_cast<int>(DeviceLocalAccountType::kKioskApp))
              .Set(ash::kAccountsPrefDeviceLocalAccountsKeyEphemeralMode,
                   static_cast<int>(
                       DeviceLocalAccount::EphemeralMode::kDisable)))));
}

TEST_F(DevicePolicyDecoderTest, DeviceLowBatterySoundEnabled) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kDeviceLowBatterySoundEnabled);

  base::Value device_low_battery_sound_enabled_value(true);
  device_policy.mutable_device_low_battery_sound()->set_enabled(
      device_low_battery_sound_enabled_value.GetBool());

  DecodeDevicePolicyTestHelper(
      device_policy, key::kDeviceLowBatterySoundEnabled,
      std::move(device_low_battery_sound_enabled_value));
}

TEST_F(DevicePolicyDecoderTest, DeviceChargingSoundsEnabled) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kDeviceChargingSoundsEnabled);

  base::Value device_charging_sounds_enabled_value(true);
  device_policy.mutable_device_charging_sounds()->set_enabled(
      device_charging_sounds_enabled_value.GetBool());

  DecodeDevicePolicyTestHelper(device_policy, key::kDeviceChargingSoundsEnabled,
                               std::move(device_charging_sounds_enabled_value));
}

TEST_F(DevicePolicyDecoderTest, DecodeDeviceAuthenticationURLBlocklist) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kDeviceAuthenticationURLBlocklist);

  em::StringList* blocklist =
      device_policy.mutable_device_authentication_url_blocklist()
          ->mutable_value();

  auto blocklist_items =
      base::Value::List().Append("example.com").Append("*.example.com");

  for (auto& item : blocklist_items) {
    blocklist->add_entries(item.GetString());
  }

  DecodeDevicePolicyTestHelper(device_policy,
                               key::kDeviceAuthenticationURLBlocklist,
                               base::Value(std::move(blocklist_items)));
}

TEST_F(DevicePolicyDecoderTest, DecodeDeviceAuthenticationURLAllowlist) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kDeviceAuthenticationURLAllowlist);

  em::StringList* allowlist =
      device_policy.mutable_device_authentication_url_allowlist()
          ->mutable_value();

  auto allowlist_items = base::Value::List()
                             .Append("allow.example.com")
                             .Append("*.allow.example.com");

  for (auto& item : allowlist_items) {
    allowlist->add_entries(item.GetString());
  }

  DecodeDevicePolicyTestHelper(device_policy,
                               key::kDeviceAuthenticationURLAllowlist,
                               base::Value(std::move(allowlist_items)));
}

TEST_F(DevicePolicyDecoderTest, DeviceSwitchFunctionKeysBehaviorEnabled) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(
      device_policy, key::kDeviceSwitchFunctionKeysBehaviorEnabled);

  base::Value device_switch_function_keys_behavior_enabled(true);
  device_policy.mutable_device_switch_function_keys_behavior_enabled()
      ->set_enabled(device_switch_function_keys_behavior_enabled.GetBool());

  DecodeDevicePolicyTestHelper(
      device_policy, key::kDeviceSwitchFunctionKeysBehaviorEnabled,
      std::move(device_switch_function_keys_behavior_enabled));
}

TEST_F(DevicePolicyDecoderTest, DeviceEphemeralNetworkPoliciesEnabled) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(
      device_policy, key::kDeviceEphemeralNetworkPoliciesEnabled);

  device_policy.mutable_device_ephemeral_network_policies_enabled()->set_value(
      true);

  DecodeDevicePolicyTestHelper(device_policy,
                               key::kDeviceEphemeralNetworkPoliciesEnabled,
                               /*expected_value=*/base::Value(true));
}

TEST_F(DevicePolicyDecoderTest, DeviceLoginScreenTouchVirtualKeyboardPolicy) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kTouchVirtualKeyboardEnabled);

  device_policy.mutable_deviceloginscreentouchvirtualkeyboardenabled()
      ->set_value(true);

  DecodeDevicePolicyTestHelper(device_policy, key::kTouchVirtualKeyboardEnabled,
                               base::Value(true));
}

TEST_F(DevicePolicyDecoderTest, DeviceExtendedAutoUpdateEnabled) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kDeviceExtendedAutoUpdateEnabled);

  base::Value deviceextendedautoupdateenabled(true);
  device_policy.mutable_deviceextendedautoupdateenabled()->set_value(
      deviceextendedautoupdateenabled.GetBool());

  DecodeDevicePolicyTestHelper(device_policy,
                               key::kDeviceExtendedAutoUpdateEnabled,
                               std::move(deviceextendedautoupdateenabled));
}

TEST_F(DevicePolicyDecoderTest, DecodeDeviceWeeklyScheduledSuspendSuccess) {
  auto decoding_result =
      DecodeJsonStringAndNormalize(kValidDeviceWeeklyScheduledSuspendList,
                                   key::kDeviceWeeklyScheduledSuspend);
  ASSERT_TRUE(decoding_result.has_value());
  ASSERT_TRUE(decoding_result->decoded_json.is_list());

  std::vector<WeeklyTimeInterval> actual_list;
  for (const auto& item : decoding_result->decoded_json.GetList()) {
    ASSERT_TRUE(item.is_dict());
    std::unique_ptr<WeeklyTimeInterval> interval =
        WeeklyTimeInterval::ExtractFromDict(item.GetDict(),
                                            /*timezone_offset=*/std::nullopt);
    ASSERT_TRUE(interval);
    actual_list.emplace_back(std::move(*interval));
  }

  EXPECT_EQ(GetDeviceWeeklyScheduledSuspendList(), actual_list);
}

TEST_F(DevicePolicyDecoderTest,
       DecodeDeviceWeeklyScheduledSuspendInvalidJsonError) {
  auto decoding_result = DecodeJsonStringAndNormalize(
      kInvalidJson, key::kDeviceWeeklyScheduledSuspend);
  EXPECT_FALSE(decoding_result.has_value());
  EXPECT_THAT(
      l10n_util::GetStringFUTF8(IDS_POLICY_PROTO_PARSING_ERROR,
                                base::UTF8ToUTF16(decoding_result.error())),
      ::testing::HasSubstr("Policy parsing error: Invalid JSON string"));
}

TEST_F(DevicePolicyDecoderTest,
       DecodeDeviceAuthenticationFlowAutoReloadInterval) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(
      device_policy, key::kDeviceAuthenticationFlowAutoReloadInterval);

  base::Value auth_flow_reload_interval(15);
  device_policy.mutable_deviceauthenticationflowautoreloadinterval()->set_value(
      auth_flow_reload_interval.GetInt());

  DecodeDevicePolicyTestHelper(device_policy,
                               key::kDeviceAuthenticationFlowAutoReloadInterval,
                               std::move(auth_flow_reload_interval));
}

TEST_F(DevicePolicyDecoderTest, DeviceExtensionsSystemLogEnabled) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kDeviceExtensionsSystemLogEnabled);

  base::Value deviceextensionssystemlogenabled(true);
  device_policy.mutable_deviceextensionssystemlogenabled()->set_value(
      deviceextensionssystemlogenabled.GetBool());

  DecodeDevicePolicyTestHelper(device_policy,
                               key::kDeviceExtensionsSystemLogEnabled,
                               std::move(deviceextensionssystemlogenabled));
}

TEST_F(DevicePolicyDecoderTest, DeviceAllowEnterpriseRemoteAccessConnections) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(
      device_policy, key::kDeviceAllowEnterpriseRemoteAccessConnections);

  base::Value value(true);
  device_policy.mutable_deviceallowenterpriseremoteaccessconnections()
      ->set_value(value.GetBool());

  DecodeDevicePolicyTestHelper(
      device_policy, key::kDeviceAllowEnterpriseRemoteAccessConnections,
      std::move(value));
}

TEST_F(DevicePolicyDecoderTest, DevicePostQuantumKeyAgreementEnabled) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kDevicePostQuantumKeyAgreementEnabled);

  base::Value devicepostquantumkeyagreementenabled(true);
  device_policy.mutable_devicepostquantumkeyagreementenabled()->set_value(
      devicepostquantumkeyagreementenabled.GetBool());

  DecodeDevicePolicyTestHelper(device_policy,
                               key::kDevicePostQuantumKeyAgreementEnabled,
                               std::move(devicepostquantumkeyagreementenabled));
}

TEST_F(DevicePolicyDecoderTest, DecodeDeviceRestrictionSchedule) {
  em::ChromeDeviceSettingsProto device_policy;

  DecodeUnsetDevicePolicyTestHelper(device_policy,
                                    key::kDeviceRestrictionSchedule);

  auto decoding_result = DecodeJsonStringAndNormalize(
      kValidDeviceRestrictionScheduleJson, key::kDeviceRestrictionSchedule);
  ASSERT_TRUE(decoding_result.has_value());
  base::Value device_restriction_schedule(
      decoding_result->decoded_json.Clone());

  device_policy.mutable_devicerestrictionschedule()->set_value(
      kValidDeviceRestrictionScheduleJson);

  DecodeDevicePolicyTestHelper(device_policy, key::kDeviceRestrictionSchedule,
                               std::move(device_restriction_schedule));
}

TEST_F(DevicePolicyDecoderTest, DecodeDeviceRestrictionScheduleError) {
  em::ChromeDeviceSettingsProto device_policy;
  device_policy.mutable_devicerestrictionschedule()->set_value(kInvalidJson);

  PolicyBundle bundle;
  PolicyMap& policies = bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));

  base::WeakPtr<ExternalDataManager> external_data_manager;
  DecodeDevicePolicy(device_policy, external_data_manager, &policies);

  const PolicyMap::Entry* entry = policies.Get(key::kDeviceRestrictionSchedule);
  ASSERT_NE(entry, nullptr);
  EXPECT_TRUE(entry->HasMessage(PolicyMap::MessageType::kError));
  EXPECT_EQ(
      kInvalidJsonParsingError,
      entry->GetLocalizedMessages(PolicyMap::MessageType::kError,
                                  PolicyMap::Entry::L10nLookupFunction()));
}

}  // namespace policy