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
12namespace policy {
13
14namespace {
15
16#define TestSchemaValidation(a, b, c, d) \
17    TestSchemaValidationHelper(          \
18        base::StringPrintf("%s:%i", __FILE__, __LINE__), a, b, c, d)
19
20const 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
131bool 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
140void 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
177void 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
188std::string SchemaObjectWrapper(const std::string& subschema) {
189  return "{"
190         "  \"type\": \"object\","
191         "  \"properties\": {"
192         "    \"SomePropertyName\":" + subschema +
193         "  }"
194         "}";
195}
196
197}  // namespace
198
199TEST(SchemaTest, MinimalSchema) {
200  EXPECT_FALSE(ParseFails("{ \"type\": \"object\" }"));
201}
202
203TEST(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
255TEST(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
292TEST(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
423TEST(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
483TEST(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
599TEST(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", new base::FundamentalValue(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
921TEST(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
974TEST(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
1025TEST(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
1056TEST(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
1095TEST(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
1128TEST(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
1163TEST(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