diff --git a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/detekt/DetektConfigurator.groovy b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/detekt/DetektConfigurator.groovy index 7e239c8..63ebbc4 100644 --- a/plugin/src/main/groovy/com/novoda/staticanalysis/internal/detekt/DetektConfigurator.groovy +++ b/plugin/src/main/groovy/com/novoda/staticanalysis/internal/detekt/DetektConfigurator.groovy @@ -11,7 +11,7 @@ import org.gradle.api.Task class DetektConfigurator implements Configurator { private static final String DETEKT_PLUGIN = 'io.gitlab.arturbosch.detekt' - private static final String LAST_COMPATIBLE_DETEKT_VERSION = '1.0.0.RC8' + private static final String LAST_COMPATIBLE_DETEKT_VERSION = '1.0.0.RC9.2' private static final String DETEKT_NOT_APPLIED = 'The Detekt plugin is configured but not applied. Please apply the plugin in your build script.\nFor more information see https://github.com/arturbosch/detekt.' private static final String OUTPUT_NOT_DEFINED = 'Output not defined! To analyze the results, `output` needs to be defined in Detekt profile.' private static final String DETEKT_CONFIGURATION_ERROR = "A problem occurred while configuring Detekt. Please make sure to use a compatible version (All versions up to $LAST_COMPATIBLE_DETEKT_VERSION)" @@ -47,22 +47,36 @@ class DetektConfigurator implements Configurator { def detekt = project.extensions.findByName('detekt') config.delegate = detekt config() - configureToolTask(detekt) + + def collectViolations = configureToolTask(detekt) + evaluateViolations.dependsOn collectViolations } } + + private CollectDetektViolationsTask configureToolTask(detekt) { + def detektTask = project.tasks.findByName('detekt') + if (detektTask?.hasProperty('reports')) { + def reports = detektTask.reports + return createCollectViolationsTask( + violations, + detektTask, + reports.xml.destination, + reports.html.destination + ) + } - private void configureToolTask(detekt) { - def detektTask = project.tasks['detektCheck'] - detektTask.group = 'verification' - - // evaluate violations after detekt + // Fallback to old Detekt versions def output = resolveOutput(detekt) if (!output) { throw new IllegalArgumentException(OUTPUT_NOT_DEFINED) } - def collectViolations = createCollectViolationsTask(violations, project.file(output)) - evaluateViolations.dependsOn collectViolations - collectViolations.dependsOn detektTask + def outputFolder = project.file(output) + return createCollectViolationsTask( + violations, + project.tasks['detektCheck'], + new File(outputFolder, 'detekt-checkstyle.xml'), + new File(outputFolder, 'detekt-report.html') + ) } private static resolveOutput(detekt) { @@ -75,11 +89,13 @@ class DetektConfigurator implements Configurator { } } - private CollectDetektViolationsTask createCollectViolationsTask(Violations violations, File outputFolder) { + private CollectDetektViolationsTask createCollectViolationsTask(Violations violations, detektTask, File xmlReportFile, File htmlReportFile) { project.tasks.create('collectDetektViolations', CollectDetektViolationsTask) { task -> - task.xmlReportFile = new File(outputFolder, 'detekt-checkstyle.xml') - task.htmlReportFile = new File(outputFolder, 'detekt-report.html') + task.xmlReportFile = xmlReportFile + task.htmlReportFile = htmlReportFile task.violations = violations + + task.dependsOn(detektTask) } } diff --git a/plugin/src/test/groovy/com/novoda/staticanalysis/internal/detekt/DetektNewIntegrationTest.groovy b/plugin/src/test/groovy/com/novoda/staticanalysis/internal/detekt/DetektNewIntegrationTest.groovy new file mode 100644 index 0000000..83e222c --- /dev/null +++ b/plugin/src/test/groovy/com/novoda/staticanalysis/internal/detekt/DetektNewIntegrationTest.groovy @@ -0,0 +1,162 @@ +package com.novoda.staticanalysis.internal.detekt + +import com.novoda.test.Fixtures +import com.novoda.test.TestProject +import com.novoda.test.TestProjectRule +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +import static com.novoda.test.LogsSubject.assertThat + +@RunWith(Parameterized.class) +class DetektNewIntegrationTest { + + private static final String DETEKT_NOT_APPLIED = 'The Detekt plugin is configured but not applied. Please apply the plugin in your build script.' + + @Parameterized.Parameters(name = "{0} with Detekt: {1}") + static Iterable rules() { + return [ + [TestProjectRule.forKotlinProject(), "1.0.0.RC9.2"], + [TestProjectRule.forAndroidKotlinProject(), "1.0.0.RC9.2"], + ]*.toArray() + } + + @Rule + public final TestProjectRule projectRule + private final String detektVersion + + DetektNewIntegrationTest(TestProjectRule projectRule, String detektVersion) { + this.projectRule = projectRule + this.detektVersion = detektVersion + } + + @Test + void shouldFailBuildOnConfigurationWhenDetektConfiguredButNotApplied() { + def result = projectRule.newProject() + .withToolsConfig(detektConfiguration(Fixtures.Detekt.SOURCES_WITH_ERRORS, detektVersion)) + .buildAndFail('check') + + assertThat(result.logs).contains(DETEKT_NOT_APPLIED) + } + + @Test + void shouldFailBuildWhenDetektWarningsOverTheThreshold() { + def result = createProjectWithZeroThreshold(Fixtures.Detekt.SOURCES_WITH_WARNINGS) + .buildAndFail('check') + + assertThat(result.logs).containsLimitExceeded(0, 1) + assertThat(result.logs).containsDetektViolations(0, 1, + result.buildFileUrl('reports/detekt/detekt.html')) + } + + @Test + void shouldFailBuildWhenDetektErrorsOverTheThreshold() { + def result = createProjectWithZeroThreshold(Fixtures.Detekt.SOURCES_WITH_ERRORS) + .buildAndFail('check') + + assertThat(result.logs).containsLimitExceeded(1, 0) + assertThat(result.logs).containsDetektViolations(1, 0, + result.buildFileUrl('reports/detekt/detekt.html')) + } + + @Test + void shouldNotFailWhenDetektIsNotConfigured() throws Exception { + def result = createProjectWithoutDetekt() + .build('check') + + assertThat(result.logs).doesNotContainDetektViolations() + } + + @Test + void shouldNotFailWhenWarningsAreWithinThreshold() throws Exception { + def result = createProjectWith(Fixtures.Detekt.SOURCES_WITH_WARNINGS, 1, 0) + .build('check') + + assertThat(result.logs).containsDetektViolations(0, 1, + result.buildFileUrl('reports/detekt/detekt.html')) + } + + @Test + void shouldNotFailWhenErrorsAreWithinThreshold() throws Exception { + def result = createProjectWith(Fixtures.Detekt.SOURCES_WITH_ERRORS, 0, 1) + .build('check') + + assertThat(result.logs).containsDetektViolations(1, 0, + result.buildFileUrl('reports/detekt/detekt.html')) + } + + @Test + void shouldNotFailBuildWhenNoDetektWarningsOrErrorsEncounteredAndNoThresholdTrespassed() { + def testProject = projectRule.newProject() + .withPlugin("io.gitlab.arturbosch.detekt", detektVersion) + .withPenalty('''{ + maxWarnings = 0 + maxErrors = 0 + }''') + .withToolsConfig(detektConfigurationWithoutInput(detektVersion)) + + TestProject.Result result = testProject + .build('check') + + assertThat(result.logs).doesNotContainLimitExceeded() + assertThat(result.logs).doesNotContainDetektViolations() + } + + private TestProject createProjectWithZeroThreshold(File sources) { + createProjectWith(sources) + } + + private TestProject createProjectWith(File sources, int maxWarnings = 0, int maxErrors = 0) { + projectRule.newProject() + .withPlugin("io.gitlab.arturbosch.detekt", detektVersion) + .withSourceSet('main', sources) + .withPenalty("""{ + maxWarnings = ${maxWarnings} + maxErrors = ${maxErrors} + }""") + .withToolsConfig(detektConfiguration(sources, detektVersion)) + } + + private TestProject createProjectWithoutDetekt() { + projectRule.newProject() + .withPlugin("io.gitlab.arturbosch.detekt", detektVersion) + .withSourceSet('main', Fixtures.Detekt.SOURCES_WITH_WARNINGS) + .withPenalty('''{ + maxWarnings = 0 + maxErrors = 0 + }''') + } + + private static String detektConfiguration(File input, String detektVersion) { + detektWith(detektVersion, """ + config = files('${Fixtures.Detekt.RULES}') + // The input just needs to be configured for the tests. + // Probably detekt doesn't pick up the changed source sets. + // In a example project it was not needed. + input = files("${input}") + """) + } + + private static String detektConfigurationWithoutInput(String detektVersion) { + detektWith(detektVersion, """ + config = files('${Fixtures.Detekt.RULES}') + """) + } + + private static String detektWith(String detektVersion, String configuration) { + """ + detekt { + toolVersion '${detektVersion}' + + ${configuration.stripIndent()} + + reports { + xml.enabled = true + html.enabled = true + } + } + """ + } +}