Skip to content

Commit 1f9e591

Browse files
fduttonFaron Dutton
andauthored
Corrects issue with deserializing JSON Schema Test Suite tests. (#805)
Resolves #804 Co-authored-by: Faron Dutton <[email protected]>
1 parent ad795de commit 1f9e591

File tree

8 files changed

+261
-114
lines changed

8 files changed

+261
-114
lines changed

src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@
1616

1717
package com.networknt.schema;
1818

19-
import com.fasterxml.jackson.core.type.TypeReference;
2019
import com.fasterxml.jackson.databind.ObjectMapper;
21-
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
2220
import com.networknt.schema.SpecVersion.VersionFlag;
21+
import com.networknt.schema.suite.TestCase;
22+
import com.networknt.schema.suite.TestSource;
23+
import com.networknt.schema.suite.TestSpec;
2324
import com.networknt.schema.uri.URITranslator;
25+
2426
import org.junit.jupiter.api.AssertionFailureBuilder;
2527
import org.junit.jupiter.api.DynamicNode;
2628
import org.opentest4j.AssertionFailedError;
2729

28-
import java.io.FileInputStream;
2930
import java.io.IOException;
30-
import java.io.InputStream;
3131
import java.io.UncheckedIOException;
3232
import java.net.URI;
3333
import java.nio.file.Files;
@@ -45,11 +45,11 @@
4545
import java.util.stream.StreamSupport;
4646

4747
import static com.networknt.schema.SpecVersionDetector.detectOptionalVersion;
48+
import static org.junit.jupiter.api.Assumptions.abort;
4849
import static org.junit.jupiter.api.DynamicContainer.dynamicContainer;
4950
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
5051

5152
public abstract class AbstractJsonSchemaTestSuite extends HTTPServiceSupport {
52-
protected static final TypeReference<List<TestCase>> testCaseType = new TypeReference<List<TestCase>>() { /* intentionally empty */};
5353
protected static final Map<String, VersionFlag> supportedVersions = new HashMap<>();
5454
static {
5555
supportedVersions.put("draft2019-09", VersionFlag.V201909);
@@ -72,24 +72,27 @@ protected boolean enabled(@SuppressWarnings("unused") Path path) {
7272
return true;
7373
}
7474

75-
protected boolean enabled(TestCase testCase) {
76-
return !testCase.isDisabled();
75+
protected Optional<String> reason(@SuppressWarnings("unused") Path path) {
76+
return Optional.empty();
7777
}
7878

79-
protected boolean enabled(TestSpec testSpec) {
80-
return !testSpec.isDisabled();
79+
private Stream<DynamicNode> buildContainers(VersionFlag defaultVersion, Path path) {
80+
boolean disabled = !enabled(path);
81+
String reason = reason(path).orElse("Unknown");
82+
return TestSource.loadFrom(path, disabled, reason)
83+
.map(testSource -> buildContainer(defaultVersion, testSource))
84+
.orElse(Stream.empty());
8185
}
8286

83-
private Stream<DynamicNode> buildContainers(VersionFlag defaultVersion, Path path) {
84-
return loadTestCases(path)
85-
.map(testCase -> buildContainer(defaultVersion, testCase));
87+
private Stream<DynamicNode> buildContainer(VersionFlag defaultVersion, TestSource testSource) {
88+
return testSource.getTestCases().stream().map(testCase -> buildContainer(defaultVersion, testCase));
8689
}
8790

8891
private DynamicNode buildContainer(VersionFlag defaultVersion, TestCase testCase) {
8992
try {
9093
JsonSchemaFactory validatorFactory = buildValidatorFactory(defaultVersion, testCase);
9194

92-
return dynamicContainer(testCase.getDisplayName(), testCase.getTests().stream().filter(this::enabled).map(testSpec -> {
95+
return dynamicContainer(testCase.getDisplayName(), testCase.getTests().stream().map(testSpec -> {
9396
return buildTest(validatorFactory, testSpec);
9497
}));
9598
} catch (JsonSchemaException e) {
@@ -102,6 +105,8 @@ private DynamicNode buildContainer(VersionFlag defaultVersion, TestCase testCase
102105
}
103106

104107
private JsonSchemaFactory buildValidatorFactory(VersionFlag defaultVersion, TestCase testCase) {
108+
if (testCase.isDisabled()) return null;
109+
105110
VersionFlag specVersion = detectVersion(testCase, defaultVersion);
106111
JsonSchemaFactory base = JsonSchemaFactory.getInstance(specVersion);
107112
return JsonSchemaFactory
@@ -115,6 +120,10 @@ private JsonSchemaFactory buildValidatorFactory(VersionFlag defaultVersion, Test
115120
}
116121

117122
private DynamicNode buildTest(JsonSchemaFactory validatorFactory, TestSpec testSpec) {
123+
if (testSpec.isDisabled()) {
124+
return dynamicTest(testSpec.getDescription(), () -> abortAndReset(testSpec.getReason()));
125+
}
126+
118127
// Configure the schemaValidator to set typeLoose's value based on the test file,
119128
// if test file do not contains typeLoose flag, use default value: false.
120129
@SuppressWarnings("deprecation") boolean typeLoose = testSpec.isTypeLoose();
@@ -161,6 +170,14 @@ private static Optional<VersionFlag> detectVersionFromPath(Path path) {
161170
.findAny();
162171
}
163172

173+
private void abortAndReset(String reason) {
174+
try {
175+
abort(reason);
176+
} finally {
177+
cleanup();
178+
}
179+
}
180+
164181
private void executeAndReset(JsonSchema schema, TestSpec testSpec) {
165182
try {
166183
executeTest(schema, testSpec);
@@ -170,6 +187,7 @@ private void executeAndReset(JsonSchema schema, TestSpec testSpec) {
170187
}
171188

172189
private static void executeTest(JsonSchema schema, TestSpec testSpec) {
190+
173191
Set<ValidationMessage> errors = schema.validate(testSpec.getData());
174192

175193
if (testSpec.isValid()) {
@@ -237,28 +255,13 @@ private static void executeTest(JsonSchema schema, TestSpec testSpec) {
237255
private List<Path> findTestCases(String basePath) {
238256
try (Stream<Path> paths = Files.walk(Paths.get(basePath))) {
239257
return paths
240-
.filter(this::enabled)
241258
.filter(path -> path.toString().endsWith(".json"))
242259
.collect(Collectors.toList());
243260
} catch (IOException e) {
244261
throw new UncheckedIOException(e);
245262
}
246263
}
247264

248-
private Stream<TestCase> loadTestCases(Path testCaseFile) {
249-
try (InputStream in = new FileInputStream(testCaseFile.toFile())) {
250-
return this.mapper.readValue(in, testCaseType)
251-
.stream()
252-
.peek(testCase -> testCase.setSpecification(testCaseFile))
253-
.filter(this::enabled);
254-
} catch (MismatchedInputException e) {
255-
System.err.append("Not a valid test case: ").println(testCaseFile);
256-
return Stream.empty();
257-
} catch (IOException e) {
258-
throw new UncheckedIOException(e);
259-
}
260-
}
261-
262265
private static Iterable<? extends DynamicNode> unsupportedMetaSchema(TestCase testCase) {
263266
return Collections.singleton(
264267
dynamicTest("Detected an unsupported schema", () -> {
Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,25 @@
11
package com.networknt.schema;
22

3-
import com.fasterxml.jackson.databind.ObjectMapper;
4-
import com.networknt.schema.SpecVersion.VersionFlag;
5-
import org.junit.jupiter.api.DisplayName;
6-
import org.junit.jupiter.api.DynamicNode;
7-
import org.junit.jupiter.api.TestFactory;
83
import java.nio.file.Path;
94
import java.nio.file.Paths;
10-
import java.util.HashSet;
11-
import java.util.Set;
5+
import java.util.HashMap;
6+
import java.util.Map;
7+
import java.util.Optional;
128
import java.util.stream.Stream;
139

10+
import org.junit.jupiter.api.DisplayName;
11+
import org.junit.jupiter.api.DynamicNode;
12+
import org.junit.jupiter.api.TestFactory;
13+
14+
import com.networknt.schema.SpecVersion.VersionFlag;
15+
1416
@DisplayName("JSON Schema Test Suite")
1517
class JsonSchemaTestSuiteTest extends AbstractJsonSchemaTestSuite {
1618

17-
private final Set<Path> disabled;
19+
private final Map<Path, String> disabled;
1820

1921
public JsonSchemaTestSuiteTest() {
20-
this.mapper = new ObjectMapper();
21-
this.disabled = new HashSet<>();
22-
23-
this.disabled.add(Paths.get("src/test/resources/data"));
24-
this.disabled.add(Paths.get("src/test/resources/issues"));
25-
this.disabled.add(Paths.get("src/test/resources/openapi3"));
26-
this.disabled.add(Paths.get("src/test/resources/remotes"));
27-
this.disabled.add(Paths.get("src/test/resources/schema"));
28-
this.disabled.add(Paths.get("src/test/resources/multipleOfScale.json")); // TODO: Used in draft7 tests
29-
this.disabled.add(Paths.get("src/test/resources/selfRef.json"));
22+
this.disabled = new HashMap<>();
3023

3124
disableV202012Tests();
3225
disableV201909Tests();
@@ -67,46 +60,51 @@ Stream<DynamicNode> draft4() {
6760

6861
@Override
6962
protected boolean enabled(Path path) {
70-
return !this.disabled.contains(path);
63+
return !this.disabled.containsKey(path);
64+
}
65+
66+
@Override
67+
protected Optional<String> reason(Path path) {
68+
return Optional.ofNullable(this.disabled.get(path));
7169
}
7270

7371
private void disableV202012Tests() {
74-
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/anchor.json"));
75-
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/defs.json"));
76-
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/dynamicRef.json"));
77-
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/id.json"));
78-
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/optional/format-assertion.json"));
79-
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/ref.json"));
80-
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/refRemote.json"));
81-
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/vocabulary.json"));
72+
this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/anchor.json"), "Unsupported behavior");
73+
this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/defs.json"), "Unsupported behavior");
74+
this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/dynamicRef.json"), "Unsupported behavior");
75+
this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/id.json"), "Unsupported behavior");
76+
this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/optional/format-assertion.json"), "Unsupported behavior");
77+
this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/ref.json"), "Unsupported behavior");
78+
this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/refRemote.json"), "Unsupported behavior");
79+
this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/vocabulary.json"), "Unsupported behavior");
8280
}
8381

8482
private void disableV201909Tests() {
85-
this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/anchor.json"));
86-
this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/defs.json"));
87-
this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/id.json"));
88-
this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/recursiveRef.json"));
89-
this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/ref.json"));
90-
this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/refRemote.json"));
91-
this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/vocabulary.json"));
83+
this.disabled.put(Paths.get("src/test/suite/tests/draft2019-09/anchor.json"), "Unsupported behavior");
84+
this.disabled.put(Paths.get("src/test/suite/tests/draft2019-09/defs.json"), "Unsupported behavior");
85+
this.disabled.put(Paths.get("src/test/suite/tests/draft2019-09/id.json"), "Unsupported behavior");
86+
this.disabled.put(Paths.get("src/test/suite/tests/draft2019-09/recursiveRef.json"), "Unsupported behavior");
87+
this.disabled.put(Paths.get("src/test/suite/tests/draft2019-09/ref.json"), "Unsupported behavior");
88+
this.disabled.put(Paths.get("src/test/suite/tests/draft2019-09/refRemote.json"), "Unsupported behavior");
89+
this.disabled.put(Paths.get("src/test/suite/tests/draft2019-09/vocabulary.json"), "Unsupported behavior");
9290
}
9391

9492
private void disableV7Tests() {
95-
this.disabled.add(Paths.get("src/test/suite/tests/draft7/anchor.json"));
96-
this.disabled.add(Paths.get("src/test/suite/tests/draft7/defs.json"));
97-
this.disabled.add(Paths.get("src/test/suite/tests/draft7/optional/content.json"));
98-
this.disabled.add(Paths.get("src/test/suite/tests/draft7/ref.json"));
99-
this.disabled.add(Paths.get("src/test/suite/tests/draft7/refRemote.json"));
93+
this.disabled.put(Paths.get("src/test/suite/tests/draft7/anchor.json"), "Unsupported behavior");
94+
this.disabled.put(Paths.get("src/test/suite/tests/draft7/defs.json"), "Unsupported behavior");
95+
this.disabled.put(Paths.get("src/test/suite/tests/draft7/optional/content.json"), "Unsupported behavior");
96+
this.disabled.put(Paths.get("src/test/suite/tests/draft7/ref.json"), "Unsupported behavior");
97+
this.disabled.put(Paths.get("src/test/suite/tests/draft7/refRemote.json"), "Unsupported behavior");
10098
}
10199

102100
private void disableV6Tests() {
103-
this.disabled.add(Paths.get("src/test/suite/tests/draft6/ref.json"));
104-
this.disabled.add(Paths.get("src/test/suite/tests/draft6/refRemote.json"));
101+
this.disabled.put(Paths.get("src/test/suite/tests/draft6/ref.json"), "Unsupported behavior");
102+
this.disabled.put(Paths.get("src/test/suite/tests/draft6/refRemote.json"), "Unsupported behavior");
105103
}
106104

107105
private void disableV4Tests() {
108-
this.disabled.add(Paths.get("src/test/suite/tests/draft4/ref.json"));
109-
this.disabled.add(Paths.get("src/test/suite/tests/draft4/refRemote.json"));
106+
this.disabled.put(Paths.get("src/test/suite/tests/draft4/ref.json"), "Unsupported behavior");
107+
this.disabled.put(Paths.get("src/test/suite/tests/draft4/refRemote.json"), "Unsupported behavior");
110108
}
111109

112110
}

src/test/java/com/networknt/schema/TestCase.java renamed to src/test/java/com/networknt/schema/suite/TestCase.java

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.networknt.schema;
1+
package com.networknt.schema.suite;
22

33
import com.fasterxml.jackson.annotation.JsonCreator;
44
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -45,9 +45,15 @@ public class TestCase {
4545
private final boolean disabled;
4646

4747
/**
48-
* The location of the specification file containing this test-case.
48+
* Describes why this test-case is disabled.
49+
* <p>
50+
* This is an extension of the schema used to describe tests in the
51+
* compliance suite
52+
* </p>
4953
*/
50-
private Path specification;
54+
private final String reason;
55+
56+
private TestSource source;
5157

5258
/**
5359
* Constructs a new TestCase
@@ -63,12 +69,14 @@ public TestCase(
6369
@JsonProperty("comment") String comment,
6470
@JsonProperty("schema") JsonNode schema,
6571
@JsonProperty("disabled") Boolean disabled,
72+
@JsonProperty("reason") String reason,
6673
@JsonProperty("tests") List<TestSpec> tests
6774
) {
6875
this.description = description;
6976
this.comment = comment;
7077
this.schema = schema;
7178
this.disabled = Boolean.TRUE.equals(disabled);
79+
this.reason = reason;
7280

7381
this.tests = tests;
7482
if (null != tests) {
@@ -81,22 +89,14 @@ public TestCase(
8189
* @return the path to the specification
8290
*/
8391
public Path getSpecification() {
84-
return specification;
85-
}
86-
87-
/**
88-
* Sets the location of the specification file containing this test-case.
89-
* @param specification the path to the specification
90-
*/
91-
public void setSpecification(Path specification) {
92-
this.specification = specification;
92+
return this.source.getPath();
9393
}
9494

9595
/**
9696
* The test case description (Required)
9797
*/
9898
public String getDescription() {
99-
return description;
99+
return this.description;
100100
}
101101

102102
public String getDisplayName() {
@@ -107,29 +107,44 @@ public String getDisplayName() {
107107
* Any additional comments about the test case
108108
*/
109109
public String getComment() {
110-
return comment;
110+
return this.comment;
111111
}
112112

113113
/**
114114
* A valid JSON Schema (one written for the corresponding version directory that
115115
* the file sits within). (Required)
116116
*/
117117
public JsonNode getSchema() {
118-
return schema;
118+
return this.schema;
119119
}
120120

121121
/**
122122
* Indicates whether this test-case should be executed
123123
*/
124124
public boolean isDisabled() {
125-
return disabled;
125+
return this.disabled || this.source.isDisabled();
126+
}
127+
128+
/**
129+
* Describes why this test is disabled.
130+
*/
131+
public String getReason() {
132+
return this.disabled ? this.reason : this.source.getReason();
126133
}
127134

128135
/**
129136
* A set of related tests all using the same schema (Required)
130137
*/
131138
public List<TestSpec> getTests() {
132-
return null != tests ? tests : Collections.emptyList();
139+
return null != this.tests ? this.tests : Collections.emptyList();
140+
}
141+
142+
public TestSource getSource() {
143+
return this.source;
144+
}
145+
146+
void setSource(TestSource source) {
147+
this.source = source;
133148
}
134149

135150
}

0 commit comments

Comments
 (0)