From 772e5757666c3042c22c8666aaf2519246591dd8 Mon Sep 17 00:00:00 2001 From: Gerd Aschemann Date: Mon, 15 Jan 2024 11:08:46 +0100 Subject: [PATCH] #318 Add tests for CLI main --- build.gradle | 2 + gradle/libs.versions.toml | 5 +- htmlSanityCheck-cli/build.gradle | 38 ++++++++- .../org/aim42/htmlsanitycheck/cli/Main.groovy | 78 +++++++++++++------ .../htmlsanitycheck/cli/MainCliSpec.groovy | 35 +++++++++ htmlSanityCheck-core/build.gradle | 2 + htmlSanityCheck-gradle-plugin/build.gradle | 8 +- 7 files changed, 133 insertions(+), 35 deletions(-) create mode 100644 htmlSanityCheck-cli/src/test/groovy/org/aim42/htmlsanitycheck/cli/MainCliSpec.groovy diff --git a/build.gradle b/build.gradle index d759537b..5afffe64 100644 --- a/build.gradle +++ b/build.gradle @@ -162,6 +162,8 @@ configure(subprojects) { description "${rootProject.description} - Module ${project.name}" dependencies { + implementation platform (libs.slf4j.bom) + testImplementation platform(libs.spock) testImplementation "org.spockframework:spock-core" testImplementation "org.spockframework:spock-junit4" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3cefec47..f6a6ac5e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,10 +14,9 @@ junit-vintage = { module = 'org.junit.vintage:junit-vintage-engine', version.ref lombok = 'org.projectlombok:lombok:1.18.36' picocli-impl = { module = 'info.picocli:picocli', version.ref = "picocli-version" } picocli-annotationprocessor = { module = 'info.picocli:picocli-codegen', version.ref = "picocli-version" } -slf4j-api = { module = 'org.slf4j:slf4j-api', version.ref = 'slf4j-version' } -slf4j-nop = { module = 'org.slf4j:slf4j-nop', version.ref = 'slf4j-version' } -string-similarity = {module = 'net.ricecode:string-similarity', version.ref ='string-similarity-version'} +slf4j-bom = { module = 'org.slf4j:slf4j-bom', version.ref = 'slf4j-version' } spock = 'org.spockframework:spock-bom:2.3-groovy-3.0' +string-similarity = {module = 'net.ricecode:string-similarity', version.ref ='string-similarity-version'} testcontainers-junit-jupiter = { module = 'org.testcontainers:junit-jupiter', version.ref = 'testcontainers-version' } wiremock-testcontainers = { module = 'org.wiremock.integrations.testcontainers:wiremock-testcontainers-module', version.ref = 'wiremock-testcontainers-version' } diff --git a/htmlSanityCheck-cli/build.gradle b/htmlSanityCheck-cli/build.gradle index 24a43f93..3fdcdf4b 100644 --- a/htmlSanityCheck-cli/build.gradle +++ b/htmlSanityCheck-cli/build.gradle @@ -6,11 +6,12 @@ dependencies { implementation libs.picocli.impl annotationProcessor libs.picocli.annotationprocessor - implementation libs.slf4j.api - implementation libs.slf4j.simple - implementation libs.groovy.all + implementation 'org.slf4j:slf4j-api' + implementation 'org.slf4j:slf4j-simple' implementation project(":htmlSanityCheck-core") + + testImplementation libs.mockito } compileGroovy { @@ -20,4 +21,33 @@ compileGroovy { application { mainClass = 'org.aim42.htmlsanitycheck.cli.Main' applicationName = 'hsc' -} \ No newline at end of file +} + +/* + * Copyright Gerd Aschemann and aim42 contributors. + * + * 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 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ diff --git a/htmlSanityCheck-cli/src/main/groovy/org/aim42/htmlsanitycheck/cli/Main.groovy b/htmlSanityCheck-cli/src/main/groovy/org/aim42/htmlsanitycheck/cli/Main.groovy index 9e983acf..ed5b080e 100644 --- a/htmlSanityCheck-cli/src/main/groovy/org/aim42/htmlsanitycheck/cli/Main.groovy +++ b/htmlSanityCheck-cli/src/main/groovy/org/aim42/htmlsanitycheck/cli/Main.groovy @@ -22,6 +22,16 @@ import java.nio.file.Paths class Main implements Runnable { private static final Logger logger = LoggerFactory.getLogger(Main.class) + MainRunner runner + + Main() { + Main(new MainRunner()) + } + + Main(MainRunner runner) { + this.runner = runner + } + @Option(names = ["-r", "--resultsDir"], description = "Results Directory") String resultsDirectoryName = "/tmp/results" @@ -38,8 +48,11 @@ class Main implements Runnable { File[] srcDocs static void main(String[] args) { - Main app = new Main() + MainRunner runner = new MainRunner() + Main app = new Main(runner) CommandLine cmd = new CommandLine(app) + runner.setMain(app) + runner.setCmd(cmd) cmd.execute(args) } @@ -52,38 +65,55 @@ class Main implements Runnable { .collect { it.toFile() } } - void run() { - var configuration = new Configuration() - - configuration.addConfigurationItem(Configuration.ITEM_NAME_sourceDir, srcDir) + static class MainRunner implements CommandLine.IFactory { - def srcDocuments = srcDocs ?: findFiles() - if (!srcDocuments) { - CommandLine cmd = new CommandLine(this) - System.err.println("Please specify at least one src document (either explicitly or implicitly)") - cmd.usage(System.out) - System.exit(1) + @Override + T create(Class cls) throws Exception { + if (cls == Main.class) { + return (T) new Main() + } else { + throw new IllegalArgumentException("Cannot create CLI applications of class '${cls}'") + } } - configuration.addConfigurationItem(Configuration.ITEM_NAME_sourceDocuments, srcDocuments) - var resultsDirectory = new File(resultsDirectoryName) - configuration.addConfigurationItem((Configuration.ITEM_NAME_checkingResultsDir), resultsDirectory) + Main main + CommandLine cmd - if (configuration.isValid()) { - // create output directory for checking results - resultsDirectory.mkdirs() + void run() { + def srcDocuments = main.srcDocs ?: main.findFiles() + if (!srcDocuments) { + System.err.println("Please specify at least one src document (either explicitly or implicitly)") + cmd.usage(System.out) + System.exit(1) + } - // create an AllChecksRunner... - var allChecksRunner = new AllChecksRunner(configuration) + var configuration = new Configuration() + configuration.addConfigurationItem(Configuration.ITEM_NAME_sourceDir, main.srcDir) + configuration.addConfigurationItem(Configuration.ITEM_NAME_sourceDocuments, srcDocuments) - // ... and perform the actual checks - var allChecks = allChecksRunner.performAllChecks() + var resultsDirectory = new File(main.resultsDirectoryName) + configuration.addConfigurationItem((Configuration.ITEM_NAME_checkingResultsDir), resultsDirectory) - // check for findings and fail build if requested - var nrOfFindingsOnAllPages = allChecks.nrOfFindingsOnAllPages() - logger.debug("Found ${nrOfFindingsOnAllPages} error(s) on all checked pages") + if (configuration.isValid()) { + // create output directory for checking results + resultsDirectory.mkdirs() + + // create an AllChecksRunner... + var allChecksRunner = new AllChecksRunner(configuration) + + // ... and perform the actual checks + var allChecks = allChecksRunner.performAllChecks() + + // check for findings and fail build if requested + var nrOfFindingsOnAllPages = allChecks.nrOfFindingsOnAllPages() + logger.debug("Found ${nrOfFindingsOnAllPages} error(s) on all checked pages") + } } } + + void run() { + runner.run() + } } /*======================================================================== diff --git a/htmlSanityCheck-cli/src/test/groovy/org/aim42/htmlsanitycheck/cli/MainCliSpec.groovy b/htmlSanityCheck-cli/src/test/groovy/org/aim42/htmlsanitycheck/cli/MainCliSpec.groovy new file mode 100644 index 00000000..96ae0f69 --- /dev/null +++ b/htmlSanityCheck-cli/src/test/groovy/org/aim42/htmlsanitycheck/cli/MainCliSpec.groovy @@ -0,0 +1,35 @@ +package org.aim42.htmlsanitycheck.cli + + +import picocli.CommandLine +import spock.lang.Specification +import spock.lang.Unroll + +class MainCliSpec extends Specification { + + Main.MainRunner myAppRunner = Mock(Main.MainRunner) + + @Unroll + def "test hsc with #args"() { + given: + def cmdLine = new CommandLine(new Main(myAppRunner)) + + when: + def exitCode = cmdLine.execute(args.split()) + + then: + exitCode == expectedExitCode + (runnerWasCalled ? 1 : 0) * myAppRunner.run() + + where: + args | expectedExitCode | runnerWasCalled + "-h" | 0 | false + "--help" | 0 | false + "-V" | 0 | false + "--version" | 0 | false + "" | 0 | true + "." | 0 | true + "-r /tmp/results" | 0 | true + "--resultsDir /tmp/results" | 0 | true + } +} diff --git a/htmlSanityCheck-core/build.gradle b/htmlSanityCheck-core/build.gradle index 8acbabc1..9da15652 100644 --- a/htmlSanityCheck-core/build.gradle +++ b/htmlSanityCheck-core/build.gradle @@ -11,6 +11,8 @@ dependencies { } implementation libs.slf4j.api testImplementation libs.slf4j.nop + implementation 'org.slf4j:slf4j-api' + testImplementation 'org.slf4j:slf4j-nop' // jsoup is our awesome html parser, see jsoup.org implementation libs.jsoup implementation libs.string.similarity diff --git a/htmlSanityCheck-gradle-plugin/build.gradle b/htmlSanityCheck-gradle-plugin/build.gradle index b6389e17..99bf1519 100755 --- a/htmlSanityCheck-gradle-plugin/build.gradle +++ b/htmlSanityCheck-gradle-plugin/build.gradle @@ -56,13 +56,13 @@ publishing { } dependencies { + implementation gradleApi() + implementation project(":htmlSanityCheck-core") + testImplementation( + libs.jsoup, gradleTestKit() ) - - implementation gradleApi() - implementation project(":htmlSanityCheck-core") - testImplementation libs.jsoup } /*