Skip to content

Commit 51668d0

Browse files
authored
Make --continue/-c govern every input shape in validate (#846)
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent 9e5ea4e commit 51668d0

11 files changed

Lines changed: 330 additions & 13 deletions

docs/validate.markdown

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ To help scripts distinguish validation errors, these are reported using exit
3737
code 2.
3838

3939
> [!NOTE]
40-
> When validating JSONL datasets, the command stops at the first entry that
41-
> fails validation. Pass `--continue`/`-c` to report all failing entries
42-
> instead.
40+
> The command stops at the first instance that fails validation, whether that
41+
> instance is a file, an entry of a JSONL dataset, or one of many arguments.
42+
> Pass `--continue`/`-c` to report every failing instance instead.
4343
4444
> [!TIP]
4545
> GZIP-compressed JSONL datasets (`.jsonl.gz`) are transparently decompressed
@@ -174,16 +174,16 @@ jsonschema validate path/to/my/schema.json path/to/my/dataset.jsonl
174174
jsonschema validate path/to/my/schema.json path/to/my/dataset.jsonl.gz
175175
```
176176

177-
### Validate a JSONL dataset reporting all failures
177+
### Report every failing instance instead of stopping at the first
178178

179179
```sh
180180
jsonschema validate path/to/my/schema.json path/to/my/dataset.jsonl --continue
181181
```
182182

183-
Note that even with `--continue`, only the first error of each failing entry is
184-
reported. Continuing validation past the first error within a single entry is a
185-
gray area not covered by the JSON Schema specification and potentially tricky to
186-
implement correctly at the evaluation level.
183+
Note that even with `--continue`, only the first error of each failing instance
184+
is reported. Continuing validation past the first error within a single instance
185+
is a gray area not covered by the JSON Schema specification and potentially
186+
tricky to implement correctly at the evaluation level.
187187

188188
### Validate a JSON instance enabling HTTP resolution
189189

src/command_validate.cc

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ auto process_entry(const sourcemeta::jsonschema::InputJSON &entry,
171171
}
172172

173173
if (benchmark) {
174-
return true;
174+
return subresult || continue_on_error;
175175
}
176176

177177
if (trace) {
@@ -192,7 +192,7 @@ auto process_entry(const sourcemeta::jsonschema::InputJSON &entry,
192192
std::cout << "\n";
193193
if (!suboutput.at("valid").to_boolean()) {
194194
result = false;
195-
if (entry.multidocument && !continue_on_error) {
195+
if (!continue_on_error) {
196196
return false;
197197
}
198198
}
@@ -223,7 +223,7 @@ auto process_entry(const sourcemeta::jsonschema::InputJSON &entry,
223223
}
224224
sourcemeta::jsonschema::print(output, entry.positions, std::cerr);
225225
result = false;
226-
if (entry.multidocument && !continue_on_error) {
226+
if (!continue_on_error) {
227227
return false;
228228
}
229229
}
@@ -376,6 +376,7 @@ auto sourcemeta::jsonschema::validate(const sourcemeta::core::Options &options)
376376
}
377377
}
378378
} else {
379+
bool proceed{true};
379380
for (const auto &instance_path_view : instance_arguments) {
380381
const std::filesystem::path instance_path{instance_path_view};
381382
if (trace && (instance_path.extension() == ".jsonl" ||
@@ -407,6 +408,7 @@ auto sourcemeta::jsonschema::validate(const sourcemeta::core::Options &options)
407408
benchmark_loop, trace, fast_mode, json_output,
408409
continue_on_error, schema_resolution_base, options,
409410
result)) {
411+
proceed = false;
410412
break;
411413
}
412414
}
@@ -458,6 +460,7 @@ auto sourcemeta::jsonschema::validate(const sourcemeta::core::Options &options)
458460
assert(suboutput.at("valid").is_boolean());
459461
if (!suboutput.at("valid").to_boolean()) {
460462
result = false;
463+
proceed = continue_on_error;
461464
}
462465

463466
sourcemeta::core::prettify(suboutput, std::cout);
@@ -476,8 +479,13 @@ auto sourcemeta::jsonschema::validate(const sourcemeta::core::Options &options)
476479
<< "\n";
477480
print(output, tracker, std::cerr);
478481
result = false;
482+
proceed = continue_on_error;
479483
}
480484
}
485+
486+
if (!proceed) {
487+
break;
488+
}
481489
}
482490
}
483491

test/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ add_jsonschema_test(validate/pass_directory)
134134
add_jsonschema_test(validate/pass_directory_verbose)
135135
add_jsonschema_test(validate/fail_directory)
136136
add_jsonschema_test(validate/fail_directory_verbose)
137+
add_jsonschema_test(validate/fail_directory_stop_verbose)
138+
add_jsonschema_test(validate/fail_directory_stop_json)
139+
add_jsonschema_test(validate/fail_directory_continue_verbose)
140+
add_jsonschema_test(validate/fail_directory_continue_json)
137141
add_jsonschema_test(validate/fail_directory_json)
138142
add_jsonschema_test(validate/pass_directory_extension)
139143
add_jsonschema_test(validate/pass_directory_extension_verbose)
@@ -244,6 +248,7 @@ add_jsonschema_test(validate/pass_many)
244248
add_jsonschema_test(validate/pass_many_verbose)
245249
add_jsonschema_test(validate/fail_many)
246250
add_jsonschema_test(validate/fail_many_verbose)
251+
add_jsonschema_test(validate/fail_many_continue_verbose)
247252
add_jsonschema_test(validate/fail_yaml)
248253
add_jsonschema_test(validate/pass_yaml_multi)
249254
add_jsonschema_test(validate/pass_yaml_multi_verbose)
@@ -292,6 +297,8 @@ add_jsonschema_test(validate/pass_benchmark)
292297
add_jsonschema_test(validate/fail_benchmark)
293298
add_jsonschema_test(validate/fail_benchmark_multiple)
294299
add_jsonschema_test(validate/fail_benchmark_directory)
300+
add_jsonschema_test(validate/fail_benchmark_jsonl)
301+
add_jsonschema_test(validate/fail_benchmark_jsonl_continue)
295302
add_jsonschema_test(validate/pass_benchmark_loop)
296303
add_jsonschema_test(validate/pass_benchmark_loop_jsonl)
297304
add_jsonschema_test(validate/fail_benchmark_zero)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
WRITE schema.json UNTIL EOF
2+
{
3+
"$schema": "https://json-schema.org/draft/2020-12/schema",
4+
"title": "Test",
5+
"description": "Test schema",
6+
"type": "string"
7+
}
8+
EOF
9+
10+
WRITE instance.jsonl UNTIL EOF
11+
"Hello World!"
12+
1
13+
"Bonjour tout le monde!"
14+
EOF
15+
16+
// Validation failure
17+
RUN validate schema.json instance.jsonl --benchmark STDIN /dev/null IN . INTO result_0.txt EXPECTING 2
18+
19+
REPLACE $CWD WITH '[CWD]' IN result_0.txt
20+
REPLACE MATCHING '[0-9]+\.[0-9]+ \+- [0-9]+\.[0-9]+ us \([0-9]+\.[0-9]+\)' WITH '[TIMING]' IN result_0.txt
21+
22+
WRITE expected_0.txt UNTIL EOF
23+
1> [CWD]/instance.jsonl[1]: PASS [TIMING]
24+
1> [CWD]/instance.jsonl[2]: FAIL [TIMING]
25+
EOF
26+
27+
COMPARE result_0.txt AGAINST expected_0.txt
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
WRITE schema.json UNTIL EOF
2+
{
3+
"$schema": "https://json-schema.org/draft/2020-12/schema",
4+
"title": "Test",
5+
"description": "Test schema",
6+
"type": "string"
7+
}
8+
EOF
9+
10+
WRITE instance.jsonl UNTIL EOF
11+
"Hello World!"
12+
1
13+
"Bonjour tout le monde!"
14+
EOF
15+
16+
// Validation failure
17+
RUN validate schema.json instance.jsonl --benchmark --continue STDIN /dev/null IN . INTO result_0.txt EXPECTING 2
18+
19+
REPLACE $CWD WITH '[CWD]' IN result_0.txt
20+
REPLACE MATCHING '[0-9]+\.[0-9]+ \+- [0-9]+\.[0-9]+ us \([0-9]+\.[0-9]+\)' WITH '[TIMING]' IN result_0.txt
21+
22+
WRITE expected_0.txt UNTIL EOF
23+
1> [CWD]/instance.jsonl[1]: PASS [TIMING]
24+
1> [CWD]/instance.jsonl[2]: FAIL [TIMING]
25+
1> [CWD]/instance.jsonl[3]: PASS [TIMING]
26+
EOF
27+
28+
COMPARE result_0.txt AGAINST expected_0.txt
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
MAKE DIRECTORY instances
2+
3+
WRITE schema.json UNTIL EOF
4+
{
5+
"$schema": "http://json-schema.org/draft-04/schema#",
6+
"title": "Test",
7+
"description": "Test schema",
8+
"type": "object",
9+
"properties": {
10+
"name": {
11+
"type": "string"
12+
},
13+
"age": {
14+
"type": "integer"
15+
}
16+
}
17+
}
18+
EOF
19+
20+
WRITE instances/instance_1.json UNTIL EOF
21+
{ "name": "Bob", "age": "invalid" }
22+
EOF
23+
24+
WRITE instances/instance_2.json UNTIL EOF
25+
{ "name": "Alice", "age": 30 }
26+
EOF
27+
28+
// Validation failure
29+
RUN validate schema.json instances --json --continue STDIN /dev/null IN . INTO result_0.txt EXPECTING 2
30+
31+
REPLACE $CWD_URI WITH '[CWD_URI]' IN result_0.txt
32+
REPLACE $CWD WITH '[CWD]' IN result_0.txt
33+
34+
WRITE expected_0.txt UNTIL EOF
35+
1> {
36+
1> "valid": false,
37+
1> "errors": [
38+
1> {
39+
1> "keywordLocation": "/properties/age/type",
40+
1> "absoluteKeywordLocation": "[CWD_URI]/schema.json#/properties/age/type",
41+
1> "instanceLocation": "/age",
42+
1> "instancePosition": [ 1, 18, 1, 33 ],
43+
1> "error": "The value was expected to be of type integer but it was of type string"
44+
1> },
45+
1> {
46+
1> "keywordLocation": "/properties",
47+
1> "absoluteKeywordLocation": "[CWD_URI]/schema.json#/properties",
48+
1> "instanceLocation": "",
49+
1> "instancePosition": [ 1, 1, 1, 35 ],
50+
1> "error": "The object value was expected to validate against the defined properties subschemas"
51+
1> }
52+
1> ]
53+
1> }
54+
1> {
55+
1> "valid": true
56+
1> }
57+
2> [CWD]/instances/instance_1.json
58+
2> [CWD]/instances/instance_2.json
59+
EOF
60+
61+
COMPARE result_0.txt AGAINST expected_0.txt
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
MAKE DIRECTORY instances
2+
3+
WRITE schema.json UNTIL EOF
4+
{
5+
"$schema": "http://json-schema.org/draft-07/schema#",
6+
"title": "Test",
7+
"description": "Test schema",
8+
"type": "object",
9+
"properties": {
10+
"foo": {
11+
"type": "string"
12+
}
13+
}
14+
}
15+
EOF
16+
17+
WRITE instances/instance_1.json UNTIL EOF
18+
{ "foo": 1 }
19+
EOF
20+
21+
WRITE instances/instance_2.json UNTIL EOF
22+
{ "foo": "bar" }
23+
EOF
24+
25+
// Validation failure
26+
RUN validate schema.json instances --continue --verbose STDIN /dev/null IN . INTO result_0.txt EXPECTING 2
27+
28+
REPLACE $CWD WITH '[CWD]' IN result_0.txt
29+
30+
WRITE expected_0.txt UNTIL EOF
31+
2> fail: [CWD]/instances/instance_1.json
32+
2> error: Schema validation failure
33+
2> The value was expected to be of type string but it was of type integer
34+
2> at instance location "/foo" (line 1, column 3)
35+
2> at evaluate path "/properties/foo/type"
36+
2> The object value was expected to validate against the single defined property subschema
37+
2> at instance location "" (line 1, column 1)
38+
2> at evaluate path "/properties"
39+
2> ok: [CWD]/instances/instance_2.json
40+
2> matches [CWD]/schema.json
41+
EOF
42+
43+
COMPARE result_0.txt AGAINST expected_0.txt
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
MAKE DIRECTORY instances
2+
3+
WRITE schema.json UNTIL EOF
4+
{
5+
"$schema": "http://json-schema.org/draft-04/schema#",
6+
"title": "Test",
7+
"description": "Test schema",
8+
"type": "object",
9+
"properties": {
10+
"name": {
11+
"type": "string"
12+
},
13+
"age": {
14+
"type": "integer"
15+
}
16+
}
17+
}
18+
EOF
19+
20+
WRITE instances/instance_1.json UNTIL EOF
21+
{ "name": "Bob", "age": "invalid" }
22+
EOF
23+
24+
WRITE instances/instance_2.json UNTIL EOF
25+
{ "name": "Alice", "age": 30 }
26+
EOF
27+
28+
// Validation failure
29+
RUN validate schema.json instances --json STDIN /dev/null IN . INTO result_0.txt EXPECTING 2
30+
31+
REPLACE $CWD_URI WITH '[CWD_URI]' IN result_0.txt
32+
REPLACE $CWD WITH '[CWD]' IN result_0.txt
33+
34+
WRITE expected_0.txt UNTIL EOF
35+
1> {
36+
1> "valid": false,
37+
1> "errors": [
38+
1> {
39+
1> "keywordLocation": "/properties/age/type",
40+
1> "absoluteKeywordLocation": "[CWD_URI]/schema.json#/properties/age/type",
41+
1> "instanceLocation": "/age",
42+
1> "instancePosition": [ 1, 18, 1, 33 ],
43+
1> "error": "The value was expected to be of type integer but it was of type string"
44+
1> },
45+
1> {
46+
1> "keywordLocation": "/properties",
47+
1> "absoluteKeywordLocation": "[CWD_URI]/schema.json#/properties",
48+
1> "instanceLocation": "",
49+
1> "instancePosition": [ 1, 1, 1, 35 ],
50+
1> "error": "The object value was expected to validate against the defined properties subschemas"
51+
1> }
52+
1> ]
53+
1> }
54+
2> [CWD]/instances/instance_1.json
55+
EOF
56+
57+
COMPARE result_0.txt AGAINST expected_0.txt
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
MAKE DIRECTORY instances
2+
3+
WRITE schema.json UNTIL EOF
4+
{
5+
"$schema": "http://json-schema.org/draft-07/schema#",
6+
"title": "Test",
7+
"description": "Test schema",
8+
"type": "object",
9+
"properties": {
10+
"foo": {
11+
"type": "string"
12+
}
13+
}
14+
}
15+
EOF
16+
17+
WRITE instances/instance_1.json UNTIL EOF
18+
{ "foo": 1 }
19+
EOF
20+
21+
WRITE instances/instance_2.json UNTIL EOF
22+
{ "foo": "bar" }
23+
EOF
24+
25+
// Validation failure
26+
RUN validate schema.json instances --verbose STDIN /dev/null IN . INTO result_0.txt EXPECTING 2
27+
28+
REPLACE $CWD WITH '[CWD]' IN result_0.txt
29+
30+
WRITE expected_0.txt UNTIL EOF
31+
2> fail: [CWD]/instances/instance_1.json
32+
2> error: Schema validation failure
33+
2> The value was expected to be of type string but it was of type integer
34+
2> at instance location "/foo" (line 1, column 3)
35+
2> at evaluate path "/properties/foo/type"
36+
2> The object value was expected to validate against the single defined property subschema
37+
2> at instance location "" (line 1, column 1)
38+
2> at evaluate path "/properties"
39+
EOF
40+
41+
COMPARE result_0.txt AGAINST expected_0.txt

0 commit comments

Comments
 (0)