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