summaryrefslogtreecommitdiffstats
path: root/components/policy
diff options
context:
space:
mode:
authorjoaodasilva@chromium.org <joaodasilva@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-16 10:51:26 +0000
committerjoaodasilva@chromium.org <joaodasilva@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-16 10:51:26 +0000
commit3a597ccf9f20e7c97bf9884e106c806266d3f074 (patch)
tree3245ab1254f285497cd467b9e88c109c278886d4 /components/policy
parent79286f1279a80699b84ab1c326c97a4bd1982780 (diff)
downloadchromium_src-3a597ccf9f20e7c97bf9884e106c806266d3f074.zip
chromium_src-3a597ccf9f20e7c97bf9884e106c806266d3f074.tar.gz
chromium_src-3a597ccf9f20e7c97bf9884e106c806266d3f074.tar.bz2
Added new policy Schema classes.
This implementation obsoletes PolicySchema and its existing users will be refactored. This implementation has the advantage of allowing compile-time static data to be generated and quickly loaded at runtime. That will allow building the Chrome policies schema without impact on startup. BUG=270667 TBR=joi@chromium.org Review URL: https://chromiumcodereview.appspot.com/23851022 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@223326 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/policy')
-rw-r--r--components/policy/core/common/schema.cc276
-rw-r--r--components/policy/core/common/schema.h150
-rw-r--r--components/policy/core/common/schema_internal.h45
-rw-r--r--components/policy/core/common/schema_unittest.cc338
4 files changed, 809 insertions, 0 deletions
diff --git a/components/policy/core/common/schema.cc b/components/policy/core/common/schema.cc
new file mode 100644
index 0000000..cbfde46
--- /dev/null
+++ b/components/policy/core/common/schema.cc
@@ -0,0 +1,276 @@
+// 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/schema.h"
+
+#include <algorithm>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "components/json_schema/json_schema_constants.h"
+#include "components/json_schema/json_schema_validator.h"
+#include "components/policy/core/common/schema_internal.h"
+
+namespace policy {
+
+namespace {
+
+bool SchemaTypeToValueType(const std::string& type_string,
+ base::Value::Type* type) {
+ // Note: "any" is not an accepted type.
+ static const struct {
+ const char* schema_type;
+ base::Value::Type value_type;
+ } kSchemaToValueTypeMap[] = {
+ { json_schema_constants::kArray, base::Value::TYPE_LIST },
+ { json_schema_constants::kBoolean, base::Value::TYPE_BOOLEAN },
+ { json_schema_constants::kInteger, base::Value::TYPE_INTEGER },
+ { json_schema_constants::kNull, base::Value::TYPE_NULL },
+ { json_schema_constants::kNumber, base::Value::TYPE_DOUBLE },
+ { json_schema_constants::kObject, base::Value::TYPE_DICTIONARY },
+ { json_schema_constants::kString, base::Value::TYPE_STRING },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) {
+ if (kSchemaToValueTypeMap[i].schema_type == type_string) {
+ *type = kSchemaToValueTypeMap[i].value_type;
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+Schema::Iterator::Iterator(const internal::PropertiesNode* properties)
+ : it_(properties->begin),
+ end_(properties->end) {}
+
+Schema::Iterator::Iterator(const Iterator& iterator)
+ : it_(iterator.it_),
+ end_(iterator.end_) {}
+
+Schema::Iterator::~Iterator() {}
+
+Schema::Iterator& Schema::Iterator::operator=(const Iterator& iterator) {
+ it_ = iterator.it_;
+ end_ = iterator.end_;
+ return *this;
+}
+
+bool Schema::Iterator::IsAtEnd() const {
+ return it_ == end_;
+}
+
+void Schema::Iterator::Advance() {
+ ++it_;
+}
+
+const char* Schema::Iterator::key() const {
+ return it_->key;
+}
+
+Schema Schema::Iterator::schema() const {
+ return Schema(it_->schema);
+}
+
+Schema::Schema(const internal::SchemaNode* schema) : schema_(schema) {}
+
+Schema::Schema(const Schema& schema) : schema_(schema.schema_) {}
+
+Schema& Schema::operator=(const Schema& schema) {
+ schema_ = schema.schema_;
+ return *this;
+}
+
+base::Value::Type Schema::type() const {
+ CHECK(valid());
+ return schema_->type;
+}
+
+Schema::Iterator Schema::GetPropertiesIterator() const {
+ CHECK(valid());
+ CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
+ return Iterator(
+ static_cast<const internal::PropertiesNode*>(schema_->extra));
+}
+
+namespace {
+
+bool CompareKeys(const internal::PropertyNode& node, const std::string& key) {
+ return node.key < key;
+}
+
+} // namespace
+
+Schema Schema::GetKnownProperty(const std::string& key) const {
+ CHECK(valid());
+ CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
+ const internal::PropertiesNode* properties_node =
+ static_cast<const internal::PropertiesNode*>(schema_->extra);
+ const internal::PropertyNode* it = std::lower_bound(
+ properties_node->begin, properties_node->end, key, CompareKeys);
+ if (it != properties_node->end && it->key == key)
+ return Schema(it->schema);
+ return Schema(NULL);
+}
+
+Schema Schema::GetAdditionalProperties() const {
+ CHECK(valid());
+ CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
+ return Schema(
+ static_cast<const internal::PropertiesNode*>(schema_->extra)->additional);
+}
+
+Schema Schema::GetProperty(const std::string& key) const {
+ Schema schema = GetKnownProperty(key);
+ return schema.valid() ? schema : GetAdditionalProperties();
+}
+
+Schema Schema::GetItems() const {
+ CHECK(valid());
+ CHECK_EQ(base::Value::TYPE_LIST, type());
+ return Schema(static_cast<const internal::SchemaNode*>(schema_->extra));
+}
+
+SchemaOwner::SchemaOwner(const internal::SchemaNode* root) : root_(root) {}
+
+SchemaOwner::~SchemaOwner() {
+ for (size_t i = 0; i < property_nodes_.size(); ++i)
+ delete[] property_nodes_[i];
+}
+
+// static
+scoped_ptr<SchemaOwner> SchemaOwner::Wrap(const internal::SchemaNode* schema) {
+ return scoped_ptr<SchemaOwner>(new SchemaOwner(schema));
+}
+
+// static
+scoped_ptr<SchemaOwner> SchemaOwner::Parse(const std::string& content,
+ std::string* error) {
+ // Validate as a generic JSON schema.
+ scoped_ptr<base::DictionaryValue> dict =
+ JSONSchemaValidator::IsValidSchema(content, error);
+ if (!dict)
+ return scoped_ptr<SchemaOwner>();
+
+ // Validate the main type.
+ std::string string_value;
+ if (!dict->GetString(json_schema_constants::kType, &string_value) ||
+ string_value != json_schema_constants::kObject) {
+ *error =
+ "The main schema must have a type attribute with \"object\" value.";
+ return scoped_ptr<SchemaOwner>();
+ }
+
+ // Checks for invalid attributes at the top-level.
+ if (dict->HasKey(json_schema_constants::kAdditionalProperties) ||
+ dict->HasKey(json_schema_constants::kPatternProperties)) {
+ *error = "\"additionalProperties\" and \"patternProperties\" are not "
+ "supported at the main schema.";
+ return scoped_ptr<SchemaOwner>();
+ }
+
+ scoped_ptr<SchemaOwner> impl(new SchemaOwner(NULL));
+ impl->root_ = impl->Parse(*dict, error);
+ if (!impl->root_)
+ impl.reset();
+ return impl.PassAs<SchemaOwner>();
+}
+
+const internal::SchemaNode* SchemaOwner::Parse(
+ const base::DictionaryValue& schema,
+ std::string* error) {
+ std::string type_string;
+ if (!schema.GetString(json_schema_constants::kType, &type_string)) {
+ *error = "The schema type must be declared.";
+ return NULL;
+ }
+
+ base::Value::Type type = base::Value::TYPE_NULL;
+ if (!SchemaTypeToValueType(type_string, &type)) {
+ *error = "Type not supported: " + type_string;
+ return NULL;
+ }
+
+ if (type == base::Value::TYPE_DICTIONARY)
+ return ParseDictionary(schema, error);
+ if (type == base::Value::TYPE_LIST)
+ return ParseList(schema, error);
+
+ internal::SchemaNode* node = new internal::SchemaNode;
+ node->type = type;
+ node->extra = NULL;
+ schema_nodes_.push_back(node);
+ return node;
+}
+
+const internal::SchemaNode* SchemaOwner::ParseDictionary(
+ const base::DictionaryValue& schema,
+ std::string* error) {
+ internal::PropertiesNode* properties_node = new internal::PropertiesNode;
+ properties_node->begin = NULL;
+ properties_node->end = NULL;
+ properties_node->additional = NULL;
+ properties_nodes_.push_back(properties_node);
+
+ const base::DictionaryValue* dict = NULL;
+ const base::DictionaryValue* properties = NULL;
+ if (schema.GetDictionary(json_schema_constants::kProperties, &properties)) {
+ internal::PropertyNode* property_nodes =
+ new internal::PropertyNode[properties->size()];
+ property_nodes_.push_back(property_nodes);
+
+ size_t index = 0;
+ for (base::DictionaryValue::Iterator it(*properties);
+ !it.IsAtEnd(); it.Advance(), ++index) {
+ // This should have been verified by the JSONSchemaValidator.
+ CHECK(it.value().GetAsDictionary(&dict));
+ const internal::SchemaNode* sub_schema = Parse(*dict, error);
+ if (!sub_schema)
+ return NULL;
+ std::string* key = new std::string(it.key());
+ keys_.push_back(key);
+ property_nodes[index].key = key->c_str();
+ property_nodes[index].schema = sub_schema;
+ }
+ CHECK_EQ(properties->size(), index);
+ properties_node->begin = property_nodes;
+ properties_node->end = property_nodes + index;
+ }
+
+ if (schema.GetDictionary(json_schema_constants::kAdditionalProperties,
+ &dict)) {
+ const internal::SchemaNode* sub_schema = Parse(*dict, error);
+ if (!sub_schema)
+ return NULL;
+ properties_node->additional = sub_schema;
+ }
+
+ internal::SchemaNode* schema_node = new internal::SchemaNode;
+ schema_node->type = base::Value::TYPE_DICTIONARY;
+ schema_node->extra = properties_node;
+ schema_nodes_.push_back(schema_node);
+ return schema_node;
+}
+
+const internal::SchemaNode* SchemaOwner::ParseList(
+ const base::DictionaryValue& schema,
+ std::string* error) {
+ const base::DictionaryValue* dict = NULL;
+ if (!schema.GetDictionary(json_schema_constants::kItems, &dict)) {
+ *error = "Arrays must declare a single schema for their items.";
+ return NULL;
+ }
+ const internal::SchemaNode* items_schema_node = Parse(*dict, error);
+ if (!items_schema_node)
+ return NULL;
+
+ internal::SchemaNode* schema_node = new internal::SchemaNode;
+ schema_node->type = base::Value::TYPE_LIST;
+ schema_node->extra = items_schema_node;
+ schema_nodes_.push_back(schema_node);
+ return schema_node;
+}
+
+} // namespace policy
diff --git a/components/policy/core/common/schema.h b/components/policy/core/common/schema.h
new file mode 100644
index 0000000..a81b7ad
--- /dev/null
+++ b/components/policy/core/common/schema.h
@@ -0,0 +1,150 @@
+// 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_SCHEMA_H_
+#define COMPONENTS_POLICY_CORE_COMMON_SCHEMA_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/values.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+namespace internal {
+
+struct POLICY_EXPORT SchemaNode;
+struct POLICY_EXPORT PropertyNode;
+struct POLICY_EXPORT PropertiesNode;
+
+} // namespace internal
+
+// Describes the expected type of one policy. Also recursively describes the
+// types of inner elements, for structured types.
+// Objects of this class refer to external, immutable data and are cheap to
+// copy.
+// Use the SchemaOwner class to parse a schema and get Schema objects.
+class POLICY_EXPORT Schema {
+ public:
+ explicit Schema(const internal::SchemaNode* schema);
+ Schema(const Schema& schema);
+
+ Schema& operator=(const Schema& schema);
+
+ // Returns true if this Schema is valid. Schemas returned by the methods below
+ // may be invalid, and in those cases the other methods must not be used.
+ bool valid() const { return schema_ != NULL; }
+
+ base::Value::Type type() const;
+
+ // Used to iterate over the known properties of TYPE_DICTIONARY schemas.
+ class POLICY_EXPORT Iterator {
+ public:
+ explicit Iterator(const internal::PropertiesNode* properties);
+ Iterator(const Iterator& iterator);
+ ~Iterator();
+
+ Iterator& operator=(const Iterator& iterator);
+
+ // The other methods must not be called if the iterator is at the end.
+ bool IsAtEnd() const;
+
+ // Advances the iterator to the next property.
+ void Advance();
+
+ // Returns the name of the current property.
+ const char* key() const;
+
+ // Returns the Schema for the current property. This Schema is always valid.
+ Schema schema() const;
+
+ private:
+ const internal::PropertyNode* it_;
+ const internal::PropertyNode* end_;
+ };
+
+ // These methods should be called only if type() == TYPE_DICTIONARY,
+ // otherwise invalid memory will be read. A CHECK is currently enforcing this.
+
+ // Returns an iterator that goes over the named properties of this schema.
+ // The returned iterator is at the beginning.
+ Iterator GetPropertiesIterator() const;
+
+ // Returns the Schema for the property named |key|. If |key| is not a known
+ // property name then the returned Schema is not valid.
+ Schema GetKnownProperty(const std::string& key) const;
+
+ // Returns the Schema for additional properties. If additional properties are
+ // not allowed for this Schema then the Schema returned is not valid.
+ Schema GetAdditionalProperties() const;
+
+ // Returns the Schema for |key| if it is a known property, otherwise returns
+ // the Schema for additional properties.
+ Schema GetProperty(const std::string& key) const;
+
+ // Returns the Schema for items of an array.
+ // This method should be called only if type() == TYPE_LIST,
+ // otherwise invalid memory will be read. A CHECK is currently enforcing this.
+ Schema GetItems() const;
+
+ private:
+ const internal::SchemaNode* schema_;
+};
+
+// Owns schemas for policies of a given component.
+class POLICY_EXPORT SchemaOwner {
+ public:
+ ~SchemaOwner();
+
+ // The returned Schema is valid only during the lifetime of the SchemaOwner
+ // that created it. It may be obtained multiple times.
+ Schema schema() const { return Schema(root_); }
+
+ // Returns a SchemaOwner that references static data. This can be used by
+ // the embedder to pass structures generated at compile time, which can then
+ // be quickly loaded at runtime.
+ // Note: PropertiesNodes must have their PropertyNodes sorted by key.
+ static scoped_ptr<SchemaOwner> Wrap(const internal::SchemaNode* schema);
+
+ // Parses the JSON schema in |schema| and returns a SchemaOwner that owns
+ // the internal representation. If |schema| is invalid then NULL is returned
+ // and |error| contains a reason for the failure.
+ static scoped_ptr<SchemaOwner> Parse(const std::string& schema,
+ std::string* error);
+
+ private:
+ explicit SchemaOwner(const internal::SchemaNode* root);
+
+ // Parses the JSON schema in |schema| and returns the root SchemaNode if
+ // successful, otherwise returns NULL. Any intermediate objects built by
+ // this method are appended to the ScopedVectors.
+ const internal::SchemaNode* Parse(const base::DictionaryValue& schema,
+ std::string* error);
+
+ // Helper for Parse().
+ const internal::SchemaNode* ParseDictionary(
+ const base::DictionaryValue& schema,
+ std::string* error);
+
+ // Helper for Parse().
+ const internal::SchemaNode* ParseList(const base::DictionaryValue& schema,
+ std::string* error);
+
+ const internal::SchemaNode* root_;
+ ScopedVector<internal::SchemaNode> schema_nodes_;
+ // Note: |property_nodes_| contains PropertyNode[] elements and must be
+ // cleared manually to properly use delete[].
+ std::vector<internal::PropertyNode*> property_nodes_;
+ ScopedVector<internal::PropertiesNode> properties_nodes_;
+ ScopedVector<std::string> keys_;
+
+ DISALLOW_COPY_AND_ASSIGN(SchemaOwner);
+};
+
+} // namespace policy
+
+#endif // COMPONENTS_POLICY_CORE_COMMON_SCHEMA_H_
diff --git a/components/policy/core/common/schema_internal.h b/components/policy/core/common/schema_internal.h
new file mode 100644
index 0000000..025a437
--- /dev/null
+++ b/components/policy/core/common/schema_internal.h
@@ -0,0 +1,45 @@
+// 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_SCHEMA_INTERNAL_H_
+#define COMPONENTS_POLICY_CORE_COMMON_SCHEMA_INTERNAL_H_
+
+#include "base/values.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+namespace internal {
+
+// These types are used internally by the SchemaOwner parser, and by the
+// compile-time code generator. They shouldn't be used directly.
+
+struct POLICY_EXPORT SchemaNode {
+ base::Value::Type type;
+
+ // If |type| is TYPE_LIST then this is a SchemaNode* describing the
+ // element type.
+ //
+ // If |type| is TYPE_DICTIONARY then this is a PropertiesNode* that can
+ // contain any number of named properties and optionally a SchemaNode* for
+ // additional properties.
+ //
+ // This is NULL if |type| has any other values.
+ const void* extra;
+};
+
+struct POLICY_EXPORT PropertyNode {
+ const char* key;
+ const SchemaNode* schema;
+};
+
+struct POLICY_EXPORT PropertiesNode {
+ const PropertyNode* begin;
+ const PropertyNode* end;
+ const SchemaNode* additional;
+};
+
+} // namespace internal
+} // namespace policy
+
+#endif // COMPONENTS_POLICY_CORE_COMMON_SCHEMA_INTERNAL_H_
diff --git a/components/policy/core/common/schema_unittest.cc b/components/policy/core/common/schema_unittest.cc
new file mode 100644
index 0000000..06fb3bf
--- /dev/null
+++ b/components/policy/core/common/schema_unittest.cc
@@ -0,0 +1,338 @@
+// 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/schema.h"
+
+#include "components/policy/core/common/schema_internal.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+#define OBJECT_TYPE "\"type\":\"object\""
+
+const internal::SchemaNode kTypeBoolean = { base::Value::TYPE_BOOLEAN, NULL, };
+const internal::SchemaNode kTypeInteger = { base::Value::TYPE_INTEGER, NULL, };
+const internal::SchemaNode kTypeNumber = { base::Value::TYPE_DOUBLE, NULL, };
+const internal::SchemaNode kTypeString = { base::Value::TYPE_STRING, NULL, };
+
+bool ParseFails(const std::string& content) {
+ std::string error;
+ scoped_ptr<SchemaOwner> schema = SchemaOwner::Parse(content, &error);
+ if (schema)
+ EXPECT_TRUE(schema->schema().valid());
+ else
+ EXPECT_FALSE(error.empty());
+ return !schema;
+}
+
+} // namespace
+
+TEST(SchemaTest, MinimalSchema) {
+ EXPECT_FALSE(ParseFails(
+ "{"
+ OBJECT_TYPE
+ "}"));
+}
+
+TEST(SchemaTest, InvalidSchemas) {
+ EXPECT_TRUE(ParseFails(""));
+ EXPECT_TRUE(ParseFails("omg"));
+ EXPECT_TRUE(ParseFails("\"omg\""));
+ EXPECT_TRUE(ParseFails("123"));
+ EXPECT_TRUE(ParseFails("[]"));
+ EXPECT_TRUE(ParseFails("null"));
+ EXPECT_TRUE(ParseFails("{}"));
+
+ EXPECT_TRUE(ParseFails(
+ "{"
+ OBJECT_TYPE ","
+ "\"additionalProperties\": { \"type\":\"object\" }"
+ "}"));
+
+ EXPECT_TRUE(ParseFails(
+ "{"
+ OBJECT_TYPE ","
+ "\"patternProperties\": { \"a+b*\": { \"type\": \"object\" } }"
+ "}"));
+
+ EXPECT_TRUE(ParseFails(
+ "{"
+ OBJECT_TYPE ","
+ "\"properties\": { \"Policy\": { \"type\": \"bogus\" } }"
+ "}"));
+
+ EXPECT_TRUE(ParseFails(
+ "{"
+ OBJECT_TYPE ","
+ "\"properties\": { \"Policy\": { \"type\": [\"string\", \"number\"] } }"
+ "}"));
+
+ EXPECT_TRUE(ParseFails(
+ "{"
+ OBJECT_TYPE ","
+ "\"properties\": { \"Policy\": { \"type\": \"any\" } }"
+ "}"));
+}
+
+TEST(SchemaTest, ValidSchema) {
+ std::string error;
+ scoped_ptr<SchemaOwner> policy_schema = SchemaOwner::Parse(
+ "{"
+ OBJECT_TYPE ","
+ "\"properties\": {"
+ " \"Boolean\": { \"type\": \"boolean\" },"
+ " \"Integer\": { \"type\": \"integer\" },"
+ " \"Null\": { \"type\": \"null\" },"
+ " \"Number\": { \"type\": \"number\" },"
+ " \"String\": { \"type\": \"string\" },"
+ " \"Array\": {"
+ " \"type\": \"array\","
+ " \"items\": { \"type\": \"string\" }"
+ " },"
+ " \"ArrayOfObjects\": {"
+ " \"type\": \"array\","
+ " \"items\": {"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"one\": { \"type\": \"string\" },"
+ " \"two\": { \"type\": \"integer\" }"
+ " }"
+ " }"
+ " },"
+ " \"ArrayOfArray\": {"
+ " \"type\": \"array\","
+ " \"items\": {"
+ " \"type\": \"array\","
+ " \"items\": { \"type\": \"string\" }"
+ " }"
+ " },"
+ " \"Object\": {"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"one\": { \"type\": \"boolean\" },"
+ " \"two\": { \"type\": \"integer\" }"
+ " },"
+ " \"additionalProperties\": { \"type\": \"string\" }"
+ " }"
+ "}"
+ "}", &error);
+ ASSERT_TRUE(policy_schema) << error;
+ ASSERT_TRUE(policy_schema->schema().valid());
+
+ Schema schema = policy_schema->schema();
+ ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+ EXPECT_FALSE(schema.GetProperty("invalid").valid());
+
+ Schema sub = schema.GetProperty("Boolean");
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_BOOLEAN, sub.type());
+
+ sub = schema.GetProperty("Integer");
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_INTEGER, sub.type());
+
+ sub = schema.GetProperty("Null");
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_NULL, sub.type());
+
+ sub = schema.GetProperty("Number");
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_DOUBLE, sub.type());
+
+ sub = schema.GetProperty("String");
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
+
+ sub = schema.GetProperty("Array");
+ ASSERT_TRUE(sub.valid());
+ ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
+ sub = sub.GetItems();
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
+
+ sub = schema.GetProperty("ArrayOfObjects");
+ ASSERT_TRUE(sub.valid());
+ ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
+ sub = sub.GetItems();
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_DICTIONARY, sub.type());
+ Schema subsub = sub.GetProperty("one");
+ ASSERT_TRUE(subsub.valid());
+ EXPECT_EQ(base::Value::TYPE_STRING, subsub.type());
+ subsub = sub.GetProperty("two");
+ ASSERT_TRUE(subsub.valid());
+ EXPECT_EQ(base::Value::TYPE_INTEGER, subsub.type());
+ subsub = sub.GetProperty("invalid");
+ EXPECT_FALSE(subsub.valid());
+
+ sub = schema.GetProperty("ArrayOfArray");
+ ASSERT_TRUE(sub.valid());
+ ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
+ sub = sub.GetItems();
+ ASSERT_TRUE(sub.valid());
+ ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
+ sub = sub.GetItems();
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
+
+ sub = schema.GetProperty("Object");
+ ASSERT_TRUE(sub.valid());
+ ASSERT_EQ(base::Value::TYPE_DICTIONARY, sub.type());
+ subsub = sub.GetProperty("one");
+ ASSERT_TRUE(subsub.valid());
+ EXPECT_EQ(base::Value::TYPE_BOOLEAN, subsub.type());
+ subsub = sub.GetProperty("two");
+ ASSERT_TRUE(subsub.valid());
+ EXPECT_EQ(base::Value::TYPE_INTEGER, subsub.type());
+ subsub = sub.GetProperty("undeclared");
+ ASSERT_TRUE(subsub.valid());
+ EXPECT_EQ(base::Value::TYPE_STRING, subsub.type());
+
+ struct {
+ const char* expected_key;
+ base::Value::Type expected_type;
+ } kExpectedProperties[] = {
+ { "Array", base::Value::TYPE_LIST },
+ { "ArrayOfArray", base::Value::TYPE_LIST },
+ { "ArrayOfObjects", base::Value::TYPE_LIST },
+ { "Boolean", base::Value::TYPE_BOOLEAN },
+ { "Integer", base::Value::TYPE_INTEGER },
+ { "Null", base::Value::TYPE_NULL },
+ { "Number", base::Value::TYPE_DOUBLE },
+ { "Object", base::Value::TYPE_DICTIONARY },
+ { "String", base::Value::TYPE_STRING },
+ };
+ Schema::Iterator it = schema.GetPropertiesIterator();
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpectedProperties); ++i) {
+ ASSERT_FALSE(it.IsAtEnd());
+ EXPECT_STREQ(kExpectedProperties[i].expected_key, it.key());
+ ASSERT_TRUE(it.schema().valid());
+ EXPECT_EQ(kExpectedProperties[i].expected_type, it.schema().type());
+ it.Advance();
+ }
+ EXPECT_TRUE(it.IsAtEnd());
+}
+
+TEST(SchemaTest, Lookups) {
+ std::string error;
+ scoped_ptr<SchemaOwner> policy_schema = SchemaOwner::Parse(
+ "{"
+ OBJECT_TYPE
+ "}", &error);
+ ASSERT_TRUE(policy_schema) << error;
+ Schema schema = policy_schema->schema();
+ ASSERT_TRUE(schema.valid());
+ ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+
+ // This empty schema should never find named properties.
+ EXPECT_FALSE(schema.GetKnownProperty("").valid());
+ EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
+ EXPECT_TRUE(schema.GetPropertiesIterator().IsAtEnd());
+
+ policy_schema = SchemaOwner::Parse(
+ "{"
+ OBJECT_TYPE ","
+ "\"properties\": {"
+ " \"Boolean\": { \"type\": \"boolean\" }"
+ "}"
+ "}", &error);
+ ASSERT_TRUE(policy_schema) << error;
+ schema = policy_schema->schema();
+ ASSERT_TRUE(schema.valid());
+ ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+
+ EXPECT_FALSE(schema.GetKnownProperty("").valid());
+ EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
+ EXPECT_TRUE(schema.GetKnownProperty("Boolean").valid());
+
+ policy_schema = SchemaOwner::Parse(
+ "{"
+ OBJECT_TYPE ","
+ "\"properties\": {"
+ " \"bb\" : { \"type\": \"null\" },"
+ " \"aa\" : { \"type\": \"boolean\" },"
+ " \"abab\" : { \"type\": \"string\" },"
+ " \"ab\" : { \"type\": \"number\" },"
+ " \"aba\" : { \"type\": \"integer\" }"
+ "}"
+ "}", &error);
+ ASSERT_TRUE(policy_schema) << error;
+ schema = policy_schema->schema();
+ ASSERT_TRUE(schema.valid());
+ ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+
+ EXPECT_FALSE(schema.GetKnownProperty("").valid());
+ EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
+
+ struct {
+ const char* expected_key;
+ base::Value::Type expected_type;
+ } kExpectedKeys[] = {
+ { "aa", base::Value::TYPE_BOOLEAN },
+ { "ab", base::Value::TYPE_DOUBLE },
+ { "aba", base::Value::TYPE_INTEGER },
+ { "abab", base::Value::TYPE_STRING },
+ { "bb", base::Value::TYPE_NULL },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpectedKeys); ++i) {
+ Schema sub = schema.GetKnownProperty(kExpectedKeys[i].expected_key);
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(kExpectedKeys[i].expected_type, sub.type());
+ }
+}
+
+TEST(SchemaTest, WrapSimpleNode) {
+ scoped_ptr<SchemaOwner> policy_schema = SchemaOwner::Wrap(&kTypeString);
+ ASSERT_TRUE(policy_schema);
+ Schema schema = policy_schema->schema();
+ ASSERT_TRUE(schema.valid());
+ EXPECT_EQ(base::Value::TYPE_STRING, schema.type());
+}
+
+TEST(SchemaTest, WrapDictionary) {
+ const internal::SchemaNode kList = {
+ base::Value::TYPE_LIST,
+ &kTypeString,
+ };
+
+ const internal::PropertyNode kPropertyNodes[] = {
+ { "Boolean", &kTypeBoolean },
+ { "Integer", &kTypeInteger },
+ { "List", &kList },
+ { "Number", &kTypeNumber },
+ { "String", &kTypeString },
+ };
+
+ const internal::PropertiesNode kProperties = {
+ kPropertyNodes,
+ kPropertyNodes + arraysize(kPropertyNodes),
+ NULL,
+ };
+
+ const internal::SchemaNode root = {
+ base::Value::TYPE_DICTIONARY,
+ &kProperties,
+ };
+
+ scoped_ptr<SchemaOwner> policy_schema = SchemaOwner::Wrap(&root);
+ ASSERT_TRUE(policy_schema);
+ Schema schema = policy_schema->schema();
+ ASSERT_TRUE(schema.valid());
+ EXPECT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+
+ Schema::Iterator it = schema.GetPropertiesIterator();
+ for (size_t i = 0; i < arraysize(kPropertyNodes); ++i) {
+ ASSERT_FALSE(it.IsAtEnd());
+ EXPECT_STREQ(kPropertyNodes[i].key, it.key());
+ Schema sub = it.schema();
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(kPropertyNodes[i].schema->type, sub.type());
+ it.Advance();
+ }
+ EXPECT_TRUE(it.IsAtEnd());
+}
+
+} // namespace policy