• 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 "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 
ParseFails(const std::string & content)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 
TestSchemaValidationHelper(const std::string & source,Schema schema,const base::Value & value,SchemaOnErrorStrategy strategy,bool expected_return_value)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 
TestSchemaValidationWithPath(Schema schema,const base::Value & value,const std::string & expected_failure_path)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 
SchemaObjectWrapper(const std::string & subschema)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 
TEST(SchemaTest,MinimalSchema)199 TEST(SchemaTest, MinimalSchema) {
200   EXPECT_FALSE(ParseFails("{ \"type\": \"object\" }"));
201 }
202 
TEST(SchemaTest,InvalidSchemas)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 
TEST(SchemaTest,Ownership)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 
TEST(SchemaTest,ValidSchema)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 
TEST(SchemaTest,Lookups)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 
TEST(SchemaTest,Wrap)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 
TEST(SchemaTest,Validate)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 
TEST(SchemaTest,InvalidReferences)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 
TEST(SchemaTest,RecursiveReferences)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 
TEST(SchemaTest,UnorderedReferences)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 
TEST(SchemaTest,AdditionalPropertiesReference)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 
TEST(SchemaTest,ItemsReference)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 
TEST(SchemaTest,EnumerationRestriction)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 
TEST(SchemaTest,RangedRestriction)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