From 0de0f6bc0c0b1b687d1843a5d56603429b5bdd35 Mon Sep 17 00:00:00 2001 From: Nikita Koval Date: Thu, 5 Sep 2019 15:23:58 +0200 Subject: [PATCH] Do not print empty scenario parts and empty threads. Fix alignment for init and post parts. Fixes #9 --- .../jetbrains/kotlinx/lincheck/Reporter.kt | 65 +++++++++++-------- .../execution/RandomExecutionGenerator.java | 1 + .../lincheck/test/AlmostEmptyScenarioTest.kt | 32 +++++++++ 3 files changed, 72 insertions(+), 26 deletions(-) create mode 100644 lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/AlmostEmptyScenarioTest.kt diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt index 5ae019941..47965e94d 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt +++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt @@ -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()) @@ -65,11 +56,11 @@ private fun printInColumns(groupedObjects: List>): String { .map { groupedObjects[it] } .map { it.getOrNull(rowIndex)?.toString().orEmpty() } // print empty strings for empty cells } - val columndWidths: List = (0 until nColumns).map { columnIndex -> + val columnWidths: List = (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") } @@ -82,32 +73,54 @@ private fun uniteActorsAndResults(actors: List, results: List): 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>, results: List>): List> { + 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, results: List): List { + 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>, results: List>): List> { - 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]) } } \ No newline at end of file diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.java index b56f02370..df41c13f5 100644 --- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.java +++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/RandomExecutionGenerator.java @@ -91,6 +91,7 @@ public ExecutionScenario nextExecution() { it.remove(); } } + parallelExecution = parallelExecution.stream().filter(actors -> !actors.isEmpty()).collect(Collectors.toList()); // Create post execution part List leftActorGenerators = new ArrayList<>(parallelGroup); for (ThreadGen threadGen : tgs2) diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/AlmostEmptyScenarioTest.kt b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/AlmostEmptyScenarioTest.kt new file mode 100644 index 000000000..78c312e93 --- /dev/null +++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/AlmostEmptyScenarioTest.kt @@ -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*\\|"))) + } + } +} \ No newline at end of file