From 8fb109892593c6db5c89adad8cc963a947bd086a Mon Sep 17 00:00:00 2001 From: Andreas Penski <18-andreas.penski@users.noreply.projekte.kosit.org> Date: Wed, 29 Jul 2020 13:22:55 +0000 Subject: [PATCH] #55 More robust reporting in case of Schematron Error --- CHANGELOG.md | 1 + .../validationtool/impl/DefaultCheck.java | 2 +- .../validationtool/impl/DefaultResult.java | 7 +- .../impl/tasks/ComputeAcceptanceAction.java | 8 +- .../tasks/SchematronValidationAction.java | 11 +- .../validationtool/impl/DefaultCheckTest.java | 57 +++-- .../de/kosit/validationtool/impl/Helper.java | 2 + .../repository/simple-schematron-error.xsl | 235 ++++++++++++++++++ .../examples/simple/scenarios-with-errors.xml | 65 +++++ 9 files changed, 365 insertions(+), 23 deletions(-) create mode 100644 src/test/resources/examples/simple/repository/simple-schematron-error.xsl create mode 100644 src/test/resources/examples/simple/scenarios-with-errors.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 09a7f99..d5e7c79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - `getFailedAsserts()` and `isSchematronValid()` in [DefaultResult.java](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/impl/DefaultResult.java) do not reflect actual schematron validation result +- Processing aborts on schematron execution errors (e.g. errors within schematron logic). The validator now generates a report in such cases. - exception while resolving when using XSLT's `unparsed-text()` function within report generation ### Changed diff --git a/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java b/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java index e985b24..3252d76 100644 --- a/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java +++ b/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java @@ -132,7 +132,7 @@ public class DefaultCheck implements Check { result.setSchemaViolations(convertErrors(t.getSchemaValidationResult().getErrors())); } result.setProcessingSuccessful(!t.isStopped() && t.isFinished()); - result.setSchematronResult(t.getReportInput().getValidationResultsSchematron().stream() + result.setSchematronResult(t.getReportInput().getValidationResultsSchematron().stream().filter(e -> e.getResults() != null) .map(e -> e.getResults().getSchematronOutput()).collect(Collectors.toList())); return result; } diff --git a/src/main/java/de/kosit/validationtool/impl/DefaultResult.java b/src/main/java/de/kosit/validationtool/impl/DefaultResult.java index 79e79e5..693a0e9 100644 --- a/src/main/java/de/kosit/validationtool/impl/DefaultResult.java +++ b/src/main/java/de/kosit/validationtool/impl/DefaultResult.java @@ -139,8 +139,13 @@ public class DefaultResult implements Result { : Collections.emptyList(); } + private boolean isSchematronEvaluated() { + return getSchematronResult() != null + && getSchematronResult().stream().noneMatch(e -> e.getActivePatternAndFiredRuleAndFailedAssert().isEmpty()); + } + @Override public boolean isSchematronValid() { - return getSchematronResult() != null && getFailedAsserts().isEmpty(); + return isSchematronEvaluated() && getFailedAsserts().isEmpty(); } } diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java index 04ccee2..5c7afd4 100644 --- a/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java +++ b/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java @@ -24,6 +24,10 @@ public class ComputeAcceptanceAction implements CheckAction { @Override public void check(final Bag results) { + if (results.isStopped() && results.getParserResult().isValid()) { + // xml wurde aus irgendwelchen Gründen nicht korrekt verarbeitet, dann lassen wir es als undefined + return; + } if (preCondtionsMatch(results)) { final Optional acceptMatch = results.getScenarioSelectionResult().getObject().getAcceptSelector(); if (results.getSchemaValidationResult().isValid() && acceptMatch.isPresent()) { @@ -44,11 +48,11 @@ public class ComputeAcceptanceAction implements CheckAction { } } - private boolean isSchematronValid(final Bag results) { + private static boolean isSchematronValid(final Bag results) { return !hasSchematronErrors(results); } - private boolean hasSchematronErrors(final Bag results) { + private static boolean hasSchematronErrors(final Bag results) { return results.getReportInput().getValidationResultsSchematron().stream().map(e -> e.getResults().getSchematronOutput()) .flatMap(e -> e.getActivePatternAndFiredRuleAndFailedAssert().stream()).anyMatch(FailedAssert.class::isInstance); } diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/SchematronValidationAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/SchematronValidationAction.java index 8f52cfe..5b3a30d 100644 --- a/src/main/java/de/kosit/validationtool/impl/tasks/SchematronValidationAction.java +++ b/src/main/java/de/kosit/validationtool/impl/tasks/SchematronValidationAction.java @@ -35,6 +35,7 @@ import de.kosit.validationtool.impl.ConversionService; import de.kosit.validationtool.impl.Scenario; import de.kosit.validationtool.model.reportInput.CreateReportInput; import de.kosit.validationtool.model.reportInput.ValidationResultsSchematron; +import de.kosit.validationtool.model.reportInput.ValidationResultsSchematron.Results; import net.sf.saxon.dom.NodeOverNodeInfo; import net.sf.saxon.s9api.SaxonApiException; @@ -81,13 +82,21 @@ public class SchematronValidationAction implements CheckAction { s.setResults(r); } catch (final SaxonApiException e) { - final String msg = String.format("Error processing schematron validation %s", validation.getResourceType().getName()); + final String msg = String.format("Error processing schematron validation %s. Error is %s", + validation.getResourceType().getName(), e.getMessage()); log.error(msg, e); results.addProcessingError(msg); + s.setResults(createErrorResult()); } return s; } + private static Results createErrorResult() { + final Results r = new Results(); + r.setSchematronOutput(new SchematronOutput()); + return r; + } + @Override public void check(final Bag results) { final CreateReportInput report = results.getReportInput(); diff --git a/src/test/java/de/kosit/validationtool/impl/DefaultCheckTest.java b/src/test/java/de/kosit/validationtool/impl/DefaultCheckTest.java index c533eb8..d952e90 100644 --- a/src/test/java/de/kosit/validationtool/impl/DefaultCheckTest.java +++ b/src/test/java/de/kosit/validationtool/impl/DefaultCheckTest.java @@ -25,6 +25,7 @@ import static de.kosit.validationtool.impl.Helper.Simple.GARBAGE; import static de.kosit.validationtool.impl.Helper.Simple.NOT_WELLFORMED; import static de.kosit.validationtool.impl.Helper.Simple.REJECTED; import static de.kosit.validationtool.impl.Helper.Simple.SCHEMATRON_INVALID; +import static de.kosit.validationtool.impl.Helper.Simple.SIMPLE_VALID; import static de.kosit.validationtool.impl.Helper.Simple.UNKNOWN; import static org.assertj.core.api.Assertions.assertThat; @@ -52,18 +53,25 @@ public class DefaultCheckTest { public static final int MULTI_COUNT = 5; - private DefaultCheck implementation; + private DefaultCheck validCheck; + + // for checking certain error scenarios. + private DefaultCheck errorCheck; @Before public void setup() { - final CheckConfiguration d = new CheckConfiguration(Simple.SCENARIOS); - d.setScenarioRepository(new File(Simple.REPOSITORY_URI).toURI()); - this.implementation = new DefaultCheck(d); + final CheckConfiguration validConfig = new CheckConfiguration(Simple.SCENARIOS); + validConfig.setScenarioRepository(new File(Simple.REPOSITORY_URI).toURI()); + this.validCheck = new DefaultCheck(validConfig); + + final CheckConfiguration errorConfig = new CheckConfiguration(Simple.ERROR_SCENARIOS); + errorConfig.setScenarioRepository(new File(Simple.REPOSITORY_URI).toURI()); + this.errorCheck = new DefaultCheck(errorConfig); } @Test public void testHappyCase() { - final Result doc = this.implementation.checkInput(read(Simple.SIMPLE_VALID)); + final Result doc = this.validCheck.checkInput(read(SIMPLE_VALID)); assertThat(doc).isNotNull(); assertThat(doc.getReport()).isNotNull(); assertThat(doc.isAcceptable()).isTrue(); @@ -74,7 +82,7 @@ public class DefaultCheckTest { @Test public void testWithoutAcceptMatch() { - final Result doc = this.implementation.checkInput(read(Simple.FOO)); + final Result doc = this.validCheck.checkInput(read(Simple.FOO)); assertThat(doc).isNotNull(); assertThat(doc.getReport()).isNotNull(); assertThat(doc.isAcceptable()).isTrue(); @@ -83,27 +91,27 @@ public class DefaultCheckTest { @Test public void testHappyCaseDocument() { - final Document doc = this.implementation.check(read(Simple.SIMPLE_VALID)); + final Document doc = this.validCheck.check(read(SIMPLE_VALID)); assertThat(doc).isNotNull(); } @Test public void testMultipleCase() { - final List input = IntStream.range(0, MULTI_COUNT).mapToObj(i -> read(Simple.SIMPLE_VALID)).collect(Collectors.toList()); - final List docs = this.implementation.checkInput(input); + final List input = IntStream.range(0, MULTI_COUNT).mapToObj(i -> read(SIMPLE_VALID)).collect(Collectors.toList()); + final List docs = this.validCheck.checkInput(input); assertThat(docs).hasSize(MULTI_COUNT); } @Test public void testMultipleCaseDocument() { - final List input = IntStream.range(0, MULTI_COUNT).mapToObj(i -> read(Simple.SIMPLE_VALID)).collect(Collectors.toList()); - final List docs = this.implementation.check(input); + final List input = IntStream.range(0, MULTI_COUNT).mapToObj(i -> read(SIMPLE_VALID)).collect(Collectors.toList()); + final List docs = this.validCheck.check(input); assertThat(docs).hasSize(MULTI_COUNT); } @Test public void testExtractHtml() { - final DefaultResult doc = (DefaultResult) this.implementation.checkInput(read(Simple.SIMPLE_VALID)); + final DefaultResult doc = (DefaultResult) this.validCheck.checkInput(read(SIMPLE_VALID)); assertThat(doc).isNotNull(); assertThat(doc.getReport()).isNotNull(); assertThat(doc.isAcceptable()).isTrue(); @@ -114,7 +122,7 @@ public class DefaultCheckTest { @Test public void testGarbage() { - final Result result = this.implementation.checkInput(read(GARBAGE)); + final Result result = this.validCheck.checkInput(read(GARBAGE)); assertThat(result).isNotNull(); assertThat(result.isWellformed()).isFalse(); assertThat(result.isSchemaValid()).isFalse(); @@ -123,7 +131,7 @@ public class DefaultCheckTest { @Test public void testNoScenario() { - final Result result = this.implementation.checkInput(read(UNKNOWN)); + final Result result = this.validCheck.checkInput(read(UNKNOWN)); assertThat(result).isNotNull(); assertThat(result.isWellformed()).isTrue(); assertThat(result.isProcessingSuccessful()).isTrue(); @@ -134,7 +142,7 @@ public class DefaultCheckTest { @Test public void testNotWellFormed() { - final Result result = this.implementation.checkInput(read(NOT_WELLFORMED)); + final Result result = this.validCheck.checkInput(read(NOT_WELLFORMED)); assertThat(result).isNotNull(); assertThat(result.isWellformed()).isFalse(); assertThat(result.isSchemaValid()).isFalse(); @@ -146,7 +154,7 @@ public class DefaultCheckTest { @Test public void testRejectAcceptMatch() { - final Result result = this.implementation.checkInput(read(REJECTED)); + final Result result = this.validCheck.checkInput(read(REJECTED)); assertThat(result).isNotNull(); assertThat(result.isWellformed()).isTrue(); assertThat(result.isSchemaValid()).isTrue(); @@ -159,7 +167,7 @@ public class DefaultCheckTest { @Test public void testSchematronFailed() { - final Result result = this.implementation.checkInput(read(SCHEMATRON_INVALID)); + final Result result = this.validCheck.checkInput(read(SCHEMATRON_INVALID)); assertThat(result).isNotNull(); assertThat(result.isWellformed()).isTrue(); assertThat(result.isSchemaValid()).isTrue(); @@ -176,7 +184,7 @@ public class DefaultCheckTest { @Test public void testSchematronFailedWithoutAcceptMatch() { - final Result result = this.implementation.checkInput(read(FOO_SCHEMATRON_INVALID)); + final Result result = this.validCheck.checkInput(read(FOO_SCHEMATRON_INVALID)); assertThat(result).isNotNull(); assertThat(result.isWellformed()).isTrue(); assertThat(result.isSchemaValid()).isTrue(); @@ -190,4 +198,17 @@ public class DefaultCheckTest { assertThat(result.getReportDocument()).isNotNull(); } + @Test + public void testSchematronExecutionError() { + final Result result = this.errorCheck.checkInput(read(SIMPLE_VALID)); + assertThat(result).isNotNull(); + assertThat(result.isProcessingSuccessful()).isFalse(); + assertThat(result.isSchematronValid()).isFalse(); + assertThat(result.isSchemaValid()).isTrue(); + assertThat(result.getAcceptRecommendation()).isEqualTo(AcceptRecommendation.UNDEFINED); + assertThat(result.isAcceptable()).isFalse(); + assertThat(result.getReport()).isNotNull(); + assertThat(result.getProcessingErrors()).hasSize(1); + } + } diff --git a/src/test/java/de/kosit/validationtool/impl/Helper.java b/src/test/java/de/kosit/validationtool/impl/Helper.java index ed5e13c..57a5a5a 100644 --- a/src/test/java/de/kosit/validationtool/impl/Helper.java +++ b/src/test/java/de/kosit/validationtool/impl/Helper.java @@ -64,6 +64,8 @@ public class Helper { public static final URI SCENARIOS = ROOT.resolve("scenarios.xml"); + public static final URI ERROR_SCENARIOS = ROOT.resolve("scenarios-with-errors.xml"); + public static final URI REPOSITORY_URI = ROOT.resolve("repository/"); public static final URI SCHEMA_INVALID = ROOT.resolve("input/simple-schema-invalid.xml"); diff --git a/src/test/resources/examples/simple/repository/simple-schematron-error.xsl b/src/test/resources/examples/simple/repository/simple-schematron-error.xsl new file mode 100644 index 0000000..03fd1db --- /dev/null +++ b/src/test/resources/examples/simple/repository/simple-schematron-error.xsl @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + / + + + + + + *: + + [namespace-uri()=' + + '] + + + + [ + + ] + + + + / + + @ + + + @*[local-name()=' + + ' and namespace-uri()=' + + '] + + + + + + + + + / + + + [ + + ] + + + + /@ + + + + + + + / + + + [ + + ] + + + + /@ + + + + + + + + + + + + + + + + + + + + + + + + . + + + + + U + + U + + + + U. + + n + + + + U. + + _ + + _ + + + + + + + + + +   +   +   + + + + + + + + + + + + + + + + Schematron Simple + + + + + + + + + + + + + + + + + content-1 + + + + The element inner appears exactly once. + + + + + + + + + + + + + + + + content-2 + + + + The element inner appears exactly once. + + + + + + + + + + diff --git a/src/test/resources/examples/simple/scenarios-with-errors.xml b/src/test/resources/examples/simple/scenarios-with-errors.xml new file mode 100644 index 0000000..f385150 --- /dev/null +++ b/src/test/resources/examples/simple/scenarios-with-errors.xml @@ -0,0 +1,65 @@ + + + + + HTML-TestSuite + 2017-08-08 + +

Szenario für Tests mit Fehlern

+
+ + + Simple + +

Schematron-Fehler

+
+ http://www.xoev.de/de/validator/framework/1/createreportinput + http://validator.kosit.de/test-sample + http://validator.kosit.de/test-report + /test:simple + + + + Sample Schema + simple.xsd + + + + + Sample Schematron + simple-schematron-error.xsl + + + + + Report für eRechnung + report.xsl + + +
+ + + + default + report.xsl + + + +