Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

collect depth info after applying changes #331

Merged
merged 2 commits into from
Dec 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@

package modulecheck.api.context

import kotlinx.coroutines.flow.toSet
import modulecheck.project.McProject
import modulecheck.project.ProjectContext
import modulecheck.utils.filterAsync

data class DependentProjects(
private val delegate: Set<McProject>
Expand All @@ -30,7 +32,7 @@ data class DependentProjects(
override suspend operator fun invoke(project: McProject): DependentProjects {
val others = project.projectCache
.values
.filter { otherProject ->
.filterAsync { otherProject ->
project.path in otherProject
.classpathDependencies()
.all()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ package modulecheck.api.finding

import modulecheck.project.McProject

fun interface FindingFactory<T : Finding> {
interface FindingFactory<T : Finding> {

suspend fun evaluate(projects: List<McProject>): List<T>
suspend fun evaluateFixable(projects: List<McProject>): List<T>
suspend fun evaluateSorts(projects: List<McProject>): List<T>
suspend fun evaluateReports(projects: List<McProject>): List<T>
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ interface ModuleCheckRule<T : Finding> {
fun McProject.kotlinBuildFileOrNull(): KtFile? = buildFile.asKtsFileOrNull()
}

interface ReportOnlyRule<T : Finding> : ModuleCheckRule<T>
interface SortRule<T : Finding> : ModuleCheckRule<T>

fun interface RuleFactory {

fun create(settings: ModuleCheckSettings): List<ModuleCheckRule<out Finding>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ package modulecheck.core.rule

import modulecheck.api.DepthFinding
import modulecheck.api.context.depthForSourceSetName
import modulecheck.api.rule.ModuleCheckRule
import modulecheck.api.rule.ReportOnlyRule
import modulecheck.api.settings.ChecksSettings
import modulecheck.project.McProject

class DepthRule : ModuleCheckRule<DepthFinding> {
class DepthRule : ReportOnlyRule<DepthFinding> {

override val id = "Depth"
override val description = "The longest path between this module and its leaf nodes"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ class DisableViewBindingRule : ModuleCheckRule<DisableViewBindingGenerationFindi
return if (usedLayouts.isNotEmpty()) {
emptyList()
} else {
listOf(DisableViewBindingGenerationFinding(project, project.path, project.buildFile))
listOf(
DisableViewBindingGenerationFinding(
dependentProject = project, project.path, buildFile = project.buildFile
)
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import kotlinx.coroutines.coroutineScope
import modulecheck.api.finding.Finding
import modulecheck.api.finding.FindingFactory
import modulecheck.api.rule.ModuleCheckRule
import modulecheck.api.rule.ReportOnlyRule
import modulecheck.api.rule.SortRule
import modulecheck.api.settings.ModuleCheckSettings
import modulecheck.project.McProject

Expand All @@ -29,11 +31,26 @@ class MultiRuleFindingFactory(
private val rules: List<ModuleCheckRule<out Finding>>
) : FindingFactory<Finding> {

override suspend fun evaluate(projects: List<McProject>): List<Finding> {
override suspend fun evaluateFixable(projects: List<McProject>): List<Finding> {
return evaluate(projects) { it !is SortRule && it !is ReportOnlyRule }
}

override suspend fun evaluateSorts(projects: List<McProject>): List<Finding> {
return evaluate(projects) { it is SortRule }
}

override suspend fun evaluateReports(projects: List<McProject>): List<Finding> {
return evaluate(projects) { it is ReportOnlyRule }
}

private suspend fun evaluate(
projects: List<McProject>,
predicate: (ModuleCheckRule<*>) -> Boolean
): List<Finding> {
return coroutineScope {
projects.flatMap { project ->
rules
.filter { it.shouldApply(settings.checks) }
.filter { predicate(it) && it.shouldApply(settings.checks) }
.map { rule -> async { rule.check(project) } }
}
.awaitAll()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,33 @@ import kotlinx.coroutines.coroutineScope
import modulecheck.api.finding.Finding
import modulecheck.api.finding.FindingFactory
import modulecheck.api.rule.ModuleCheckRule
import modulecheck.api.rule.ReportOnlyRule
import modulecheck.api.rule.SortRule
import modulecheck.project.McProject

class SingleRuleFindingFactory<T : Finding>(
val rule: ModuleCheckRule<T>
) : FindingFactory<Finding> {

override suspend fun evaluate(projects: List<McProject>): List<T> {
override suspend fun evaluateFixable(projects: List<McProject>): List<T> {
return if (rule !is SortRule && rule !is ReportOnlyRule) {
evaluateRule(projects)
} else emptyList()
}

override suspend fun evaluateSorts(projects: List<McProject>): List<T> {
return if (rule is SortRule) {
evaluateRule(projects)
} else emptyList()
}

override suspend fun evaluateReports(projects: List<McProject>): List<T> {
return if (rule is ReportOnlyRule) {
evaluateRule(projects)
} else emptyList()
}

private suspend fun evaluateRule(projects: List<McProject>): List<T> {
return coroutineScope {
projects
.map { project -> async { rule.check(project) } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

package modulecheck.core.rule

import modulecheck.api.rule.ModuleCheckRule
import modulecheck.api.rule.SortRule
import modulecheck.api.settings.ChecksSettings
import modulecheck.api.settings.ModuleCheckSettings
import modulecheck.core.parse
Expand All @@ -27,7 +27,7 @@ import java.util.Locale

class SortDependenciesRule(
settings: ModuleCheckSettings
) : ModuleCheckRule<SortDependenciesFinding> {
) : SortRule<SortDependenciesFinding> {

override val id = "SortDependencies"
override val description = "Sorts all dependencies within a dependencies { ... } block"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

package modulecheck.core.rule

import modulecheck.api.rule.ModuleCheckRule
import modulecheck.api.rule.SortRule
import modulecheck.api.settings.ChecksSettings
import modulecheck.api.settings.ModuleCheckSettings
import modulecheck.core.parse
Expand All @@ -27,7 +27,7 @@ import modulecheck.project.McProject

class SortPluginsRule(
settings: ModuleCheckSettings
) : ModuleCheckRule<SortPluginsFinding> {
) : SortRule<SortPluginsFinding> {

override val id = "SortPlugins"
override val description =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package modulecheck.core

import modulecheck.core.rule.DepthRule
import modulecheck.core.rule.ModuleCheckRuleFactory
import modulecheck.core.rule.MultiRuleFindingFactory
import modulecheck.core.rule.SingleRuleFindingFactory
import modulecheck.project.ConfigurationName
import modulecheck.runtime.test.RunnerTest
Expand Down Expand Up @@ -62,6 +63,54 @@ internal class DepthOutputTest : RunnerTest() {
"""
}

@Test
fun `reported depths should be from after fixes are applied`() {

settings.checks.depths = true

val runner = runner(
autoCorrect = true,
findingFactory = MultiRuleFindingFactory(
settings,
ruleFactory.create(settings)
)
)

val lib1 = project(":lib1")

project(":lib2") {
addDependency(ConfigurationName.implementation, lib1)

buildFile.writeText(
"""
plugins {
kotlin("jvm")
}

dependencies {
implementation(project(path = ":lib1"))
}
""".trimIndent()
)
}

runner.run(allProjects()).isSuccess shouldBe true

logger.collectReport()
.joinToString()
.clean() shouldBe """
:lib2
dependency name source build file
✔ :lib1 unusedDependency /lib2/build.gradle.kts: (6, 3):

-- ModuleCheck main source set depth results --
depth modules
0 [:lib1, :lib2]

ModuleCheck found 1 issue
"""
}

@Test
fun `test source set depths should not be reported`() {

Expand Down
107 changes: 107 additions & 0 deletions modulecheck-core/src/test/kotlin/modulecheck/core/DepthReportTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package modulecheck.core
import modulecheck.api.test.TestSettings
import modulecheck.core.rule.DepthRule
import modulecheck.core.rule.ModuleCheckRuleFactory
import modulecheck.core.rule.MultiRuleFindingFactory
import modulecheck.core.rule.SingleRuleFindingFactory
import modulecheck.project.ConfigurationName
import modulecheck.project.SourceSet
Expand Down Expand Up @@ -246,6 +247,112 @@ internal class DepthReportTest : RunnerTest() {
"""
}

@Test
fun `depth report should be calculated after fixes are applied`() {

settings.checks.depths = true
settings.reports.depths.enabled = true

val runner = runner(
autoCorrect = true,
findingFactory = MultiRuleFindingFactory(
settings,
ruleFactory.create(settings)
)
)

val lib1 = project(":lib1") {

addSource(
"com/modulecheck/lib1/Lib1Class.kt",
"""
package com.modulecheck.lib1

class Lib1Class
""".trimIndent()
)
}

val lib2 = project(":lib2") {
addDependency(ConfigurationName.implementation, lib1)

addSource(
"com/modulecheck/lib2/Lib2Class.kt",
"""
package com.modulecheck.lib2

class Lib2Class
""".trimIndent()
)

buildFile.writeText(
"""
plugins {
kotlin("jvm")
}

dependencies {
implementation(project(path = ":lib1"))
}
""".trimIndent()
)
}

project(":app") {
addDependency(ConfigurationName.implementation, lib1)
addDependency(ConfigurationName.implementation, lib2)

addSource(
"com/modulecheck/app/App.kt",
"""
package com.modulecheck.app

import com.modulecheck.lib1.Lib1Class
import com.modulecheck.lib2.Lib2Class

class App {
private val lib1Class = Lib1Class()
private val lib2Class = Lib2Class()
}
""".trimIndent()
)
}

runner.run(allProjects()).isSuccess shouldBe true

logger.collectReport()
.joinToString()
.clean() shouldBe """
:lib2
dependency name source build file
✔ :lib1 unusedDependency /lib2/build.gradle.kts: (6, 3):

-- ModuleCheck main source set depth results --
depth modules
0 [:lib1, :lib2]
1 [:app]

ModuleCheck found 1 issue
"""

outputFile.readText() shouldBe """
-- ModuleCheck Depth results --

:app
source set depth most expensive dependencies
main 1 [:lib1, :lib2]

:lib1
source set depth most expensive dependencies
main 0 []

:lib2
source set depth most expensive dependencies
main 0 []

"""
}

@Test
fun `debug source set depth should be reported`() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ import modulecheck.project.McProject
interface ProjectProvider : HasProjectCache {

fun get(path: String): McProject

fun getAll(): List<McProject>

fun clearCaches()
}
Loading