This source file includes following definitions.
- ParseFails
- TestSchemaValidation
- TestSchemaValidationWithPath
- SchemaObjectWrapper
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
#include "components/policy/core/common/schema.h"
#include "base/memory/scoped_ptr.h"
#include "components/policy/core/common/schema_internal.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace policy {
namespace {
const char kTestSchema[] =
"{"
" \"type\": \"object\","
" \"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\" }"
" },"
" \"ObjectOfObject\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"Object\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"one\": { \"type\": \"string\" },"
" \"two\": { \"type\": \"integer\" }"
" }"
" }"
" }"
" },"
" \"IntegerWithEnums\": {"
" \"type\": \"integer\","
" \"enum\": [1, 2, 3]"
" },"
" \"IntegerWithEnumsGaps\": {"
" \"type\": \"integer\","
" \"enum\": [10, 20, 30]"
" },"
" \"StringWithEnums\": {"
" \"type\": \"string\","
" \"enum\": [\"one\", \"two\", \"three\"]"
" },"
" \"IntegerWithRange\": {"
" \"type\": \"integer\","
" \"minimum\": 1,"
" \"maximum\": 3"
" },"
" \"ObjectOfArray\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"List\": {"
" \"type\": \"array\","
" \"items\": { \"type\": \"integer\" }"
" }"
" }"
" },"
" \"ArrayOfObjectOfArray\": {"
" \"type\": \"array\","
" \"items\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"List\": {"
" \"type\": \"array\","
" \"items\": { \"type\": \"string\" }"
" }"
" }"
" }"
" }"
" }"
"}";
bool ParseFails(const std::string& content) {
std::string error;
Schema schema = Schema::Parse(content, &error);
if (schema.valid())
return false;
EXPECT_FALSE(error.empty());
return true;
}
void TestSchemaValidation(Schema schema,
const base::Value& value,
SchemaOnErrorStrategy strategy,
bool expected_return_value) {
std::string error;
static const char kNoErrorReturned[] = "No error returned.";
error = kNoErrorReturned;
bool returned = schema.Validate(value, strategy, NULL, &error);
EXPECT_EQ(returned, expected_return_value) << error;
error = kNoErrorReturned;
scoped_ptr<base::Value> cloned_value(value.DeepCopy());
bool touched = false;
returned =
schema.Normalize(cloned_value.get(), strategy, NULL, &error, &touched);
EXPECT_EQ(returned, expected_return_value) << error;
bool strictly_valid = schema.Validate(value, SCHEMA_STRICT, NULL, &error);
EXPECT_EQ(!strictly_valid && returned, touched);
if (expected_return_value) {
EXPECT_TRUE(
schema.Validate(*cloned_value.get(), SCHEMA_STRICT, NULL, &error));
EXPECT_TRUE(schema.Normalize(
cloned_value.get(), SCHEMA_STRICT, NULL, &error, NULL));
}
}
void TestSchemaValidationWithPath(Schema schema,
const base::Value& value,
const std::string& expected_failure_path) {
std::string error_path = "NOT_SET";
std::string error;
bool returned = schema.Validate(value, SCHEMA_STRICT, &error_path, &error);
ASSERT_FALSE(returned) << error_path;
EXPECT_EQ(error_path, expected_failure_path);
}
std::string SchemaObjectWrapper(const std::string& subschema) {
return "{"
" \"type\": \"object\","
" \"properties\": {"
" \"SomePropertyName\":" + subschema +
" }"
"}";
}
}
TEST(SchemaTest, MinimalSchema) {
EXPECT_FALSE(ParseFails("{ \"type\": \"object\" }"));
}
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(
"{"
" \"type\": \"object\","
"\"additionalProperties\": { \"type\":\"object\" }"
"}"));
EXPECT_TRUE(ParseFails(
"{"
" \"type\": \"object\","
" \"patternProperties\": { \"a+b*\": { \"type\": \"object\" } }"
"}"));
EXPECT_TRUE(ParseFails(
"{"
" \"type\": \"object\","
" \"properties\": { \"Policy\": { \"type\": \"bogus\" } }"
"}"));
EXPECT_TRUE(ParseFails(
"{"
" \"type\": \"object\","
" \"properties\": { \"Policy\": { \"type\": [\"string\", \"number\"] } }"
"}"));
EXPECT_TRUE(ParseFails(
"{"
" \"type\": \"object\","
" \"properties\": { \"Policy\": { \"type\": \"any\" } }"
"}"));
EXPECT_TRUE(ParseFails(
"{"
" \"type\": \"object\","
" \"properties\": { \"Policy\": 123 }"
"}"));
EXPECT_FALSE(ParseFails(
"{"
" \"type\": \"object\","
" \"unknown attribute\": \"is ignored\""
"}"));
}
TEST(SchemaTest, Ownership) {
std::string error;
Schema schema = Schema::Parse(
"{"
" \"type\": \"object\","
" \"properties\": {"
" \"sub\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"subsub\": { \"type\": \"string\" }"
" }"
" }"
" }"
"}", &error);
ASSERT_TRUE(schema.valid()) << error;
ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
schema = schema.GetKnownProperty("sub");
ASSERT_TRUE(schema.valid());
ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
{
Schema::Iterator it = schema.GetPropertiesIterator();
ASSERT_FALSE(it.IsAtEnd());
EXPECT_STREQ("subsub", it.key());
schema = it.schema();
it.Advance();
EXPECT_TRUE(it.IsAtEnd());
}
ASSERT_TRUE(schema.valid());
EXPECT_EQ(base::Value::TYPE_STRING, schema.type());
}
TEST(SchemaTest, ValidSchema) {
std::string error;
Schema schema = Schema::Parse(kTestSchema, &error);
ASSERT_TRUE(schema.valid()) << error;
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());
sub = schema.GetProperty("IntegerWithEnums");
ASSERT_TRUE(sub.valid());
ASSERT_EQ(base::Value::TYPE_INTEGER, sub.type());
sub = schema.GetProperty("IntegerWithEnumsGaps");
ASSERT_TRUE(sub.valid());
ASSERT_EQ(base::Value::TYPE_INTEGER, sub.type());
sub = schema.GetProperty("StringWithEnums");
ASSERT_TRUE(sub.valid());
ASSERT_EQ(base::Value::TYPE_STRING, sub.type());
sub = schema.GetProperty("IntegerWithRange");
ASSERT_TRUE(sub.valid());
ASSERT_EQ(base::Value::TYPE_INTEGER, sub.type());
struct {
const char* expected_key;
base::Value::Type expected_type;
} kExpectedProperties[] = {
{ "Array", base::Value::TYPE_LIST },
{ "ArrayOfArray", base::Value::TYPE_LIST },
{ "ArrayOfObjectOfArray", base::Value::TYPE_LIST },
{ "ArrayOfObjects", base::Value::TYPE_LIST },
{ "Boolean", base::Value::TYPE_BOOLEAN },
{ "Integer", base::Value::TYPE_INTEGER },
{ "IntegerWithEnums", base::Value::TYPE_INTEGER },
{ "IntegerWithEnumsGaps", base::Value::TYPE_INTEGER },
{ "IntegerWithRange", base::Value::TYPE_INTEGER },
{ "Null", base::Value::TYPE_NULL },
{ "Number", base::Value::TYPE_DOUBLE },
{ "Object", base::Value::TYPE_DICTIONARY },
{ "ObjectOfArray", base::Value::TYPE_DICTIONARY },
{ "ObjectOfObject", base::Value::TYPE_DICTIONARY },
{ "String", base::Value::TYPE_STRING },
{ "StringWithEnums", 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;
Schema schema = Schema::Parse("{ \"type\": \"object\" }", &error);
ASSERT_TRUE(schema.valid()) << error;
ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
EXPECT_FALSE(schema.GetKnownProperty("").valid());
EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
EXPECT_TRUE(schema.GetPropertiesIterator().IsAtEnd());
schema = Schema::Parse(
"{"
" \"type\": \"object\","
" \"properties\": {"
" \"Boolean\": { \"type\": \"boolean\" }"
" }"
"}", &error);
ASSERT_TRUE(schema.valid()) << error;
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());
schema = Schema::Parse(
"{"
" \"type\": \"object\","
" \"properties\": {"
" \"bb\" : { \"type\": \"null\" },"
" \"aa\" : { \"type\": \"boolean\" },"
" \"abab\" : { \"type\": \"string\" },"
" \"ab\" : { \"type\": \"number\" },"
" \"aba\" : { \"type\": \"integer\" }"
" }"
"}", &error);
ASSERT_TRUE(schema.valid()) << error;
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, Wrap) {
const internal::SchemaNode kSchemas[] = {
{ base::Value::TYPE_DICTIONARY, 0 },
{ base::Value::TYPE_BOOLEAN, -1 },
{ base::Value::TYPE_INTEGER, -1 },
{ base::Value::TYPE_DOUBLE, -1 },
{ base::Value::TYPE_STRING, -1 },
{ base::Value::TYPE_LIST, 4 },
{ base::Value::TYPE_LIST, 5 },
{ base::Value::TYPE_INTEGER, 0 },
{ base::Value::TYPE_INTEGER, 1 },
{ base::Value::TYPE_STRING, 2 },
};
const internal::PropertyNode kPropertyNodes[] = {
{ "Boolean", 1 },
{ "Integer", 2 },
{ "Number", 3 },
{ "String", 4 },
{ "List", 5 },
{ "IntEnum", 7 },
{ "RangedInt", 8 },
{ "StrEnum", 9 },
};
const internal::PropertiesNode kProperties[] = {
{ 0, 8, 6 },
};
const internal::RestrictionNode kRestriction[] = {
{{0, 3}},
{{5, 1}},
{{0, 3}},
};
const int kIntEnums[] = {1, 2, 3};
const char* kStringEnums[] = {
"one",
"two",
"three",
};
const internal::SchemaData kData = {
kSchemas,
kPropertyNodes,
kProperties,
kRestriction,
kIntEnums,
kStringEnums,
};
Schema schema = Schema::Wrap(&kData);
ASSERT_TRUE(schema.valid());
EXPECT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
struct {
const char* key;
base::Value::Type type;
} kExpectedProperties[] = {
{ "Boolean", base::Value::TYPE_BOOLEAN },
{ "Integer", base::Value::TYPE_INTEGER },
{ "Number", base::Value::TYPE_DOUBLE },
{ "String", base::Value::TYPE_STRING },
{ "List", base::Value::TYPE_LIST },
{ "IntEnum", base::Value::TYPE_INTEGER },
{ "RangedInt", base::Value::TYPE_INTEGER },
{ "StrEnum", 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].key, it.key());
Schema sub = it.schema();
ASSERT_TRUE(sub.valid());
EXPECT_EQ(kExpectedProperties[i].type, sub.type());
if (sub.type() == base::Value::TYPE_LIST) {
Schema items = sub.GetItems();
ASSERT_TRUE(items.valid());
EXPECT_EQ(base::Value::TYPE_STRING, items.type());
}
it.Advance();
}
EXPECT_TRUE(it.IsAtEnd());
Schema sub = schema.GetAdditionalProperties();
ASSERT_TRUE(sub.valid());
ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
Schema subsub = sub.GetItems();
ASSERT_TRUE(subsub.valid());
ASSERT_EQ(base::Value::TYPE_LIST, subsub.type());
Schema subsubsub = subsub.GetItems();
ASSERT_TRUE(subsubsub.valid());
ASSERT_EQ(base::Value::TYPE_STRING, subsubsub.type());
}
TEST(SchemaTest, Validate) {
std::string error;
Schema schema = Schema::Parse(kTestSchema, &error);
ASSERT_TRUE(schema.valid()) << error;
base::DictionaryValue bundle;
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
bundle.SetBoolean("Integer", true);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
{
bundle.Clear();
base::ListValue list;
list.AppendInteger(1);
bundle.Set("Array", list.DeepCopy());
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
}
{
bundle.Clear();
base::DictionaryValue dict;
dict.SetString("one", "one");
bundle.Set("Object", dict.DeepCopy());
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
}
bundle.Clear();
bundle.SetBoolean("Unknown", true);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
bundle.Clear();
bundle.SetBoolean("Boolean", true);
bundle.SetInteger("Integer", 123);
bundle.Set("Null", base::Value::CreateNullValue());
bundle.Set("Number", base::Value::CreateDoubleValue(3.14));
bundle.SetString("String", "omg");
{
base::ListValue list;
list.AppendString("a string");
list.AppendString("another string");
bundle.Set("Array", list.DeepCopy());
}
{
base::DictionaryValue dict;
dict.SetString("one", "string");
dict.SetInteger("two", 2);
base::ListValue list;
list.Append(dict.DeepCopy());
list.Append(dict.DeepCopy());
bundle.Set("ArrayOfObjects", list.DeepCopy());
}
{
base::ListValue list;
list.AppendString("a string");
list.AppendString("another string");
base::ListValue listlist;
listlist.Append(list.DeepCopy());
listlist.Append(list.DeepCopy());
bundle.Set("ArrayOfArray", listlist.DeepCopy());
}
{
base::DictionaryValue dict;
dict.SetBoolean("one", true);
dict.SetInteger("two", 2);
dict.SetString("additionally", "a string");
dict.SetString("and also", "another string");
bundle.Set("Object", dict.DeepCopy());
}
bundle.SetInteger("IntegerWithEnums", 1);
bundle.SetInteger("IntegerWithEnumsGaps", 20);
bundle.SetString("StringWithEnums", "two");
bundle.SetInteger("IntegerWithRange", 3);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
bundle.SetInteger("IntegerWithEnums", 0);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
bundle.SetInteger("IntegerWithEnums", 1);
bundle.SetInteger("IntegerWithEnumsGaps", 0);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
bundle.SetInteger("IntegerWithEnumsGaps", 9);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
bundle.SetInteger("IntegerWithEnumsGaps", 10);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
bundle.SetInteger("IntegerWithEnumsGaps", 11);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
bundle.SetInteger("IntegerWithEnumsGaps", 19);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
bundle.SetInteger("IntegerWithEnumsGaps", 21);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
bundle.SetInteger("IntegerWithEnumsGaps", 29);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
bundle.SetInteger("IntegerWithEnumsGaps", 30);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
bundle.SetInteger("IntegerWithEnumsGaps", 31);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
bundle.SetInteger("IntegerWithEnumsGaps", 100);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
bundle.SetInteger("IntegerWithEnumsGaps", 20);
bundle.SetString("StringWithEnums", "FOUR");
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
bundle.SetString("StringWithEnums", "two");
bundle.SetInteger("IntegerWithRange", 4);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
bundle.SetInteger("IntegerWithRange", 3);
bundle.SetString("boom", "bang");
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, true);
TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_UNKNOWN, true);
TestSchemaValidationWithPath(schema, bundle, "");
bundle.Remove("boom", NULL);
bundle.SetInteger("Boolean", 12345);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_INVALID, true);
TestSchemaValidationWithPath(schema, bundle, "Boolean");
bundle.SetBoolean("Boolean", true);
{
Schema subschema = schema.GetProperty("ObjectOfObject");
ASSERT_TRUE(subschema.valid());
base::DictionaryValue root;
root.SetBoolean("Object.three", false);
TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
TestSchemaValidationWithPath(subschema, root, "Object");
root.Remove("Object.three", NULL);
root.SetInteger("Object.one", 12345);
TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
TestSchemaValidationWithPath(subschema, root, "Object.one");
root.Remove("Object.one", NULL);
}
{
Schema subschema = schema.GetProperty("ArrayOfObjects");
ASSERT_TRUE(subschema.valid());
base::ListValue root;
base::DictionaryValue* dict_value = new base::DictionaryValue();
dict_value->SetBoolean("three", true);
root.Append(dict_value);
TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
TestSchemaValidationWithPath(subschema, root, "items[0]");
root.Remove(root.GetSize() - 1, NULL);
dict_value = new base::DictionaryValue();
dict_value->SetBoolean("two", true);
root.Append(dict_value);
TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
TestSchemaValidationWithPath(subschema, root, "items[0].two");
root.Remove(root.GetSize() - 1, NULL);
}
{
Schema subschema = schema.GetProperty("ObjectOfArray");
ASSERT_TRUE(subschema.valid());
base::DictionaryValue root;
base::ListValue* list_value = new base::ListValue();
root.Set("List", list_value);
list_value->Append(new base::FundamentalValue(12345));
TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
list_value->Append(new base::StringValue("blabla"));
TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
TestSchemaValidationWithPath(subschema, root, "List.items[1]");
}
{
Schema subschema = schema.GetProperty("ArrayOfObjectOfArray");
ASSERT_TRUE(subschema.valid());
base::ListValue root;
base::ListValue* list_value = new base::ListValue();
base::DictionaryValue* dict_value = new base::DictionaryValue();
dict_value->Set("List", list_value);
root.Append(dict_value);
list_value->Append(new base::StringValue("blabla"));
TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
list_value->Append(new base::FundamentalValue(12345));
TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
TestSchemaValidationWithPath(subschema, root, "items[0].List.items[1]");
}
bundle.SetInteger("Number", 31415);
TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
}
TEST(SchemaTest, InvalidReferences) {
EXPECT_TRUE(ParseFails(
"{"
" \"type\": \"object\","
" \"properties\": {"
" \"name\": { \"$ref\": \"undeclared\" }"
" }"
"}"));
EXPECT_TRUE(ParseFails(
"{"
" \"type\": \"object\","
" \"properties\": {"
" \"name\": {"
" \"id\": \"self\","
" \"$ref\": \"self\""
" }"
" }"
"}"));
EXPECT_TRUE(ParseFails(
"{"
" \"type\": \"object\","
" \"properties\": {"
" \"name\": {"
" \"id\": \"x\","
" \"type\": \"string\""
" },"
" \"another\": {"
" \"id\": \"x\","
" \"type\": \"string\""
" }"
" }"
"}"));
EXPECT_TRUE(ParseFails(
"{"
" \"type\": \"object\","
" \"id\": \"main\","
" \"$ref\": \"main\""
"}"));
EXPECT_TRUE(ParseFails(
"{"
" \"type\": \"object\","
" \"$ref\": \"main\""
"}"));
}
TEST(SchemaTest, RecursiveReferences) {
std::string error;
Schema schema = Schema::Parse(
"{"
" \"type\": \"object\","
" \"properties\": {"
" \"bookmarks\": {"
" \"type\": \"array\","
" \"id\": \"ListOfBookmarks\","
" \"items\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"name\": { \"type\": \"string\" },"
" \"url\": { \"type\": \"string\" },"
" \"children\": { \"$ref\": \"ListOfBookmarks\" }"
" }"
" }"
" }"
" }"
"}", &error);
ASSERT_TRUE(schema.valid()) << error;
ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
Schema parent = schema.GetKnownProperty("bookmarks");
ASSERT_TRUE(parent.valid());
ASSERT_EQ(base::Value::TYPE_LIST, parent.type());
for (int i = 0; i < 10; ++i) {
Schema items = parent.GetItems();
ASSERT_TRUE(items.valid());
ASSERT_EQ(base::Value::TYPE_DICTIONARY, items.type());
Schema prop = items.GetKnownProperty("name");
ASSERT_TRUE(prop.valid());
ASSERT_EQ(base::Value::TYPE_STRING, prop.type());
prop = items.GetKnownProperty("url");
ASSERT_TRUE(prop.valid());
ASSERT_EQ(base::Value::TYPE_STRING, prop.type());
prop = items.GetKnownProperty("children");
ASSERT_TRUE(prop.valid());
ASSERT_EQ(base::Value::TYPE_LIST, prop.type());
parent = prop;
}
}
TEST(SchemaTest, UnorderedReferences) {
std::string error;
Schema schema = Schema::Parse(
"{"
" \"type\": \"object\","
" \"properties\": {"
" \"a\": { \"$ref\": \"shared\" },"
" \"b\": { \"$ref\": \"shared\" },"
" \"c\": { \"$ref\": \"shared\" },"
" \"d\": { \"$ref\": \"shared\" },"
" \"e\": {"
" \"type\": \"boolean\","
" \"id\": \"shared\""
" },"
" \"f\": { \"$ref\": \"shared\" },"
" \"g\": { \"$ref\": \"shared\" },"
" \"h\": { \"$ref\": \"shared\" },"
" \"i\": { \"$ref\": \"shared\" }"
" }"
"}", &error);
ASSERT_TRUE(schema.valid()) << error;
ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
for (char c = 'a'; c <= 'i'; ++c) {
Schema sub = schema.GetKnownProperty(std::string(1, c));
ASSERT_TRUE(sub.valid()) << c;
ASSERT_EQ(base::Value::TYPE_BOOLEAN, sub.type()) << c;
}
}
TEST(SchemaTest, AdditionalPropertiesReference) {
std::string error;
Schema schema = Schema::Parse(
"{"
" \"type\": \"object\","
" \"properties\": {"
" \"policy\": {"
" \"type\": \"object\","
" \"properties\": {"
" \"foo\": {"
" \"type\": \"boolean\","
" \"id\": \"FooId\""
" }"
" },"
" \"additionalProperties\": { \"$ref\": \"FooId\" }"
" }"
" }"
"}", &error);
ASSERT_TRUE(schema.valid()) << error;
ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
Schema policy = schema.GetKnownProperty("policy");
ASSERT_TRUE(policy.valid());
ASSERT_EQ(base::Value::TYPE_DICTIONARY, policy.type());
Schema foo = policy.GetKnownProperty("foo");
ASSERT_TRUE(foo.valid());
EXPECT_EQ(base::Value::TYPE_BOOLEAN, foo.type());
Schema additional = policy.GetAdditionalProperties();
ASSERT_TRUE(additional.valid());
EXPECT_EQ(base::Value::TYPE_BOOLEAN, additional.type());
Schema x = policy.GetProperty("x");
ASSERT_TRUE(x.valid());
EXPECT_EQ(base::Value::TYPE_BOOLEAN, x.type());
}
TEST(SchemaTest, ItemsReference) {
std::string error;
Schema schema = Schema::Parse(
"{"
" \"type\": \"object\","
" \"properties\": {"
" \"foo\": {"
" \"type\": \"boolean\","
" \"id\": \"FooId\""
" },"
" \"list\": {"
" \"type\": \"array\","
" \"items\": { \"$ref\": \"FooId\" }"
" }"
" }"
"}", &error);
ASSERT_TRUE(schema.valid()) << error;
ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
Schema foo = schema.GetKnownProperty("foo");
ASSERT_TRUE(foo.valid());
EXPECT_EQ(base::Value::TYPE_BOOLEAN, foo.type());
Schema list = schema.GetKnownProperty("list");
ASSERT_TRUE(list.valid());
ASSERT_EQ(base::Value::TYPE_LIST, list.type());
Schema items = list.GetItems();
ASSERT_TRUE(items.valid());
ASSERT_EQ(base::Value::TYPE_BOOLEAN, items.type());
}
TEST(SchemaTest, EnumerationRestriction) {
EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
"{"
" \"type\": \"string\","
" \"enum\": 12"
"}")));
EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
"{"
" \"type\": \"integer\","
" \"enum\": []"
"}")));
EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
"{"
" \"type\": \"string\","
" \"enum\": [1, 2, 3]"
"}")));
EXPECT_FALSE(ParseFails(SchemaObjectWrapper(
"{"
" \"type\": \"integer\","
" \"enum\": [1, 2, 3]"
"}")));
EXPECT_FALSE(ParseFails(SchemaObjectWrapper(
"{"
" \"type\": \"string\","
" \"enum\": [\"1\", \"2\", \"3\"]"
"}")));
}
TEST(SchemaTest, RangedRestriction) {
EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
"{"
" \"type\": \"integer\","
" \"minimum\": 10,"
" \"maximum\": 5"
"}")));
EXPECT_FALSE(ParseFails(SchemaObjectWrapper(
"{"
" \"type\": \"integer\","
" \"minimum\": 10,"
" \"maximum\": 20"
"}")));
}
}