diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt index 6ea2617c4..51ce2278c 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/CTestConfiguration.kt @@ -39,7 +39,7 @@ abstract class CTestConfiguration( ) { abstract fun createStrategy( testClass: Class<*>, scenario: ExecutionScenario, validationFunction: Actor?, - stateRepresentationMethod: Method?, verifier: Verifier + stateRepresentationMethod: Method? ): Strategy companion object { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt index e8eda2af4..49ed24c8e 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/LinChecker.kt @@ -13,12 +13,14 @@ import org.jetbrains.kotlinx.lincheck.annotations.* import org.jetbrains.kotlinx.lincheck.execution.* import org.jetbrains.kotlinx.lincheck.strategy.* import org.jetbrains.kotlinx.lincheck.verifier.* +import org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.* +import org.jetbrains.kotlinx.lincheck.strategy.stress.* import kotlin.reflect.* /** * This class runs concurrent tests. */ -class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { +class LinChecker(private val testClass: Class<*>, options: Options<*, *>?) { private val testStructure = CTestStructure.getFromTestClass(testClass) private val testConfigurations: List private val reporter: Reporter @@ -50,80 +52,65 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { check(testConfigurations.isNotEmpty()) { "No Lincheck test configuration to run" } for (testCfg in testConfigurations) { val failure = testCfg.checkImpl() - if (failure != null) return failure + if (failure != null) + return failure } return null } private fun CTestConfiguration.checkImpl(): LincheckFailure? { - val exGen = createExecutionGenerator(testStructure.randomProvider) - for (i in customScenarios.indices) { - val verifier = createVerifier() - val scenario = customScenarios[i] - scenario.validate() - reporter.logIteration(i + 1, customScenarios.size, scenario) - val failure = scenario.run(this, verifier) - if (failure != null) return failure - } var verifier = createVerifier() - repeat(iterations) { i -> + val generator = createExecutionGenerator(testStructure.randomProvider) + val randomScenarios = generateSequence { + generator.nextExecution().also { + // reset the parameter generator ranges to start with the same initial bounds for each scenario. + testStructure.parameterGenerators.forEach { it.reset() } + } + } + val scenarios = customScenarios.asSequence() + randomScenarios.take(iterations) + val scenariosSize = customScenarios.size + iterations + scenarios.forEachIndexed { i, scenario -> + val isCustomScenario = (i < customScenarios.size) // For performance reasons, verifier re-uses LTS from previous iterations. - // This behaviour is similar to a memory leak and can potentially cause OutOfMemoryError. + // This behavior is similar to a memory leak and can potentially cause OutOfMemoryError. // This is why we periodically create a new verifier to still have increased performance // from re-using LTS and limit the size of potential memory leak. // https://github.com/Kotlin/kotlinx-lincheck/issues/124 if ((i + 1) % VERIFIER_REFRESH_CYCLE == 0) verifier = createVerifier() - val scenario = exGen.nextExecution() scenario.validate() - reporter.logIteration(i + 1 + customScenarios.size, iterations, scenario) - val failure = scenario.run(this, verifier) - if (failure != null) { - val minimizedFailedIteration = if (!minimizeFailedScenario) failure else failure.minimize(this) - reporter.logFailedIteration(minimizedFailedIteration) - return minimizedFailedIteration - } - // Reset the parameter generator ranges to start with the same initial bounds on each scenario generation. - testStructure.parameterGenerators.forEach { it.reset() } - } - return null - } - - // Tries to minimize the specified failing scenario to make the error easier to understand. - // The algorithm is greedy: it tries to remove one actor from the scenario and checks - // whether a test with the modified one fails with error as well. If it fails, - // then the scenario has been successfully minimized, and the algorithm tries to minimize it again, recursively. - // Otherwise, if no actor can be removed so that the generated test fails, the minimization is completed. - // Thus, the algorithm works in the linear time of the total number of actors. - private fun LincheckFailure.minimize(testCfg: CTestConfiguration): LincheckFailure { - reporter.logScenarioMinimization(scenario) - var minimizedFailure = this - while (true) { - minimizedFailure = minimizedFailure.scenario.tryMinimize(testCfg) ?: break - } - return minimizedFailure - } - - private fun ExecutionScenario.tryMinimize(testCfg: CTestConfiguration): LincheckFailure? { - // Reversed indices to avoid conflicts with in-loop removals - for (i in threads.indices.reversed()) { - for (j in threads[i].indices.reversed()) { - tryMinimize(i, j) - ?.run(testCfg, testCfg.createVerifier()) - ?.let { return it } + reporter.logIteration(i + 1, scenariosSize, scenario) + var failure = scenario.run(i, this, verifier) + if (failure == null) + return@forEachIndexed + if (minimizeFailedScenario && !isCustomScenario) { + var j = i + 1 + reporter.logScenarioMinimization(scenario) + failure = failure.minimize { minimizedScenario -> + minimizedScenario.run(j++, this, createVerifier()) + } } + reporter.logFailedIteration(failure) + return failure } return null } - private fun ExecutionScenario.run(testCfg: CTestConfiguration, verifier: Verifier): LincheckFailure? = - testCfg.createStrategy( + private fun ExecutionScenario.run( + iteration: Int, + testCfg: CTestConfiguration, + verifier: Verifier, + ): LincheckFailure? { + val strategy = testCfg.createStrategy( testClass = testClass, scenario = this, validationFunction = testStructure.validationFunction, stateRepresentationMethod = testStructure.stateRepresentation, - verifier = verifier - ).run() + ) + return strategy.use { + it.runIteration(iteration, testCfg.invocationsPerIteration, verifier) + } + } private fun CTestConfiguration.createVerifier() = verifierClass.getConstructor(Class::class.java).newInstance(sequentialSpecification) @@ -135,6 +122,12 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { RandomProvider::class.java ).newInstance(this, testStructure, randomProvider) + private val CTestConfiguration.invocationsPerIteration get() = when (this) { + is ModelCheckingCTestConfiguration -> this.invocationsPerIteration + is StressCTestConfiguration -> this.invocationsPerIteration + else -> error("unexpected") + } + // This companion object is used for backwards compatibility. companion object { /** @@ -153,6 +146,33 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) { } } +// Tries to minimize the specified failing scenario to make the error easier to understand. +// The algorithm is greedy: it tries to remove one actor from the scenario and checks +// whether a test with the modified one fails with error as well. If it fails, +// then the scenario has been successfully minimized, and the algorithm tries to minimize it again, recursively. +// Otherwise, if no actor can be removed so that the generated test fails, the minimization is completed. +// Thus, the algorithm works in the linear time of the total number of actors. +private fun LincheckFailure.minimize(checkScenario: (ExecutionScenario) -> LincheckFailure?): LincheckFailure { + var minimizedFailure = this + while (true) { + minimizedFailure = minimizedFailure.scenario.tryMinimize(checkScenario) + ?: break + } + return minimizedFailure +} + +private fun ExecutionScenario.tryMinimize(checkScenario: (ExecutionScenario) -> LincheckFailure?): LincheckFailure? { + // Reversed indices to avoid conflicts with in-loop removals + for (i in threads.indices.reversed()) { + for (j in threads[i].indices.reversed()) { + tryMinimize(i, j) + ?.run(checkScenario) + ?.let { return it } + } + } + return null +} + /** * This is a short-cut for the following code: diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt index 558da9926..6c7c7dfa4 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/Strategy.kt @@ -9,9 +9,12 @@ */ package org.jetbrains.kotlinx.lincheck.strategy +import org.jetbrains.kotlinx.lincheck.runner.* import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario -import org.jetbrains.kotlinx.lincheck.runner.ExecutionPart +import org.jetbrains.kotlinx.lincheck.strategy.managed.Trace +import org.jetbrains.kotlinx.lincheck.verifier.Verifier import org.objectweb.asm.ClassVisitor +import java.io.Closeable /** * Implementation of this class describes how to run the generated execution. @@ -22,18 +25,130 @@ import org.objectweb.asm.ClassVisitor */ abstract class Strategy protected constructor( val scenario: ExecutionScenario -) { +) : Closeable { + /** + * Determines if the strategy requires bytecode transformation. + * + * @return `true` if a transformation is needed, `false` otherwise. + */ open fun needsTransformation() = false + + /** + * Creates a bytecode transformer required by the strategy.. + * + * @param cv the [ClassVisitor] to create a transformer for. + * @return a [ClassVisitor] representing the transformer. + */ open fun createTransformer(cv: ClassVisitor): ClassVisitor { throw UnsupportedOperationException("$javaClass strategy does not transform classes") } - abstract fun run(): LincheckFailure? + /** + * Sets the internal state of strategy to run the next invocation. + * + * @return true if there is next invocation to run, false if all invocations have been studied. + */ + open fun nextInvocation(): Boolean = true + /** + * Initializes the invocation. + * Should be called before each call to [runInvocation]. + */ + open fun initializeInvocation() {} + + /** + * Runs the current invocation and returns its result. + * + * Should be called after [initializeInvocation] and only if previous call to [nextInvocation] returned `true`: + * + * ```kotlin + * with(strategy) { + * if (nextInvocation()) { + * initializeInvocation() + * runInvocation() + * } + * } + * ``` + * + * For deterministic strategies, consecutive calls to [runInvocation] + * (without intervening [nextInvocation] calls) + * should run the same invocation, leading to the same results. + * + * @return the result of the invocation run. + */ + abstract fun runInvocation(): InvocationResult + + /** + * Tries to construct the trace leading to the given invocation result. + * + * @param result The invocation result. + * @return The collected trace, or null if it was not possible to collect the trace. + */ + open fun tryCollectTrace(result: InvocationResult): Trace? = null + + /** + * This method is called before the execution of a specific scenario part. + * + * @param part The execution part that is about to be executed. + */ open fun beforePart(part: ExecutionPart) {} /** - * Is invoked before each actor execution. + * This method is called before each actor execution. + * + * @param iThread the thread index on which the actor is starting */ open fun onActorStart(iThread: Int) {} + + /** + * Closes the strategy and releases any resources associated with it. + */ + override fun close() {} +} + +/** + * Runs one Lincheck's test iteration with the given strategy and verifier. + * + * @param iteration the id of the iteration. + * @param invocationsBound number of invocations to run. + * @param verifier the verifier to be used. + * + * @return the failure, if detected, null otherwise. + */ +fun Strategy.runIteration(iteration: Int, invocationsBound: Int, verifier: Verifier): LincheckFailure? { + var spinning = false + for (invocation in 0 until invocationsBound) { + if (!(spinning || nextInvocation())) + return null + spinning = false + initializeInvocation() + val failure = run { + val result = runInvocation() + spinning = (result is SpinCycleFoundAndReplayRequired) + if (!spinning) + verify(result, verifier) + else null + } + if (failure != null) + return failure + } + return null } + +/** + * Verifies the results of the given invocation. + * Attempts to collect the trace in case of incorrect results. + * + * @param result invocation result to verify. + * @param verifier the verifier to be used. + * + * @return failure, if invocation results are incorrect, null otherwise. + */ +fun Strategy.verify(result: InvocationResult, verifier: Verifier): LincheckFailure? = when (result) { + is CompletedInvocationResult -> + if (!verifier.verifyResults(scenario, result.results)) { + IncorrectResultsFailure(scenario, result.results, tryCollectTrace(result)) + } else null + else -> + result.toLincheckFailure(scenario, tryCollectTrace(result)) +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt index 296fb44bb..9372c91be 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt @@ -34,7 +34,6 @@ import kotlin.collections.set abstract class ManagedStrategy( private val testClass: Class<*>, scenario: ExecutionScenario, - private val verifier: Verifier, private val validationFunction: Actor?, private val stateRepresentationFunction: Method?, private val testCfg: ManagedCTestConfiguration @@ -109,6 +108,10 @@ abstract class ManagedStrategy( } } + override fun close() { + runner.close() + } + private fun createRunner(): ManagedStrategyRunner = ManagedStrategyRunner(this, testClass, validationFunction, stateRepresentationFunction, testCfg.timeoutMs, UseClocks.ALWAYS) @@ -127,19 +130,8 @@ abstract class ManagedStrategy( fun useBytecodeCache(): Boolean = !collectTrace && testCfg.eliminateLocalObjects && (testCfg.guarantees == ManagedCTestConfiguration.DEFAULT_GUARANTEES) - override fun run(): LincheckFailure? = try { - runImpl() - } finally { - runner.close() - } - // == STRATEGY INTERFACE METHODS == - /** - * This method implements the strategy logic. - */ - protected abstract fun runImpl(): LincheckFailure? - /** * This method is invoked before every thread context switch. * @param iThread current thread that is about to be switched @@ -160,9 +152,10 @@ abstract class ManagedStrategy( protected abstract fun chooseThread(iThread: Int): Int /** - * Returns all data to the initial state. + * Resets all internal data to the initial state and initializes current invocation to be run. */ - protected open fun initializeInvocation() { + override fun initializeInvocation() { + super.initializeInvocation() finished.fill(false) isSuspended.fill(false) currentActorId.fill(-1) @@ -175,34 +168,31 @@ abstract class ManagedStrategy( ManagedStrategyStateHolder.setState(runner.classLoader, this, testClass) } - override fun beforePart(part: ExecutionPart) { - traceCollector?.passCodeLocation(SectionDelimiterTracePoint(part)) + /** + * Runs the current invocation. + */ + override fun runInvocation(): InvocationResult { + val result = runner.run() + // Has strategy already determined the invocation result? + suddenInvocationResult?.let { return it } + return result } // == BASIC STRATEGY METHODS == - /** - * Checks whether the [result] is a failing one or is [CompletedInvocationResult] - * but the verification fails, and return the corresponding failure. - * Returns `null` if the result is correct. - */ - protected fun checkResult(result: InvocationResult): LincheckFailure? = when (result) { - is CompletedInvocationResult -> { - if (verifier.verifyResults(scenario, result.results)) null - else IncorrectResultsFailure(scenario, result.results, collectTrace(result)) - } - else -> result.toLincheckFailure(scenario, collectTrace(result)) + override fun beforePart(part: ExecutionPart) { + traceCollector?.passCodeLocation(SectionDelimiterTracePoint(part)) } /** * Re-runs the last invocation to collect its trace. */ - private fun collectTrace(failingResult: InvocationResult): Trace? { + override fun tryCollectTrace(result: InvocationResult): Trace? { val detectedByStrategy = suddenInvocationResult != null val canCollectTrace = when { detectedByStrategy -> true // ObstructionFreedomViolationInvocationResult or UnexpectedExceptionInvocationResult - failingResult is CompletedInvocationResult -> true - failingResult is ValidationFailureInvocationResult -> true + result is CompletedInvocationResult -> true + result is ValidationFailureInvocationResult -> true else -> false } @@ -219,19 +209,20 @@ abstract class ManagedStrategy( runner = createRunner() ManagedStrategyStateHolder.setState(runner.classLoader, this, testClass) runner.initialize() + initializeInvocation() loopDetector.enableReplayMode( - failDueToDeadlockInTheEnd = failingResult is DeadlockInvocationResult || failingResult is ObstructionFreedomViolationInvocationResult + failDueToDeadlockInTheEnd = result is DeadlockInvocationResult || result is ObstructionFreedomViolationInvocationResult ) val loggedResults = runInvocation() - val sameResultTypes = loggedResults.javaClass == failingResult.javaClass - val sameResults = loggedResults !is CompletedInvocationResult || failingResult !is CompletedInvocationResult || loggedResults.results == failingResult.results + val sameResultTypes = loggedResults.javaClass == result.javaClass + val sameResults = loggedResults !is CompletedInvocationResult || result !is CompletedInvocationResult || loggedResults.results == result.results check(sameResultTypes && sameResults) { StringBuilder().apply { appendln("Non-determinism found. Probably caused by non-deterministic code (WeakHashMap, Object.hashCode, etc).") appendln("== Reporting the first execution without execution trace ==") - appendln(failingResult.toLincheckFailure(scenario, null)) + appendln(result.toLincheckFailure(scenario, null)) appendln("== Reporting the second execution ==") appendln(loggedResults.toLincheckFailure(scenario, Trace(traceCollector!!.trace)).toString()) }.toString() @@ -239,17 +230,6 @@ abstract class ManagedStrategy( return Trace(traceCollector!!.trace) } - /** - * Runs the next invocation with the same [scenario][ExecutionScenario]. - */ - protected fun runInvocation(): InvocationResult { - initializeInvocation() - val result = runner.run() - // Has strategy already determined the invocation result? - suddenInvocationResult?.let { return it } - return result - } - private fun failIfObstructionFreedomIsRequired(lazyMessage: () -> String) { if (testCfg.checkObstructionFreedom && !curActorIsBlocking && !concurrentActorCausesBlocking) { suddenInvocationResult = ObstructionFreedomViolationInvocationResult(lazyMessage()) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt index 026d417dc..8a98b91a6 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingCTestConfiguration.kt @@ -44,7 +44,10 @@ class ModelCheckingCTestConfiguration(testClass: Class<*>, iterations: Int, thre eliminateLocalObjects = eliminateLocalObjects, customScenarios = customScenarios ) { - override fun createStrategy(testClass: Class<*>, scenario: ExecutionScenario, validationFunction: Actor?, - stateRepresentationMethod: Method?, verifier: Verifier): Strategy - = ModelCheckingStrategy(this, testClass, scenario, validationFunction, stateRepresentationMethod, verifier) + override fun createStrategy( + testClass: Class<*>, + scenario: ExecutionScenario, + validationFunction: Actor?, + stateRepresentationMethod: Method?, + ): Strategy = ModelCheckingStrategy(this, testClass, scenario, validationFunction, stateRepresentationMethod) } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt index 8201e56ab..25c2a18ea 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt @@ -34,17 +34,12 @@ import kotlin.random.* * than the number of all possible interleavings on the current depth level. */ internal class ModelCheckingStrategy( - testCfg: ModelCheckingCTestConfiguration, - testClass: Class<*>, - scenario: ExecutionScenario, - validationFunction: Actor?, - stateRepresentation: Method?, - verifier: Verifier -) : ManagedStrategy(testClass, scenario, verifier, validationFunction, stateRepresentation, testCfg) { - // The number of invocations that the strategy is eligible to use to search for an incorrect execution. - private val maxInvocations = testCfg.invocationsPerIteration - // The number of already used invocations. - private var usedInvocations = 0 + testCfg: ModelCheckingCTestConfiguration, + testClass: Class<*>, + scenario: ExecutionScenario, + validationFunction: Actor?, + stateRepresentation: Method?, +) : ManagedStrategy(testClass, scenario, validationFunction, stateRepresentation, testCfg) { // The maximum number of thread switch choices that strategy should perform // (increases when all the interleavings with the current depth are studied). private var maxNumberOfSwitches = 0 @@ -55,23 +50,22 @@ internal class ModelCheckingStrategy( // The interleaving that will be studied on the next invocation. private lateinit var currentInterleaving: Interleaving - override fun runImpl(): LincheckFailure? { - currentInterleaving = root.nextInterleaving() ?: return null - while (usedInvocations < maxInvocations) { - // run invocation and check its results - val invocationResult = runInvocation() - if (suddenInvocationResult is SpinCycleFoundAndReplayRequired) { - // Restart the current interleaving with - // the collected knowledge about the detected spin loop. + override fun nextInvocation(): Boolean { + currentInterleaving = root.nextInterleaving() + ?: return false + return true + } + + override fun initializeInvocation() { + super.initializeInvocation() + currentInterleaving.initialize() + } + + override fun runInvocation(): InvocationResult { + return super.runInvocation().also { + if (it is SpinCycleFoundAndReplayRequired) currentInterleaving.rollbackAfterSpinCycleFound() - continue - } - usedInvocations++ - checkResult(invocationResult)?.let { return it } - // get new unexplored interleaving - currentInterleaving = root.nextInterleaving() ?: break } - return null } override fun onNewSwitch(iThread: Int, mustSwitch: Boolean) { @@ -92,11 +86,6 @@ internal class ModelCheckingStrategy( return currentInterleaving.isSwitchPosition() } - override fun initializeInvocation() { - currentInterleaving.initialize() - super.initializeInvocation() - } - override fun beforePart(part: ExecutionPart) { super.beforePart(part) val nextThread = when (part) { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt index aad5a9895..c7269d050 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressCTestConfiguration.kt @@ -36,9 +36,12 @@ class StressCTestConfiguration( timeoutMs = timeoutMs, customScenarios = customScenarios ) { - override fun createStrategy(testClass: Class<*>, scenario: ExecutionScenario, validationFunction: Actor?, - stateRepresentationMethod: Method?, verifier: Verifier) = - StressStrategy(this, testClass, scenario, validationFunction, stateRepresentationMethod, verifier) + override fun createStrategy( + testClass: Class<*>, + scenario: ExecutionScenario, + validationFunction: Actor?, + stateRepresentationMethod: Method? + ) = StressStrategy(this, testClass, scenario, validationFunction, stateRepresentationMethod) companion object { const val DEFAULT_INVOCATIONS = 10000 diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt index d3e69c2fa..cbc405a55 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.kt @@ -22,9 +22,7 @@ class StressStrategy( scenario: ExecutionScenario, validationFunction: Actor?, stateRepresentationFunction: Method?, - private val verifier: Verifier ) : Strategy(scenario) { - private val invocations = testCfg.invocationsPerIteration private val runner: Runner init { @@ -44,19 +42,11 @@ class StressStrategy( } } - override fun run(): LincheckFailure? { - runner.use { - // Run invocations - for (invocation in 0 until invocations) { - when (val ir = runner.run()) { - is CompletedInvocationResult -> { - if (!verifier.verifyResults(scenario, ir.results)) - return IncorrectResultsFailure(scenario, ir.results) - } - else -> return ir.toLincheckFailure(scenario) - } - } - return null - } + override fun runInvocation(): InvocationResult { + return runner.run() + } + + override fun close() { + runner.close() } } \ No newline at end of file diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/ParallelThreadsRunnerExceptionTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/ParallelThreadsRunnerExceptionTest.kt index f8a6e00b4..cc0d54558 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/ParallelThreadsRunnerExceptionTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/ParallelThreadsRunnerExceptionTest.kt @@ -181,5 +181,5 @@ class ParallelThreadExecutionExceptionsTest { } fun mockStrategy(scenario: ExecutionScenario) = object : Strategy(scenario) { - override fun run(): LincheckFailure? = error("Not yet implemented") + override fun runInvocation(): InvocationResult = error("Not yet implemented") } \ No newline at end of file diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/TestThreadExecutionHelperTest.java b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/TestThreadExecutionHelperTest.java index 948b150ee..8f79613a0 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/TestThreadExecutionHelperTest.java +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/TestThreadExecutionHelperTest.java @@ -29,7 +29,7 @@ public void setUp() { ExecutionScenario scenario = new ExecutionScenario(emptyList(), emptyList(), emptyList(), null); Strategy strategy = new Strategy(scenario) { @Override - public LincheckFailure run() { + public InvocationResult runInvocation() { throw new UnsupportedOperationException(); } };