diff --git a/CHANGELOG.md b/CHANGELOG.md index e464e0d..8492376 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,9 @@ Please add your entries according to this format. ## Unreleased +- Add new regex property to exclude sourceSets from analysis (#328) - Fix custom KtFmt tasks not compatible with configuration cache (#290) +- Fix custom source directories not correctly recognized (#292) - Fix mixed up task descriptions (#342) - KtFmt to 0.52 diff --git a/README.md b/README.md index 34f8307..57f1fc6 100644 --- a/README.md +++ b/README.md @@ -114,11 +114,24 @@ ktfmt { } ``` +By default, sourceSets that are in your `build` folder are ignored. To customize this, you can set a regex pattern to +exclude certain sourceSets: + +```kotlin +ktfmt { + srcSetPathExclusionPattern = Regex(".*generated.*") +} +``` + +Please note, that this property will only affect the sourceSet path and not the individual files inside the sourceSet. +To include or exclude files inside a sourceSet, use the include and exclude properties of the ktfmt tasks. + ## Using with a pre-commit hook 🎣 You can leverage the `--include-only` to let ktfmt-gradle run only on a specific subset of files. -To this you can register a simple task of type `KtfmtCheckTask` or `KtfmtFormatTask` in your `build.gradle.kts` as follows: +To do this you can register a simple task of type `KtfmtCheckTask` or `KtfmtFormatTask` in your `build.gradle.kts` as +follows: ```kotlin import com.ncorti.ktfmt.gradle.tasks.* @@ -129,7 +142,8 @@ tasks.register("ktfmtPrecommit") { } ``` -You can then invoke the task with `--include-only` and a comma-separated (or colon-separated) list of relative path of files: +You can then invoke the task with `--include-only` and a comma-separated (or colon-separated) list of relative path of +files: ``` ./gradlew ktfmtPrecommit --include-only=src/main/java/File1.kt:src/main/java/File2.kt diff --git a/example/build.gradle.kts b/example/build.gradle.kts index ef75fe4..eb0a694 100644 --- a/example/build.gradle.kts +++ b/example/build.gradle.kts @@ -24,4 +24,3 @@ tasks.withType { useJUnitPlatform() } tasks.withType { enabled = false } sqldelight { databases { create("Database") { packageName.set("com.example") } } } - diff --git a/plugin-build/plugin/api/plugin.api b/plugin-build/plugin/api/plugin.api index ba1a2c5..5b197b6 100644 --- a/plugin-build/plugin/api/plugin.api +++ b/plugin-build/plugin/api/plugin.api @@ -6,6 +6,7 @@ public abstract class com/ncorti/ktfmt/gradle/KtfmtExtension { public abstract fun getManageTrailingCommas ()Lorg/gradle/api/provider/Property; public abstract fun getMaxWidth ()Lorg/gradle/api/provider/Property; public abstract fun getRemoveUnusedImports ()Lorg/gradle/api/provider/Property; + public abstract fun getSrcSetPathExclusionPattern ()Lorg/gradle/api/provider/Property; public final fun googleStyle ()V public final fun kotlinLangStyle ()V } @@ -23,6 +24,7 @@ public final class com/ncorti/ktfmt/gradle/KtfmtPlugin$Companion { public abstract class com/ncorti/ktfmt/gradle/tasks/KtfmtBaseTask : org/gradle/api/tasks/SourceTask { protected abstract fun execute (Lorg/gradle/workers/WorkQueue;)V public abstract fun getIncludeOnly ()Lorg/gradle/api/provider/Property; + public fun getSource ()Lorg/gradle/api/file/FileTree; } public abstract class com/ncorti/ktfmt/gradle/tasks/KtfmtCheckTask : com/ncorti/ktfmt/gradle/tasks/KtfmtBaseTask { @@ -32,6 +34,5 @@ public abstract class com/ncorti/ktfmt/gradle/tasks/KtfmtCheckTask : com/ncorti/ public abstract class com/ncorti/ktfmt/gradle/tasks/KtfmtFormatTask : com/ncorti/ktfmt/gradle/tasks/KtfmtBaseTask { protected fun execute (Lorg/gradle/workers/WorkQueue;)V - protected final fun getOutputFiles ()Lorg/gradle/api/file/FileCollection; } diff --git a/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtAndroidUtils.kt b/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtAndroidUtils.kt index b6ccdbc..a708a8f 100644 --- a/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtAndroidUtils.kt +++ b/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtAndroidUtils.kt @@ -14,6 +14,7 @@ internal object KtfmtAndroidUtils { project: Project, topLevelFormat: TaskProvider, topLevelCheck: TaskProvider, + ktfmtExtension: KtfmtExtension, isKmpProject: Boolean = false ) { fun applyKtfmtForAndroid() { @@ -43,6 +44,7 @@ internal object KtfmtAndroidUtils { project, sourceSetName, project.files(Callable { srcDirs }), + ktfmtExtension, topLevelFormat, topLevelCheck, ) diff --git a/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtExtension.kt b/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtExtension.kt index c9918f8..db0bd74 100644 --- a/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtExtension.kt +++ b/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtExtension.kt @@ -12,6 +12,7 @@ abstract class KtfmtExtension { removeUnusedImports.convention(DEFAULT_REMOVE_UNUSED_IMPORTS) debuggingPrintOpsAfterFormatting.convention(DEFAULT_DEBUGGING_PRINT_OPTS) manageTrailingCommas.convention(DEFAULT_MANAGE_TRAILING_COMMAS) + srcSetPathExclusionPattern.convention(DEFAULT_SRC_SET_PATH_EXCLUSION_PATTERN) } /** ktfmt breaks lines longer than maxWidth. Default 100. */ @@ -53,6 +54,15 @@ abstract class KtfmtExtension { /** Whether ktfmt should remove imports that are not used. */ abstract val removeUnusedImports: Property + /** + * Regex to define what sourceSets paths should not be formatted + * + * For example the sourceSet "main" in the "build" folder would be excluded with the regex + * "^(.*[\\/])?build([\\/].*)?$". This does not affect files inside a sourceSet. To exclude a + * file inside a sourceSet use the include & exclude options on the ktfmt task. + */ + abstract val srcSetPathExclusionPattern: Property + /** * Print the Ops generated by KotlinInputAstVisitor to help reason about formatting (i.e., * newline) decisions @@ -94,5 +104,7 @@ abstract class KtfmtExtension { internal const val DEFAULT_REMOVE_UNUSED_IMPORTS: Boolean = true internal const val DEFAULT_DEBUGGING_PRINT_OPTS: Boolean = false internal const val DEFAULT_MANAGE_TRAILING_COMMAS: Boolean = false + internal val DEFAULT_SRC_SET_PATH_EXCLUSION_PATTERN = + Regex("^(.*[\\\\/])?build([\\\\/].*)?\$") } } diff --git a/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtPlugin.kt b/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtPlugin.kt index 71b75e7..a7fa03e 100644 --- a/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtPlugin.kt +++ b/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtPlugin.kt @@ -56,34 +56,35 @@ abstract class KtfmtPlugin : Plugin { topLevelFormat = createTopLevelFormatTask(project) topLevelCheck = createTopLevelCheckTask(project) - project.plugins.withId("kotlin") { applyKtfmt(project) } + project.plugins.withId("kotlin") { applyKtfmt(project, ktfmtExtension) } project.plugins.withId("kotlin-android") { if (project.plugins.hasPlugin("org.jetbrains.kotlin.multiplatform")) { project.logger.i("Skipping Android task creation, as KMP is applied") } else { - applyKtfmtToAndroidProject(project, topLevelFormat, topLevelCheck) + applyKtfmtToAndroidProject(project, topLevelFormat, topLevelCheck, ktfmtExtension) } } - project.plugins.withId("org.jetbrains.kotlin.js") { applyKtfmt(project) } + project.plugins.withId("org.jetbrains.kotlin.js") { applyKtfmt(project, ktfmtExtension) } project.plugins.withId("org.jetbrains.kotlin.multiplatform") { - applyKtfmtToMultiplatformProject(project) + applyKtfmtToMultiplatformProject(project, ktfmtExtension) } } - private fun applyKtfmt(project: Project) { + private fun applyKtfmt(project: Project, ktfmtExtension: KtfmtExtension) { val extension = project.extensions.getByType(KotlinProjectExtension::class.java) extension.sourceSets.all { createTasksForSourceSet( project, it.name, it.kotlin.sourceDirectories, + ktfmtExtension, topLevelFormat, topLevelCheck, ) } } - private fun applyKtfmtToMultiplatformProject(project: Project) { + private fun applyKtfmtToMultiplatformProject(project: Project, ktfmtExtension: KtfmtExtension) { val extension = project.extensions.getByType(KotlinMultiplatformExtension::class.java) extension.sourceSets.all { val name = "kmp ${it.name}" @@ -96,6 +97,7 @@ abstract class KtfmtPlugin : Plugin { project, name, it.kotlin.sourceDirectories, + ktfmtExtension, topLevelFormat, topLevelCheck, ) @@ -104,7 +106,7 @@ abstract class KtfmtPlugin : Plugin { extension.targets.all { kotlinTarget -> if (kotlinTarget.platformType == KotlinPlatformType.androidJvm) { applyKtfmtToAndroidProject( - project, topLevelFormat, topLevelCheck, isKmpProject = true) + project, topLevelFormat, topLevelCheck, ktfmtExtension, isKmpProject = true) } } } @@ -125,8 +127,6 @@ abstract class KtfmtPlugin : Plugin { } companion object { - internal val defaultExcludes = listOf("**/build/**") - internal val defaultExcludesRegex = Regex("^(.*[\\\\/])?build([\\\\/].*)?\$") internal val defaultIncludes = listOf("**/*.kt", "**/*.kts") } } diff --git a/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtPluginUtils.kt b/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtPluginUtils.kt index 409aea3..b83e768 100644 --- a/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtPluginUtils.kt +++ b/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtPluginUtils.kt @@ -2,9 +2,11 @@ package com.ncorti.ktfmt.gradle import com.ncorti.ktfmt.gradle.tasks.KtfmtCheckTask import com.ncorti.ktfmt.gradle.tasks.KtfmtFormatTask +import java.io.File import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.FileCollection +import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskProvider import org.gradle.language.base.plugins.LifecycleBasePlugin import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -34,6 +36,7 @@ internal object KtfmtPluginUtils { project: Project, srcSetName: String, srcSetDir: FileCollection, + ktfmtExtension: KtfmtExtension, topLevelFormat: TaskProvider, topLevelCheck: TaskProvider ) { @@ -41,8 +44,8 @@ internal object KtfmtPluginUtils { return } - val srcCheckTask = createCheckTask(project, srcSetName, srcSetDir) - val srcFormatTask = createFormatTask(project, srcSetName, srcSetDir) + val srcCheckTask = createCheckTask(project, srcSetName, srcSetDir, ktfmtExtension) + val srcFormatTask = createFormatTask(project, srcSetName, srcSetDir, ktfmtExtension) // When running together with compileKotlin, ktfmt tasks should have precedence as // they're editing the source code @@ -63,7 +66,8 @@ internal object KtfmtPluginUtils { private fun createCheckTask( project: Project, name: String, - srcDir: FileCollection + srcDir: FileCollection, + ktfmtExtension: KtfmtExtension, ): TaskProvider { val capitalizedName = name.split(" ").joinToString("") { @@ -77,20 +81,21 @@ internal object KtfmtPluginUtils { charArray.concatToString() } val taskName = "$TASK_NAME_CHECK$capitalizedName" - val inputDirs = srcDir.toList() + + val inputDirs = project.getSelectedSrcSets(srcDir, ktfmtExtension) return project.tasks.register(taskName, KtfmtCheckTask::class.java) { it.description = "Run Ktfmt formatter validation for sourceSet '$name' on project '${project.name}'" it.setSource(inputDirs) it.setIncludes(KtfmtPlugin.defaultIncludes) - it.setExcludes(KtfmtPlugin.defaultExcludes) } } private fun createFormatTask( project: Project, name: String, - srcDir: FileCollection + srcDir: FileCollection, + ktfmtExtension: KtfmtExtension, ): TaskProvider { val srcSetName = name.split(" ").joinToString("") { @@ -104,13 +109,24 @@ internal object KtfmtPluginUtils { charArray.concatToString() } val taskName = "$TASK_NAME_FORMAT$srcSetName" - val inputDirs = srcDir.toList() + + val inputDirs = project.getSelectedSrcSets(srcDir, ktfmtExtension) return project.tasks.register(taskName, KtfmtFormatTask::class.java) { it.description = "Run Ktfmt formatter for sourceSet '$name' on project '${project.name}'" it.setSource(inputDirs) it.setIncludes(KtfmtPlugin.defaultIncludes) - it.setExcludes(KtfmtPlugin.defaultExcludes) + } + } + + private fun Project.getSelectedSrcSets( + srcDir: FileCollection, + ktfmtExtension: KtfmtExtension + ): Provider> { + val excludedSourceSets = ktfmtExtension.srcSetPathExclusionPattern + + return provider { + srcDir.toList().filterNot { it.absolutePath.matches(excludedSourceSets.get()) } } } } diff --git a/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/tasks/KtfmtBaseTask.kt b/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/tasks/KtfmtBaseTask.kt index 3806a74..28e5b25 100644 --- a/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/tasks/KtfmtBaseTask.kt +++ b/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/tasks/KtfmtBaseTask.kt @@ -1,24 +1,22 @@ package com.ncorti.ktfmt.gradle.tasks import com.ncorti.ktfmt.gradle.FormattingOptionsBean -import com.ncorti.ktfmt.gradle.KtfmtPlugin import com.ncorti.ktfmt.gradle.tasks.worker.KtfmtWorkAction import com.ncorti.ktfmt.gradle.tasks.worker.Result import com.ncorti.ktfmt.gradle.util.d -import java.io.File -import java.util.* +import java.util.UUID import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.FileCollection +import org.gradle.api.file.FileTree import org.gradle.api.file.ProjectLayout import org.gradle.api.provider.Property -import org.gradle.api.specs.Spec import org.gradle.api.tasks.Classpath import org.gradle.api.tasks.IgnoreEmptyDirectories import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.Internal import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.SkipWhenEmpty import org.gradle.api.tasks.SourceTask import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.options.Option @@ -49,11 +47,11 @@ internal constructor( @get:Input abstract val includeOnly: Property - @get:PathSensitive(PathSensitivity.RELATIVE) - @get:InputFiles - @get:IgnoreEmptyDirectories - internal val inputFiles: FileCollection - get() = super.getSource().filter(defaultExcludesFilter) + @PathSensitive(PathSensitivity.RELATIVE) + @InputFiles + @IgnoreEmptyDirectories + @SkipWhenEmpty + override fun getSource(): FileTree = super.getSource() @TaskAction internal fun taskAction() { @@ -64,16 +62,6 @@ internal constructor( protected abstract fun execute(workQueue: WorkQueue) - @get:Internal - internal val defaultExcludesFilter: Spec = - Spec { - if (this.excludes.containsAll(KtfmtPlugin.defaultExcludes) && this.excludes.size == 1) { - it.absolutePath.matches(KtfmtPlugin.defaultExcludesRegex).not() - } else { - true - } - } - internal fun FileCollection.submitToQueue( queue: WorkQueue, action: Class diff --git a/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/tasks/KtfmtCheckTask.kt b/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/tasks/KtfmtCheckTask.kt index 91a9bb4..a0b7881 100644 --- a/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/tasks/KtfmtCheckTask.kt +++ b/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/tasks/KtfmtCheckTask.kt @@ -29,7 +29,7 @@ internal constructor(workerExecutor: WorkerExecutor, layout: ProjectLayout) : } override fun execute(workQueue: WorkQueue) { - val results = inputFiles.submitToQueue(workQueue, KtfmtCheckAction::class.java) + val results = source.submitToQueue(workQueue, KtfmtCheckAction::class.java) logger.d("Check results: $results") val failures = results.filterIsInstance() diff --git a/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/tasks/KtfmtFormatTask.kt b/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/tasks/KtfmtFormatTask.kt index 1122072..10688b0 100644 --- a/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/tasks/KtfmtFormatTask.kt +++ b/plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/tasks/KtfmtFormatTask.kt @@ -6,9 +6,7 @@ import com.ncorti.ktfmt.gradle.util.KtfmtUtils import com.ncorti.ktfmt.gradle.util.d import com.ncorti.ktfmt.gradle.util.i import javax.inject.Inject -import org.gradle.api.file.FileCollection import org.gradle.api.file.ProjectLayout -import org.gradle.api.tasks.OutputFiles import org.gradle.workers.WorkQueue import org.gradle.workers.WorkerExecutor @@ -18,16 +16,14 @@ abstract class KtfmtFormatTask internal constructor(workerExecutor: WorkerExecutor, layout: ProjectLayout) : KtfmtBaseTask(workerExecutor, layout) { - @get:OutputFiles - protected val outputFiles: FileCollection - get() = inputFiles - init { group = KtfmtUtils.GROUP_FORMATTING + // Since the input files are also the output files, we do not have to specify outputs again + outputs.upToDateWhen { true } } override fun execute(workQueue: WorkQueue) { - val results = inputFiles.submitToQueue(workQueue, KtfmtFormatAction::class.java) + val results = source.submitToQueue(workQueue, KtfmtFormatAction::class.java) logger.d("Format results: $results") val failures = results.filterIsInstance() diff --git a/plugin-build/plugin/src/test/java/com/ncorti/ktfmt/gradle/tasks/KtfmtBaseTaskTest.kt b/plugin-build/plugin/src/test/java/com/ncorti/ktfmt/gradle/tasks/KtfmtBaseTaskTest.kt index 020d54c..ea97a3e 100644 --- a/plugin-build/plugin/src/test/java/com/ncorti/ktfmt/gradle/tasks/KtfmtBaseTaskTest.kt +++ b/plugin-build/plugin/src/test/java/com/ncorti/ktfmt/gradle/tasks/KtfmtBaseTaskTest.kt @@ -39,27 +39,7 @@ internal class KtfmtBaseTaskTest { val file = createTempFile(fileName = "file.kt", content = "") underTest.source(file) - assertThat(underTest.inputFiles.files.contains(file)).isTrue() - } - - @Test - fun `files in build folder are excluded`() { - val underTest = project.tasks.getByName("ktfmtFormatMain") as KtfmtBaseTask - val buildFile = createTempFile(fileName = "something/build/folder/file.kt", content = "") - underTest.source(buildFile) - - assertThat(underTest.inputFiles.files.contains(buildFile)).isFalse() - } - - @Test - fun `files in build folder are not excluded if users provided a custom exclude`() { - val underTest = project.tasks.getByName("ktfmtFormatMain") as KtfmtBaseTask - val buildFile = createTempFile(fileName = "something/build/folder/file.kt", content = "") - underTest.source(buildFile) - - underTest.exclude("**/generated/**") - - assertThat(underTest.inputFiles.files.contains(buildFile)).isTrue() + assertThat(underTest.source.files.contains(file)).isTrue() } private fun createTempFile( diff --git a/plugin-build/plugin/src/test/java/com/ncorti/ktfmt/gradle/tasks/KtfmtCheckTaskIntegrationTest.kt b/plugin-build/plugin/src/test/java/com/ncorti/ktfmt/gradle/tasks/KtfmtCheckTaskIntegrationTest.kt index 3b7b938..3357b1f 100644 --- a/plugin-build/plugin/src/test/java/com/ncorti/ktfmt/gradle/tasks/KtfmtCheckTaskIntegrationTest.kt +++ b/plugin-build/plugin/src/test/java/com/ncorti/ktfmt/gradle/tasks/KtfmtCheckTaskIntegrationTest.kt @@ -6,6 +6,7 @@ import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome.FAILED import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE +import org.gradle.testkit.runner.TaskOutcome.NO_SOURCE import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE import org.intellij.lang.annotations.Language @@ -21,8 +22,6 @@ internal class KtfmtCheckTaskIntegrationTest { @BeforeEach fun setUp() { - File(tempDir, "src/main/java").mkdirs() - File(tempDir, "src/test/java").mkdirs() File("src/test/resources/jvmProject").copyRecursively(tempDir) } @@ -279,12 +278,126 @@ internal class KtfmtCheckTaskIntegrationTest { .build() } + @Test + fun `check task should detect the source and test files in a flattened project structure`() { + appendToBuildGradle( + """ + |kotlin { + | sourceSets.main { + | kotlin.setSrcDirs(listOf("src")) + | } + | sourceSets.test { + | kotlin.setSrcDirs(listOf("test")) + | } + |} + """ + .trimMargin()) + + createTempFile("val answer = 42\n", path = "src/someFolder") + createTempFile("val answer = 42\n", path = "test/someOtherFolder") + + val result = + GradleRunner.create() + .withProjectDir(tempDir) + .withPluginClasspath() + .withArguments("ktfmtCheck") + .build() + + assertThat(result.task(":ktfmtCheckMain")?.outcome).isNotEqualTo(NO_SOURCE) + assertThat(result.task(":ktfmtCheckTest")?.outcome).isNotEqualTo(NO_SOURCE) + } + + @Test + fun `check task should by default ignore sourceSets in the build folder`() { + appendToBuildGradle( + """ + |kotlin { + | sourceSets.main { + | kotlin.srcDirs("build/main") + | } + |} + """ + .trimMargin()) + + createTempFile(content = "val answer=42\n", path = "build/main") + + val result = + GradleRunner.create() + .withProjectDir(tempDir) + .withPluginClasspath() + .withArguments("ktfmtCheck") + .forwardOutput() + .build() + + assertThat(result.task(":ktfmtCheckMain")?.outcome).isEqualTo(NO_SOURCE) + } + + @Test + fun `check task should not ignore sourceSets in build folder when a custom exclusion pattern is specified`() { + appendToBuildGradle( + """ + |kotlin { + | sourceSets.main { + | kotlin.srcDirs("build/generated") + | } + |} + | + |ktfmt{ + | srcSetPathExclusionPattern.set(Regex("customRules.*")) + |} + """ + .trimMargin()) + + createTempFile(content = "val answer=42\n", path = "build/generated/main") + + val result = + GradleRunner.create() + .withProjectDir(tempDir) + .withPluginClasspath() + .withArguments("ktfmtCheck") + .forwardOutput() + .buildAndFail() + + assertThat(result.task(":ktfmtCheckMain")?.outcome).isEqualTo(FAILED) + } + + @Test + fun `check task should ignore the main sourceSets when specified as exclusion pattern`() { + appendToBuildGradle( + """ + |ktfmt{ + | srcSetPathExclusionPattern.set(Regex(".*[\\\\/]main[\\\\/].*")) + |} + """ + .trimMargin()) + + createTempFile(content = "val answer=42\n") + + val result = + GradleRunner.create() + .withProjectDir(tempDir) + .withPluginClasspath() + .withArguments("ktfmtCheck") + .forwardOutput() + .build() + + assertThat(result.task(":ktfmtCheckMain")?.outcome).isEqualTo(NO_SOURCE) + } + + private fun appendToBuildGradle(content: String) { + tempDir.resolve("build.gradle.kts").apply { + appendText(System.lineSeparator()) + appendText(content) + } + } + private fun createTempFile( @Language("kotlin") content: String, fileName: String = "TestFile.kt", path: String = "src/main/java" - ) = - File(File(tempDir, path), fileName).apply { + ): File = + tempDir.resolve(path).resolve(fileName).apply { + parentFile.mkdirs() createNewFile() writeText(content) } diff --git a/plugin-build/plugin/src/test/java/com/ncorti/ktfmt/gradle/tasks/KtfmtFormatTaskIntegrationTest.kt b/plugin-build/plugin/src/test/java/com/ncorti/ktfmt/gradle/tasks/KtfmtFormatTaskIntegrationTest.kt index fd16535..e1d2909 100644 --- a/plugin-build/plugin/src/test/java/com/ncorti/ktfmt/gradle/tasks/KtfmtFormatTaskIntegrationTest.kt +++ b/plugin-build/plugin/src/test/java/com/ncorti/ktfmt/gradle/tasks/KtfmtFormatTaskIntegrationTest.kt @@ -4,6 +4,7 @@ import com.google.common.truth.Truth.assertThat import java.io.File import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome.FAILED +import org.gradle.testkit.runner.TaskOutcome.NO_SOURCE import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE import org.intellij.lang.annotations.Language @@ -19,7 +20,6 @@ internal class KtfmtFormatTaskIntegrationTest { @BeforeEach fun setUp() { - File(tempDir, "src/main/java").mkdirs() File("src/test/resources/jvmProject").copyRecursively(tempDir) } @@ -343,12 +343,124 @@ internal class KtfmtFormatTaskIntegrationTest { "}") } + @Test + fun `format task should detect the source and test files in a flattened project structure and format them`() { + appendToBuildGradle( + """ + |kotlin { + | sourceSets.main { + | kotlin.setSrcDirs(listOf("src")) + | } + | sourceSets.test { + | kotlin.setSrcDirs(listOf("test")) + | } + |} + """ + .trimMargin()) + + val sourceFile = createTempFile("val answer = 42\n", path = "src/someFolder") + val testFile = createTempFile("val answer = 42\n", path = "test/someOtherFolder") + + val result = + GradleRunner.create() + .withProjectDir(tempDir) + .withPluginClasspath() + .withArguments("ktfmtFormat") + .build() + + assertThat(result.task(":ktfmtFormatMain")?.outcome).isNotEqualTo(NO_SOURCE) + assertThat(result.task(":ktfmtFormatTest")?.outcome).isNotEqualTo(NO_SOURCE) + + assertThat(sourceFile.readText()).contains("val answer = 42\n") + assertThat(testFile.readText()).contains("val answer = 42\n") + } + + @Test + fun `format task should by default not format sourceSets in the build folder`() { + appendToBuildGradle( + """ + |kotlin { + | sourceSets.main { + | kotlin.srcDirs("build/main") + | } + |} + """ + .trimMargin()) + + val file = createTempFile(content = "val answer=42\n", path = "build/main") + + val result = + GradleRunner.create() + .withProjectDir(tempDir) + .withPluginClasspath() + .withArguments("ktfmtFormat") + .forwardOutput() + .build() + + val actual = file.readText() + assertThat(actual).isEqualTo("val answer=42\n") + assertThat(result.task(":ktfmtFormatMain")?.outcome).isEqualTo(NO_SOURCE) + } + + @Test + fun `format task should not ignore sourceSets in build folder when a custom exclusion pattern is specified`() { + appendToBuildGradle( + """ + |kotlin { + | sourceSets.main { + | kotlin.srcDirs("build/generated") + | } + |} + | + |ktfmt{ + | srcSetPathExclusionPattern.set(Regex("customRules.*")) + |} + """ + .trimMargin()) + + val file = createTempFile(content = "val answer=42\n", path = "build/generated/main") + + GradleRunner.create() + .withProjectDir(tempDir) + .withPluginClasspath() + .withArguments("ktfmtFormat", "--info") + .forwardOutput() + .build() + + val actual = file.readText() + assertThat(actual).isEqualTo("val answer = 42\n") + } + + @Test + fun `format task should ignore the main sourceSets when specified as exclusion pattern`() { + appendToBuildGradle( + """ + |ktfmt{ + | srcSetPathExclusionPattern.set(Regex(".*[\\\\/]main[\\\\/].*")) + |} + """ + .trimMargin()) + + createTempFile(content = "val answer=42\n") + + val result = + GradleRunner.create() + .withProjectDir(tempDir) + .withPluginClasspath() + .withArguments("ktfmtFormat") + .forwardOutput() + .build() + + assertThat(result.task(":ktfmtFormatMain")?.outcome).isEqualTo(NO_SOURCE) + } + private fun createTempFile( @Language("kotlin") content: String, fileName: String = "TestFile.kt", path: String = "src/main/java" - ) = - File(File(tempDir, path), fileName).apply { + ): File = + tempDir.resolve(path).resolve(fileName).apply { + parentFile.mkdirs() createNewFile() writeText(content) }