Skip to content

Commit

Permalink
Do not print empty scenario parts and empty threads. Fix alignment fo…
Browse files Browse the repository at this point in the history
…r init and post parts.

Fixes #9
  • Loading branch information
ndkoval committed Sep 5, 2019
1 parent f91c372 commit 0de0f6b
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 26 deletions.
65 changes: 39 additions & 26 deletions lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,6 @@ class Reporter @JvmOverloads constructor(val logLevel: LoggingLevel, val out: Pr
}
}

private fun StringBuilder.appendExecutionScenario(scenario: ExecutionScenario) {
appendln("Execution scenario (init part):")
appendln(scenario.initExecution)
appendln("Execution scenario (parallel part):")
appendln(printInColumns(scenario.parallelExecution))
appendln("Execution scenario (post part):")
append(scenario.postExecution)
}

inline fun log(logLevel: LoggingLevel, crossinline msg: () -> String) {
if (this.logLevel > logLevel) return
out.println(msg())
Expand All @@ -65,11 +56,11 @@ private fun <T> printInColumns(groupedObjects: List<List<T>>): String {
.map { groupedObjects[it] }
.map { it.getOrNull(rowIndex)?.toString().orEmpty() } // print empty strings for empty cells
}
val columndWidths: List<Int> = (0 until nColumns).map { columnIndex ->
val columnWidths: List<Int> = (0 until nColumns).map { columnIndex ->
(0 until nRows).map { rowIndex -> rows[rowIndex][columnIndex].length }.max()!!
}
return (0 until nRows)
.map { rowIndex -> rows[rowIndex].mapIndexed { columnIndex, cell -> cell.padEnd(columndWidths[columnIndex]) } }
.map { rowIndex -> rows[rowIndex].mapIndexed { columnIndex, cell -> cell.padEnd(columnWidths[columnIndex]) } }
.map { rowCells -> rowCells.joinToString(separator = " | ", prefix = "| ", postfix = " |") }
.joinToString(separator = "\n")
}
Expand All @@ -82,32 +73,54 @@ private fun uniteActorsAndResults(actors: List<Actor>, results: List<Result>): L
require(actors.size == results.size) {
"Different numbers of actors and matching results found (${actors.size} != ${results.size})"
}
return actors.indices.map { ActorWithResult("${actors[it]}", 1, "${results[it]}") }
}

val actorRepresentations = actors.map { it.toString() }
val resultRepresentations = results.map { it.toString() }
private fun uniteParallelActorsAndResults(actors: List<List<Actor>>, results: List<List<Result>>): List<List<ActorWithResult>> {
require(actors.size == results.size) {
"Different numbers of threads and matching results found (${actors.size} != ${results.size})"
}
return actors.mapIndexed { id, threadActors -> uniteActorsAndResultsAligned(threadActors, results[id]) }
}

private fun uniteActorsAndResultsAligned(actors: List<Actor>, results: List<Result>): List<ActorWithResult> {
require(actors.size == results.size) {
"Different numbers of actors and matching results found (${actors.size} != ${results.size})"
}
val actorRepresentations = actors.map { it.toString() }
val maxActorLength = actorRepresentations.map { it.length }.max()!!

return actorRepresentations.mapIndexed { id, actorRepr ->
val spaces = 1 + maxActorLength - actorRepr.length
ActorWithResult(actorRepr, spaces, resultRepresentations[id])
ActorWithResult(actorRepr, spaces, "${results[id]}")
}
}

private fun StringBuilder.appendExecutionScenario(scenario: ExecutionScenario) {
if (scenario.initExecution.isNotEmpty()) {
appendln("Execution scenario (init part):")
appendln(scenario.initExecution)
}
appendln("Execution scenario (parallel part):")
append(printInColumns(scenario.parallelExecution))
if (scenario.parallelExecution.isNotEmpty()) {
appendln()
appendln("Execution scenario (post part):")
append(scenario.postExecution)
}
}

fun StringBuilder.appendIncorrectResults(scenario: ExecutionScenario, results: ExecutionResult) {
appendln("= Invalid execution results: =")
appendln("Init part:")
appendln(uniteActorsAndResults(scenario.initExecution, results.initResults))
if (scenario.initExecution.isNotEmpty()) {
appendln("Init part:")
appendln(uniteActorsAndResults(scenario.initExecution, results.initResults))
}
appendln("Parallel part:")
val parallelExecutionData = uniteParallelActorsAndResults(scenario.parallelExecution, results.parallelResults)
appendln(printInColumns(parallelExecutionData))
appendln("Post part:")
append(uniteActorsAndResults(scenario.postExecution, results.postResults))
}

private fun uniteParallelActorsAndResults(actors: List<List<Actor>>, results: List<List<Result>>): List<List<ActorWithResult>> {
require(actors.size == results.size) {
"Different numbers of threads and matching results found (${actors.size} != ${results.size})"
append(printInColumns(parallelExecutionData))
if (scenario.postExecution.isNotEmpty()) {
appendln()
appendln("Post part:")
append(uniteActorsAndResults(scenario.postExecution, results.postResults))
}
return actors.mapIndexed { id, threadActors -> uniteActorsAndResults(threadActors, results[id]) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public ExecutionScenario nextExecution() {
it.remove();
}
}
parallelExecution = parallelExecution.stream().filter(actors -> !actors.isEmpty()).collect(Collectors.toList());
// Create post execution part
List<ActorGenerator> leftActorGenerators = new ArrayList<>(parallelGroup);
for (ThreadGen threadGen : tgs2)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.jetbrains.kotlinx.lincheck.test

import org.jetbrains.kotlinx.lincheck.LinChecker
import org.jetbrains.kotlinx.lincheck.annotations.Operation
import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest
import org.junit.Assert.*
import org.junit.Test
import java.util.concurrent.ThreadLocalRandom

@StressCTest(iterations = 1, requireStateEquivalenceImplCheck = false, actorsBefore = 1, actorsAfter = 1, threads = 3)
class AlmostEmptyScenarioTest {
@Operation(runOnce = true)
fun operation1() = ThreadLocalRandom.current().nextInt(5)

@Operation(runOnce = true)
fun operation2() = ThreadLocalRandom.current().nextInt(5)

@Test
fun test() {
try {
LinChecker.check(AlmostEmptyScenarioTest::class.java)
fail("Should fail with AssertionError")
} catch (e: AssertionError) {
val m = e.message!!
println(m)
assertFalse("Empty init/post parts should not be printed", m.contains(Regex("\\\\[\\s*\\\\]")))
assertFalse("Empty init/post parts should not be printed", m.contains(Regex("Init")))
assertFalse("Empty init/post parts should not be printed", m.contains(Regex("Post")))
assertFalse("Empty threads should not be printed", m.contains(Regex("\\|\\s*\\|")))
}
}
}

0 comments on commit 0de0f6b

Please sign in to comment.