[CORDA-1676] Adjust progress tracking rendering for skipped elements (#4372)

* Adjust styling for flow steps that are skipped
This commit is contained in:
Dimos Raptis 2018-12-10 15:00:47 +00:00 committed by GitHub
parent bad7b9b187
commit f58757bda9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 11 deletions

View File

@ -52,8 +52,6 @@ object Emoji {
val CODE_DEVELOPER: String = codePointsString(0x1F469, 0x200D, 0x1F4BB) val CODE_DEVELOPER: String = codePointsString(0x1F469, 0x200D, 0x1F4BB)
@JvmStatic @JvmStatic
val CODE_WARNING_SIGN: String = codePointsString(0x26A0, 0xFE0F) val CODE_WARNING_SIGN: String = codePointsString(0x26A0, 0xFE0F)
@JvmStatic
val CROSS_MARK_BUTTON: String = codePointsString(0x274E)
/** /**
* When non-null, toString() methods are allowed to use emoji in the output as we're going to render them to a * When non-null, toString() methods are allowed to use emoji in the output as we're going to render them to a
@ -81,7 +79,6 @@ object Emoji {
val rightArrow: String get() = if (emojiMode.get() != null) "$CODE_RIGHT_ARROW " else "▶︎" val rightArrow: String get() = if (emojiMode.get() != null) "$CODE_RIGHT_ARROW " else "▶︎"
val skullAndCrossbones: String get() = if (emojiMode.get() != null) "$CODE_SKULL_AND_CROSSBONES " else "" val skullAndCrossbones: String get() = if (emojiMode.get() != null) "$CODE_SKULL_AND_CROSSBONES " else ""
val noEntry: String get() = if (emojiMode.get() != null) "$CODE_NO_ENTRY " else "" val noEntry: String get() = if (emojiMode.get() != null) "$CODE_NO_ENTRY " else ""
val notRun: String get() = if (emojiMode.get() != null) "$CROSS_MARK_BUTTON " else "-"
inline fun <T> renderIfSupported(body: () -> T): T { inline fun <T> renderIfSupported(body: () -> T): T {
if (hasEmojiTerminal) if (hasEmojiTerminal)

View File

@ -10,6 +10,7 @@ import org.apache.logging.log4j.core.appender.ConsoleAppender
import org.apache.logging.log4j.core.appender.OutputStreamManager import org.apache.logging.log4j.core.appender.OutputStreamManager
import org.crsh.text.RenderPrintWriter import org.crsh.text.RenderPrintWriter
import org.fusesource.jansi.Ansi import org.fusesource.jansi.Ansi
import org.fusesource.jansi.Ansi.Attribute
import org.fusesource.jansi.AnsiConsole import org.fusesource.jansi.AnsiConsole
import org.fusesource.jansi.AnsiOutputStream import org.fusesource.jansi.AnsiOutputStream
import rx.Subscription import rx.Subscription
@ -146,22 +147,25 @@ abstract class ANSIProgressRenderer {
with(ansi) { with(ansi) {
var lines = 0 var lines = 0
for ((index, step) in tree.withIndex()) { for ((index, step) in tree.withIndex()) {
val processedStep = treeIndexProcessed.contains(index)
val skippedStep = index < treeIndex && !processedStep
val activeStep = index == treeIndex
val marker = when { val marker = when {
treeIndexProcessed.contains(index) -> " ${Emoji.greenTick} " processedStep -> " ${Emoji.greenTick} "
index < treeIndex -> " ${Emoji.notRun} " skippedStep -> " "
treeIndex == tree.lastIndex -> "${Emoji.greenTick} " activeStep -> "${Emoji.rightArrow} "
index == treeIndex -> "${Emoji.rightArrow} "
error -> "${Emoji.noEntry} " error -> "${Emoji.noEntry} "
else -> " " // Not reached yet. else -> " " // Not reached yet.
} }
a(" ".repeat(step.first)) a(" ".repeat(step.first))
a(marker) a(marker)
val active = index == treeIndex when {
if (active) bold() activeStep -> renderInBold(step.second, ansi)
a(step.second) skippedStep -> renderInFaint(step.second, ansi)
if (active) boldOff() else -> a(step.second)
}
eraseLine(Ansi.Erase.FORWARD) eraseLine(Ansi.Erase.FORWARD)
newline() newline()
@ -171,6 +175,21 @@ abstract class ANSIProgressRenderer {
} }
} }
private fun renderInBold(payload: String, ansi: Ansi): Unit {
with(ansi) {
a(Attribute.INTENSITY_BOLD)
a(payload)
a(Attribute.INTENSITY_BOLD_OFF)
}
}
private fun renderInFaint(payload: String, ansi: Ansi): Unit {
with(ansi) {
a(Attribute.INTENSITY_FAINT)
a(payload)
a(Attribute.INTENSITY_BOLD_OFF)
}
}
} }

View File

@ -0,0 +1,64 @@
package net.corda.tools.shell.utilities
import com.nhaarman.mockito_kotlin.argumentCaptor
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.verify
import net.corda.core.flows.StateMachineRunId
import net.corda.core.internal.concurrent.openFuture
import net.corda.core.messaging.DataFeed
import net.corda.core.messaging.FlowProgressHandleImpl
import net.corda.tools.shell.utlities.ANSIProgressRenderer
import net.corda.tools.shell.utlities.CRaSHANSIProgressRenderer
import org.assertj.core.api.Assertions.assertThat
import org.crsh.text.RenderPrintWriter
import org.junit.Test
import rx.Observable
import org.fusesource.jansi.Ansi
import org.junit.Before
import rx.subjects.PublishSubject
class ANSIProgressRendererTest {
companion object {
private const val INTENSITY_BOLD_ON_ASCII = "[1m"
private const val INTENSITY_OFF_ASCII = "[22m"
private const val INTENSITY_FAINT_ON_ASCII = "[2m"
private const val STEP_1_LABEL = "Running step 1"
private const val STEP_2_LABEL = "Running step 2"
private const val STEP_3_LABEL = "Running step 3"
private const val STEP_1_SUCCESS_OUTPUT = """$STEP_1_LABEL"""
private const val STEP_2_SKIPPED_OUTPUT = """ $INTENSITY_FAINT_ON_ASCII$STEP_2_LABEL$INTENSITY_OFF_ASCII"""
private const val STEP_3_ACTIVE_OUTPUT = """$INTENSITY_BOLD_ON_ASCII$STEP_3_LABEL$INTENSITY_OFF_ASCII"""
}
lateinit var printWriter: RenderPrintWriter
lateinit var progressRenderer: ANSIProgressRenderer
@Before
fun setup() {
printWriter = mock()
progressRenderer = CRaSHANSIProgressRenderer(printWriter)
}
@Test
fun `test that steps are rendered appropriately depending on their status`() {
val indexSubject = PublishSubject.create<Int>()
val feedSubject = PublishSubject.create<List<Pair<Int, String>>>()
val stepsTreeIndexFeed = DataFeed<Int, Int>(0, indexSubject)
val stepsTreeFeed = DataFeed<List<Pair<Int, String>>, List<Pair<Int, String>>>(listOf(), feedSubject)
val flowProgressHandle = FlowProgressHandleImpl(StateMachineRunId.createRandom(), openFuture<String>(), Observable.empty(), stepsTreeIndexFeed, stepsTreeFeed)
progressRenderer.render(flowProgressHandle)
// The flow is currently at step 3, while step 1 has been completed and step 2 has been skipped.
indexSubject.onNext(2)
feedSubject.onNext(listOf(Pair(0, STEP_1_LABEL), Pair(0, STEP_2_LABEL), Pair(0, STEP_3_LABEL)))
val captor = argumentCaptor<Ansi>()
verify(printWriter).print(captor.capture())
assertThat(captor.firstValue.toString()).containsSequence(STEP_1_SUCCESS_OUTPUT, STEP_2_SKIPPED_OUTPUT, STEP_3_ACTIVE_OUTPUT)
verify(printWriter).flush()
}
}