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 "components/policy/core/common/schema_internal.h"
8#include "testing/gtest/include/gtest/gtest.h"
9
10namespace policy {
11
12namespace {
13
14const char kTestSchema[] =
15    "{"
16    "  \"type\": \"object\","
17    "  \"properties\": {"
18    "    \"Boolean\": { \"type\": \"boolean\" },"
19    "    \"Integer\": { \"type\": \"integer\" },"
20    "    \"Null\": { \"type\": \"null\" },"
21    "    \"Number\": { \"type\": \"number\" },"
22    "    \"String\": { \"type\": \"string\" },"
23    "    \"Array\": {"
24    "      \"type\": \"array\","
25    "      \"items\": { \"type\": \"string\" }"
26    "    },"
27    "    \"ArrayOfObjects\": {"
28    "      \"type\": \"array\","
29    "      \"items\": {"
30    "        \"type\": \"object\","
31    "        \"properties\": {"
32    "          \"one\": { \"type\": \"string\" },"
33    "          \"two\": { \"type\": \"integer\" }"
34    "        }"
35    "      }"
36    "    },"
37    "    \"ArrayOfArray\": {"
38    "      \"type\": \"array\","
39    "      \"items\": {"
40    "        \"type\": \"array\","
41    "        \"items\": { \"type\": \"string\" }"
42    "      }"
43    "    },"
44    "    \"Object\": {"
45    "      \"type\": \"object\","
46    "      \"properties\": {"
47    "        \"one\": { \"type\": \"boolean\" },"
48    "        \"two\": { \"type\": \"integer\" }"
49    "      },"
50    "      \"additionalProperties\": { \"type\": \"string\" }"
51    "    }"
52    "  }"
53    "}";
54
55bool ParseFails(const std::string& content) {
56  std::string error;
57  Schema schema = Schema::Parse(content, &error);
58  if (schema.valid())
59    return false;
60  EXPECT_FALSE(error.empty());
61  return true;
62}
63
64}  // namespace
65
66TEST(SchemaTest, MinimalSchema) {
67  EXPECT_FALSE(ParseFails("{ \"type\": \"object\" }"));
68}
69
70TEST(SchemaTest, InvalidSchemas) {
71  EXPECT_TRUE(ParseFails(""));
72  EXPECT_TRUE(ParseFails("omg"));
73  EXPECT_TRUE(ParseFails("\"omg\""));
74  EXPECT_TRUE(ParseFails("123"));
75  EXPECT_TRUE(ParseFails("[]"));
76  EXPECT_TRUE(ParseFails("null"));
77  EXPECT_TRUE(ParseFails("{}"));
78
79  EXPECT_TRUE(ParseFails(
80      "{"
81      "  \"type\": \"object\","
82        "\"additionalProperties\": { \"type\":\"object\" }"
83      "}"));
84
85  EXPECT_TRUE(ParseFails(
86      "{"
87      "  \"type\": \"object\","
88      "  \"patternProperties\": { \"a+b*\": { \"type\": \"object\" } }"
89      "}"));
90
91  EXPECT_TRUE(ParseFails(
92      "{"
93      "  \"type\": \"object\","
94      "  \"properties\": { \"Policy\": { \"type\": \"bogus\" } }"
95      "}"));
96
97  EXPECT_TRUE(ParseFails(
98      "{"
99      "  \"type\": \"object\","
100      "  \"properties\": { \"Policy\": { \"type\": [\"string\", \"number\"] } }"
101      "}"));
102
103  EXPECT_TRUE(ParseFails(
104      "{"
105      "  \"type\": \"object\","
106      "  \"properties\": { \"Policy\": { \"type\": \"any\" } }"
107      "}"));
108
109  EXPECT_TRUE(ParseFails(
110      "{"
111      "  \"type\": \"object\","
112      "  \"properties\": { \"Policy\": 123 }"
113      "}"));
114
115  EXPECT_FALSE(ParseFails(
116      "{"
117      "  \"type\": \"object\","
118      "  \"unknown attribute\": \"is ignored\""
119      "}"));
120}
121
122TEST(SchemaTest, Ownership) {
123  std::string error;
124  Schema schema = Schema::Parse(
125      "{"
126      "  \"type\": \"object\","
127      "  \"properties\": {"
128      "    \"sub\": {"
129      "      \"type\": \"object\","
130      "      \"properties\": {"
131      "        \"subsub\": { \"type\": \"string\" }"
132      "      }"
133      "    }"
134      "  }"
135      "}", &error);
136  ASSERT_TRUE(schema.valid()) << error;
137  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
138
139  schema = schema.GetKnownProperty("sub");
140  ASSERT_TRUE(schema.valid());
141  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
142
143  {
144    Schema::Iterator it = schema.GetPropertiesIterator();
145    ASSERT_FALSE(it.IsAtEnd());
146    EXPECT_STREQ("subsub", it.key());
147
148    schema = it.schema();
149    it.Advance();
150    EXPECT_TRUE(it.IsAtEnd());
151  }
152
153  ASSERT_TRUE(schema.valid());
154  EXPECT_EQ(base::Value::TYPE_STRING, schema.type());
155
156  // This test shouldn't leak nor use invalid memory.
157}
158
159TEST(SchemaTest, ValidSchema) {
160  std::string error;
161  Schema schema = Schema::Parse(kTestSchema, &error);
162  ASSERT_TRUE(schema.valid()) << error;
163
164  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
165  EXPECT_FALSE(schema.GetProperty("invalid").valid());
166
167  Schema sub = schema.GetProperty("Boolean");
168  ASSERT_TRUE(sub.valid());
169  EXPECT_EQ(base::Value::TYPE_BOOLEAN, sub.type());
170
171  sub = schema.GetProperty("Integer");
172  ASSERT_TRUE(sub.valid());
173  EXPECT_EQ(base::Value::TYPE_INTEGER, sub.type());
174
175  sub = schema.GetProperty("Null");
176  ASSERT_TRUE(sub.valid());
177  EXPECT_EQ(base::Value::TYPE_NULL, sub.type());
178
179  sub = schema.GetProperty("Number");
180  ASSERT_TRUE(sub.valid());
181  EXPECT_EQ(base::Value::TYPE_DOUBLE, sub.type());
182
183  sub = schema.GetProperty("String");
184  ASSERT_TRUE(sub.valid());
185  EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
186
187  sub = schema.GetProperty("Array");
188  ASSERT_TRUE(sub.valid());
189  ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
190  sub = sub.GetItems();
191  ASSERT_TRUE(sub.valid());
192  EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
193
194  sub = schema.GetProperty("ArrayOfObjects");
195  ASSERT_TRUE(sub.valid());
196  ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
197  sub = sub.GetItems();
198  ASSERT_TRUE(sub.valid());
199  EXPECT_EQ(base::Value::TYPE_DICTIONARY, sub.type());
200  Schema subsub = sub.GetProperty("one");
201  ASSERT_TRUE(subsub.valid());
202  EXPECT_EQ(base::Value::TYPE_STRING, subsub.type());
203  subsub = sub.GetProperty("two");
204  ASSERT_TRUE(subsub.valid());
205  EXPECT_EQ(base::Value::TYPE_INTEGER, subsub.type());
206  subsub = sub.GetProperty("invalid");
207  EXPECT_FALSE(subsub.valid());
208
209  sub = schema.GetProperty("ArrayOfArray");
210  ASSERT_TRUE(sub.valid());
211  ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
212  sub = sub.GetItems();
213  ASSERT_TRUE(sub.valid());
214  ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
215  sub = sub.GetItems();
216  ASSERT_TRUE(sub.valid());
217  EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
218
219  sub = schema.GetProperty("Object");
220  ASSERT_TRUE(sub.valid());
221  ASSERT_EQ(base::Value::TYPE_DICTIONARY, sub.type());
222  subsub = sub.GetProperty("one");
223  ASSERT_TRUE(subsub.valid());
224  EXPECT_EQ(base::Value::TYPE_BOOLEAN, subsub.type());
225  subsub = sub.GetProperty("two");
226  ASSERT_TRUE(subsub.valid());
227  EXPECT_EQ(base::Value::TYPE_INTEGER, subsub.type());
228  subsub = sub.GetProperty("undeclared");
229  ASSERT_TRUE(subsub.valid());
230  EXPECT_EQ(base::Value::TYPE_STRING, subsub.type());
231
232  struct {
233    const char* expected_key;
234    base::Value::Type expected_type;
235  } kExpectedProperties[] = {
236    { "Array",            base::Value::TYPE_LIST },
237    { "ArrayOfArray",     base::Value::TYPE_LIST },
238    { "ArrayOfObjects",   base::Value::TYPE_LIST },
239    { "Boolean",          base::Value::TYPE_BOOLEAN },
240    { "Integer",          base::Value::TYPE_INTEGER },
241    { "Null",             base::Value::TYPE_NULL },
242    { "Number",           base::Value::TYPE_DOUBLE },
243    { "Object",           base::Value::TYPE_DICTIONARY },
244    { "String",           base::Value::TYPE_STRING },
245  };
246  Schema::Iterator it = schema.GetPropertiesIterator();
247  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpectedProperties); ++i) {
248    ASSERT_FALSE(it.IsAtEnd());
249    EXPECT_STREQ(kExpectedProperties[i].expected_key, it.key());
250    ASSERT_TRUE(it.schema().valid());
251    EXPECT_EQ(kExpectedProperties[i].expected_type, it.schema().type());
252    it.Advance();
253  }
254  EXPECT_TRUE(it.IsAtEnd());
255}
256
257TEST(SchemaTest, Lookups) {
258  std::string error;
259
260  Schema schema = Schema::Parse("{ \"type\": \"object\" }", &error);
261  ASSERT_TRUE(schema.valid()) << error;
262  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
263
264  // This empty schema should never find named properties.
265  EXPECT_FALSE(schema.GetKnownProperty("").valid());
266  EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
267  EXPECT_TRUE(schema.GetPropertiesIterator().IsAtEnd());
268
269  schema = Schema::Parse(
270      "{"
271      "  \"type\": \"object\","
272      "  \"properties\": {"
273      "    \"Boolean\": { \"type\": \"boolean\" }"
274      "  }"
275      "}", &error);
276  ASSERT_TRUE(schema.valid()) << error;
277  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
278
279  EXPECT_FALSE(schema.GetKnownProperty("").valid());
280  EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
281  EXPECT_TRUE(schema.GetKnownProperty("Boolean").valid());
282
283  schema = Schema::Parse(
284      "{"
285      "  \"type\": \"object\","
286      "  \"properties\": {"
287      "    \"bb\" : { \"type\": \"null\" },"
288      "    \"aa\" : { \"type\": \"boolean\" },"
289      "    \"abab\" : { \"type\": \"string\" },"
290      "    \"ab\" : { \"type\": \"number\" },"
291      "    \"aba\" : { \"type\": \"integer\" }"
292      "  }"
293      "}", &error);
294  ASSERT_TRUE(schema.valid()) << error;
295  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
296
297  EXPECT_FALSE(schema.GetKnownProperty("").valid());
298  EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
299
300  struct {
301    const char* expected_key;
302    base::Value::Type expected_type;
303  } kExpectedKeys[] = {
304    { "aa",     base::Value::TYPE_BOOLEAN },
305    { "ab",     base::Value::TYPE_DOUBLE },
306    { "aba",    base::Value::TYPE_INTEGER },
307    { "abab",   base::Value::TYPE_STRING },
308    { "bb",     base::Value::TYPE_NULL },
309  };
310  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpectedKeys); ++i) {
311    Schema sub = schema.GetKnownProperty(kExpectedKeys[i].expected_key);
312    ASSERT_TRUE(sub.valid());
313    EXPECT_EQ(kExpectedKeys[i].expected_type, sub.type());
314  }
315}
316
317TEST(SchemaTest, Wrap) {
318  const internal::SchemaNode kSchemas[] = {
319    { base::Value::TYPE_DICTIONARY,   0 },    // 0: root node
320    { base::Value::TYPE_BOOLEAN,      -1 },   // 1
321    { base::Value::TYPE_INTEGER,      -1 },   // 2
322    { base::Value::TYPE_DOUBLE,       -1 },   // 3
323    { base::Value::TYPE_STRING,       -1 },   // 4
324    { base::Value::TYPE_LIST,         4 },    // 5: list of strings.
325    { base::Value::TYPE_LIST,         5 },    // 6: list of lists of strings.
326  };
327
328  const internal::PropertyNode kPropertyNodes[] = {
329    { "Boolean",  1 },
330    { "Integer",  2 },
331    { "Number",   3 },
332    { "String",   4 },
333    { "List",     5 },
334  };
335
336  const internal::PropertiesNode kProperties[] = {
337    // Properties 0 to 5 (exclusive) are known, from kPropertyNodes.
338    // SchemaNode offset 6 is for additionalProperties (list of lists).
339    { 0, 5, 6 },
340  };
341
342  const internal::SchemaData kData = {
343    kSchemas,
344    kPropertyNodes,
345    kProperties,
346  };
347
348  Schema schema = Schema::Wrap(&kData);
349  ASSERT_TRUE(schema.valid());
350  EXPECT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
351
352  struct {
353    const char* key;
354    base::Value::Type type;
355  } kExpectedProperties[] = {
356    { "Boolean", base::Value::TYPE_BOOLEAN },
357    { "Integer", base::Value::TYPE_INTEGER },
358    { "Number", base::Value::TYPE_DOUBLE },
359    { "String", base::Value::TYPE_STRING },
360    { "List", base::Value::TYPE_LIST },
361  };
362
363  Schema::Iterator it = schema.GetPropertiesIterator();
364  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpectedProperties); ++i) {
365    ASSERT_FALSE(it.IsAtEnd());
366    EXPECT_STREQ(kExpectedProperties[i].key, it.key());
367    Schema sub = it.schema();
368    ASSERT_TRUE(sub.valid());
369    EXPECT_EQ(kExpectedProperties[i].type, sub.type());
370
371    if (sub.type() == base::Value::TYPE_LIST) {
372      ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
373      Schema items = sub.GetItems();
374      ASSERT_TRUE(items.valid());
375      EXPECT_EQ(base::Value::TYPE_STRING, items.type());
376    }
377
378    it.Advance();
379  }
380  EXPECT_TRUE(it.IsAtEnd());
381
382  Schema sub = schema.GetAdditionalProperties();
383  ASSERT_TRUE(sub.valid());
384  ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
385  Schema subsub = sub.GetItems();
386  ASSERT_TRUE(subsub.valid());
387  ASSERT_EQ(base::Value::TYPE_LIST, subsub.type());
388  Schema subsubsub = subsub.GetItems();
389  ASSERT_TRUE(subsubsub.valid());
390  ASSERT_EQ(base::Value::TYPE_STRING, subsubsub.type());
391}
392
393TEST(SchemaTest, Validate) {
394  std::string error;
395  Schema schema = Schema::Parse(kTestSchema, &error);
396  ASSERT_TRUE(schema.valid()) << error;
397
398  base::DictionaryValue bundle;
399  EXPECT_TRUE(schema.Validate(bundle));
400
401  // Wrong type, expected integer.
402  bundle.SetBoolean("Integer", true);
403  EXPECT_FALSE(schema.Validate(bundle));
404
405  // Wrong type, expected list of strings.
406  {
407    bundle.Clear();
408    base::ListValue list;
409    list.AppendInteger(1);
410    bundle.Set("Array", list.DeepCopy());
411    EXPECT_FALSE(schema.Validate(bundle));
412  }
413
414  // Wrong type in a sub-object.
415  {
416    bundle.Clear();
417    base::DictionaryValue dict;
418    dict.SetString("one", "one");
419    bundle.Set("Object", dict.DeepCopy());
420    EXPECT_FALSE(schema.Validate(bundle));
421  }
422
423  // Unknown name.
424  bundle.Clear();
425  bundle.SetBoolean("Unknown", true);
426  EXPECT_FALSE(schema.Validate(bundle));
427
428  // All of these will be valid.
429  bundle.Clear();
430  bundle.SetBoolean("Boolean", true);
431  bundle.SetInteger("Integer", 123);
432  bundle.Set("Null", base::Value::CreateNullValue());
433  bundle.Set("Number", base::Value::CreateDoubleValue(3.14));
434  bundle.SetString("String", "omg");
435
436  {
437    base::ListValue list;
438    list.AppendString("a string");
439    list.AppendString("another string");
440    bundle.Set("Array", list.DeepCopy());
441  }
442
443  {
444    base::DictionaryValue dict;
445    dict.SetString("one", "string");
446    dict.SetInteger("two", 2);
447    base::ListValue list;
448    list.Append(dict.DeepCopy());
449    list.Append(dict.DeepCopy());
450    bundle.Set("ArrayOfObjects", list.DeepCopy());
451  }
452
453  {
454    base::ListValue list;
455    list.AppendString("a string");
456    list.AppendString("another string");
457    base::ListValue listlist;
458    listlist.Append(list.DeepCopy());
459    listlist.Append(list.DeepCopy());
460    bundle.Set("ArrayOfArray", listlist.DeepCopy());
461  }
462
463  {
464    base::DictionaryValue dict;
465    dict.SetBoolean("one", true);
466    dict.SetInteger("two", 2);
467    dict.SetString("additionally", "a string");
468    dict.SetString("and also", "another string");
469    bundle.Set("Object", dict.DeepCopy());
470  }
471
472  EXPECT_TRUE(schema.Validate(bundle));
473
474  bundle.SetString("boom", "bang");
475  EXPECT_FALSE(schema.Validate(bundle));
476
477}
478TEST(SchemaTest, InvalidReferences) {
479  // References to undeclared schemas fail.
480  EXPECT_TRUE(ParseFails(
481      "{"
482      "  \"type\": \"object\","
483      "  \"properties\": {"
484      "    \"name\": { \"$ref\": \"undeclared\" }"
485      "  }"
486      "}"));
487
488  // Can't refer to self.
489  EXPECT_TRUE(ParseFails(
490      "{"
491      "  \"type\": \"object\","
492      "  \"properties\": {"
493      "    \"name\": {"
494      "      \"id\": \"self\","
495      "      \"$ref\": \"self\""
496      "    }"
497      "  }"
498      "}"));
499
500  // Duplicated IDs are invalid.
501  EXPECT_TRUE(ParseFails(
502      "{"
503      "  \"type\": \"object\","
504      "  \"properties\": {"
505      "    \"name\": {"
506      "      \"id\": \"x\","
507      "      \"type\": \"string\""
508      "    },"
509      "    \"another\": {"
510      "      \"id\": \"x\","
511      "      \"type\": \"string\""
512      "    }"
513      "  }"
514      "}"));
515
516  // Main object can't be a reference.
517  EXPECT_TRUE(ParseFails(
518      "{"
519      "  \"type\": \"object\","
520      "  \"id\": \"main\","
521      "  \"$ref\": \"main\""
522      "}"));
523
524  EXPECT_TRUE(ParseFails(
525      "{"
526      "  \"type\": \"object\","
527      "  \"$ref\": \"main\""
528      "}"));
529}
530
531TEST(SchemaTest, RecursiveReferences) {
532  // Verifies that references can go to a parent schema, to define a
533  // recursive type.
534  std::string error;
535  Schema schema = Schema::Parse(
536      "{"
537      "  \"type\": \"object\","
538      "  \"properties\": {"
539      "    \"bookmarks\": {"
540      "      \"type\": \"array\","
541      "      \"id\": \"ListOfBookmarks\","
542      "      \"items\": {"
543      "        \"type\": \"object\","
544      "        \"properties\": {"
545      "          \"name\": { \"type\": \"string\" },"
546      "          \"url\": { \"type\": \"string\" },"
547      "          \"children\": { \"$ref\": \"ListOfBookmarks\" }"
548      "        }"
549      "      }"
550      "    }"
551      "  }"
552      "}", &error);
553  ASSERT_TRUE(schema.valid()) << error;
554  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
555
556  Schema parent = schema.GetKnownProperty("bookmarks");
557  ASSERT_TRUE(parent.valid());
558  ASSERT_EQ(base::Value::TYPE_LIST, parent.type());
559
560  // Check the recursive type a number of times.
561  for (int i = 0; i < 10; ++i) {
562    Schema items = parent.GetItems();
563    ASSERT_TRUE(items.valid());
564    ASSERT_EQ(base::Value::TYPE_DICTIONARY, items.type());
565
566    Schema prop = items.GetKnownProperty("name");
567    ASSERT_TRUE(prop.valid());
568    ASSERT_EQ(base::Value::TYPE_STRING, prop.type());
569
570    prop = items.GetKnownProperty("url");
571    ASSERT_TRUE(prop.valid());
572    ASSERT_EQ(base::Value::TYPE_STRING, prop.type());
573
574    prop = items.GetKnownProperty("children");
575    ASSERT_TRUE(prop.valid());
576    ASSERT_EQ(base::Value::TYPE_LIST, prop.type());
577
578    parent = prop;
579  }
580}
581
582TEST(SchemaTest, UnorderedReferences) {
583  // Verifies that references and IDs can come in any order.
584  std::string error;
585  Schema schema = Schema::Parse(
586      "{"
587      "  \"type\": \"object\","
588      "  \"properties\": {"
589      "    \"a\": { \"$ref\": \"shared\" },"
590      "    \"b\": { \"$ref\": \"shared\" },"
591      "    \"c\": { \"$ref\": \"shared\" },"
592      "    \"d\": { \"$ref\": \"shared\" },"
593      "    \"e\": {"
594      "      \"type\": \"boolean\","
595      "      \"id\": \"shared\""
596      "    },"
597      "    \"f\": { \"$ref\": \"shared\" },"
598      "    \"g\": { \"$ref\": \"shared\" },"
599      "    \"h\": { \"$ref\": \"shared\" },"
600      "    \"i\": { \"$ref\": \"shared\" }"
601      "  }"
602      "}", &error);
603  ASSERT_TRUE(schema.valid()) << error;
604  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
605
606  for (char c = 'a'; c <= 'i'; ++c) {
607    Schema sub = schema.GetKnownProperty(std::string(1, c));
608    ASSERT_TRUE(sub.valid()) << c;
609    ASSERT_EQ(base::Value::TYPE_BOOLEAN, sub.type()) << c;
610  }
611}
612
613TEST(SchemaTest, AdditionalPropertiesReference) {
614  // Verifies that "additionalProperties" can be a reference.
615  std::string error;
616  Schema schema = Schema::Parse(
617      "{"
618      "  \"type\": \"object\","
619      "  \"properties\": {"
620      "    \"policy\": {"
621      "      \"type\": \"object\","
622      "      \"properties\": {"
623      "        \"foo\": {"
624      "          \"type\": \"boolean\","
625      "          \"id\": \"FooId\""
626      "        }"
627      "      },"
628      "      \"additionalProperties\": { \"$ref\": \"FooId\" }"
629      "    }"
630      "  }"
631      "}", &error);
632  ASSERT_TRUE(schema.valid()) << error;
633  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
634
635  Schema policy = schema.GetKnownProperty("policy");
636  ASSERT_TRUE(policy.valid());
637  ASSERT_EQ(base::Value::TYPE_DICTIONARY, policy.type());
638
639  Schema foo = policy.GetKnownProperty("foo");
640  ASSERT_TRUE(foo.valid());
641  EXPECT_EQ(base::Value::TYPE_BOOLEAN, foo.type());
642
643  Schema additional = policy.GetAdditionalProperties();
644  ASSERT_TRUE(additional.valid());
645  EXPECT_EQ(base::Value::TYPE_BOOLEAN, additional.type());
646
647  Schema x = policy.GetProperty("x");
648  ASSERT_TRUE(x.valid());
649  EXPECT_EQ(base::Value::TYPE_BOOLEAN, x.type());
650}
651
652TEST(SchemaTest, ItemsReference) {
653  // Verifies that "items" can be a reference.
654  std::string error;
655  Schema schema = Schema::Parse(
656      "{"
657      "  \"type\": \"object\","
658      "  \"properties\": {"
659      "    \"foo\": {"
660      "      \"type\": \"boolean\","
661      "      \"id\": \"FooId\""
662      "    },"
663      "    \"list\": {"
664      "      \"type\": \"array\","
665      "      \"items\": { \"$ref\": \"FooId\" }"
666      "    }"
667      "  }"
668      "}", &error);
669  ASSERT_TRUE(schema.valid()) << error;
670  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
671
672  Schema foo = schema.GetKnownProperty("foo");
673  ASSERT_TRUE(foo.valid());
674  EXPECT_EQ(base::Value::TYPE_BOOLEAN, foo.type());
675
676  Schema list = schema.GetKnownProperty("list");
677  ASSERT_TRUE(list.valid());
678  ASSERT_EQ(base::Value::TYPE_LIST, list.type());
679
680  Schema items = list.GetItems();
681  ASSERT_TRUE(items.valid());
682  ASSERT_EQ(base::Value::TYPE_BOOLEAN, items.type());
683}
684
685
686}  // namespace policy
687