Skip to content

Commit 03fb7ec

Browse files
authored
Merge pull request #4 from mtrakal/feature/sync-with-root
Feature/sync with root
2 parents ddcdbf1 + e7c9129 commit 03fb7ec

File tree

12 files changed

+128
-38
lines changed

12 files changed

+128
-38
lines changed

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,13 @@ moduleGraphAssert {
5555
Apply the plugin to a module, which dependencies graph you want to assert.
5656
```groovy
5757
plugins {
58-
id "cz.mtrakal.module.graph.assertion" version "2.5.0"
58+
id "cz.mtrakal.module.graph.assertion" version "3.0.0"
5959
}
6060
```
6161

6262
- You can run `./gradlew assertModuleGraph` to execute configured checks or `./gradlew check` where `assertModuleGraph` will be included.
6363
- Alternative option is using `assertOnAnyBuild = true` configuration to run the checks on every single Gradle build without need for running explicit tasks - see https://github.com/jraska/modules-graph-assert/pull/184 for more details.
64+
- Hint: Gradle [Configuration On Demand](https://docs.gradle.org/current/userguide/multi_project_configuration_and_execution.html) may hide some modules from the plugin visibility. If you notice some modules are missing, try the `--no-configure-on-demand` flag.
6465

6566
### Configuration
6667
Rules are applied on the Gradle module and its `api` and `implementation` dependencies by default. Typically you would want to apply this in your final app module, however configuration for any module is possible. [Example](https://github.com/jraska/github-client/blob/master/app/build.gradle#L141)
@@ -162,11 +163,9 @@ Please feel free to create PR or issue with any suggestions or ideas. No special
162163

163164
### Debugging
164165

165-
**Setting up a composite build:**
166+
**Setting up a composite build**
166167

167-
This case is helpful when you need to debug in a real project.
168-
Composite builds are consumed directly without publishing a snapshot version.
169-
This is done already in `example` project, but you can do the same in any other project:
168+
[Composite builds](https://docs.gradle.org/current/userguide/composite_builds.html#settings_defined_composite) are consumed directly without publishing a version.
170169

171170
settings.gradle:
172171
```groovy

gradle/wrapper/gradle-wrapper.jar

9 Bytes
Binary file not shown.

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

plugin/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ gradlePlugin {
2121
plugins {
2222
create("modulesGraphAssert") {
2323
id = "cz.mtrakal.module.graph.assertion"
24-
version = "3.0.0"
24+
version = "3.0.1"
2525
displayName = "Modules Graph Assert Mermaid"
2626
description = "Gradle plugin to keep your modules graph healthy and lean with Mermaid output."
2727
implementationClass = "com.jraska.module.graph.assertion.ModuleGraphAssertionsPlugin"

plugin/config/ktlint/baseline.xml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<baseline version="1.0">
3-
<file name="src/main/kotlin/com/jraska/module/graph/assertion/ModuleTreeHeightAssert.kt">
4-
<error line="27" column="141" source="standard:max-line-length" />
5-
<error line="37" column="141" source="standard:max-line-length" />
6-
</file>
7-
<file name="src/test/kotlin/com/jraska/module/graph/assertion/OnAnyBuildAssertTest.kt">
8-
<error line="128" column="141" source="standard:max-line-length" />
3+
<file name="src/test/kotlin/com/jraska/module/graph/assertion/FullProjectRootGradleTest.kt">
4+
<error line="95" column="141" source="standard:max-line-length" />
95
</file>
106
</baseline>

plugin/detekt-baseline.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
<ManuallySuppressedIssues></ManuallySuppressedIssues>
44
<CurrentIssues>
55
<ID>LongMethod:DependencyGraphTest.kt$DependencyGraphTest$@Test fun countsStatisticsWell()</ID>
6+
<ID>MaxLineLength:FullProjectMultipleAppliedGradleTest.kt$FullProjectMultipleAppliedGradleTest$"GraphStatistics(modulesCount=3, edgesCount=3, height=2, longestPath=':app -&gt; :core -&gt; :core-api')${System.lineSeparator()}"</ID>
7+
<ID>MaxLineLength:FullProjectRootGradleTest.kt$FullProjectRootGradleTest$"GraphStatistics\\(modulesCount=1, edgesCount=0, height=0, longestPath=\'root.*\'\\)${System.lineSeparator()}${System.lineSeparator()}"</ID>
68
<ID>MaxLineLength:ModuleTreeHeightAssert.kt$ModuleTreeHeightAssert$"Module $moduleName is allowed to have maximum height of $maxHeight, but has $height, problematic dependencies: ${longestPath.pathString()}"</ID>
79
<ID>MaxLineLength:ModuleTreeHeightAssert.kt$ModuleTreeHeightAssert$"Module Graph is allowed to have maximum height of $maxHeight, but has $height, problematic dependencies: ${longestPath.pathString()}"</ID>
810
<ID>MaxLineLength:OnAnyBuildAssertTest.kt$OnAnyBuildAssertTest$"""["App"(':app') -&gt; "Implementation"(':core'), "App"(':app') -&gt; "Implementation"(':feature')] not allowed by any of ['Implementation -&gt; Api', 'App -&gt; Api']"""</ID>

plugin/src/main/kotlin/com/jraska/module/graph/DependencyGraph.kt

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class DependencyGraph private constructor() {
5959
require(nodes.contains(key)) { "Dependency Tree doesn't contain module: $key" }
6060

6161
val connections = mutableListOf<Pair<String, String>>()
62-
addConnections(nodes.getValue(key), connections)
62+
addConnections(nodes.getValue(key), connections, mutableSetOf(), mutableSetOf())
6363

6464
return if (connections.isEmpty()) {
6565
createSingular(key)
@@ -77,11 +77,28 @@ class DependencyGraph private constructor() {
7777
private fun addConnections(
7878
node: Node,
7979
into: MutableList<Pair<String, String>>,
80+
path: MutableSet<Node>,
81+
visited: MutableSet<Node>,
8082
) {
81-
node.dependsOn.forEach {
82-
into.add(node.key to it.key)
83-
addConnections(it, into)
83+
if (visited.contains(node)) {
84+
return
85+
} else {
86+
visited.add(node)
87+
}
88+
89+
path.add(node)
90+
node.dependsOn.forEach { dependant ->
91+
into.add(node.key to dependant.key)
92+
93+
val nodeInCurrentPath = path.contains(dependant)
94+
if (nodeInCurrentPath) {
95+
val pathText = path.joinToString(separator = ", ") { it.key }
96+
error("Dependency cycle detected! Cycle in nodes: '$pathText'.")
97+
}
98+
addConnections(dependant, into, path, visited)
8499
}
100+
101+
path.remove(node)
85102
}
86103

87104
private fun countEdges(): Int = nodes().flatMap { node -> node.dependsOn }.count()

plugin/src/test/kotlin/com/jraska/module/graph/DependencyGraphPerformanceTest.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,28 @@ class DependencyGraphPerformanceTest {
2929
assert(statistics.edgesCount == 15259)
3030
assert(statistics.longestPath.pathString().startsWith("23 -> 31 -> 36 -> 57 -> 61 -> 72 -> 74 -> 75"))
3131
}
32+
33+
@Test(timeout = 1_000)
34+
fun whenTheGraphIsLarge_statisticsOfSubgraphMatchFast() {
35+
val subGraphStatistics = dependencyGraph.subTree("31").statistics()
36+
37+
assert(subGraphStatistics.height == 58)
38+
assert(subGraphStatistics.longestPath.pathString().startsWith("31 -> 36 -> 57 -> 61 -> 72 -> 74 -> 75"))
39+
}
40+
41+
@Test(timeout = 1_000)
42+
fun whenTheGraphIsLarge_statisticsCreatedFast() {
43+
val subGraphStatistics = dependencyGraph.subTree("500").statistics()
44+
assert(subGraphStatistics.modulesCount == 281)
45+
}
46+
47+
@Test(timeout = 1_000) // was running out of heap before optimisation
48+
fun whenTheGraphIsLarge_statisticsLargeCreatedFast() {
49+
val subGraphStatistics = dependencyGraph.subTree("2").statistics()
50+
51+
assert(subGraphStatistics.modulesCount == 870)
52+
assert(subGraphStatistics.edgesCount == 11650)
53+
assert(subGraphStatistics.height == 55)
54+
assert(subGraphStatistics.longestPath.pathString().startsWith("2 -> 30 -> 76 -> 105 -> 119 -> "))
55+
}
3256
}

plugin/src/test/kotlin/com/jraska/module/graph/DependencyGraphTest.kt

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,4 +165,63 @@ class DependencyGraphTest {
165165
),
166166
)
167167
}
168+
169+
@Test(expected = IllegalStateException::class)
170+
fun cyclesDoNotOverflow() {
171+
val dependencyTree =
172+
DependencyGraph.create(
173+
"feature" to "lib",
174+
"lib" to "core",
175+
"lib" to "feature",
176+
"feature" to "core",
177+
"app" to "feature",
178+
)
179+
180+
dependencyTree.subTree("app")
181+
}
182+
183+
@Test
184+
fun doesNotDetectCycleOnMultiEntry() {
185+
val dependencyTree =
186+
DependencyGraph.create(
187+
"feature" to "lib",
188+
"app" to "lib",
189+
"app" to "feature",
190+
)
191+
192+
assert(dependencyTree.subTree("app").findRoot().key == "app")
193+
}
194+
195+
@Test(expected = IllegalStateException::class)
196+
fun detectsTwoNodesCycle() {
197+
val dependencyTree =
198+
DependencyGraph.create(
199+
"feature" to "app",
200+
"app" to "feature",
201+
)
202+
203+
dependencyTree.subTree("app")
204+
}
205+
206+
@Test(expected = IllegalStateException::class)
207+
fun detectsThreeNodesCycle() {
208+
val dependencyTree =
209+
DependencyGraph.create(
210+
"feature" to "app",
211+
"app" to "lib",
212+
"lib" to "feature",
213+
)
214+
215+
dependencyTree.subTree("app")
216+
}
217+
218+
@Test(expected = IllegalStateException::class)
219+
fun detectsSingleNodeCycle() {
220+
val dependencyTree =
221+
DependencyGraph.create(
222+
"app" to "app",
223+
)
224+
225+
dependencyTree.subTree("app")
226+
}
168227
}

plugin/src/test/kotlin/com/jraska/module/graph/assertion/FullProjectGradleTest.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.jraska.module.graph.assertion
22

3-
import org.hamcrest.CoreMatchers
4-
import org.hamcrest.MatcherAssert
53
import org.junit.Before
64
import org.junit.Rule
75
import org.junit.Test
@@ -129,9 +127,8 @@ class FullProjectGradleTest {
129127
"-Pmodules.graph.of.module=:feature",
130128
).output
131129

132-
MatcherAssert.assertThat(
133-
output,
134-
CoreMatchers.containsString(
130+
assert(
131+
output.contains(
135132
"digraph G {\n" +
136133
"\":feature('Implementation')\" -> \":core-api('Api')\" [color=red style=bold]\n" +
137134
"}",
@@ -148,9 +145,8 @@ class FullProjectGradleTest {
148145
"-Pmodules.graph.of.module=:feature",
149146
).output
150147

151-
MatcherAssert.assertThat(
152-
output,
153-
CoreMatchers.containsString(
148+
assert(
149+
output.contains(
154150
"GraphStatistics(modulesCount=2, edgesCount=1, height=1, longestPath=':feature -> :core-api')",
155151
),
156152
)

0 commit comments

Comments
 (0)