Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / policy / core / common / schema_unittest.cc
blobdb1490cecc253569a42b05e25808542b4bf58be1
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"
12 namespace policy {
14 namespace {
16 #define TestSchemaValidation(a, b, c, d) \
17 TestSchemaValidationHelper( \
18 base::StringPrintf("%s:%i", __FILE__, __LINE__), a, b, c, d)
20 const char kTestSchema[] =
21 "{"
22 " \"type\": \"object\","
23 " \"properties\": {"
24 " \"Boolean\": { \"type\": \"boolean\" },"
25 " \"Integer\": { \"type\": \"integer\" },"
26 " \"Null\": { \"type\": \"null\" },"
27 " \"Number\": { \"type\": \"number\" },"
28 " \"String\": { \"type\": \"string\" },"
29 " \"Array\": {"
30 " \"type\": \"array\","
31 " \"items\": { \"type\": \"string\" }"
32 " },"
33 " \"ArrayOfObjects\": {"
34 " \"type\": \"array\","
35 " \"items\": {"
36 " \"type\": \"object\","
37 " \"properties\": {"
38 " \"one\": { \"type\": \"string\" },"
39 " \"two\": { \"type\": \"integer\" }"
40 " }"
41 " }"
42 " },"
43 " \"ArrayOfArray\": {"
44 " \"type\": \"array\","
45 " \"items\": {"
46 " \"type\": \"array\","
47 " \"items\": { \"type\": \"string\" }"
48 " }"
49 " },"
50 " \"Object\": {"
51 " \"type\": \"object\","
52 " \"properties\": {"
53 " \"one\": { \"type\": \"boolean\" },"
54 " \"two\": { \"type\": \"integer\" }"
55 " },"
56 " \"additionalProperties\": { \"type\": \"string\" }"
57 " },"
58 " \"ObjectOfObject\": {"
59 " \"type\": \"object\","
60 " \"properties\": {"
61 " \"Object\": {"
62 " \"type\": \"object\","
63 " \"properties\": {"
64 " \"one\": { \"type\": \"string\" },"
65 " \"two\": { \"type\": \"integer\" }"
66 " }"
67 " }"
68 " }"
69 " },"
70 " \"IntegerWithEnums\": {"
71 " \"type\": \"integer\","
72 " \"enum\": [1, 2, 3]"
73 " },"
74 " \"IntegerWithEnumsGaps\": {"
75 " \"type\": \"integer\","
76 " \"enum\": [10, 20, 30]"
77 " },"
78 " \"StringWithEnums\": {"
79 " \"type\": \"string\","
80 " \"enum\": [\"one\", \"two\", \"three\"]"
81 " },"
82 " \"IntegerWithRange\": {"
83 " \"type\": \"integer\","
84 " \"minimum\": 1,"
85 " \"maximum\": 3"
86 " },"
87 " \"ObjectOfArray\": {"
88 " \"type\": \"object\","
89 " \"properties\": {"
90 " \"List\": {"
91 " \"type\": \"array\","
92 " \"items\": { \"type\": \"integer\" }"
93 " }"
94 " }"
95 " },"
96 " \"ArrayOfObjectOfArray\": {"
97 " \"type\": \"array\","
98 " \"items\": {"
99 " \"type\": \"object\","
100 " \"properties\": {"
101 " \"List\": {"
102 " \"type\": \"array\","
103 " \"items\": { \"type\": \"string\" }"
104 " }"
105 " }"
106 " }"
107 " },"
108 " \"StringWithPattern\": {"
109 " \"type\": \"string\","
110 " \"pattern\": \"^foo+$\""
111 " },"
112 " \"ObjectWithPatternProperties\": {"
113 " \"type\": \"object\","
114 " \"patternProperties\": {"
115 " \"^foo+$\": { \"type\": \"integer\" },"
116 " \"^bar+$\": {"
117 " \"type\": \"string\","
118 " \"enum\": [\"one\", \"two\"]"
119 " }"
120 " },"
121 " \"properties\": {"
122 " \"bar\": {"
123 " \"type\": \"string\","
124 " \"enum\": [\"one\", \"three\"]"
125 " }"
126 " }"
127 " }"
128 " }"
129 "}";
131 bool ParseFails(const std::string& content) {
132 std::string error;
133 Schema schema = Schema::Parse(content, &error);
134 if (schema.valid())
135 return false;
136 EXPECT_FALSE(error.empty());
137 return true;
140 void TestSchemaValidationHelper(const std::string& source,
141 Schema schema,
142 const base::Value& value,
143 SchemaOnErrorStrategy strategy,
144 bool expected_return_value) {
145 std::string error;
146 static const char kNoErrorReturned[] = "No error returned.";
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;
158 returned =
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
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;
177 void TestSchemaValidationWithPath(Schema schema,
178 const base::Value& value,
179 const std::string& expected_failure_path) {
180 std::string error_path = "NOT_SET";
181 std::string error;
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) {
189 return "{"
190 " \"type\": \"object\","
191 " \"properties\": {"
192 " \"SomePropertyName\":" + subschema +
193 " }"
194 "}";
197 } // namespace
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\" }"
216 "}"));
218 EXPECT_TRUE(ParseFails(
220 " \"type\": \"object\","
221 " \"patternProperties\": { \"a+b*\": { \"type\": \"object\" } }"
222 "}"));
224 EXPECT_TRUE(ParseFails(
226 " \"type\": \"object\","
227 " \"properties\": { \"Policy\": { \"type\": \"bogus\" } }"
228 "}"));
230 EXPECT_TRUE(ParseFails(
232 " \"type\": \"object\","
233 " \"properties\": { \"Policy\": { \"type\": [\"string\", \"number\"] } }"
234 "}"));
236 EXPECT_TRUE(ParseFails(
238 " \"type\": \"object\","
239 " \"properties\": { \"Policy\": { \"type\": \"any\" } }"
240 "}"));
242 EXPECT_TRUE(ParseFails(
244 " \"type\": \"object\","
245 " \"properties\": { \"Policy\": 123 }"
246 "}"));
248 EXPECT_FALSE(ParseFails(
250 " \"type\": \"object\","
251 " \"unknown attribute\": \"is ignored\""
252 "}"));
255 TEST(SchemaTest, Ownership) {
256 std::string error;
257 Schema schema = Schema::Parse(
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());
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();
282 it.Advance();
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) {
293 std::string error;
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());
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 },
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());
418 it.Advance();
420 EXPECT_TRUE(it.IsAtEnd());
423 TEST(SchemaTest, Lookups) {
424 std::string error;
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\","
438 " \"properties\": {"
439 " \"Boolean\": { \"type\": \"boolean\" }"
440 " }"
441 "}", &error);
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\","
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());
463 EXPECT_FALSE(schema.GetKnownProperty("").valid());
464 EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
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 },
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
503 { "List", 5 }, // 4
504 { "IntEnum", 7 }, // 5
505 { "RangedInt", 8 }, // 6
506 { "StrEnum", 9 }, // 7
507 { "StrPat", 10 }, // 8
508 { "bar+$", 4 }, // 9
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 },
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[] = {
527 "one", // 0
528 "two", // 1
529 "three", // 2
530 "foo+", // 3
533 const internal::SchemaData kData = {
534 kSchemas,
535 kPropertyNodes,
536 kProperties,
537 kRestriction,
538 kIntEnums,
539 kStringEnums,
542 Schema schema = Schema::Wrap(&kData);
543 ASSERT_TRUE(schema.valid());
544 EXPECT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
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 },
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());
575 it.Advance();
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) {
600 std::string error;
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.
613 bundle.Clear();
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.
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);
629 // Unknown name.
630 bundle.Clear();
631 bundle.SetBoolean("Unknown", true);
632 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
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");
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;
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);
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);
762 // Tests on ArrayOfObjects.
764 Schema subschema = schema.GetProperty("ArrayOfObjects");
765 ASSERT_TRUE(subschema.valid());
766 base::ListValue root;
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);
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);
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\","
926 " \"properties\": {"
927 " \"name\": { \"$ref\": \"undeclared\" }"
928 " }"
929 "}"));
931 // Can't refer to self.
932 EXPECT_TRUE(ParseFails(
934 " \"type\": \"object\","
935 " \"properties\": {"
936 " \"name\": {"
937 " \"id\": \"self\","
938 " \"$ref\": \"self\""
939 " }"
940 " }"
941 "}"));
943 // Duplicated IDs are invalid.
944 EXPECT_TRUE(ParseFails(
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 "}"));
959 // Main object can't be a reference.
960 EXPECT_TRUE(ParseFails(
962 " \"type\": \"object\","
963 " \"id\": \"main\","
964 " \"$ref\": \"main\""
965 "}"));
967 EXPECT_TRUE(ParseFails(
969 " \"type\": \"object\","
970 " \"$ref\": \"main\""
971 "}"));
974 TEST(SchemaTest, RecursiveReferences) {
975 // Verifies that references can go to a parent schema, to define a
976 // recursive type.
977 std::string error;
978 Schema schema = Schema::Parse(
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());
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());
1021 parent = prop;
1025 TEST(SchemaTest, UnorderedReferences) {
1026 // Verifies that references and IDs can come in any order.
1027 std::string error;
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\" },"
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());
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.
1058 std::string error;
1059 Schema schema = Schema::Parse(
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());
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.
1097 std::string error;
1098 Schema schema = Schema::Parse(
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());
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\","
1133 " \"enum\": 12"
1134 "}")));
1136 // Empty enum attributes is not allowed.
1137 EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
1139 " \"type\": \"integer\","
1140 " \"enum\": []"
1141 "}")));
1143 // Enum elements type should be same as stated.
1144 EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
1146 " \"type\": \"string\","
1147 " \"enum\": [1, 2, 3]"
1148 "}")));
1150 EXPECT_FALSE(ParseFails(SchemaObjectWrapper(
1152 " \"type\": \"integer\","
1153 " \"enum\": [1, 2, 3]"
1154 "}")));
1156 EXPECT_FALSE(ParseFails(SchemaObjectWrapper(
1158 " \"type\": \"string\","
1159 " \"enum\": [\"1\", \"2\", \"3\"]"
1160 "}")));
1163 TEST(SchemaTest, RangedRestriction) {
1164 EXPECT_TRUE(ParseFails(SchemaObjectWrapper(
1166 " \"type\": \"integer\","
1167 " \"minimum\": 10,"
1168 " \"maximum\": 5"
1169 "}")));
1171 EXPECT_FALSE(ParseFails(SchemaObjectWrapper(
1173 " \"type\": \"integer\","
1174 " \"minimum\": 10,"
1175 " \"maximum\": 20"
1176 "}")));
1179 } // namespace policy