Felipe97/llama-cpp-compiled
01.1k
1#include "json-schema.h"2#include "json.h"3#include "testing.h"4 5#include <cstdlib>6#include <iostream>7#include <stdexcept>8#include <string>9#include <utility>10 11static common_chat_schema_document parse(const std::string & schema) {12 return common_chat_schema_from_json(common_json::parse(schema));13}14 15// the node as T, aborting the current test when it is some other kind16template <typename T>17static const T & as(testing & t, const common_chat_schema * node, const char * what) {18 const T * typed = dynamic_cast<const T *>(node);19 if (!t.assert_true(std::string(what) + " has the expected kind", typed != nullptr)) {20 throw std::runtime_error(std::string(what) + " has the wrong kind");21 }22 return *typed;23}24 25template <typename T>26static const T & root(testing & t, const common_chat_schema_document & doc) {27 return as<T>(t, doc.root.get(), "root");28}29 30static void assert_error(testing & t, const std::string & schema, const std::string & needle) {31 try {32 parse(schema);33 t.assert_true(schema + " is rejected", false);34 } catch (const std::runtime_error & e) {35 std::string what = e.what();36 t.assert_true(schema + " -> " + what, what.find(needle) != std::string::npos);37 }38}39 40static void test_any(testing & t) {41 t.test("empty schema", [](testing & t) {42 auto doc = parse("{}");43 root<common_chat_schema_any>(t, doc);44 t.assert_true("no refs", doc.refs.empty());45 });46 47 t.test("keywords that do not imply a type", [](testing & t) {48 auto doc = parse(R"({"description": "x", "format": "email", "additionalProperties": true})");49 root<common_chat_schema_any>(t, doc);50 });51}52 53static void test_primitives(testing & t) {54 t.test("null, boolean, number", [](testing & t) {55 auto doc_null = parse(R"({"type": "null"})");56 root<common_chat_schema_null>(t, doc_null);57 auto doc_bool = parse(R"({"type": "boolean"})");58 root<common_chat_schema_boolean>(t, doc_bool);59 auto doc_num = parse(R"({"type": "number", "minimum": 1, "maximum": 2})");60 root<common_chat_schema_number>(t, doc_num);61 });62}63 64static void test_integer(testing & t) {65 t.test("unbounded", [](testing & t) {66 auto doc = parse(R"({"type": "integer"})");67 const auto & i = root<common_chat_schema_integer>(t, doc);68 t.assert_equal("minimum", INT64_MIN, i.minimum);69 t.assert_equal("maximum", INT64_MAX, i.maximum);70 });71 72 t.test("inclusive bounds", [](testing & t) {73 auto doc = parse(R"({"type": "integer", "minimum": -5, "maximum": 10})");74 const auto & i = root<common_chat_schema_integer>(t, doc);75 t.assert_equal("minimum", -5, i.minimum);76 t.assert_equal("maximum", 10, i.maximum);77 });78 79 t.test("exclusive bounds are folded", [](testing & t) {80 auto doc = parse(R"({"type": "integer", "exclusiveMinimum": 0, "exclusiveMaximum": 10})");81 const auto & i = root<common_chat_schema_integer>(t, doc);82 t.assert_equal("minimum", 1, i.minimum);83 t.assert_equal("maximum", 9, i.maximum);84 });85 86 t.test("fractional bounds round inwards", [](testing & t) {87 auto doc = parse(R"({"type": "integer", "minimum": 1.5, "exclusiveMaximum": 9.5})");88 const auto & i = root<common_chat_schema_integer>(t, doc);89 t.assert_equal("minimum", 2, i.minimum);90 t.assert_equal("maximum", 9, i.maximum);91 });92}93 94static void test_string(testing & t) {95 t.test("defaults", [](testing & t) {96 auto doc = parse(R"({"type": "string"})");97 const auto & s = root<common_chat_schema_string>(t, doc);98 t.assert_equal("pattern", "", s.pattern);99 t.assert_equal("format", common_chat_schema::FORMAT_NONE, s.format);100 t.assert_equal("min_length", 0, s.min_length);101 t.assert_equal("max_length", -1, s.max_length);102 });103 104 t.test("all keywords are kept", [](testing & t) {105 auto doc = parse(R"({"type": "string", "pattern": "^[a-z]+$", "format": "date", "minLength": 2, "maxLength": 8})");106 const auto & s = root<common_chat_schema_string>(t, doc);107 t.assert_equal("pattern", "^[a-z]+$", s.pattern);108 t.assert_equal("format", common_chat_schema::FORMAT_DATE, s.format);109 t.assert_equal("min_length", 2, s.min_length);110 t.assert_equal("max_length", 8, s.max_length);111 });112 113 t.test("formats", [](testing & t) {114 auto expect = [&](const char * format, common_chat_schema::string_format expected) {115 auto doc = parse(std::string(R"({"type": "string", "format": ")") + format + "\"}");116 t.assert_equal(format, expected, root<common_chat_schema_string>(t, doc).format);117 };118 expect("time", common_chat_schema::FORMAT_TIME);119 expect("date-time", common_chat_schema::FORMAT_DATE_TIME);120 expect("uuid", common_chat_schema::FORMAT_UUID);121 expect("uuid5", common_chat_schema::FORMAT_UUID);122 expect("email", common_chat_schema::FORMAT_NONE);123 });124 125 t.test("pattern, length and known format imply a string", [](testing & t) {126 auto doc_pattern = parse(R"({"pattern": "^a$"})");127 t.assert_equal("pattern", "^a$", root<common_chat_schema_string>(t, doc_pattern).pattern);128 auto doc_length = parse(R"({"minLength": 1, "maxLength": 3})");129 t.assert_equal("min_length", 1, root<common_chat_schema_string>(t, doc_length).min_length);130 t.assert_equal("max_length", 3, root<common_chat_schema_string>(t, doc_length).max_length);131 auto doc_format = parse(R"({"format": "uuid"})");132 t.assert_equal("format", common_chat_schema::FORMAT_UUID, root<common_chat_schema_string>(t, doc_format).format);133 });134}135 136static void test_array(testing & t) {137 t.test("items with bounds", [](testing & t) {138 auto doc = parse(R"({"type": "array", "items": {"type": "integer"}, "minItems": 1, "maxItems": 3})");139 const auto & a = root<common_chat_schema_array>(t, doc);140 as<common_chat_schema_integer>(t, a.items.get(), "items");141 t.assert_equal("min_items", 1, a.min_items);142 t.assert_equal("max_items", 3, a.max_items);143 });144 145 t.test("no items", [](testing & t) {146 auto doc = parse(R"({"type": "array"})");147 const auto & a = root<common_chat_schema_array>(t, doc);148 as<common_chat_schema_any>(t, a.items.get(), "items");149 t.assert_equal("min_items", 0, a.min_items);150 t.assert_equal("max_items", -1, a.max_items);151 });152 153 t.test("items imply an array", [](testing & t) {154 auto doc = parse(R"({"items": {"type": "string"}})");155 const auto & a = root<common_chat_schema_array>(t, doc);156 as<common_chat_schema_string>(t, a.items.get(), "items");157 });158}159 160static void test_tuple(testing & t) {161 t.test("prefixItems", [](testing & t) {162 auto doc = parse(R"({"prefixItems": [{"type": "string"}, {"type": "number"}]})");163 const auto & tup = root<common_chat_schema_tuple>(t, doc);164 t.assert_equal("size", (size_t) 2, tup.items.size());165 as<common_chat_schema_string>(t, tup.items[0].get(), "items[0]");166 as<common_chat_schema_number>(t, tup.items[1].get(), "items[1]");167 });168 169 t.test("items as an array", [](testing & t) {170 auto doc = parse(R"({"type": "array", "items": [{"type": "boolean"}]})");171 const auto & tup = root<common_chat_schema_tuple>(t, doc);172 t.assert_equal("size", (size_t) 1, tup.items.size());173 as<common_chat_schema_boolean>(t, tup.items[0].get(), "items[0]");174 });175}176 177static void test_object(testing & t) {178 t.test("type alone accepts any object", [](testing & t) {179 auto doc = parse(R"({"type": "object"})");180 const auto & o = root<common_chat_schema_object>(t, doc);181 t.assert_true("no properties", o.properties.empty());182 as<common_chat_schema_any>(t, o.additional_properties.get(), "additional_properties");183 });184 185 t.test("properties", [](testing & t) {186 auto doc = parse(R"({187 "type": "object",188 "properties": {189 "b": {"type": "string"},190 "a": {"type": "integer"},191 "c": {"type": "boolean"}192 },193 "required": ["a", "c"]194 })");195 const auto & o = root<common_chat_schema_object>(t, doc);196 t.assert_equal("size", (size_t) 3, o.properties.size());197 t.assert_equal("order", "b", o.properties[0].name);198 t.assert_equal("order", "a", o.properties[1].name);199 t.assert_equal("order", "c", o.properties[2].name);200 t.assert_true("b optional", !o.properties[0].required);201 t.assert_true("a required", o.properties[1].required);202 t.assert_true("c required", o.properties[2].required);203 as<common_chat_schema_string>(t, o.properties[0].schema.get(), "b");204 as<common_chat_schema_integer>(t, o.properties[1].schema.get(), "a");205 as<common_chat_schema_boolean>(t, o.properties[2].schema.get(), "c");206 t.assert_true("closed", o.additional_properties == nullptr);207 });208 209 t.test("unknown required entries are ignored", [](testing & t) {210 auto doc = parse(R"({"properties": {"a": {}}, "required": ["a", "zzz", 1]})");211 const auto & o = root<common_chat_schema_object>(t, doc);212 t.assert_equal("size", (size_t) 1, o.properties.size());213 t.assert_true("a required", o.properties[0].required);214 });215 216 t.test("additionalProperties false implies an object", [](testing & t) {217 auto doc = parse(R"({"additionalProperties": false})");218 const auto & o = root<common_chat_schema_object>(t, doc);219 t.assert_true("no properties", o.properties.empty());220 t.assert_true("closed", o.additional_properties == nullptr);221 });222 223 t.test("additionalProperties schema", [](testing & t) {224 auto doc = parse(R"({"properties": {"a": {}}, "additionalProperties": {"type": "integer", "minimum": 0}})");225 const auto & o = root<common_chat_schema_object>(t, doc);226 t.assert_equal("size", (size_t) 1, o.properties.size());227 const auto & v = as<common_chat_schema_integer>(t, o.additional_properties.get(), "additional_properties");228 t.assert_equal("minimum", 0, v.minimum);229 });230 231 t.test("nested", [](testing & t) {232 auto doc = parse(R"({"properties": {"inner": {"properties": {"leaf": {"type": "null"}}, "required": ["leaf"]}}})");233 const auto & o = root<common_chat_schema_object>(t, doc);234 const auto & inner = as<common_chat_schema_object>(t, o.properties[0].schema.get(), "inner");235 t.assert_equal("leaf name", "leaf", inner.properties[0].name);236 t.assert_true("leaf required", inner.properties[0].required);237 as<common_chat_schema_null>(t, inner.properties[0].schema.get(), "leaf");238 });239}240 241static void test_const_enum(testing & t) {242 t.test("const", [](testing & t) {243 auto doc = parse(R"({"const": {"a": [1, null]}})");244 t.assert_equal("value", R"({"a":[1,null]})", root<common_chat_schema_const>(t, doc).value.dump());245 });246 247 t.test("enum", [](testing & t) {248 auto doc = parse(R"({"enum": ["a", 1, null, true]})");249 const auto & e = root<common_chat_schema_enum>(t, doc);250 t.assert_equal("size", (size_t) 4, e.values.size());251 t.assert_equal("values[0]", "\"a\"", e.values[0].dump());252 t.assert_equal("values[1]", "1", e.values[1].dump());253 t.assert_equal("values[2]", "null", e.values[2].dump());254 t.assert_equal("values[3]", "true", e.values[3].dump());255 });256 257 t.test("const wins over enum, enum wins over type", [](testing & t) {258 auto doc_enum = parse(R"({"type": "integer", "enum": [1, 2]})");259 root<common_chat_schema_enum>(t, doc_enum);260 auto doc_const = parse(R"({"type": "string", "const": "x", "enum": ["y"]})");261 t.assert_equal("value", "\"x\"", root<common_chat_schema_const>(t, doc_const).value.dump());262 });263}264 265static void test_any_of(testing & t) {266 t.test("anyOf and oneOf", [](testing & t) {267 auto doc_any = parse(R"({"anyOf": [{"type": "string"}, {"type": "number"}]})");268 const auto & u = root<common_chat_schema_any_of>(t, doc_any);269 t.assert_equal("size", (size_t) 2, u.children.size());270 as<common_chat_schema_string>(t, u.children[0].get(), "children[0]");271 as<common_chat_schema_number>(t, u.children[1].get(), "children[1]");272 273 auto doc_one = parse(R"({"oneOf": [{"type": "null"}]})");274 const auto & o = root<common_chat_schema_any_of>(t, doc_one);275 t.assert_equal("size", (size_t) 1, o.children.size());276 as<common_chat_schema_null>(t, o.children[0].get(), "children[0]");277 });278 279 t.test("oneOf wins over anyOf and type", [](testing & t) {280 auto doc = parse(R"({"type": "string", "oneOf": [{"type": "null"}], "anyOf": [{"type": "number"}, {"type": "boolean"}]})");281 const auto & u = root<common_chat_schema_any_of>(t, doc);282 t.assert_equal("size", (size_t) 1, u.children.size());283 as<common_chat_schema_null>(t, u.children[0].get(), "children[0]");284 });285 286 t.test("type array expands with sibling keywords", [](testing & t) {287 auto doc = parse(R"({"type": ["string", "null", "integer"], "minLength": 2, "minimum": 5})");288 const auto & u = root<common_chat_schema_any_of>(t, doc);289 t.assert_equal("size", (size_t) 3, u.children.size());290 t.assert_equal("min_length", 2, as<common_chat_schema_string>(t, u.children[0].get(), "children[0]").min_length);291 as<common_chat_schema_null>(t, u.children[1].get(), "children[1]");292 t.assert_equal("minimum", 5, as<common_chat_schema_integer>(t, u.children[2].get(), "children[2]").minimum);293 });294}295 296static void test_all_of(testing & t) {297 t.test("components", [](testing & t) {298 auto doc = parse(R"({"allOf": [{"properties": {"a": {}}}, {"anyOf": [{"properties": {"b": {}}}, {"type": "null"}]}]})");299 const auto & all = root<common_chat_schema_all_of>(t, doc);300 t.assert_equal("size", (size_t) 2, all.children.size());301 as<common_chat_schema_object>(t, all.children[0].get(), "children[0]");302 as<common_chat_schema_any_of>(t, all.children[1].get(), "children[1]");303 304 auto doc_typed = parse(R"({"type": "object", "allOf": [{"properties": {"a": {}}}]})");305 root<common_chat_schema_all_of>(t, doc_typed);306 });307 308 t.test("properties win over allOf", [](testing & t) {309 auto doc = parse(R"({"type": "object", "properties": {"a": {}}, "allOf": [{"properties": {"b": {}}}]})");310 t.assert_equal("size", (size_t) 1, root<common_chat_schema_object>(t, doc).properties.size());311 });312 313 t.test("other types ignore allOf", [](testing & t) {314 auto doc = parse(R"({"type": "integer", "allOf": [{"minimum": 1}]})");315 root<common_chat_schema_integer>(t, doc);316 });317}318 319static void test_ref(testing & t) {320 t.test("target is owned by the document", [](testing & t) {321 auto doc = parse(R"({"$ref": "#/$defs/t", "type": "string", "$defs": {"t": {"type": "boolean"}}})");322 const auto & r = root<common_chat_schema_ref>(t, doc);323 t.assert_equal("ref", "#/$defs/t", r.ref);324 t.assert_equal("refs", (size_t) 1, doc.refs.size());325 t.assert_true("target", r.target != nullptr && r.target == doc.refs.at("#/$defs/t").get());326 as<common_chat_schema_boolean>(t, r.target, "target");327 });328 329 t.test("definitions", [](testing & t) {330 auto doc = parse(R"({"properties": {"a": {"$ref": "#/definitions/t"}}, "definitions": {"t": {"type": "number"}}})");331 const auto & o = root<common_chat_schema_object>(t, doc);332 const auto & r = as<common_chat_schema_ref>(t, o.properties[0].schema.get(), "a");333 as<common_chat_schema_number>(t, r.target, "target");334 });335 336 t.test("recursive", [](testing & t) {337 auto doc = parse(R"({338 "$ref": "#/$defs/node",339 "$defs": {340 "node": {341 "type": "object",342 "properties": {343 "value": {"type": "number"},344 "next": {"$ref": "#/$defs/node"}345 },346 "required": ["value"]347 }348 }349 })");350 const auto & r = root<common_chat_schema_ref>(t, doc);351 const auto & node = as<common_chat_schema_object>(t, r.target, "node");352 t.assert_equal("properties", (size_t) 2, node.properties.size());353 const auto & next = as<common_chat_schema_ref>(t, node.properties[1].schema.get(), "next");354 t.assert_true("cycle", next.target == r.target);355 t.assert_equal("refs", (size_t) 1, doc.refs.size());356 });357 358 t.test("pointer through an array", [](testing & t) {359 auto doc = parse(R"({"oneOf": [{"type": "null"}, {"$ref": "#/oneOf/0"}]})");360 const auto & u = root<common_chat_schema_any_of>(t, doc);361 const auto & r = as<common_chat_schema_ref>(t, u.children[1].get(), "children[1]");362 as<common_chat_schema_null>(t, r.target, "target");363 });364 365 t.test("targets survive moving the document", [](testing & t) {366 auto parsed = parse(R"({"items": {"$ref": "#/$defs/t"}, "$defs": {"t": {"type": "null"}}})");367 common_chat_schema_document doc = std::move(parsed);368 const auto & a = root<common_chat_schema_array>(t, doc);369 const auto & r = as<common_chat_schema_ref>(t, a.items.get(), "items");370 t.assert_true("target", r.target == doc.refs.at("#/$defs/t").get());371 as<common_chat_schema_null>(t, r.target, "target");372 });373}374 375static void test_may_be_string(testing & t) {376 auto check = [](testing & t, const std::string & schema, bool expected) {377 t.assert_equal(schema, expected, parse(schema).root->may_be_string());378 };379 380 t.test("leaves", [&](testing & t) {381 check(t, R"({"type": "string"})", true);382 check(t, R"({"type": "integer"})", false);383 check(t, R"({"minLength": 1})", true);384 check(t, R"({"pattern": "^[a-z]+$"})", true);385 check(t, R"({"const": "hello"})", true);386 check(t, R"({"const": 123})", false);387 check(t, R"({"enum": [1, "a", null]})", true);388 check(t, R"({"enum": [1, 2, 3]})", false);389 });390 391 t.test("composites", [&](testing & t) {392 check(t, R"({"type": ["integer", "string"]})", true);393 check(t, R"({"anyOf": [{"type": "integer"}, {"type": "boolean"}]})", false);394 check(t, R"({"allOf": [{"type": "string"}, {"minLength": 1}]})", true);395 check(t, R"({"allOf": [{"type": "string"}, {"type": "integer"}]})", false);396 check(t, R"({"allOf": [{"minLength": 1}, {"maxLength": 2}]})", true);397 });398 399 t.test("ref", [&](testing & t) {400 check(t, R"({"$ref": "#/$defs/n", "$defs": {"n": {"anyOf": [{"$ref": "#/$defs/n"}, {"type": "string"}]}}})", true);401 check(t, R"({"$ref": "#/$defs/n", "$defs": {"n": {"$ref": "#/$defs/n"}}})", false);402 check(t, R"({"anyOf": [{"$ref": "#/$defs/a"}, {"$ref": "#/$defs/b"}], "$defs": {"a": {"allOf": [{"$ref": "#/$defs/b"}, {"type": "integer"}]}, "b": {"type": "string"}}})", true);403 });404}405 406// e.g. {number, integer}, in type order407static std::string dump(const common_chat_schema::type_set & types) {408 static const common_chat_schema::value_type order[] = { common_chat_schema::TYPE_NULL, common_chat_schema::TYPE_BOOLEAN, common_chat_schema::TYPE_NUMBER,409 common_chat_schema::TYPE_INTEGER, common_chat_schema::TYPE_STRING, common_chat_schema::TYPE_ARRAY,410 common_chat_schema::TYPE_OBJECT };411 std::string out;412 for (auto type : order) {413 if (types.has(type)) {414 out += (out.empty() ? "" : ", ") + std::string(common_chat_schema::type_name(type));415 }416 }417 return "{" + out + "}";418}419 420static void test_value_types(testing & t) {421 auto check = [](testing & t, const std::string & schema, const common_chat_schema::type_set & expected) {422 t.assert_equal(schema, dump(expected), dump(parse(schema).root->value_types()));423 };424 425 t.test("leaves", [&](testing & t) {426 check(t, R"({"type": "string"})", { common_chat_schema::TYPE_STRING });427 check(t, R"({"type": "number"})", { common_chat_schema::TYPE_NUMBER, common_chat_schema::TYPE_INTEGER });428 check(t, R"({"description": "x"})", common_chat_schema::type_set::all());429 check(t, R"({"properties": {"a": {"type": "string"}}})", { common_chat_schema::TYPE_OBJECT });430 check(t, R"({"items": {"type": "string"}})", { common_chat_schema::TYPE_ARRAY });431 check(t, R"({"const": 1.5})", { common_chat_schema::TYPE_NUMBER });432 check(t, R"({"enum": [1, "a", null]})", { common_chat_schema::TYPE_INTEGER, common_chat_schema::TYPE_STRING, common_chat_schema::TYPE_NULL });433 });434 435 t.test("any_of is the union, all_of is the intersection", [&](testing & t) {436 check(t, R"({"type": ["string", "null"]})", { common_chat_schema::TYPE_STRING, common_chat_schema::TYPE_NULL });437 check(t, R"({"allOf": [{"type": ["string", "number"]}, {"type": ["number", "object"]}]})", { common_chat_schema::TYPE_NUMBER, common_chat_schema::TYPE_INTEGER });438 check(t, R"({"allOf": [{"type": "string"}, {"type": "integer"}]})", {});439 });440 441 t.test("ref", [&](testing & t) {442 check(t, R"({"$ref": "#/$defs/n", "$defs": {"n": {"anyOf": [{"$ref": "#/$defs/n"}, {"type": "string"}]}}})",443 { common_chat_schema::TYPE_STRING });444 });445}446 447static void test_errors(testing & t) {448 t.test("not a schema", [](testing & t) {449 assert_error(t, R"([])", "#: schema must be an object");450 });451 452 t.test("type", [](testing & t) {453 assert_error(t, R"({"type": 5})", "#: type must be a string or an array of strings");454 assert_error(t, R"({"type": []})", "#: type must not be empty");455 assert_error(t, R"({"type": ["string", "bad"]})", "#/type/1: unrecognized type bad");456 });457 458 t.test("ref", [](testing & t) {459 assert_error(t, R"({"$ref": 5})", "#: $ref must be a string");460 assert_error(t, R"({"$ref": "https://example.com/x.json"})", "#: unsupported $ref https://example.com/x.json");461 assert_error(t, R"({"$ref": ""})", "#: unsupported $ref ,");462 assert_error(t, R"({"$ref": "#"})", "#: unsupported $ref #,");463 assert_error(t, R"({"$defs": {}, "$ref": "#/$defs/missing"})", "#: cannot resolve $ref #/$defs/missing, missing not found");464 assert_error(t, R"({"oneOf": [{}], "$ref": "#/oneOf/1"})", "#: cannot resolve $ref #/oneOf/1, 1 is out of range");465 assert_error(t, R"({"$defs": {"a": {"$ref": "#/$defs/a/nope"}}, "$ref": "#/$defs/a"})", "#/$defs/a: cannot resolve $ref #/$defs/a/nope, nope not found");466 });467 468 t.test("alternatives", [](testing & t) {469 assert_error(t, R"({"oneOf": []})", "#/oneOf: must not be empty");470 assert_error(t, R"({"anyOf": {}})", "#/anyOf: must be an array of schemas");471 assert_error(t, R"({"anyOf": [{"type": "string"}, {"items": {"type": "x"}}]})", "#/anyOf/1/items: unrecognized type x");472 });473 474 t.test("keywords", [](testing & t) {475 assert_error(t, R"({"enum": []})", "#: enum must be a non-empty array");476 assert_error(t, R"({"type": "string", "pattern": 5})", "#: pattern must be a string");477 assert_error(t, R"({"type": "string", "minLength": -1})", "#: minLength must be a non-negative integer");478 assert_error(t, R"({"type": "integer", "minimum": "1"})", "#: minimum must be a number");479 assert_error(t, R"({"type": "array", "maxItems": 1.5})", "#: maxItems must be a non-negative integer");480 assert_error(t, R"({"properties": []})", "#: properties must be an object");481 assert_error(t, R"({"properties": {"a": {"type": "nope"}}})", "#/properties/a: unrecognized type nope");482 assert_error(t, R"({"additionalProperties": null})", "#: additionalProperties must be a boolean or a schema");483 });484}485 486int main(int argc, char * argv[]) {487 testing t(std::cout);488 if (argc >= 2) {489 t.set_filter(argv[1]);490 }491 492 const char * verbose = getenv("LLAMA_TEST_VERBOSE");493 if (verbose) {494 t.verbose = std::string(verbose) == "1";495 }496 497 t.test("any", test_any);498 t.test("primitives", test_primitives);499 t.test("integer", test_integer);500 t.test("string", test_string);501 t.test("array", test_array);502 t.test("tuple", test_tuple);503 t.test("object", test_object);504 t.test("const and enum", test_const_enum);505 t.test("any_of", test_any_of);506 t.test("all_of", test_all_of);507 t.test("ref", test_ref);508 t.test("may_be_string", test_may_be_string);509 t.test("value_types", test_value_types);510 t.test("errors", test_errors);511 512 return t.summary();513}514 