-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: Generate tasks diagram (#2102)
### Motivation Scaling the `flank.corellium.domain.test.android.task` package by adding new tasks is increasing the complexity of relations between them. Graphical visualization of the graph can improve development by giving an additional point of view. ### Goal Automatically generate a diagram of relations between tasks. ### Done * Adds module `:tool:execution:parallel:plantuml` for auto generating tasks relation graph basing on `:tool:execution:parallel` lib. * Adds `AndroidTasksDiagram` for triggering diagram generation. ### Result Generated diagram: ![TestAndroid.execute graph](http://www.plantuml.com/plantuml/proxy?cache=no&fmt=svg&src=https://raw.githubusercontent.com/Flank/flank/2083_Add_module_tool-execution-parallel-plantuml/corellium/domain/TestAndroid-execute.puml)
- Loading branch information
Showing
9 changed files
with
246 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
@startuml | ||
|
||
skinparam componentStyle rectangle | ||
|
||
note as N #ffffff | ||
* Brighter tasks are required by the darker tasks. | ||
* The brightness means how fast the task will start. | ||
* White tasks are starting first. | ||
end note | ||
|
||
[Authorize] #fbfbfb | ||
[OutputDir] #fbfbfb | ||
[ParseApkInfo] #fbfbfb | ||
[ParseTestCases] #fbfbfb | ||
[LoadPreviousDurations] #dfdfdf | ||
[PrepareShards] #c3c3c3 | ||
[DumpShards] #a7a7a7 | ||
[InvokeDevices] #a7a7a7 | ||
[InstallApks] #8b8b8b | ||
[ExecuteTests] #6f6f6f | ||
[GenerateReport] #535353 | ||
[CompleteTests] #373737 | ||
|
||
[DumpShards] --> [PrepareShards] | ||
[DumpShards] --> [OutputDir] | ||
[ExecuteTests] --> [PrepareShards] | ||
[ExecuteTests] --> [ParseApkInfo] | ||
[ExecuteTests] --> [Authorize] | ||
[ExecuteTests] --> [InvokeDevices] | ||
[ExecuteTests] --> [InstallApks] | ||
[CompleteTests] --> [GenerateReport] | ||
[CompleteTests] --> [DumpShards] | ||
[GenerateReport] --> [ExecuteTests] | ||
[GenerateReport] --> [OutputDir] | ||
[InstallApks] --> [Authorize] | ||
[InstallApks] --> [PrepareShards] | ||
[InstallApks] --> [InvokeDevices] | ||
[InvokeDevices] --> [Authorize] | ||
[InvokeDevices] --> [PrepareShards] | ||
[LoadPreviousDurations] --> [ParseTestCases] | ||
[PrepareShards] --> [ParseTestCases] | ||
[PrepareShards] --> [LoadPreviousDurations] | ||
|
||
@enduml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
corellium/domain/src/test/kotlin/flank/corellium/domain/AndroidTestDiagram.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package flank.corellium.domain | ||
|
||
import flank.exection.parallel.plantuml.generatePlanUml | ||
import org.junit.Test | ||
|
||
class AndroidTestDiagram { | ||
|
||
@Test | ||
fun generate() { | ||
TestAndroid.run { generatePlanUml(execute - context.validate) } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile | ||
|
||
plugins { | ||
kotlin(Plugins.Kotlin.PLUGIN_JVM) | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
tasks.withType<KotlinCompile> { kotlinOptions.jvmTarget = "1.8" } | ||
|
||
dependencies { | ||
api(project(":tool:execution:parallel")) | ||
implementation(Dependencies.KOTLIN_COROUTINES_CORE) | ||
testImplementation(Dependencies.JUNIT) | ||
} |
66 changes: 66 additions & 0 deletions
66
.../execution/parallel/plantuml/src/main/kotlin/flank/exection/parallel/plantuml/PlantUml.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package flank.exection.parallel.plantuml | ||
|
||
import flank.exection.parallel.Tasks | ||
import flank.exection.parallel.plantuml.internal.generatePlanUmlFile | ||
import flank.exection.parallel.plantuml.internal.generatePlantUmlString | ||
import java.io.File | ||
|
||
/** | ||
* Generates plantuml file and saves on drive. | ||
* | ||
* @receiver Source object of execution. Used for generating filename and reducing tasks names. | ||
* @param tasks Graph of tasks required to generate diagram. | ||
* @param dir Optional path to directory for generated file. | ||
*/ | ||
fun Any.generatePlanUml( | ||
tasks: Tasks, | ||
dir: String = "" | ||
): File = generatePlanUmlFile( | ||
tasks = tasks, | ||
path = File(dir).resolve(javaClass.simpleName).absolutePath + "-execute.puml", | ||
prefixToRemove = javaClass.name | ||
) | ||
|
||
/** | ||
* Generates plantuml file and saves on drive. | ||
* | ||
* @param tasks Graph of tasks required to generate diagram. | ||
* @param path Path to generated file. | ||
* @param prefixToRemove Optional prefix to remove from each task name. | ||
*/ | ||
fun generatePlanUml( | ||
tasks: Tasks, | ||
path: String, | ||
prefixToRemove: String = "", | ||
): File = generatePlanUmlFile( | ||
tasks = tasks, | ||
path = path, | ||
prefixToRemove = prefixToRemove | ||
) | ||
|
||
/** | ||
* Generates plantuml string. | ||
* | ||
* @receiver Source object of execution. Used for reducing tasks names. | ||
* @param tasks Graph of tasks required to generate diagram. | ||
*/ | ||
fun Any.generatePlantUml( | ||
tasks: Tasks | ||
): String = generatePlantUmlString( | ||
tasks = tasks, | ||
prefixToRemove = javaClass.name | ||
) | ||
|
||
/** | ||
* Generates plantuml string. | ||
* | ||
* @param tasks Graph of tasks required to generate diagram. | ||
* @param prefixToRemove Optional prefix to remove from each task name. | ||
*/ | ||
fun generatePlantUml( | ||
tasks: Tasks, | ||
prefixToRemove: String = "", | ||
): String = generatePlantUmlString( | ||
tasks = tasks, | ||
prefixToRemove = prefixToRemove | ||
) |
14 changes: 14 additions & 0 deletions
14
...lantuml/src/main/kotlin/flank/exection/parallel/plantuml/internal/GeneratePlantUmlFile.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package flank.exection.parallel.plantuml.internal | ||
|
||
import flank.exection.parallel.Tasks | ||
import java.io.File | ||
|
||
internal fun generatePlanUmlFile( | ||
tasks: Tasks, | ||
path: String, | ||
prefixToRemove: String, | ||
): File { | ||
val plant = generatePlantUmlString(tasks, prefixToRemove) | ||
println(plant) | ||
return File(path).apply { writeText(plant) } | ||
} |
85 changes: 85 additions & 0 deletions
85
...ntuml/src/main/kotlin/flank/exection/parallel/plantuml/internal/GeneratePlantUmlString.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package flank.exection.parallel.plantuml.internal | ||
|
||
import flank.exection.parallel.Parallel | ||
import flank.exection.parallel.Tasks | ||
import java.awt.Color | ||
|
||
// =================== Internal API =================== | ||
|
||
internal fun generatePlantUmlString( | ||
tasks: Tasks, | ||
prefixToRemove: String = "", | ||
): String { | ||
val graph: Graph = tasks.associate { task -> task.signature.type to task.signature.args } | ||
val depth: Map<Node, Int> = graph.calculateDepth() | ||
val maxDepth: Int = depth.values.maxOrNull() ?: 0 | ||
val colors: Colors = depth.mapValues { (_, value) -> calculateColor(maxDepth, value) } | ||
val name = fun Any.() = javaClass.name.removePrefix("$prefixToRemove$").replace('$', '.').let { "[$it]" } | ||
|
||
return """ | ||
@startuml | ||
skinparam componentStyle rectangle | ||
note as N #ffffff | ||
* Brighter tasks are required by the darker tasks. | ||
* The brightness means how fast the task will start. | ||
* White tasks are starting first. | ||
end note | ||
${colors.printColors(name)} | ||
${graph.printRelations(name)} | ||
@enduml | ||
""".trimIndent() | ||
} | ||
|
||
// =================== Private implementation =================== | ||
|
||
private typealias Graph = Map<Node, Set<Node>> | ||
private typealias Node = Parallel.Type<*> | ||
private typealias Colors = Map<Node, String> | ||
|
||
private fun calculateColor(maxDepth: Int, value: Int): String { | ||
val c = (COLOR_MAX / maxDepth) * (maxDepth - value) + COLOR_OFFSET | ||
return Integer.toHexString(Color(c, c, c).rgb).drop(2) // drop alpha | ||
} | ||
|
||
private const val COLOR_MAX = 200 | ||
private const val COLOR_OFFSET = 55 | ||
|
||
private fun <T> Map<T, Set<T>>.calculateDepth(): Map<T, Int> { | ||
var jump = 0 | ||
val state = (values.flatten() - keys).toMutableList() | ||
val remaining: MutableMap<T, Set<T>> = toMutableMap() | ||
val depth = mutableMapOf<T, Int>() | ||
|
||
while (depth.size < size) { | ||
val current = remaining | ||
.filterValues { state.containsAll(it) }.keys | ||
.onEach { depth[it] = jump } | ||
state += current | ||
remaining -= current | ||
jump++ | ||
} | ||
|
||
return depth | ||
} | ||
|
||
private fun Colors.printColors( | ||
name: Node.() -> String | ||
) = toList().joinToString("\n") { (node, value) -> | ||
"${node.name()} #$value" | ||
} | ||
|
||
private fun Graph.printRelations( | ||
name: Node.() -> String | ||
) = filterValues { dependencies -> | ||
dependencies.isNotEmpty() | ||
}.toList().joinToString("\n") { (node, dependencies) -> | ||
dependencies.joinToString("\n") { dep -> | ||
node.name() + " --> " + dep.name() | ||
} | ||
} |