diff --git a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/ProjectGuardPlugin.kt b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/ProjectGuardPlugin.kt index 780aaea..1eed2c9 100644 --- a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/ProjectGuardPlugin.kt +++ b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/ProjectGuardPlugin.kt @@ -290,7 +290,7 @@ class ProjectGuardPlugin : Plugin { group = "other" description = "Generates a JSON containing the dependencies of this module." projectPath.set(project.path) - dependencies.set(graphBuilder.buildFromProject(project)) + dependencyGraph.set(graphBuilder.buildFromProject(project)) outputFile.set( project.layout.buildDirectory.file(dependenciesFilePath) ) diff --git a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyConfiguration.kt b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyConfiguration.kt index bae17b9..6aa9b46 100644 --- a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyConfiguration.kt +++ b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyConfiguration.kt @@ -18,11 +18,15 @@ package com.rubensousa.projectguard.plugin.internal internal object DependencyConfiguration { + const val COMPILE = "compileClasspath" + const val TEST = "testCompileClasspath" + const val TEST_FIXTURE = "testFixturesCompileClasspath" + private val supportedConfigurations = mutableSetOf( "androidTestUtil", - "compileClasspath", - "testCompileClasspath", - "testFixturesCompileClasspath", + COMPILE, + TEST, + TEST_FIXTURE, ) fun isConfigurationSupported(configurationId: String): Boolean { @@ -38,4 +42,8 @@ internal object DependencyConfiguration { && !lowerCaseConfiguration.contains("metadata") } + fun isTestConfiguration(configurationId: String): Boolean { + return configurationId.lowercase().contains("test") + } + } diff --git a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraph.kt b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraph.kt index 78dd55b..7f1e3a3 100644 --- a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraph.kt +++ b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraph.kt @@ -18,85 +18,145 @@ package com.rubensousa.projectguard.plugin.internal import java.io.Serializable -internal class DependencyGraph( - val configurationId: String, - val nodes: MutableMap> = mutableMapOf(), -) : Serializable { +internal class DependencyGraph : Serializable { + private val configurations = mutableMapOf() private val libraries = mutableSetOf() - fun addInternalDependency(module: String, dependency: String) { - addDependency(module, dependency) - } + fun getConfigurations() = configurations.values.toList() - fun addExternalDependency(module: String, dependency: String) { - addDependency(module, dependency) - libraries.add(dependency) + fun addInternalDependency( + module: String, + dependency: String, + configurationId: String = DependencyConfiguration.COMPILE, + ) { + addDependency( + module = module, + dependency = dependency, + configurationId = configurationId + ) } - private fun addDependency(module: String, dependency: String) { - val existingDependencies = nodes.getOrPut(module) { mutableSetOf() } - existingDependencies.add(dependency) + fun addExternalDependency( + module: String, + dependency: String, + configurationId: String = DependencyConfiguration.COMPILE, + ) { + addDependency( + module = module, + dependency = dependency, + configurationId = configurationId + ) + libraries.add(dependency) } fun isExternalLibrary(dependency: String): Boolean { return libraries.contains(dependency) } - fun getDependencies(module: String): Set { - return nodes[module] ?: emptySet() - } - - fun getAllDependencies(module: String): List { - /** - * Until https://github.com/rubensousa/ProjectGuard/issues/3 is resolved, - * exclude transitive dependency traversals for test configurations - */ - if (configurationId.contains("test")) { - return getDependencies(module).map { - DirectDependency(it) - } - } + fun getDependencies(module: String): List { val visitedDependencies = mutableSetOf() val paths = mutableMapOf() - val stack = ArrayDeque() - stack.addAll(getDependencies(module).map { dependency -> - TraversalState(dependency, emptyList()) - }) - while (stack.isNotEmpty()) { - val currentModule = stack.removeFirst() - val currentDependency = currentModule.dependency + val queue = ArrayDeque() + configurations.values.forEach { configuration -> + configuration.getDependencies(module).forEach { dependency -> + queue.addFirst( + TraversalState( + configurationId = configuration.id, + dependency = dependency, + path = emptyList() + ) + ) + } + } + while (queue.isNotEmpty()) { + val currentTraversal = queue.removeFirst() + val currentDependency = currentTraversal.dependency if (visitedDependencies.contains(currentDependency)) { continue } - paths[currentDependency] = if (currentModule.path.isEmpty()) { + paths[currentDependency] = if (currentTraversal.path.isEmpty()) { DirectDependency(currentDependency) } else { TransitiveDependency( currentDependency, - currentModule.path + currentDependency + currentTraversal.path + currentDependency ) } visitedDependencies.add(currentDependency) - getDependencies(currentDependency).forEach { nextDependency -> - stack.addFirst( - TraversalState( - nextDependency, - currentModule.path + currentDependency - ) - ) + configurations.values.forEach { configuration -> + // Search only for non-test configurations as they're not considered transitive at this point + if (!DependencyConfiguration.isTestConfiguration(configuration.id)) { + configuration.getDependencies(currentDependency).forEach { nextDependency -> + queue.addFirst( + TraversalState( + configurationId = configuration.id, + dependency = nextDependency, + path = currentTraversal.path + currentDependency + ) + ) + } + } } } - return paths.values.toList() + return paths.values.sortedBy { it.id } } - override fun toString(): String { - return "DependencyGraph(configurationId='$configurationId', nodes=$nodes)" + private fun addDependency( + module: String, + dependency: String, + configurationId: String, + ) { + val configuration = configurations.getOrPut(configurationId) { + Configuration(configurationId) + } + configuration.add(module = module, dependency = dependency) } private data class TraversalState( + val configurationId: String, val dependency: String, val path: List, ) + class Configuration(val id: String) : Serializable { + + private val nodes = mutableMapOf>() + + fun add(module: String, dependency: String) { + val existingDependencies = nodes.getOrPut(module) { mutableSetOf() } + existingDependencies.add(dependency) + } + + fun getDependencies(module: String): Set { + return nodes[module] ?: emptySet() + } + + override fun equals(other: Any?): Boolean { + return other is Configuration + && other.id == this.id + && other.nodes == this.nodes + } + + override fun hashCode(): Int { + var result = id.hashCode() + result = 31 * result + nodes.hashCode() + return result + } + + } + + override fun equals(other: Any?): Boolean { + return other is DependencyGraph + && other.libraries == this.libraries + && other.configurations == this.configurations + } + + override fun hashCode(): Int { + var result = configurations.hashCode() + result = 31 * result + libraries.hashCode() + return result + } + + } diff --git a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraphBuilder.kt b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraphBuilder.kt index 2c3c096..22ca7d2 100644 --- a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraphBuilder.kt +++ b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraphBuilder.kt @@ -23,21 +23,20 @@ import org.gradle.api.artifacts.ProjectDependency internal class DependencyGraphBuilder { - fun buildFromDump(projectDump: DependencyGraphDump): List { - val graphs = mutableMapOf() + fun buildFromDump(projectDump: DependencyGraphDump): DependencyGraph { + val graph = DependencyGraph() projectDump.modules.forEach { report -> report.configurations.forEach { configuration -> - val graph = graphs.getOrPut(configuration.id) { - DependencyGraph(configurationId = configuration.id) - } configuration.dependencies.forEach { dependency -> if (dependency.isLibrary) { graph.addExternalDependency( + configurationId = configuration.id, module = report.module, dependency = dependency.id, ) } else { graph.addInternalDependency( + configurationId = configuration.id, module = report.module, dependency = dependency.id, ) @@ -45,16 +44,14 @@ internal class DependencyGraphBuilder { } } } - return graphs.values.toList() + return graph } - fun buildFromProject(project: Project): List { - return project.configurations + fun buildFromProject(project: Project): DependencyGraph { + val graph = DependencyGraph() + project.configurations .filter { config -> config.isCanBeResolved && DependencyConfiguration.isConfigurationSupported(config.name) } - .map { config -> - val graph = DependencyGraph( - configurationId = config.name, - ) + .forEach { config -> val moduleId = project.path config.incoming.dependencies .forEach { dependency -> @@ -63,7 +60,8 @@ internal class DependencyGraphBuilder { if (dependency.path != moduleId) { graph.addInternalDependency( module = moduleId, - dependency = dependency.path + dependency = dependency.path, + configurationId = config.name ) } } @@ -72,11 +70,12 @@ internal class DependencyGraphBuilder { graph.addExternalDependency( module = moduleId, dependency = "${dependency.group}:${dependency.name}", + configurationId = config.name ) } } } - graph } + return graph } } \ No newline at end of file diff --git a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyRestrictionFinder.kt b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyRestrictionFinder.kt index 49229f0..72bafe3 100644 --- a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyRestrictionFinder.kt +++ b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyRestrictionFinder.kt @@ -16,35 +16,22 @@ package com.rubensousa.projectguard.plugin.internal -internal class DependencyRestrictionFinder { +internal class DependencyRestrictionFinder( + private val graph: DependencyGraph, +) { fun find( moduleId: String, - graph: DependencyGraph, - spec: ProjectGuardSpec, - ): List { - return find( - moduleId = moduleId, - graphs = listOf(graph), - spec = spec - ) - } - - fun find( - moduleId: String, - graphs: List, spec: ProjectGuardSpec, ): List { val restrictions = mutableListOf() - graphs.forEach { graph -> - graph.getAllDependencies(moduleId).forEach { dependency -> - fillRestrictions( - restrictions = restrictions, - moduleId = moduleId, - dependency = dependency, - spec = spec, - ) - } + graph.getDependencies(moduleId).forEach { dependency -> + fillRestrictions( + restrictions = restrictions, + moduleId = moduleId, + dependency = dependency, + spec = spec, + ) } // We might find multiple restrictions to the same dependency, just filter them out return filterRestrictions(moduleId, restrictions) diff --git a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/task/TaskCheck.kt b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/task/TaskCheck.kt index 6b58a3a..0a8b35d 100644 --- a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/task/TaskCheck.kt +++ b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/task/TaskCheck.kt @@ -98,7 +98,10 @@ internal class CheckExecutor( val htmlReportGenerator = HtmlReportGenerator() htmlReportGenerator.generate(report, reportDir) if (fatalMatches.isNotEmpty()) { - throw VerificationException("Found ${fatalMatches.size} fatal match(es). See report at file:///$reportFilePath") + throw VerificationException( + "${fatalMatches.take(10).joinToString("\n\n") { it.getDescription() }} \n " + + "Found ${fatalMatches.size} fatal match(es). See full report at file:///$reportFilePath" + ) } else { println("No fatal matches found. See report at file:///$reportFilePath") } diff --git a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/task/TaskDependencyDump.kt b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/task/TaskDependencyDump.kt index fc2bf29..3072a5f 100644 --- a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/task/TaskDependencyDump.kt +++ b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/task/TaskDependencyDump.kt @@ -24,7 +24,6 @@ import com.rubensousa.projectguard.plugin.internal.report.DependencyReferenceDum import com.rubensousa.projectguard.plugin.internal.report.JsonFileWriter import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputFile @@ -39,7 +38,7 @@ internal abstract class TaskDependencyDump : DefaultTask() { internal abstract val projectPath: Property @get:Input - internal abstract val dependencies: ListProperty + internal abstract val dependencyGraph: Property @get:OutputFile internal abstract val outputFile: RegularFileProperty @@ -49,7 +48,7 @@ internal abstract class TaskDependencyDump : DefaultTask() { val executor = DependencyDumpExecutor( moduleId = projectPath.get(), outputFile = outputFile.get().asFile, - dependencyGraphs = dependencies.get() + dependencyGraph = dependencyGraph.get() ) executor.execute() } @@ -59,7 +58,7 @@ internal abstract class TaskDependencyDump : DefaultTask() { internal class DependencyDumpExecutor( private val moduleId: String, private val outputFile: File, - private val dependencyGraphs: List, + private val dependencyGraph: DependencyGraph, ) { private val jsonWriter = JsonFileWriter() @@ -69,11 +68,11 @@ internal class DependencyDumpExecutor( modules = listOf( DependencyGraphModuleDump( module = moduleId, - configurations = dependencyGraphs.map { graph -> + configurations = dependencyGraph.getConfigurations().map { configuration -> ConfigurationDependencies( - id = graph.configurationId, - dependencies = graph.getDependencies(moduleId).toList().map { dependencyId -> - DependencyReferenceDump(dependencyId, graph.isExternalLibrary(dependencyId)) + id = configuration.id, + dependencies = configuration.getDependencies(moduleId).toList().map { dependencyId -> + DependencyReferenceDump(dependencyId, dependencyGraph.isExternalLibrary(dependencyId)) } ) } diff --git a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/task/TaskRestrictionDump.kt b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/task/TaskRestrictionDump.kt index cfa3e4e..01a14ec 100644 --- a/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/task/TaskRestrictionDump.kt +++ b/projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/internal/task/TaskRestrictionDump.kt @@ -16,11 +16,11 @@ package com.rubensousa.projectguard.plugin.internal.task +import com.rubensousa.projectguard.plugin.internal.DependencyGraphBuilder import com.rubensousa.projectguard.plugin.internal.DependencyRestrictionFinder import com.rubensousa.projectguard.plugin.internal.DirectDependencyRestriction import com.rubensousa.projectguard.plugin.internal.ProjectGuardSpec import com.rubensousa.projectguard.plugin.internal.TransitiveDependencyRestriction -import com.rubensousa.projectguard.plugin.internal.DependencyGraphBuilder import com.rubensousa.projectguard.plugin.internal.report.DependencyGraphDump import com.rubensousa.projectguard.plugin.internal.report.JsonFileWriter import com.rubensousa.projectguard.plugin.internal.report.RestrictionDependencyReport @@ -74,11 +74,10 @@ internal class RestrictionDumpExecutor( fun execute() { val dependencyGraphDump = Json.decodeFromString(dependenciesFile.readText()) val graphBuilder = DependencyGraphBuilder() - val graphs = graphBuilder.buildFromDump(dependencyGraphDump) - val restrictionFinder = DependencyRestrictionFinder() + val graph = graphBuilder.buildFromDump(dependencyGraphDump) + val restrictionFinder = DependencyRestrictionFinder(graph) val restrictions = restrictionFinder.find( moduleId = moduleId, - graphs = graphs, spec = spec ) val module = RestrictionModuleReport( diff --git a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/DependencyPluginSimulator.kt b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/DependencyPluginSimulator.kt index 6c72d3b..05fa95e 100644 --- a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/DependencyPluginSimulator.kt +++ b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/DependencyPluginSimulator.kt @@ -35,13 +35,13 @@ internal class DependencyPluginSimulator( moduleId: String, action: DependencyGraph.() -> Unit = {}, ): File { - val graph = DependencyGraph(configurationId = "compileClasspath") + val graph = DependencyGraph() graph.action() val outputFile = getDependencyFile(moduleId) val executor = DependencyDumpExecutor( moduleId = moduleId, outputFile = outputFile, - dependencyGraphs = listOf(graph), + dependencyGraph = graph, ) executor.execute() return outputFile diff --git a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/ProjectGuardFixtures.kt b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/ProjectGuardFixtures.kt index 7c196aa..5bb7945 100644 --- a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/ProjectGuardFixtures.kt +++ b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/ProjectGuardFixtures.kt @@ -16,7 +16,6 @@ package com.rubensousa.projectguard.plugin -import com.rubensousa.projectguard.plugin.internal.DependencyGraph import com.rubensousa.projectguard.plugin.internal.ProjectGuardSpec import org.gradle.testfixtures.ProjectBuilder @@ -29,9 +28,3 @@ internal fun projectGuard(scope: ProjectGuardScope.() -> Unit): ProjectGuardSpec extension.scope() return extension.getSpec() } - -internal fun buildDependencyGraph(scope: DependencyGraph.() -> Unit): DependencyGraph { - val graph = DependencyGraph("implementation") - graph.apply(scope) - return graph -} diff --git a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/TaskDependencyDumpTest.kt b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/TaskDependencyDumpTest.kt index b43e7ec..cdad1fa 100644 --- a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/TaskDependencyDumpTest.kt +++ b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/TaskDependencyDumpTest.kt @@ -17,6 +17,7 @@ package com.rubensousa.projectguard.plugin import com.google.common.truth.Truth.assertThat +import com.rubensousa.projectguard.plugin.internal.DependencyConfiguration import com.rubensousa.projectguard.plugin.internal.DependencyGraph import com.rubensousa.projectguard.plugin.internal.report.ConfigurationDependencies import com.rubensousa.projectguard.plugin.internal.report.DependencyGraphDump @@ -36,7 +37,7 @@ class TaskDependencyDumpTest { val temporaryFolder = TemporaryFolder() private val inputModule = "module" - private val dependencies = mutableListOf() + private val graph = DependencyGraph() private lateinit var outputFile: File private lateinit var executor: DependencyDumpExecutor @@ -46,7 +47,7 @@ class TaskDependencyDumpTest { executor = DependencyDumpExecutor( moduleId = inputModule, outputFile = outputFile, - dependencyGraphs = dependencies, + dependencyGraph = graph ) } @@ -55,19 +56,15 @@ class TaskDependencyDumpTest { // given val firstDependency = "domain:a" val secondDependency = "domain:b" - dependencies.add( - DependencyGraph( - configurationId = "implementation", - ).apply { - addInternalDependency(inputModule, firstDependency) - }, + graph.addInternalDependency( + module = inputModule, + dependency = firstDependency, + configurationId = DependencyConfiguration.COMPILE ) - dependencies.add( - DependencyGraph( - configurationId = "testImplementation", - ).apply { - addInternalDependency(inputModule, secondDependency) - }, + graph.addInternalDependency( + module = inputModule, + dependency = secondDependency, + configurationId = DependencyConfiguration.TEST ) // when @@ -81,11 +78,11 @@ class TaskDependencyDumpTest { module = inputModule, configurations = listOf( ConfigurationDependencies( - id = "implementation", + id = DependencyConfiguration.COMPILE, dependencies = listOf(DependencyReferenceDump(firstDependency, false)) ), ConfigurationDependencies( - id = "testImplementation", + id = DependencyConfiguration.TEST, dependencies = listOf(DependencyReferenceDump(secondDependency, false)) ) ) diff --git a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraphBuilderTest.kt b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraphBuilderTest.kt index c2b80d3..6933cc9 100644 --- a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraphBuilderTest.kt +++ b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraphBuilderTest.kt @@ -53,13 +53,12 @@ class DependencyGraphBuilderTest { val legacyProjectB = consumerProject.addLegacyDependency("b") // when - val graphs = graphBuilder.buildFromProject(consumerProject) + val graph = graphBuilder.buildFromProject(consumerProject) // then - val compileGraph = graphs.findCompilationGraph()!! - assertThat(compileGraph.getDependencies(consumerProject.path)).isEqualTo( - setOf(legacyProjectA.path, legacyProjectB.path) - ) + val compileConfiguration = graph.getConfigurations().find { it.id == DependencyConfiguration.COMPILE }!! + assertThat(compileConfiguration.getDependencies(consumerProject.path)) + .isEqualTo(setOf(legacyProjectA.path, legacyProjectB.path)) } @Test @@ -69,21 +68,12 @@ class DependencyGraphBuilderTest { val legacyProjectC = consumerProject.addLegacyTestDependency("c") // when - val graphs = graphBuilder.buildFromProject(consumerProject) + val graph = graphBuilder.buildFromProject(consumerProject) // then - val testGraph = graphs.findTestGraph()!! - assertThat(testGraph.getDependencies(consumerProject.path)).isEqualTo( - setOf(legacyProjectA.path, legacyProjectC.path) - ) - } - - private fun List.findCompilationGraph(): DependencyGraph? { - return this.find { it.configurationId == "compileClasspath" } - } - - private fun List.findTestGraph(): DependencyGraph? { - return this.find { it.configurationId == "testCompileClasspath" } + val testConfiguration = graph.getConfigurations().find { it.id == DependencyConfiguration.TEST }!! + assertThat(testConfiguration.getDependencies(consumerProject.path)) + .isEqualTo(setOf(legacyProjectA.path, legacyProjectC.path)) } private fun Project.addLegacyDependency(dependency: String): Project { diff --git a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraphTest.kt b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraphTest.kt index 00e1cea..056ab71 100644 --- a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraphTest.kt +++ b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyGraphTest.kt @@ -17,13 +17,15 @@ package com.rubensousa.projectguard.plugin.internal import com.google.common.truth.Truth.assertThat +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.ObjectInputStream +import java.io.ObjectOutputStream import kotlin.test.Test class DependencyGraphTest { - private val graph = DependencyGraph( - configurationId = "implementation" - ) + private val graph = DependencyGraph() @Test fun `transitive dependencies are returned`() { @@ -45,7 +47,7 @@ class DependencyGraphTest { graph.addInternalDependency(module = transitiveDependencyC, dependency = transitiveDependencyE) // when - val dependencies = graph.getAllDependencies(consumer).map { it.id } + val dependencies = graph.getDependencies(consumer).map { it.id } // then assertThat(dependencies).containsExactly( @@ -74,7 +76,7 @@ class DependencyGraphTest { graph.addInternalDependency(module = transitiveDependencyC, dependency = transitiveDependencyD) // when - val dependencies = graph.getAllDependencies(consumer) + val dependencies = graph.getDependencies(consumer) // then val dependency = dependencies.find { it.id == transitiveDependencyB }!! as TransitiveDependency @@ -101,7 +103,7 @@ class DependencyGraphTest { graph.addInternalDependency(module = directDependencyA, dependency = transitiveDependencyD) // when - val dependencies = graph.getAllDependencies(consumer) + val dependencies = graph.getDependencies(consumer) // then val dependency = dependencies.find { it.id == transitiveDependencyD }!! as TransitiveDependency @@ -109,4 +111,108 @@ class DependencyGraphTest { .isEqualTo(listOf(directDependencyA, transitiveDependencyD)) } + @Test + fun `implementation of a test dependency is considered a transitive dependency`() { + // given + val consumer = "consumer" + val consumerDependency = "dependencyA" + val dependencyOfConsumerDependency = "dependencyB" + graph.addInternalDependency( + configurationId = DependencyConfiguration.TEST, + module = consumer, + dependency = consumerDependency + ) + graph.addInternalDependency( + configurationId = DependencyConfiguration.COMPILE, + module = consumerDependency, + dependency = dependencyOfConsumerDependency + ) + + // when + val dependencies = graph.getDependencies(consumer) + + // then + assertThat(dependencies).isEqualTo( + listOf( + DirectDependency(consumerDependency), + TransitiveDependency(dependencyOfConsumerDependency, listOf(consumerDependency, dependencyOfConsumerDependency)) + ) + ) + } + + @Test + fun `test implementation of a test dependency is not considered a transitive dependency`() { + // given + val consumer = "consumer" + val consumerDependency = "dependencyA" + val dependencyOfConsumerDependency = "dependencyB" + graph.addInternalDependency( + configurationId = DependencyConfiguration.TEST, + module = consumer, + dependency = consumerDependency + ) + graph.addInternalDependency( + configurationId = DependencyConfiguration.TEST, + module = consumerDependency, + dependency = dependencyOfConsumerDependency + ) + + // when + val dependencies = graph.getDependencies(consumer) + + // then + assertThat(dependencies).isEqualTo( + listOf( + DirectDependency(consumerDependency), + ) + ) + } + + @Test + fun `test implementation of a direct dependency is not considered a transitive dependency`() { + // given + val consumer = "consumer" + val consumerDependency = "dependencyA" + val dependencyOfConsumerDependency = "dependencyB" + graph.addInternalDependency( + configurationId = DependencyConfiguration.COMPILE, + module = consumer, + dependency = consumerDependency + ) + graph.addInternalDependency( + configurationId = DependencyConfiguration.TEST, + module = consumerDependency, + dependency = dependencyOfConsumerDependency + ) + + // when + val dependencies = graph.getDependencies(consumer) + + // then + assertThat(dependencies).isEqualTo( + listOf( + DirectDependency(consumerDependency), + ) + ) + } + + @Test + fun `graph can be deserialized`() { + // given + graph.addInternalDependency( + module = "consumer", + dependency = "dependencyA" + ) + + // when + val byteArray = ByteArrayOutputStream() + ObjectOutputStream(byteArray).use { stream -> stream.writeObject(graph) } + + // then + val deserializedGraph = ObjectInputStream(ByteArrayInputStream(byteArray.toByteArray())).use { stream -> + stream.readObject() + } as DependencyGraph + assertThat(graph).isEqualTo(deserializedGraph) + } + } diff --git a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyRestrictionTest.kt b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyRestrictionTest.kt index cc2c9a2..387eaff 100644 --- a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyRestrictionTest.kt +++ b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/DependencyRestrictionTest.kt @@ -17,16 +17,13 @@ package com.rubensousa.projectguard.plugin.internal import com.google.common.truth.Truth.assertThat -import com.rubensousa.projectguard.plugin.buildDependencyGraph import com.rubensousa.projectguard.plugin.projectGuard import kotlin.test.Test class DependencyRestrictionTest { - private val graph = DependencyGraph( - configurationId = "implementation" - ) - private val finder = DependencyRestrictionFinder() + private val graph = DependencyGraph() + private val finder = DependencyRestrictionFinder(graph) @Test fun `there is a restriction for a direct match`() { @@ -39,7 +36,6 @@ class DependencyRestrictionTest { // when val restrictions = finder.find( moduleId = ":domain", - graph = graph, spec = spec ) @@ -58,7 +54,6 @@ class DependencyRestrictionTest { // when val restrictions = finder.find( moduleId = ":domain:a", - graph = graph, spec = spec ) @@ -73,15 +68,13 @@ class DependencyRestrictionTest { restrictDependency(":legacy") restrictDependency(":deprecated") } - val graph = buildDependencyGraph { - addInternalDependency(":domain", ":legacy") - addInternalDependency(":domain", ":deprecated") - } + + graph.addInternalDependency(":domain", ":legacy") + graph.addInternalDependency(":domain", ":deprecated") // when val restrictions = finder.find( moduleId = ":domain", - graph = graph, spec = spec ) @@ -104,7 +97,6 @@ class DependencyRestrictionTest { // when val restrictions = finder.find( moduleId = ":data:a", - graph = graph, spec = spec ) @@ -135,7 +127,6 @@ class DependencyRestrictionTest { // when val restrictions = finder.find( moduleId = ":domain:a", - graph = graph, spec = spec ) @@ -166,10 +157,10 @@ class DependencyRestrictionTest { graph.addInternalDependency(":legacy:d", ":legacy:e") // when - val legacyARestrictions = finder.find(moduleId = ":legacy:a", graph = graph, spec = spec) - val legacyBRestrictions = finder.find(moduleId = ":legacy:b", graph = graph, spec = spec) - val legacyCRestrictions = finder.find(moduleId = ":legacy:c", graph = graph, spec = spec) - val legacyDRestrictions = finder.find(moduleId = ":legacy:d", graph = graph, spec = spec) + val legacyARestrictions = finder.find(moduleId = ":legacy:a", spec = spec) + val legacyBRestrictions = finder.find(moduleId = ":legacy:b", spec = spec) + val legacyCRestrictions = finder.find(moduleId = ":legacy:c", spec = spec) + val legacyDRestrictions = finder.find(moduleId = ":legacy:d", spec = spec) // then @@ -211,7 +202,6 @@ class DependencyRestrictionTest { // when val restrictions = finder.find( moduleId = ":domain:a", - graph = graph, spec = spec ) diff --git a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/GuardRestrictionTest.kt b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/GuardRestrictionTest.kt index 219120a..8fd3dfc 100644 --- a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/GuardRestrictionTest.kt +++ b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/GuardRestrictionTest.kt @@ -17,16 +17,13 @@ package com.rubensousa.projectguard.plugin.internal import com.google.common.truth.Truth.assertThat -import com.rubensousa.projectguard.plugin.buildDependencyGraph import com.rubensousa.projectguard.plugin.projectGuard import kotlin.test.Test class GuardRestrictionTest { - private val graph = DependencyGraph( - configurationId = "implementation" - ) - private val finder = DependencyRestrictionFinder() + private val graph = DependencyGraph() + private val finder = DependencyRestrictionFinder(graph) @Test fun `module is restricted to concrete child but not its sibling`() { @@ -44,7 +41,6 @@ class GuardRestrictionTest { // when val restrictions = finder.find( moduleId = ":domain:a", - graph = graph, spec = spec ) @@ -67,7 +63,6 @@ class GuardRestrictionTest { // when val restrictions = finder.find( moduleId = ":domain:a", - graph = graph, spec = spec ) @@ -89,11 +84,7 @@ class GuardRestrictionTest { } // when - val restrictions = finder.find( - moduleId = ":another", - graph = graph, - spec = spec - ) + val restrictions = finder.find(moduleId = ":another", spec = spec) // then assertThat(restrictions).isEmpty() @@ -112,11 +103,7 @@ class GuardRestrictionTest { } // when - val restrictions = finder.find( - moduleId = ":domain", - graph = graph, - spec = spec - ) + val restrictions = finder.find(moduleId = ":domain", spec = spec) // then assertThat(restrictions).containsExactly(DirectDependencyRestriction(":legacy")) @@ -133,11 +120,7 @@ class GuardRestrictionTest { graph.addInternalDependency(":domain:a", ":legacy:a") // when - val restrictions = finder.find( - moduleId = ":domain:a", - graph = graph, - spec = spec - ) + val restrictions = finder.find(moduleId = ":domain:a", spec = spec) // then assertThat(restrictions).containsExactly(DirectDependencyRestriction(":legacy:a")) @@ -147,13 +130,11 @@ class GuardRestrictionTest { fun `there is no restriction by default`() { // given val spec = projectGuard {} + graph.addInternalDependency(":domain", ":legacy") // when val restrictions = finder.find( moduleId = ":domain", - graph = buildDependencyGraph { - addInternalDependency(":domain", ":legacy") - }, spec = spec ) @@ -178,11 +159,7 @@ class GuardRestrictionTest { } // when - val restrictions = finder.find( - moduleId = ":domain:a", - graph = graph, - spec = spec - ) + val restrictions = finder.find(moduleId = ":domain:a", spec = spec) // then assertThat(restrictions).containsExactly( @@ -206,11 +183,7 @@ class GuardRestrictionTest { // when - val restrictions = finder.find( - moduleId = ":domain:a", - graph = graph, - spec = spec - ) + val restrictions = finder.find(moduleId = ":domain:a", spec = spec) // then assertThat(restrictions).containsExactly(DirectDependencyRestriction(":legacy:a", reason)) diff --git a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/ModuleRestrictionTest.kt b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/ModuleRestrictionTest.kt index 721ca46..4d5bd96 100644 --- a/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/ModuleRestrictionTest.kt +++ b/projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/internal/ModuleRestrictionTest.kt @@ -17,14 +17,13 @@ package com.rubensousa.projectguard.plugin.internal import com.google.common.truth.Truth.assertThat -import com.rubensousa.projectguard.plugin.buildDependencyGraph import com.rubensousa.projectguard.plugin.projectGuard import kotlin.test.Test class ModuleRestrictionTest { - private val graph = DependencyGraph(configurationId = "implementation") - private val finder = DependencyRestrictionFinder() + private val graph = DependencyGraph() + private val finder = DependencyRestrictionFinder(graph) @Test fun `restrictModule denies all by default`() { @@ -37,11 +36,7 @@ class ModuleRestrictionTest { // when - val restrictions = finder.find( - moduleId = ":domain", - graph = graph, - spec = spec - ) + val restrictions = finder.find(moduleId = ":domain", spec = spec) // then assertThat(restrictions).containsExactly( @@ -59,11 +54,7 @@ class ModuleRestrictionTest { graph.addInternalDependency(":domain:a", ":legacy:a") // when - val restrictions = finder.find( - moduleId = ":domain:a", - graph = graph, - spec = spec - ) + val restrictions = finder.find(moduleId = ":domain:a", spec = spec) // then assertThat(restrictions).containsExactly(DirectDependencyRestriction(":legacy:a")) @@ -76,14 +67,13 @@ class ModuleRestrictionTest { restrictModule(":domain") restrictModule(":data") } - val graph = buildDependencyGraph { - addInternalDependency(":domain", ":legacy") - addInternalDependency(":data", ":legacy") - } + + graph.addInternalDependency(":domain", ":legacy") + graph.addInternalDependency(":data", ":legacy") // when - val domainRestrictions = finder.find(moduleId = ":domain", graph = graph, spec = spec) - val dataRestrictions = finder.find(moduleId = ":data", graph = graph, spec = spec) + val domainRestrictions = finder.find(moduleId = ":domain", spec = spec) + val dataRestrictions = finder.find(moduleId = ":data", spec = spec) // then assertThat(domainRestrictions).containsExactly(DirectDependencyRestriction(":legacy")) @@ -102,7 +92,7 @@ class ModuleRestrictionTest { graph.addInternalDependency(":domain:b", ":legacy:a") // when - val restrictions = finder.find(moduleId = ":domain:a", graph = graph, spec = spec) + val restrictions = finder.find(moduleId = ":domain:a", spec = spec) // then assertThat(restrictions).containsExactly( @@ -128,7 +118,7 @@ class ModuleRestrictionTest { graph.addInternalDependency(":domain:a", ":legacy:a") // when - val restrictions = finder.find(moduleId = ":domain:a", graph = graph, spec = spec) + val restrictions = finder.find(moduleId = ":domain:a", spec = spec) // then assertThat(restrictions).containsExactly( @@ -156,9 +146,9 @@ class ModuleRestrictionTest { graph.addInternalDependency(":domain:d", ":domain:c") // when - val restrictionsB = finder.find(moduleId = ":domain:b", graph = graph, spec = spec) - val restrictionsC = finder.find(moduleId = ":domain:c", graph = graph, spec = spec) - val restrictionsD = finder.find(moduleId = ":domain:d", graph = graph, spec = spec) + val restrictionsB = finder.find(moduleId = ":domain:b", spec = spec) + val restrictionsC = finder.find(moduleId = ":domain:c", spec = spec) + val restrictionsD = finder.find(moduleId = ":domain:d", spec = spec) // then @@ -181,11 +171,7 @@ class ModuleRestrictionTest { graph.addInternalDependency(":domain:a", ":legacy:a") // when - val restrictions = finder.find( - moduleId = ":domain:a", - graph = graph, - spec = spec - ) + val restrictions = finder.find(moduleId = ":domain:a", spec = spec) // then assertThat(restrictions).containsExactly(DirectDependencyRestriction(":legacy:a")) diff --git a/sample/legacy/a/build.gradle.kts b/sample/legacy/a/build.gradle.kts index 3b43358..b93bfcb 100644 --- a/sample/legacy/a/build.gradle.kts +++ b/sample/legacy/a/build.gradle.kts @@ -4,4 +4,5 @@ plugins { dependencies { implementation(project(":legacy:b")) + testImplementation(libs.mockk) } \ No newline at end of file