[CORDA-2326] Ensure right step is rendered by AnsiProgressRenderer (#4514)

* Add test to reproduce problem

* Remap step indices when the step tree changes
This commit is contained in:
JamesHR3 2019-01-11 09:53:58 +00:00 committed by GitHub
parent 7bbe4668d6
commit 5e32e718ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 54 additions and 17 deletions

View File

@ -85,6 +85,7 @@ abstract class ANSIProgressRenderer {
stepsTreeFeed?.apply {
tree = snapshot
subscriptionTree = updates.subscribe({
remapIndices(it)
tree = it
draw(true)
}, { done(it) }, { done(null) })
@ -92,7 +93,15 @@ abstract class ANSIProgressRenderer {
}
}
private fun remapIndices(newTree: List<Pair<Int, String>>) {
val newIndices = newTree.filter {
treeIndexProcessed.contains(tree.indexOf(it))
}.map {
newTree.indexOf(it)
}.toMutableSet()
treeIndex = newIndices.max() ?: 0
treeIndexProcessed = if (newIndices.isNotEmpty()) newIndices else mutableSetOf(0)
}
@Synchronized protected fun draw(moveUp: Boolean, error: Throwable? = null) {
if (!usingANSI) {

View File

@ -1,8 +1,6 @@
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 com.nhaarman.mockito_kotlin.*
import net.corda.core.flows.StateMachineRunId
import net.corda.core.internal.concurrent.openFuture
import net.corda.core.messaging.DataFeed
@ -28,38 +26,68 @@ class ANSIProgressRendererTest {
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_4_LABEL = "Running step 4"
private const val STEP_5_LABEL = "Running step 5"
private val STEP_1_SUCCESS_OUTPUT = if (SystemUtils.IS_OS_WINDOWS) """DONE: $STEP_1_LABEL""" else """$STEP_1_LABEL"""
private const val STEP_2_SKIPPED_OUTPUT = """ $INTENSITY_FAINT_ON_ASCII$STEP_2_LABEL$INTENSITY_OFF_ASCII"""
private val STEP_3_ACTIVE_OUTPUT = if (SystemUtils.IS_OS_WINDOWS) """CURRENT: $INTENSITY_BOLD_ON_ASCII$STEP_3_LABEL$INTENSITY_OFF_ASCII""" else """▶︎ $INTENSITY_BOLD_ON_ASCII$STEP_3_LABEL$INTENSITY_OFF_ASCII"""
fun stepSuccess(stepLabel: String): String {
return if (SystemUtils.IS_OS_WINDOWS) """DONE: $stepLabel""" else """$stepLabel"""
}
fun stepSkipped(stepLabel: String): String {
return """ $INTENSITY_FAINT_ON_ASCII$stepLabel$INTENSITY_OFF_ASCII"""
}
fun stepActive(stepLabel: String): String {
return if (SystemUtils.IS_OS_WINDOWS) """CURRENT: $INTENSITY_BOLD_ON_ASCII$stepLabel$INTENSITY_OFF_ASCII""" else """▶︎ $INTENSITY_BOLD_ON_ASCII$stepLabel$INTENSITY_OFF_ASCII"""
}
}
lateinit var printWriter: RenderPrintWriter
lateinit var progressRenderer: ANSIProgressRenderer
lateinit var indexSubject: PublishSubject<Int>
lateinit var feedSubject: PublishSubject<List<Pair<Int, String>>>
lateinit var flowProgressHandle: FlowProgressHandleImpl<*>
@Before
fun setup() {
printWriter = mock()
progressRenderer = CRaSHANSIProgressRenderer(printWriter)
indexSubject = PublishSubject.create<Int>()
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)
flowProgressHandle = FlowProgressHandleImpl(StateMachineRunId.createRandom(), openFuture<String>(), Observable.empty(), stepsTreeIndexFeed, stepsTreeFeed)
}
@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)
feedSubject.onNext(listOf(Pair(0, STEP_1_LABEL), Pair(0, STEP_2_LABEL), Pair(0, STEP_3_LABEL)))
// 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()
verify(printWriter, times(2)).print(captor.capture())
assertThat(captor.secondValue.toString()).containsSequence(stepSuccess(STEP_1_LABEL), stepSkipped(STEP_2_LABEL), stepActive(STEP_3_LABEL))
verify(printWriter, times(2)).flush()
}
@Test
fun `changing tree causes correct steps to be marked as done`() {
progressRenderer.render(flowProgressHandle)
feedSubject.onNext(listOf(Pair(0, STEP_1_LABEL), Pair(1, STEP_2_LABEL), Pair(1, STEP_3_LABEL), Pair(0, STEP_4_LABEL), Pair(0, STEP_5_LABEL)))
indexSubject.onNext(1)
indexSubject.onNext(2)
val captor = argumentCaptor<Ansi>()
verify(printWriter, times(3)).print(captor.capture())
assertThat(captor.lastValue.toString()).containsSequence(stepSuccess(STEP_1_LABEL), stepSuccess(STEP_2_LABEL), stepActive(STEP_3_LABEL))
verify(printWriter, times(3)).flush()
feedSubject.onNext(listOf(Pair(0, STEP_1_LABEL), Pair(0, STEP_4_LABEL), Pair(0, STEP_5_LABEL)))
verify(printWriter, times(4)).print(captor.capture())
assertThat(captor.lastValue.toString()).containsSequence(stepActive(STEP_1_LABEL))
assertThat(captor.lastValue.toString()).doesNotContain(stepActive(STEP_5_LABEL))
verify(printWriter, times(4)).flush()
}
}