]> git.proxmox.com Git - ceph.git/blob - ceph/src/rapidjson/test/unittest/schematest.cpp
update sources to v12.1.0
[ceph.git] / ceph / src / rapidjson / test / unittest / schematest.cpp
1 // Tencent is pleased to support the open source community by making RapidJSON available.
2 //
3 // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
4 //
5 // Licensed under the MIT License (the "License"); you may not use this file except
6 // in compliance with the License. You may obtain a copy of the License at
7 //
8 // http://opensource.org/licenses/MIT
9 //
10 // Unless required by applicable law or agreed to in writing, software distributed
11 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12 // CONDITIONS OF ANY KIND, either express or implied. See the License for the
13 // specific language governing permissions and limitations under the License.
14
15 #include "unittest.h"
16 #include "rapidjson/schema.h"
17 #include "rapidjson/stringbuffer.h"
18 #include "rapidjson/writer.h"
19
20 #ifdef __clang__
21 RAPIDJSON_DIAG_PUSH
22 RAPIDJSON_DIAG_OFF(variadic-macros)
23 #endif
24
25 using namespace rapidjson;
26
27 #define TEST_HASHER(json1, json2, expected) \
28 {\
29 Document d1, d2;\
30 d1.Parse(json1);\
31 ASSERT_FALSE(d1.HasParseError());\
32 d2.Parse(json2);\
33 ASSERT_FALSE(d2.HasParseError());\
34 internal::Hasher<Value, CrtAllocator> h1, h2;\
35 d1.Accept(h1);\
36 d2.Accept(h2);\
37 ASSERT_TRUE(h1.IsValid());\
38 ASSERT_TRUE(h2.IsValid());\
39 /*printf("%s: 0x%016llx\n%s: 0x%016llx\n\n", json1, h1.GetHashCode(), json2, h2.GetHashCode());*/\
40 EXPECT_TRUE(expected == (h1.GetHashCode() == h2.GetHashCode()));\
41 }
42
43 TEST(SchemaValidator, Hasher) {
44 TEST_HASHER("null", "null", true);
45
46 TEST_HASHER("true", "true", true);
47 TEST_HASHER("false", "false", true);
48 TEST_HASHER("true", "false", false);
49 TEST_HASHER("false", "true", false);
50 TEST_HASHER("true", "null", false);
51 TEST_HASHER("false", "null", false);
52
53 TEST_HASHER("1", "1", true);
54 TEST_HASHER("2147483648", "2147483648", true); // 2^31 can only be fit in unsigned
55 TEST_HASHER("-2147483649", "-2147483649", true); // -2^31 - 1 can only be fit in int64_t
56 TEST_HASHER("2147483648", "2147483648", true); // 2^31 can only be fit in unsigned
57 TEST_HASHER("4294967296", "4294967296", true); // 2^32 can only be fit in int64_t
58 TEST_HASHER("9223372036854775808", "9223372036854775808", true); // 2^63 can only be fit in uint64_t
59 TEST_HASHER("1.5", "1.5", true);
60 TEST_HASHER("1", "1.0", true);
61 TEST_HASHER("1", "-1", false);
62 TEST_HASHER("0.0", "-0.0", false);
63 TEST_HASHER("1", "true", false);
64 TEST_HASHER("0", "false", false);
65 TEST_HASHER("0", "null", false);
66
67 TEST_HASHER("\"\"", "\"\"", true);
68 TEST_HASHER("\"\"", "\"\\u0000\"", false);
69 TEST_HASHER("\"Hello\"", "\"Hello\"", true);
70 TEST_HASHER("\"Hello\"", "\"World\"", false);
71 TEST_HASHER("\"Hello\"", "null", false);
72 TEST_HASHER("\"Hello\\u0000\"", "\"Hello\"", false);
73 TEST_HASHER("\"\"", "null", false);
74 TEST_HASHER("\"\"", "true", false);
75 TEST_HASHER("\"\"", "false", false);
76
77 TEST_HASHER("[]", "[ ]", true);
78 TEST_HASHER("[1, true, false]", "[1, true, false]", true);
79 TEST_HASHER("[1, true, false]", "[1, true]", false);
80 TEST_HASHER("[1, 2]", "[2, 1]", false);
81 TEST_HASHER("[[1], 2]", "[[1, 2]]", false);
82 TEST_HASHER("[1, 2]", "[1, [2]]", false);
83 TEST_HASHER("[]", "null", false);
84 TEST_HASHER("[]", "true", false);
85 TEST_HASHER("[]", "false", false);
86 TEST_HASHER("[]", "0", false);
87 TEST_HASHER("[]", "0.0", false);
88 TEST_HASHER("[]", "\"\"", false);
89
90 TEST_HASHER("{}", "{ }", true);
91 TEST_HASHER("{\"a\":1}", "{\"a\":1}", true);
92 TEST_HASHER("{\"a\":1}", "{\"b\":1}", false);
93 TEST_HASHER("{\"a\":1}", "{\"a\":2}", false);
94 TEST_HASHER("{\"a\":1, \"b\":2}", "{\"b\":2, \"a\":1}", true); // Member order insensitive
95 TEST_HASHER("{}", "null", false);
96 TEST_HASHER("{}", "false", false);
97 TEST_HASHER("{}", "true", false);
98 TEST_HASHER("{}", "0", false);
99 TEST_HASHER("{}", "0.0", false);
100 TEST_HASHER("{}", "\"\"", false);
101 }
102
103 // Test cases following http://spacetelescope.github.io/understanding-json-schema
104
105 #define VALIDATE(schema, json, expected) \
106 {\
107 SchemaValidator validator(schema);\
108 Document d;\
109 /*printf("\n%s\n", json);*/\
110 d.Parse(json);\
111 EXPECT_FALSE(d.HasParseError());\
112 EXPECT_TRUE(expected == d.Accept(validator));\
113 EXPECT_TRUE(expected == validator.IsValid());\
114 if ((expected) && !validator.IsValid()) {\
115 StringBuffer sb;\
116 validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);\
117 printf("Invalid schema: %s\n", sb.GetString());\
118 printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());\
119 sb.Clear();\
120 validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);\
121 printf("Invalid document: %s\n", sb.GetString());\
122 }\
123 }
124
125 #define INVALIDATE(schema, json, invalidSchemaPointer, invalidSchemaKeyword, invalidDocumentPointer) \
126 {\
127 SchemaValidator validator(schema);\
128 Document d;\
129 /*printf("\n%s\n", json);*/\
130 d.Parse(json);\
131 EXPECT_FALSE(d.HasParseError());\
132 EXPECT_FALSE(d.Accept(validator));\
133 EXPECT_FALSE(validator.IsValid());\
134 if (validator.GetInvalidSchemaPointer() != Pointer(invalidSchemaPointer)) {\
135 StringBuffer sb;\
136 validator.GetInvalidSchemaPointer().Stringify(sb);\
137 printf("GetInvalidSchemaPointer() Expected: %s Actual: %s\n", invalidSchemaPointer, sb.GetString());\
138 ADD_FAILURE();\
139 }\
140 ASSERT_TRUE(validator.GetInvalidSchemaKeyword() != 0);\
141 if (strcmp(validator.GetInvalidSchemaKeyword(), invalidSchemaKeyword) != 0) {\
142 printf("GetInvalidSchemaKeyword() Expected: %s Actual %s\n", invalidSchemaKeyword, validator.GetInvalidSchemaKeyword());\
143 ADD_FAILURE();\
144 }\
145 if (validator.GetInvalidDocumentPointer() != Pointer(invalidDocumentPointer)) {\
146 StringBuffer sb;\
147 validator.GetInvalidDocumentPointer().Stringify(sb);\
148 printf("GetInvalidDocumentPointer() Expected: %s Actual: %s\n", invalidDocumentPointer, sb.GetString());\
149 ADD_FAILURE();\
150 }\
151 }
152
153 TEST(SchemaValidator, Typeless) {
154 Document sd;
155 sd.Parse("{}");
156 SchemaDocument s(sd);
157
158 VALIDATE(s, "42", true);
159 VALIDATE(s, "\"I'm a string\"", true);
160 VALIDATE(s, "{ \"an\": [ \"arbitrarily\", \"nested\" ], \"data\": \"structure\" }", true);
161 }
162
163 TEST(SchemaValidator, MultiType) {
164 Document sd;
165 sd.Parse("{ \"type\": [\"number\", \"string\"] }");
166 SchemaDocument s(sd);
167
168 VALIDATE(s, "42", true);
169 VALIDATE(s, "\"Life, the universe, and everything\"", true);
170 INVALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "type", "");
171 }
172
173 TEST(SchemaValidator, Enum_Typed) {
174 Document sd;
175 sd.Parse("{ \"type\": \"string\", \"enum\" : [\"red\", \"amber\", \"green\"] }");
176 SchemaDocument s(sd);
177
178 VALIDATE(s, "\"red\"", true);
179 INVALIDATE(s, "\"blue\"", "", "enum", "");
180 }
181
182 TEST(SchemaValidator, Enum_Typless) {
183 Document sd;
184 sd.Parse("{ \"enum\": [\"red\", \"amber\", \"green\", null, 42] }");
185 SchemaDocument s(sd);
186
187 VALIDATE(s, "\"red\"", true);
188 VALIDATE(s, "null", true);
189 VALIDATE(s, "42", true);
190 INVALIDATE(s, "0", "", "enum", "");
191 }
192
193 TEST(SchemaValidator, Enum_InvalidType) {
194 Document sd;
195 sd.Parse("{ \"type\": \"string\", \"enum\": [\"red\", \"amber\", \"green\", null] }");
196 SchemaDocument s(sd);
197
198 VALIDATE(s, "\"red\"", true);
199 INVALIDATE(s, "null", "", "type", "");
200 }
201
202 TEST(SchemaValidator, AllOf) {
203 {
204 Document sd;
205 sd.Parse("{\"allOf\": [{ \"type\": \"string\" }, { \"type\": \"string\", \"maxLength\": 5 }]}");
206 SchemaDocument s(sd);
207
208 VALIDATE(s, "\"ok\"", true);
209 INVALIDATE(s, "\"too long\"", "", "allOf", "");
210 }
211 {
212 Document sd;
213 sd.Parse("{\"allOf\": [{ \"type\": \"string\" }, { \"type\": \"number\" } ] }");
214 SchemaDocument s(sd);
215
216 VALIDATE(s, "\"No way\"", false);
217 INVALIDATE(s, "-1", "", "allOf", "");
218 }
219 }
220
221 TEST(SchemaValidator, AnyOf) {
222 Document sd;
223 sd.Parse("{\"anyOf\": [{ \"type\": \"string\" }, { \"type\": \"number\" } ] }");
224 SchemaDocument s(sd);
225
226 VALIDATE(s, "\"Yes\"", true);
227 VALIDATE(s, "42", true);
228 INVALIDATE(s, "{ \"Not a\": \"string or number\" }", "", "anyOf", "");
229 }
230
231 TEST(SchemaValidator, OneOf) {
232 Document sd;
233 sd.Parse("{\"oneOf\": [{ \"type\": \"number\", \"multipleOf\": 5 }, { \"type\": \"number\", \"multipleOf\": 3 } ] }");
234 SchemaDocument s(sd);
235
236 VALIDATE(s, "10", true);
237 VALIDATE(s, "9", true);
238 INVALIDATE(s, "2", "", "oneOf", "");
239 INVALIDATE(s, "15", "", "oneOf", "");
240 }
241
242 TEST(SchemaValidator, Not) {
243 Document sd;
244 sd.Parse("{\"not\":{ \"type\": \"string\"}}");
245 SchemaDocument s(sd);
246
247 VALIDATE(s, "42", true);
248 VALIDATE(s, "{ \"key\": \"value\" }", true);
249 INVALIDATE(s, "\"I am a string\"", "", "not", "");
250 }
251
252 TEST(SchemaValidator, Ref) {
253 Document sd;
254 sd.Parse(
255 "{"
256 " \"$schema\": \"http://json-schema.org/draft-04/schema#\","
257 ""
258 " \"definitions\": {"
259 " \"address\": {"
260 " \"type\": \"object\","
261 " \"properties\": {"
262 " \"street_address\": { \"type\": \"string\" },"
263 " \"city\": { \"type\": \"string\" },"
264 " \"state\": { \"type\": \"string\" }"
265 " },"
266 " \"required\": [\"street_address\", \"city\", \"state\"]"
267 " }"
268 " },"
269 " \"type\": \"object\","
270 " \"properties\": {"
271 " \"billing_address\": { \"$ref\": \"#/definitions/address\" },"
272 " \"shipping_address\": { \"$ref\": \"#/definitions/address\" }"
273 " }"
274 "}");
275 SchemaDocument s(sd);
276
277 VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"}, \"billing_address\": {\"street_address\": \"1st Street SE\", \"city\": \"Washington\", \"state\": \"DC\"} }", true);
278 }
279
280 TEST(SchemaValidator, Ref_AllOf) {
281 Document sd;
282 sd.Parse(
283 "{"
284 " \"$schema\": \"http://json-schema.org/draft-04/schema#\","
285 ""
286 " \"definitions\": {"
287 " \"address\": {"
288 " \"type\": \"object\","
289 " \"properties\": {"
290 " \"street_address\": { \"type\": \"string\" },"
291 " \"city\": { \"type\": \"string\" },"
292 " \"state\": { \"type\": \"string\" }"
293 " },"
294 " \"required\": [\"street_address\", \"city\", \"state\"]"
295 " }"
296 " },"
297 " \"type\": \"object\","
298 " \"properties\": {"
299 " \"billing_address\": { \"$ref\": \"#/definitions/address\" },"
300 " \"shipping_address\": {"
301 " \"allOf\": ["
302 " { \"$ref\": \"#/definitions/address\" },"
303 " { \"properties\":"
304 " { \"type\": { \"enum\": [ \"residential\", \"business\" ] } },"
305 " \"required\": [\"type\"]"
306 " }"
307 " ]"
308 " }"
309 " }"
310 "}");
311 SchemaDocument s(sd);
312
313 INVALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"} }", "/properties/shipping_address", "allOf", "/shipping_address");
314 VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\", \"type\": \"business\"} }", true);
315 }
316
317 TEST(SchemaValidator, String) {
318 Document sd;
319 sd.Parse("{\"type\":\"string\"}");
320 SchemaDocument s(sd);
321
322 VALIDATE(s, "\"I'm a string\"", true);
323 INVALIDATE(s, "42", "", "type", "");
324 INVALIDATE(s, "2147483648", "", "type", ""); // 2^31 can only be fit in unsigned
325 INVALIDATE(s, "-2147483649", "", "type", ""); // -2^31 - 1 can only be fit in int64_t
326 INVALIDATE(s, "4294967296", "", "type", ""); // 2^32 can only be fit in int64_t
327 INVALIDATE(s, "3.1415926", "", "type", "");
328 }
329
330 TEST(SchemaValidator, String_LengthRange) {
331 Document sd;
332 sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}");
333 SchemaDocument s(sd);
334
335 INVALIDATE(s, "\"A\"", "", "minLength", "");
336 VALIDATE(s, "\"AB\"", true);
337 VALIDATE(s, "\"ABC\"", true);
338 INVALIDATE(s, "\"ABCD\"", "", "maxLength", "");
339 }
340
341 #if RAPIDJSON_SCHEMA_HAS_REGEX
342 TEST(SchemaValidator, String_Pattern) {
343 Document sd;
344 sd.Parse("{\"type\":\"string\",\"pattern\":\"^(\\\\([0-9]{3}\\\\))?[0-9]{3}-[0-9]{4}$\"}");
345 SchemaDocument s(sd);
346
347 VALIDATE(s, "\"555-1212\"", true);
348 VALIDATE(s, "\"(888)555-1212\"", true);
349 INVALIDATE(s, "\"(888)555-1212 ext. 532\"", "", "pattern", "");
350 INVALIDATE(s, "\"(800)FLOWERS\"", "", "pattern", "");
351 }
352
353 TEST(SchemaValidator, String_Pattern_Invalid) {
354 Document sd;
355 sd.Parse("{\"type\":\"string\",\"pattern\":\"a{0}\"}"); // TODO: report regex is invalid somehow
356 SchemaDocument s(sd);
357
358 VALIDATE(s, "\"\"", true);
359 VALIDATE(s, "\"a\"", true);
360 VALIDATE(s, "\"aa\"", true);
361 }
362 #endif
363
364 TEST(SchemaValidator, Integer) {
365 Document sd;
366 sd.Parse("{\"type\":\"integer\"}");
367 SchemaDocument s(sd);
368
369 VALIDATE(s, "42", true);
370 VALIDATE(s, "-1", true);
371 VALIDATE(s, "2147483648", true); // 2^31 can only be fit in unsigned
372 VALIDATE(s, "-2147483649", true); // -2^31 - 1 can only be fit in int64_t
373 VALIDATE(s, "2147483648", true); // 2^31 can only be fit in unsigned
374 VALIDATE(s, "4294967296", true); // 2^32 can only be fit in int64_t
375 INVALIDATE(s, "3.1415926", "", "type", "");
376 INVALIDATE(s, "\"42\"", "", "type", "");
377 }
378
379 TEST(SchemaValidator, Integer_Range) {
380 Document sd;
381 sd.Parse("{\"type\":\"integer\",\"minimum\":0,\"maximum\":100,\"exclusiveMaximum\":true}");
382 SchemaDocument s(sd);
383
384 INVALIDATE(s, "-1", "", "minimum", "");
385 VALIDATE(s, "0", true);
386 VALIDATE(s, "10", true);
387 VALIDATE(s, "99", true);
388 INVALIDATE(s, "100", "", "maximum", "");
389 INVALIDATE(s, "101", "", "maximum", "");
390 }
391
392 TEST(SchemaValidator, Integer_Range64Boundary) {
393 Document sd;
394 sd.Parse("{\"type\":\"integer\",\"minimum\":-9223372036854775807,\"maximum\":9223372036854775806}");
395 SchemaDocument s(sd);
396
397 INVALIDATE(s, "-9223372036854775808", "", "minimum", "");
398 VALIDATE(s, "-9223372036854775807", true);
399 VALIDATE(s, "-2147483648", true); // int min
400 VALIDATE(s, "0", true);
401 VALIDATE(s, "2147483647", true); // int max
402 VALIDATE(s, "2147483648", true); // unsigned first
403 VALIDATE(s, "4294967295", true); // unsigned max
404 VALIDATE(s, "9223372036854775806", true);
405 INVALIDATE(s, "9223372036854775807", "", "maximum", "");
406 INVALIDATE(s, "18446744073709551615", "", "maximum", ""); // uint64_t max
407 }
408
409 TEST(SchemaValidator, Integer_RangeU64Boundary) {
410 Document sd;
411 sd.Parse("{\"type\":\"integer\",\"minimum\":9223372036854775808,\"maximum\":18446744073709551614}");
412 SchemaDocument s(sd);
413
414 INVALIDATE(s, "-9223372036854775808", "", "minimum", "");
415 INVALIDATE(s, "9223372036854775807", "", "minimum", "");
416 INVALIDATE(s, "-2147483648", "", "minimum", ""); // int min
417 INVALIDATE(s, "0", "", "minimum", "");
418 INVALIDATE(s, "2147483647", "", "minimum", ""); // int max
419 INVALIDATE(s, "2147483648", "", "minimum", ""); // unsigned first
420 INVALIDATE(s, "4294967295", "", "minimum", ""); // unsigned max
421 VALIDATE(s, "9223372036854775808", true);
422 VALIDATE(s, "18446744073709551614", true);
423 INVALIDATE(s, "18446744073709551615", "", "maximum", "");
424 }
425
426 TEST(SchemaValidator, Integer_Range64BoundaryExclusive) {
427 Document sd;
428 sd.Parse("{\"type\":\"integer\",\"minimum\":-9223372036854775808,\"maximum\":18446744073709551615,\"exclusiveMinimum\":true,\"exclusiveMaximum\":true}");
429 SchemaDocument s(sd);
430
431 INVALIDATE(s, "-9223372036854775808", "", "minimum", "");
432 VALIDATE(s, "-9223372036854775807", true);
433 VALIDATE(s, "18446744073709551614", true);
434 INVALIDATE(s, "18446744073709551615", "", "maximum", "");
435 }
436
437 TEST(SchemaValidator, Integer_MultipleOf) {
438 Document sd;
439 sd.Parse("{\"type\":\"integer\",\"multipleOf\":10}");
440 SchemaDocument s(sd);
441
442 VALIDATE(s, "0", true);
443 VALIDATE(s, "10", true);
444 VALIDATE(s, "-10", true);
445 VALIDATE(s, "20", true);
446 INVALIDATE(s, "23", "", "multipleOf", "");
447 INVALIDATE(s, "-23", "", "multipleOf", "");
448 }
449
450 TEST(SchemaValidator, Integer_MultipleOf64Boundary) {
451 Document sd;
452 sd.Parse("{\"type\":\"integer\",\"multipleOf\":18446744073709551615}");
453 SchemaDocument s(sd);
454
455 VALIDATE(s, "0", true);
456 VALIDATE(s, "18446744073709551615", true);
457 INVALIDATE(s, "18446744073709551614", "", "multipleOf", "");
458 }
459
460 TEST(SchemaValidator, Number_Range) {
461 Document sd;
462 sd.Parse("{\"type\":\"number\",\"minimum\":0,\"maximum\":100,\"exclusiveMaximum\":true}");
463 SchemaDocument s(sd);
464
465 INVALIDATE(s, "-1", "", "minimum", "");
466 VALIDATE(s, "0", true);
467 VALIDATE(s, "0.1", true);
468 VALIDATE(s, "10", true);
469 VALIDATE(s, "99", true);
470 VALIDATE(s, "99.9", true);
471 INVALIDATE(s, "100", "", "maximum", "");
472 INVALIDATE(s, "100.0", "", "maximum", "");
473 INVALIDATE(s, "101.5", "", "maximum", "");
474 }
475
476 TEST(SchemaValidator, Number_RangeInt) {
477 Document sd;
478 sd.Parse("{\"type\":\"number\",\"minimum\":-100,\"maximum\":-1,\"exclusiveMaximum\":true}");
479 SchemaDocument s(sd);
480
481 INVALIDATE(s, "-101", "", "minimum", "");
482 INVALIDATE(s, "-100.1", "", "minimum", "");
483 VALIDATE(s, "-100", true);
484 VALIDATE(s, "-2", true);
485 INVALIDATE(s, "-1", "", "maximum", "");
486 INVALIDATE(s, "-0.9", "", "maximum", "");
487 INVALIDATE(s, "0", "", "maximum", "");
488 INVALIDATE(s, "2147483647", "", "maximum", ""); // int max
489 INVALIDATE(s, "2147483648", "", "maximum", ""); // unsigned first
490 INVALIDATE(s, "4294967295", "", "maximum", ""); // unsigned max
491 INVALIDATE(s, "9223372036854775808", "", "maximum", "");
492 INVALIDATE(s, "18446744073709551614", "", "maximum", "");
493 INVALIDATE(s, "18446744073709551615", "", "maximum", "");
494 }
495
496 TEST(SchemaValidator, Number_RangeDouble) {
497 Document sd;
498 sd.Parse("{\"type\":\"number\",\"minimum\":0.1,\"maximum\":100.1,\"exclusiveMaximum\":true}");
499 SchemaDocument s(sd);
500
501 INVALIDATE(s, "-9223372036854775808", "", "minimum", "");
502 INVALIDATE(s, "-2147483648", "", "minimum", ""); // int min
503 INVALIDATE(s, "-1", "", "minimum", "");
504 VALIDATE(s, "0.1", true);
505 VALIDATE(s, "10", true);
506 VALIDATE(s, "99", true);
507 VALIDATE(s, "100", true);
508 INVALIDATE(s, "101", "", "maximum", "");
509 INVALIDATE(s, "101.5", "", "maximum", "");
510 INVALIDATE(s, "18446744073709551614", "", "maximum", "");
511 INVALIDATE(s, "18446744073709551615", "", "maximum", "");
512 INVALIDATE(s, "2147483647", "", "maximum", ""); // int max
513 INVALIDATE(s, "2147483648", "", "maximum", ""); // unsigned first
514 INVALIDATE(s, "4294967295", "", "maximum", ""); // unsigned max
515 INVALIDATE(s, "9223372036854775808", "", "maximum", "");
516 INVALIDATE(s, "18446744073709551614", "", "maximum", "");
517 INVALIDATE(s, "18446744073709551615", "", "maximum", "");
518 }
519
520 TEST(SchemaValidator, Number_RangeDoubleU64Boundary) {
521 Document sd;
522 sd.Parse("{\"type\":\"number\",\"minimum\":9223372036854775808.0,\"maximum\":18446744073709550000.0}");
523 SchemaDocument s(sd);
524
525 INVALIDATE(s, "-9223372036854775808", "", "minimum", "");
526 INVALIDATE(s, "-2147483648", "", "minimum", ""); // int min
527 INVALIDATE(s, "0", "", "minimum", "");
528 INVALIDATE(s, "2147483647", "", "minimum", ""); // int max
529 INVALIDATE(s, "2147483648", "", "minimum", ""); // unsigned first
530 INVALIDATE(s, "4294967295", "", "minimum", ""); // unsigned max
531 VALIDATE(s, "9223372036854775808", true);
532 VALIDATE(s, "18446744073709540000", true);
533 INVALIDATE(s, "18446744073709551615", "", "maximum", "");
534 }
535
536 TEST(SchemaValidator, Number_MultipleOf) {
537 Document sd;
538 sd.Parse("{\"type\":\"number\",\"multipleOf\":10.0}");
539 SchemaDocument s(sd);
540
541 VALIDATE(s, "0", true);
542 VALIDATE(s, "10", true);
543 VALIDATE(s, "-10", true);
544 VALIDATE(s, "20", true);
545 INVALIDATE(s, "23", "", "multipleOf", "");
546 INVALIDATE(s, "-2147483648", "", "multipleOf", ""); // int min
547 VALIDATE(s, "-2147483640", true);
548 INVALIDATE(s, "2147483647", "", "multipleOf", ""); // int max
549 INVALIDATE(s, "2147483648", "", "multipleOf", ""); // unsigned first
550 VALIDATE(s, "2147483650", true);
551 INVALIDATE(s, "4294967295", "", "multipleOf", ""); // unsigned max
552 VALIDATE(s, "4294967300", true);
553 }
554
555 TEST(SchemaValidator, Number_MultipleOfOne) {
556 Document sd;
557 sd.Parse("{\"type\":\"number\",\"multipleOf\":1}");
558 SchemaDocument s(sd);
559
560 VALIDATE(s, "42", true);
561 VALIDATE(s, "42.0", true);
562 INVALIDATE(s, "3.1415926", "", "multipleOf", "");
563 }
564
565 TEST(SchemaValidator, Object) {
566 Document sd;
567 sd.Parse("{\"type\":\"object\"}");
568 SchemaDocument s(sd);
569
570 VALIDATE(s, "{\"key\":\"value\",\"another_key\":\"another_value\"}", true);
571 VALIDATE(s, "{\"Sun\":1.9891e30,\"Jupiter\":1.8986e27,\"Saturn\":5.6846e26,\"Neptune\":10.243e25,\"Uranus\":8.6810e25,\"Earth\":5.9736e24,\"Venus\":4.8685e24,\"Mars\":6.4185e23,\"Mercury\":3.3022e23,\"Moon\":7.349e22,\"Pluto\":1.25e22}", true);
572 INVALIDATE(s, "[\"An\", \"array\", \"not\", \"an\", \"object\"]", "", "type", "");
573 INVALIDATE(s, "\"Not an object\"", "", "type", "");
574 }
575
576 TEST(SchemaValidator, Object_Properties) {
577 Document sd;
578 sd.Parse(
579 "{"
580 " \"type\": \"object\","
581 " \"properties\" : {"
582 " \"number\": { \"type\": \"number\" },"
583 " \"street_name\" : { \"type\": \"string\" },"
584 " \"street_type\" : { \"type\": \"string\", \"enum\" : [\"Street\", \"Avenue\", \"Boulevard\"] }"
585 " }"
586 "}");
587
588 SchemaDocument s(sd);
589
590 VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true);
591 INVALIDATE(s, "{ \"number\": \"1600\", \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", "/properties/number", "type", "/number");
592 VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\" }", true);
593 VALIDATE(s, "{}", true);
594 VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", true);
595 }
596
597 TEST(SchemaValidator, Object_AdditionalPropertiesBoolean) {
598 Document sd;
599 sd.Parse(
600 "{"
601 " \"type\": \"object\","
602 " \"properties\" : {"
603 " \"number\": { \"type\": \"number\" },"
604 " \"street_name\" : { \"type\": \"string\" },"
605 " \"street_type\" : { \"type\": \"string\","
606 " \"enum\" : [\"Street\", \"Avenue\", \"Boulevard\"]"
607 " }"
608 " },"
609 " \"additionalProperties\": false"
610 "}");
611
612 SchemaDocument s(sd);
613
614 VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true);
615 INVALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", "", "additionalProperties", "/direction");
616 }
617
618 TEST(SchemaValidator, Object_AdditionalPropertiesObject) {
619 Document sd;
620 sd.Parse(
621 "{"
622 " \"type\": \"object\","
623 " \"properties\" : {"
624 " \"number\": { \"type\": \"number\" },"
625 " \"street_name\" : { \"type\": \"string\" },"
626 " \"street_type\" : { \"type\": \"string\","
627 " \"enum\" : [\"Street\", \"Avenue\", \"Boulevard\"]"
628 " }"
629 " },"
630 " \"additionalProperties\": { \"type\": \"string\" }"
631 "}");
632 SchemaDocument s(sd);
633
634 VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true);
635 VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", true);
636 INVALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"office_number\": 201 }", "/additionalProperties", "type", "/office_number");
637 }
638
639 TEST(SchemaValidator, Object_Required) {
640 Document sd;
641 sd.Parse(
642 "{"
643 " \"type\": \"object\","
644 " \"properties\" : {"
645 " \"name\": { \"type\": \"string\" },"
646 " \"email\" : { \"type\": \"string\" },"
647 " \"address\" : { \"type\": \"string\" },"
648 " \"telephone\" : { \"type\": \"string\" }"
649 " },"
650 " \"required\":[\"name\", \"email\"]"
651 "}");
652 SchemaDocument s(sd);
653
654 VALIDATE(s, "{ \"name\": \"William Shakespeare\", \"email\" : \"bill@stratford-upon-avon.co.uk\" }", true);
655 VALIDATE(s, "{ \"name\": \"William Shakespeare\", \"email\" : \"bill@stratford-upon-avon.co.uk\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\", \"authorship\" : \"in question\"}", true);
656 INVALIDATE(s, "{ \"name\": \"William Shakespeare\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\" }", "", "required", "");
657 }
658
659
660 TEST(SchemaValidator, Object_PropertiesRange) {
661 Document sd;
662 sd.Parse("{\"type\":\"object\", \"minProperties\":2, \"maxProperties\":3}");
663 SchemaDocument s(sd);
664
665 INVALIDATE(s, "{}", "", "minProperties", "");
666 INVALIDATE(s, "{\"a\":0}", "", "minProperties", "");
667 VALIDATE(s, "{\"a\":0,\"b\":1}", true);
668 VALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2}", true);
669 INVALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2,\"d\":3}", "", "maxProperties", "");
670 }
671
672 TEST(SchemaValidator, Object_PropertyDependencies) {
673 Document sd;
674 sd.Parse(
675 "{"
676 " \"type\": \"object\","
677 " \"properties\": {"
678 " \"name\": { \"type\": \"string\" },"
679 " \"credit_card\": { \"type\": \"number\" },"
680 " \"billing_address\": { \"type\": \"string\" }"
681 " },"
682 " \"required\": [\"name\"],"
683 " \"dependencies\": {"
684 " \"credit_card\": [\"billing_address\"]"
685 " }"
686 "}");
687 SchemaDocument s(sd);
688
689 VALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555, \"billing_address\": \"555 Debtor's Lane\" }", true);
690 INVALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555 }", "", "dependencies", "");
691 VALIDATE(s, "{ \"name\": \"John Doe\"}", true);
692 VALIDATE(s, "{ \"name\": \"John Doe\", \"billing_address\": \"555 Debtor's Lane\" }", true);
693 }
694
695 TEST(SchemaValidator, Object_SchemaDependencies) {
696 Document sd;
697 sd.Parse(
698 "{"
699 " \"type\": \"object\","
700 " \"properties\" : {"
701 " \"name\": { \"type\": \"string\" },"
702 " \"credit_card\" : { \"type\": \"number\" }"
703 " },"
704 " \"required\" : [\"name\"],"
705 " \"dependencies\" : {"
706 " \"credit_card\": {"
707 " \"properties\": {"
708 " \"billing_address\": { \"type\": \"string\" }"
709 " },"
710 " \"required\" : [\"billing_address\"]"
711 " }"
712 " }"
713 "}");
714 SchemaDocument s(sd);
715
716 VALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555,\"billing_address\" : \"555 Debtor's Lane\"}", true);
717 INVALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555 }", "", "dependencies", "");
718 VALIDATE(s, "{\"name\": \"John Doe\", \"billing_address\" : \"555 Debtor's Lane\"}", true);
719 }
720
721 #if RAPIDJSON_SCHEMA_HAS_REGEX
722 TEST(SchemaValidator, Object_PatternProperties) {
723 Document sd;
724 sd.Parse(
725 "{"
726 " \"type\": \"object\","
727 " \"patternProperties\": {"
728 " \"^S_\": { \"type\": \"string\" },"
729 " \"^I_\": { \"type\": \"integer\" }"
730 " }"
731 "}");
732 SchemaDocument s(sd);
733
734 VALIDATE(s, "{ \"S_25\": \"This is a string\" }", true);
735 VALIDATE(s, "{ \"I_0\": 42 }", true);
736 INVALIDATE(s, "{ \"S_0\": 42 }", "", "patternProperties", "/S_0");
737 INVALIDATE(s, "{ \"I_42\": \"This is a string\" }", "", "patternProperties", "/I_42");
738 VALIDATE(s, "{ \"keyword\": \"value\" }", true);
739 }
740
741 TEST(SchemaValidator, Object_PatternProperties_AdditionalProperties) {
742 Document sd;
743 sd.Parse(
744 "{"
745 " \"type\": \"object\","
746 " \"properties\": {"
747 " \"builtin\": { \"type\": \"number\" }"
748 " },"
749 " \"patternProperties\": {"
750 " \"^S_\": { \"type\": \"string\" },"
751 " \"^I_\": { \"type\": \"integer\" }"
752 " },"
753 " \"additionalProperties\": { \"type\": \"string\" }"
754 "}");
755 SchemaDocument s(sd);
756
757 VALIDATE(s, "{ \"builtin\": 42 }", true);
758 VALIDATE(s, "{ \"keyword\": \"value\" }", true);
759 INVALIDATE(s, "{ \"keyword\": 42 }", "/additionalProperties", "type", "/keyword");
760 }
761 #endif
762
763 TEST(SchemaValidator, Array) {
764 Document sd;
765 sd.Parse("{\"type\":\"array\"}");
766 SchemaDocument s(sd);
767
768 VALIDATE(s, "[1, 2, 3, 4, 5]", true);
769 VALIDATE(s, "[3, \"different\", { \"types\" : \"of values\" }]", true);
770 INVALIDATE(s, "{\"Not\": \"an array\"}", "", "type", "");
771 }
772
773 TEST(SchemaValidator, Array_ItemsList) {
774 Document sd;
775 sd.Parse(
776 "{"
777 " \"type\": \"array\","
778 " \"items\" : {"
779 " \"type\": \"number\""
780 " }"
781 "}");
782 SchemaDocument s(sd);
783
784 VALIDATE(s, "[1, 2, 3, 4, 5]", true);
785 INVALIDATE(s, "[1, 2, \"3\", 4, 5]", "/items", "type", "/2");
786 VALIDATE(s, "[]", true);
787 }
788
789 TEST(SchemaValidator, Array_ItemsTuple) {
790 Document sd;
791 sd.Parse(
792 "{"
793 " \"type\": \"array\","
794 " \"items\": ["
795 " {"
796 " \"type\": \"number\""
797 " },"
798 " {"
799 " \"type\": \"string\""
800 " },"
801 " {"
802 " \"type\": \"string\","
803 " \"enum\": [\"Street\", \"Avenue\", \"Boulevard\"]"
804 " },"
805 " {"
806 " \"type\": \"string\","
807 " \"enum\": [\"NW\", \"NE\", \"SW\", \"SE\"]"
808 " }"
809 " ]"
810 "}");
811 SchemaDocument s(sd);
812
813 VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\"]", true);
814 INVALIDATE(s, "[24, \"Sussex\", \"Drive\"]", "/items/2", "enum", "/2");
815 INVALIDATE(s, "[\"Palais de l'Elysee\"]", "/items/0", "type", "/0");
816 VALIDATE(s, "[10, \"Downing\", \"Street\"]", true);
817 VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", true);
818 }
819
820 TEST(SchemaValidator, Array_AdditionalItmes) {
821 Document sd;
822 sd.Parse(
823 "{"
824 " \"type\": \"array\","
825 " \"items\": ["
826 " {"
827 " \"type\": \"number\""
828 " },"
829 " {"
830 " \"type\": \"string\""
831 " },"
832 " {"
833 " \"type\": \"string\","
834 " \"enum\": [\"Street\", \"Avenue\", \"Boulevard\"]"
835 " },"
836 " {"
837 " \"type\": \"string\","
838 " \"enum\": [\"NW\", \"NE\", \"SW\", \"SE\"]"
839 " }"
840 " ],"
841 " \"additionalItems\": false"
842 "}");
843 SchemaDocument s(sd);
844
845 VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\"]", true);
846 VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\"]", true);
847 INVALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", "", "items", "/4");
848 }
849
850 TEST(SchemaValidator, Array_ItemsRange) {
851 Document sd;
852 sd.Parse("{\"type\": \"array\",\"minItems\": 2,\"maxItems\" : 3}");
853 SchemaDocument s(sd);
854
855 INVALIDATE(s, "[]", "", "minItems", "");
856 INVALIDATE(s, "[1]", "", "minItems", "");
857 VALIDATE(s, "[1, 2]", true);
858 VALIDATE(s, "[1, 2, 3]", true);
859 INVALIDATE(s, "[1, 2, 3, 4]", "", "maxItems", "");
860 }
861
862 TEST(SchemaValidator, Array_UniqueItems) {
863 Document sd;
864 sd.Parse("{\"type\": \"array\", \"uniqueItems\": true}");
865 SchemaDocument s(sd);
866
867 VALIDATE(s, "[1, 2, 3, 4, 5]", true);
868 INVALIDATE(s, "[1, 2, 3, 3, 4]", "", "uniqueItems", "/3");
869 VALIDATE(s, "[]", true);
870 }
871
872 TEST(SchemaValidator, Boolean) {
873 Document sd;
874 sd.Parse("{\"type\":\"boolean\"}");
875 SchemaDocument s(sd);
876
877 VALIDATE(s, "true", true);
878 VALIDATE(s, "false", true);
879 INVALIDATE(s, "\"true\"", "", "type", "");
880 INVALIDATE(s, "0", "", "type", "");
881 }
882
883 TEST(SchemaValidator, Null) {
884 Document sd;
885 sd.Parse("{\"type\":\"null\"}");
886 SchemaDocument s(sd);
887
888 VALIDATE(s, "null", true);
889 INVALIDATE(s, "false", "", "type", "");
890 INVALIDATE(s, "0", "", "type", "");
891 INVALIDATE(s, "\"\"", "", "type", "");
892 }
893
894 // Additional tests
895
896 TEST(SchemaValidator, ObjectInArray) {
897 Document sd;
898 sd.Parse("{\"type\":\"array\", \"items\": { \"type\":\"string\" }}");
899 SchemaDocument s(sd);
900
901 VALIDATE(s, "[\"a\"]", true);
902 INVALIDATE(s, "[1]", "/items", "type", "/0");
903 INVALIDATE(s, "[{}]", "/items", "type", "/0");
904 }
905
906 TEST(SchemaValidator, MultiTypeInObject) {
907 Document sd;
908 sd.Parse(
909 "{"
910 " \"type\":\"object\","
911 " \"properties\": {"
912 " \"tel\" : {"
913 " \"type\":[\"integer\", \"string\"]"
914 " }"
915 " }"
916 "}");
917 SchemaDocument s(sd);
918
919 VALIDATE(s, "{ \"tel\": 999 }", true);
920 VALIDATE(s, "{ \"tel\": \"123-456\" }", true);
921 INVALIDATE(s, "{ \"tel\": true }", "/properties/tel", "type", "/tel");
922 }
923
924 TEST(SchemaValidator, MultiTypeWithObject) {
925 Document sd;
926 sd.Parse(
927 "{"
928 " \"type\": [\"object\",\"string\"],"
929 " \"properties\": {"
930 " \"tel\" : {"
931 " \"type\": \"integer\""
932 " }"
933 " }"
934 "}");
935 SchemaDocument s(sd);
936
937 VALIDATE(s, "\"Hello\"", true);
938 VALIDATE(s, "{ \"tel\": 999 }", true);
939 INVALIDATE(s, "{ \"tel\": \"fail\" }", "/properties/tel", "type", "/tel");
940 }
941
942 TEST(SchemaValidator, AllOf_Nested) {
943 Document sd;
944 sd.Parse(
945 "{"
946 " \"allOf\": ["
947 " { \"type\": \"string\", \"minLength\": 2 },"
948 " { \"type\": \"string\", \"maxLength\": 5 },"
949 " { \"allOf\": [ { \"enum\" : [\"ok\", \"okay\", \"OK\", \"o\"] }, { \"enum\" : [\"ok\", \"OK\", \"o\"]} ] }"
950 " ]"
951 "}");
952 SchemaDocument s(sd);
953
954 VALIDATE(s, "\"ok\"", true);
955 VALIDATE(s, "\"OK\"", true);
956 INVALIDATE(s, "\"okay\"", "", "allOf", "");
957 INVALIDATE(s, "\"o\"", "", "allOf", "");
958 INVALIDATE(s, "\"n\"", "", "allOf", "");
959 INVALIDATE(s, "\"too long\"", "", "allOf", "");
960 INVALIDATE(s, "123", "", "allOf", "");
961 }
962
963 TEST(SchemaValidator, EscapedPointer) {
964 Document sd;
965 sd.Parse(
966 "{"
967 " \"type\": \"object\","
968 " \"properties\": {"
969 " \"~/\": { \"type\": \"number\" }"
970 " }"
971 "}");
972 SchemaDocument s(sd);
973 INVALIDATE(s, "{\"~/\":true}", "/properties/~0~1", "type", "/~0~1");
974 }
975
976 template <typename Allocator>
977 static char* ReadFile(const char* filename, Allocator& allocator) {
978 const char *paths[] = {
979 "",
980 "bin/",
981 "../bin/",
982 "../../bin/",
983 "../../../bin/"
984 };
985 char buffer[1024];
986 FILE *fp = 0;
987 for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
988 sprintf(buffer, "%s%s", paths[i], filename);
989 fp = fopen(buffer, "rb");
990 if (fp)
991 break;
992 }
993
994 if (!fp)
995 return 0;
996
997 fseek(fp, 0, SEEK_END);
998 size_t length = static_cast<size_t>(ftell(fp));
999 fseek(fp, 0, SEEK_SET);
1000 char* json = reinterpret_cast<char*>(allocator.Malloc(length + 1));
1001 size_t readLength = fread(json, 1, length, fp);
1002 json[readLength] = '\0';
1003 fclose(fp);
1004 return json;
1005 }
1006
1007 TEST(SchemaValidator, ValidateMetaSchema) {
1008 CrtAllocator allocator;
1009 char* json = ReadFile("draft-04/schema", allocator);
1010 Document d;
1011 d.Parse(json);
1012 ASSERT_FALSE(d.HasParseError());
1013 SchemaDocument sd(d);
1014 SchemaValidator validator(sd);
1015 if (!d.Accept(validator)) {
1016 StringBuffer sb;
1017 validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
1018 printf("Invalid schema: %s\n", sb.GetString());
1019 printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());
1020 sb.Clear();
1021 validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);
1022 printf("Invalid document: %s\n", sb.GetString());
1023 ADD_FAILURE();
1024 }
1025 CrtAllocator::Free(json);
1026 }
1027
1028 TEST(SchemaValidator, ValidateMetaSchema_UTF16) {
1029 typedef GenericDocument<UTF16<> > D;
1030 typedef GenericSchemaDocument<D::ValueType> SD;
1031 typedef GenericSchemaValidator<SD> SV;
1032
1033 CrtAllocator allocator;
1034 char* json = ReadFile("draft-04/schema", allocator);
1035
1036 D d;
1037 StringStream ss(json);
1038 d.ParseStream<0, UTF8<> >(ss);
1039 ASSERT_FALSE(d.HasParseError());
1040 SD sd(d);
1041 SV validator(sd);
1042 if (!d.Accept(validator)) {
1043 GenericStringBuffer<UTF16<> > sb;
1044 validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
1045 wprintf(L"Invalid schema: %ls\n", sb.GetString());
1046 wprintf(L"Invalid keyword: %ls\n", validator.GetInvalidSchemaKeyword());
1047 sb.Clear();
1048 validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);
1049 wprintf(L"Invalid document: %ls\n", sb.GetString());
1050 ADD_FAILURE();
1051 }
1052 CrtAllocator::Free(json);
1053 }
1054
1055 template <typename SchemaDocumentType = SchemaDocument>
1056 class RemoteSchemaDocumentProvider : public IGenericRemoteSchemaDocumentProvider<SchemaDocumentType> {
1057 public:
1058 RemoteSchemaDocumentProvider() :
1059 documentAllocator_(documentBuffer_, sizeof(documentBuffer_)),
1060 schemaAllocator_(schemaBuffer_, sizeof(schemaBuffer_))
1061 {
1062 const char* filenames[kCount] = {
1063 "jsonschema/remotes/integer.json",
1064 "jsonschema/remotes/subSchemas.json",
1065 "jsonschema/remotes/folder/folderInteger.json",
1066 "draft-04/schema"
1067 };
1068
1069 for (size_t i = 0; i < kCount; i++) {
1070 sd_[i] = 0;
1071
1072 char jsonBuffer[8192];
1073 MemoryPoolAllocator<> jsonAllocator(jsonBuffer, sizeof(jsonBuffer));
1074 char* json = ReadFile(filenames[i], jsonAllocator);
1075 if (!json) {
1076 printf("json remote file %s not found", filenames[i]);
1077 ADD_FAILURE();
1078 }
1079 else {
1080 char stackBuffer[4096];
1081 MemoryPoolAllocator<> stackAllocator(stackBuffer, sizeof(stackBuffer));
1082 DocumentType d(&documentAllocator_, 1024, &stackAllocator);
1083 d.Parse(json);
1084 sd_[i] = new SchemaDocumentType(d, 0, &schemaAllocator_);
1085 MemoryPoolAllocator<>::Free(json);
1086 }
1087 };
1088 }
1089
1090 ~RemoteSchemaDocumentProvider() {
1091 for (size_t i = 0; i < kCount; i++)
1092 delete sd_[i];
1093 }
1094
1095 virtual const SchemaDocumentType* GetRemoteDocument(const char* uri, SizeType length) {
1096 const char* uris[kCount] = {
1097 "http://localhost:1234/integer.json",
1098 "http://localhost:1234/subSchemas.json",
1099 "http://localhost:1234/folder/folderInteger.json",
1100 "http://json-schema.org/draft-04/schema"
1101 };
1102
1103 for (size_t i = 0; i < kCount; i++)
1104 if (strncmp(uri, uris[i], length) == 0)
1105 return sd_[i];
1106 return 0;
1107 }
1108
1109 private:
1110 typedef GenericDocument<typename SchemaDocumentType::EncodingType, MemoryPoolAllocator<>, MemoryPoolAllocator<> > DocumentType;
1111
1112 RemoteSchemaDocumentProvider(const RemoteSchemaDocumentProvider&);
1113 RemoteSchemaDocumentProvider& operator=(const RemoteSchemaDocumentProvider&);
1114
1115 static const size_t kCount = 4;
1116 SchemaDocumentType* sd_[kCount];
1117 typename DocumentType::AllocatorType documentAllocator_;
1118 typename SchemaDocumentType::AllocatorType schemaAllocator_;
1119 char documentBuffer_[16384];
1120 char schemaBuffer_[128 * 1024];
1121 };
1122
1123 TEST(SchemaValidator, TestSuite) {
1124 const char* filenames[] = {
1125 "additionalItems.json",
1126 "additionalProperties.json",
1127 "allOf.json",
1128 "anyOf.json",
1129 "default.json",
1130 "definitions.json",
1131 "dependencies.json",
1132 "enum.json",
1133 "items.json",
1134 "maximum.json",
1135 "maxItems.json",
1136 "maxLength.json",
1137 "maxProperties.json",
1138 "minimum.json",
1139 "minItems.json",
1140 "minLength.json",
1141 "minProperties.json",
1142 "multipleOf.json",
1143 "not.json",
1144 "oneOf.json",
1145 "pattern.json",
1146 "patternProperties.json",
1147 "properties.json",
1148 "ref.json",
1149 "refRemote.json",
1150 "required.json",
1151 "type.json",
1152 "uniqueItems.json"
1153 };
1154
1155 const char* onlyRunDescription = 0;
1156 //const char* onlyRunDescription = "a string is a string";
1157
1158 unsigned testCount = 0;
1159 unsigned passCount = 0;
1160
1161 typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
1162 RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
1163
1164 char jsonBuffer[65536];
1165 char documentBuffer[65536];
1166 char documentStackBuffer[65536];
1167 char schemaBuffer[65536];
1168 char validatorBuffer[65536];
1169 MemoryPoolAllocator<> jsonAllocator(jsonBuffer, sizeof(jsonBuffer));
1170 MemoryPoolAllocator<> documentAllocator(documentBuffer, sizeof(documentBuffer));
1171 MemoryPoolAllocator<> documentStackAllocator(documentStackBuffer, sizeof(documentStackBuffer));
1172 MemoryPoolAllocator<> schemaAllocator(schemaBuffer, sizeof(schemaBuffer));
1173 MemoryPoolAllocator<> validatorAllocator(validatorBuffer, sizeof(validatorBuffer));
1174
1175 for (size_t i = 0; i < sizeof(filenames) / sizeof(filenames[0]); i++) {
1176 char filename[FILENAME_MAX];
1177 sprintf(filename, "jsonschema/tests/draft4/%s", filenames[i]);
1178 char* json = ReadFile(filename, jsonAllocator);
1179 if (!json) {
1180 printf("json test suite file %s not found", filename);
1181 ADD_FAILURE();
1182 }
1183 else {
1184 GenericDocument<UTF8<>, MemoryPoolAllocator<>, MemoryPoolAllocator<> > d(&documentAllocator, 1024, &documentStackAllocator);
1185 d.Parse(json);
1186 if (d.HasParseError()) {
1187 printf("json test suite file %s has parse error", filename);
1188 ADD_FAILURE();
1189 }
1190 else {
1191 for (Value::ConstValueIterator schemaItr = d.Begin(); schemaItr != d.End(); ++schemaItr) {
1192 {
1193 SchemaDocumentType schema((*schemaItr)["schema"], &provider, &schemaAllocator);
1194 GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > validator(schema, &validatorAllocator);
1195 const char* description1 = (*schemaItr)["description"].GetString();
1196 const Value& tests = (*schemaItr)["tests"];
1197 for (Value::ConstValueIterator testItr = tests.Begin(); testItr != tests.End(); ++testItr) {
1198 const char* description2 = (*testItr)["description"].GetString();
1199 if (!onlyRunDescription || strcmp(description2, onlyRunDescription) == 0) {
1200 const Value& data = (*testItr)["data"];
1201 bool expected = (*testItr)["valid"].GetBool();
1202 testCount++;
1203 validator.Reset();
1204 bool actual = data.Accept(validator);
1205 if (expected != actual)
1206 printf("Fail: %30s \"%s\" \"%s\"\n", filename, description1, description2);
1207 else
1208 passCount++;
1209 }
1210 }
1211 //printf("%zu %zu %zu\n", documentAllocator.Size(), schemaAllocator.Size(), validatorAllocator.Size());
1212 }
1213 schemaAllocator.Clear();
1214 validatorAllocator.Clear();
1215 }
1216 }
1217 }
1218 documentAllocator.Clear();
1219 MemoryPoolAllocator<>::Free(json);
1220 jsonAllocator.Clear();
1221 }
1222 printf("%d / %d passed (%2d%%)\n", passCount, testCount, passCount * 100 / testCount);
1223 // if (passCount != testCount)
1224 // ADD_FAILURE();
1225 }
1226
1227 TEST(SchemaValidatingReader, Simple) {
1228 Document sd;
1229 sd.Parse("{ \"type\": \"string\", \"enum\" : [\"red\", \"amber\", \"green\"] }");
1230 SchemaDocument s(sd);
1231
1232 Document d;
1233 StringStream ss("\"red\"");
1234 SchemaValidatingReader<kParseDefaultFlags, StringStream, UTF8<> > reader(ss, s);
1235 d.Populate(reader);
1236 EXPECT_TRUE(reader.GetParseResult());
1237 EXPECT_TRUE(reader.IsValid());
1238 EXPECT_TRUE(d.IsString());
1239 EXPECT_STREQ("red", d.GetString());
1240 }
1241
1242 TEST(SchemaValidatingReader, Invalid) {
1243 Document sd;
1244 sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}");
1245 SchemaDocument s(sd);
1246
1247 Document d;
1248 StringStream ss("\"ABCD\"");
1249 SchemaValidatingReader<kParseDefaultFlags, StringStream, UTF8<> > reader(ss, s);
1250 d.Populate(reader);
1251 EXPECT_FALSE(reader.GetParseResult());
1252 EXPECT_FALSE(reader.IsValid());
1253 EXPECT_EQ(kParseErrorTermination, reader.GetParseResult().Code());
1254 EXPECT_STREQ("maxLength", reader.GetInvalidSchemaKeyword());
1255 EXPECT_TRUE(reader.GetInvalidSchemaPointer() == SchemaDocument::PointerType(""));
1256 EXPECT_TRUE(reader.GetInvalidDocumentPointer() == SchemaDocument::PointerType(""));
1257 EXPECT_TRUE(d.IsNull());
1258 }
1259
1260 TEST(SchemaValidatingWriter, Simple) {
1261 Document sd;
1262 sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}");
1263 SchemaDocument s(sd);
1264
1265 Document d;
1266 StringBuffer sb;
1267 Writer<StringBuffer> writer(sb);
1268 GenericSchemaValidator<SchemaDocument, Writer<StringBuffer> > validator(s, writer);
1269
1270 d.Parse("\"red\"");
1271 EXPECT_TRUE(d.Accept(validator));
1272 EXPECT_TRUE(validator.IsValid());
1273 EXPECT_STREQ("\"red\"", sb.GetString());
1274
1275 sb.Clear();
1276 validator.Reset();
1277 d.Parse("\"ABCD\"");
1278 EXPECT_FALSE(d.Accept(validator));
1279 EXPECT_FALSE(validator.IsValid());
1280 EXPECT_TRUE(validator.GetInvalidSchemaPointer() == SchemaDocument::PointerType(""));
1281 EXPECT_TRUE(validator.GetInvalidDocumentPointer() == SchemaDocument::PointerType(""));
1282 }
1283
1284 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
1285
1286 static SchemaDocument ReturnSchemaDocument() {
1287 Document sd;
1288 sd.Parse("{ \"type\": [\"number\", \"string\"] }");
1289 SchemaDocument s(sd);
1290 return s;
1291 }
1292
1293 TEST(Schema, Issue552) {
1294 SchemaDocument s = ReturnSchemaDocument();
1295 VALIDATE(s, "42", true);
1296 VALIDATE(s, "\"Life, the universe, and everything\"", true);
1297 INVALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "type", "");
1298 }
1299
1300 #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
1301
1302 TEST(SchemaValidator, Issue608) {
1303 Document sd;
1304 sd.Parse("{\"required\": [\"a\", \"b\"] }");
1305 SchemaDocument s(sd);
1306
1307 VALIDATE(s, "{\"a\" : null, \"b\": null}", true);
1308 INVALIDATE(s, "{\"a\" : null, \"a\" : null}", "", "required", "");
1309 }
1310
1311 #ifdef __clang__
1312 RAPIDJSON_DIAG_POP
1313 #endif