Skip to content

Commit

Permalink
Fix oneOf bug reported in #13.
Browse files Browse the repository at this point in the history
  • Loading branch information
leadpony committed Apr 12, 2019
1 parent 3f84c04 commit 6431f50
Show file tree
Hide file tree
Showing 64 changed files with 5,756 additions and 539 deletions.
2 changes: 1 addition & 1 deletion justify/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<jsonb.impl.groupId>org.eclipse</jsonb.impl.groupId>
<jsonb.impl.artifactId>yasson</jsonb.impl.artifactId>
<jsonb.impl.version>1.0.2</jsonb.impl.version>
<junit.jupiter.version>5.4.0</junit.jupiter.version>
<junit.jupiter.version>5.4.1</junit.jupiter.version>
<test.log.level>OFF</test.log.level>
</properties>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class ExclusiveEvaluator extends AbstractExclusiveEvaluator {

@Override
public Result evaluate(Event event, int depth, ProblemDispatcher dispatcher) {
if (evaluationsAsTrue == 0) {
if (evaluationsAsTrue <= 1) {
evaluateAll(event, depth, dispatcher);
}
evaluateAllNegated(event, depth, dispatcher);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
/*
* Copyright 2018-2019 the Justify authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.leadpony.justify.api;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;

import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonBuilderFactory;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonValue;
import javax.json.stream.JsonParser;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.TestInfo;
import org.leadpony.justify.internal.annotation.Spec;

/**
* A test type for official test suite.
*
* @author leadpony
*/
public abstract class AbstractOfficialTest {

/**
* A test fixture for official test suite.
*
* @author leadpony
*/
public static class Fixture {

private final String name;
private final int index;
private final JsonValue schema;
private final JsonValue data;
private final String description;
private final boolean result;

Fixture(String name,
int index,
JsonValue schema,
JsonValue data,
String description,
boolean result) {
this.name = name;
this.index = index;
this.schema = schema;
this.data = data;
this.description = description;
this.result = result;
}

JsonValue getSchema() {
return schema;
}

JsonValue getData() {
return data;
}

boolean getResult() {
return result;
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
int beginIndex = name.lastIndexOf('/') + 1;
int endIndex = name.lastIndexOf('.');
builder.append(name.substring(beginIndex, endIndex))
.append("[").append(index).append("]")
.append(" ").append(description);
return builder.toString();
}
}

static final Logger log = Logger.getLogger(AbstractOfficialTest.class.getName());

static final JsonBuilderFactory jsonBuilderFactory = Json.createBuilderFactory(null);
static final JsonValidationService service = JsonValidationServices.get();
static final ProblemHandler printer = service.createProblemPrinter(log::info);

@SuppressWarnings("serial")
private static final Map<SpecVersion, String> basePaths = new EnumMap<SpecVersion, String>(SpecVersion.class) {{
put(SpecVersion.DRAFT_06, "/org/json_schema/tests/draft6/");
put(SpecVersion.DRAFT_07, "/org/json_schema/tests/draft7/");
}};

private static SpecVersion specVersion;
private static String basePath;
private static JsonSchemaReaderFactory schemaReaderFactory;

private static JsonValue lastValue;
private static JsonSchema lastSchema;

@BeforeAll
public static void setUpOnce(TestInfo testInfo) {
Class<?> testClass = testInfo.getTestClass().get();
Spec spec = testClass.getAnnotation(Spec.class);
specVersion = spec.value()[0];
basePath = basePaths.get(specVersion);
schemaReaderFactory = service.createSchemaReaderFactoryBuilder()
.withSpecVersion(specVersion)
.withSchemaResolver(new LocalSchemaResolver())
.build();
}

public static Stream<Fixture> generateFixtures(String... files) {
return Stream.of(files).flatMap(AbstractOfficialTest::readFixtures);
}

public void test(Fixture fixture) {
JsonSchema schema = getSchema(fixture.getSchema());
JsonValue data = fixture.getData();

List<Problem> problems = new ArrayList<>();

JsonParser parser = createValidator(data, schema, problems::addAll);
while (parser.hasNext()) {
parser.next();
}
parser.close();

assertThat(problems.isEmpty()).isEqualTo(fixture.getResult());
checkProblems(problems);
printProblems(fixture, problems);
}

public void testNegated(Fixture fixture) {
JsonSchema schema = getNegatedSchema(fixture.getSchema());
JsonValue data = fixture.getData();

List<Problem> problems = new ArrayList<>();

JsonParser parser = createValidator(data, schema, problems::addAll);
while (parser.hasNext()) {
parser.next();
}
parser.close();

assertThat(problems.isEmpty()).isEqualTo(!fixture.getResult());
checkProblems(problems);
printProblems(fixture, problems);
}

private JsonSchema getSchema(JsonValue value) {
if (value == lastValue) {
return lastSchema;
}
JsonSchema schema = readSchema(value);
lastValue = value;
lastSchema = schema;
return schema;
}

private JsonSchema getNegatedSchema(JsonValue value) {
JsonSchema schema = getSchema(value);
return service.createSchemaBuilderFactory()
.createBuilder()
.withNot(schema)
.build();
}

private JsonSchema readSchema(JsonValue value) {
StringReader reader = new StringReader(value.toString());
try (JsonSchemaReader schemaReader = createSchemaReader(reader)) {
return schemaReader.read();
}
}

private JsonSchemaReader createSchemaReader(Reader reader) {
return schemaReaderFactory.createSchemaReader(reader);
}

private JsonParser createValidator(JsonValue data, JsonSchema schema, ProblemHandler handler) {
StringReader reader = new StringReader(data.toString());
return service.createParser(reader, schema, handler);
}

private static Stream<Fixture> readFixtures(String name) {
Function<JsonObject, Stream<Fixture>> mapper = new Function<JsonObject, Stream<Fixture>>() {
int index;
@Override
public Stream<Fixture> apply(JsonObject schema) {
return schema.getJsonArray("tests").stream()
.map(JsonValue::asJsonObject)
.map(test->new Fixture(
name,
index++,
schema.getValue("/schema"),
test.get("data"),
test.getString("description"),
test.getBoolean("valid")
));
}
};
return readJsonArray(name).stream()
.map(JsonValue::asJsonObject)
.flatMap(mapper);
}

private static JsonArray readJsonArray(String name) {
name = resolveResource(name);
try (JsonReader reader = Json.createReader(openResource(name))) {
return reader.readArray();
}
}

private static String resolveResource(String name) {
if (name.startsWith("/")) {
return name;
} else {
return basePath + name;
}
}

private static void checkProblems(List<Problem> problems) {
for (Problem problem : problems) {
assertThat(problem.getSchema()).isNotNull();
if (problem.hasBranches()) {
int index = 0;
while (index < problem.countBranches()) {
checkProblems(problem.getBranch(index++));
}
} else {
assertThat(problem.getLocation()).isNotNull();
assertThat(problem.getPointer()).isNotNull();
}
}
}

private void printProblems(Fixture fixture, List<Problem> problems) {
if (problems.isEmpty() || !log.isLoggable(Level.INFO)) {
return;
}
StringBuilder builder = new StringBuilder("- ");
builder.append(fixture.toString());
log.info(builder.toString());
printer.handleProblems(problems);
log.info("");
}

private static InputStream openResource(String name) {
return AbstractOfficialTest.class.getResourceAsStream(name);
}

private static class LocalSchemaResolver implements JsonSchemaResolver {

private static final String BASE_PATH = "/org/json_schema/remotes";

@Override
public JsonSchema resolveSchema(URI id) {
String path = BASE_PATH + id.getPath();
try (JsonSchemaReader reader = service.createSchemaReader(openResource(path))) {
return reader.read();
}
}
}
}
Loading

0 comments on commit 6431f50

Please sign in to comment.