Repository: jeremymailen/kotlinter-gradle
Pull request #349 (2023-10-23 19:14:36)

Add optional configuration for `formatKotlin` task to fail build if it cannot fix all lint errors
Fix for issue [#347](https://github.com/jeremymailen/kotlinter-gradle/issues/347).

Please see issue for explanation. The optional configuration mentioned is named `failBuildWhenCannotAutoFormat`.


Patch:
diff --git a/README.md b/README.md
index 3c05916..e16ca13 100644
--- a/README.md
+++ b/README.md
@@ -155,6 +155,7 @@ Options are configured in the `kotlinter` extension. Defaults shown (you may omi
 
 ```kotlin
 kotlinter {
+    failBuildWhenCannotAutoFormat = false
     ignoreFailures = false
     reporters = arrayOf("checkstyle", "plain")
 }
@@ -167,6 +168,7 @@ kotlinter {
 
 ```groovy
 kotlinter {
+    failBuildWhenCannotAutoFormat = false
     ignoreFailures = false
     reporters = ['checkstyle', 'plain']
 }
@@ -174,6 +176,8 @@ kotlinter {
 
 </details>
 
+Setting `failBuildWhenCannotAutoFormat` to `true` will configure the `formatKotlin` task to fail the build when auto-format is not able to fix a lint error. This is overrided by setting `ignoreFailures` to `true`.
+
 Options for `reporters`: `checkstyle`, `html`, `json`, `plain`, `sarif`
 
 Reporters behave as described at: https://github.com/pinterest/ktlint
diff --git a/build.gradle.kts b/build.gradle.kts
index 48401b6..dfcb3f6 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -69,6 +69,7 @@ dependencies {
 
     testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.junit}")
     testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.junit}")
+    testImplementation("org.junit.jupiter:junit-jupiter-params:${Versions.junit}")
     testImplementation("commons-io:commons-io:2.12.0")
     testImplementation("org.mockito.kotlin:mockito-kotlin:${Versions.mockitoKotlin}")
 }
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterExtension.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterExtension.kt
index 2618933..2c0ce31 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterExtension.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterExtension.kt
@@ -4,11 +4,12 @@ import org.jmailen.gradle.kotlinter.support.ReporterType
 
 open class KotlinterExtension {
     companion object {
+        const val DEFAULT_FAIL_BUILD_WHEN_CANNOT_AUTO_FORMAT = false
         const val DEFAULT_IGNORE_FAILURES = false
         val DEFAULT_REPORTER = ReporterType.checkstyle.name
     }
 
+    var failBuildWhenCannotAutoFormat = DEFAULT_FAIL_BUILD_WHEN_CANNOT_AUTO_FORMAT
     var ignoreFailures = DEFAULT_IGNORE_FAILURES
-
     var reporters = arrayOf(DEFAULT_REPORTER)
 }
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterPlugin.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterPlugin.kt
index 9fc3f12..9d1af7a 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterPlugin.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterPlugin.kt
@@ -59,6 +59,8 @@ class KotlinterPlugin : Plugin<Project> {
                         FormatTask::class.java,
                     ) { formatTask ->
                         formatTask.source(resolvedSources)
+                        formatTask.failBuildWhenCannotAutoFormat.set(provider { kotlinterExtension.failBuildWhenCannotAutoFormat })
+                        formatTask.ignoreFailures.set(provider { kotlinterExtension.ignoreFailures })
                         formatTask.report.set(reportFile("$id-format.txt"))
                     }
                     formatKotlin.configure { formatTask ->
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/FormatTask.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/FormatTask.kt
index fd2d681..0169bb9 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/FormatTask.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/FormatTask.kt
@@ -4,13 +4,17 @@ import org.gradle.api.GradleException
 import org.gradle.api.file.ProjectLayout
 import org.gradle.api.file.RegularFileProperty
 import org.gradle.api.model.ObjectFactory
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.OutputFile
 import org.gradle.api.tasks.TaskAction
 import org.gradle.work.InputChanges
 import org.gradle.workers.WorkerExecutor
 import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
+import org.jmailen.gradle.kotlinter.KotlinterExtension
 import org.jmailen.gradle.kotlinter.support.KotlinterError
+import org.jmailen.gradle.kotlinter.support.LintFailure
 import org.jmailen.gradle.kotlinter.tasks.format.FormatWorkerAction
 import javax.inject.Inject
 
@@ -27,6 +31,14 @@ open class FormatTask @Inject constructor(
     @Optional
     val report: RegularFileProperty = objectFactory.fileProperty()
 
+    @Input
+    val ignoreFailures: Property<Boolean> = objectFactory.property(default = KotlinterExtension.DEFAULT_IGNORE_FAILURES)
+
+    @Input
+    val failBuildWhenCannotAutoFormat: Property<Boolean> = objectFactory.property(
+        default = KotlinterExtension.DEFAULT_FAIL_BUILD_WHEN_CANNOT_AUTO_FORMAT,
+    )
+
     init {
         outputs.upToDateWhen { false }
     }
@@ -48,5 +60,12 @@ open class FormatTask @Inject constructor(
             forEach { logger.error(it.message, it.cause) }
             throw GradleException("error formatting sources for $name")
         }
+
+        if (failBuildWhenCannotAutoFormat.get()) {
+            val lintFailures = result.exceptionOrNull()?.workErrorCauses<LintFailure>() ?: emptyList()
+            if (lintFailures.isNotEmpty() && !ignoreFailures.get()) {
+                throw GradleException("$name sources failed lint check")
+            }
+        }
     }
 }
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerAction.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerAction.kt
index ae88f57..09f90a0 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerAction.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerAction.kt
@@ -7,6 +7,7 @@ import org.gradle.api.logging.Logging
 import org.gradle.internal.logging.slf4j.DefaultContextAwareTaskLogger
 import org.gradle.workers.WorkAction
 import org.jmailen.gradle.kotlinter.support.KotlinterError
+import org.jmailen.gradle.kotlinter.support.LintFailure
 import org.jmailen.gradle.kotlinter.support.ktlintEngine
 import org.jmailen.gradle.kotlinter.support.resetEditorconfigCacheIfNeeded
 import org.jmailen.gradle.kotlinter.tasks.FormatTask
@@ -26,6 +27,7 @@ abstract class FormatWorkerAction : WorkAction<FormatWorkerParameters> {
         )
         val fixes = mutableListOf<String>()
 
+        var hasError = false
         try {
             files.forEach { file ->
 
@@ -44,7 +46,12 @@ abstract class FormatWorkerAction : WorkAction<FormatWorkerParameters> {
                         true -> "${file.path}:${error.line}:${error.col}: Format fixed > [${error.ruleId.value}] ${error.detail}"
                         false -> "${file.path}:${error.line}:${error.col}: Format could not fix > [${error.ruleId.value}] ${error.detail}"
                     }
-                    logger.warn(msg)
+                    if (corrected) {
+                        logger.warn(msg)
+                    } else {
+                        hasError = true
+                        logger.error(msg)
+                    }
                     fixes.add(msg)
                 }
                 if (!formattedText.contentEquals(sourceText)) {
@@ -56,6 +63,10 @@ abstract class FormatWorkerAction : WorkAction<FormatWorkerParameters> {
             throw KotlinterError("format worker execution error", t)
         }
 
+        if (hasError) {
+            throw LintFailure("kotlin source failed lint check")
+        }
+
         output?.writeText(
             when (fixes.isEmpty()) {
                 true -> "ok"


Test patch:
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/EditorConfigTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/EditorConfigTest.kt
index 8b93fa3..2b19e0d 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/EditorConfigTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/EditorConfigTest.kt
@@ -1,6 +1,7 @@
 package org.jmailen.gradle.kotlinter.functional
 
 import org.gradle.testkit.runner.TaskOutcome
+import org.jmailen.gradle.kotlinter.functional.utils.KotlinterConfig
 import org.jmailen.gradle.kotlinter.functional.utils.editorConfig
 import org.jmailen.gradle.kotlinter.functional.utils.kotlinClass
 import org.jmailen.gradle.kotlinter.functional.utils.resolve
@@ -8,7 +9,6 @@ import org.jmailen.gradle.kotlinter.functional.utils.settingsFile
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Assertions.assertFalse
 import org.junit.jupiter.api.Assertions.assertTrue
-import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
 import java.io.File
 
@@ -16,27 +16,50 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() {
 
     lateinit var projectRoot: File
 
-    @BeforeEach
-    fun setUp() {
+    private fun setup(kotlinterConfig: KotlinterConfig) {
         projectRoot = testProjectDir.apply {
             resolve("settings.gradle") { writeText(settingsFile) }
             resolve("build.gradle") {
-                // language=groovy
-                val buildScript =
-                    """
-                plugins {
-                    id 'kotlin'
-                    id 'org.jmailen.kotlinter'
+                val buildscript = when (kotlinterConfig) {
+                    KotlinterConfig.DEFAULT ->
+                        """
+                        plugins {
+                            id 'kotlin'
+                            id 'org.jmailen.kotlinter'
+                        }
+                        """.trimIndent()
+                    KotlinterConfig.FAIL_BUILD_WHEN_CANNOT_AUTO_FORMAT ->
+                        """
+                        plugins {
+                            id 'org.jetbrains.kotlin.js'
+                            id 'org.jmailen.kotlinter'
+                        }
+
+                        kotlinter {
+                            failBuildWhenCannotAutoFormat = true
+                        }
+                        """.trimIndent()
+                    KotlinterConfig.IGNORE_FAILURES ->
+                        """
+                        plugins {
+                            id 'org.jetbrains.kotlin.js'
+                            id 'org.jmailen.kotlinter'
+                        }
+
+                        kotlinter {
+                            ignoreFailures = true
+                            failBuildWhenCannotAutoFormat = true
+                        }
+                        """.trimIndent()
                 }
-                
-                    """.trimIndent()
-                writeText(buildScript)
+                writeText(buildscript)
             }
         }
     }
 
     @Test
     fun `lintTask uses default indentation if editorconfig absent`() {
+        setup(KotlinterConfig.DEFAULT)
         projectRoot.resolve("src/main/kotlin/FourSpacesByDefault.kt") {
             writeText(
                 """ |package com.example
@@ -56,6 +79,7 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() {
 
     @Test
     fun `plugin respects disabled_rules set in editorconfig`() {
+        setup(KotlinterConfig.DEFAULT)
         projectRoot.resolve(".editorconfig") {
             appendText(
                 // language=editorconfig
@@ -76,6 +100,7 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() {
 
     @Test
     fun `plugin respects 'indent_size' set in editorconfig`() {
+        setup(KotlinterConfig.DEFAULT)
         projectRoot.resolve(".editorconfig") {
             appendText(
                 // language=editorconfig
@@ -107,6 +132,7 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() {
 
     @Test
     fun `editorconfig changes are taken into account on lint task re-runs`() {
+        setup(KotlinterConfig.DEFAULT)
         projectRoot.resolve(".editorconfig") {
             writeText(
                 // language=editorconfig
@@ -148,6 +174,7 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() {
 
     @Test
     fun `editorconfig changes are ignored for format task re-runs`() {
+        setup(KotlinterConfig.DEFAULT)
         projectRoot.resolve(".editorconfig") {
             writeText(editorConfig)
         }
@@ -177,4 +204,37 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() {
             assertFalse(output.contains("resetting KtLint caches"))
         }
     }
+
+    @Test
+    fun `editorconfig changes are ignored for format task re-runs when failBuildWhenCannotAutoFormat enabled`() {
+        setup(KotlinterConfig.FAIL_BUILD_WHEN_CANNOT_AUTO_FORMAT)
+        projectRoot.resolve(".editorconfig") {
+            writeText(editorConfig)
+        }
+
+        projectRoot.resolve("src/main/kotlin/FileName.kt") {
+            writeText(kotlinClass("DifferentClassName"))
+        }
+        buildAndFail("formatKotlin").apply {
+            assertEquals(TaskOutcome.FAILED, task(":formatKotlinMain")?.outcome)
+            assertTrue(
+                output.contains("Format could not fix > [standard:filename] File 'FileName.kt' contains a single top level declaration"),
+            )
+        }
+
+        projectRoot.resolve(".editorconfig") {
+            writeText(
+                // language=editorconfig
+                """
+                    [*.{kt,kts}]
+                    ktlint_standard_filename = disabled
+                """.trimIndent(),
+            )
+        }
+        buildAndFail("formatKotlin", "--info").apply {
+            assertEquals(TaskOutcome.FAILED, task(":formatKotlinMain")?.outcome)
+            assertTrue(output.contains("Format could not fix"))
+            assertFalse(output.contains("resetting KtLint caches"))
+        }
+    }
 }
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinJsProjectTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinJsProjectTest.kt
index 61a7908..c2df724 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinJsProjectTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinJsProjectTest.kt
@@ -1,48 +1,88 @@
 package org.jmailen.gradle.kotlinter.functional
 
 import org.gradle.testkit.runner.TaskOutcome
+import org.jmailen.gradle.kotlinter.functional.utils.KotlinterConfig
 import org.jmailen.gradle.kotlinter.functional.utils.kotlinClass
 import org.jmailen.gradle.kotlinter.functional.utils.resolve
 import org.jmailen.gradle.kotlinter.functional.utils.settingsFile
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Assertions.assertTrue
-import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
 import java.io.File
-
 class KotlinJsProjectTest : WithGradleTest.Kotlin() {
 
     lateinit var projectRoot: File
-
-    @BeforeEach
-    fun setup() {
+    private fun setup(kotlinterConfig: KotlinterConfig) {
         projectRoot = testProjectDir.apply {
             resolve("settings.gradle") { writeText(settingsFile) }
             resolve("build.gradle") {
-                // language=groovy
-                writeText(
-                    """
-                    plugins {
-                        id 'org.jetbrains.kotlin.js'
-                        id 'org.jmailen.kotlinter'
-                    }
-                    
-                    repositories.mavenCentral()
-                    
-                    kotlin {
-                        js(IR) {
-                            browser()
-                            binaries.executable()
+                val buildscript = when (kotlinterConfig) {
+                    KotlinterConfig.DEFAULT ->
+                        """
+                        plugins {
+                            id 'org.jetbrains.kotlin.js'
+                            id 'org.jmailen.kotlinter'
                         }
-                    }
-                    """.trimIndent(),
-                )
+    
+                        repositories.mavenCentral()
+    
+                        kotlin {
+                            js(IR) {
+                                browser()
+                                binaries.executable()
+                            }
+                        }
+                        """.trimIndent()
+                    KotlinterConfig.FAIL_BUILD_WHEN_CANNOT_AUTO_FORMAT ->
+                        """
+                        plugins {
+                            id 'org.jetbrains.kotlin.js'
+                            id 'org.jmailen.kotlinter'
+                        }
+    
+                        repositories.mavenCentral()
+    
+                        kotlin {
+                            js(IR) {
+                                browser()
+                                binaries.executable()
+                            }
+                        }
+                        
+                        kotlinter {
+                            failBuildWhenCannotAutoFormat = true
+                        }
+                        """.trimIndent()
+                    KotlinterConfig.IGNORE_FAILURES ->
+                        """
+                        plugins {
+                            id 'org.jetbrains.kotlin.js'
+                            id 'org.jmailen.kotlinter'
+                        }
+    
+                        repositories.mavenCentral()
+    
+                        kotlin {
+                            js(IR) {
+                                browser()
+                                binaries.executable()
+                            }
+                        }
+                        
+                        kotlinter {
+                            ignoreFailures = true
+                            failBuildWhenCannotAutoFormat = true
+                        }
+                        """.trimIndent()
+                }
+                writeText(buildscript)
             }
         }
     }
 
     @Test
     fun `lintKotlin passes when on valid kotlin files`() {
+        setup(KotlinterConfig.DEFAULT)
         projectRoot.resolve("src/main/kotlin/FixtureFileName.kt") {
             writeText(kotlinClass("FixtureFileName"))
         }
@@ -58,6 +98,7 @@ class KotlinJsProjectTest : WithGradleTest.Kotlin() {
 
     @Test
     fun `lintKotlin fails when lint errors detected`() {
+        setup(KotlinterConfig.DEFAULT)
         projectRoot.resolve("src/main/kotlin/FixtureFileName.kt") {
             writeText(kotlinClass("DifferentClassName"))
         }
@@ -79,6 +120,89 @@ class KotlinJsProjectTest : WithGradleTest.Kotlin() {
 
     @Test
     fun `formatKotlin reports formatted and unformatted files`() {
+        setup(KotlinterConfig.DEFAULT)
+        projectRoot.resolve("src/main/kotlin/FixtureClass.kt") {
+            // language=kotlin
+            val kotlinClass =
+                """
+                import System.*
+                
+                class FixtureClass{
+                    private fun hi() {
+                        out.println("Hello")
+                    }
+                }
+                """.trimIndent()
+            writeText(kotlinClass)
+        }
+        projectRoot.resolve("src/test/kotlin/FixtureTestClass.kt") {
+            // language=kotlin
+            val kotlinClass =
+                """
+                import System.*
+                
+                class FixtureTestClass{
+                    private fun hi() {
+                        out.println("Hello")
+                    }
+                }
+                """.trimIndent()
+            writeText(kotlinClass)
+        }
+        build("formatKotlin").apply {
+            assertEquals(TaskOutcome.SUCCESS, task(":formatKotlinMain")?.outcome)
+            assertTrue(output.contains("FixtureClass.kt:3:19: Format fixed > [standard:curly-spacing] Missing spacing before \"{\""))
+            assertTrue(output.contains("FixtureClass.kt:1:1: Format could not fix > [standard:no-wildcard-imports] Wildcard import"))
+            assertEquals(TaskOutcome.SUCCESS, task(":formatKotlinTest")?.outcome)
+            assertTrue(output.contains("FixtureTestClass.kt:3:23: Format fixed > [standard:curly-spacing] Missing spacing before \"{\""))
+            assertTrue(output.contains("FixtureTestClass.kt:1:1: Format could not fix > [standard:no-wildcard-imports] Wildcard import"))
+        }
+    }
+
+    @Test
+    fun `formatKotlin fails when lint errors not automatically fixed and failBuildWhenCannotAutoFormat enabled`() {
+        setup(KotlinterConfig.FAIL_BUILD_WHEN_CANNOT_AUTO_FORMAT)
+        projectRoot.resolve("src/main/kotlin/FixtureClass.kt") {
+            // language=kotlin
+            val kotlinClass =
+                """
+                import System.*
+                
+                class FixtureClass{
+                    private fun hi() {
+                        out.println("Hello")
+                    }
+                }
+                """.trimIndent()
+            writeText(kotlinClass)
+        }
+        projectRoot.resolve("src/test/kotlin/FixtureTestClass.kt") {
+            // language=kotlin
+            val kotlinClass =
+                """
+                import System.*
+                
+                class FixtureTestClass{
+                    private fun hi() {
+                        out.println("Hello")
+                    }
+                }
+                """.trimIndent()
+            writeText(kotlinClass)
+        }
+        buildAndFail("formatKotlin").apply {
+            assertEquals(TaskOutcome.FAILED, task(":formatKotlinMain")?.outcome)
+            assertTrue(output.contains("FixtureClass.kt:3:19: Format fixed > [standard:curly-spacing] Missing spacing before \"{\""))
+            assertTrue(output.contains("FixtureClass.kt:1:1: Format could not fix > [standard:no-wildcard-imports] Wildcard import"))
+            assertEquals(TaskOutcome.FAILED, task(":formatKotlinTest")?.outcome)
+            assertTrue(output.contains("FixtureTestClass.kt:3:23: Format fixed > [standard:curly-spacing] Missing spacing before \"{\""))
+            assertTrue(output.contains("FixtureTestClass.kt:1:1: Format could not fix > [standard:no-wildcard-imports] Wildcard import"))
+        }
+    }
+
+    @Test
+    fun `formatKotlin reports formatted and unformatted files when failBuildWhenCannotAutoFormat and ignoreFailures enabled`() {
+        setup(KotlinterConfig.IGNORE_FAILURES)
         projectRoot.resolve("src/main/kotlin/FixtureClass.kt") {
             // language=kotlin
             val kotlinClass =
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinProjectTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinProjectTest.kt
index 1530ed6..95037c6 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinProjectTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinProjectTest.kt
@@ -102,6 +102,58 @@ internal class KotlinProjectTest : WithGradleTest.Kotlin() {
         }
     }
 
+    @Test
+    fun `formatKotlin fails when lint errors not automatically fixed and failBuildWhenCannotAutoFormat enabled`() {
+        settingsFile()
+        buildFileFailBuildWhenCannotAutoFormat()
+        // language=kotlin
+        val kotlinClass =
+            """
+            import System.*
+            
+            class KotlinClass{
+                private fun hi() {
+                    out.println("Hello")
+                }
+            }
+            """.trimIndent()
+        kotlinSourceFile("KotlinClass.kt", kotlinClass)
+
+        buildAndFail("formatKotlin").apply {
+            assertEquals(FAILED, task(":formatKotlinMain")?.outcome)
+            output.lines().filter { it.contains("Format could not fix") }.forEach { line ->
+                val filePath = pathPattern.find(line)?.groups?.get(1)?.value.orEmpty()
+                assertTrue(File(filePath).exists())
+            }
+        }
+    }
+
+    @Test
+    fun `formatKotlin reports formatted and unformatted files when failBuildWhenCannotAutoFormat and ignoreFailures enabled`() {
+        settingsFile()
+        buildFileIgnoreFailures()
+        // language=kotlin
+        val kotlinClass =
+            """
+            import System.*
+            
+            class KotlinClass{
+                private fun hi() {
+                    out.println("Hello")
+                }
+            }
+            """.trimIndent()
+        kotlinSourceFile("KotlinClass.kt", kotlinClass)
+
+        build("formatKotlin").apply {
+            assertEquals(SUCCESS, task(":formatKotlinMain")?.outcome)
+            output.lines().filter { it.contains("Format could not fix") }.forEach { line ->
+                val filePath = pathPattern.find(line)?.groups?.get(1)?.value.orEmpty()
+                assertTrue(File(filePath).exists())
+            }
+        }
+    }
+
     @Test
     fun `check task runs lintFormat`() {
         settingsFile()
@@ -210,6 +262,47 @@ internal class KotlinProjectTest : WithGradleTest.Kotlin() {
         writeText(buildscript)
     }
 
+    private fun buildFileFailBuildWhenCannotAutoFormat() = buildFile.apply {
+        // language=groovy
+        val buildscript =
+            """
+            plugins {
+                id 'org.jetbrains.kotlin.jvm'
+                id 'org.jmailen.kotlinter'
+            }
+
+            repositories {
+                mavenCentral()
+            }
+            
+            kotlinter {
+                failBuildWhenCannotAutoFormat = true
+            }
+            """.trimIndent()
+        writeText(buildscript)
+    }
+
+    private fun buildFileIgnoreFailures() = buildFile.apply {
+        // language=groovy
+        val buildscript =
+            """
+            plugins {
+                id 'org.jetbrains.kotlin.jvm'
+                id 'org.jmailen.kotlinter'
+            }
+
+            repositories {
+                mavenCentral()
+            }
+            
+            kotlinter {
+                ignoreFailures = true
+                failBuildWhenCannotAutoFormat = true
+            }
+            """.trimIndent()
+        writeText(buildscript)
+    }
+
     private fun kotlinSourceFile(name: String, content: String) = File(sourceDir, name).apply {
         writeText(content)
     }
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/utils/KotlinterConfig.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/utils/KotlinterConfig.kt
new file mode 100644
index 0000000..b7ea030
--- /dev/null
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/utils/KotlinterConfig.kt
@@ -0,0 +1,7 @@
+package org.jmailen.gradle.kotlinter.functional.utils
+
+enum class KotlinterConfig {
+    DEFAULT,
+    FAIL_BUILD_WHEN_CANNOT_AUTO_FORMAT,
+    IGNORE_FAILURES,
+}