summaryrefslogtreecommitdiff
path: root/tests/test-json-schema-to-grammar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test-json-schema-to-grammar.cpp')
-rwxr-xr-xtests/test-json-schema-to-grammar.cpp455
1 files changed, 414 insertions, 41 deletions
diff --git a/tests/test-json-schema-to-grammar.cpp b/tests/test-json-schema-to-grammar.cpp
index 87bc66b6..65486ac5 100755
--- a/tests/test-json-schema-to-grammar.cpp
+++ b/tests/test-json-schema-to-grammar.cpp
@@ -81,6 +81,232 @@ static void test_all(const std::string & lang, std::function<void(const TestCase
};
test({
+ SUCCESS,
+ "min 0",
+ R"""({
+ "type": "integer",
+ "minimum": 0
+ })""",
+ R"""(
+ root ::= ([0] | [1-9] [0-9]{0,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 1",
+ R"""({
+ "type": "integer",
+ "minimum": 1
+ })""",
+ R"""(
+ root ::= ([1-9] [0-9]{0,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 3",
+ R"""({
+ "type": "integer",
+ "minimum": 3
+ })""",
+ R"""(
+ root ::= ([1-2] [0-9]{1,15} | [3-9] [0-9]{0,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 9",
+ R"""({
+ "type": "integer",
+ "minimum": 9
+ })""",
+ R"""(
+ root ::= ([1-8] [0-9]{1,15} | [9] [0-9]{0,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 10",
+ R"""({
+ "type": "integer",
+ "minimum": 10
+ })""",
+ R"""(
+ root ::= ([1] ([0-9]{1,15}) | [2-9] [0-9]{1,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 25",
+ R"""({
+ "type": "integer",
+ "minimum": 25
+ })""",
+ R"""(
+ root ::= ([1] [0-9]{2,15} | [2] ([0-4] [0-9]{1,14} | [5-9] [0-9]{0,14}) | [3-9] [0-9]{1,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "max 30",
+ R"""({
+ "type": "integer",
+ "maximum": 30
+ })""",
+ R"""(
+ root ::= ("-" [1-9] [0-9]{0,15} | [0-9] | ([1-2] [0-9] | [3] "0")) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min -5",
+ R"""({
+ "type": "integer",
+ "minimum": -5
+ })""",
+ R"""(
+ root ::= ("-" ([0-5]) | [0] | [1-9] [0-9]{0,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min -123",
+ R"""({
+ "type": "integer",
+ "minimum": -123
+ })""",
+ R"""(
+ root ::= ("-" ([0-9] | ([1-8] [0-9] | [9] [0-9]) | "1" ([0-1] [0-9] | [2] [0-3])) | [0] | [1-9] [0-9]{0,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "max -5",
+ R"""({
+ "type": "integer",
+ "maximum": -5
+ })""",
+ R"""(
+ root ::= ("-" ([0-4] [0-9]{1,15} | [5-9] [0-9]{0,15})) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "max 1",
+ R"""({
+ "type": "integer",
+ "maximum": 1
+ })""",
+ R"""(
+ root ::= ("-" [1-9] [0-9]{0,15} | [0-1]) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "max 100",
+ R"""({
+ "type": "integer",
+ "maximum": 100
+ })""",
+ R"""(
+ root ::= ("-" [1-9] [0-9]{0,15} | [0-9] | ([1-8] [0-9] | [9] [0-9]) | "100") space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 0 max 23",
+ R"""({
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 23
+ })""",
+ R"""(
+ root ::= ([0-9] | ([1] [0-9] | [2] [0-3])) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 15 max 300",
+ R"""({
+ "type": "integer",
+ "minimum": 15,
+ "maximum": 300
+ })""",
+ R"""(
+ root ::= (([1] ([5-9]) | [2-9] [0-9]) | ([1-2] [0-9]{2} | [3] "00")) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 5 max 30",
+ R"""({
+ "type": "integer",
+ "minimum": 5,
+ "maximum": 30
+ })""",
+ R"""(
+ root ::= ([5-9] | ([1-2] [0-9] | [3] "0")) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min -123 max 42",
+ R"""({
+ "type": "integer",
+ "minimum": -123,
+ "maximum": 42
+ })""",
+ R"""(
+ root ::= ("-" ([0-9] | ([1-8] [0-9] | [9] [0-9]) | "1" ([0-1] [0-9] | [2] [0-3])) | [0-9] | ([1-3] [0-9] | [4] [0-2])) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min -10 max 10",
+ R"""({
+ "type": "integer",
+ "minimum": -10,
+ "maximum": 10
+ })""",
+ R"""(
+ root ::= ("-" ([0-9] | "10") | [0-9] | "10") space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
FAILURE,
"unknown type",
R"""({
@@ -247,7 +473,7 @@ static void test_all(const std::string & lang, std::function<void(const TestCase
"const": "foo"
})""",
R"""(
- root ::= "\"foo\""
+ root ::= "\"foo\"" space
space ::= | " " | "\n" [ \t]{0,20}
)"""
});
@@ -259,7 +485,7 @@ static void test_all(const std::string & lang, std::function<void(const TestCase
"const": 123
})""",
R"""(
- root ::= "123"
+ root ::= "123" space
space ::= | " " | "\n" [ \t]{0,20}
)"""
});
@@ -271,8 +497,40 @@ static void test_all(const std::string & lang, std::function<void(const TestCase
"enum": ["red", "amber", "green", null, 42, ["foo"]]
})""",
R"""(
- root ::= "\"red\"" | "\"amber\"" | "\"green\"" | "null" | "42" | "[\"foo\"]"
+ root ::= ("\"red\"" | "\"amber\"" | "\"green\"" | "null" | "42" | "[\"foo\"]") space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "string array",
+ R"""({
+ "type": "array",
+ "prefixItems": { "type": "string" }
+ })""",
+ R"""(
+ char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
+ root ::= "[" space (string ("," space string)*)? "]" space
+ space ::= | " " | "\n" [ \t]{0,20}
+ string ::= "\"" char* "\"" space
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "nullable string array",
+ R"""({
+ "type": ["array", "null"],
+ "prefixItems": { "type": "string" }
+ })""",
+ R"""(
+ alternative-0 ::= "[" space (string ("," space string)*)? "]" space
+ char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
+ null ::= "null" space
+ root ::= alternative-0 | null
space ::= | " " | "\n" [ \t]{0,20}
+ string ::= "\"" char* "\"" space
)"""
});
@@ -392,6 +650,44 @@ static void test_all(const std::string & lang, std::function<void(const TestCase
test({
SUCCESS,
+ "min + max items with min + max values across zero",
+ R"""({
+ "items": {
+ "type": "integer",
+ "minimum": -12,
+ "maximum": 207
+ },
+ "minItems": 3,
+ "maxItems": 5
+ })""",
+ R"""(
+ item ::= ("-" ([0-9] | "1" [0-2]) | [0-9] | ([1-8] [0-9] | [9] [0-9]) | ([1] [0-9]{2} | [2] "0" [0-7])) space
+ root ::= "[" space item ("," space item){2,4} "]" space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min + max items with min + max values",
+ R"""({
+ "items": {
+ "type": "integer",
+ "minimum": 12,
+ "maximum": 207
+ },
+ "minItems": 3,
+ "maxItems": 5
+ })""",
+ R"""(
+ item ::= (([1] ([2-9]) | [2-9] [0-9]) | ([1] [0-9]{2} | [2] "0" [0-7])) space
+ root ::= "[" space item ("," space item){2,4} "]" space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
"simple regexp",
R"""({
"type": "string",
@@ -552,13 +848,12 @@ static void test_all(const std::string & lang, std::function<void(const TestCase
})""",
R"""(
additional-kv ::= string ":" space additional-value
- additional-kvs ::= additional-kv ( "," space additional-kv )*
additional-value ::= "[" space (number ("," space number)*)? "]" space
char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
decimal-part ::= [0-9]{1,16}
integral-part ::= [0] | [1-9] [0-9]{0,15}
number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
- root ::= "{" space (additional-kvs )? "}" space
+ root ::= "{" space (additional-kv ( "," space additional-kv )* )? "}" space
space ::= | " " | "\n" [ \t]{0,20}
string ::= "\"" char* "\"" space
)"""
@@ -635,13 +930,13 @@ static void test_all(const std::string & lang, std::function<void(const TestCase
})""",
R"""(
a-kv ::= "\"a\"" space ":" space number
- additional-kv ::= string ":" space string
- additional-kvs ::= additional-kv ( "," space additional-kv )*
+ additional-k ::= ["] ( [a] char+ | [^"a] char* )? ["] space
+ additional-kv ::= additional-k ":" space string
char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
decimal-part ::= [0-9]{1,16}
integral-part ::= [0] | [1-9] [0-9]{0,15}
number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
- root ::= "{" space a-kv ( "," space ( additional-kvs ) )? "}" space
+ root ::= "{" space a-kv ( "," space ( additional-kv ( "," space additional-kv )* ) )? "}" space
space ::= | " " | "\n" [ \t]{0,20}
string ::= "\"" char* "\"" space
)"""
@@ -659,16 +954,15 @@ static void test_all(const std::string & lang, std::function<void(const TestCase
})""",
R"""(
a-kv ::= "\"a\"" space ":" space number
- a-rest ::= additional-kvs
- additional-kv ::= string ":" space number
- additional-kvs ::= additional-kv ( "," space additional-kv )*
+ a-rest ::= ( "," space additional-kv )*
+ additional-k ::= ["] ( [a] char+ | [^"a] char* )? ["] space
+ additional-kv ::= additional-k ":" space number
char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
decimal-part ::= [0-9]{1,16}
integral-part ::= [0] | [1-9] [0-9]{0,15}
number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
- root ::= "{" space (a-kv a-rest | additional-kvs )? "}" space
+ root ::= "{" space (a-kv a-rest | additional-kv ( "," space additional-kv )* )? "}" space
space ::= | " " | "\n" [ \t]{0,20}
- string ::= "\"" char* "\"" space
)"""
});
@@ -678,25 +972,100 @@ static void test_all(const std::string & lang, std::function<void(const TestCase
R"""({
"type": "object",
"properties": {
- "a": {"type": "number"},
- "b": {"type": "number"}
+ "and": {"type": "number"},
+ "also": {"type": "number"}
},
- "required": ["a"],
+ "required": ["and"],
"additionalProperties": {"type": "number"}
})""",
R"""(
- a-kv ::= "\"a\"" space ":" space number
- additional-kv ::= string ":" space number
- additional-kvs ::= additional-kv ( "," space additional-kv )*
- b-kv ::= "\"b\"" space ":" space number
- b-rest ::= additional-kvs
+ additional-k ::= ["] ( [a] ([l] ([s] ([o] char+ | [^"o] char*) | [^"s] char*) | [n] ([d] char+ | [^"d] char*) | [^"ln] char*) | [^"a] char* )? ["] space
+ additional-kv ::= additional-k ":" space number
+ also-kv ::= "\"also\"" space ":" space number
+ also-rest ::= ( "," space additional-kv )*
+ and-kv ::= "\"and\"" space ":" space number
char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
decimal-part ::= [0-9]{1,16}
integral-part ::= [0] | [1-9] [0-9]{0,15}
number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
- root ::= "{" space a-kv ( "," space ( b-kv b-rest | additional-kvs ) )? "}" space
+ root ::= "{" space and-kv ( "," space ( also-kv also-rest | additional-kv ( "," space additional-kv )* ) )? "}" space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "optional props with empty name",
+ R"""({
+ "properties": {
+ "": {"type": "integer"},
+ "a": {"type": "integer"}
+ },
+ "additionalProperties": {"type": "integer"}
+ })""",
+ R"""(
+ -kv ::= "\"\"" space ":" space root
+ -rest ::= ( "," space a-kv )? a-rest
+ a-kv ::= "\"a\"" space ":" space integer
+ a-rest ::= ( "," space additional-kv )*
+ additional-k ::= ["] ( [a] char+ | [^"a] char* ) ["] space
+ additional-kv ::= additional-k ":" space integer
+ char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
+ integer ::= ("-"? integral-part) space
+ integral-part ::= [0] | [1-9] [0-9]{0,15}
+ root ::= ("-"? integral-part) space
+ root0 ::= "{" space (-kv -rest | a-kv a-rest | additional-kv ( "," space additional-kv )* )? "}" space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "optional props with nested names",
+ R"""({
+ "properties": {
+ "a": {"type": "integer"},
+ "aa": {"type": "integer"}
+ },
+ "additionalProperties": {"type": "integer"}
+ })""",
+ R"""(
+ a-kv ::= "\"a\"" space ":" space integer
+ a-rest ::= ( "," space aa-kv )? aa-rest
+ aa-kv ::= "\"aa\"" space ":" space integer
+ aa-rest ::= ( "," space additional-kv )*
+ additional-k ::= ["] ( [a] ([a] char+ | [^"a] char*) | [^"a] char* )? ["] space
+ additional-kv ::= additional-k ":" space integer
+ char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
+ integer ::= ("-"? integral-part) space
+ integral-part ::= [0] | [1-9] [0-9]{0,15}
+ root ::= "{" space (a-kv a-rest | aa-kv aa-rest | additional-kv ( "," space additional-kv )* )? "}" space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "optional props with common prefix",
+ R"""({
+ "properties": {
+ "ab": {"type": "integer"},
+ "ac": {"type": "integer"}
+ },
+ "additionalProperties": {"type": "integer"}
+ })""",
+ R"""(
+ ab-kv ::= "\"ab\"" space ":" space integer
+ ab-rest ::= ( "," space ac-kv )? ac-rest
+ ac-kv ::= "\"ac\"" space ":" space integer
+ ac-rest ::= ( "," space additional-kv )*
+ additional-k ::= ["] ( [a] ([b] char+ | [c] char+ | [^"bc] char*) | [^"a] char* )? ["] space
+ additional-kv ::= additional-k ":" space integer
+ char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
+ integer ::= ("-"? integral-part) space
+ integral-part ::= [0] | [1-9] [0-9]{0,15}
+ root ::= "{" space (ab-kv ab-rest | ac-kv ac-rest | additional-kv ( "," space additional-kv )* )? "}" space
space ::= | " " | "\n" [ \t]{0,20}
- string ::= "\"" char* "\"" space
)"""
});
@@ -870,26 +1239,30 @@ int main() {
}
});
- if (getenv("LLAMA_PYTHON_AVAILABLE") || (std::system("python -c \"import sys; exit(1) if sys.version_info < (3, 8) else print('Python version is sufficient')\"") == 0)) {
- test_all("Python", [](const TestCase & tc) {
- write("test-json-schema-input.tmp", tc.schema);
- tc.verify_status(std::system(
- "python ./examples/json_schema_to_grammar.py test-json-schema-input.tmp > test-grammar-output.tmp") == 0 ? SUCCESS : FAILURE);
- tc.verify(read("test-grammar-output.tmp"));
- });
+ if (getenv("LLAMA_SKIP_TESTS_SLOW_ON_EMULATOR")) {
+ fprintf(stderr, "\033[33mWARNING: Skipping slow tests on emulator.\n\033[0m");
} else {
- fprintf(stderr, "\033[33mWARNING: Python not found (min version required is 3.8), skipping Python JSON schema -> grammar tests.\n\033[0m");
- }
+ if (getenv("LLAMA_PYTHON_AVAILABLE") || (std::system("python -c \"import sys; exit(1) if sys.version_info < (3, 8) else print('Python version is sufficient')\"") == 0)) {
+ test_all("Python", [](const TestCase & tc) {
+ write("test-json-schema-input.tmp", tc.schema);
+ tc.verify_status(std::system(
+ "python ./examples/json_schema_to_grammar.py test-json-schema-input.tmp > test-grammar-output.tmp") == 0 ? SUCCESS : FAILURE);
+ tc.verify(read("test-grammar-output.tmp"));
+ });
+ } else {
+ fprintf(stderr, "\033[33mWARNING: Python not found (min version required is 3.8), skipping Python JSON schema -> grammar tests.\n\033[0m");
+ }
- if (getenv("LLAMA_NODE_AVAILABLE") || (std::system("node --version") == 0)) {
- test_all("JavaScript", [](const TestCase & tc) {
- write("test-json-schema-input.tmp", tc.schema);
- tc.verify_status(std::system(
- "node ./tests/run-json-schema-to-grammar.mjs test-json-schema-input.tmp > test-grammar-output.tmp") == 0 ? SUCCESS : FAILURE);
- tc.verify(read("test-grammar-output.tmp"));
- });
- } else {
- fprintf(stderr, "\033[33mWARNING: Node not found, skipping JavaScript JSON schema -> grammar tests.\n\033[0m");
+ if (getenv("LLAMA_NODE_AVAILABLE") || (std::system("node --version") == 0)) {
+ test_all("JavaScript", [](const TestCase & tc) {
+ write("test-json-schema-input.tmp", tc.schema);
+ tc.verify_status(std::system(
+ "node ./tests/run-json-schema-to-grammar.mjs test-json-schema-input.tmp > test-grammar-output.tmp") == 0 ? SUCCESS : FAILURE);
+ tc.verify(read("test-grammar-output.tmp"));
+ });
+ } else {
+ fprintf(stderr, "\033[33mWARNING: Node not found, skipping JavaScript JSON schema -> grammar tests.\n\033[0m");
+ }
}
test_all("Check Expectations Validity", [](const TestCase & tc) {