| Index: chrome/browser/policy/policy_loader_win_unittest.cc
|
| diff --git a/chrome/browser/policy/policy_loader_win_unittest.cc b/chrome/browser/policy/policy_loader_win_unittest.cc
|
| index 888fa8a05580460948ba309f9bd1d02bbccd2077..9173c26a7c124f740e5de4f10761ab4666e55fe6 100644
|
| --- a/chrome/browser/policy/policy_loader_win_unittest.cc
|
| +++ b/chrome/browser/policy/policy_loader_win_unittest.cc
|
| @@ -9,6 +9,7 @@
|
| #include "base/json/json_writer.h"
|
| #include "base/string16.h"
|
| #include "base/string_number_conversions.h"
|
| +#include "base/string_util.h"
|
| #include "base/utf_string_conversions.h"
|
| #include "base/win/registry.h"
|
| #include "chrome/browser/policy/async_policy_provider.h"
|
| @@ -19,6 +20,7 @@
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| using base::win::RegKey;
|
| +using namespace policy::registry_constants;
|
|
|
| namespace policy {
|
|
|
| @@ -30,54 +32,162 @@ const wchar_t kUnitTestMachineOverrideSubKey[] =
|
| const wchar_t kUnitTestUserOverrideSubKey[] =
|
| L"SOFTWARE\\Chromium Unit Tests\\HKCU Override";
|
|
|
| -// Installs |dict| at the given |path|, in the given |hive|. Currently only
|
| -// string, int and dictionary types are converted; other types cause a failure.
|
| -// Returns false if there was any failure, and true if |dict| was successfully
|
| -// written.
|
| -// TODO(joaodasilva): generate a schema for |dict| too, so that all types can
|
| -// be retrieved.
|
| -bool InstallDictionary(const base::DictionaryValue& dict,
|
| - HKEY hive,
|
| - const string16& path) {
|
| +// Installs |value| in the given registry |path| and |hive|, under the key
|
| +// |name|. Returns false on errors.
|
| +// Some of the possible Value types are stored after a conversion (e.g. doubles
|
| +// are stored as strings), and can only be retrieved if a corresponding schema
|
| +// is written.
|
| +bool InstallValue(const base::Value& value,
|
| + HKEY hive,
|
| + const string16& path,
|
| + const string16& name) {
|
| // KEY_ALL_ACCESS causes the ctor to create the key if it does not exist yet.
|
| RegKey key(hive, path.c_str(), KEY_ALL_ACCESS);
|
| - const string16 kPathSep = ASCIIToUTF16("\\");
|
| -
|
| - for (base::DictionaryValue::Iterator it(dict); it.HasNext(); it.Advance()) {
|
| - string16 name(UTF8ToUTF16(it.key()));
|
| - switch (it.value().GetType()) {
|
| - case base::Value::TYPE_STRING: {
|
| - string16 value;
|
| - if (!it.value().GetAsString(&value))
|
| - return false;
|
| - if (key.WriteValue(name.c_str(), value.c_str()) != ERROR_SUCCESS)
|
| + switch (value.GetType()) {
|
| + case base::Value::TYPE_NULL:
|
| + return key.WriteValue(name.c_str(), L"") == ERROR_SUCCESS;
|
| +
|
| + case base::Value::TYPE_BOOLEAN: {
|
| + bool bool_value;
|
| + if (!value.GetAsBoolean(&bool_value))
|
| + return false;
|
| + return key.WriteValue(name.c_str(), bool_value ? 1 : 0) == ERROR_SUCCESS;
|
| + }
|
| +
|
| + case base::Value::TYPE_INTEGER: {
|
| + int int_value;
|
| + if (!value.GetAsInteger(&int_value))
|
| + return false;
|
| + return key.WriteValue(name.c_str(), int_value) == ERROR_SUCCESS;
|
| + }
|
| +
|
| + case base::Value::TYPE_DOUBLE: {
|
| + double double_value;
|
| + if (!value.GetAsDouble(&double_value))
|
| + return false;
|
| + string16 str_value = UTF8ToUTF16(base::DoubleToString(double_value));
|
| + return key.WriteValue(name.c_str(), str_value.c_str()) == ERROR_SUCCESS;
|
| + }
|
| +
|
| + case base::Value::TYPE_STRING: {
|
| + string16 str_value;
|
| + if (!value.GetAsString(&str_value))
|
| + return false;
|
| + return key.WriteValue(name.c_str(), str_value.c_str()) == ERROR_SUCCESS;
|
| + }
|
| +
|
| + case base::Value::TYPE_DICTIONARY: {
|
| + const base::DictionaryValue* sub_dict = NULL;
|
| + if (!value.GetAsDictionary(&sub_dict))
|
| + return false;
|
| + for (base::DictionaryValue::Iterator it(*sub_dict);
|
| + it.HasNext(); it.Advance()) {
|
| + if (!InstallValue(it.value(), hive, path + kPathSep + name,
|
| + UTF8ToUTF16(it.key()))) {
|
| return false;
|
| - break;
|
| + }
|
| }
|
| + return true;
|
| + }
|
|
|
| - case base::Value::TYPE_INTEGER: {
|
| - int value;
|
| - if (!it.value().GetAsInteger(&value))
|
| + case base::Value::TYPE_LIST: {
|
| + const base::ListValue* list = NULL;
|
| + if (!value.GetAsList(&list))
|
| + return false;
|
| + for (size_t i = 0; i < list->GetSize(); ++i) {
|
| + base::Value* item;
|
| + if (!list->Get(i, &item))
|
| return false;
|
| - if (key.WriteValue(name.c_str(), value) != ERROR_SUCCESS)
|
| + if (!InstallValue(*item, hive, path + kPathSep + name,
|
| + base::UintToString16(i + 1))) {
|
| return false;
|
| - break;
|
| + }
|
| }
|
| + return true;
|
| + }
|
|
|
| - case base::Value::TYPE_DICTIONARY: {
|
| - const base::DictionaryValue* sub_dict = NULL;
|
| - if (!it.value().GetAsDictionary(&sub_dict))
|
| - return false;
|
| - if (!InstallDictionary(*sub_dict, hive, path + kPathSep + name))
|
| - return false;
|
| - break;
|
| + case base::Value::TYPE_BINARY:
|
| + return false;
|
| + }
|
| + NOTREACHED();
|
| + return false;
|
| +}
|
| +
|
| +// Builds a JSON schema that represents the types contained in |value|.
|
| +// Ownership is transferred to the caller.
|
| +base::DictionaryValue* BuildSchema(const base::Value& value) {
|
| + base::DictionaryValue* schema = new base::DictionaryValue();
|
| + switch (value.GetType()) {
|
| + case base::Value::TYPE_NULL:
|
| + schema->SetString(kType, "null");
|
| + break;
|
| + case base::Value::TYPE_BOOLEAN:
|
| + schema->SetString(kType, "boolean");
|
| + break;
|
| + case base::Value::TYPE_INTEGER:
|
| + schema->SetString(kType, "integer");
|
| + break;
|
| + case base::Value::TYPE_DOUBLE:
|
| + schema->SetString(kType, "number");
|
| + break;
|
| + case base::Value::TYPE_STRING:
|
| + schema->SetString(kType, "string");
|
| + break;
|
| +
|
| + case base::Value::TYPE_LIST: {
|
| + // Assumes every list element has the same type.
|
| + const base::ListValue* list = NULL;
|
| + if (value.GetAsList(&list) && !list->empty()) {
|
| + schema->SetString(kType, "array");
|
| + schema->Set(kItems, BuildSchema(**list->begin()));
|
| }
|
| + break;
|
| + }
|
|
|
| - default:
|
| - return false;
|
| + case base::Value::TYPE_DICTIONARY: {
|
| + const base::DictionaryValue* dict = NULL;
|
| + if (value.GetAsDictionary(&dict)) {
|
| + base::DictionaryValue* properties = new base::DictionaryValue();
|
| + for (base::DictionaryValue::Iterator it(*dict);
|
| + it.HasNext(); it.Advance()) {
|
| + properties->Set(it.key(), BuildSchema(it.value()));
|
| + }
|
| + schema->SetString(kType, "object");
|
| + schema->Set(kProperties, properties);
|
| + }
|
| + break;
|
| }
|
| +
|
| + case base::Value::TYPE_BINARY:
|
| + break;
|
| }
|
| - return true;
|
| + return schema;
|
| +}
|
| +
|
| +// Writes a JSON |schema| at the registry entry |name| at |path|
|
| +// in the given |hive|. Returns false on failure.
|
| +bool WriteSchema(const base::DictionaryValue& schema,
|
| + HKEY hive,
|
| + const string16& path,
|
| + const string16& name) {
|
| + std::string encoded;
|
| + base::JSONWriter::Write(&schema, &encoded);
|
| + if (encoded.empty())
|
| + return false;
|
| + string16 encoded16 = UTF8ToUTF16(encoded);
|
| + // KEY_ALL_ACCESS causes the ctor to create the key if it does not exist yet.
|
| + RegKey key(hive, path.c_str(), KEY_ALL_ACCESS);
|
| + return key.WriteValue(name.c_str(), encoded16.c_str()) == ERROR_SUCCESS;
|
| +}
|
| +
|
| +// Builds a JSON schema for |value| and writes it at the registry entry |name|
|
| +// at |path| in the given |hive|. Returns false on failure.
|
| +bool InstallSchema(const base::Value& value,
|
| + HKEY hive,
|
| + const string16& path,
|
| + const string16& name) {
|
| + scoped_ptr<base::DictionaryValue> schema_dict(BuildSchema(value));
|
| + return WriteSchema(*schema_dict, hive, path, name);
|
| }
|
|
|
| // This class provides sandboxing and mocking for the parts of the Windows
|
| @@ -130,6 +240,8 @@ class TestHarness : public PolicyProviderTestHarness {
|
| virtual void InstallDictionaryPolicy(
|
| const std::string& policy_name,
|
| const base::DictionaryValue* policy_value) OVERRIDE;
|
| + virtual void Install3rdPartyPolicy(
|
| + const base::DictionaryValue* policies) OVERRIDE;
|
|
|
| // Creates a harness instance that will install policy in HKCU or HKLM,
|
| // respectively.
|
| @@ -247,6 +359,30 @@ void TestHarness::InstallDictionaryPolicy(
|
| UTF8ToUTF16(json).c_str());
|
| }
|
|
|
| +void TestHarness::Install3rdPartyPolicy(const base::DictionaryValue* policies) {
|
| + // The first level entries are domains, and the second level entries map
|
| + // components to their policy.
|
| + const string16 kPathPrefix = string16(kRegistryMandatorySubKey) + kPathSep +
|
| + kThirdParty + kPathSep;
|
| + for (base::DictionaryValue::Iterator domain(*policies);
|
| + domain.HasNext(); domain.Advance()) {
|
| + const base::DictionaryValue* components = NULL;
|
| + if (!domain.value().GetAsDictionary(&components)) {
|
| + ADD_FAILURE();
|
| + continue;
|
| + }
|
| + for (base::DictionaryValue::Iterator component(*components);
|
| + component.HasNext(); component.Advance()) {
|
| + const string16 path = string16(kRegistryMandatorySubKey) + kPathSep +
|
| + kThirdParty + kPathSep +
|
| + UTF8ToUTF16(domain.key()) + kPathSep +
|
| + UTF8ToUTF16(component.key());
|
| + InstallValue(component.value(), hive_, path, kMandatory);
|
| + EXPECT_TRUE(InstallSchema(component.value(), hive_, path, kSchema));
|
| + }
|
| + }
|
| +}
|
| +
|
| // static
|
| PolicyProviderTestHarness* TestHarness::CreateHKCU() {
|
| return new TestHarness(HKEY_CURRENT_USER, POLICY_SCOPE_USER);
|
| @@ -265,12 +401,24 @@ INSTANTIATE_TEST_CASE_P(
|
| ConfigurationPolicyProviderTest,
|
| testing::Values(TestHarness::CreateHKCU, TestHarness::CreateHKLM));
|
|
|
| +// Instantiate abstract test case for 3rd party policy reading tests.
|
| +INSTANTIATE_TEST_CASE_P(
|
| + ThirdPartyPolicyProviderWinTest,
|
| + Configuration3rdPartyPolicyProviderTest,
|
| + testing::Values(TestHarness::CreateHKCU, TestHarness::CreateHKLM));
|
| +
|
| // Test cases for windows policy provider specific functionality.
|
| class PolicyLoaderWinTest : public PolicyTestBase {
|
| protected:
|
| PolicyLoaderWinTest() {}
|
| virtual ~PolicyLoaderWinTest() {}
|
|
|
| + bool Matches(const PolicyBundle& expected) {
|
| + PolicyLoaderWin loader(&test_policy_definitions::kList);
|
| + scoped_ptr<PolicyBundle> loaded(loader.Load());
|
| + return loaded->Equals(expected);
|
| + }
|
| +
|
| ScopedGroupPolicyRegistrySandbox registry_sandbox_;
|
| };
|
|
|
| @@ -282,21 +430,16 @@ TEST_F(PolicyLoaderWinTest, HKLMOverHKCU) {
|
| hkcu_key.WriteValue(UTF8ToUTF16(test_policy_definitions::kKeyString).c_str(),
|
| UTF8ToUTF16("hkcu").c_str());
|
|
|
| - PolicyLoaderWin loader(&test_policy_definitions::kList);
|
| - scoped_ptr<PolicyBundle> bundle(loader.Load());
|
| -
|
| - PolicyBundle expected_bundle;
|
| - expected_bundle.Get(POLICY_DOMAIN_CHROME, "")
|
| + PolicyBundle expected;
|
| + expected.Get(POLICY_DOMAIN_CHROME, "")
|
| .Set(test_policy_definitions::kKeyString,
|
| POLICY_LEVEL_MANDATORY,
|
| POLICY_SCOPE_MACHINE,
|
| base::Value::CreateStringValue("hklm"));
|
| - EXPECT_TRUE(bundle->Equals(expected_bundle));
|
| + EXPECT_TRUE(Matches(expected));
|
| }
|
|
|
| -// TODO(joaodasilva): share tests for 3rd party policy with
|
| -// ConfigDirPolicyProvider once PolicyLoaderWin is able to load all types.
|
| -TEST_F(PolicyLoaderWinTest, Load3rdParty) {
|
| +TEST_F(PolicyLoaderWinTest, Load3rdPartyWithoutSchema) {
|
| base::DictionaryValue dict;
|
| dict.SetString("str", "string value");
|
| dict.SetInteger("int", 123);
|
| @@ -305,22 +448,19 @@ TEST_F(PolicyLoaderWinTest, Load3rdParty) {
|
| dict.Set("subsubsubdict", dict.DeepCopy());
|
|
|
| base::DictionaryValue policy_dict;
|
| - policy_dict.Set("3rdparty.extensions.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.policy",
|
| + policy_dict.Set("extensions.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.policy",
|
| dict.DeepCopy());
|
| - policy_dict.Set("3rdparty.extensions.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.policy",
|
| + policy_dict.Set("extensions.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.policy",
|
| dict.DeepCopy());
|
| - EXPECT_TRUE(InstallDictionary(policy_dict, HKEY_LOCAL_MACHINE,
|
| - kRegistryMandatorySubKey));
|
| + EXPECT_TRUE(InstallValue(policy_dict, HKEY_LOCAL_MACHINE,
|
| + kRegistryMandatorySubKey, kThirdParty));
|
|
|
| PolicyBundle expected;
|
| expected.Get(POLICY_DOMAIN_EXTENSIONS, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
| .LoadFrom(&dict, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE);
|
| expected.Get(POLICY_DOMAIN_EXTENSIONS, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
|
| .LoadFrom(&dict, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE);
|
| -
|
| - PolicyLoaderWin loader(&test_policy_definitions::kList);
|
| - scoped_ptr<PolicyBundle> loaded(loader.Load());
|
| - EXPECT_TRUE(loaded->Equals(expected));
|
| + EXPECT_TRUE(Matches(expected));
|
| }
|
|
|
| TEST_F(PolicyLoaderWinTest, Merge3rdPartyPolicies) {
|
| @@ -329,8 +469,6 @@ TEST_F(PolicyLoaderWinTest, Merge3rdPartyPolicies) {
|
|
|
| const string16 kPathSuffix =
|
| kRegistryMandatorySubKey + ASCIIToUTF16("\\3rdparty\\extensions\\merge");
|
| - const string16 kMandatoryPath = kPathSuffix + ASCIIToUTF16("\\policy");
|
| - const string16 kRecommendedPath = kPathSuffix + ASCIIToUTF16("\\recommended");
|
|
|
| const char kUserMandatory[] = "user-mandatory";
|
| const char kUserRecommended[] = "user-recommended";
|
| @@ -339,19 +477,23 @@ TEST_F(PolicyLoaderWinTest, Merge3rdPartyPolicies) {
|
|
|
| base::DictionaryValue policy;
|
| policy.SetString("a", kMachineMandatory);
|
| - EXPECT_TRUE(InstallDictionary(policy, HKEY_LOCAL_MACHINE, kMandatoryPath));
|
| + EXPECT_TRUE(InstallValue(policy, HKEY_LOCAL_MACHINE,
|
| + kPathSuffix, kMandatory));
|
| policy.SetString("a", kUserMandatory);
|
| policy.SetString("b", kUserMandatory);
|
| - EXPECT_TRUE(InstallDictionary(policy, HKEY_CURRENT_USER, kMandatoryPath));
|
| + EXPECT_TRUE(InstallValue(policy, HKEY_CURRENT_USER,
|
| + kPathSuffix, kMandatory));
|
| policy.SetString("a", kMachineRecommended);
|
| policy.SetString("b", kMachineRecommended);
|
| policy.SetString("c", kMachineRecommended);
|
| - EXPECT_TRUE(InstallDictionary(policy, HKEY_LOCAL_MACHINE, kRecommendedPath));
|
| + EXPECT_TRUE(InstallValue(policy, HKEY_LOCAL_MACHINE,
|
| + kPathSuffix, kRecommended));
|
| policy.SetString("a", kUserRecommended);
|
| policy.SetString("b", kUserRecommended);
|
| policy.SetString("c", kUserRecommended);
|
| policy.SetString("d", kUserRecommended);
|
| - EXPECT_TRUE(InstallDictionary(policy, HKEY_CURRENT_USER, kRecommendedPath));
|
| + EXPECT_TRUE(InstallValue(policy, HKEY_CURRENT_USER,
|
| + kPathSuffix, kRecommended));
|
|
|
| PolicyBundle expected;
|
| PolicyMap& expected_policy = expected.Get(POLICY_DOMAIN_EXTENSIONS, "merge");
|
| @@ -363,10 +505,112 @@ TEST_F(PolicyLoaderWinTest, Merge3rdPartyPolicies) {
|
| base::Value::CreateStringValue(kMachineRecommended));
|
| expected_policy.Set("d", POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
|
| base::Value::CreateStringValue(kUserRecommended));
|
| + EXPECT_TRUE(Matches(expected));
|
| +}
|
| +
|
| +TEST_F(PolicyLoaderWinTest, LoadStringEncodedValues) {
|
| + // Create a dictionary with all the types that can be stored encoded in a
|
| + // string, to pass to InstallSchema(). Also build an equivalent dictionary
|
| + // with the encoded values, to pass to InstallValue().
|
| + base::DictionaryValue policy;
|
| + policy.Set("null", base::Value::CreateNullValue());
|
| + policy.SetBoolean("bool", true);
|
| + policy.SetInteger("int", -123);
|
| + policy.SetDouble("double", 456.78e9);
|
| + base::ListValue list;
|
| + list.Append(policy.DeepCopy());
|
| + list.Append(policy.DeepCopy());
|
| + policy.Set("list", list.DeepCopy());
|
| + // Encode |policy| before adding the "dict" entry.
|
| + std::string encoded_dict;
|
| + base::JSONWriter::Write(&policy, &encoded_dict);
|
| + ASSERT_FALSE(encoded_dict.empty());
|
| + policy.Set("dict", policy.DeepCopy());
|
| +
|
| + std::string encoded_list;
|
| + base::JSONWriter::Write(&list, &encoded_list);
|
| + ASSERT_FALSE(encoded_list.empty());
|
| + base::DictionaryValue encoded_policy;
|
| + encoded_policy.SetString("null", "");
|
| + encoded_policy.SetString("bool", "1");
|
| + encoded_policy.SetString("int", "-123");
|
| + encoded_policy.SetString("double", "456.78e9");
|
| + encoded_policy.SetString("list", encoded_list);
|
| + encoded_policy.SetString("dict", encoded_dict);
|
| +
|
| + const string16 kPathSuffix =
|
| + kRegistryMandatorySubKey + ASCIIToUTF16("\\3rdparty\\extensions\\string");
|
| + EXPECT_TRUE(InstallSchema(policy, HKEY_CURRENT_USER, kPathSuffix, kSchema));
|
| + EXPECT_TRUE(
|
| + InstallValue(encoded_policy, HKEY_CURRENT_USER, kPathSuffix, kMandatory));
|
| +
|
| + PolicyBundle expected;
|
| + expected.Get(POLICY_DOMAIN_EXTENSIONS, "string")
|
| + .LoadFrom(&policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER);
|
| + EXPECT_TRUE(Matches(expected));
|
| +}
|
| +
|
| +TEST_F(PolicyLoaderWinTest, LoadIntegerEncodedValues) {
|
| + base::DictionaryValue policy;
|
| + policy.SetBoolean("bool", true);
|
| + policy.SetInteger("int", 123);
|
| + policy.SetDouble("double", 456.0);
|
| +
|
| + base::DictionaryValue encoded_policy;
|
| + encoded_policy.SetInteger("bool", 1);
|
| + encoded_policy.SetInteger("int", 123);
|
| + encoded_policy.SetInteger("double", 456);
|
| +
|
| + const string16 kPathSuffix =
|
| + kRegistryMandatorySubKey + ASCIIToUTF16("\\3rdparty\\extensions\\int");
|
| + EXPECT_TRUE(InstallSchema(policy, HKEY_CURRENT_USER, kPathSuffix, kSchema));
|
| + EXPECT_TRUE(
|
| + InstallValue(encoded_policy, HKEY_CURRENT_USER, kPathSuffix, kMandatory));
|
|
|
| - PolicyLoaderWin loader(&test_policy_definitions::kList);
|
| - scoped_ptr<PolicyBundle> loaded(loader.Load());
|
| - EXPECT_TRUE(loaded->Equals(expected));
|
| + PolicyBundle expected;
|
| + expected.Get(POLICY_DOMAIN_EXTENSIONS, "int")
|
| + .LoadFrom(&policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER);
|
| + EXPECT_TRUE(Matches(expected));
|
| +}
|
| +
|
| +TEST_F(PolicyLoaderWinTest, DefaultPropertySchemaType) {
|
| + // Build a schema for an "object" with a default schema for its properties.
|
| + base::DictionaryValue default_schema;
|
| + default_schema.SetString(kType, "number");
|
| + base::DictionaryValue integer_schema;
|
| + integer_schema.SetString(kType, "integer");
|
| + base::DictionaryValue properties;
|
| + properties.Set("special-int1", integer_schema.DeepCopy());
|
| + properties.Set("special-int2", integer_schema.DeepCopy());
|
| + base::DictionaryValue schema;
|
| + schema.SetString(kType, "object");
|
| + schema.Set(kProperties, properties.DeepCopy());
|
| + schema.Set(kAdditionalProperties, default_schema.DeepCopy());
|
| +
|
| + const string16 kPathSuffix =
|
| + kRegistryMandatorySubKey + ASCIIToUTF16("\\3rdparty\\extensions\\test");
|
| + EXPECT_TRUE(WriteSchema(schema, HKEY_CURRENT_USER, kPathSuffix, kSchema));
|
| +
|
| + // Write some test values.
|
| + base::DictionaryValue policy;
|
| + // These special values have a specific schema for them.
|
| + policy.SetInteger("special-int1", 123);
|
| + policy.SetString("special-int2", "-456");
|
| + // Other values default to be loaded as doubles.
|
| + policy.SetInteger("double1", 789.0);
|
| + policy.SetString("double2", "123.456e7");
|
| + policy.SetString("invalid", "omg");
|
| + EXPECT_TRUE(InstallValue(policy, HKEY_CURRENT_USER, kPathSuffix, kMandatory));
|
| +
|
| + base::DictionaryValue expected_policy;
|
| + expected_policy.SetInteger("special-int1", 123);
|
| + expected_policy.SetInteger("special-int2", -456);
|
| + expected_policy.SetDouble("double1", 789.0);
|
| + expected_policy.SetDouble("double2", 123.456e7);
|
| + PolicyBundle expected;
|
| + expected.Get(POLICY_DOMAIN_EXTENSIONS, "test")
|
| + .LoadFrom(&expected_policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER);
|
| + EXPECT_TRUE(Matches(expected));
|
| }
|
|
|
| } // namespace policy
|
|
|