Repository: analysis-dev/diktat (kotlin)

Issue:
In gradle plugin, should check if config file is relative to current project dir
Gradle's file API (`project.files()`) always returns absolute paths. When using `java.io.File` to specify diktat config location, looking for file in parent dirs works; when using `project.file()` it does not.

Pull request:
Gradle file API returns absolute paths relative to project's directory, so no need to check whether it's absolute. Need to use proper way to specify path in build.gradle instead.
Added more tests.

### What's done:
* Fixes & tests

This pull request closes #703

Patch:
diff --git a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt
index 88700e21ea..33f2521551 100644
--- a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt
+++ b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt
@@ -20,8 +20,9 @@ open class DiktatExtension {
 
     /**
      * Path to diktat yml config file. Can be either absolute or relative to project's root directory.
+     * Default value: `diktat-analysis.yml` in rootDir.
      */
-    var diktatConfigFile: File = File("diktat-analysis.yml")
+    lateinit var diktatConfigFile: File
 
     /**
      * Paths that will be excluded from diktat run
diff --git a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePlugin.kt b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePlugin.kt
index 395319a49f..97237e0b7f 100644
--- a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePlugin.kt
+++ b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePlugin.kt
@@ -20,7 +20,7 @@ class DiktatGradlePlugin : Plugin<Project> {
             inputs = project.fileTree("src").apply {
                 include("**/*.kt")
             }
-            reporter = PlainReporter(System.out)
+            diktatConfigFile = project.rootProject.file("diktat-analysis.yml")
             excludes = project.files()
             reporter = PlainReporter(System.out)
         }
diff --git a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt
index 794bf397d5..49d608b78a 100644
--- a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt
+++ b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt
@@ -120,15 +120,14 @@ open class DiktatJavaExecTaskBase @Inject constructor(
         add((if (negate) "!" else "") + path)
     }
 
-    /**
-     * todo: share logic with maven plugin, it's basically copy-paste
-     */
-    @Suppress("ForbiddenComment")
     private fun resolveConfigFile(file: File): String {
-        if (file.isAbsolute) {
+        if (file.toPath().startsWith(project.rootDir.toPath())) {
+            // In gradle, project.files() returns File relative to project.projectDir.
+            // There is no need to resolve file further if it has been passed via gradle files API.
             return file.absolutePath
         }
 
+        // otherwise, e.g. if file is passed as java.io.File with relative path, we try to find it
         return generateSequence(project.projectDir) { it.parentFile }
             .map { it.resolve(file) }
             .run {
diff --git a/examples/gradle-kotlin-dsl-multiproject/build.gradle.kts b/examples/gradle-kotlin-dsl-multiproject/build.gradle.kts
index d60bbdc835..229d7c1970 100644
--- a/examples/gradle-kotlin-dsl-multiproject/build.gradle.kts
+++ b/examples/gradle-kotlin-dsl-multiproject/build.gradle.kts
@@ -12,6 +12,7 @@ allprojects {
     }
     apply(plugin = "org.cqfn.diktat.diktat-gradle-plugin")
     configure<DiktatExtension> {
+        diktatConfigFile = rootProject.file("diktat-analysis.yml")
         inputs = files("src/**/*.kt")
         debug = true
     }


Test patch:
diff --git a/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt
index 482fa95a5a..533c6ab497 100644
--- a/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt
+++ b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt
@@ -1,11 +1,13 @@
 package org.cqfn.diktat.plugin.gradle
 
+import org.cqfn.diktat.ruleset.rules.DIKTAT_CONF_PROPERTY
 import org.gradle.api.Project
 import org.gradle.testfixtures.ProjectBuilder
 import org.junit.jupiter.api.Assertions
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
 import java.io.File
+import java.nio.file.Files
 
 class DiktatJavaExecTaskTest {
     private val projectBuilder = ProjectBuilder.builder()
@@ -13,7 +15,9 @@ class DiktatJavaExecTaskTest {
 
     @BeforeEach
     fun setUp() {
-        project = projectBuilder.build()
+        project = projectBuilder
+            .withName("testProject")
+            .build()
     }
 
     @Test
@@ -49,22 +53,102 @@ class DiktatJavaExecTaskTest {
 
     @Test
     fun `check command line with non-existent inputs`() {
-        val task = registerDiktatTask {
+        val task = project.registerDiktatTask {
             inputs = project.files()
         }
         Assertions.assertFalse(task.shouldRun)
     }
 
-    private fun registerDiktatTask(extensionConfiguration: DiktatExtension.() -> Unit): DiktatJavaExecTaskBase {
-        DiktatGradlePlugin().apply(project)
-        project.extensions.configure("diktat", extensionConfiguration)
-        return project.tasks.getByName("diktatCheck") as DiktatJavaExecTaskBase
+    @Test
+    fun `check system property with default config`() {
+        val task = project.registerDiktatTask {
+            inputs = project.files()
+        }
+        Assertions.assertEquals(File(project.projectDir, "diktat-analysis.yml").absolutePath, task.systemProperties[DIKTAT_CONF_PROPERTY])
+    }
+
+    @Test
+    fun `check system property with custom config`() {
+        val task = project.registerDiktatTask {
+            inputs = project.files()
+            diktatConfigFile = project.file("../diktat-analysis.yml")
+        }
+        Assertions.assertEquals(File(project.projectDir.parentFile, "diktat-analysis.yml").absolutePath, task.systemProperties[DIKTAT_CONF_PROPERTY])
+    }
+
+    @Test
+    fun `check system property with multiproject build with default config`() {
+        setupMultiProject()
+        val subproject = project.subprojects.first()
+        project.allprojects {
+            it.registerDiktatTask {
+                inputs = it.files()
+            }
+        }
+        val task = project.tasks.getByName(DIKTAT_CHECK_TASK) as DiktatJavaExecTaskBase
+        val subprojectTask = subproject.tasks.getByName(DIKTAT_CHECK_TASK) as DiktatJavaExecTaskBase
+        Assertions.assertEquals(File(project.projectDir, "diktat-analysis.yml").absolutePath, task.systemProperties[DIKTAT_CONF_PROPERTY])
+        Assertions.assertEquals(File(project.projectDir, "diktat-analysis.yml").absolutePath, subprojectTask.systemProperties[DIKTAT_CONF_PROPERTY])
+    }
+
+    @Test
+    fun `check system property with multiproject build with custom config`() {
+        setupMultiProject()
+        val subproject = project.subprojects.first()
+        project.allprojects {
+            it.registerDiktatTask {
+                inputs = it.files()
+                diktatConfigFile = it.rootProject.file("diktat-analysis.yml")
+            }
+        }
+        val task = project.tasks.getByName(DIKTAT_CHECK_TASK) as DiktatJavaExecTaskBase
+        val subprojectTask = subproject.tasks.getByName(DIKTAT_CHECK_TASK) as DiktatJavaExecTaskBase
+        Assertions.assertEquals(File(project.projectDir, "diktat-analysis.yml").absolutePath, task.systemProperties[DIKTAT_CONF_PROPERTY])
+        Assertions.assertEquals(File(project.projectDir, "diktat-analysis.yml").absolutePath, subprojectTask.systemProperties[DIKTAT_CONF_PROPERTY])
+    }
+
+    @Test
+    fun `check system property with multiproject build with custom config and two config files`() {
+        setupMultiProject()
+        val subproject = project.subprojects.single()
+        subproject.file("diktat-analysis.yml").createNewFile()
+        project.allprojects {
+            it.registerDiktatTask {
+                inputs = it.files()
+                diktatConfigFile = it.file("diktat-analysis.yml")
+            }
+        }
+        val task = project.tasks.getByName(DIKTAT_CHECK_TASK) as DiktatJavaExecTaskBase
+        val subprojectTask = subproject.tasks.getByName(DIKTAT_CHECK_TASK) as DiktatJavaExecTaskBase
+        Assertions.assertEquals(File(project.projectDir, "diktat-analysis.yml").absolutePath, task.systemProperties[DIKTAT_CONF_PROPERTY])
+        Assertions.assertEquals(File(subproject.projectDir, "diktat-analysis.yml").absolutePath, subprojectTask.systemProperties[DIKTAT_CONF_PROPERTY])
+    }
+
+    private fun Project.registerDiktatTask(extensionConfiguration: DiktatExtension.() -> Unit): DiktatJavaExecTaskBase {
+        DiktatGradlePlugin().apply(this)
+        extensions.configure("diktat", extensionConfiguration)
+        return tasks.getByName(DIKTAT_CHECK_TASK) as DiktatJavaExecTaskBase
     }
 
     private fun assertCommandLineEquals(expected: List<String?>, extensionConfiguration: DiktatExtension.() -> Unit) {
-        val task = registerDiktatTask(extensionConfiguration)
+        val task = project.registerDiktatTask(extensionConfiguration)
         Assertions.assertIterableEquals(expected, task.commandLine)
     }
 
+    private fun setupMultiProject() {
+        ProjectBuilder.builder()
+            .withParent(project)
+            .withName("testSubproject")
+            .withProjectDir(File(project.projectDir, "testSubproject").also {
+                Files.createDirectory(it.toPath())
+            })
+            .build()
+        project.file("diktat-analysis.yml").createNewFile()
+    }
+
     private fun combinePathParts(vararg parts: String) = parts.joinToString(File.separator)
+    
+    companion object {
+        private const val DIKTAT_CHECK_TASK = "diktatCheck"
+    }
 }