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 <stddef.h>
8
9 #include <memory>
10 #include <utility>
11
12 #include "base/macros.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/values.h"
15 #include "components/policy/core/common/schema_internal.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 namespace policy {
19
20 namespace {
21
22 #define TestSchemaValidation(a, b, c, d) \
23 TestSchemaValidationHelper( \
24 base::StringPrintf("%s:%i", __FILE__, __LINE__), a, b, c, d)
25
26 const char kTestSchema[] = R"({
27 "type": "object",
28 "properties": {
29 "Boolean": { "type": "boolean" },
30 "Integer": { "type": "integer" },
31 "Null": { "type": "null" },
32 "Number": { "type": "number" },
33 "String": { "type": "string" },
34 "Array": {
35 "type": "array",
36 "items": { "type": "string" }
37 },
38 "ArrayOfObjects": {
39 "type": "array",
40 "items": {
41 "type": "object",
42 "properties": {
43 "one": { "type": "string" },
44 "two": { "type": "integer" }
45 }
46 }
47 },
48 "ArrayOfArray": {
49 "type": "array",
50 "items": {
51 "type": "array",
52 "items": { "type": "string" }
53 }
54 },
55 "Object": {
56 "type": "object",
57 "properties": {
58 "one": { "type": "boolean" },
59 "two": { "type": "integer" }
60 },
61 "additionalProperties": { "type": "string" }
62 },
63 "ObjectOfObject": {
64 "type": "object",
65 "properties": {
66 "Object": {
67 "type": "object",
68 "properties": {
69 "one": { "type": "string" },
70 "two": { "type": "integer" }
71 }
72 }
73 }
74 },
75 "IntegerWithEnums": {
76 "type": "integer",
77 "enum": [1, 2, 3]
78 },
79 "IntegerWithEnumsGaps": {
80 "type": "integer",
81 "enum": [10, 20, 30]
82 },
83 "StringWithEnums": {
84 "type": "string",
85 "enum": ["one", "two", "three"]
86 },
87 "IntegerWithRange": {
88 "type": "integer",
89 "minimum": 1,
90 "maximum": 3
91 },
92 "ObjectOfArray": {
93 "type": "object",
94 "properties": {
95 "List": {
96 "type": "array",
97 "items": { "type": "integer" }
98 }
99 }
100 },
101 "ArrayOfObjectOfArray": {
102 "type": "array",
103 "items": {
104 "type": "object",
105 "properties": {
106 "List": {
107 "type": "array",
108 "items": { "type": "string" }
109 }
110 }
111 }
112 },
113 "StringWithPattern": {
114 "type": "string",
115 "pattern": "^foo+$"
116 },
117 "ObjectWithPatternProperties": {
118 "type": "object",
119 "patternProperties": {
120 "^foo+$": { "type": "integer" },
121 "^bar+$": {
122 "type": "string",
123 "enum": ["one", "two"]
124 }
125 },
126 "properties": {
127 "bar": {
128 "type": "string",
129 "enum": ["one", "three"]
130 }
131 }
132 },
133 "ObjectWithRequiredProperties": {
134 "type": "object",
135 "properties": {
136 "Integer": {
137 "type": "integer",
138 "enum": [1, 2]
139 },
140 "String": { "type": "string" },
141 "Number": { "type": "number" }
142 },
143 "patternProperties": {
144 "^Integer": {
145 "type": "integer",
146 "enum": [1, 3]
147 }
148 },
149 "required": [ "Integer", "String" ]
150 }
151 }
152 })";
153
ParseFails(const std::string & content)154 bool ParseFails(const std::string& content) {
155 std::string error;
156 Schema schema = Schema::Parse(content, &error);
157 if (schema.valid())
158 return false;
159 EXPECT_FALSE(error.empty());
160 return true;
161 }
162
TestSchemaValidationHelper(const std::string & source,Schema schema,const base::Value & value,SchemaOnErrorStrategy strategy,bool expected_return_value)163 void TestSchemaValidationHelper(const std::string& source,
164 Schema schema,
165 const base::Value& value,
166 SchemaOnErrorStrategy strategy,
167 bool expected_return_value) {
168 std::string error;
169 static const char kNoErrorReturned[] = "No error returned.";
170
171 // Test that Schema::Validate() works as expected.
172 error = kNoErrorReturned;
173 bool returned = schema.Validate(value, strategy, nullptr, &error);
174 ASSERT_EQ(expected_return_value, returned) << source << ": " << error;
175
176 // Test that Schema::Normalize() will return the same value as
177 // Schema::Validate().
178 error = kNoErrorReturned;
179 std::unique_ptr<base::Value> cloned_value(value.DeepCopy());
180 bool touched = false;
181 returned =
182 schema.Normalize(cloned_value.get(), strategy, nullptr, &error, &touched);
183 EXPECT_EQ(expected_return_value, returned) << source << ": " << error;
184
185 bool strictly_valid = schema.Validate(value, SCHEMA_STRICT, nullptr, &error);
186 EXPECT_EQ(touched, !strictly_valid && returned) << source;
187
188 // Test that Schema::Normalize() have actually dropped invalid and unknown
189 // properties.
190 if (expected_return_value) {
191 EXPECT_TRUE(schema.Validate(*cloned_value, SCHEMA_STRICT, nullptr, &error))
192 << source;
193 EXPECT_TRUE(schema.Normalize(cloned_value.get(), SCHEMA_STRICT, nullptr,
194 &error, nullptr))
195 << source;
196 }
197 }
198
TestSchemaValidationWithPath(Schema schema,const base::Value & value,const std::string & expected_failure_path)199 void TestSchemaValidationWithPath(Schema schema,
200 const base::Value& value,
201 const std::string& expected_failure_path) {
202 std::string error_path = "NOT_SET";
203 std::string error;
204
205 bool returned = schema.Validate(value, SCHEMA_STRICT, &error_path, &error);
206 ASSERT_FALSE(returned) << error_path;
207 EXPECT_EQ(error_path, expected_failure_path);
208 }
209
SchemaObjectWrapper(const std::string & subschema)210 std::string SchemaObjectWrapper(const std::string& subschema) {
211 return "{"
212 " \"type\": \"object\","
213 " \"properties\": {"
214 " \"SomePropertyName\":" + subschema +
215 " }"
216 "}";
217 }
218
219 } // namespace
220
TEST(SchemaTest,MinimalSchema)221 TEST(SchemaTest, MinimalSchema) {
222 EXPECT_FALSE(ParseFails(R"({ "type": "object" })"));
223 }
224
TEST(SchemaTest,InvalidSchemas)225 TEST(SchemaTest, InvalidSchemas) {
226 EXPECT_TRUE(ParseFails(""));
227 EXPECT_TRUE(ParseFails("omg"));
228 EXPECT_TRUE(ParseFails("\"omg\""));
229 EXPECT_TRUE(ParseFails("123"));
230 EXPECT_TRUE(ParseFails("[]"));
231 EXPECT_TRUE(ParseFails("null"));
232 EXPECT_TRUE(ParseFails("{}"));
233
234 EXPECT_TRUE(ParseFails(R"({
235 "type": "object",
236 "additionalProperties": { "type":"object" }
237 })"));
238
239 EXPECT_TRUE(ParseFails(R"({
240 "type": "object",
241 "patternProperties": { "a+b*": { "type": "object" } }
242 })"));
243
244 EXPECT_TRUE(ParseFails(R"({
245 "type": "object",
246 "properties": { "Policy": { "type": "bogus" } }
247 })"));
248
249 EXPECT_TRUE(ParseFails(R"({
250 "type": "object",
251 "properties": { "Policy": { "type": ["string", "number"] } }
252 })"));
253
254 EXPECT_TRUE(ParseFails(R"({
255 "type": "object",
256 "properties": { "Policy": { "type": "any" } }
257 })"));
258
259 EXPECT_TRUE(ParseFails(R"({
260 "type": "object",
261 "properties": { "Policy": 123 }
262 })"));
263
264 EXPECT_FALSE(ParseFails(R"({
265 "type": "object",
266 "unknown attribute": "is ignored"
267 })"));
268 }
269
TEST(SchemaTest,Ownership)270 TEST(SchemaTest, Ownership) {
271 std::string error;
272 Schema schema = Schema::Parse(R"({
273 "type": "object",
274 "properties": {
275 "sub": {
276 "type": "object",
277 "properties": {
278 "subsub": { "type": "string" }
279 }
280 }
281 }
282 })",
283 &error);
284 ASSERT_TRUE(schema.valid()) << error;
285 ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
286
287 schema = schema.GetKnownProperty("sub");
288 ASSERT_TRUE(schema.valid());
289 ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
290
291 {
292 Schema::Iterator it = schema.GetPropertiesIterator();
293 ASSERT_FALSE(it.IsAtEnd());
294 EXPECT_STREQ("subsub", it.key());
295
296 schema = it.schema();
297 it.Advance();
298 EXPECT_TRUE(it.IsAtEnd());
299 }
300
301 ASSERT_TRUE(schema.valid());
302 EXPECT_EQ(base::Value::Type::STRING, schema.type());
303
304 // This test shouldn't leak nor use invalid memory.
305 }
306
TEST(SchemaTest,ValidSchema)307 TEST(SchemaTest, ValidSchema) {
308 std::string error;
309 Schema schema = Schema::Parse(kTestSchema, &error);
310 ASSERT_TRUE(schema.valid()) << error;
311
312 ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
313 EXPECT_FALSE(schema.GetProperty("invalid").valid());
314
315 Schema sub = schema.GetProperty("Boolean");
316 ASSERT_TRUE(sub.valid());
317 EXPECT_EQ(base::Value::Type::BOOLEAN, sub.type());
318
319 sub = schema.GetProperty("Integer");
320 ASSERT_TRUE(sub.valid());
321 EXPECT_EQ(base::Value::Type::INTEGER, sub.type());
322
323 sub = schema.GetProperty("Null");
324 ASSERT_TRUE(sub.valid());
325 EXPECT_EQ(base::Value::Type::NONE, sub.type());
326
327 sub = schema.GetProperty("Number");
328 ASSERT_TRUE(sub.valid());
329 EXPECT_EQ(base::Value::Type::DOUBLE, sub.type());
330
331 sub = schema.GetProperty("String");
332 ASSERT_TRUE(sub.valid());
333 EXPECT_EQ(base::Value::Type::STRING, sub.type());
334
335 sub = schema.GetProperty("Array");
336 ASSERT_TRUE(sub.valid());
337 ASSERT_EQ(base::Value::Type::LIST, sub.type());
338 sub = sub.GetItems();
339 ASSERT_TRUE(sub.valid());
340 EXPECT_EQ(base::Value::Type::STRING, sub.type());
341
342 sub = schema.GetProperty("ArrayOfObjects");
343 ASSERT_TRUE(sub.valid());
344 ASSERT_EQ(base::Value::Type::LIST, sub.type());
345 sub = sub.GetItems();
346 ASSERT_TRUE(sub.valid());
347 EXPECT_EQ(base::Value::Type::DICTIONARY, sub.type());
348 Schema subsub = sub.GetProperty("one");
349 ASSERT_TRUE(subsub.valid());
350 EXPECT_EQ(base::Value::Type::STRING, subsub.type());
351 subsub = sub.GetProperty("two");
352 ASSERT_TRUE(subsub.valid());
353 EXPECT_EQ(base::Value::Type::INTEGER, subsub.type());
354 subsub = sub.GetProperty("invalid");
355 EXPECT_FALSE(subsub.valid());
356
357 sub = schema.GetProperty("ArrayOfArray");
358 ASSERT_TRUE(sub.valid());
359 ASSERT_EQ(base::Value::Type::LIST, sub.type());
360 sub = sub.GetItems();
361 ASSERT_TRUE(sub.valid());
362 ASSERT_EQ(base::Value::Type::LIST, sub.type());
363 sub = sub.GetItems();
364 ASSERT_TRUE(sub.valid());
365 EXPECT_EQ(base::Value::Type::STRING, sub.type());
366
367 sub = schema.GetProperty("Object");
368 ASSERT_TRUE(sub.valid());
369 ASSERT_EQ(base::Value::Type::DICTIONARY, sub.type());
370 subsub = sub.GetProperty("one");
371 ASSERT_TRUE(subsub.valid());
372 EXPECT_EQ(base::Value::Type::BOOLEAN, subsub.type());
373 subsub = sub.GetProperty("two");
374 ASSERT_TRUE(subsub.valid());
375 EXPECT_EQ(base::Value::Type::INTEGER, subsub.type());
376 subsub = sub.GetProperty("undeclared");
377 ASSERT_TRUE(subsub.valid());
378 EXPECT_EQ(base::Value::Type::STRING, subsub.type());
379
380 sub = schema.GetProperty("IntegerWithEnums");
381 ASSERT_TRUE(sub.valid());
382 ASSERT_EQ(base::Value::Type::INTEGER, sub.type());
383
384 sub = schema.GetProperty("IntegerWithEnumsGaps");
385 ASSERT_TRUE(sub.valid());
386 ASSERT_EQ(base::Value::Type::INTEGER, sub.type());
387
388 sub = schema.GetProperty("StringWithEnums");
389 ASSERT_TRUE(sub.valid());
390 ASSERT_EQ(base::Value::Type::STRING, sub.type());
391
392 sub = schema.GetProperty("IntegerWithRange");
393 ASSERT_TRUE(sub.valid());
394 ASSERT_EQ(base::Value::Type::INTEGER, sub.type());
395
396 sub = schema.GetProperty("StringWithPattern");
397 ASSERT_TRUE(sub.valid());
398 ASSERT_EQ(base::Value::Type::STRING, sub.type());
399
400 sub = schema.GetProperty("ObjectWithPatternProperties");
401 ASSERT_TRUE(sub.valid());
402 ASSERT_EQ(base::Value::Type::DICTIONARY, sub.type());
403
404 sub = schema.GetProperty("ObjectWithRequiredProperties");
405 ASSERT_TRUE(sub.valid());
406 ASSERT_EQ(base::Value::Type::DICTIONARY, sub.type());
407
408 struct {
409 const char* expected_key;
410 base::Value::Type expected_type;
411 } kExpectedProperties[] = {
412 { "Array", base::Value::Type::LIST },
413 { "ArrayOfArray", base::Value::Type::LIST },
414 { "ArrayOfObjectOfArray", base::Value::Type::LIST },
415 { "ArrayOfObjects", base::Value::Type::LIST },
416 { "Boolean", base::Value::Type::BOOLEAN },
417 { "Integer", base::Value::Type::INTEGER },
418 { "IntegerWithEnums", base::Value::Type::INTEGER },
419 { "IntegerWithEnumsGaps", base::Value::Type::INTEGER },
420 { "IntegerWithRange", base::Value::Type::INTEGER },
421 { "Null", base::Value::Type::NONE },
422 { "Number", base::Value::Type::DOUBLE },
423 { "Object", base::Value::Type::DICTIONARY },
424 { "ObjectOfArray", base::Value::Type::DICTIONARY },
425 { "ObjectOfObject", base::Value::Type::DICTIONARY },
426 { "ObjectWithPatternProperties", base::Value::Type::DICTIONARY },
427 { "ObjectWithRequiredProperties", base::Value::Type::DICTIONARY },
428 { "String", base::Value::Type::STRING },
429 { "StringWithEnums", base::Value::Type::STRING },
430 { "StringWithPattern", base::Value::Type::STRING },
431 };
432 Schema::Iterator it = schema.GetPropertiesIterator();
433 for (size_t i = 0; i < arraysize(kExpectedProperties); ++i) {
434 ASSERT_FALSE(it.IsAtEnd());
435 EXPECT_STREQ(kExpectedProperties[i].expected_key, it.key());
436 ASSERT_TRUE(it.schema().valid());
437 EXPECT_EQ(kExpectedProperties[i].expected_type, it.schema().type());
438 it.Advance();
439 }
440 EXPECT_TRUE(it.IsAtEnd());
441 }
442
TEST(SchemaTest,Lookups)443 TEST(SchemaTest, Lookups) {
444 std::string error;
445
446 Schema schema = Schema::Parse(R"({ "type": "object" })", &error);
447 ASSERT_TRUE(schema.valid()) << error;
448 ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
449
450 // This empty schema should never find named properties.
451 EXPECT_FALSE(schema.GetKnownProperty("").valid());
452 EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
453 EXPECT_TRUE(schema.GetPropertiesIterator().IsAtEnd());
454
455 schema = Schema::Parse(R"({
456 "type": "object",
457 "properties": {
458 "Boolean": { "type": "boolean" }
459 }
460 })",
461 &error);
462 ASSERT_TRUE(schema.valid()) << error;
463 ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
464
465 EXPECT_FALSE(schema.GetKnownProperty("").valid());
466 EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
467 EXPECT_TRUE(schema.GetKnownProperty("Boolean").valid());
468
469 schema = Schema::Parse(R"({
470 "type": "object",
471 "properties": {
472 "bb" : { "type": "null" },
473 "aa" : { "type": "boolean" },
474 "abab" : { "type": "string" },
475 "ab" : { "type": "number" },
476 "aba" : { "type": "integer" }
477 }
478 })",
479 &error);
480 ASSERT_TRUE(schema.valid()) << error;
481 ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
482
483 EXPECT_FALSE(schema.GetKnownProperty("").valid());
484 EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
485
486 struct {
487 const char* expected_key;
488 base::Value::Type expected_type;
489 } kExpectedKeys[] = {
490 { "aa", base::Value::Type::BOOLEAN },
491 { "ab", base::Value::Type::DOUBLE },
492 { "aba", base::Value::Type::INTEGER },
493 { "abab", base::Value::Type::STRING },
494 { "bb", base::Value::Type::NONE },
495 };
496 for (size_t i = 0; i < arraysize(kExpectedKeys); ++i) {
497 Schema sub = schema.GetKnownProperty(kExpectedKeys[i].expected_key);
498 ASSERT_TRUE(sub.valid());
499 EXPECT_EQ(kExpectedKeys[i].expected_type, sub.type());
500 }
501
502 schema = Schema::Parse(R"(
503 {
504 "type": "object",
505 "properties": {
506 "String": { "type": "string" },
507 "Object": {
508 "type": "object",
509 "properties": {"Integer": {"type": "integer"}},
510 "required": [ "Integer" ]
511 },
512 "Number": { "type": "number" }
513 },
514 "required": [ "String", "Object"]
515 })",
516 &error);
517 ASSERT_TRUE(schema.valid()) << error;
518 ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
519
520 EXPECT_EQ(std::vector<std::string>({"String", "Object"}),
521 schema.GetRequiredProperties());
522
523 schema = schema.GetKnownProperty("Object");
524 ASSERT_TRUE(schema.valid()) << error;
525 ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
526
527 EXPECT_EQ(std::vector<std::string>({"Integer"}),
528 schema.GetRequiredProperties());
529 }
530
TEST(SchemaTest,Wrap)531 TEST(SchemaTest, Wrap) {
532 const internal::SchemaNode kSchemas[] = {
533 { base::Value::Type::DICTIONARY, 0 }, // 0: root node
534 { base::Value::Type::BOOLEAN, -1 }, // 1
535 { base::Value::Type::INTEGER, -1 }, // 2
536 { base::Value::Type::DOUBLE, -1 }, // 3
537 { base::Value::Type::STRING, -1 }, // 4
538 { base::Value::Type::LIST, 4 }, // 5: list of strings.
539 { base::Value::Type::LIST, 5 }, // 6: list of lists of strings.
540 { base::Value::Type::INTEGER, 0 }, // 7: integer enumerations.
541 { base::Value::Type::INTEGER, 1 }, // 8: ranged integers.
542 { base::Value::Type::STRING, 2 }, // 9: string enumerations.
543 { base::Value::Type::STRING, 3 }, // 10: string with pattern.
544 { base::Value::Type::DICTIONARY, 1 }, // 11: dictionary with required
545 // properties
546 };
547
548 const internal::PropertyNode kPropertyNodes[] = {
549 { "Boolean", 1}, // 0
550 { "DictRequired", 11}, // 1
551 { "Integer", 2}, // 2
552 { "List", 5}, // 3
553 { "Number", 3}, // 4
554 { "String", 4}, // 5
555 { "IntEnum", 7}, // 6
556 { "RangedInt", 8}, // 7
557 { "StrEnum", 9}, // 8
558 { "StrPat", 10}, // 9
559 { "bar+$", 4}, // 10
560 { "String", 4}, // 11
561 { "Number", 3}, // 12
562 };
563
564 const internal::PropertiesNode kProperties[] = {
565 // 0 to 10 (exclusive) are the known properties in kPropertyNodes, 9 is
566 // patternProperties and 6 is the additionalProperties node.
567 { 0, 10, 11, 0, 0, 6 },
568 // 11 to 13 (exclusive) are the known properties in kPropertyNodes. 0 to
569 // 1 (exclusive) are the required properties in kRequired. -1 indicates
570 // no additionalProperties.
571 { 11, 13, 13, 0, 1, -1 },
572 };
573
574 const internal::RestrictionNode kRestriction[] = {
575 {{0, 3}}, // 0: [1, 2, 3]
576 {{5, 1}}, // 1: minimum = 1, maximum = 5
577 {{0, 3}}, // 2: ["one", "two", "three"]
578 {{3, 3}}, // 3: pattern "foo+"
579 };
580
581 const char* kRequired[] = {"String"};
582
583 const int kIntEnums[] = {1, 2, 3};
584
585 const char* kStringEnums[] = {
586 "one", // 0
587 "two", // 1
588 "three", // 2
589 "foo+", // 3
590 };
591
592 const internal::SchemaData kData = {
593 kSchemas,
594 kPropertyNodes,
595 kProperties,
596 kRestriction,
597 kRequired,
598 kIntEnums,
599 kStringEnums,
600 };
601
602 Schema schema = Schema::Wrap(&kData);
603 ASSERT_TRUE(schema.valid());
604 EXPECT_EQ(base::Value::Type::DICTIONARY, schema.type());
605
606 struct {
607 const char* key;
608 base::Value::Type type;
609 } kExpectedProperties[] = {
610 { "Boolean", base::Value::Type::BOOLEAN },
611 { "DictRequired", base::Value::Type::DICTIONARY },
612 { "Integer", base::Value::Type::INTEGER },
613 { "List", base::Value::Type::LIST },
614 { "Number", base::Value::Type::DOUBLE },
615 { "String", base::Value::Type::STRING },
616 { "IntEnum", base::Value::Type::INTEGER },
617 { "RangedInt", base::Value::Type::INTEGER },
618 { "StrEnum", base::Value::Type::STRING },
619 { "StrPat", base::Value::Type::STRING },
620 };
621
622 Schema::Iterator it = schema.GetPropertiesIterator();
623 for (size_t i = 0; i < arraysize(kExpectedProperties); ++i) {
624 ASSERT_FALSE(it.IsAtEnd());
625 EXPECT_STREQ(kExpectedProperties[i].key, it.key());
626 Schema sub = it.schema();
627 ASSERT_TRUE(sub.valid());
628 EXPECT_EQ(kExpectedProperties[i].type, sub.type());
629
630 if (sub.type() == base::Value::Type::LIST) {
631 Schema items = sub.GetItems();
632 ASSERT_TRUE(items.valid());
633 EXPECT_EQ(base::Value::Type::STRING, items.type());
634 }
635
636 it.Advance();
637 }
638 EXPECT_TRUE(it.IsAtEnd());
639
640 Schema sub = schema.GetAdditionalProperties();
641 ASSERT_TRUE(sub.valid());
642 ASSERT_EQ(base::Value::Type::LIST, sub.type());
643 Schema subsub = sub.GetItems();
644 ASSERT_TRUE(subsub.valid());
645 ASSERT_EQ(base::Value::Type::LIST, subsub.type());
646 Schema subsubsub = subsub.GetItems();
647 ASSERT_TRUE(subsubsub.valid());
648 ASSERT_EQ(base::Value::Type::STRING, subsubsub.type());
649
650 SchemaList schema_list = schema.GetPatternProperties("barr");
651 ASSERT_EQ(1u, schema_list.size());
652 sub = schema_list[0];
653 ASSERT_TRUE(sub.valid());
654 EXPECT_EQ(base::Value::Type::STRING, sub.type());
655
656 EXPECT_TRUE(schema.GetPatternProperties("ba").empty());
657 EXPECT_TRUE(schema.GetPatternProperties("bar+$").empty());
658
659 Schema dict = schema.GetKnownProperty("DictRequired");
660 ASSERT_TRUE(dict.valid());
661 ASSERT_EQ(base::Value::Type::DICTIONARY, dict.type());
662
663 EXPECT_EQ(std::vector<std::string>({"String"}), dict.GetRequiredProperties());
664 }
665
TEST(SchemaTest,Validate)666 TEST(SchemaTest, Validate) {
667 std::string error;
668 Schema schema = Schema::Parse(kTestSchema, &error);
669 ASSERT_TRUE(schema.valid()) << error;
670
671 base::DictionaryValue bundle;
672 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
673
674 // Wrong type, expected integer.
675 bundle.SetBoolean("Integer", true);
676 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
677
678 // Wrong type, expected list of strings.
679 {
680 bundle.Clear();
681 base::ListValue list;
682 list.AppendInteger(1);
683 bundle.SetKey("Array", std::move(list));
684 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
685 }
686
687 // Wrong type in a sub-object.
688 {
689 bundle.Clear();
690 base::DictionaryValue dict;
691 dict.SetString("one", "one");
692 bundle.SetKey("Object", std::move(dict));
693 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
694 }
695
696 // Unknown name.
697 bundle.Clear();
698 bundle.SetBoolean("Unknown", true);
699 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
700
701 // All of these will be valid.
702 bundle.Clear();
703 bundle.SetBoolean("Boolean", true);
704 bundle.SetInteger("Integer", 123);
705 bundle.Set("Null", std::make_unique<base::Value>());
706 bundle.SetDouble("Number", 3.14);
707 bundle.SetString("String", "omg");
708
709 {
710 base::ListValue list;
711 list.AppendString("a string");
712 list.AppendString("another string");
713 bundle.SetKey("Array", std::move(list));
714 }
715
716 {
717 base::DictionaryValue dict;
718 dict.SetString("one", "string");
719 dict.SetInteger("two", 2);
720 base::ListValue list;
721 list.GetList().push_back(dict.Clone());
722 list.GetList().push_back(std::move(dict));
723 bundle.SetKey("ArrayOfObjects", std::move(list));
724 }
725
726 {
727 base::ListValue list;
728 list.AppendString("a string");
729 list.AppendString("another string");
730 base::ListValue listlist;
731 listlist.GetList().push_back(list.Clone());
732 listlist.GetList().push_back(std::move(list));
733 bundle.SetKey("ArrayOfArray", std::move(listlist));
734 }
735
736 {
737 base::DictionaryValue dict;
738 dict.SetBoolean("one", true);
739 dict.SetInteger("two", 2);
740 dict.SetString("additionally", "a string");
741 dict.SetString("and also", "another string");
742 bundle.SetKey("Object", std::move(dict));
743 }
744
745 {
746 base::DictionaryValue dict;
747 dict.SetInteger("Integer", 1);
748 dict.SetString("String", "a string");
749 dict.SetDouble("Number", 3.14);
750 bundle.SetKey("ObjectWithRequiredProperties", std::move(dict));
751 }
752
753 bundle.SetInteger("IntegerWithEnums", 1);
754 bundle.SetInteger("IntegerWithEnumsGaps", 20);
755 bundle.SetString("StringWithEnums", "two");
756 bundle.SetInteger("IntegerWithRange", 3);
757
758 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
759
760 bundle.SetInteger("IntegerWithEnums", 0);
761 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
762 bundle.SetInteger("IntegerWithEnums", 1);
763
764 bundle.SetInteger("IntegerWithEnumsGaps", 0);
765 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
766 bundle.SetInteger("IntegerWithEnumsGaps", 9);
767 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
768 bundle.SetInteger("IntegerWithEnumsGaps", 10);
769 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
770 bundle.SetInteger("IntegerWithEnumsGaps", 11);
771 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
772 bundle.SetInteger("IntegerWithEnumsGaps", 19);
773 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
774 bundle.SetInteger("IntegerWithEnumsGaps", 21);
775 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
776 bundle.SetInteger("IntegerWithEnumsGaps", 29);
777 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
778 bundle.SetInteger("IntegerWithEnumsGaps", 30);
779 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
780 bundle.SetInteger("IntegerWithEnumsGaps", 31);
781 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
782 bundle.SetInteger("IntegerWithEnumsGaps", 100);
783 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
784 bundle.SetInteger("IntegerWithEnumsGaps", 20);
785
786 bundle.SetString("StringWithEnums", "FOUR");
787 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
788 bundle.SetString("StringWithEnums", "two");
789
790 bundle.SetInteger("IntegerWithRange", 4);
791 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
792 bundle.SetInteger("IntegerWithRange", 3);
793
794 // Unknown top level property.
795 bundle.SetString("boom", "bang");
796 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
797 TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, true);
798 TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_UNKNOWN, true);
799 TestSchemaValidationWithPath(schema, bundle, "");
800 bundle.Remove("boom", nullptr);
801
802 // Invalid top level property.
803 bundle.SetInteger("Boolean", 12345);
804 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
805 TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
806 TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_INVALID, true);
807 TestSchemaValidationWithPath(schema, bundle, "Boolean");
808 bundle.SetBoolean("Boolean", true);
809
810 // Tests on ObjectOfObject.
811 {
812 Schema subschema = schema.GetProperty("ObjectOfObject");
813 ASSERT_TRUE(subschema.valid());
814 base::DictionaryValue root;
815
816 // Unknown property.
817 root.SetBoolean("Object.three", false);
818 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
819 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
820 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
821 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
822 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
823 TestSchemaValidationWithPath(subschema, root, "Object");
824 root.Remove("Object.three", nullptr);
825
826 // Invalid property.
827 root.SetInteger("Object.one", 12345);
828 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
829 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
830 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
831 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
832 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
833 TestSchemaValidationWithPath(subschema, root, "Object.one");
834 root.Remove("Object.one", nullptr);
835 }
836
837 // Tests on ArrayOfObjects.
838 {
839 Schema subschema = schema.GetProperty("ArrayOfObjects");
840 ASSERT_TRUE(subschema.valid());
841 base::ListValue root;
842
843 // Unknown property.
844 std::unique_ptr<base::DictionaryValue> dict_value(
845 new base::DictionaryValue());
846 dict_value->SetBoolean("three", true);
847 root.Append(std::move(dict_value));
848 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
849 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
850 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
851 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
852 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
853 TestSchemaValidationWithPath(subschema, root, "items[0]");
854 root.Remove(root.GetSize() - 1, nullptr);
855
856 // Invalid property.
857 dict_value.reset(new base::DictionaryValue());
858 dict_value->SetBoolean("two", true);
859 root.Append(std::move(dict_value));
860 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
861 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
862 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
863 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
864 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
865 TestSchemaValidationWithPath(subschema, root, "items[0].two");
866 root.Remove(root.GetSize() - 1, nullptr);
867 }
868
869 // Tests on ObjectOfArray.
870 {
871 Schema subschema = schema.GetProperty("ObjectOfArray");
872 ASSERT_TRUE(subschema.valid());
873 base::DictionaryValue root;
874
875 base::ListValue* list_value =
876 root.SetList("List", std::make_unique<base::ListValue>());
877
878 // Test that there are not errors here.
879 list_value->AppendInteger(12345);
880 TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
881 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, true);
882 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
883 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
884 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
885
886 // Invalid list item.
887 list_value->AppendString("blabla");
888 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
889 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
890 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
891 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
892 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
893 TestSchemaValidationWithPath(subschema, root, "List.items[1]");
894 }
895
896 // Tests on ArrayOfObjectOfArray.
897 {
898 Schema subschema = schema.GetProperty("ArrayOfObjectOfArray");
899 ASSERT_TRUE(subschema.valid());
900 base::ListValue root;
901
902 auto dict_value = std::make_unique<base::DictionaryValue>();
903 base::ListValue* list_value =
904 dict_value->SetList("List", std::make_unique<base::ListValue>());
905 root.Append(std::move(dict_value));
906
907 // Test that there are not errors here.
908 list_value->AppendString("blabla");
909 TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
910 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, true);
911 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
912 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
913 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
914
915 // Invalid list item.
916 list_value->AppendInteger(12345);
917 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
918 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
919 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
920 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
921 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
922 TestSchemaValidationWithPath(subschema, root, "items[0].List.items[1]");
923 }
924
925 // Tests on StringWithPattern.
926 {
927 Schema subschema = schema.GetProperty("StringWithPattern");
928 ASSERT_TRUE(subschema.valid());
929
930 TestSchemaValidation(subschema, base::Value("foobar"), SCHEMA_STRICT,
931 false);
932 TestSchemaValidation(subschema, base::Value("foo"), SCHEMA_STRICT, true);
933 TestSchemaValidation(subschema, base::Value("fo"), SCHEMA_STRICT, false);
934 TestSchemaValidation(subschema, base::Value("fooo"), SCHEMA_STRICT, true);
935 TestSchemaValidation(subschema, base::Value("^foo+$"), SCHEMA_STRICT,
936 false);
937 }
938
939 // Tests on ObjectWithPatternProperties.
940 {
941 Schema subschema = schema.GetProperty("ObjectWithPatternProperties");
942 ASSERT_TRUE(subschema.valid());
943 base::DictionaryValue root;
944
945 ASSERT_EQ(1u, subschema.GetPatternProperties("fooo").size());
946 ASSERT_EQ(1u, subschema.GetPatternProperties("foo").size());
947 ASSERT_EQ(1u, subschema.GetPatternProperties("barr").size());
948 ASSERT_EQ(1u, subschema.GetPatternProperties("bar").size());
949 ASSERT_EQ(1u, subschema.GetMatchingProperties("fooo").size());
950 ASSERT_EQ(1u, subschema.GetMatchingProperties("foo").size());
951 ASSERT_EQ(1u, subschema.GetMatchingProperties("barr").size());
952 ASSERT_EQ(2u, subschema.GetMatchingProperties("bar").size());
953 ASSERT_TRUE(subschema.GetPatternProperties("foobar").empty());
954
955 root.SetInteger("fooo", 123);
956 TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
957 root.SetBoolean("fooo", false);
958 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
959 root.Remove("fooo", nullptr);
960
961 root.SetInteger("foo", 123);
962 TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
963 root.SetBoolean("foo", false);
964 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
965 root.Remove("foo", nullptr);
966
967 root.SetString("barr", "one");
968 TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
969 root.SetString("barr", "three");
970 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
971 root.SetBoolean("barr", false);
972 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
973 root.Remove("barr", nullptr);
974
975 root.SetString("bar", "one");
976 TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
977 root.SetString("bar", "two");
978 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
979 root.SetString("bar", "three");
980 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
981 root.Remove("bar", nullptr);
982
983 root.SetInteger("foobar", 123);
984 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
985 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
986 root.Remove("foobar", nullptr);
987 }
988
989 // Tests on ObjectWithRequiredProperties
990 {
991 Schema subschema = schema.GetProperty("ObjectWithRequiredProperties");
992 ASSERT_TRUE(subschema.valid());
993 base::DictionaryValue root;
994
995 // Required property missing.
996 root.SetInteger("Integer", 1);
997 root.SetDouble("Number", 3.14);
998 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
999 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
1000 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
1001 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, false);
1002 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, false);
1003
1004 // Invalid required property.
1005 root.SetInteger("String", 123);
1006 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
1007 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
1008 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
1009 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, false);
1010 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, false);
1011 root.SetString("String", "a string");
1012
1013 // Invalid subschema of required property with multiple subschemas.
1014 //
1015 // The "Integer" property has two subschemas, one in "properties" and one
1016 // in "patternProperties". The first test generates a valid schema for the
1017 // first subschema and the second test generates a valid schema for the
1018 // second subschema. In both cases validation should fail because one of the
1019 // required properties is invalid.
1020 root.SetInteger("Integer", 2);
1021 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
1022 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
1023 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
1024 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, false);
1025 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, false);
1026
1027 root.SetInteger("Integer", 3);
1028 TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
1029 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
1030 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
1031 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, false);
1032 TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, false);
1033 }
1034
1035 // Test that integer to double promotion is allowed.
1036 bundle.SetInteger("Number", 31415);
1037 TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
1038 }
1039
TEST(SchemaTest,InvalidReferences)1040 TEST(SchemaTest, InvalidReferences) {
1041 // References to undeclared schemas fail.
1042 EXPECT_TRUE(ParseFails(R"({
1043 "type": "object",
1044 "properties": {
1045 "name": { "$ref": "undeclared" }
1046 }
1047 })"));
1048
1049 // Can't refer to self.
1050 EXPECT_TRUE(ParseFails(R"({
1051 "type": "object",
1052 "properties": {
1053 "name": {
1054 "id": "self",
1055 "$ref": "self"
1056 }
1057 }
1058 })"));
1059
1060 // Duplicated IDs are invalid.
1061 EXPECT_TRUE(ParseFails(R"({
1062 "type": "object",
1063 "properties": {
1064 "name": {
1065 "id": "x",
1066 "type": "string"
1067 },
1068 "another": {
1069 "id": "x",
1070 "type": "string"
1071 }
1072 }
1073 })"));
1074
1075 // Main object can't be a reference.
1076 EXPECT_TRUE(ParseFails(R"({
1077 "type": "object",
1078 "id": "main",
1079 "$ref": "main"
1080 })"));
1081
1082 EXPECT_TRUE(ParseFails(R"({
1083 "type": "object",
1084 "$ref": "main"
1085 })"));
1086 }
1087
TEST(SchemaTest,RecursiveReferences)1088 TEST(SchemaTest, RecursiveReferences) {
1089 // Verifies that references can go to a parent schema, to define a
1090 // recursive type.
1091 std::string error;
1092 Schema schema = Schema::Parse(R"({
1093 "type": "object",
1094 "properties": {
1095 "bookmarks": {
1096 "type": "array",
1097 "id": "ListOfBookmarks",
1098 "items": {
1099 "type": "object",
1100 "properties": {
1101 "name": { "type": "string" },
1102 "url": { "type": "string" },
1103 "children": { "$ref": "ListOfBookmarks" }
1104 }
1105 }
1106 }
1107 }
1108 })",
1109 &error);
1110 ASSERT_TRUE(schema.valid()) << error;
1111 ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
1112
1113 Schema parent = schema.GetKnownProperty("bookmarks");
1114 ASSERT_TRUE(parent.valid());
1115 ASSERT_EQ(base::Value::Type::LIST, parent.type());
1116
1117 // Check the recursive type a number of times.
1118 for (int i = 0; i < 10; ++i) {
1119 Schema items = parent.GetItems();
1120 ASSERT_TRUE(items.valid());
1121 ASSERT_EQ(base::Value::Type::DICTIONARY, items.type());
1122
1123 Schema prop = items.GetKnownProperty("name");
1124 ASSERT_TRUE(prop.valid());
1125 ASSERT_EQ(base::Value::Type::STRING, prop.type());
1126
1127 prop = items.GetKnownProperty("url");
1128 ASSERT_TRUE(prop.valid());
1129 ASSERT_EQ(base::Value::Type::STRING, prop.type());
1130
1131 prop = items.GetKnownProperty("children");
1132 ASSERT_TRUE(prop.valid());
1133 ASSERT_EQ(base::Value::Type::LIST, prop.type());
1134
1135 parent = prop;
1136 }
1137 }
1138
TEST(SchemaTest,UnorderedReferences)1139 TEST(SchemaTest, UnorderedReferences) {
1140 // Verifies that references and IDs can come in any order.
1141 std::string error;
1142 Schema schema = Schema::Parse(R"({
1143 "type": "object",
1144 "properties": {
1145 "a": { "$ref": "shared" },
1146 "b": { "$ref": "shared" },
1147 "c": { "$ref": "shared" },
1148 "d": { "$ref": "shared" },
1149 "e": {
1150 "type": "boolean",
1151 "id": "shared"
1152 },
1153 "f": { "$ref": "shared" },
1154 "g": { "$ref": "shared" },
1155 "h": { "$ref": "shared" },
1156 "i": { "$ref": "shared" }
1157 }
1158 })",
1159 &error);
1160 ASSERT_TRUE(schema.valid()) << error;
1161 ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
1162
1163 for (char c = 'a'; c <= 'i'; ++c) {
1164 Schema sub = schema.GetKnownProperty(std::string(1, c));
1165 ASSERT_TRUE(sub.valid()) << c;
1166 ASSERT_EQ(base::Value::Type::BOOLEAN, sub.type()) << c;
1167 }
1168 }
1169
TEST(SchemaTest,AdditionalPropertiesReference)1170 TEST(SchemaTest, AdditionalPropertiesReference) {
1171 // Verifies that "additionalProperties" can be a reference.
1172 std::string error;
1173 Schema schema = Schema::Parse(R"({
1174 "type": "object",
1175 "properties": {
1176 "policy": {
1177 "type": "object",
1178 "properties": {
1179 "foo": {
1180 "type": "boolean",
1181 "id": "FooId"
1182 }
1183 },
1184 "additionalProperties": { "$ref": "FooId" }
1185 }
1186 }
1187 })",
1188 &error);
1189 ASSERT_TRUE(schema.valid()) << error;
1190 ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
1191
1192 Schema policy = schema.GetKnownProperty("policy");
1193 ASSERT_TRUE(policy.valid());
1194 ASSERT_EQ(base::Value::Type::DICTIONARY, policy.type());
1195
1196 Schema foo = policy.GetKnownProperty("foo");
1197 ASSERT_TRUE(foo.valid());
1198 EXPECT_EQ(base::Value::Type::BOOLEAN, foo.type());
1199
1200 Schema additional = policy.GetAdditionalProperties();
1201 ASSERT_TRUE(additional.valid());
1202 EXPECT_EQ(base::Value::Type::BOOLEAN, additional.type());
1203
1204 Schema x = policy.GetProperty("x");
1205 ASSERT_TRUE(x.valid());
1206 EXPECT_EQ(base::Value::Type::BOOLEAN, x.type());
1207 }
1208
TEST(SchemaTest,ItemsReference)1209 TEST(SchemaTest, ItemsReference) {
1210 // Verifies that "items" can be a reference.
1211 std::string error;
1212 Schema schema = Schema::Parse(R"({
1213 "type": "object",
1214 "properties": {
1215 "foo": {
1216 "type": "boolean",
1217 "id": "FooId"
1218 },
1219 "list": {
1220 "type": "array",
1221 "items": { "$ref": "FooId" }
1222 }
1223 }
1224 })",
1225 &error);
1226 ASSERT_TRUE(schema.valid()) << error;
1227 ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
1228
1229 Schema foo = schema.GetKnownProperty("foo");
1230 ASSERT_TRUE(foo.valid());
1231 EXPECT_EQ(base::Value::Type::BOOLEAN, foo.type());
1232
1233 Schema list = schema.GetKnownProperty("list");
1234 ASSERT_TRUE(list.valid());
1235 ASSERT_EQ(base::Value::Type::LIST, list.type());
1236
1237 Schema items = list.GetItems();
1238 ASSERT_TRUE(items.valid());
1239 ASSERT_EQ(base::Value::Type::BOOLEAN, items.type());
1240 }
1241
TEST(SchemaTest,EnumerationRestriction)1242 TEST(SchemaTest, EnumerationRestriction) {
1243 // Enum attribute is a list.
1244 EXPECT_TRUE(ParseFails(SchemaObjectWrapper(R"({
1245 "type": "string",
1246 "enum": 12
1247 })")));
1248
1249 // Empty enum attributes is not allowed.
1250 EXPECT_TRUE(ParseFails(SchemaObjectWrapper(R"({
1251 "type": "integer",
1252 "enum": []
1253 })")));
1254
1255 // Enum elements type should be same as stated.
1256 EXPECT_TRUE(ParseFails(SchemaObjectWrapper(R"({
1257 "type": "string",
1258 "enum": [1, 2, 3]
1259 })")));
1260
1261 EXPECT_FALSE(ParseFails(SchemaObjectWrapper(R"({
1262 "type": "integer",
1263 "enum": [1, 2, 3]
1264 })")));
1265
1266 EXPECT_FALSE(ParseFails(SchemaObjectWrapper(R"({
1267 "type": "string",
1268 "enum": ["1", "2", "3"]
1269 })")));
1270 }
1271
TEST(SchemaTest,RangedRestriction)1272 TEST(SchemaTest, RangedRestriction) {
1273 EXPECT_TRUE(ParseFails(SchemaObjectWrapper(R"({
1274 "type": "integer",
1275 "minimum": 10,
1276 "maximum": 5
1277 })")));
1278
1279 EXPECT_FALSE(ParseFails(SchemaObjectWrapper(R"({
1280 "type": "integer",
1281 "minimum": 10,
1282 "maximum": 20
1283 })")));
1284 }
1285
1286 } // namespace policy
1287