Home | History | Annotate | Download | only in common
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "components/policy/core/common/schema.h"
      6 
      7 #include "base/memory/scoped_ptr.h"
      8 #include "base/strings/stringprintf.h"
      9 #include "components/policy/core/common/schema_internal.h"
     10 #include "testing/gtest/include/gtest/gtest.h"
     11 
     12 namespace policy {
     13 
     14 namespace {
     15 
     16 #define TestSchemaValidation(a, b, c, d) \
     17     TestSchemaValidationHelper(          \
     18         base::StringPrintf("%s:%i", __FILE__, __LINE__), a, b, c, d)
     19 
     20 const char kTestSchema[] =
     21     "{"
     22     "  \"type\": \"object\","
     23     "  \"properties\": {"
     24     "    \"Boolean\": { \"type\": \"boolean\" },"
     25     "    \"Integer\": { \"type\": \"integer\" },"
     26     "    \"Null\": { \"type\": \"null\" },"
     27     "    \"Number\": { \"type\": \"number\" },"
     28     "    \"String\": { \"type\": \"string\" },"
     29     "    \"Array\": {"
     30     "      \"type\": \"array\","
     31     "      \"items\": { \"type\": \"string\" }"
     32     "    },"
     33     "    \"ArrayOfObjects\": {"
     34     "      \"type\": \"array\","
     35     "      \"items\": {"
     36     "        \"type\": \"object\","
     37     "        \"properties\": {"
     38     "          \"one\": { \"type\": \"string\" },"
     39     "          \"two\": { \"type\": \"integer\" }"
     40     "        }"
     41     "      }"
     42     "    },"
     43     "    \"ArrayOfArray\": {"
     44     "      \"type\": \"array\","
     45     "      \"items\": {"
     46     "        \"type\": \"array\","
     47     "        \"items\": { \"type\": \"string\" }"
     48     "      }"
     49     "    },"
     50     "    \"Object\": {"
     51     "      \"type\": \"object\","
     52     "      \"properties\": {"
     53     "        \"one\": { \"type\": \"boolean\" },"
     54     "        \"two\": { \"type\": \"integer\" }"
     55     "      },"
     56     "      \"additionalProperties\": { \"type\": \"string\" }"
     57     "    },"
     58     "    \"ObjectOfObject\": {"
     59     "      \"type\": \"object\","
     60     "      \"properties\": {"
     61     "        \"Object\": {"
     62     "          \"type\": \"object\","
     63     "          \"properties\": {"
     64     "            \"one\": { \"type\": \"string\" },"
     65     "            \"two\": { \"type\": \"integer\" }"
     66     "          }"
     67     "        }"
     68     "      }"
     69     "    },"
     70     "    \"IntegerWithEnums\": {"
     71     "      \"type\": \"integer\","
     72     "      \"enum\": [1, 2, 3]"
     73     "    },"
     74     "    \"IntegerWithEnumsGaps\": {"
     75     "      \"type\": \"integer\","
     76     "      \"enum\": [10, 20, 30]"
     77     "    },"
     78     "    \"StringWithEnums\": {"
     79     "      \"type\": \"string\","
     80     "      \"enum\": [\"one\", \"two\", \"three\"]"
     81     "    },"
     82     "    \"IntegerWithRange\": {"
     83     "      \"type\": \"integer\","
     84     "      \"minimum\": 1,"
     85     "      \"maximum\": 3"
     86     "    },"
     87     "    \"ObjectOfArray\": {"
     88     "      \"type\": \"object\","
     89     "      \"properties\": {"
     90     "        \"List\": {"
     91     "          \"type\": \"array\","
     92     "          \"items\": { \"type\": \"integer\" }"
     93     "        }"
     94     "      }"
     95     "    },"
     96     "    \"ArrayOfObjectOfArray\": {"
     97     "      \"type\": \"array\","
     98     "      \"items\": {"
     99     "        \"type\": \"object\","
    100     "        \"properties\": {"
    101     "          \"List\": {"
    102     "            \"type\": \"array\","
    103     "            \"items\": { \"type\": \"string\" }"
    104     "          }"
    105     "        }"
    106     "      }"
    107     "    },"
    108     "    \"StringWithPattern\": {"
    109     "      \"type\": \"string\","
    110     "      \"pattern\": \"^foo+$\""
    111     "    },"
    112     "    \"ObjectWithPatternProperties\": {"
    113     "      \"type\": \"object\","
    114     "      \"patternProperties\": {"
    115     "        \"^foo+$\": { \"type\": \"integer\" },"
    116     "        \"^bar+$\": {"
    117     "          \"type\": \"string\","
    118     "          \"enum\": [\"one\", \"two\"]"
    119     "        }"
    120     "      },"
    121     "      \"properties\": {"
    122     "        \"bar\": {"
    123     "          \"type\": \"string\","
    124     "          \"enum\": [\"one\", \"three\"]"
    125     "        }"
    126     "      }"
    127     "    }"
    128     "  }"
    129     "}";
    130 
    131 bool ParseFails(const std::string& content) {
    132   std::string error;
    133   Schema schema = Schema::Parse(content, &error);
    134   if (schema.valid())
    135     return false;
    136   EXPECT_FALSE(error.empty());
    137   return true;
    138 }
    139 
    140 void TestSchemaValidationHelper(const std::string& source,
    141                                 Schema schema,
    142                                 const base::Value& value,
    143                                 SchemaOnErrorStrategy strategy,
    144                                 bool expected_return_value) {
    145   std::string error;
    146   static const char kNoErrorReturned[] = "No error returned.";
    147 
    148   // Test that Schema::Validate() works as expected.
    149   error = kNoErrorReturned;
    150   bool returned = schema.Validate(value, strategy, NULL, &error);
    151   ASSERT_EQ(expected_return_value, returned) << source << ": " << error;
    152 
    153   // Test that Schema::Normalize() will return the same value as
    154   // Schema::Validate().
    155   error = kNoErrorReturned;
    156   scoped_ptr<base::Value> cloned_value(value.DeepCopy());
    157   bool touched = false;
    158   returned =
    159       schema.Normalize(cloned_value.get(), strategy, NULL, &error, &touched);
    160   EXPECT_EQ(expected_return_value, returned) << source << ": " << error;
    161 
    162   bool strictly_valid = schema.Validate(value, SCHEMA_STRICT, NULL, &error);
    163   EXPECT_EQ(touched, !strictly_valid && returned) << source;
    164 
    165   // Test that Schema::Normalize() have actually dropped invalid and unknown
    166   // properties.
    167   if (expected_return_value) {
    168     EXPECT_TRUE(
    169         schema.Validate(*cloned_value.get(), SCHEMA_STRICT, NULL, &error))
    170         << source;
    171     EXPECT_TRUE(
    172         schema.Normalize(cloned_value.get(), SCHEMA_STRICT, NULL, &error, NULL))
    173         << source;
    174   }
    175 }
    176 
    177 void TestSchemaValidationWithPath(Schema schema,
    178                                   const base::Value& value,
    179                                   const std::string& expected_failure_path) {
    180   std::string error_path = "NOT_SET";
    181   std::string error;
    182 
    183   bool returned = schema.Validate(value, SCHEMA_STRICT, &error_path, &error);
    184   ASSERT_FALSE(returned) << error_path;
    185   EXPECT_EQ(error_path, expected_failure_path);
    186 }
    187 
    188 std::string SchemaObjectWrapper(const std::string& subschema) {
    189   return "{"
    190          "  \"type\": \"object\","
    191          "  \"properties\": {"
    192          "    \"SomePropertyName\":" + subschema +
    193          "  }"
    194          "}";
    195 }
    196 
    197 }  // namespace
    198 
    199 TEST(SchemaTest, MinimalSchema) {
    200   EXPECT_FALSE(ParseFails("{ \"type\": \"object\" }"));
    201 }
    202 
    203 TEST(SchemaTest, InvalidSchemas) {
    204   EXPECT_TRUE(ParseFails(""));
    205   EXPECT_TRUE(ParseFails("omg"));
    206   EXPECT_TRUE(ParseFails("\"omg\""));
    207   EXPECT_TRUE(ParseFails("123"));
    208   EXPECT_TRUE(ParseFails("[]"));
    209   EXPECT_TRUE(ParseFails("null"));
    210   EXPECT_TRUE(ParseFails("{}"));
    211 
    212   EXPECT_TRUE(ParseFails(
    213       "{"
    214       "  \"type\": \"object\","
    215         "\"additionalProperties\": { \"type\":\"object\" }"
    216       "}"));
    217 
    218   EXPECT_TRUE(ParseFails(
    219       "{"
    220       "  \"type\": \"object\","
    221       "  \"patternProperties\": { \"a+b*\": { \"type\": \"object\" } }"
    222       "}"));
    223 
    224   EXPECT_TRUE(ParseFails(
    225       "{"
    226       "  \"type\": \"object\","
    227       "  \"properties\": { \"Policy\": { \"type\": \"bogus\" } }"
    228       "}"));
    229 
    230   EXPECT_TRUE(ParseFails(
    231       "{"
    232       "  \"type\": \"object\","
    233       "  \"properties\": { \"Policy\": { \"type\": [\"string\", \"number\"] } }"
    234       "}"));
    235 
    236   EXPECT_TRUE(ParseFails(
    237       "{"
    238       "  \"type\": \"object\","
    239       "  \"properties\": { \"Policy\": { \"type\": \"any\" } }"
    240       "}"));
    241 
    242   EXPECT_TRUE(ParseFails(
    243       "{"
    244       "  \"type\": \"object\","
    245       "  \"properties\": { \"Policy\": 123 }"
    246       "}"));
    247 
    248   EXPECT_FALSE(ParseFails(
    249       "{"
    250       "  \"type\": \"object\","
    251       "  \"unknown attribute\": \"is ignored\""
    252       "}"));
    253 }
    254 
    255 TEST(SchemaTest, Ownership) {
    256   std::string error;
    257   Schema schema = Schema::Parse(
    258       "{"
    259       "  \"type\": \"object\","
    260       "  \"properties\": {"
    261       "    \"sub\": {"
    262       "      \"type\": \"object\","
    263       "      \"properties\": {"
    264       "        \"subsub\": { \"type\": \"string\" }"
    265       "      }"
    266       "    }"
    267       "  }"
    268       "}", &error);
    269   ASSERT_TRUE(schema.valid()) << error;
    270   ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
    271 
    272   schema = schema.GetKnownProperty("sub");
    273   ASSERT_TRUE(schema.valid());
    274   ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
    275 
    276   {
    277     Schema::Iterator it = schema.GetPropertiesIterator();
    278     ASSERT_FALSE(it.IsAtEnd());
    279     EXPECT_STREQ("subsub", it.key());
    280 
    281     schema = it.schema();
    282     it.Advance();
    283     EXPECT_TRUE(it.IsAtEnd());
    284   }
    285 
    286   ASSERT_TRUE(schema.valid());
    287   EXPECT_EQ(base::Value::TYPE_STRING, schema.type());
    288 
    289   // This test shouldn't leak nor use invalid memory.
    290 }
    291 
    292 TEST(SchemaTest, ValidSchema) {
    293   std::string error;
    294   Schema schema = Schema::Parse(kTestSchema, &error);
    295   ASSERT_TRUE(schema.valid()) << error;
    296 
    297   ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
    298   EXPECT_FALSE(schema.GetProperty("invalid").valid());
    299 
    300   Schema sub = schema.GetProperty("Boolean");
    301   ASSERT_TRUE(sub.valid());
    302   EXPECT_EQ(base::Value::TYPE_BOOLEAN, sub.type());
    303 
    304   sub = schema.GetProperty("Integer");
    305   ASSERT_TRUE(sub.valid());
    306   EXPECT_EQ(base::Value::TYPE_INTEGER, sub.type());
    307 
    308   sub = schema.GetProperty("Null");
    309   ASSERT_TRUE(sub.valid());
    310   EXPECT_EQ(base::Value::TYPE_NULL, sub.type());
    311 
    312   sub = schema.GetProperty("Number");
    313   ASSERT_TRUE(sub.valid());
    314   EXPECT_EQ(base::Value::TYPE_DOUBLE, sub.type());
    315 
    316   sub = schema.GetProperty("String");
    317   ASSERT_TRUE(sub.valid());
    318   EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
    319 
    320   sub = schema.GetProperty("Array");
    321   ASSERT_TRUE(sub.valid());
    322   ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
    323   sub = sub.GetItems();
    324   ASSERT_TRUE(sub.valid());
    325   EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
    326 
    327   sub = schema.GetProperty("ArrayOfObjects");
    328   ASSERT_TRUE(sub.valid());
    329   ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
    330   sub = sub.GetItems();
    331   ASSERT_TRUE(sub.valid());
    332   EXPECT_EQ(base::Value::TYPE_DICTIONARY, sub.type());
    333   Schema subsub = sub.GetProperty("one");
    334   ASSERT_TRUE(subsub.valid());
    335   EXPECT_EQ(base::Value::TYPE_STRING, subsub.type());
    336   subsub = sub.GetProperty("two");
    337   ASSERT_TRUE(subsub.valid());
    338   EXPECT_EQ(base::Value::TYPE_INTEGER, subsub.type());
    339   subsub = sub.GetProperty("invalid");
    340   EXPECT_FALSE(subsub.valid());
    341 
    342   sub = schema.GetProperty("ArrayOfArray");
    343   ASSERT_TRUE(sub.valid());
    344   ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
    345   sub = sub.GetItems();
    346   ASSERT_TRUE(sub.valid());
    347   ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
    348   sub = sub.GetItems();
    349   ASSERT_TRUE(sub.valid());
    350   EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
    351 
    352   sub = schema.GetProperty("Object");
    353   ASSERT_TRUE(sub.valid());
    354   ASSERT_EQ(base::Value::TYPE_DICTIONARY, sub.type());
    355   subsub = sub.GetProperty("one");
    356   ASSERT_TRUE(subsub.valid());
    357   EXPECT_EQ(base::Value::TYPE_BOOLEAN, subsub.type());
    358   subsub = sub.GetProperty("two");
    359   ASSERT_TRUE(subsub.valid());
    360   EXPECT_EQ(base::Value::TYPE_INTEGER, subsub.type());
    361   subsub = sub.GetProperty("undeclared");
    362   ASSERT_TRUE(subsub.valid());
    363   EXPECT_EQ(base::Value::TYPE_STRING, subsub.type());
    364 
    365   sub = schema.GetProperty("IntegerWithEnums");
    366   ASSERT_TRUE(sub.valid());
    367   ASSERT_EQ(base::Value::TYPE_INTEGER, sub.type());
    368 
    369   sub = schema.GetProperty("IntegerWithEnumsGaps");
    370   ASSERT_TRUE(sub.valid());
    371   ASSERT_EQ(base::Value::TYPE_INTEGER, sub.type());
    372 
    373   sub = schema.GetProperty("StringWithEnums");
    374   ASSERT_TRUE(sub.valid());
    375   ASSERT_EQ(base::Value::TYPE_STRING, sub.type());
    376 
    377   sub = schema.GetProperty("IntegerWithRange");
    378   ASSERT_TRUE(sub.valid());
    379   ASSERT_EQ(base::Value::TYPE_INTEGER, sub.type());
    380 
    381   sub = schema.GetProperty("StringWithPattern");
    382   ASSERT_TRUE(sub.valid());
    383   ASSERT_EQ(base::Value::TYPE_STRING, sub.type());
    384 
    385   sub = schema.GetProperty("ObjectWithPatternProperties");
    386   ASSERT_TRUE(sub.valid());
    387   ASSERT_EQ(base::Value::TYPE_DICTIONARY, sub.type());
    388 
    389   struct {
    390     const char* expected_key;
    391     base::Value::Type expected_type;
    392   } kExpectedProperties[] = {
    393     { "Array",                       base::Value::TYPE_LIST },
    394     { "ArrayOfArray",                base::Value::TYPE_LIST },
    395     { "ArrayOfObjectOfArray",        base::Value::TYPE_LIST },
    396     { "ArrayOfObjects",              base::Value::TYPE_LIST },
    397     { "Boolean",                     base::Value::TYPE_BOOLEAN },
    398     { "Integer",                     base::Value::TYPE_INTEGER },
    399     { "IntegerWithEnums",            base::Value::TYPE_INTEGER },
    400     { "IntegerWithEnumsGaps",        base::Value::TYPE_INTEGER },
    401     { "IntegerWithRange",            base::Value::TYPE_INTEGER },
    402     { "Null",                        base::Value::TYPE_NULL },
    403     { "Number",                      base::Value::TYPE_DOUBLE },
    404     { "Object",                      base::Value::TYPE_DICTIONARY },
    405     { "ObjectOfArray",               base::Value::TYPE_DICTIONARY },
    406     { "ObjectOfObject",              base::Value::TYPE_DICTIONARY },
    407     { "ObjectWithPatternProperties", base::Value::TYPE_DICTIONARY },
    408     { "String",                      base::Value::TYPE_STRING },
    409     { "StringWithEnums",             base::Value::TYPE_STRING },
    410     { "StringWithPattern",           base::Value::TYPE_STRING },
    411   };
    412   Schema::Iterator it = schema.GetPropertiesIterator();
    413   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpectedProperties); ++i) {
    414     ASSERT_FALSE(it.IsAtEnd());
    415     EXPECT_STREQ(kExpectedProperties[i].expected_key, it.key());
    416     ASSERT_TRUE(it.schema().valid());
    417     EXPECT_EQ(kExpectedProperties[i].expected_type, it.schema().type());
    418     it.Advance();
    419   }
    420   EXPECT_TRUE(it.IsAtEnd());
    421 }
    422 
    423 TEST(SchemaTest, Lookups) {
    424   std::string error;
    425 
    426   Schema schema = Schema::Parse("{ \"type\": \"object\" }", &error);
    427   ASSERT_TRUE(schema.valid()) << error;
    428   ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
    429 
    430   // This empty schema should never find named properties.
    431   EXPECT_FALSE(schema.GetKnownProperty("").valid());
    432   EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
    433   EXPECT_TRUE(schema.GetPropertiesIterator().IsAtEnd());
    434 
    435   schema = Schema::Parse(
    436       "{"
    437       "  \"type\": \"object\","
    438       "  \"properties\": {"
    439       "    \"Boolean\": { \"type\": \"boolean\" }"
    440       "  }"
    441       "}", &error);
    442   ASSERT_TRUE(schema.valid()) << error;
    443   ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
    444 
    445   EXPECT_FALSE(schema.GetKnownProperty("").valid());
    446   EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
    447   EXPECT_TRUE(schema.GetKnownProperty("Boolean").valid());
    448 
    449   schema = Schema::Parse(
    450       "{"
    451       "  \"type\": \"object\","
    452       "  \"properties\": {"
    453       "    \"bb\" : { \"type\": \"null\" },"
    454       "    \"aa\" : { \"type\": \"boolean\" },"
    455       "    \"abab\" : { \"type\": \"string\" },"
    456       "    \"ab\" : { \"type\": \"number\" },"
    457       "    \"aba\" : { \"type\": \"integer\" }"
    458       "  }"
    459       "}", &error);
    460   ASSERT_TRUE(schema.valid()) << error;
    461   ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
    462 
    463   EXPECT_FALSE(schema.GetKnownProperty("").valid());
    464   EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
    465 
    466   struct {
    467     const char* expected_key;
    468     base::Value::Type expected_type;
    469   } kExpectedKeys[] = {
    470     { "aa",     base::Value::TYPE_BOOLEAN },
    471     { "ab",     base::Value::TYPE_DOUBLE },
    472     { "aba",    base::Value::TYPE_INTEGER },
    473     { "abab",   base::Value::TYPE_STRING },
    474     { "bb",     base::Value::TYPE_NULL },
    475   };
    476   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpectedKeys); ++i) {
    477     Schema sub = schema.GetKnownProperty(kExpectedKeys[i].expected_key);
    478     ASSERT_TRUE(sub.valid());
    479     EXPECT_EQ(kExpectedKeys[i].expected_type, sub.type());
    480   }
    481 }
    482 
    483 TEST(SchemaTest, Wrap) {
    484   const internal::SchemaNode kSchemas[] = {
    485     { base::Value::TYPE_DICTIONARY,   0 },    //  0: root node
    486     { base::Value::TYPE_BOOLEAN,      -1 },   //  1
    487     { base::Value::TYPE_INTEGER,      -1 },   //  2
    488     { base::Value::TYPE_DOUBLE,       -1 },   //  3
    489     { base::Value::TYPE_STRING,       -1 },   //  4
    490     { base::Value::TYPE_LIST,         4 },    //  5: list of strings.
    491     { base::Value::TYPE_LIST,         5 },    //  6: list of lists of strings.
    492     { base::Value::TYPE_INTEGER,      0 },    //  7: integer enumerations.
    493     { base::Value::TYPE_INTEGER,      1 },    //  8: ranged integers.
    494     { base::Value::TYPE_STRING,       2 },    //  9: string enumerations.
    495     { base::Value::TYPE_STRING,       3 },    // 10: string with pattern.
    496   };
    497 
    498   const internal::PropertyNode kPropertyNodes[] = {
    499     { "Boolean",   1 },  // 0
    500     { "Integer",   2 },  // 1
    501     { "Number",    3 },  // 2
    502     { "String",    4 },  // 3
    503     { "List",      5 },  // 4
    504     { "IntEnum",   7 },  // 5
    505     { "RangedInt", 8 },  // 6
    506     { "StrEnum",   9 },  // 7
    507     { "StrPat",   10 },  // 8
    508     { "bar+$",     4 },  // 9
    509   };
    510 
    511   const internal::PropertiesNode kProperties[] = {
    512     // 0 to 9 (exclusive) are the known properties in kPropertyNodes, 9 is
    513     // patternProperties and 6 is the additionalProperties node.
    514     { 0, 9, 10, 6 },
    515   };
    516 
    517   const internal::RestrictionNode kRestriction[] = {
    518     {{0, 3}},  // 0: [1, 2, 3]
    519     {{5, 1}},  // 1: minimum = 1, maximum = 5
    520     {{0, 3}},  // 2: ["one", "two", "three"]
    521     {{3, 3}},  // 3: pattern "foo+"
    522   };
    523 
    524   const int kIntEnums[] = {1, 2, 3};
    525 
    526   const char* kStringEnums[] = {
    527     "one",    // 0
    528     "two",    // 1
    529     "three",  // 2
    530     "foo+",   // 3
    531   };
    532 
    533   const internal::SchemaData kData = {
    534     kSchemas,
    535     kPropertyNodes,
    536     kProperties,
    537     kRestriction,
    538     kIntEnums,
    539     kStringEnums,
    540   };
    541 
    542   Schema schema = Schema::Wrap(&kData);
    543   ASSERT_TRUE(schema.valid());
    544   EXPECT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
    545 
    546   struct {
    547     const char* key;
    548     base::Value::Type type;
    549   } kExpectedProperties[] = {
    550     { "Boolean", base::Value::TYPE_BOOLEAN },
    551     { "Integer", base::Value::TYPE_INTEGER },
    552     { "Number", base::Value::TYPE_DOUBLE },
    553     { "String", base::Value::TYPE_STRING },
    554     { "List", base::Value::TYPE_LIST },
    555     { "IntEnum", base::Value::TYPE_INTEGER },
    556     { "RangedInt", base::Value::TYPE_INTEGER },
    557     { "StrEnum", base::Value::TYPE_STRING },
    558     { "StrPat", base::Value::TYPE_STRING },
    559   };
    560 
    561   Schema::Iterator it = schema.GetPropertiesIterator();
    562   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpectedProperties); ++i) {
    563     ASSERT_FALSE(it.IsAtEnd());
    564     EXPECT_STREQ(kExpectedProperties[i].key, it.key());
    565     Schema sub = it.schema();
    566     ASSERT_TRUE(sub.valid());
    567     EXPECT_EQ(kExpectedProperties[i].type, sub.type());
    568 
    569     if (sub.type() == base::Value::TYPE_LIST) {
    570       Schema items = sub.GetItems();
    571       ASSERT_TRUE(items.valid());
    572       EXPECT_EQ(base::Value::TYPE_STRING, items.type());
    573     }
    574 
    575     it.Advance();
    576   }
    577   EXPECT_TRUE(it.IsAtEnd());
    578 
    579   Schema sub = schema.GetAdditionalProperties();
    580   ASSERT_TRUE(sub.valid());
    581   ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
    582   Schema subsub = sub.GetItems();
    583   ASSERT_TRUE(subsub.valid());
    584   ASSERT_EQ(base::Value::TYPE_LIST, subsub.type());
    585   Schema subsubsub = subsub.GetItems();
    586   ASSERT_TRUE(subsubsub.valid());
    587   ASSERT_EQ(base::Value::TYPE_STRING, subsubsub.type());
    588 
    589   SchemaList schema_list = schema.GetPatternProperties("barr");
    590   ASSERT_EQ(1u, schema_list.size());
    591   sub = schema_list[0];
    592   ASSERT_TRUE(sub.valid());
    593   EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
    594 
    595   EXPECT_TRUE(schema.GetPatternProperties("ba").empty());
    596   EXPECT_TRUE(schema.GetPatternProperties("bar+$").empty());
    597 }
    598 
    599 TEST(SchemaTest, Validate) {
    600   std::string error;
    601   Schema schema = Schema::Parse(kTestSchema, &error);
    602   ASSERT_TRUE(schema.valid()) << error;
    603 
    604   base::DictionaryValue bundle;
    605   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
    606 
    607   // Wrong type, expected integer.
    608   bundle.SetBoolean("Integer", true);
    609   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    610 
    611   // Wrong type, expected list of strings.
    612   {
    613     bundle.Clear();
    614     base::ListValue list;
    615     list.AppendInteger(1);
    616     bundle.Set("Array", list.DeepCopy());
    617     TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    618   }
    619 
    620   // Wrong type in a sub-object.
    621   {
    622     bundle.Clear();
    623     base::DictionaryValue dict;
    624     dict.SetString("one", "one");
    625     bundle.Set("Object", dict.DeepCopy());
    626     TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    627   }
    628 
    629   // Unknown name.
    630   bundle.Clear();
    631   bundle.SetBoolean("Unknown", true);
    632   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    633 
    634   // All of these will be valid.
    635   bundle.Clear();
    636   bundle.SetBoolean("Boolean", true);
    637   bundle.SetInteger("Integer", 123);
    638   bundle.Set("Null", base::Value::CreateNullValue());
    639   bundle.Set("Number", base::Value::CreateDoubleValue(3.14));
    640   bundle.SetString("String", "omg");
    641 
    642   {
    643     base::ListValue list;
    644     list.AppendString("a string");
    645     list.AppendString("another string");
    646     bundle.Set("Array", list.DeepCopy());
    647   }
    648 
    649   {
    650     base::DictionaryValue dict;
    651     dict.SetString("one", "string");
    652     dict.SetInteger("two", 2);
    653     base::ListValue list;
    654     list.Append(dict.DeepCopy());
    655     list.Append(dict.DeepCopy());
    656     bundle.Set("ArrayOfObjects", list.DeepCopy());
    657   }
    658 
    659   {
    660     base::ListValue list;
    661     list.AppendString("a string");
    662     list.AppendString("another string");
    663     base::ListValue listlist;
    664     listlist.Append(list.DeepCopy());
    665     listlist.Append(list.DeepCopy());
    666     bundle.Set("ArrayOfArray", listlist.DeepCopy());
    667   }
    668 
    669   {
    670     base::DictionaryValue dict;
    671     dict.SetBoolean("one", true);
    672     dict.SetInteger("two", 2);
    673     dict.SetString("additionally", "a string");
    674     dict.SetString("and also", "another string");
    675     bundle.Set("Object", dict.DeepCopy());
    676   }
    677 
    678   bundle.SetInteger("IntegerWithEnums", 1);
    679   bundle.SetInteger("IntegerWithEnumsGaps", 20);
    680   bundle.SetString("StringWithEnums", "two");
    681   bundle.SetInteger("IntegerWithRange", 3);
    682 
    683   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
    684 
    685   bundle.SetInteger("IntegerWithEnums", 0);
    686   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    687   bundle.SetInteger("IntegerWithEnums", 1);
    688 
    689   bundle.SetInteger("IntegerWithEnumsGaps", 0);
    690   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    691   bundle.SetInteger("IntegerWithEnumsGaps", 9);
    692   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    693   bundle.SetInteger("IntegerWithEnumsGaps", 10);
    694   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
    695   bundle.SetInteger("IntegerWithEnumsGaps", 11);
    696   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    697   bundle.SetInteger("IntegerWithEnumsGaps", 19);
    698   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    699   bundle.SetInteger("IntegerWithEnumsGaps", 21);
    700   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    701   bundle.SetInteger("IntegerWithEnumsGaps", 29);
    702   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    703   bundle.SetInteger("IntegerWithEnumsGaps", 30);
    704   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
    705   bundle.SetInteger("IntegerWithEnumsGaps", 31);
    706   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    707   bundle.SetInteger("IntegerWithEnumsGaps", 100);
    708   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    709   bundle.SetInteger("IntegerWithEnumsGaps", 20);
    710 
    711   bundle.SetString("StringWithEnums", "FOUR");
    712   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    713   bundle.SetString("StringWithEnums", "two");
    714 
    715   bundle.SetInteger("IntegerWithRange", 4);
    716   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    717   bundle.SetInteger("IntegerWithRange", 3);
    718 
    719   // Unknown top level property.
    720   bundle.SetString("boom", "bang");
    721   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    722   TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, true);
    723   TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_UNKNOWN, true);
    724   TestSchemaValidationWithPath(schema, bundle, "");
    725   bundle.Remove("boom", NULL);
    726 
    727   // Invalid top level property.
    728   bundle.SetInteger("Boolean", 12345);
    729   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
    730   TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
    731   TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_INVALID, true);
    732   TestSchemaValidationWithPath(schema, bundle, "Boolean");
    733   bundle.SetBoolean("Boolean", true);
    734 
    735   // Tests on ObjectOfObject.
    736   {
    737     Schema subschema = schema.GetProperty("ObjectOfObject");
    738     ASSERT_TRUE(subschema.valid());
    739     base::DictionaryValue root;
    740 
    741     // Unknown property.
    742     root.SetBoolean("Object.three", false);
    743     TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
    744     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
    745     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
    746     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
    747     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
    748     TestSchemaValidationWithPath(subschema, root, "Object");
    749     root.Remove("Object.three", NULL);
    750 
    751     // Invalid property.
    752     root.SetInteger("Object.one", 12345);
    753     TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
    754     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
    755     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
    756     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
    757     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
    758     TestSchemaValidationWithPath(subschema, root, "Object.one");
    759     root.Remove("Object.one", NULL);
    760   }
    761 
    762   // Tests on ArrayOfObjects.
    763   {
    764     Schema subschema = schema.GetProperty("ArrayOfObjects");
    765     ASSERT_TRUE(subschema.valid());
    766     base::ListValue root;
    767 
    768     // Unknown property.
    769     base::DictionaryValue* dict_value = new base::DictionaryValue();
    770     dict_value->SetBoolean("three", true);
    771     root.Append(dict_value);  // Pass ownership to root.
    772     TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
    773     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
    774     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
    775     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
    776     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
    777     TestSchemaValidationWithPath(subschema, root, "items[0]");
    778     root.Remove(root.GetSize() - 1, NULL);
    779 
    780     // Invalid property.
    781     dict_value = new base::DictionaryValue();
    782     dict_value->SetBoolean("two", true);
    783     root.Append(dict_value);  // Pass ownership to root.
    784     TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
    785     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
    786     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
    787     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
    788     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
    789     TestSchemaValidationWithPath(subschema, root, "items[0].two");
    790     root.Remove(root.GetSize() - 1, NULL);
    791   }
    792 
    793   // Tests on ObjectOfArray.
    794   {
    795     Schema subschema = schema.GetProperty("ObjectOfArray");
    796     ASSERT_TRUE(subschema.valid());
    797     base::DictionaryValue root;
    798 
    799     base::ListValue* list_value = new base::ListValue();
    800     root.Set("List", list_value);  // Pass ownership to root.
    801 
    802     // Test that there are not errors here.
    803     list_value->Append(new base::FundamentalValue(12345));
    804     TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
    805     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, true);
    806     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
    807     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
    808     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
    809 
    810     // Invalid list item.
    811     list_value->Append(new base::StringValue("blabla"));
    812     TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
    813     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
    814     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
    815     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
    816     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
    817     TestSchemaValidationWithPath(subschema, root, "List.items[1]");
    818   }
    819 
    820   // Tests on ArrayOfObjectOfArray.
    821   {
    822     Schema subschema = schema.GetProperty("ArrayOfObjectOfArray");
    823     ASSERT_TRUE(subschema.valid());
    824     base::ListValue root;
    825 
    826     base::ListValue* list_value = new base::ListValue();
    827     base::DictionaryValue* dict_value = new base::DictionaryValue();
    828     dict_value->Set("List", list_value);  // Pass ownership to dict_value.
    829     root.Append(dict_value);  // Pass ownership to root.
    830 
    831     // Test that there are not errors here.
    832     list_value->Append(new base::StringValue("blabla"));
    833     TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
    834     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, true);
    835     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
    836     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
    837     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
    838 
    839     // Invalid list item.
    840     list_value->Append(new base::FundamentalValue(12345));
    841     TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
    842     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
    843     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
    844     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
    845     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
    846     TestSchemaValidationWithPath(subschema, root, "items[0].List.items[1]");
    847   }
    848 
    849   // Tests on StringWithPattern.
    850   {
    851     Schema subschema = schema.GetProperty("StringWithPattern");
    852     ASSERT_TRUE(subschema.valid());
    853 
    854     TestSchemaValidation(
    855         subschema, base::StringValue("foobar"), SCHEMA_STRICT, false);
    856     TestSchemaValidation(
    857         subschema, base::StringValue("foo"), SCHEMA_STRICT, true);
    858     TestSchemaValidation(
    859         subschema, base::StringValue("fo"), SCHEMA_STRICT, false);
    860     TestSchemaValidation(
    861         subschema, base::StringValue("fooo"), SCHEMA_STRICT, true);
    862     TestSchemaValidation(
    863         subschema, base::StringValue("^foo+$"), SCHEMA_STRICT, false);
    864   }
    865 
    866   // Tests on ObjectWithPatternProperties.
    867   {
    868     Schema subschema = schema.GetProperty("ObjectWithPatternProperties");
    869     ASSERT_TRUE(subschema.valid());
    870     base::DictionaryValue root;
    871 
    872     ASSERT_EQ(1u, subschema.GetPatternProperties("fooo").size());
    873     ASSERT_EQ(1u, subschema.GetPatternProperties("foo").size());
    874     ASSERT_EQ(1u, subschema.GetPatternProperties("barr").size());
    875     ASSERT_EQ(1u, subschema.GetPatternProperties("bar").size());
    876     ASSERT_EQ(1u, subschema.GetMatchingProperties("fooo").size());
    877     ASSERT_EQ(1u, subschema.GetMatchingProperties("foo").size());
    878     ASSERT_EQ(1u, subschema.GetMatchingProperties("barr").size());
    879     ASSERT_EQ(2u, subschema.GetMatchingProperties("bar").size());
    880     ASSERT_TRUE(subschema.GetPatternProperties("foobar").empty());
    881 
    882     root.SetInteger("fooo", 123);
    883     TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
    884     root.SetBoolean("fooo", false);
    885     TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
    886     root.Remove("fooo", NULL);
    887 
    888     root.SetInteger("foo", 123);
    889     TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
    890     root.SetBoolean("foo", false);
    891     TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
    892     root.Remove("foo", NULL);
    893 
    894     root.SetString("barr", "one");
    895     TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
    896     root.SetString("barr", "three");
    897     TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
    898     root.SetBoolean("barr", false);
    899     TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
    900     root.Remove("barr", NULL);
    901 
    902     root.SetString("bar", "one");
    903     TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
    904     root.SetString("bar", "two");
    905     TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
    906     root.SetString("bar", "three");
    907     TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
    908     root.Remove("bar", NULL);
    909 
    910     root.SetInteger("foobar", 123);
    911     TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
    912     TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
    913     root.Remove("foobar", NULL);
    914   }
    915 
    916   // Test that integer to double promotion is allowed.
    917   bundle.SetInteger("Number", 31415);
    918   TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
    919 }
    920 
    921 TEST(SchemaTest, InvalidReferences) {
    922   // References to undeclared schemas fail.
    923   EXPECT_TRUE(ParseFails(
    924       "{"
    925       "  \"type\": \"object\","
    926       "  \"properties\": {"
    927       "    \"name\": { \"$ref\": \"undeclared\" }"
    928       "  }"
    929       "}"));
    930 
    931   // Can't refer to self.
    932   EXPECT_TRUE(ParseFails(
    933       "{"
    934       "  \"type\": \"object\","
    935       "  \"properties\": {"
    936       "    \"name\": {"
    937       "      \"id\": \"self\","
    938       "      \"$ref\": \"self\""
    939       "    }"
    940       "  }"
    941       "}"));
    942 
    943   // Duplicated IDs are invalid.
    944   EXPECT_TRUE(ParseFails(
    945       "{"
    946       "  \"type\": \"object\","
    947       "  \"properties\": {"
    948       "    \"name\": {"
    949       "      \"id\": \"x\","
    950       "      \"type\": \"string\""
    951       "    },"
    952       "    \"another\": {"
    953       "      \"id\": \"x\","
    954       "      \"type\": \"string\""
    955       "    }"
    956       "  }"
    957       "}"));
    958 
    959   // Main object can't be a reference.
    960   EXPECT_TRUE(ParseFails(
    961       "{"
    962       "  \"type\": \"object\","
    963       "  \"id\": \"main\","
    964       "  \"$ref\": \"main\""
    965       "}"));
    966 
    967   EXPECT_TRUE(ParseFails(
    968       "{"
    969       "  \"type\": \"object\","
    970       "  \"$ref\": \"main\""
    971       "}"));
    972 }
    973 
    974 TEST(SchemaTest, RecursiveReferences) {
    975   // Verifies that references can go to a parent schema, to define a
    976   // recursive type.
    977   std::string error;
    978   Schema schema = Schema::Parse(
    979       "{"
    980       "  \"type\": \"object\","
    981       "  \"properties\": {"
    982       "    \"bookmarks\": {"
    983       "      \"type\": \"array\","
    984       "      \"id\": \"ListOfBookmarks\","
    985       "      \"items\": {"
    986       "        \"type\": \"object\","
    987       "        \"properties\": {"
    988       "          \"name\": { \"type\": \"string\" },"
    989       "          \"url\": { \"type\": \"string\" },"
    990       "          \"children\": { \"$ref\": \"ListOfBookmarks\" }"
    991       "        }"
    992       "      }"
    993       "    }"
    994       "  }"
    995       "}", &error);
    996   ASSERT_TRUE(schema.valid()) << error;
    997   ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
    998 
    999   Schema parent = schema.GetKnownProperty("bookmarks");
   1000   ASSERT_TRUE(parent.valid());
   1001   ASSERT_EQ(base::Value::TYPE_LIST, parent.type());
   1002 
   1003   // Check the recursive type a number of times.
   1004   for (int i = 0; i < 10; ++i) {
   1005     Schema items = parent.GetItems();
   1006     ASSERT_TRUE(items.valid());
   1007     ASSERT_EQ(base::Value::TYPE_DICTIONARY, items.type());
   1008 
   1009     Schema prop = items.GetKnownProperty("name");
   1010     ASSERT_TRUE(prop.valid());
   1011     ASSERT_EQ(base::Value::TYPE_STRING, prop.type());
   1012 
   1013     prop = items.GetKnownProperty("url");
   1014     ASSERT_TRUE(prop.valid());
   1015     ASSERT_EQ(base::Value::TYPE_STRING, prop.type());
   1016 
   1017     prop = items.GetKnownProperty("children");
   1018     ASSERT_TRUE(prop.valid());
   1019     ASSERT_EQ(base::Value::TYPE_LIST, prop.type());
   1020 
   1021     parent = prop;
   1022   }
   1023 }
   1024 
   1025 TEST(SchemaTest, UnorderedReferences) {
   1026   // Verifies that references and IDs can come in any order.
   1027   std::string error;
   1028   Schema schema = Schema::Parse(
   1029       "{"
   1030       "  \"type\": \"object\","
   1031       "  \"properties\": {"
   1032       "    \"a\": { \"$ref\": \"shared\" },"
   1033       "    \"b\": { \"$ref\": \"shared\" },"
   1034       "    \"c\": { \"$ref\": \"shared\" },"
   1035       "    \"d\": { \"$ref\": \"shared\" },"
   1036       "    \"e\": {"
   1037       "      \"type\": \"boolean\","
   1038       "      \"id\": \"shared\""
   1039       "    },"
   1040       "    \"f\": { \"$ref\": \"shared\" },"
   1041       "    \"g\": { \"$ref\": \"shared\" },"
   1042       "    \"h\": { \"$ref\": \"shared\" },"
   1043       "    \"i\": { \"$ref\": \"shared\" }"
   1044       "  }"
   1045       "}", &error);
   1046   ASSERT_TRUE(schema.valid()) << error;
   1047   ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
   1048 
   1049   for (char c = 'a'; c <= 'i'; ++c) {
   1050     Schema sub = schema.GetKnownProperty(std::string(1, c));
   1051     ASSERT_TRUE(sub.valid()) << c;
   1052     ASSERT_EQ(base::Value::TYPE_BOOLEAN, sub.type()) << c;
   1053   }
   1054 }
   1055 
   1056 TEST(SchemaTest, AdditionalPropertiesReference) {
   1057   // Verifies that "additionalProperties" can be a reference.
   1058   std::string error;
   1059   Schema schema = Schema::Parse(
   1060       "{"
   1061       "  \"type\": \"object\","
   1062       "  \"properties\": {"
   1063       "    \"policy\": {"
   1064       "      \"type\": \"object\","
   1065       "      \"properties\": {"
   1066       "        \"foo\": {"
   1067       "          \"type\": \"boolean\","
   1068       "          \"id\": \"FooId\""
   1069       "        }"
   1070       "      },"
   1071       "      \"additionalProperties\": { \"$ref\": \"FooId\" }"
   1072       "    }"
   1073       "  }"
   1074       "}", &error);
   1075   ASSERT_TRUE(schema.valid()) << error;
   1076   ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
   1077 
   1078   Schema policy = schema.GetKnownProperty("policy");
   1079   ASSERT_TRUE(policy.valid());
   1080   ASSERT_EQ(base::Value::TYPE_DICTIONARY, policy.type());
   1081 
   1082   Schema foo = policy.GetKnownProperty("foo");
   1083   ASSERT_TRUE(foo.valid());
   1084   EXPECT_EQ(base::Value::TYPE_BOOLEAN, foo.type());
   1085 
   1086   Schema additional = policy.GetAdditionalProperties();
   1087   ASSERT_TRUE(additional.valid());
   1088   EXPECT_EQ(base::Value::TYPE_BOOLEAN, additional.type());
   1089 
   1090   Schema x = policy.GetProperty("x");
   1091   ASSERT_TRUE(x.valid());
   1092   EXPECT_EQ(base::Value::TYPE_BOOLEAN, x.type());
   1093 }
   1094 
   1095 TEST(SchemaTest, ItemsReference) {
   1096   // Verifies that "items" can be a reference.
   1097   std::string error;
   1098   Schema schema = Schema::Parse(
   1099       "{"
   1100       "  \"type\": \"object\","
   1101       "  \"properties\": {"
   1102       "    \"foo\": {"
   1103       "      \"type\": \"boolean\","
   1104       "      \"id\": \"FooId\""
   1105       "    },"
   1106       "    \"list\": {"
   1107       "      \"type\": \"array\","
   1108       "      \"items\": { \"$ref\": \"FooId\" }"
   1109       "    }"
   1110       "  }"
   1111       "}", &error);
   1112   ASSERT_TRUE(schema.valid()) << error;
   1113   ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
   1114 
   1115   Schema foo = schema.GetKnownProperty("foo");
   1116   ASSERT_TRUE(foo.valid());
   1117   EXPECT_EQ(base::Value::TYPE_BOOLEAN, foo.type());
   1118 
   1119   Schema list = schema.GetKnownProperty("list");
   1120   ASSERT_TRUE(list.valid());
   1121   ASSERT_EQ(base::Value::TYPE_LIST, list.type());
   1122 
   1123   Schema items = list.GetItems();
   1124   ASSERT_TRUE(items.valid());
   1125   ASSERT_EQ(base::Value::TYPE_BOOLEAN, items.type());
   1126 }
   1127 
   1128 TEST(SchemaTest, EnumerationRestriction) {
   1129   // Enum attribute is a list.
   1130   EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
   1131       "{"
   1132       "  \"type\": \"string\","
   1133       "  \"enum\": 12"
   1134       "}")));
   1135 
   1136   // Empty enum attributes is not allowed.
   1137   EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
   1138       "{"
   1139       "  \"type\": \"integer\","
   1140       "  \"enum\": []"
   1141       "}")));
   1142 
   1143   // Enum elements type should be same as stated.
   1144   EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
   1145       "{"
   1146       "  \"type\": \"string\","
   1147       "  \"enum\": [1, 2, 3]"
   1148       "}")));
   1149 
   1150   EXPECT_FALSE(ParseFails(SchemaObjectWrapper(
   1151       "{"
   1152       "  \"type\": \"integer\","
   1153       "  \"enum\": [1, 2, 3]"
   1154       "}")));
   1155 
   1156   EXPECT_FALSE(ParseFails(SchemaObjectWrapper(
   1157       "{"
   1158       "  \"type\": \"string\","
   1159       "  \"enum\": [\"1\", \"2\", \"3\"]"
   1160       "}")));
   1161 }
   1162 
   1163 TEST(SchemaTest, RangedRestriction) {
   1164   EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
   1165       "{"
   1166       "  \"type\": \"integer\","
   1167       "  \"minimum\": 10,"
   1168       "  \"maximum\": 5"
   1169       "}")));
   1170 
   1171   EXPECT_FALSE(ParseFails(SchemaObjectWrapper(
   1172       "{"
   1173       "  \"type\": \"integer\","
   1174       "  \"minimum\": 10,"
   1175       "  \"maximum\": 20"
   1176       "}")));
   1177 }
   1178 
   1179 }  // namespace policy
   1180