diff --git a/.gitignore b/.gitignore index 363ecd5..5afaf07 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ /.project /.classpath /.settings/org.eclipse.buildship.core.prefs +/.settings/org.eclipse.core.resources.prefs +/.settings/org.eclipse.jdt.apt.core.prefs +/.settings/org.eclipse.m2e.core.prefs /bin/ /build/ /.idea/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 46519fe..d72b830 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [PR #80](https://github.com/itsallcode/openfasttrace-gradle/pull/80) - Fix JavaDoc warnings and let build fail on warnings +- [PR #82](https://github.com/itsallcode/openfasttrace-gradle/pull/82) + - Refactor plugin integration tests ## [3.2.0] - 2026-08-18 diff --git a/build.gradle b/build.gradle index 5f32a3a..5cb7ddc 100644 --- a/build.gradle +++ b/build.gradle @@ -156,8 +156,6 @@ test { finalizedBy jacocoTestReport } -project.tasks["sonar"].dependsOn jacocoTestReport - sonarqube { properties { property "sonar.host.url", "https://sonarcloud.io" diff --git a/src/test/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePluginTest.java b/src/test/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePluginTest.java index 34d9f15..ad30b19 100644 --- a/src/test/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePluginTest.java +++ b/src/test/java/org/itsallcode/openfasttrace/gradle/OpenFastTracePluginTest.java @@ -6,31 +6,26 @@ import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.either; import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.api.Assumptions.assumeTrue; import java.io.*; import java.nio.file.*; -import java.util.*; -import org.gradle.api.logging.Logging; import org.gradle.internal.impldep.org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.gradle.internal.impldep.org.apache.commons.compress.archivers.zip.ZipFile; -import org.gradle.testkit.runner.*; +import org.gradle.testkit.runner.TaskOutcome; +import org.gradle.testkit.runner.UnexpectedBuildFailure; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.Parameter; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.EnumSource; -import org.slf4j.Logger; @ParameterizedClass(name = "OpenFastTracePluginTest {0}") @EnumSource(GradleTestConfig.class) class OpenFastTracePluginTest { - private static final Logger LOG = Logging.getLogger(OpenFastTracePluginTest.class); - - private static final boolean ENABLE_WARNINGS = true; private static final Path EXAMPLES_DIR = Paths.get("example-projects").toAbsolutePath(); - private static final Path PROJECT_DEFAULT_CONFIG_DIR = EXAMPLES_DIR.resolve("default-config"); + private static final Path PROJECT_DEFAULT_CONFIG_DIR = EXAMPLES_DIR + .resolve("default-config"); private static final Path PROJECT_CUSTOM_CONFIG_DIR = EXAMPLES_DIR.resolve("custom-config"); private static final Path MULTI_PROJECT_DIR = EXAMPLES_DIR.resolve("multi-project"); private static final Path DEPENDENCY_CONFIG_DIR = EXAMPLES_DIR.resolve("dependency-config"); @@ -43,9 +38,9 @@ class OpenFastTracePluginTest @Test void tracingTaskAddedToProject() { - final BuildResult buildResult = runBuild(PROJECT_DEFAULT_CONFIG_DIR, "tasks"); - assertThat(buildResult.getOutput(), containsString( - "traceRequirements - Trace requirements and generate tracing report")); + fixture(PROJECT_DEFAULT_CONFIG_DIR).withArgs("tasks").run() + .assertOutput(containsString( + "traceRequirements - Trace requirements and generate tracing report")); } @Test @@ -62,247 +57,249 @@ void pluginUsesConfigurationCacheWithMultiModuleProject() private void testConfigurationCache(final Path projectDir) { - assumeTrue(configurationCacheEnabled(), "Configuration cache is not enabled"); - BuildResult buildResult = runBuild(projectDir, "tasks"); - assertThat(buildResult.getOutput(), containsString( + PluginTestFixture.assumeConfigurationCacheEnabled(); + final PluginTestFixture fixture = fixture(projectDir).withArgs("tasks"); + + fixture.run().assertOutput(containsString( "traceRequirements - Trace requirements and generate tracing report")); - buildResult = runBuild(projectDir, "tasks"); - assertThat(buildResult.getOutput(), - allOf(containsString( - "traceRequirements - Trace requirements and generate tracing report"), - containsString("Reusing configuration cache."))); + + fixture.run().assertOutput(allOf(containsString( + "traceRequirements - Trace requirements and generate tracing report"), + containsString("Reusing configuration cache."))); } @Test void testTraceExampleProjectWithDefaultConfig() { - final BuildResult buildResult = runBuild(PROJECT_DEFAULT_CONFIG_DIR, "clean", - "traceRequirements"); - assertThat(buildResult.task(":traceRequirements").getOutcome(), - either(is(TaskOutcome.SUCCESS)).or(is(TaskOutcome.FROM_CACHE))); - TestUtil.assertFileContent(PROJECT_DEFAULT_CONFIG_DIR.resolve("build/reports/tracing.txt"), - "ok - 0 total"); + fixture(PROJECT_DEFAULT_CONFIG_DIR).withArgs("clean", "traceRequirements") + .withReportFile(Path.of("build/reports/tracing.txt")) + .run() + .assertTraceOutcomeSuccessOrFromCache() + .assertReportFileLines("ok - 0 total"); } @Test void testCollectExampleProjectWithCustomConfig() { - final BuildResult buildResult = runBuild(PROJECT_CUSTOM_CONFIG_DIR, "clean", - "collectRequirements"); - assertThat(buildResult.task(":collectRequirements").getOutcome(), - either(is(TaskOutcome.SUCCESS)).or(is(TaskOutcome.FROM_CACHE))); - TestUtil.assertFileContent( - PROJECT_CUSTOM_CONFIG_DIR.resolve("build/reports/requirements.xml"), - "\n" + - "", - """ - - - exampleB\ - """, """ - - approved - 0 - """, - - """ - 1 - - - dsn:exampleB - 1 - - - """, - - """ - - - exampleB - Tracing Example - draft - 1 - """, - - """ - 2 - Example requirement - - utest - impl - - - """, - - " \n" + - ""); + fixture(PROJECT_CUSTOM_CONFIG_DIR).withArgs("clean", "collectRequirements") + .withReportFile(Path.of("build/reports/requirements.xml")) + .run().assertCollectOutcomeSuccessOrFromCache() + .assertReportFileLines( + "\n" + + "", + """ + + + exampleB\ + """, """ + + approved + 0 + """, + + """ + 1 + + + dsn:exampleB + 1 + + + """, + + """ + + + exampleB + Tracing Example + draft + 1 + """, + + """ + 2 + Example requirement + + utest + impl + + + """, + + " \n" + + ""); } @Test void testCollectIsUpToDateWhenAlreadyRunBefore() { - BuildResult buildResult = runBuild(PROJECT_CUSTOM_CONFIG_DIR, "clean", - "collectRequirements"); - assertThat(buildResult.task(":clean").getOutcome(), - either(is(TaskOutcome.SUCCESS)).or(is(TaskOutcome.FROM_CACHE))); - assertThat(buildResult.task(":collectRequirements").getOutcome(), - either(is(TaskOutcome.SUCCESS)).or(is(TaskOutcome.FROM_CACHE))); - buildResult = runBuild(PROJECT_CUSTOM_CONFIG_DIR, "collectRequirements"); - assertEquals(TaskOutcome.UP_TO_DATE, - buildResult.task(":collectRequirements").getOutcome()); + final PluginTestFixture fixture = fixture(PROJECT_CUSTOM_CONFIG_DIR); + fixture.withArgs("clean", "collectRequirements").run() + .assertOutcome(":clean", + either(is(TaskOutcome.SUCCESS)) + .or(is(TaskOutcome.UP_TO_DATE))) + .assertCollectOutcomeSuccessOrFromCache(); + + fixture.withArgs("collectRequirements").run() + .assertOutcome(":collectRequirements", TaskOutcome.UP_TO_DATE); } @Test void testHtmlReportConfig() { - final BuildResult buildResult = runBuild(HTML_REPORT_CONFIG_DIR, "clean", - "traceRequirements"); - assertThat(buildResult.task(":traceRequirements").getOutcome(), - either(is(TaskOutcome.SUCCESS)).or(is(TaskOutcome.FROM_CACHE))); - TestUtil.assertFileContent(HTML_REPORT_CONFIG_DIR.resolve("build/reports/tracing.html"), - "", - "", - "
"); + fixture(HTML_REPORT_CONFIG_DIR) + .withArgs("clean", "traceRequirements") + .withReportFile(Path.of("build/reports/tracing.html")) + .run() + .assertTraceOutcomeSuccessOrFromCache() + .assertReportFileLines("", + "", + "
"); } @Test void testTraceTaskUpToDateWhenAlreadyRun() { - BuildResult buildResult = runBuild(HTML_REPORT_CONFIG_DIR, "clean", - "traceRequirements"); - assertThat(buildResult.task(":traceRequirements").getOutcome(), - either(is(TaskOutcome.SUCCESS)).or(is(TaskOutcome.FROM_CACHE))); - buildResult = runBuild(HTML_REPORT_CONFIG_DIR, "traceRequirements"); - assertEquals(TaskOutcome.UP_TO_DATE, - buildResult.task(":traceRequirements").getOutcome()); + final PluginTestFixture fixture = fixture(HTML_REPORT_CONFIG_DIR); + fixture.withArgs("clean", "traceRequirements").run() + .assertTraceOutcomeSuccessOrFromCache(); + fixture.withArgs("traceRequirements").run().assertOutcome(":traceRequirements", + TaskOutcome.UP_TO_DATE); } @Test void testTraceExampleProjectWithCustomConfig() { - final BuildResult buildResult = runBuild(PROJECT_CUSTOM_CONFIG_DIR, "clean", - "traceRequirements"); - assertThat(buildResult.task(":traceRequirements").getOutcome(), - either(is(TaskOutcome.SUCCESS)).or(is(TaskOutcome.FROM_CACHE))); - TestUtil.assertFileContent(PROJECT_CUSTOM_CONFIG_DIR.resolve("build/custom-report.txt"), - "not ok [ in: 1 / 1 ✔ | out: 0 / 0 ] dsn~exampleB~1 [draft] (impl, -utest)", - "not ok - 2 total, 1 direct, 0 transitive defects"); + fixture(PROJECT_CUSTOM_CONFIG_DIR).withArgs("clean", "traceRequirements") + .withReportFile(Path.of("build/custom-report.txt")) + .run() + .assertTraceOutcomeSuccessOrFromCache() + .assertReportFileLines( + "not ok [ in: 1 / 1 ✔ | out: 0 / 0 ] dsn~exampleB~1 [draft] (impl, -utest)", + "not ok - 2 total, 1 direct, 0 transitive defects"); } @Test void testTraceExampleProjectWithCustomConfigFailBuild() { - final BuildResult buildResult = runBuildExpectFailure(PROJECT_CUSTOM_CONFIG_DIR, - "clean", "traceRequirements", "-PfailBuild=true"); - assertEquals(TaskOutcome.FAILED, - buildResult.task(":traceRequirements").getOutcome()); - TestUtil.assertFileContent(PROJECT_CUSTOM_CONFIG_DIR.resolve("build/custom-report.txt"), - "not ok [ in: 1 / 1 ✔ | out: 0 / 0 ] dsn~exampleB~1 [draft] (impl, -utest)", - "not ok - 2 total, 1 direct, 0 transitive defects"); + fixture(PROJECT_CUSTOM_CONFIG_DIR) + .withArgs("clean", "traceRequirements", "-PfailBuild=true") + .withReportFile(Path.of("build/custom-report.txt")) + .runExpectingFailure() + .assertOutcome(":traceRequirements", TaskOutcome.FAILED) + .assertReportFileLines( + "not ok [ in: 1 / 1 ✔ | out: 0 / 0 ] dsn~exampleB~1 [draft] (impl, -utest)", + "not ok - 2 total, 1 direct, 0 transitive defects"); } @Test void filteredArtifactTypes() { - final BuildResult buildResult = runBuild(PROJECT_CUSTOM_CONFIG_DIR, "clean", - "traceRequirements", "-PfailBuild=true", "-PfilteredArtifactTypes=dsn"); - assertThat(buildResult.task(":traceRequirements").getOutcome(), - either(is(TaskOutcome.SUCCESS)).or(is(TaskOutcome.FROM_CACHE))); + fixture(PROJECT_CUSTOM_CONFIG_DIR) + .withArgs("clean", "traceRequirements", "-PfailBuild=true", + "-PfilteredArtifactTypes=dsn") + .run() + .assertTraceOutcomeSuccessOrFromCache(); } @Test void filteredWantedStatuses() { - final BuildResult buildResult = runBuild(PROJECT_CUSTOM_CONFIG_DIR, "clean", - "traceRequirements", - "-PfilterWantedStatuses=draft,approved"); - assertThat(buildResult.task(":traceRequirements").getOutcome(), - either(is(TaskOutcome.SUCCESS)).or(is(TaskOutcome.FROM_CACHE))); - TestUtil.assertFileContent(PROJECT_CUSTOM_CONFIG_DIR.resolve("build/custom-report.txt"), - "not ok [ in: 1 / 1 ✔ | out: 0 / 0 ] dsn~exampleB~1 [draft] (impl, -utest)", - "not ok - 2 total, 1 direct, 0 transitive defects"); + fixture(PROJECT_CUSTOM_CONFIG_DIR) + .withArgs("clean", "traceRequirements", + "-PfilterWantedStatuses=draft,approved") + .withReportFile(Path.of("build/custom-report.txt")) + .run() + .assertTraceOutcomeSuccessOrFromCache() + .assertReportFileLines( + "not ok [ in: 1 / 1 ✔ | out: 0 / 0 ] dsn~exampleB~1 [draft] (impl, -utest)", + "not ok - 2 total, 1 direct, 0 transitive defects"); } @Test void filteredWantedStatusesNoMatch() { - final BuildResult buildResult = runBuild(PROJECT_CUSTOM_CONFIG_DIR, "clean", - "traceRequirements", - "-PfilterWantedStatuses=approved"); - assertThat(buildResult.task(":traceRequirements").getOutcome(), - either(is(TaskOutcome.SUCCESS)).or(is(TaskOutcome.FROM_CACHE))); - TestUtil.assertFileContent( - PROJECT_CUSTOM_CONFIG_DIR.resolve("build/custom-report.txt"), - // Generated ID depends on JVM - "not ok [ in: 0 / 0 | out: 0 / 1 ✘ ] impl~exampleB-", - "not ok - 1 total, 1 direct, 0 transitive defects"); + fixture(PROJECT_CUSTOM_CONFIG_DIR) + .withArgs("clean", "traceRequirements", + "-PfilterWantedStatuses=approved") + .withReportFile(Path.of("build/custom-report.txt")) + .run() + .assertTraceOutcomeSuccessOrFromCache() + .assertReportFileLines( + // Generated ID depends on JVM + "not ok [ in: 0 / 0 | out: 0 / 1 ✘ ] impl~exampleB-", + "not ok - 1 total, 1 direct, 0 transitive defects"); } @Test void filteredWantedStatusesInvalidStatus() { - final BuildResult buildResult = runBuildExpectFailure(PROJECT_CUSTOM_CONFIG_DIR, "clean", - "traceRequirements", - "-PfilterWantedStatuses=invalid"); - assertThat(buildResult.getOutput(), containsString( - "Invalid status 'invalid'. Valid statuses are: APPROVED, PROPOSED, DRAFT, REJECTED")); + fixture(PROJECT_CUSTOM_CONFIG_DIR) + .withArgs("clean", "traceRequirements", + "-PfilterWantedStatuses=invalid") + .runExpectingFailure() + .assertOutput(containsString( + "Invalid status 'invalid'. Valid statuses are: APPROVED, PROPOSED, DRAFT, REJECTED")); } @Test void testTraceExampleProjectWithCustomConfigFailBuildErrorMessage() { - try - { - runBuild(PROJECT_CUSTOM_CONFIG_DIR, "clean", "traceRequirements", - "-PfailBuild=true"); - } - catch (final UnexpectedBuildFailure e) - { - assertAll( - () -> assertEquals(TaskOutcome.FAILED, - e.getBuildResult().task(":traceRequirements").getOutcome()), - () -> assertThat(e.getMessage(), - startsWith("Unexpected build execution failure")), - () -> assertThat(e.getMessage(), - containsString("Requirement tracing found 1 defects. See report at"))); - } + final PluginTestFixture fixture = fixture(PROJECT_CUSTOM_CONFIG_DIR) + .withArgs("clean", "traceRequirements", "-PfailBuild=true"); + final UnexpectedBuildFailure exception = assertThrows(UnexpectedBuildFailure.class, + fixture::run); + assertAll( + () -> assertEquals(TaskOutcome.FAILED, + exception.getBuildResult() + .task(":traceRequirements") + .getOutcome()), + () -> assertThat(exception.getMessage(), + startsWith("Unexpected build execution failure")), + () -> assertThat(exception.getMessage(), + containsString("Requirement tracing found 1 defects. See report at"))); } @Test void testTraceMultiProject() { - final BuildResult buildResult = runBuild(MULTI_PROJECT_DIR, "clean", - "traceRequirements"); - assertThat(buildResult.task(":traceRequirements").getOutcome(), - either(is(TaskOutcome.SUCCESS)).or(is(TaskOutcome.FROM_CACHE))); - TestUtil.assertFileContent(MULTI_PROJECT_DIR.resolve("build/custom-report.txt"), - "ok - 6 total"); + fixture(MULTI_PROJECT_DIR).withArgs("clean", "traceRequirements") + .withReportFile(Path.of("build/custom-report.txt")) + .run() + .assertTraceOutcomeSuccessOrFromCache() + .assertReportFileLines("ok - 6 total"); } @Test void traceDependencyProject() { - BuildResult buildResult = runBuild(DEPENDENCY_CONFIG_DIR, "clean"); - assertThat(buildResult.task(":clean").getOutcome(), - either(is(TaskOutcome.SUCCESS)).or(is(TaskOutcome.UP_TO_DATE))); - final Path dependencyZip = DEPENDENCY_CONFIG_DIR.resolve("build/repo/requirements-1.0.zip"); + final PluginTestFixture fixture = fixture(DEPENDENCY_CONFIG_DIR); + fixture.withArgs("clean") + .run() + .assertOutcome(":clean", + either(is(TaskOutcome.SUCCESS)) + .or(is(TaskOutcome.UP_TO_DATE))); + + final Path dependencyZip = DEPENDENCY_CONFIG_DIR + .resolve("build/repo/requirements-1.0.zip"); createDependencyZip(dependencyZip); - buildResult = runBuild(DEPENDENCY_CONFIG_DIR, "traceRequirements"); - assertThat(buildResult.task(":traceRequirements").getOutcome(), - either(is(TaskOutcome.SUCCESS)).or(is(TaskOutcome.FROM_CACHE))); - TestUtil.assertFileContent(DEPENDENCY_CONFIG_DIR.resolve("build/reports/tracing.txt"), - "requirements-1.0.zip!spec.md:2", - "requirements-1.0.zip!source.java:1", - "not ok - 2 total, 1 direct, 0 transitive defects"); + fixture.withArgs("traceRequirements") + .withReportFile(Path.of("build/reports/tracing.txt")) + .run() + .assertTraceOutcomeSuccessOrFromCache() + .assertReportFileLines( + "requirements-1.0.zip!spec.md:2", + "requirements-1.0.zip!source.java:1", + "not ok - 2 total, 1 direct, 0 transitive defects"); } @Test void publishToMavenRepo() { - final BuildResult buildResult = runBuild(PUBLISH_CONFIG_DIR, "clean", - "publishToMavenLocal"); - assertEquals(TaskOutcome.SUCCESS, buildResult.task(":publishToMavenLocal").getOutcome()); + fixture(PUBLISH_CONFIG_DIR).withArgs("clean", "publishToMavenLocal") + .run() + .assertOutcome(":publishToMavenLocal", TaskOutcome.SUCCESS); final Path archive = PUBLISH_CONFIG_DIR .resolve("build/distributions/publish-config-1.0.zip"); @@ -366,64 +363,13 @@ private static void createDependencyZip(final Path dependencyZip) } catch (final IOException e) { - throw new UncheckedIOException("Failed to create dependency zip " + dependencyZip, e); - } - } - - private BuildResult runBuildExpectFailure(final Path projectDir, final String... arguments) - { - return createGradleRunner(projectDir, arguments).buildAndFail(); - } - - private BuildResult runBuild(final Path projectDir, final String... arguments) - { - return createGradleRunner(projectDir, arguments).build(); - } - - private GradleRunner createGradleRunner(final Path projectDir, final String... arguments) - { - configureJacoco(projectDir); - final List allArgs = new ArrayList<>(); - allArgs.addAll(List.of(arguments)); - allArgs.addAll(List.of("--info", "--stacktrace", "--build-cache")); - if (configurationCacheEnabled()) - { - allArgs.add("--configuration-cache"); - } - if (ENABLE_WARNINGS) - { - allArgs.addAll(List.of("--warning-mode", "all")); + throw new UncheckedIOException( + "Failed to create dependency zip " + dependencyZip, e); } - final GradleRunner runner = GradleRunner.create() - .withProjectDir(projectDir.toFile()) - .withPluginClasspath() - .withArguments(allArgs) - .forwardOutput(); - if (config.gradleVersion != null) - { - runner.withGradleVersion(config.gradleVersion); - } - return runner; } - private static boolean configurationCacheEnabled() + private PluginTestFixture fixture(final Path projectDir) { - return System.getProperty("enableConfigurationCache", "false") - .equalsIgnoreCase("true"); - } - - private static void configureJacoco(final Path projectDir) - { - final Optional testkitGradleConfig = TestUtil - .readResource(OpenFastTracePluginTest.class, "/testkit-gradle.properties"); - if (testkitGradleConfig.isEmpty()) - { - LOG.info("Testkit gradle config not available. Skipping configuration"); - return; - } - LOG.info("Found testkit gradle config: {}", testkitGradleConfig.get()); - final Path gradleProperties = projectDir.resolve("gradle.properties"); - LOG.info("Writing testkit gradle config to {}", gradleProperties); - TestUtil.writeFile(gradleProperties, testkitGradleConfig.get()); + return PluginTestFixture.create(config, projectDir); } } diff --git a/src/test/java/org/itsallcode/openfasttrace/gradle/PluginTestFixture.java b/src/test/java/org/itsallcode/openfasttrace/gradle/PluginTestFixture.java new file mode 100644 index 0000000..ba20c4d --- /dev/null +++ b/src/test/java/org/itsallcode/openfasttrace/gradle/PluginTestFixture.java @@ -0,0 +1,170 @@ +package org.itsallcode.openfasttrace.gradle; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import java.nio.file.Path; +import java.util.*; + +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.testkit.runner.*; +import org.hamcrest.Matcher; + +class PluginTestFixture +{ + private static final Logger LOG = Logging.getLogger(PluginTestFixture.class); + + private final GradleTestConfig config; + private final Path projectDir; + private String[] arguments; + private Path relativeReportPath; + + private PluginTestFixture(final GradleTestConfig config, final Path projectDir) + { + this.config = config; + this.projectDir = projectDir; + } + + static PluginTestFixture create(final GradleTestConfig config, final Path projectDir) + { + return new PluginTestFixture(config, projectDir); + } + + PluginTestFixture withArgs(final String... args) + { + this.arguments = args; + return this; + } + + public PluginTestFixture withReportFile(final Path relativeReportPath) + { + this.relativeReportPath = relativeReportPath; + return this; + } + + Result run() + { + final GradleRunner runner = createGradleRunner(); + final BuildResult buildResult = runner.build(); + return new Result(buildResult); + } + + Result runExpectingFailure() + { + final GradleRunner runner = createGradleRunner(); + final BuildResult buildResult = runner.buildAndFail(); + return new Result(buildResult); + } + + private GradleRunner createGradleRunner() + { + assertNotNull(arguments, "Arguments must be set before running the test fixture"); + configureJacoco(projectDir); + final List allArgs = new ArrayList<>(); + allArgs.addAll(List.of(arguments)); + allArgs.addAll(List.of("--info", "--stacktrace", "--build-cache")); + if (configurationCacheEnabled()) + { + allArgs.add("--configuration-cache"); + } + allArgs.addAll(List.of("--warning-mode", "all")); + final GradleRunner runner = GradleRunner.create() + .withProjectDir(projectDir.toFile()) + .withPluginClasspath() + .withArguments(allArgs) + .forwardOutput(); + if (config.gradleVersion != null) + { + runner.withGradleVersion(config.gradleVersion); + } + return runner; + } + + static void assumeConfigurationCacheEnabled() + { + assumeTrue(configurationCacheEnabled(), "Configuration cache is not enabled"); + } + + static boolean configurationCacheEnabled() + { + return System.getProperty("enableConfigurationCache", "false") + .equalsIgnoreCase("true"); + } + + private static void configureJacoco(final Path projectDir) + { + final Optional testkitGradleConfig = TestUtil + .readResource(PluginTestFixture.class, + "/testkit-gradle.properties"); + if (testkitGradleConfig.isEmpty()) + { + LOG.info("Testkit gradle config not available. Skipping configuration"); + return; + } + LOG.info("Found testkit gradle config: {}", testkitGradleConfig.get()); + final Path gradleProperties = projectDir.resolve("gradle.properties"); + LOG.info("Writing testkit gradle config to {}", gradleProperties); + TestUtil.writeFile(gradleProperties, testkitGradleConfig.get()); + } + + class Result + { + private final BuildResult buildResult; + + private Result(final BuildResult buildResult) + { + this.buildResult = buildResult; + } + + void assertOutput(final Matcher matcher) + { + assertThat(buildResult.getOutput(), matcher); + } + + Result assertTraceOutcomeSuccessOrFromCache() + { + return assertOutcomeSuccessOrFromCache(":traceRequirements"); + } + + Result assertCollectOutcomeUpToDate() + { + return this.assertOutcome(":collectRequirements", TaskOutcome.UP_TO_DATE); + } + + Result assertOutcome(final String taskPath, final TaskOutcome expectedOutcome) + { + return assertOutcome(taskPath, equalTo(expectedOutcome)); + } + + Result assertOutcome(final String taskPath, final Matcher matcher) + { + final BuildTask task = buildResult.task(taskPath); + assertNotNull(task, "Task '" + taskPath + "' was not executed"); + assertThat("Outcome of task " + taskPath, task.getOutcome(), matcher); + return this; + } + + Result assertCollectOutcomeSuccessOrFromCache() + { + return assertOutcomeSuccessOrFromCache(":collectRequirements"); + } + + Result assertOutcomeSuccessOrFromCache(final String taskPath) + { + return assertOutcome(taskPath, + either(is(TaskOutcome.SUCCESS)).or(is(TaskOutcome.FROM_CACHE))); + } + + Result assertReportFileLines(final String... lines) + { + assertNotNull(relativeReportPath, + "Report file path must be set before asserting report file lines"); + final Path reportFile = projectDir.resolve(relativeReportPath); + TestUtil.assertFileContent(reportFile, lines); + return this; + } + } +}