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.
5 #include "components/policy/core/common/schema.h"
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"
16 #define TestSchemaValidation(a, b, c, d) \
17 TestSchemaValidationHelper( \
18 base::StringPrintf("%s:%i", __FILE__, __LINE__), a, b, c, d)
20 const char kTestSchema
[] =
22 " \"type\": \"object\","
24 " \"Boolean\": { \"type\": \"boolean\" },"
25 " \"Integer\": { \"type\": \"integer\" },"
26 " \"Null\": { \"type\": \"null\" },"
27 " \"Number\": { \"type\": \"number\" },"
28 " \"String\": { \"type\": \"string\" },"
30 " \"type\": \"array\","
31 " \"items\": { \"type\": \"string\" }"
33 " \"ArrayOfObjects\": {"
34 " \"type\": \"array\","
36 " \"type\": \"object\","
38 " \"one\": { \"type\": \"string\" },"
39 " \"two\": { \"type\": \"integer\" }"
43 " \"ArrayOfArray\": {"
44 " \"type\": \"array\","
46 " \"type\": \"array\","
47 " \"items\": { \"type\": \"string\" }"
51 " \"type\": \"object\","
53 " \"one\": { \"type\": \"boolean\" },"
54 " \"two\": { \"type\": \"integer\" }"
56 " \"additionalProperties\": { \"type\": \"string\" }"
58 " \"ObjectOfObject\": {"
59 " \"type\": \"object\","
62 " \"type\": \"object\","
64 " \"one\": { \"type\": \"string\" },"
65 " \"two\": { \"type\": \"integer\" }"
70 " \"IntegerWithEnums\": {"
71 " \"type\": \"integer\","
72 " \"enum\": [1, 2, 3]"
74 " \"IntegerWithEnumsGaps\": {"
75 " \"type\": \"integer\","
76 " \"enum\": [10, 20, 30]"
78 " \"StringWithEnums\": {"
79 " \"type\": \"string\","
80 " \"enum\": [\"one\", \"two\", \"three\"]"
82 " \"IntegerWithRange\": {"
83 " \"type\": \"integer\","
87 " \"ObjectOfArray\": {"
88 " \"type\": \"object\","
91 " \"type\": \"array\","
92 " \"items\": { \"type\": \"integer\" }"
96 " \"ArrayOfObjectOfArray\": {"
97 " \"type\": \"array\","
99 " \"type\": \"object\","
102 " \"type\": \"array\","
103 " \"items\": { \"type\": \"string\" }"
108 " \"StringWithPattern\": {"
109 " \"type\": \"string\","
110 " \"pattern\": \"^foo+$\""
112 " \"ObjectWithPatternProperties\": {"
113 " \"type\": \"object\","
114 " \"patternProperties\": {"
115 " \"^foo+$\": { \"type\": \"integer\" },"
117 " \"type\": \"string\","
118 " \"enum\": [\"one\", \"two\"]"
123 " \"type\": \"string\","
124 " \"enum\": [\"one\", \"three\"]"
131 bool ParseFails(const std::string
& content
) {
133 Schema schema
= Schema::Parse(content
, &error
);
136 EXPECT_FALSE(error
.empty());
140 void TestSchemaValidationHelper(const std::string
& source
,
142 const base::Value
& value
,
143 SchemaOnErrorStrategy strategy
,
144 bool expected_return_value
) {
146 static const char kNoErrorReturned
[] = "No error returned.";
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
;
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;
159 schema
.Normalize(cloned_value
.get(), strategy
, NULL
, &error
, &touched
);
160 EXPECT_EQ(expected_return_value
, returned
) << source
<< ": " << error
;
162 bool strictly_valid
= schema
.Validate(value
, SCHEMA_STRICT
, NULL
, &error
);
163 EXPECT_EQ(touched
, !strictly_valid
&& returned
) << source
;
165 // Test that Schema::Normalize() have actually dropped invalid and unknown
167 if (expected_return_value
) {
169 schema
.Validate(*cloned_value
.get(), SCHEMA_STRICT
, NULL
, &error
))
172 schema
.Normalize(cloned_value
.get(), SCHEMA_STRICT
, NULL
, &error
, NULL
))
177 void TestSchemaValidationWithPath(Schema schema
,
178 const base::Value
& value
,
179 const std::string
& expected_failure_path
) {
180 std::string error_path
= "NOT_SET";
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
);
188 std::string
SchemaObjectWrapper(const std::string
& subschema
) {
190 " \"type\": \"object\","
192 " \"SomePropertyName\":" + subschema
+
199 TEST(SchemaTest
, MinimalSchema
) {
200 EXPECT_FALSE(ParseFails("{ \"type\": \"object\" }"));
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("{}"));
212 EXPECT_TRUE(ParseFails(
214 " \"type\": \"object\","
215 "\"additionalProperties\": { \"type\":\"object\" }"
218 EXPECT_TRUE(ParseFails(
220 " \"type\": \"object\","
221 " \"patternProperties\": { \"a+b*\": { \"type\": \"object\" } }"
224 EXPECT_TRUE(ParseFails(
226 " \"type\": \"object\","
227 " \"properties\": { \"Policy\": { \"type\": \"bogus\" } }"
230 EXPECT_TRUE(ParseFails(
232 " \"type\": \"object\","
233 " \"properties\": { \"Policy\": { \"type\": [\"string\", \"number\"] } }"
236 EXPECT_TRUE(ParseFails(
238 " \"type\": \"object\","
239 " \"properties\": { \"Policy\": { \"type\": \"any\" } }"
242 EXPECT_TRUE(ParseFails(
244 " \"type\": \"object\","
245 " \"properties\": { \"Policy\": 123 }"
248 EXPECT_FALSE(ParseFails(
250 " \"type\": \"object\","
251 " \"unknown attribute\": \"is ignored\""
255 TEST(SchemaTest
, Ownership
) {
257 Schema schema
= Schema::Parse(
259 " \"type\": \"object\","
262 " \"type\": \"object\","
264 " \"subsub\": { \"type\": \"string\" }"
269 ASSERT_TRUE(schema
.valid()) << error
;
270 ASSERT_EQ(base::Value::TYPE_DICTIONARY
, schema
.type());
272 schema
= schema
.GetKnownProperty("sub");
273 ASSERT_TRUE(schema
.valid());
274 ASSERT_EQ(base::Value::TYPE_DICTIONARY
, schema
.type());
277 Schema::Iterator it
= schema
.GetPropertiesIterator();
278 ASSERT_FALSE(it
.IsAtEnd());
279 EXPECT_STREQ("subsub", it
.key());
281 schema
= it
.schema();
283 EXPECT_TRUE(it
.IsAtEnd());
286 ASSERT_TRUE(schema
.valid());
287 EXPECT_EQ(base::Value::TYPE_STRING
, schema
.type());
289 // This test shouldn't leak nor use invalid memory.
292 TEST(SchemaTest
, ValidSchema
) {
294 Schema schema
= Schema::Parse(kTestSchema
, &error
);
295 ASSERT_TRUE(schema
.valid()) << error
;
297 ASSERT_EQ(base::Value::TYPE_DICTIONARY
, schema
.type());
298 EXPECT_FALSE(schema
.GetProperty("invalid").valid());
300 Schema sub
= schema
.GetProperty("Boolean");
301 ASSERT_TRUE(sub
.valid());
302 EXPECT_EQ(base::Value::TYPE_BOOLEAN
, sub
.type());
304 sub
= schema
.GetProperty("Integer");
305 ASSERT_TRUE(sub
.valid());
306 EXPECT_EQ(base::Value::TYPE_INTEGER
, sub
.type());
308 sub
= schema
.GetProperty("Null");
309 ASSERT_TRUE(sub
.valid());
310 EXPECT_EQ(base::Value::TYPE_NULL
, sub
.type());
312 sub
= schema
.GetProperty("Number");
313 ASSERT_TRUE(sub
.valid());
314 EXPECT_EQ(base::Value::TYPE_DOUBLE
, sub
.type());
316 sub
= schema
.GetProperty("String");
317 ASSERT_TRUE(sub
.valid());
318 EXPECT_EQ(base::Value::TYPE_STRING
, sub
.type());
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());
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());
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());
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());
365 sub
= schema
.GetProperty("IntegerWithEnums");
366 ASSERT_TRUE(sub
.valid());
367 ASSERT_EQ(base::Value::TYPE_INTEGER
, sub
.type());
369 sub
= schema
.GetProperty("IntegerWithEnumsGaps");
370 ASSERT_TRUE(sub
.valid());
371 ASSERT_EQ(base::Value::TYPE_INTEGER
, sub
.type());
373 sub
= schema
.GetProperty("StringWithEnums");
374 ASSERT_TRUE(sub
.valid());
375 ASSERT_EQ(base::Value::TYPE_STRING
, sub
.type());
377 sub
= schema
.GetProperty("IntegerWithRange");
378 ASSERT_TRUE(sub
.valid());
379 ASSERT_EQ(base::Value::TYPE_INTEGER
, sub
.type());
381 sub
= schema
.GetProperty("StringWithPattern");
382 ASSERT_TRUE(sub
.valid());
383 ASSERT_EQ(base::Value::TYPE_STRING
, sub
.type());
385 sub
= schema
.GetProperty("ObjectWithPatternProperties");
386 ASSERT_TRUE(sub
.valid());
387 ASSERT_EQ(base::Value::TYPE_DICTIONARY
, sub
.type());
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
},
412 Schema::Iterator it
= schema
.GetPropertiesIterator();
413 for (size_t i
= 0; i
< arraysize(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());
420 EXPECT_TRUE(it
.IsAtEnd());
423 TEST(SchemaTest
, Lookups
) {
426 Schema schema
= Schema::Parse("{ \"type\": \"object\" }", &error
);
427 ASSERT_TRUE(schema
.valid()) << error
;
428 ASSERT_EQ(base::Value::TYPE_DICTIONARY
, schema
.type());
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());
435 schema
= Schema::Parse(
437 " \"type\": \"object\","
439 " \"Boolean\": { \"type\": \"boolean\" }"
442 ASSERT_TRUE(schema
.valid()) << error
;
443 ASSERT_EQ(base::Value::TYPE_DICTIONARY
, schema
.type());
445 EXPECT_FALSE(schema
.GetKnownProperty("").valid());
446 EXPECT_FALSE(schema
.GetKnownProperty("xyz").valid());
447 EXPECT_TRUE(schema
.GetKnownProperty("Boolean").valid());
449 schema
= Schema::Parse(
451 " \"type\": \"object\","
453 " \"bb\" : { \"type\": \"null\" },"
454 " \"aa\" : { \"type\": \"boolean\" },"
455 " \"abab\" : { \"type\": \"string\" },"
456 " \"ab\" : { \"type\": \"number\" },"
457 " \"aba\" : { \"type\": \"integer\" }"
460 ASSERT_TRUE(schema
.valid()) << error
;
461 ASSERT_EQ(base::Value::TYPE_DICTIONARY
, schema
.type());
463 EXPECT_FALSE(schema
.GetKnownProperty("").valid());
464 EXPECT_FALSE(schema
.GetKnownProperty("xyz").valid());
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
},
476 for (size_t i
= 0; i
< arraysize(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());
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.
498 const internal::PropertyNode kPropertyNodes
[] = {
499 { "Boolean", 1 }, // 0
500 { "Integer", 2 }, // 1
501 { "Number", 3 }, // 2
502 { "String", 4 }, // 3
504 { "IntEnum", 7 }, // 5
505 { "RangedInt", 8 }, // 6
506 { "StrEnum", 9 }, // 7
507 { "StrPat", 10 }, // 8
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.
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+"
524 const int kIntEnums
[] = {1, 2, 3};
526 const char* kStringEnums
[] = {
533 const internal::SchemaData kData
= {
542 Schema schema
= Schema::Wrap(&kData
);
543 ASSERT_TRUE(schema
.valid());
544 EXPECT_EQ(base::Value::TYPE_DICTIONARY
, schema
.type());
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
},
561 Schema::Iterator it
= schema
.GetPropertiesIterator();
562 for (size_t i
= 0; i
< arraysize(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());
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());
577 EXPECT_TRUE(it
.IsAtEnd());
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());
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());
595 EXPECT_TRUE(schema
.GetPatternProperties("ba").empty());
596 EXPECT_TRUE(schema
.GetPatternProperties("bar+$").empty());
599 TEST(SchemaTest
, Validate
) {
601 Schema schema
= Schema::Parse(kTestSchema
, &error
);
602 ASSERT_TRUE(schema
.valid()) << error
;
604 base::DictionaryValue bundle
;
605 TestSchemaValidation(schema
, bundle
, SCHEMA_STRICT
, true);
607 // Wrong type, expected integer.
608 bundle
.SetBoolean("Integer", true);
609 TestSchemaValidation(schema
, bundle
, SCHEMA_STRICT
, false);
611 // Wrong type, expected list of strings.
614 base::ListValue list
;
615 list
.AppendInteger(1);
616 bundle
.Set("Array", list
.DeepCopy());
617 TestSchemaValidation(schema
, bundle
, SCHEMA_STRICT
, false);
620 // Wrong type in a sub-object.
623 base::DictionaryValue dict
;
624 dict
.SetString("one", "one");
625 bundle
.Set("Object", dict
.DeepCopy());
626 TestSchemaValidation(schema
, bundle
, SCHEMA_STRICT
, false);
631 bundle
.SetBoolean("Unknown", true);
632 TestSchemaValidation(schema
, bundle
, SCHEMA_STRICT
, false);
634 // All of these will be valid.
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");
643 base::ListValue list
;
644 list
.AppendString("a string");
645 list
.AppendString("another string");
646 bundle
.Set("Array", list
.DeepCopy());
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());
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());
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());
678 bundle
.SetInteger("IntegerWithEnums", 1);
679 bundle
.SetInteger("IntegerWithEnumsGaps", 20);
680 bundle
.SetString("StringWithEnums", "two");
681 bundle
.SetInteger("IntegerWithRange", 3);
683 TestSchemaValidation(schema
, bundle
, SCHEMA_STRICT
, true);
685 bundle
.SetInteger("IntegerWithEnums", 0);
686 TestSchemaValidation(schema
, bundle
, SCHEMA_STRICT
, false);
687 bundle
.SetInteger("IntegerWithEnums", 1);
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);
711 bundle
.SetString("StringWithEnums", "FOUR");
712 TestSchemaValidation(schema
, bundle
, SCHEMA_STRICT
, false);
713 bundle
.SetString("StringWithEnums", "two");
715 bundle
.SetInteger("IntegerWithRange", 4);
716 TestSchemaValidation(schema
, bundle
, SCHEMA_STRICT
, false);
717 bundle
.SetInteger("IntegerWithRange", 3);
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
);
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);
735 // Tests on ObjectOfObject.
737 Schema subschema
= schema
.GetProperty("ObjectOfObject");
738 ASSERT_TRUE(subschema
.valid());
739 base::DictionaryValue root
;
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
);
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
);
762 // Tests on ArrayOfObjects.
764 Schema subschema
= schema
.GetProperty("ArrayOfObjects");
765 ASSERT_TRUE(subschema
.valid());
766 base::ListValue root
;
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
);
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
);
793 // Tests on ObjectOfArray.
795 Schema subschema
= schema
.GetProperty("ObjectOfArray");
796 ASSERT_TRUE(subschema
.valid());
797 base::DictionaryValue root
;
799 base::ListValue
* list_value
= new base::ListValue();
800 root
.Set("List", list_value
); // Pass ownership to root.
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);
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]");
820 // Tests on ArrayOfObjectOfArray.
822 Schema subschema
= schema
.GetProperty("ArrayOfObjectOfArray");
823 ASSERT_TRUE(subschema
.valid());
824 base::ListValue root
;
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.
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);
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]");
849 // Tests on StringWithPattern.
851 Schema subschema
= schema
.GetProperty("StringWithPattern");
852 ASSERT_TRUE(subschema
.valid());
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);
866 // Tests on ObjectWithPatternProperties.
868 Schema subschema
= schema
.GetProperty("ObjectWithPatternProperties");
869 ASSERT_TRUE(subschema
.valid());
870 base::DictionaryValue root
;
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());
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
);
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
);
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
);
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
);
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
);
916 // Test that integer to double promotion is allowed.
917 bundle
.SetInteger("Number", 31415);
918 TestSchemaValidation(schema
, bundle
, SCHEMA_STRICT
, true);
921 TEST(SchemaTest
, InvalidReferences
) {
922 // References to undeclared schemas fail.
923 EXPECT_TRUE(ParseFails(
925 " \"type\": \"object\","
927 " \"name\": { \"$ref\": \"undeclared\" }"
931 // Can't refer to self.
932 EXPECT_TRUE(ParseFails(
934 " \"type\": \"object\","
938 " \"$ref\": \"self\""
943 // Duplicated IDs are invalid.
944 EXPECT_TRUE(ParseFails(
946 " \"type\": \"object\","
950 " \"type\": \"string\""
954 " \"type\": \"string\""
959 // Main object can't be a reference.
960 EXPECT_TRUE(ParseFails(
962 " \"type\": \"object\","
964 " \"$ref\": \"main\""
967 EXPECT_TRUE(ParseFails(
969 " \"type\": \"object\","
970 " \"$ref\": \"main\""
974 TEST(SchemaTest
, RecursiveReferences
) {
975 // Verifies that references can go to a parent schema, to define a
978 Schema schema
= Schema::Parse(
980 " \"type\": \"object\","
983 " \"type\": \"array\","
984 " \"id\": \"ListOfBookmarks\","
986 " \"type\": \"object\","
988 " \"name\": { \"type\": \"string\" },"
989 " \"url\": { \"type\": \"string\" },"
990 " \"children\": { \"$ref\": \"ListOfBookmarks\" }"
996 ASSERT_TRUE(schema
.valid()) << error
;
997 ASSERT_EQ(base::Value::TYPE_DICTIONARY
, schema
.type());
999 Schema parent
= schema
.GetKnownProperty("bookmarks");
1000 ASSERT_TRUE(parent
.valid());
1001 ASSERT_EQ(base::Value::TYPE_LIST
, parent
.type());
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());
1009 Schema prop
= items
.GetKnownProperty("name");
1010 ASSERT_TRUE(prop
.valid());
1011 ASSERT_EQ(base::Value::TYPE_STRING
, prop
.type());
1013 prop
= items
.GetKnownProperty("url");
1014 ASSERT_TRUE(prop
.valid());
1015 ASSERT_EQ(base::Value::TYPE_STRING
, prop
.type());
1017 prop
= items
.GetKnownProperty("children");
1018 ASSERT_TRUE(prop
.valid());
1019 ASSERT_EQ(base::Value::TYPE_LIST
, prop
.type());
1025 TEST(SchemaTest
, UnorderedReferences
) {
1026 // Verifies that references and IDs can come in any order.
1028 Schema schema
= Schema::Parse(
1030 " \"type\": \"object\","
1031 " \"properties\": {"
1032 " \"a\": { \"$ref\": \"shared\" },"
1033 " \"b\": { \"$ref\": \"shared\" },"
1034 " \"c\": { \"$ref\": \"shared\" },"
1035 " \"d\": { \"$ref\": \"shared\" },"
1037 " \"type\": \"boolean\","
1038 " \"id\": \"shared\""
1040 " \"f\": { \"$ref\": \"shared\" },"
1041 " \"g\": { \"$ref\": \"shared\" },"
1042 " \"h\": { \"$ref\": \"shared\" },"
1043 " \"i\": { \"$ref\": \"shared\" }"
1046 ASSERT_TRUE(schema
.valid()) << error
;
1047 ASSERT_EQ(base::Value::TYPE_DICTIONARY
, schema
.type());
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
;
1056 TEST(SchemaTest
, AdditionalPropertiesReference
) {
1057 // Verifies that "additionalProperties" can be a reference.
1059 Schema schema
= Schema::Parse(
1061 " \"type\": \"object\","
1062 " \"properties\": {"
1064 " \"type\": \"object\","
1065 " \"properties\": {"
1067 " \"type\": \"boolean\","
1068 " \"id\": \"FooId\""
1071 " \"additionalProperties\": { \"$ref\": \"FooId\" }"
1075 ASSERT_TRUE(schema
.valid()) << error
;
1076 ASSERT_EQ(base::Value::TYPE_DICTIONARY
, schema
.type());
1078 Schema policy
= schema
.GetKnownProperty("policy");
1079 ASSERT_TRUE(policy
.valid());
1080 ASSERT_EQ(base::Value::TYPE_DICTIONARY
, policy
.type());
1082 Schema foo
= policy
.GetKnownProperty("foo");
1083 ASSERT_TRUE(foo
.valid());
1084 EXPECT_EQ(base::Value::TYPE_BOOLEAN
, foo
.type());
1086 Schema additional
= policy
.GetAdditionalProperties();
1087 ASSERT_TRUE(additional
.valid());
1088 EXPECT_EQ(base::Value::TYPE_BOOLEAN
, additional
.type());
1090 Schema x
= policy
.GetProperty("x");
1091 ASSERT_TRUE(x
.valid());
1092 EXPECT_EQ(base::Value::TYPE_BOOLEAN
, x
.type());
1095 TEST(SchemaTest
, ItemsReference
) {
1096 // Verifies that "items" can be a reference.
1098 Schema schema
= Schema::Parse(
1100 " \"type\": \"object\","
1101 " \"properties\": {"
1103 " \"type\": \"boolean\","
1104 " \"id\": \"FooId\""
1107 " \"type\": \"array\","
1108 " \"items\": { \"$ref\": \"FooId\" }"
1112 ASSERT_TRUE(schema
.valid()) << error
;
1113 ASSERT_EQ(base::Value::TYPE_DICTIONARY
, schema
.type());
1115 Schema foo
= schema
.GetKnownProperty("foo");
1116 ASSERT_TRUE(foo
.valid());
1117 EXPECT_EQ(base::Value::TYPE_BOOLEAN
, foo
.type());
1119 Schema list
= schema
.GetKnownProperty("list");
1120 ASSERT_TRUE(list
.valid());
1121 ASSERT_EQ(base::Value::TYPE_LIST
, list
.type());
1123 Schema items
= list
.GetItems();
1124 ASSERT_TRUE(items
.valid());
1125 ASSERT_EQ(base::Value::TYPE_BOOLEAN
, items
.type());
1128 TEST(SchemaTest
, EnumerationRestriction
) {
1129 // Enum attribute is a list.
1130 EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
1132 " \"type\": \"string\","
1136 // Empty enum attributes is not allowed.
1137 EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
1139 " \"type\": \"integer\","
1143 // Enum elements type should be same as stated.
1144 EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
1146 " \"type\": \"string\","
1147 " \"enum\": [1, 2, 3]"
1150 EXPECT_FALSE(ParseFails(SchemaObjectWrapper(
1152 " \"type\": \"integer\","
1153 " \"enum\": [1, 2, 3]"
1156 EXPECT_FALSE(ParseFails(SchemaObjectWrapper(
1158 " \"type\": \"string\","
1159 " \"enum\": [\"1\", \"2\", \"3\"]"
1163 TEST(SchemaTest
, RangedRestriction
) {
1164 EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
1166 " \"type\": \"integer\","
1171 EXPECT_FALSE(ParseFails(SchemaObjectWrapper(
1173 " \"type\": \"integer\","
1179 } // namespace policy