All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.sunnyday.test.impact.plugin.TestImpactPlugin.kt Maven / Gradle / Ivy

There is a newer version: 1.0.2
Show newest version
package dev.sunnyday.test.impact.plugin

import dev.sunnyday.test.impact.plugin.domain.graph.ProjectGraphResolver
import dev.sunnyday.test.impact.plugin.extension.TestImpactExtension
import dev.sunnyday.test.impact.plugin.task.testimpact.TestImpactTask
import dev.sunnyday.test.impact.plugin.task.testimpact.TestImpactTaskOutput
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskProvider
import java.io.File

class TestImpactPlugin : Plugin {

    override fun apply(target: Project) {
        PluginApplier(target).apply()
    }

    private class PluginApplier(
        private val target: Project
    ) {

        private val extension = registerExtension()

        private val startParameterTaskNamesProvider by lazy { target.provider { target.gradle.startParameter.taskNames } }
        private val graphResolverProvider by lazy { target.provider { ProjectGraphResolver(target) } }
        private val testImpactTaskOutputProvider by lazy {
            target.provider {
                TestImpactTaskOutput(
                    getTestImpactOutputFile()
                )
            }
        }
        private val testImpactTaskProvider by lazy {
            target.tasks.register("testImpact", TestImpactTask::class.java) {
                group = "verification"
                projectName = target.name
                if (!(isWrapperTask() || isGradleStartedForTestImpact())) {
                    throw IllegalStateException(
                        """
                            Currently :testImpact allowed to run only as a gradle start task.
                            Current start tasks: ${target.gradle.startParameter.taskNames}"
                        """.trimIndent()
                    )
                }

                changedFiles = extension.changesSource.getChangedFiles()
                inputImpactGraph = graphResolverProvider.get().getProjectsGraph()
                taskOutput = testImpactTaskOutputProvider.get()
            }
        }

        fun apply() {
            if (!isGradleStartedForTestImpact()) {
                return
            }

            target.gradle.projectsEvaluated {
                buildGraph()
                buildTestImpactTasksGraph()
            }
        }

        private fun registerExtension(): TestImpactExtension {
            return target.extensions.create("testImpact", TestImpactExtension::class.java)
        }

        private fun buildGraph() {
            val graphResolver = graphResolverProvider.get()

            iterateOverProjects(graphResolver::onProjectEvaluated)
            graphResolver.markGraphCompleted()
        }

        private fun buildTestImpactTasksGraph() {
            iterateOverProjects { project ->
                val projectTestTasks = extension.testTaskNameProvider.getTestTasks(project)
                projectTestTasks.forEach { testTask ->
                    project.tasks.findByName(testTask)
                        ?.let(::setupTestTaskDependency)
                }
            }
        }

        private fun setupTestTaskDependency(testTask: Task) {
            val testImpactTask = testImpactTaskProvider.get()

            testTask.apply {
                dependsOn(testImpactTask.path)

                val testImpactTaskOutputProvider = testImpactTaskOutputProvider
                val testProjectPath = testTask.project.path
                setOnlyIf {
                    val testTaskOutputGraph = testImpactTaskOutputProvider.get()
                        .readImpactGraph()
                    testTaskOutputGraph.getProjectByProjectPath(testProjectPath).hasChanges
                }

                doFirst {
                    // TODO: setup tests filter
                }
            }

            val startTasks = startParameterTaskNamesProvider.get()
            if (startTasks.last() == getTestImpactStartTaskName()) {
                testImpactTask.finalizedBy(testTask.path)
            }
        }

        private fun iterateOverProjects(action: (Project) -> Unit) {
            val projectsQueue = ArrayDeque()
            projectsQueue.add(target)
            while (projectsQueue.isNotEmpty()) {
                val project = projectsQueue.removeFirst()
                action.invoke(project)

                project.childProjects.values.forEach(projectsQueue::add)
            }
        }

        private fun isGradleStartedForTestImpact(): Boolean {
            return startParameterTaskNamesProvider.get().contains(getTestImpactStartTaskName())
        }

        private fun getTestImpactStartTaskName(): String {
            return if (target.rootProject === target) "testImpact" else "${target.path}:testImpact"
        }

        private fun isWrapperTask(): Boolean {
            return startParameterTaskNamesProvider.get() == listOf("wrapper")
        }

        private fun getTestImpactOutputFile(): File {
            return File(target.layout.buildDirectory.get().asFile, "tmp/testImpact/output.txt")
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy