From f2223552ad74a6cc1f1065aa1a5f542d3a9b1dfe Mon Sep 17 00:00:00 2001 From: Andreas Penski <18-andreas.penski@users.noreply.projekte.kosit.org> Date: Wed, 2 Sep 2020 12:34:20 +0000 Subject: [PATCH] #67 [CLI,DAEMON] Return proper return codes / status codes --- .gitlab-ci.yml | 31 +--- CHANGELOG.md | 5 +- README.md | 47 +++-- docs/cli.md | 46 +++++ docs/daemon.md | 17 +- pom.xml | 2 - .../cmd/CommandLineApplication.java | 21 +-- .../cmd/CommandLineOptions.java | 1 - .../validationtool/cmd/InternalCheck.java | 19 +- .../kosit/validationtool/cmd/ReturnValue.java | 28 +++ .../kosit/validationtool/cmd/Validator.java | 165 +++++++++--------- .../validationtool/daemon/BaseHandler.java | 13 +- .../validationtool/daemon/CheckHandler.java | 26 ++- .../validationtool/daemon/HttpStatus.java | 32 ++++ .../validationtool/impl/DefaultCheck.java | 3 +- .../cmd/CheckAssertionActionTest.java | 6 +- .../kosit/validationtool/cmd/CommandLine.java | 137 +++++++++------ .../cmd/CommandlineApplicationTest.java | 56 +++--- .../cmd/PrintReportActionTest.java | 10 +- .../validationtool/daemon/CheckHandlerIT.java | 3 +- .../daemon/ConfigHandlerTest.java | 4 +- 21 files changed, 424 insertions(+), 248 deletions(-) create mode 100644 docs/cli.md create mode 100644 src/main/java/de/kosit/validationtool/cmd/ReturnValue.java create mode 100644 src/main/java/de/kosit/validationtool/daemon/HttpStatus.java diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2985761..039bbdd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -27,37 +27,10 @@ build-java-14: script: - mvn $MAVEN_CLI_OPTS $BUILD_PROPS $CI_JOB_TIMESTAMP verify artifacts: + when: on_failure name: java-14 paths: - - target/*.jar - reports: - junit: - - target/surefire-reports/*.xml - - target/failsafe-reports/*.xml - -build-java-13: - stage: build - image: maven:3-jdk-13 - script: - - mvn $MAVEN_CLI_OPTS $BUILD_PROPS $CI_JOB_TIMESTAMP verify - artifacts: - name: java-13 - paths: - - target/*.jar - reports: - junit: - - target/surefire-reports/*.xml - - target/failsafe-reports/*.xml - -build-java-12: - stage: build - image: maven:3-jdk-12 - script: - - mvn $MAVEN_CLI_OPTS $BUILD_PROPS $CI_JOB_TIMESTAMP verify - artifacts: - name: java-12 - paths: - - target/*.jar + - target/* reports: junit: - target/surefire-reports/*.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index f85f4b1..3b9af62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,11 +15,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [CLI] custom output without the various log messages - [CLI] options to set the log level (`-X` = full debug output, `-l ` set a specific level) - [CLI] return code ist <> 0 on rejected results +- [CLI] read (single) test target from stdin +- [DAEMON] name inputs via request URI (see [daemon documentation](./docs/daemon.md#status-codes)) ### Changed - InputFactory has methods to read any java.xml.transform.Source as Input not only StreamSources - InputFactory uses a generated UUID as name for SourceInput, if no "real" name can be derived - saxon dependency update (minor, 9.9.1-7) +- [DAEMON] proper status codes when returning results ## 1.3.1 ### Fixed @@ -29,7 +32,7 @@ do not reflect actual schematron validation result - exception while resolving when using XSLT's `unparsed-text()` function within report generation ### Added -- [CLI] add summary report +- [CLI] summary report ### Changed - engine info contains version number of the validator (configurations can output this in the report for maintainance puposes) diff --git a/README.md b/README.md index be743c4..6b91c6c 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,34 @@ -# Validator +# KoSIT Validator +- [Introduction](#introduction) +- [Validation Configurations](#validation-configurations) + * [Third Party Validation Configurations](#third-party-validation-configurations) +- [Usage](#usage) + * [Standalone Command-Line Interface](#standalone-command-line-interface) + * [Application User Interface (API / embedded usage)](#application-user-interface--api---embedded-usage-) + * [Daemon-Mode](#daemon-mode) +- [Packages](#packages) -The validator is an XML validation-engine. It validates XML documents against XML Schema and Schematron Rules depending on self defined [scenarios](docs/configurations.md) which are used to fully configure the validation process. -The validator always outputs a [validation report in XML](docs/configurations.md#validators-report) including all validation errors and data about the validation. +## Introduction +The validator is an XML validation engine to validate and process XML files in various formats. It basically does the following in order: -See [architecture](docs/architecture.md) for informations about the actual validation process. +1. identify actual xml format +1. validate the xml file (using schema and schematron rules) +1. generate a custom report / extract custom data from the xml file +1. compute an acceptance status (according the supplied schema and rules) -## Packages +The validator depends on self defined [scenarios](docs/configurations.md) which are used to fully configure the process. +It always creates a [validation report in XML](docs/configurations.md#validators-report). The actual content of this is controlled by the scenario. -The validator distribution contains the following artifacts: - -1. **validationtool-``.jar**: Java library for embedded use within an application -1. **validationtool-`-standalone.jar**: Uber-JAR for standalone usage containing all dependencies in one jar file. This file comes with JAXB *embedded* and can be used with Java 8 and Java >= 11) -1. **validationtool-`-java8-standalone.jar**: Uber-JAR for standalone usage with Java JDK 8 containing all dependencies in one jar file. This file file *does not* contain JAXB and depends on the bundled version of the JDK. -1. **libs/***: directory containing all (incl. optional) dependencies of the validator +See [architecture](docs/architecture.md) for information about the actual validation process. -## Validation Configurations +## Validation configurations -The validator is just an engine and does not know anything about XML Documents and has no own validation rules. +The validator is just an engine and does not know anything about XML documents and has no own validation rules. Validation rules and details are defined in [validation scenarios](docs/configurations.md) which are used to fully configure the validation process. All configurations are self-contained modules which are deployed and developed on their own. -### Third Party Validation Configurations +### Third party validation configurations Currently, there are two public third party validation configurations available. @@ -34,7 +41,7 @@ Currently, there are two public third party validation configurations available. ## Usage -The validator is designed to be used in three different ways: +The validator can be used in three different ways: * as standalone application running from the cli * as library embedded within a custom application @@ -57,6 +64,8 @@ java -jar validationtool--standalone.jar --help A concrete example with a specific validator configuration can be found on [GitHub](https://github.com/itplr-kosit/validator-configuration-xrechnung) +The [CLI documentation](./docs/cli.md) shows further configuration options. + ### Application User Interface (API / embedded usage) The validator can also be used in own Java Applications via the API. An example use of the API as follows: @@ -85,3 +94,11 @@ java -jar validationtool--standalone.jar -s -D The [daemon documentation](./docs/daemon.md) shows more usage details and further configuration options. +## Packages + +The validator distribution contains the following artifacts: + +1. **validationtool-``.jar**: Java library for embedded use within an application +1. **validationtool-`-standalone.jar**: Uber-JAR for standalone usage containing all dependencies in one jar file. This file comes with JAXB *embedded* and can be used with Java 8 and Java >= 11) +1. **validationtool-`-java8-standalone.jar**: Uber-JAR for standalone usage with Java JDK 8 containing all dependencies in one jar file. This file file *does not* contain JAXB and depends on the bundled version of the JDK. +1. **libs/***: directory containing all (incl. optional) dependencies of the validator \ No newline at end of file diff --git a/docs/cli.md b/docs/cli.md new file mode 100644 index 0000000..8287f81 --- /dev/null +++ b/docs/cli.md @@ -0,0 +1,46 @@ +# Validator CLI + +The validator comes with a commandline interface (CLI) which allows validating any number of input xml files. + +The general way using the CLI is: + +```shell +java -jar validationtool--standalone.jar -s [OPTIONS] [FILE] [FILE] [FILE] ... +``` + +The validator can also read the xml file from the standard input + +```shell script +# via redirection +java -jar validationtool--standalone.jar -s [OPTIONS] < my-input.xml + +# read from pipe +cat my-input.xml | validationtool--standalone.jar -s [OPTIONS] +``` + +The help option displays further CLI options to customize the process: + +```shell +java -jar validationtool--standalone.jar --help +``` + +## Special features +Besides the obvious functionality of validating, the cli provides additional functionality to customize the processing: + +|name | option | description | +| - | - | - | +| [Daemon mode](daemon.md) | `-D` | Starts the validator in daemon mode as an HTTP service | +| print mode | `-p` | Print the report to stdout | +| extract html | `-h` | Extracts any html blocks within the report and saves the content to the filesystem. Note: the file name is derived from the node name the html appears in | +| print memory stats | `-m` | Prints some memory usage information. Mainly for debugging purposes on processing huge xml files | +| check assertions | `-c ` | Check assertions on the generated reports. This is mainly useful for scenario developers. Ask KoSIT for documentation, if you want to use this feauture | + + +## Return codes + +| code | description | +|-|-| +| 0 | All validated xml files are acceptable according to the scenario configurations | +| positive integer | Number of rejected (e.g. not acceptable) xml files according to the scenario configurations| +| -1 | Parsing error. The commandline arguments specified are incorrect | +| -2 | Configuration error. There is an error loading the configuration and/or validation targets | diff --git a/docs/daemon.md b/docs/daemon.md index ff5cd65..e3f7996 100644 --- a/docs/daemon.md +++ b/docs/daemon.md @@ -42,9 +42,11 @@ The possible customizations are: ## Access the HTTP interface -The validation service listens to `POST`-requests on any server URL. You need to supply the xml/object to validate in the HTTP body. +The validation service listens to `POST`-requests on any server URL. You need to supply the xml/object to validate in the HTTP body. +The last segment of the request URI is treated as the name of the input. E.g. requests to `/myfile.xml`, `/mypath/myfile.xml` and `/mypath/myfile.xml?someParam=1` +would all result in an input named `myfile.xml`. If you don't specify a specific request URI (e.g. POST to `/`), the name is auto generated for you. -The service expects a single XML input in the HTTP body, e.g. `multipart/form-data` is not supported. +The service expects a single XML input in the HTTP body, e.g. `multipart/form-data` is NOT supported. Examples: @@ -84,6 +86,15 @@ fetch("http://localhost:8080", requestOptions) .then(result => console.log(result)) .catch(error => console.log('error', error)); ``` +## Status codes +| code | description | +|-|-| +| 200 | The xml file is acceptable according to the scenario configurations | +| 400 | Bad request. the request contains errors, e.g. no content supplied | +| 405 | Method not allowed. Thec check service is only answering on POST requests | +| 406 | The xml file is NOT acceptable according to the scenario configurations| +| 422 | Unprocessable entity. Indicates an error while processing the xml file. This hints to errors in the scenario configuration | +| 500 | Internal server error. Something went wrong | ## Authorization There is no mechanism to check, whether client is allowed to consume the service or not. The user is responsible to secure access to the service. @@ -101,7 +112,7 @@ The daemon provides a simple GUI when issuing `GET` requests providing the follo 1. information about the actual [validator configuration](configurations.md) used by this daemon 1. a simple form to test the daemon with custom inputs - The GUI can be disabled using the API (see above) or via CLI + The GUI can be disabled using the API (see above) or via CLI: ```shell script java -jar validationtool--standalone.jar -s -D --disable-gui diff --git a/pom.xml b/pom.xml index 8eba135..b290a51 100644 --- a/pom.xml +++ b/pom.xml @@ -504,8 +504,6 @@ - - diff --git a/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java b/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java index 38a8ded..d91f75a 100644 --- a/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java +++ b/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java @@ -19,7 +19,6 @@ package de.kosit.validationtool.cmd; -import static de.kosit.validationtool.cmd.CommandLineOptions.DAEMON_SIGNAL; import static de.kosit.validationtool.cmd.CommandLineOptions.HELP; import static de.kosit.validationtool.cmd.CommandLineOptions.createHelpOptions; import static de.kosit.validationtool.cmd.CommandLineOptions.createOptions; @@ -59,16 +58,18 @@ public class CommandLineApplication { * @param args die Eingabe-Argumente */ public static void main(final String[] args) { - final int resultStatus = mainProgram(args); - if (DAEMON_SIGNAL != resultStatus) { + final ReturnValue resultStatus = mainProgram(args); + if (!resultStatus.equals(ReturnValue.DAEMON_MODE)) { sayGoodby(resultStatus); - System.exit(resultStatus); + System.exit(resultStatus.getCode()); + } else { + Runtime.getRuntime().addShutdownHook(new Thread(() -> Printer.writeOut("Shutting down daemon ..."))); } } - private static void sayGoodby(final int resultStatus) { + private static void sayGoodby(final ReturnValue resultStatus) { Printer.writeOut("\n##############################"); - if (resultStatus == 0) { + if (resultStatus.equals(ReturnValue.SUCCESS)) { Printer.writeOut("# " + new Line(Code.GREEN).add("Validation succesful!").render(false, false) + " #"); } else { Printer.writeOut("# " + new Line(Code.RED).add("Validation failed!").render(false, false) + " #"); @@ -77,14 +78,14 @@ public class CommandLineApplication { } // for testing purposes. Unless jvm is terminated during tests. See above - static int mainProgram(final String[] args) { + static ReturnValue mainProgram(final String[] args) { final Options options = createOptions(); - int resultStatus; + ReturnValue resultStatus; try { if (isHelpRequested(args)) { printHelp(options); - resultStatus = 0; + resultStatus = ReturnValue.SUCCESS; } else { final CommandLineParser parser = new DefaultParser(); final CommandLine cmd = parser.parse(options, args); @@ -94,7 +95,7 @@ public class CommandLineApplication { } catch (final ParseException e) { writeErr("Error processing command line arguments: {0}", e.getMessage(), e); printHelp(options); - resultStatus = 1; + resultStatus = ReturnValue.PARSING_ERROR; } return resultStatus; } diff --git a/src/main/java/de/kosit/validationtool/cmd/CommandLineOptions.java b/src/main/java/de/kosit/validationtool/cmd/CommandLineOptions.java index 992e083..c7bc6d0 100644 --- a/src/main/java/de/kosit/validationtool/cmd/CommandLineOptions.java +++ b/src/main/java/de/kosit/validationtool/cmd/CommandLineOptions.java @@ -37,7 +37,6 @@ public class CommandLineOptions { static final Option DEBUG_LOG = Option.builder("X").longOpt("debug-logging").desc("Enables full debug log. Alias for -l debug").build(); static final Option LOG_LEVEL = Option.builder("l").longOpt("log-level").hasArg() .desc("Enables a certain log level for debugging " + "purposes").build(); - public static final int DAEMON_SIGNAL = 100; static final Option PRINT_MEM_STATS = Option.builder("m").longOpt("memory-stats").desc("Prints some memory stats").build(); private CommandLineOptions() { diff --git a/src/main/java/de/kosit/validationtool/cmd/InternalCheck.java b/src/main/java/de/kosit/validationtool/cmd/InternalCheck.java index 93ddd4b..1cedd08 100644 --- a/src/main/java/de/kosit/validationtool/cmd/InternalCheck.java +++ b/src/main/java/de/kosit/validationtool/cmd/InternalCheck.java @@ -20,10 +20,9 @@ package de.kosit.validationtool.cmd; import java.io.PrintWriter; -import java.nio.file.Path; import java.text.MessageFormat; -import java.util.Comparator; import java.util.Map; +import java.util.Map.Entry; import java.util.stream.Collectors; import org.fusesource.jansi.AnsiRenderer.Code; @@ -80,7 +79,7 @@ class InternalCheck extends DefaultCheck { return result; } - void printResults(final Map results) { + void printResults(final Map results) { final PrintWriter writer = new PrintWriter(System.out);// NOSONAR writer.write("Results:\n"); writer.write(createResultGrid(results).render()); @@ -105,14 +104,18 @@ class InternalCheck extends DefaultCheck { } @Override - public boolean isSuccessful(final Map results) { + public boolean isSuccessful(final Map results) { if (this.checkAssertions > 0) { return this.failedAssertions == 0; } return super.isSuccessful(results); } - private static String createStatusLine(final Map results) { + public int getNotAcceptableCount(final Map results) { + return (int) (this.failedAssertions + results.values().stream().filter(e -> !e.isAcceptable()).count()); + } + + private static String createStatusLine(final Map results) { final long acceptable = results.entrySet().stream().filter(e -> e.getValue().isAcceptable()).count(); final long rejected = results.entrySet().stream().filter(e -> !e.getValue().isAcceptable()).count(); final long errors = results.entrySet().stream().filter(e -> !e.getValue().isProcessingSuccessful()).count(); @@ -125,7 +128,7 @@ class InternalCheck extends DefaultCheck { return line.render(true, false); } - private static Grid createResultGrid(final Map results) { + private static Grid createResultGrid(final Map results) { final Grid grid = new Grid( //@formatter:off new ColumnDefinition("filename", 60, 10, 1), @@ -135,11 +138,11 @@ class InternalCheck extends DefaultCheck { new ColumnDefinition("Error/Description", 60,20,3) ); //@formatter:on - results.entrySet().stream().sorted(Comparator.comparing(e -> e.getKey().getFileName())).forEach(e -> { + results.entrySet().stream().sorted(Entry.comparingByKey()).forEach(e -> { final Result value = e.getValue(); final Code textcolor = value.isAcceptable() ? Code.GREEN : Code.RED; - grid.addCell(e.getKey().getFileName(), textcolor); + grid.addCell(e.getKey(), textcolor); grid.addCell(value.isSchemaValid() ? "Y" : "N", textcolor); grid.addCell(value.isSchematronValid() ? "Y" : "N", textcolor); grid.addCell(value.getAcceptRecommendation(), textcolor); diff --git a/src/main/java/de/kosit/validationtool/cmd/ReturnValue.java b/src/main/java/de/kosit/validationtool/cmd/ReturnValue.java new file mode 100644 index 0000000..16b1e0e --- /dev/null +++ b/src/main/java/de/kosit/validationtool/cmd/ReturnValue.java @@ -0,0 +1,28 @@ +package de.kosit.validationtool.cmd; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * CLI return codes. + * + * @author Andreas Penski + */ +@RequiredArgsConstructor +@Getter +public class ReturnValue { + + public static final ReturnValue SUCCESS = new ReturnValue(0); + + public static final ReturnValue CONFIGURATION_ERROR = new ReturnValue(-2); + + public static final ReturnValue DAEMON_MODE = new ReturnValue(-100); + + public static final ReturnValue PARSING_ERROR = new ReturnValue(-1);; + + private final int code; + + public static ReturnValue createFailed(final int count) { + return new ReturnValue(count); + } +} diff --git a/src/main/java/de/kosit/validationtool/cmd/Validator.java b/src/main/java/de/kosit/validationtool/cmd/Validator.java index 0c9d5fd..bd986ae 100644 --- a/src/main/java/de/kosit/validationtool/cmd/Validator.java +++ b/src/main/java/de/kosit/validationtool/cmd/Validator.java @@ -1,7 +1,6 @@ package de.kosit.validationtool.cmd; import static de.kosit.validationtool.cmd.CommandLineOptions.CHECK_ASSERTIONS; -import static de.kosit.validationtool.cmd.CommandLineOptions.DAEMON_SIGNAL; import static de.kosit.validationtool.cmd.CommandLineOptions.DEBUG; import static de.kosit.validationtool.cmd.CommandLineOptions.DISABLE_GUI; import static de.kosit.validationtool.cmd.CommandLineOptions.EXTRACT_HTML; @@ -75,20 +74,29 @@ public class Validator { * * @param cmd parsed commandline. */ - static int mainProgram(final CommandLine cmd) { + static ReturnValue mainProgram(final CommandLine cmd) { greeting(); final org.apache.commons.cli.Options options = createOptions(); - int returnValue = 0; - if (cmd.hasOption(SERVER.getOpt())) { - returnValue = startDaemonMode(cmd); - } else if (cmd.hasOption(HELP.getOpt()) || cmd.getArgList().isEmpty()) { - printHelp(options); - } else if (cmd.getArgList().isEmpty()) { - printHelp(options); - } else { - returnValue = processActions(cmd); + final ReturnValue returnValue; + try { + if (cmd.hasOption(SERVER.getOpt())) { + startDaemonMode(cmd); + returnValue = ReturnValue.DAEMON_MODE; + } else if (cmd.hasOption(HELP.getOpt()) || (cmd.getArgList().isEmpty() && !isPiped())) { + printHelp(options); + returnValue = ReturnValue.PARSING_ERROR; + } else { + returnValue = processActions(cmd); + } + } catch (final Exception e) { + Printer.writeErr(e.getMessage()); + if (cmd.hasOption(DEBUG.getOpt())) { + log.error(e.getMessage(), e); + } else { + log.error(e.getMessage()); + } + return ReturnValue.CONFIGURATION_ERROR; } - return returnValue; } @@ -120,7 +128,7 @@ public class Validator { return host; } - private static int startDaemonMode(final CommandLine cmd) { + private static void startDaemonMode(final CommandLine cmd) { final Option[] unavailable = new Option[] { PRINT, CHECK_ASSERTIONS, DEBUG, OUTPUT, EXTRACT_HTML, REPORT_POSTFIX, REPORT_PREFIX }; warnUnusedOptions(cmd, unavailable, true); final ConfigurationLoader config = getConfiguration(cmd); @@ -132,7 +140,6 @@ public class Validator { printScenarios(configuration); Printer.writeOut("\nStarting daemon mode ..."); validDaemon.startServer(configuration); - return DAEMON_SIGNAL; } private static void warnUnusedOptions(final CommandLine cmd, final Option[] unavailable, final boolean daemon) { @@ -143,69 +150,57 @@ public class Validator { } } - private static int processActions(final CommandLine cmd) { - try { + private static ReturnValue processActions(final CommandLine cmd) throws IOException { + long start = System.currentTimeMillis(); + final Option[] unavailable = new Option[] { HOST, PORT, WORKER_COUNT, DISABLE_GUI }; + warnUnusedOptions(cmd, unavailable, false); + final Configuration config = getConfiguration(cmd).build(); + printScenarios(config); + final InternalCheck check = new InternalCheck(config); + final Path outputDirectory = determineOutputDirectory(cmd); - long start = System.currentTimeMillis(); - final Option[] unavailable = new Option[] { HOST, PORT, WORKER_COUNT, DISABLE_GUI }; - warnUnusedOptions(cmd, unavailable, false); - final Configuration config = getConfiguration(cmd).build(); - printScenarios(config); - final InternalCheck check = new InternalCheck(config); - final Path outputDirectory = determineOutputDirectory(cmd); - - final Processor processor = config.getContentRepository().getProcessor(); - if (cmd.hasOption(EXTRACT_HTML.getOpt())) { - check.getCheckSteps().add(new ExtractHtmlContentAction(processor, outputDirectory)); - } - check.getCheckSteps().add(new SerializeReportAction(outputDirectory, processor, determineNamingStrategy(cmd))); - if (cmd.hasOption(SERIALIZE_REPORT_INPUT.getOpt())) { - check.getCheckSteps().add(new SerializeReportInputAction(outputDirectory, check.getConversionService())); - } - if (cmd.hasOption(PRINT.getOpt())) { - check.getCheckSteps().add(new PrintReportAction(processor)); - } - - if (cmd.hasOption(CHECK_ASSERTIONS.getOpt())) { - final Assertions assertions = loadAssertions(cmd.getOptionValue(CHECK_ASSERTIONS.getOpt())); - check.getCheckSteps().add(new CheckAssertionAction(assertions, processor)); - } - if (cmd.hasOption(PRINT_MEM_STATS.getOpt())) { - check.getCheckSteps().add(new PrintMemoryStats()); - } - log.info("Setup completed in {}ms\n", System.currentTimeMillis() - start); - - final Collection targets = determineTestTargets(cmd); - start = System.currentTimeMillis(); - final Map results = new HashMap<>(); - Printer.writeOut("\nProcessing of {0} objects started", targets.size()); - long tick = System.currentTimeMillis(); - for (final Path p : targets) { - final Input input = InputFactory.read(p); - results.put(p, check.checkInput(input)); - if (((System.currentTimeMillis() - tick) / 1000) > 5) { - tick = System.currentTimeMillis(); - Printer.writeOut("{0}/{1} objects processed", results.size(), targets.size()); - } - } - final long processingTime = System.currentTimeMillis() - start; - Printer.writeOut("Processing of {0} objects completed in {1}ms", targets.size(), processingTime); - - check.printResults(results); - log.info("Processing {} object(s) completed in {}ms", targets.size(), processingTime); - return check.isSuccessful(results) ? 0 : 1; - - } catch (final Exception e) { - Printer.writeErr(e.getMessage()); - if (cmd.hasOption(DEBUG.getOpt())) { - log.error(e.getMessage(), e); - } else { - log.error(e.getMessage()); - } - return -1; + final Processor processor = config.getContentRepository().getProcessor(); + if (cmd.hasOption(EXTRACT_HTML.getOpt())) { + check.getCheckSteps().add(new ExtractHtmlContentAction(processor, outputDirectory)); } + check.getCheckSteps().add(new SerializeReportAction(outputDirectory, processor, determineNamingStrategy(cmd))); + if (cmd.hasOption(SERIALIZE_REPORT_INPUT.getOpt())) { + check.getCheckSteps().add(new SerializeReportInputAction(outputDirectory, check.getConversionService())); + } + if (cmd.hasOption(PRINT.getOpt())) { + check.getCheckSteps().add(new PrintReportAction(processor)); + } + + if (cmd.hasOption(CHECK_ASSERTIONS.getOpt())) { + final Assertions assertions = loadAssertions(cmd.getOptionValue(CHECK_ASSERTIONS.getOpt())); + check.getCheckSteps().add(new CheckAssertionAction(assertions, processor)); + } + if (cmd.hasOption(PRINT_MEM_STATS.getOpt())) { + check.getCheckSteps().add(new PrintMemoryStats()); + } + log.info("Setup completed in {}ms\n", System.currentTimeMillis() - start); + + final Collection targets = determineTestTargets(cmd); + start = System.currentTimeMillis(); + final Map results = new HashMap<>(); + Printer.writeOut("\nProcessing of {0} objects started", targets.size()); + long tick = System.currentTimeMillis(); + for (final Input input : targets) { + results.put(input.getName(), check.checkInput(input)); + if (((System.currentTimeMillis() - tick) / 1000) > 5) { + tick = System.currentTimeMillis(); + Printer.writeOut("{0}/{1} objects processed", results.size(), targets.size()); + } + } + final long processingTime = System.currentTimeMillis() - start; + Printer.writeOut("Processing of {0} objects completed in {1}ms", targets.size(), processingTime); + + check.printResults(results); + log.info("Processing {} object(s) completed in {}ms", targets.size(), processingTime); + return check.isSuccessful(results) ? ReturnValue.SUCCESS : ReturnValue.createFailed(check.getNotAcceptableCount(results)); } + private static ConfigurationLoader getConfiguration(final CommandLine cmd) { final URI scenarioLocation = determineDefinition(cmd); final URI repositoryLocation = determineRepository(cmd); @@ -265,32 +260,44 @@ public class Validator { return fir; } - private static Collection determineTestTargets(final CommandLine cmd) { - final Collection targets = new ArrayList<>(); + private static Collection determineTestTargets(final CommandLine cmd) throws IOException { + final Collection targets = new ArrayList<>(); if (!cmd.getArgList().isEmpty()) { cmd.getArgList().forEach(e -> targets.addAll(determineTestTarget(e))); } + if (isPiped()) { + targets.add(readFromPipe()); + } if (targets.isEmpty()) { throw new IllegalStateException("No test targets found. Nothing to check. Will quit now!"); } return targets; } - private static Collection determineTestTarget(final String s) { + private static boolean isPiped() throws IOException { + return System.in.available() > 0; + } + + private static Input readFromPipe() { + return InputFactory.read(System.in, "stdin"); + } + + private static Collection determineTestTarget(final String s) { final Path d = Paths.get(s); if (Files.isDirectory(d)) { return listDirectoryTargets(d); } else if (Files.exists(d)) { - return Collections.singleton(d); + return Collections.singleton(InputFactory.read(d)); } log.warn("The specified test target {} does not exist. Will be ignored", s); return Collections.emptyList(); } - private static Collection listDirectoryTargets(final Path d) { + private static Collection listDirectoryTargets(final Path d) { try ( final Stream stream = Files.list(d) ) { - return stream.filter(path -> path.toString().toLowerCase().endsWith(".xml")).collect(Collectors.toList()); + return stream.filter(path -> path.toString().toLowerCase().endsWith(".xml")).map(InputFactory::read) + .collect(Collectors.toList()); } catch (final IOException e) { throw new IllegalStateException("IOException while list directory content. Can not determine test targets.", e); } diff --git a/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java b/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java index 9e75fd4..ad58082 100644 --- a/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java +++ b/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java @@ -15,15 +15,20 @@ abstract class BaseHandler implements HttpHandler { protected static final String APPLICATION_XML = "application/xml"; - static final int OK = 200; protected static void write(final HttpExchange exchange, final byte[] content, final String contentType) throws IOException { - write(exchange, contentType, os -> os.write(content)); + write(exchange, content, contentType, HttpStatus.SC_OK); } - protected static void write(final HttpExchange exchange, final String contentType, final Write write) throws IOException { + protected static void write(final HttpExchange exchange, final byte[] content, final String contentType, final int statusCode) + throws IOException { + write(exchange, contentType, os -> os.write(content), statusCode); + } + + protected static void write(final HttpExchange exchange, final String contentType, final Write write, final int statusCode) + throws IOException { exchange.getResponseHeaders().add("Content-Type", contentType); - exchange.sendResponseHeaders(OK, 0); + exchange.sendResponseHeaders(statusCode, 0); final OutputStream os = exchange.getResponseBody(); write.write(os); os.close(); diff --git a/src/main/java/de/kosit/validationtool/daemon/CheckHandler.java b/src/main/java/de/kosit/validationtool/daemon/CheckHandler.java index 29e0d62..276f552 100644 --- a/src/main/java/de/kosit/validationtool/daemon/CheckHandler.java +++ b/src/main/java/de/kosit/validationtool/daemon/CheckHandler.java @@ -3,6 +3,7 @@ package de.kosit.validationtool.daemon; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.util.concurrent.atomic.AtomicLong; import com.sun.net.httpserver.HttpExchange; @@ -49,21 +50,36 @@ class CheckHandler extends BaseHandler { final InputStream inputStream = httpExchange.getRequestBody(); if (inputStream.available() > 0) { final SourceInput serverInput = (SourceInput) InputFactory.read(inputStream, - "supplied_instance_" + counter.incrementAndGet()); + resolveInputName(httpExchange.getRequestURI())); final Result result = this.implemenation.checkInput(serverInput); - write(httpExchange, serialize(result), APPLICATION_XML); + write(httpExchange, serialize(result), APPLICATION_XML, resolveStatus(result)); } else { - error(httpExchange, 400, "No content supplied"); + error(httpExchange, HttpStatus.SC_BAD_REQUEST, "No content supplied"); } } else { - error(httpExchange, 405, "Method not supported"); + error(httpExchange, HttpStatus.SC_METHOD_NOT_ALLOWED, "Method not supported"); } } catch (final Exception e) { - error(httpExchange, 500, "Internal error: " + e.getMessage()); + error(httpExchange, HttpStatus.SC_INTERNAL_SERVER_ERROR, "Internal error: " + e.getMessage()); } } + private static String resolveInputName(final URI requestURI) { + final String path = requestURI.getPath(); + if (path.equalsIgnoreCase("/")) { + return "supplied_instance_" + counter.incrementAndGet(); + } + return path.substring((path.lastIndexOf('/') + 1)); + } + + private static int resolveStatus(final Result result) { + if (result.isProcessingSuccessful()) { + return result.isAcceptable() ? HttpStatus.SC_OK : HttpStatus.SC_NOT_ACCEPTABLE; + } + return HttpStatus.SC_UNPROCESSABLE_ENTITY; + } + private byte[] serialize(final Result result) { try ( final ByteArrayOutputStream out = new ByteArrayOutputStream() ) { final Serializer serializer = this.processor.newSerializer(out); diff --git a/src/main/java/de/kosit/validationtool/daemon/HttpStatus.java b/src/main/java/de/kosit/validationtool/daemon/HttpStatus.java new file mode 100644 index 0000000..a29c902 --- /dev/null +++ b/src/main/java/de/kosit/validationtool/daemon/HttpStatus.java @@ -0,0 +1,32 @@ +package de.kosit.validationtool.daemon; + +/** + * Status codes for the HTTP daemon. + * + * @author Andreas Penski + */ +public interface HttpStatus { + + // --- 2xx Success --- + + /** {@code 200 OK} (HTTP/1.0 - RFC 1945) */ + int SC_OK = 200; + + // --- 4xx Client Error --- + + /** {@code 400 Bad Request} (HTTP/1.1 - RFC 2616) */ + int SC_BAD_REQUEST = 400; + + /** {@code 405 Method Not Allowed} (HTTP/1.1 - RFC 2616) */ + int SC_METHOD_NOT_ALLOWED = 405; + + /** {@code 406 Not Acceptable} (HTTP/1.1 - RFC 2616) */ + int SC_NOT_ACCEPTABLE = 406; + + /** {@code 422 Unprocessable Entity} (WebDAV - RFC 2518) */ + public static final int SC_UNPROCESSABLE_ENTITY = 422; + + /** {@code 500 Server Error} (HTTP/1.0 - RFC 1945) */ + int SC_INTERNAL_SERVER_ERROR = 500; + +} \ No newline at end of file diff --git a/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java b/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java index 551ba91..e11f0fa 100644 --- a/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java +++ b/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java @@ -21,7 +21,6 @@ package de.kosit.validationtool.impl; import static de.kosit.validationtool.impl.DateFactory.createTimestamp; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -103,7 +102,7 @@ public class DefaultCheck implements Check { return type; } - protected boolean isSuccessful(final Map results) { + protected boolean isSuccessful(final Map results) { return results.entrySet().stream().allMatch(e -> e.getValue().isAcceptable()); } diff --git a/src/test/java/de/kosit/validationtool/cmd/CheckAssertionActionTest.java b/src/test/java/de/kosit/validationtool/cmd/CheckAssertionActionTest.java index a170d84..29e8e7c 100644 --- a/src/test/java/de/kosit/validationtool/cmd/CheckAssertionActionTest.java +++ b/src/test/java/de/kosit/validationtool/cmd/CheckAssertionActionTest.java @@ -53,14 +53,14 @@ public class CheckAssertionActionTest { @Before public void setup() throws IOException { this.commandLine = new CommandLine(); - this.commandLine.activate(); + CommandLine.activate(); } @Test public void testEmptyInput() { final CheckAssertionAction a = new CheckAssertionAction(new Assertions(), TestObjectFactory.createProcessor()); a.check(new CheckAction.Bag(InputFactory.read(SAMPLE), new CreateReportInput())); - assertThat(this.commandLine.getErrorOutput()).contains("Can not find assertions for"); + assertThat(CommandLine.getErrorOutput()).contains("Can not find assertions for"); } @Test @@ -72,6 +72,6 @@ public class CheckAssertionActionTest { final CheckAssertionAction a = new CheckAssertionAction(assertions, TestObjectFactory.createProcessor()); a.check(bag); - assertThat(this.commandLine.getErrorOutput()).contains("Assertion mismatch"); + assertThat(CommandLine.getErrorOutput()).contains("Assertion mismatch"); } } diff --git a/src/test/java/de/kosit/validationtool/cmd/CommandLine.java b/src/test/java/de/kosit/validationtool/cmd/CommandLine.java index 1412124..cae55e8 100644 --- a/src/test/java/de/kosit/validationtool/cmd/CommandLine.java +++ b/src/test/java/de/kosit/validationtool/cmd/CommandLine.java @@ -19,7 +19,14 @@ package de.kosit.validationtool.cmd; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.Reader; import java.util.List; import org.apache.commons.io.IOUtils; @@ -35,23 +42,85 @@ import lombok.Setter; */ public class CommandLine { - private static ReplaceableOutputStream out = new ReplaceableOutputStream<>(); + /** + * Simpler Proxy für {@link OutputStream}, dessen target ausgetauscht werden kann. + * + * @param Typ des eigentlichen {@link OutputStream} + */ + private static class ReplaceableOutputStream extends OutputStream { - private static ReplaceableOutputStream error = new ReplaceableOutputStream<>(); + @Getter + @Setter + private O out; + + @Override + public void write(final int idx) throws IOException { + if (this.out != null) { + this.out.write(idx); + } + } + + @Override + public void write(final byte[] bts) throws IOException { + if (this.out != null) { + this.out.write(bts); + } + } + + @Override + public void write(final byte[] bts, final int st, final int end) throws IOException { + if (this.out != null) { + this.out.write(bts, st, end); + } + } + + @Override + public void flush() throws IOException { + + if (this.out != null) { + this.out.flush(); + } + } + + @Override + public void close() throws IOException { + if (this.out != null) { + this.out.close(); + } + } + } + + private static final ReplaceableOutputStream out = new ReplaceableOutputStream<>(); + + private static final ReplaceableOutputStream error = new ReplaceableOutputStream<>(); static { // Initialisierung muss vor SL4J's SimpleLogger erfolgen, sonst sind logs nicht erfasst. // deshalb darf diese Klasse kein Log haben System.setOut(new PrintStream(new TeeOutputStream(System.out, out))); System.setErr(new PrintStream(new TeeOutputStream(System.err, error))); + setStandardInput(nullInputStream()); } - public String getOutput() { + public static void setStandardInput(final InputStream in) { + System.setIn(in); + } + + public static InputStream nullInputStream() { + return new InputStream() { + + @Override + public int read() throws IOException { + return 0; + } + }; + } + + public static String getOutput() { return new String(out.getOut().toByteArray()); } - - public String getErrorOutput() { + public static String getErrorOutput() { return new String(error.getOut().toByteArray()); } @@ -63,68 +132,26 @@ public class CommandLine { return readLines(error.getOut().toByteArray()); } - private List readLines(byte[] bytes) { - try ( ByteArrayInputStream in = new ByteArrayInputStream(bytes); - Reader r = new InputStreamReader(in) ) { + private List readLines(final byte[] bytes) { + try ( final ByteArrayInputStream in = new ByteArrayInputStream(bytes); + final Reader r = new InputStreamReader(in) ) { return IOUtils.readLines(r); - } catch (IOException e) { + } catch (final IOException e) { throw new IllegalStateException("Can not read input"); } } - public void activate() { + public static void activate() { out.setOut(new ByteArrayOutputStream()); error.setOut(new ByteArrayOutputStream()); } - public void deactivate() { + public static void deactivate() { out.setOut(null); error.setOut(null); - } - - /** - * Simpler Proxy für {@link OutputStream}, dessen target ausgetauscht werden kann. - * - * @param Typ des eigentlichen {@link OutputStream} - */ - private static class ReplaceableOutputStream extends OutputStream { - - @Getter - @Setter - private O out; - - public void write(int idx) throws IOException { - if (out != null) { - this.out.write(idx); - } - } - - public void write(byte[] bts) throws IOException { - if (out != null) { - this.out.write(bts); - } - } - - public void write(byte[] bts, int st, int end) throws IOException { - if (out != null) { - this.out.write(bts, st, end); - } - } - - public void flush() throws IOException { - - if (out != null) { - this.out.flush(); - } - } - - public void close() throws IOException { - if (out != null) { - this.out.close(); - } - } + setStandardInput(nullInputStream()); } } diff --git a/src/test/java/de/kosit/validationtool/cmd/CommandlineApplicationTest.java b/src/test/java/de/kosit/validationtool/cmd/CommandlineApplicationTest.java index 1fe995b..285a059 100644 --- a/src/test/java/de/kosit/validationtool/cmd/CommandlineApplicationTest.java +++ b/src/test/java/de/kosit/validationtool/cmd/CommandlineApplicationTest.java @@ -56,7 +56,7 @@ public class CommandlineApplicationTest { @Before public void setup() throws IOException { this.commandLine = new CommandLine(); - this.commandLine.activate(); + CommandLine.activate(); if (Files.exists(this.output)) { FileUtils.deleteDirectory(this.output.toFile()); } @@ -71,13 +71,14 @@ public class CommandlineApplicationTest { log.error("Error deleting file", e); } }); + CommandLine.deactivate(); } @Test public void testHelp() { final String[] args = new String[] { "-?" }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).isEmpty(); + assertThat(CommandLine.getErrorOutput()).isEmpty(); checkForHelp(this.commandLine.getOutputLines()); } @@ -90,24 +91,24 @@ public class CommandlineApplicationTest { public void testRequiredScenarioFile() { final String[] args = new String[] { "-d", "arguments", "egal welche", "argument drin sind" }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).isNotEmpty(); - assertThat(this.commandLine.getErrorOutput()).contains("Missing required option: s"); + assertThat(CommandLine.getErrorOutput()).isNotEmpty(); + assertThat(CommandLine.getErrorOutput()).contains("Missing required option: s"); } @Test public void testNotExistingScenarioFile() { final String[] args = new String[] { "-s", Paths.get(Simple.NOT_EXISTING).toString(), Paths.get(Simple.NOT_EXISTING).toString() }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).isNotEmpty(); - assertThat(this.commandLine.getErrorOutput()).contains("Not a valid path for scenario definition specified"); + assertThat(CommandLine.getErrorOutput()).isNotEmpty(); + assertThat(CommandLine.getErrorOutput()).contains("Not a valid path for scenario definition specified"); } @Test public void testIncorrectRepository() { final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), Paths.get(Simple.NOT_EXISTING).toString() }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).isNotEmpty(); - assertThat(this.commandLine.getErrorOutput()).contains("Can not resolve"); + assertThat(CommandLine.getErrorOutput()).isNotEmpty(); + assertThat(CommandLine.getErrorOutput()).contains("Can not resolve"); } @Test @@ -115,8 +116,8 @@ public class CommandlineApplicationTest { final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-r", Paths.get(Simple.REPOSITORY_URI).toString(), Paths.get(Simple.NOT_EXISTING).toString() }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).isNotEmpty(); - assertThat(this.commandLine.getErrorOutput()).contains("No test targets found"); + assertThat(CommandLine.getErrorOutput()).isNotEmpty(); + assertThat(CommandLine.getErrorOutput()).contains("No test targets found"); } @Test @@ -124,7 +125,7 @@ public class CommandlineApplicationTest { final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-r", Paths.get(Simple.REPOSITORY_URI).toString(), Paths.get(Simple.SIMPLE_VALID).toString() }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).contains(RESULT_OUTPUT); + assertThat(CommandLine.getErrorOutput()).contains(RESULT_OUTPUT); } @Test @@ -133,8 +134,8 @@ public class CommandlineApplicationTest { Paths.get(Simple.REPOSITORY_URI).toString(), Paths.get(Simple.SIMPLE_VALID).toString(), "--report-prefix", "somePrefix", "--report-postfix", "somePostfix" }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).contains(RESULT_OUTPUT); - assertThat(this.commandLine.getErrorOutput()).contains("somePrefix-simple-somePostfix"); + assertThat(CommandLine.getErrorOutput()).contains(RESULT_OUTPUT); + assertThat(CommandLine.getErrorOutput()).contains("somePrefix-simple-somePostfix"); } @Test @@ -142,7 +143,7 @@ public class CommandlineApplicationTest { final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-o", this.output.toString(), "-r", Paths.get(Simple.REPOSITORY_URI).toString(), Paths.get(Simple.SIMPLE_VALID).toString(), Paths.get(Simple.FOO).toString() }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).contains("Processing 2 object(s) completed"); + assertThat(CommandLine.getErrorOutput()).contains("Processing 2 object(s) completed"); } @Test @@ -150,7 +151,7 @@ public class CommandlineApplicationTest { final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-o", this.output.toString(), "-r", Paths.get(Simple.REPOSITORY_URI).toString(), Paths.get(Simple.EXAMPLES).toString() }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).contains("Processing 8 object(s) completed"); + assertThat(CommandLine.getErrorOutput()).contains("Processing 8 object(s) completed"); } @Test @@ -159,7 +160,7 @@ public class CommandlineApplicationTest { final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-o", this.output.toString(), "-r", Paths.get(Simple.REPOSITORY_URI).toString(), Paths.get(Simple.SIMPLE_VALID).toString() }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).contains(RESULT_OUTPUT); + assertThat(CommandLine.getErrorOutput()).contains(RESULT_OUTPUT); assertThat(this.output).exists(); assertThat(Files.list(this.output)).hasSize(1); } @@ -179,7 +180,7 @@ public class CommandlineApplicationTest { final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-p", "-r", Paths.get(Simple.REPOSITORY_URI).toString(), "-o", this.output.toString(), Paths.get(Simple.SIMPLE_VALID).toString() }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).contains(RESULT_OUTPUT); + assertThat(CommandLine.getErrorOutput()).contains(RESULT_OUTPUT); assertThat(this.commandLine.getOutputLines()).haveAtLeastOne(new Condition<>( s -> StringUtils.contains(s, ""), "Must " + "contain xml preambel")); } @@ -190,7 +191,7 @@ public class CommandlineApplicationTest { this.output.toAbsolutePath().toString(), "-r", Paths.get(Simple.REPOSITORY_URI).toString(), Paths.get(Simple.SIMPLE_VALID).toString() }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).contains(RESULT_OUTPUT); + assertThat(CommandLine.getErrorOutput()).contains(RESULT_OUTPUT); assertThat(Files.list(this.output).filter(f -> f.toString().endsWith(".html")).count()).isGreaterThan(0); } @@ -200,8 +201,8 @@ public class CommandlineApplicationTest { Paths.get(Simple.REPOSITORY_URI).toString(), "-o", this.output.toString(), "-c", Paths.get(ASSERTIONS).toString(), Paths.get(Simple.REPOSITORY_URI).toString(), Paths.get(Simple.SIMPLE_VALID).toString() }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).contains(RESULT_OUTPUT); - assertThat(this.commandLine.getErrorOutput()).contains("Can not find assertions for "); + assertThat(CommandLine.getErrorOutput()).contains(RESULT_OUTPUT); + assertThat(CommandLine.getErrorOutput()).contains("Can not find assertions for "); } @Test @@ -209,7 +210,7 @@ public class CommandlineApplicationTest { final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-r", "unknown", "-o", this.output.toString(), "-d", Paths.get(ASSERTIONS).toString() }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).contains("at de.kosit.validationtool"); + assertThat(CommandLine.getErrorOutput()).contains("at de.kosit.validationtool"); } @Test @@ -217,7 +218,16 @@ public class CommandlineApplicationTest { final String[] args = new String[] { "-m", "-s", Paths.get(Simple.SCENARIOS).toString(), "-r", Paths.get(Simple.REPOSITORY_URI).toString(), Paths.get(Simple.SIMPLE_VALID).toString() }; CommandLineApplication.mainProgram(args); - assertThat(this.commandLine.getErrorOutput()).contains(RESULT_OUTPUT); - assertThat(this.commandLine.getErrorOutput()).contains("total"); + assertThat(CommandLine.getErrorOutput()).contains(RESULT_OUTPUT); + assertThat(CommandLine.getErrorOutput()).contains("total"); + } + + @Test + public void testReadFromPipe() throws IOException { + final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-r", + Paths.get(Simple.REPOSITORY_URI).toString() }; + CommandLine.setStandardInput(Files.newInputStream(Paths.get(Simple.SIMPLE_VALID))); + CommandLineApplication.mainProgram(args); + assertThat(CommandLine.getErrorOutput()).contains(RESULT_OUTPUT); } } diff --git a/src/test/java/de/kosit/validationtool/cmd/PrintReportActionTest.java b/src/test/java/de/kosit/validationtool/cmd/PrintReportActionTest.java index 303eff1..b5b614d 100644 --- a/src/test/java/de/kosit/validationtool/cmd/PrintReportActionTest.java +++ b/src/test/java/de/kosit/validationtool/cmd/PrintReportActionTest.java @@ -46,13 +46,13 @@ public class PrintReportActionTest { @Before public void setup() { this.commandLine = new CommandLine(); - this.commandLine.activate(); + CommandLine.activate(); this.action = new PrintReportAction(TestObjectFactory.createProcessor()); } @After public void tearDown() { - this.commandLine.deactivate(); + CommandLine.deactivate(); } @Test @@ -62,9 +62,9 @@ public class PrintReportActionTest { assertThat(this.action.isSkipped(b)).isFalse(); this.action.check(b); assertThat(b.isStopped()).isFalse(); - assertThat(this.commandLine.getOutput()).isNotEmpty(); - assertThat(this.commandLine.getOutput()).contains("