unittest/schematest.cpp
Go to the documentation of this file.
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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;
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)) {
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>
1057 public:
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_);
1086  }
1087  };
1088  }
1089 
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 && strlen(uris[i]) == length)
1105  return sd_[i];
1106  return 0;
1107  }
1108 
1109 private:
1111 
1114 
1115  static const size_t kCount = 4;
1116  SchemaDocumentType* sd_[kCount];
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;
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();
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 
1228  Document sd;
1229  sd.Parse("{ \"type\": \"string\", \"enum\" : [\"red\", \"amber\", \"green\"] }");
1230  SchemaDocument s(sd);
1231 
1232  Document d;
1233  StringStream ss("\"red\"");
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 
1243  Document sd;
1244  sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}");
1245  SchemaDocument s(sd);
1246 
1247  Document d;
1248  StringStream ss("\"ABCD\"");
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);
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 TEST(Schema, Issue848) {
1287  rapidjson::GenericSchemaValidator<rapidjson::SchemaDocument, rapidjson::Document> v(s);
1288 }
1289 
1290 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
1291 
1292 static SchemaDocument ReturnSchemaDocument() {
1293  Document sd;
1294  sd.Parse("{ \"type\": [\"number\", \"string\"] }");
1295  SchemaDocument s(sd);
1296  return s;
1297 }
1298 
1299 TEST(Schema, Issue552) {
1300  SchemaDocument s = ReturnSchemaDocument();
1301  VALIDATE(s, "42", true);
1302  VALIDATE(s, "\"Life, the universe, and everything\"", true);
1303  INVALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "type", "");
1304 }
1305 
1306 #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
1307 
1308 TEST(SchemaValidator, Issue608) {
1309  Document sd;
1310  sd.Parse("{\"required\": [\"a\", \"b\"] }");
1311  SchemaDocument s(sd);
1312 
1313  VALIDATE(s, "{\"a\" : null, \"b\": null}", true);
1314  INVALIDATE(s, "{\"a\" : null, \"a\" : null}", "", "required", "");
1315 }
1316 
1317 // Fail to resolve $ref in allOf causes crash in SchemaValidator::StartObject()
1318 TEST(SchemaValidator, Issue728_AllOfRef) {
1319  Document sd;
1320  sd.Parse("{\"allOf\": [{\"$ref\": \"#/abc\"}]}");
1321  SchemaDocument s(sd);
1322  VALIDATE(s, "{\"key1\": \"abc\", \"key2\": \"def\"}", true);
1323 }
1324 
1325 TEST(SchemaValidator, Issue825) {
1326  Document sd;
1327  sd.Parse("{\"type\": \"object\", \"additionalProperties\": false, \"patternProperties\": {\"^i\": { \"type\": \"string\" } } }");
1328  SchemaDocument s(sd);
1329  VALIDATE(s, "{ \"item\": \"hello\" }", true);
1330 }
1331 
1332 TEST(SchemaValidator, Issue1017_allOfHandler) {
1333  Document sd;
1334  sd.Parse("{\"allOf\": [{\"type\": \"object\",\"properties\": {\"cyanArray2\": {\"type\": \"array\",\"items\": { \"type\": \"string\" }}}},{\"type\": \"object\",\"properties\": {\"blackArray\": {\"type\": \"array\",\"items\": { \"type\": \"string\" }}},\"required\": [ \"blackArray\" ]}]}");
1335  SchemaDocument s(sd);
1336  StringBuffer sb;
1337  Writer<StringBuffer> writer(sb);
1339  EXPECT_TRUE(validator.StartObject());
1340  EXPECT_TRUE(validator.Key("cyanArray2", 10, false));
1341  EXPECT_TRUE(validator.StartArray());
1342  EXPECT_TRUE(validator.EndArray(0));
1343  EXPECT_TRUE(validator.Key("blackArray", 10, false));
1344  EXPECT_TRUE(validator.StartArray());
1345  EXPECT_TRUE(validator.EndArray(0));
1346  EXPECT_TRUE(validator.EndObject(0));
1347  EXPECT_TRUE(validator.IsValid());
1348  EXPECT_STREQ("{\"cyanArray2\":[],\"blackArray\":[]}", sb.GetString());
1349 }
1350 
1351 #ifdef __clang__
1352 RAPIDJSON_DIAG_POP
1353 #endif
d
RAPIDJSON_NAMESPACE_BEGIN typedef unsigned SizeType
Size type (for string lengths, array sizes, etc.)
Definition: rapidjson.h:389
#define INVALIDATE(schema, json, invalidSchemaPointer, invalidSchemaKeyword, invalidDocumentPointer)
Parsing was terminated.
Definition: error.h:88
JSON writer.
Definition: fwd.h:95
const Ch * GetString() const
Definition: stringbuffer.h:73
JSON schema document.
Definition: fwd.h:136
Read-only string stream.
Definition: fwd.h:47
GenericDocument & Populate(Generator &g)
Populate this document by a generator which produces SAX events.
Definition: document.h:2210
TEST(SchemaValidator, Hasher)
XmlRpcServer s
Represents a JSON value. Use Value for UTF8 encoding and default allocator.
Definition: document.h:67
bool EndArray(SizeType elementCount)
Definition: schema.h:1743
#define VALIDATE(schema, json, expected)
void Reset()
Reset the internal states.
Definition: schema.h:1640
static char * ReadFile(const char *filename, Allocator &allocator)
SchemaDocumentType::AllocatorType schemaAllocator_
void Clear()
Deallocates all memory chunks, excluding the user-supplied buffer.
Definition: allocators.h:145
UTF-8 encoding.
Definition: encodings.h:96
static const char json[]
bool IsValid() const
Definition: schema.h:1997
const PointerType & GetInvalidSchemaPointer() const
Definition: schema.h:1998
bool Key(const Ch *str, SizeType len, bool copy)
Definition: schema.h:1722
ParseErrorCode Code() const
Get the error code.
Definition: error.h:116
const ParseResult & GetParseResult() const
Definition: schema.h:1996
Concept for allocating, resizing and freeing memory block.
GenericDocument< UTF8<> > Document
GenericDocument with UTF8 encoding.
Definition: document.h:2477
const PointerType & GetInvalidDocumentPointer() const
Definition: schema.h:2000
PointerType GetInvalidSchemaPointer() const
Gets the JSON pointer pointed to the invalid schema.
Definition: schema.h:1652
DocumentType::AllocatorType documentAllocator_
GenericDocument & Parse(const typename SourceEncoding::Ch *str)
Parse JSON text from a read-only string (with Encoding conversion)
Definition: document.h:2296
GenericDocument< typename SchemaDocumentType::EncodingType, MemoryPoolAllocator<>, MemoryPoolAllocator<> > DocumentType
main RapidJSON namespace
#define TEST_HASHER(json1, json2, expected)
PointerType GetInvalidDocumentPointer() const
Gets the JSON pointer pointed to the invalid value.
Definition: schema.h:1662
C-runtime library allocator.
Definition: allocators.h:62
virtual bool IsValid() const
Checks whether the current state is valid.
Definition: schema.h:1649
A helper class for parsing with validation.
Definition: schema.h:1963
virtual const SchemaDocumentType * GetRemoteDocument(const char *uri, SizeType length)
const Ch * GetInvalidSchemaKeyword() const
Gets the keyword of invalid schema.
Definition: schema.h:1657
static void Free(void *ptr)
Definition: allocators.h:79
static void Free(void *ptr)
Frees a memory block (concept Allocator)
Definition: allocators.h:225
GenericSchemaDocument< Value, CrtAllocator > SchemaDocument
Definition: fwd.h:136
const Ch * GetInvalidSchemaKeyword() const
Definition: schema.h:1999
JSON Schema Validator.
Definition: fwd.h:145
bool HasParseError() const
Whether a parse error has occured in the last parsing.
Definition: document.h:2359
bool EndObject(SizeType memberCount)
Definition: schema.h:1730
GenericPointer< ValueType, Allocator > PointerType
Definition: schema.h:1326


choreo_rapidjson
Author(s):
autogenerated on Thu Jul 18 2019 03:59:09