From 905d323ddce34e8af5238d6a9ae036c6fbe2816e Mon Sep 17 00:00:00 2001 From: Gerd Aschemann Date: Wed, 18 Sep 2024 09:49:01 +0200 Subject: [PATCH] Avoid NPE in config initialization and provide strong test coverage for Gradle task --- .../HtmlSanityCheckTask.groovy | 26 ++----- .../HtmlSanityCheckBaseSpec.groovy | 44 +++++++++++ ... HtmlSanityCheckTaskFunctionalSpec.groovy} | 73 +++++-------------- .../HtmlSanityCheckTaskSpec.groovy | 50 +++++++++++++ 4 files changed, 118 insertions(+), 75 deletions(-) create mode 100644 htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckBaseSpec.groovy rename htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/{HtmlSanityCheckTaskFunctionalTest.groovy => HtmlSanityCheckTaskFunctionalSpec.groovy} (61%) create mode 100644 htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckTaskSpec.groovy diff --git a/htmlSanityCheck-gradle-plugin/src/main/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckTask.groovy b/htmlSanityCheck-gradle-plugin/src/main/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckTask.groovy index 0c5938ae..6bd9e400 100644 --- a/htmlSanityCheck-gradle-plugin/src/main/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckTask.groovy +++ b/htmlSanityCheck-gradle-plugin/src/main/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckTask.groovy @@ -23,7 +23,6 @@ import org.gradle.api.tasks.TaskAction */ class HtmlSanityCheckTask extends DefaultTask { - // // we support checking several named files @InputFiles FileCollection sourceDocuments @@ -80,11 +79,8 @@ class HtmlSanityCheckTask extends DefaultTask { // private stuff // ************************************************************************** - private Set allFilesToCheck - private Configuration myConfig - /** * Sets sensible defaults for important attributes. * @@ -92,16 +88,13 @@ class HtmlSanityCheckTask extends DefaultTask { * by setting outputs.upToDateWhen to false. */ HtmlSanityCheckTask() { - // Never consider this task up-to-date. // thx https://github.com/stevesaliman/gradle-cobertura-plugin/commit/d61191f7d5f4e8e89abcd5f3839a210985526648 outputs.upToDateWhen { false } // give sensible default for output directory, see https://github.com/aim42/htmlSanityCheck/issues/205 - checkingResultsDir = new File(project.buildDir, '/reports/htmlSanityCheck/') - junitResultsDir = new File(project.buildDir, '/test-results/htmlSanityCheck/') - - + checkingResultsDir = new File(project.DEFAULT_BUILD_DIR_NAME, '/reports/htmlSanityCheck/') + junitResultsDir = new File(project.DEFAULT_BUILD_DIR_NAME, '/test-results/htmlSanityCheck/') } void setSourceDir(File sourceDir) { @@ -118,7 +111,6 @@ class HtmlSanityCheckTask extends DefaultTask { */ @TaskAction void sanityCheckHtml() { - // tell us about these parameters logBuildParameter() @@ -138,10 +130,9 @@ class HtmlSanityCheckTask extends DefaultTask { assert junitResultsDir.canWrite() } - // TODO: unclear: do we need to adjust pathnames if running on Windows(tm)?? + // TODO: unclear: do we need to adjust path-names if running on Windows(tm)?? - logger.info("buildfile-info", sourceDocuments?.toString()) - logger.info("allFilesToCheck" + allFilesToCheck.toString(), "") + logger.info("Source documents: '{}'", sourceDocuments) // create an AllChecksRunner... def allChecksRunner = new AllChecksRunner(myConfig) @@ -167,7 +158,7 @@ See ${checkingResultsDir} for a detailed report.""" /** * setup a @Configuration instance containing all given configuration parameters - * from the gradle buildfile. + * from the gradle build-file. * * This method has to be updated in case of new configuration parameters!! * @@ -177,7 +168,7 @@ See ${checkingResultsDir} for a detailed report.""" protected Configuration setupConfiguration() { Configuration result = Configuration.builder() - .sourceDocuments(sourceDocuments.files) + .sourceDocuments(sourceDocuments?.files) .sourceDir(sourceDir) .checkingResultsDir(checkingResultsDir) .junitResultsDir(junitResultsDir) @@ -210,16 +201,13 @@ See ${checkingResultsDir} for a detailed report.""" private void logBuildParameter() { logger.info "=" * 70 - logger.info "Parameters given to sanityCheck plugin from gradle buildfile..." + logger.info "Parameters given to sanityCheck plugin from gradle build-file..." logger.info "Files to check : $sourceDocuments" logger.info "Source directory: $sourceDir" logger.info "Results dir : $checkingResultsDir" logger.info "JUnit dir : $junitResultsDir" logger.info "Fail on errors : $failOnErrors" - } - - } /*======================================================================== diff --git a/htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckBaseSpec.groovy b/htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckBaseSpec.groovy new file mode 100644 index 00000000..0fee6a8d --- /dev/null +++ b/htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckBaseSpec.groovy @@ -0,0 +1,44 @@ +package org.aim42.htmlsanitycheck + +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import spock.lang.Specification + +class HtmlSanityCheckBaseSpec extends Specification { + final static VALID_HTML = """""" + final static INVALID_HTML = """ """ + + @Rule + TemporaryFolder testProjectDir = new TemporaryFolder() + File sourceDir + File buildDir + File buildFile + File htmlFile + + def setup() { + buildDir = testProjectDir.newFolder("build") + sourceDir = testProjectDir.newFolder("src") + sourceDir.mkdirs() + htmlFile = new File (sourceDir, "test.html") + } + + protected void createBuildFile(String extendedTaskConfig = "") { + // a note on writing paths to the build script on windows: + // - the default file separator is a backslash + // - as the path is written into a quoted string, backslashes should be quoted + // - to avoid string manipulation or similar, we use URIs to avoid the problem + // (URIs consist of / instead of backslashes) + buildFile = testProjectDir.newFile('build.gradle') << """ + plugins { + id 'org.aim42.htmlSanityCheck' + } + + htmlSanityCheck { + sourceDir = file ("src") + checkingResultsDir = file ("build") + + ${extendedTaskConfig} + } + """.stripIndent() + } +} \ No newline at end of file diff --git a/htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckTaskFunctionalTest.groovy b/htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckTaskFunctionalSpec.groovy similarity index 61% rename from htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckTaskFunctionalTest.groovy rename to htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckTaskFunctionalSpec.groovy index 631558ba..3dee0e27 100644 --- a/htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckTaskFunctionalTest.groovy +++ b/htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckTaskFunctionalSpec.groovy @@ -3,57 +3,26 @@ package org.aim42.htmlsanitycheck import org.gradle.testkit.runner.GradleRunner import org.jsoup.Jsoup import org.jsoup.nodes.Element -import org.junit.Rule -import org.junit.rules.TemporaryFolder -import spock.lang.Specification import spock.lang.Unroll import static org.gradle.testkit.runner.TaskOutcome.FAILED import static org.gradle.testkit.runner.TaskOutcome.SUCCESS -class HtmlSanityCheckTaskFunctionalTest extends Specification { - private final static VALID_HTML = """""" - private final static INVALID_HTML = """ """ +class HtmlSanityCheckTaskFunctionalSpec extends HtmlSanityCheckBaseSpec { private final static GRADLE_VERSIONS = [ // 6.x or older does not work! - '7.6.3', // latest 7.x - '8.0.2', '8.1.1', '8.2.1', '8.3', '8.4', + '7.6.3', // latest 7.x + '8.0.2', '8.1.1', '8.2.1', '8.3', '8.4', '8.5', '8.6', '8.7', '8.8', '8.9', - '8.10', '8.10.1' // all 8.x (latest patches) - ] - - @Rule - TemporaryFolder testProjectDir = new TemporaryFolder() - File buildDir - File buildFile - File htmlFile - - def setup() { - buildDir = testProjectDir.newFolder("build") - htmlFile = testProjectDir.newFile("test.html") - // a note on writing paths to the build script on windows: - // - the default file separator is a backslash - // - as the path is written into a quoted string, backslashes should be quoted - // - to avoid string manipulation or similar, we use URIs to avoid the problem - // (URIs consist of / instead of backslashes) - buildFile = testProjectDir.newFile('build.gradle') << """ - plugins { - id 'org.aim42.htmlSanityCheck' - } - - htmlSanityCheck { - sourceDir = file( "${htmlFile.parentFile.toURI().path}" ) - checkingResultsDir = file( "${buildDir.toURI().path}" ) - } - """ - } + '8.10.1' // all 8.x (latest patches) + ] @Unroll def "can execute htmlSanityCheck task with Gradle version #gradleVersion"() { given: htmlFile << VALID_HTML + createBuildFile() when: - def result = runnerForHtmlSanityCheckTask(gradleVersion).build() then: @@ -67,11 +36,9 @@ class HtmlSanityCheckTaskFunctionalTest extends Specification { def "invalid HTML fails build with failOnErrors=true and Gradle version #gradleVersion"() { given: htmlFile << INVALID_HTML - buildFile << """ - htmlSanityCheck { + createBuildFile(""" failOnErrors = true - } - """ + """) when: @@ -90,14 +57,12 @@ class HtmlSanityCheckTaskFunctionalTest extends Specification { given: htmlFile << VALID_HTML testProjectDir.newFile("test-invalid.html") << INVALID_HTML - buildFile << """ - htmlSanityCheck { + createBuildFile(""" sourceDocuments = fileTree(sourceDir) { include '**/$htmlFile.name' } failOnErrors = true - } - """ + """) when: def result = runnerForHtmlSanityCheckTask(gradleVersion).build() @@ -113,13 +78,9 @@ class HtmlSanityCheckTaskFunctionalTest extends Specification { def "can select a subset of all checks to be performed with Gradle version #gradleVersion"() { given: htmlFile << VALID_HTML - buildFile << """ - import org.aim42.htmlsanitycheck.check.AllCheckers - - htmlSanityCheck { - checkerClasses = [AllCheckers.CHECKER_CLASSES.first()] - } - """ + createBuildFile(""" + checkerClasses = [org.aim42.htmlsanitycheck.check.AllCheckers.CHECKER_CLASSES.first()] + """) when: runnerForHtmlSanityCheckTask(gradleVersion).build() @@ -134,10 +95,10 @@ class HtmlSanityCheckTaskFunctionalTest extends Specification { private GradleRunner runnerForHtmlSanityCheckTask(String gradleVersion) { GradleRunner.create() - .withGradleVersion(gradleVersion) - .withProjectDir(testProjectDir.root) - .withPluginClasspath() - .withArguments('htmlSanityCheck') + .withGradleVersion(gradleVersion) + .withProjectDir(testProjectDir.root) + .withPluginClasspath() + .withArguments('htmlSanityCheck') } private static class HtmlReport { diff --git a/htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckTaskSpec.groovy b/htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckTaskSpec.groovy new file mode 100644 index 00000000..1aae32c5 --- /dev/null +++ b/htmlSanityCheck-gradle-plugin/src/test/groovy/org/aim42/htmlsanitycheck/HtmlSanityCheckTaskSpec.groovy @@ -0,0 +1,50 @@ +package org.aim42.htmlsanitycheck + +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.testfixtures.ProjectBuilder +import spock.lang.Specification + +class HtmlSanityCheckTaskSpec extends HtmlSanityCheckBaseSpec { + Project project + Task task + + def setup () { + project = ProjectBuilder.builder().withProjectDir(testProjectDir.root).build() + task = project.tasks.register(HtmlSanityCheckPlugin.HTML_SANITY_CHECK, HtmlSanityCheckTask).get() + } + + def "should initialize task with defaults"() { + expect: + task.failOnErrors == false + task.httpConnectionTimeout == 5000 + task.ignoreLocalHost == false + task.ignoreIPAddresses == false + task.checkingResultsDir == new File(project.DEFAULT_BUILD_DIR_NAME, '/reports/htmlSanityCheck/') + task.junitResultsDir == new File(project.DEFAULT_BUILD_DIR_NAME, '/test-results/htmlSanityCheck/') + } + + def "should work with simple file"() { + given: + htmlFile << VALID_HTML + + when: + task.setSourceDir(testProjectDir.root) + task.httpSuccessCodes = [299] + task.httpErrorCodes = [599] + task.httpWarningCodes = [199] + task.sanityCheckHtml() + + then: + task.sourceDocuments != null + } + + def "should throw exception if configuration is invalid"() { + when: + task.sanityCheckHtml() + + then: + def e = thrown(MisconfigurationException) + e.message.contains("source directory must not be null") + } +} \ No newline at end of file