diff options
author | dconnelly@chromium.org <dconnelly@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-26 13:53:37 +0000 |
---|---|---|
committer | dconnelly@chromium.org <dconnelly@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-26 13:53:37 +0000 |
commit | 23a8cf5cd9e002b27fdb038c44f34faf5ecb6751 (patch) | |
tree | 287c8db5d89332514ed83d61a59c6de4e94cf797 /components/policy | |
parent | 6b1445545b12abd150153c9573de92992acd855e (diff) | |
download | chromium_src-23a8cf5cd9e002b27fdb038c44f34faf5ecb6751.zip chromium_src-23a8cf5cd9e002b27fdb038c44f34faf5ecb6751.tar.gz chromium_src-23a8cf5cd9e002b27fdb038c44f34faf5ecb6751.tar.bz2 |
Move RegistryDict to components/policy/.
This facilitates the refactoring of chrome/browser/policy into a layered
component.
BUG=271392
Review URL: https://codereview.chromium.org/79063003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@237330 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/policy')
-rw-r--r-- | components/policy/core/common/registry_dict_win.cc | 400 | ||||
-rw-r--r-- | components/policy/core/common/registry_dict_win.h | 91 | ||||
-rw-r--r-- | components/policy/core/common/registry_dict_win_unittest.cc | 241 |
3 files changed, 732 insertions, 0 deletions
diff --git a/components/policy/core/common/registry_dict_win.cc b/components/policy/core/common/registry_dict_win.cc new file mode 100644 index 0000000..7673079 --- /dev/null +++ b/components/policy/core/common/registry_dict_win.cc @@ -0,0 +1,400 @@ +// Copyright 2013 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 "components/policy/core/common/registry_dict_win.h" + +#include "base/json/json_reader.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/sys_byteorder.h" +#include "base/values.h" +#include "base/win/registry.h" +#include "components/json_schema/json_schema_constants.h" + +namespace schema = json_schema_constants; + +using base::win::RegistryKeyIterator; +using base::win::RegistryValueIterator; + +namespace policy { + +namespace { + +// Returns the entry with key |name| in |dictionary| (can be NULL), or NULL. +const base::DictionaryValue* GetEntry(const base::DictionaryValue* dictionary, + const std::string& name) { + if (!dictionary) + return NULL; + const base::DictionaryValue* entry = NULL; + dictionary->GetDictionaryWithoutPathExpansion(name, &entry); + return entry; +} + +// Returns the Value type described in |schema|, or |default_type| if not found. +base::Value::Type GetValueTypeForSchema(const base::DictionaryValue* schema, + base::Value::Type default_type) { + // JSON-schema types to base::Value::Type mapping. + static const struct { + // JSON schema type. + const char* schema_type; + // Correspondent value type. + base::Value::Type value_type; + } kSchemaToValueTypeMap[] = { + { schema::kArray, base::Value::TYPE_LIST }, + { schema::kBoolean, base::Value::TYPE_BOOLEAN }, + { schema::kInteger, base::Value::TYPE_INTEGER }, + { schema::kNull, base::Value::TYPE_NULL }, + { schema::kNumber, base::Value::TYPE_DOUBLE }, + { schema::kObject, base::Value::TYPE_DICTIONARY }, + { schema::kString, base::Value::TYPE_STRING }, + }; + + if (!schema) + return default_type; + std::string type; + if (!schema->GetStringWithoutPathExpansion(schema::kType, &type)) + return default_type; + for (size_t i = 0; i < arraysize(kSchemaToValueTypeMap); ++i) { + if (type == kSchemaToValueTypeMap[i].schema_type) + return kSchemaToValueTypeMap[i].value_type; + } + return default_type; +} + +// Returns the schema for property |name| given the |schema| of an object. +// Returns the "additionalProperties" schema if no specific schema for +// |name| is present. Returns NULL if no schema is found. +const base::DictionaryValue* GetSchemaFor(const base::DictionaryValue* schema, + const std::string& name) { + const base::DictionaryValue* properties = + GetEntry(schema, schema::kProperties); + const base::DictionaryValue* sub_schema = GetEntry(properties, name); + if (sub_schema) + return sub_schema; + // "additionalProperties" can be a boolean, but that case is ignored. + return GetEntry(schema, schema::kAdditionalProperties); +} + +// Converts a value (as read from the registry) to meet |schema|, converting +// types as necessary. Unconvertible types will show up as NULL values in the +// result. +scoped_ptr<base::Value> ConvertValue(const base::Value& value, + const base::DictionaryValue* schema) { + // Figure out the type to convert to from the schema. + const base::Value::Type result_type( + GetValueTypeForSchema(schema, value.GetType())); + + // If the type is good already, go with it. + if (value.IsType(result_type)) { + // Recurse for complex types if there is a schema. + if (schema) { + const base::DictionaryValue* dict = NULL; + const base::ListValue* list = NULL; + if (value.GetAsDictionary(&dict)) { + scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue()); + for (base::DictionaryValue::Iterator entry(*dict); !entry.IsAtEnd(); + entry.Advance()) { + scoped_ptr<base::Value> converted_value( + ConvertValue(entry.value(), GetSchemaFor(schema, entry.key()))); + result->SetWithoutPathExpansion(entry.key(), + converted_value.release()); + } + return result.Pass(); + } else if (value.GetAsList(&list)) { + scoped_ptr<base::ListValue> result(new base::ListValue()); + const base::DictionaryValue* item_schema = + GetEntry(schema, schema::kItems); + for (base::ListValue::const_iterator entry(list->begin()); + entry != list->end(); ++entry) { + result->Append(ConvertValue(**entry, item_schema).release()); + } + return result.Pass(); + } + } + return make_scoped_ptr(value.DeepCopy()); + } + + // Else, do some conversions to map windows registry data types to JSON types. + std::string string_value; + int int_value = 0; + switch (result_type) { + case base::Value::TYPE_NULL: { + return make_scoped_ptr(base::Value::CreateNullValue()); + } + case base::Value::TYPE_BOOLEAN: { + // Accept booleans encoded as either string or integer. + if (value.GetAsInteger(&int_value) || + (value.GetAsString(&string_value) && + base::StringToInt(string_value, &int_value))) { + return make_scoped_ptr(Value::CreateBooleanValue(int_value != 0)); + } + break; + } + case base::Value::TYPE_INTEGER: { + // Integers may be string-encoded. + if (value.GetAsString(&string_value) && + base::StringToInt(string_value, &int_value)) { + return make_scoped_ptr(base::Value::CreateIntegerValue(int_value)); + } + break; + } + case base::Value::TYPE_DOUBLE: { + // Doubles may be string-encoded or integer-encoded. + double double_value = 0; + if (value.GetAsInteger(&int_value)) { + return make_scoped_ptr(base::Value::CreateDoubleValue(int_value)); + } else if (value.GetAsString(&string_value) && + base::StringToDouble(string_value, &double_value)) { + return make_scoped_ptr(base::Value::CreateDoubleValue(double_value)); + } + break; + } + case base::Value::TYPE_LIST: { + // Lists are encoded as subkeys with numbered value in the registry. + const base::DictionaryValue* dict = NULL; + if (value.GetAsDictionary(&dict)) { + scoped_ptr<base::ListValue> result(new base::ListValue()); + const base::DictionaryValue* item_schema = + GetEntry(schema, schema::kItems); + for (int i = 1; ; ++i) { + const base::Value* entry = NULL; + if (!dict->Get(base::IntToString(i), &entry)) + break; + result->Append(ConvertValue(*entry, item_schema).release()); + } + return result.Pass(); + } + // Fall through in order to accept lists encoded as JSON strings. + } + case base::Value::TYPE_DICTIONARY: { + // Dictionaries may be encoded as JSON strings. + if (value.GetAsString(&string_value)) { + scoped_ptr<base::Value> result(base::JSONReader::Read(string_value)); + if (result && result->IsType(result_type)) + return result.Pass(); + } + break; + } + case base::Value::TYPE_STRING: + case base::Value::TYPE_BINARY: + // No conversion possible. + break; + } + + LOG(WARNING) << "Failed to convert " << value.GetType() + << " to " << result_type; + return make_scoped_ptr(base::Value::CreateNullValue()); +} + +} // namespace + +bool CaseInsensitiveStringCompare::operator()(const std::string& a, + const std::string& b) const { + return base::strcasecmp(a.c_str(), b.c_str()) < 0; +} + +RegistryDict::RegistryDict() {} + +RegistryDict::~RegistryDict() { + ClearKeys(); + ClearValues(); +} + +RegistryDict* RegistryDict::GetKey(const std::string& name) { + KeyMap::iterator entry = keys_.find(name); + return entry != keys_.end() ? entry->second : NULL; +} + +const RegistryDict* RegistryDict::GetKey(const std::string& name) const { + KeyMap::const_iterator entry = keys_.find(name); + return entry != keys_.end() ? entry->second : NULL; +} + +void RegistryDict::SetKey(const std::string& name, + scoped_ptr<RegistryDict> dict) { + if (!dict) { + RemoveKey(name); + return; + } + + RegistryDict*& entry = keys_[name]; + delete entry; + entry = dict.release(); +} + +scoped_ptr<RegistryDict> RegistryDict::RemoveKey(const std::string& name) { + scoped_ptr<RegistryDict> result; + KeyMap::iterator entry = keys_.find(name); + if (entry != keys_.end()) { + result.reset(entry->second); + keys_.erase(entry); + } + return result.Pass(); +} + +void RegistryDict::ClearKeys() { + STLDeleteValues(&keys_); +} + +base::Value* RegistryDict::GetValue(const std::string& name) { + ValueMap::iterator entry = values_.find(name); + return entry != values_.end() ? entry->second : NULL; +} + +const base::Value* RegistryDict::GetValue(const std::string& name) const { + ValueMap::const_iterator entry = values_.find(name); + return entry != values_.end() ? entry->second : NULL; +} + +void RegistryDict::SetValue(const std::string& name, + scoped_ptr<base::Value> dict) { + if (!dict) { + RemoveValue(name); + return; + } + + Value*& entry = values_[name]; + delete entry; + entry = dict.release(); +} + +scoped_ptr<base::Value> RegistryDict::RemoveValue(const std::string& name) { + scoped_ptr<base::Value> result; + ValueMap::iterator entry = values_.find(name); + if (entry != values_.end()) { + result.reset(entry->second); + values_.erase(entry); + } + return result.Pass(); +} + +void RegistryDict::ClearValues() { + STLDeleteValues(&values_); +} + +void RegistryDict::Merge(const RegistryDict& other) { + for (KeyMap::const_iterator entry(other.keys_.begin()); + entry != other.keys_.end(); ++entry) { + RegistryDict*& subdict = keys_[entry->first]; + if (!subdict) + subdict = new RegistryDict(); + subdict->Merge(*entry->second); + } + + for (ValueMap::const_iterator entry(other.values_.begin()); + entry != other.values_.end(); ++entry) { + SetValue(entry->first, make_scoped_ptr(entry->second->DeepCopy())); + } +} + +void RegistryDict::Swap(RegistryDict* other) { + keys_.swap(other->keys_); + values_.swap(other->values_); +} + +void RegistryDict::ReadRegistry(HKEY hive, const string16& root) { + ClearKeys(); + ClearValues(); + + // First, read all the values of the key. + for (RegistryValueIterator it(hive, root.c_str()); it.Valid(); ++it) { + const std::string name = UTF16ToUTF8(it.Name()); + switch (it.Type()) { + case REG_SZ: + case REG_EXPAND_SZ: + SetValue( + name, + make_scoped_ptr(new base::StringValue(UTF16ToUTF8(it.Value())))); + continue; + case REG_DWORD_LITTLE_ENDIAN: + case REG_DWORD_BIG_ENDIAN: + if (it.ValueSize() == sizeof(DWORD)) { + DWORD dword_value = *(reinterpret_cast<const DWORD*>(it.Value())); + if (it.Type() == REG_DWORD_BIG_ENDIAN) + dword_value = base::NetToHost32(dword_value); + else + dword_value = base::ByteSwapToLE32(dword_value); + SetValue( + name, + make_scoped_ptr(base::Value::CreateIntegerValue(dword_value))); + continue; + } + case REG_NONE: + case REG_LINK: + case REG_MULTI_SZ: + case REG_RESOURCE_LIST: + case REG_FULL_RESOURCE_DESCRIPTOR: + case REG_RESOURCE_REQUIREMENTS_LIST: + case REG_QWORD_LITTLE_ENDIAN: + // Unsupported type, message gets logged below. + break; + } + + LOG(WARNING) << "Failed to read hive " << hive << " at " + << root << "\\" << name + << " type " << it.Type(); + } + + // Recurse for all subkeys. + for (RegistryKeyIterator it(hive, root.c_str()); it.Valid(); ++it) { + std::string name(UTF16ToUTF8(it.Name())); + scoped_ptr<RegistryDict> subdict(new RegistryDict()); + subdict->ReadRegistry(hive, root + L"\\" + it.Name()); + SetKey(name, subdict.Pass()); + } +} + +scoped_ptr<base::Value> RegistryDict::ConvertToJSON( + const base::DictionaryValue* schema) const { + base::Value::Type type = + GetValueTypeForSchema(schema, base::Value::TYPE_DICTIONARY); + switch (type) { + case base::Value::TYPE_DICTIONARY: { + scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue()); + for (RegistryDict::ValueMap::const_iterator entry(values_.begin()); + entry != values_.end(); ++entry) { + result->SetWithoutPathExpansion( + entry->first, + ConvertValue(*entry->second, + GetSchemaFor(schema, entry->first)).release()); + } + for (RegistryDict::KeyMap::const_iterator entry(keys_.begin()); + entry != keys_.end(); ++entry) { + result->SetWithoutPathExpansion( + entry->first, + entry->second->ConvertToJSON( + GetSchemaFor(schema, entry->first)).release()); + } + return result.Pass(); + } + case base::Value::TYPE_LIST: { + scoped_ptr<base::ListValue> result(new base::ListValue()); + const base::DictionaryValue* item_schema = + GetEntry(schema, schema::kItems); + for (int i = 1; ; ++i) { + const std::string name(base::IntToString(i)); + const RegistryDict* key = GetKey(name); + if (key) { + result->Append(key->ConvertToJSON(item_schema).release()); + continue; + } + const base::Value* value = GetValue(name); + if (value) { + result->Append(ConvertValue(*value, item_schema).release()); + continue; + } + break; + } + return result.Pass(); + } + default: + LOG(WARNING) << "Can't convert registry key to schema type " << type; + } + + return make_scoped_ptr(base::Value::CreateNullValue()); +} + +} // namespace policy diff --git a/components/policy/core/common/registry_dict_win.h b/components/policy/core/common/registry_dict_win.h new file mode 100644 index 0000000..1cce59b --- /dev/null +++ b/components/policy/core/common/registry_dict_win.h @@ -0,0 +1,91 @@ +// Copyright 2013 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. + +#ifndef COMPONENTS_POLICY_CORE_COMMON_REGISTRY_DICT_WIN_H_ +#define COMPONENTS_POLICY_CORE_COMMON_REGISTRY_DICT_WIN_H_ + +#include <windows.h> + +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" +#include "components/policy/policy_export.h" + +namespace base { +class DictionaryValue; +class Value; +} + +namespace policy { + +// A case-insensitive string comparison functor. +struct POLICY_EXPORT CaseInsensitiveStringCompare { + bool operator()(const std::string& a, const std::string& b) const; +}; + +// In-memory representation of a registry subtree. Using a +// base::DictionaryValue directly seems tempting, but that doesn't handle the +// registry's case-insensitive-but-case-preserving semantics properly. +class POLICY_EXPORT RegistryDict { + public: + typedef std::map<std::string, RegistryDict*, + CaseInsensitiveStringCompare> KeyMap; + typedef std::map<std::string, base::Value*, + CaseInsensitiveStringCompare> ValueMap; + + RegistryDict(); + ~RegistryDict(); + + // Returns a pointer to an existing key, NULL if not present. + RegistryDict* GetKey(const std::string& name); + const RegistryDict* GetKey(const std::string& name) const; + // Sets a key. If |dict| is NULL, clears that key. + void SetKey(const std::string& name, scoped_ptr<RegistryDict> dict); + // Removes a key. If the key doesn't exist, NULL is returned. + scoped_ptr<RegistryDict> RemoveKey(const std::string& name); + // Clears all keys. + void ClearKeys(); + + // Returns a pointer to a value, NULL if not present. + base::Value* GetValue(const std::string& name); + const base::Value* GetValue(const std::string& name) const; + // Sets a value. If |value| is NULL, removes the value. + void SetValue(const std::string& name, scoped_ptr<base::Value> value); + // Removes a value. If the value doesn't exist, NULL is returned. + scoped_ptr<base::Value> RemoveValue(const std::string& name); + // Clears all values. + void ClearValues(); + + // Merge keys and values from |other|, giving precedence to |other|. + void Merge(const RegistryDict& other); + + // Swap with |other|. + void Swap(RegistryDict* other); + + // Read a Windows registry subtree into this registry dictionary object. + void ReadRegistry(HKEY hive, const string16& root); + + // Converts the dictionary to base::Value representation. For key/value name + // collisions, the key wins. |schema| supplies an optional JSON schema that + // will be used to map types to base::Value types. The returned object is + // either a base::DictionaryValue or a base::ListValue. + scoped_ptr<base::Value> ConvertToJSON( + const base::DictionaryValue* schema) const; + + const KeyMap& keys() const { return keys_; } + const ValueMap& values() const { return values_; } + + private: + KeyMap keys_; + ValueMap values_; + + DISALLOW_COPY_AND_ASSIGN(RegistryDict); +}; + +} // namespace policy + +#endif // COMPONENTS_POLICY_CORE_COMMON_REGISTRY_DICT_WIN_H_ diff --git a/components/policy/core/common/registry_dict_win_unittest.cc b/components/policy/core/common/registry_dict_win_unittest.cc new file mode 100644 index 0000000..e413b54 --- /dev/null +++ b/components/policy/core/common/registry_dict_win_unittest.cc @@ -0,0 +1,241 @@ +// Copyright 2013 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 "components/policy/core/common/registry_dict_win.h" + +#include "base/values.h" +#include "components/json_schema/json_schema_constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace schema = json_schema_constants; + +namespace policy { +namespace { + +TEST(RegistryDictTest, SetAndGetValue) { + RegistryDict test_dict; + + base::FundamentalValue int_value(42); + base::StringValue string_value("fortytwo"); + + test_dict.SetValue("one", make_scoped_ptr(int_value.DeepCopy())); + EXPECT_EQ(1, test_dict.values().size()); + EXPECT_TRUE(base::Value::Equals(&int_value, test_dict.GetValue("one"))); + EXPECT_FALSE(test_dict.GetValue("two")); + + test_dict.SetValue("two", make_scoped_ptr(string_value.DeepCopy())); + EXPECT_EQ(2, test_dict.values().size()); + EXPECT_TRUE(base::Value::Equals(&int_value, test_dict.GetValue("one"))); + EXPECT_TRUE(base::Value::Equals(&string_value, test_dict.GetValue("two"))); + + scoped_ptr<base::Value> one(test_dict.RemoveValue("one")); + EXPECT_EQ(1, test_dict.values().size()); + EXPECT_TRUE(base::Value::Equals(&int_value, one.get())); + EXPECT_FALSE(test_dict.GetValue("one")); + EXPECT_TRUE(base::Value::Equals(&string_value, test_dict.GetValue("two"))); + + test_dict.ClearValues(); + EXPECT_FALSE(test_dict.GetValue("one")); + EXPECT_FALSE(test_dict.GetValue("two")); + EXPECT_TRUE(test_dict.values().empty()); +} + +TEST(RegistryDictTest, CaseInsensitiveButPreservingValueNames) { + RegistryDict test_dict; + + base::FundamentalValue int_value(42); + base::StringValue string_value("fortytwo"); + + test_dict.SetValue("One", make_scoped_ptr(int_value.DeepCopy())); + EXPECT_EQ(1, test_dict.values().size()); + EXPECT_TRUE(base::Value::Equals(&int_value, test_dict.GetValue("oNe"))); + + RegistryDict::ValueMap::const_iterator entry = test_dict.values().begin(); + ASSERT_NE(entry, test_dict.values().end()); + EXPECT_EQ("One", entry->first); + + test_dict.SetValue("ONE", make_scoped_ptr(string_value.DeepCopy())); + EXPECT_EQ(1, test_dict.values().size()); + EXPECT_TRUE(base::Value::Equals(&string_value, test_dict.GetValue("one"))); + + scoped_ptr<base::Value> removed_value(test_dict.RemoveValue("onE")); + EXPECT_TRUE(base::Value::Equals(&string_value, removed_value.get())); + EXPECT_TRUE(test_dict.values().empty()); +} + +TEST(RegistryDictTest, SetAndGetKeys) { + RegistryDict test_dict; + + base::FundamentalValue int_value(42); + base::StringValue string_value("fortytwo"); + + scoped_ptr<RegistryDict> subdict(new RegistryDict()); + subdict->SetValue("one", make_scoped_ptr(int_value.DeepCopy())); + test_dict.SetKey("two", subdict.Pass()); + EXPECT_EQ(1, test_dict.keys().size()); + RegistryDict* actual_subdict = test_dict.GetKey("two"); + ASSERT_TRUE(actual_subdict); + EXPECT_TRUE(base::Value::Equals(&int_value, actual_subdict->GetValue("one"))); + + subdict.reset(new RegistryDict()); + subdict->SetValue("three", make_scoped_ptr(string_value.DeepCopy())); + test_dict.SetKey("four", subdict.Pass()); + EXPECT_EQ(2, test_dict.keys().size()); + actual_subdict = test_dict.GetKey("two"); + ASSERT_TRUE(actual_subdict); + EXPECT_TRUE(base::Value::Equals(&int_value, actual_subdict->GetValue("one"))); + actual_subdict = test_dict.GetKey("four"); + ASSERT_TRUE(actual_subdict); + EXPECT_TRUE(base::Value::Equals(&string_value, + actual_subdict->GetValue("three"))); + + test_dict.ClearKeys(); + EXPECT_FALSE(test_dict.GetKey("one")); + EXPECT_FALSE(test_dict.GetKey("three")); + EXPECT_TRUE(test_dict.keys().empty()); +} + +TEST(RegistryDictTest, CaseInsensitiveButPreservingKeyNames) { + RegistryDict test_dict; + + base::FundamentalValue int_value(42); + + test_dict.SetKey("One", make_scoped_ptr(new RegistryDict())); + EXPECT_EQ(1, test_dict.keys().size()); + RegistryDict* actual_subdict = test_dict.GetKey("One"); + ASSERT_TRUE(actual_subdict); + EXPECT_TRUE(actual_subdict->values().empty()); + + RegistryDict::KeyMap::const_iterator entry = test_dict.keys().begin(); + ASSERT_NE(entry, test_dict.keys().end()); + EXPECT_EQ("One", entry->first); + + scoped_ptr<RegistryDict> subdict(new RegistryDict()); + subdict->SetValue("two", make_scoped_ptr(int_value.DeepCopy())); + test_dict.SetKey("ONE", subdict.Pass()); + EXPECT_EQ(1, test_dict.keys().size()); + actual_subdict = test_dict.GetKey("One"); + ASSERT_TRUE(actual_subdict); + EXPECT_TRUE(base::Value::Equals(&int_value, + actual_subdict->GetValue("two"))); + + scoped_ptr<RegistryDict> removed_key(test_dict.RemoveKey("one")); + ASSERT_TRUE(removed_key); + EXPECT_TRUE(base::Value::Equals(&int_value, + removed_key->GetValue("two"))); + EXPECT_TRUE(test_dict.keys().empty()); +} + +TEST(RegistryDictTest, Merge) { + RegistryDict dict_a; + RegistryDict dict_b; + + base::FundamentalValue int_value(42); + base::StringValue string_value("fortytwo"); + + dict_a.SetValue("one", make_scoped_ptr(int_value.DeepCopy())); + scoped_ptr<RegistryDict> subdict(new RegistryDict()); + subdict->SetValue("two", make_scoped_ptr(string_value.DeepCopy())); + dict_a.SetKey("three", subdict.Pass()); + + dict_b.SetValue("four", make_scoped_ptr(string_value.DeepCopy())); + subdict.reset(new RegistryDict()); + subdict->SetValue("two", make_scoped_ptr(int_value.DeepCopy())); + dict_b.SetKey("three", subdict.Pass()); + subdict.reset(new RegistryDict()); + subdict->SetValue("five", make_scoped_ptr(int_value.DeepCopy())); + dict_b.SetKey("six", subdict.Pass()); + + dict_a.Merge(dict_b); + + EXPECT_TRUE(base::Value::Equals(&int_value, dict_a.GetValue("one"))); + EXPECT_TRUE(base::Value::Equals(&string_value, dict_b.GetValue("four"))); + RegistryDict* actual_subdict = dict_a.GetKey("three"); + ASSERT_TRUE(actual_subdict); + EXPECT_TRUE(base::Value::Equals(&int_value, actual_subdict->GetValue("two"))); + actual_subdict = dict_a.GetKey("six"); + ASSERT_TRUE(actual_subdict); + EXPECT_TRUE(base::Value::Equals(&int_value, + actual_subdict->GetValue("five"))); +} + +TEST(RegistryDictTest, Swap) { + RegistryDict dict_a; + RegistryDict dict_b; + + base::FundamentalValue int_value(42); + base::StringValue string_value("fortytwo"); + + dict_a.SetValue("one", make_scoped_ptr(int_value.DeepCopy())); + dict_a.SetKey("two", make_scoped_ptr(new RegistryDict())); + dict_b.SetValue("three", make_scoped_ptr(string_value.DeepCopy())); + + dict_a.Swap(&dict_b); + + EXPECT_TRUE(base::Value::Equals(&int_value, dict_b.GetValue("one"))); + EXPECT_TRUE(dict_b.GetKey("two")); + EXPECT_FALSE(dict_b.GetValue("two")); + + EXPECT_TRUE(base::Value::Equals(&string_value, dict_a.GetValue("three"))); + EXPECT_FALSE(dict_a.GetValue("one")); + EXPECT_FALSE(dict_a.GetKey("two")); +} + +TEST(RegistryDictTest, ConvertToJSON) { + RegistryDict test_dict; + + base::FundamentalValue int_value(42); + base::StringValue string_value("fortytwo"); + + test_dict.SetValue("one", make_scoped_ptr(int_value.DeepCopy())); + scoped_ptr<RegistryDict> subdict(new RegistryDict()); + subdict->SetValue("two", make_scoped_ptr(string_value.DeepCopy())); + test_dict.SetKey("three", subdict.Pass()); + scoped_ptr<RegistryDict> list(new RegistryDict()); + list->SetValue("1", make_scoped_ptr(string_value.DeepCopy())); + test_dict.SetKey("four", list.Pass()); + + base::DictionaryValue schema; + scoped_ptr<base::DictionaryValue> list_schema(new base::DictionaryValue()); + list_schema->SetString(schema::kType, schema::kArray); + scoped_ptr<base::DictionaryValue> properties(new base::DictionaryValue()); + properties->Set("four", list_schema.release()); + schema.SetString(schema::kType, schema::kObject); + schema.Set(schema::kProperties, properties.release()); + + scoped_ptr<base::Value> actual(test_dict.ConvertToJSON(&schema)); + + base::DictionaryValue expected; + expected.Set("one", int_value.DeepCopy()); + scoped_ptr<base::DictionaryValue> expected_subdict( + new base::DictionaryValue()); + expected_subdict->Set("two", string_value.DeepCopy()); + expected.Set("three", expected_subdict.release()); + scoped_ptr<base::ListValue> expected_list(new base::ListValue()); + expected_list->Append(string_value.DeepCopy()); + expected.Set("four", expected_list.release()); + + EXPECT_TRUE(base::Value::Equals(actual.get(), &expected)); +} + +TEST(RegistryDictTest, KeyValueNameClashes) { + RegistryDict test_dict; + + base::FundamentalValue int_value(42); + base::StringValue string_value("fortytwo"); + + test_dict.SetValue("one", make_scoped_ptr(int_value.DeepCopy())); + scoped_ptr<RegistryDict> subdict(new RegistryDict()); + subdict->SetValue("two", make_scoped_ptr(string_value.DeepCopy())); + test_dict.SetKey("one", subdict.Pass()); + + EXPECT_TRUE(base::Value::Equals(&int_value, test_dict.GetValue("one"))); + RegistryDict* actual_subdict = test_dict.GetKey("one"); + ASSERT_TRUE(actual_subdict); + EXPECT_TRUE(base::Value::Equals(&string_value, + actual_subdict->GetValue("two"))); +} + +} // namespace +} // namespace policy |