Skip to content

Commit

Permalink
Add PlantUml export test for showEventLabels flag
Browse files Browse the repository at this point in the history
  • Loading branch information
nsk90 committed Dec 5, 2023
1 parent 84446a6 commit 04c2f79
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 30 deletions.
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,7 @@ to [PlantUML state diagram](https://plantuml.com/en/state-diagram).
```kotlin
val machine = createStateMachine(scope) { /* ... */ }
println(machine.exportToPlantUml(showEventLabels = false))
println(machine.exportToPlantUml())
```
Copy/paste resulting output to [Plant UML online editor](http://www.plantuml.com/plantuml/)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import ru.nsk.kstatemachine.TransitionDirectionProducerPolicy.CollectTargetState
*
* Conditional transitions currently are not supported.
*/
internal class ExportPlantUmlVisitor(val showEventLabels: Boolean = false) : CoVisitor {
internal class ExportPlantUmlVisitor(private val showEventLabels: Boolean) : CoVisitor {
private val builder = StringBuilder()
private var indent = 0
private val crossLevelTransitions = mutableListOf<String>()
Expand Down Expand Up @@ -69,8 +69,7 @@ internal class ExportPlantUmlVisitor(val showEventLabels: Boolean = false) : CoV
targetState.graphName()
}

val eventLabel = if(showEventLabels) transition.eventMatcher.eventClass.simpleName else null
val transitionString = "$sourceState --> $graphName${label(transition.name, eventLabel)}"
val transitionString = "$sourceState --> $graphName${transitionLabel(transition)}"

if (transition.sourceState.isNeighbor(targetState))
line(transitionString)
Expand Down Expand Up @@ -103,6 +102,12 @@ internal class ExportPlantUmlVisitor(val showEventLabels: Boolean = false) : CoV

private fun line(text: String) = builder.appendLine(SINGLE_INDENT.repeat(indent) + text)

private fun transitionLabel(transition: Transition<*>): String {
val eventName = if (showEventLabels) transition.eventMatcher.eventClass.simpleName else null
val entries = listOfNotNull(transition.name, eventName)
return label(entries.joinToString())
}

private companion object {
const val STAR = "[*]"
const val SINGLE_INDENT = " "
Expand All @@ -115,16 +120,15 @@ internal class ExportPlantUmlVisitor(val showEventLabels: Boolean = false) : CoV
return if (this !is StateMachine) name else "${name}_StateMachine"
}

fun label(name: String?, eventName: String? = null) =
if (name == null && eventName == null) "" else
" :${name?.let { " $it" } ?: ""}${eventName?.let { " $it" } ?: ""}"
fun label(text: String?) = if (!text.isNullOrBlank()) " : $text" else ""
}
}

suspend fun StateMachine.exportToPlantUml(showEventLabels: Boolean = false) = with(ExportPlantUmlVisitor(showEventLabels)) {
accept(this)
export()
}
suspend fun StateMachine.exportToPlantUml(showEventLabels: Boolean = false) =
with(ExportPlantUmlVisitor(showEventLabels)) {
accept(this)
export()
}

fun StateMachine.exportToPlantUmlBlocking(showEventLabels: Boolean = false) = coroutineAbstraction.runBlocking {
with(ExportPlantUmlVisitor(showEventLabels)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package ru.nsk.kstatemachine

import io.kotest.core.spec.style.StringSpec
import io.kotest.data.forAll
import io.kotest.data.headers
import io.kotest.data.row
import io.kotest.data.table
import io.kotest.matchers.shouldBe
import ru.nsk.kstatemachine.HistoryType.DEEP
import ru.nsk.kstatemachine.visitors.exportToPlantUml
Expand All @@ -27,6 +31,28 @@ State3 --> [*]
@enduml
"""

private const val PLANTUML_NESTED_STATES_SHOW_EVENT_LABELS_RESULT = """@startuml
hide empty description
state State1
state State3
state State2 {
state Final_subState
state Initial_subState
[*] --> Initial_subState
Initial_subState --> Final_subState : SwitchEvent
Final_subState --> [*]
}
[*] --> State1
State1 --> State2 : to State2, SwitchEvent
State1 --> State1 : SwitchEvent
State2 --> State3 : SwitchEvent
State2 --> State1 : back, SwitchEvent
State3 --> [*]
@enduml
"""

private const val PLANTUML_PARALLEL_STATES_RESULT = """@startuml
hide empty description
state parallel_states {
Expand Down Expand Up @@ -89,29 +115,35 @@ state inner_machine_StateMachine

class ExportToPlantUmlTest : StringSpec({
CoroutineStarterType.values().forEach { coroutineStarterType ->
"export nested states" {
val machine = createTestStateMachine(coroutineStarterType, name = "Nested states") {
val state1 = initialState("State1")
val state3 = finalState("State3")

val state2 = state("State2") {
transition<SwitchEvent> { targetState = state3 }
transition<SwitchEvent>("back") { targetState = state1 }

val finalSubState = finalState("Final subState")
initialState("Initial subState") {
transition<SwitchEvent> { targetState = finalSubState }
table(
headers("showEventLabels", "result"),
row(false, PLANTUML_NESTED_STATES_RESULT),
row(true, PLANTUML_NESTED_STATES_SHOW_EVENT_LABELS_RESULT),
).forAll { showEventLabels, result ->
"export nested states" {
val machine = createTestStateMachine(coroutineStarterType, name = "Nested states") {
val state1 = initialState("State1")
val state3 = finalState("State3")

val state2 = state("State2") {
transition<SwitchEvent> { targetState = state3 }
transition<SwitchEvent>("back") { targetState = state1 }

val finalSubState = finalState("Final subState")
initialState("Initial subState") {
transition<SwitchEvent> { targetState = finalSubState }
}
}
}

state1 {
transition<SwitchEvent>("to ${state2.name}") { targetState = state2 }
transition<SwitchEvent> { targetState = this@state1 }
transition<SwitchEvent>()
state1 {
transition<SwitchEvent>("to ${state2.name}") { targetState = state2 }
transition<SwitchEvent> { targetState = this@state1 }
transition<SwitchEvent>()
}
}
}

machine.exportToPlantUml() shouldBe PLANTUML_NESTED_STATES_RESULT
machine.exportToPlantUml(showEventLabels) shouldBe result
}
}

"export parallel states" {
Expand Down

0 comments on commit 04c2f79

Please sign in to comment.