CTest} annotations on the specified test class.
- *
- * @throws AssertionError if algorithm or data structure is not correct.
- */
- public static void check(Class> testClass) {
- check(testClass, null);
- }
-
- /**
- * Runs concurrent test on specified class with the specified by options environment.
- *
- * NOTE: this method ignores {@code @CTest} annotations on the test class.
- *
- * @throws AssertionError if algorithm or data structure is not correct.
- */
- public static void check(Class> testClass, Options options) {
- new LinChecker(testClass, options).check();
- }
-
- /**
- * @throws AssertionError if algorithm or data structure is not correct
- */
- private void check() throws AssertionError {
- if (testConfigurations.isEmpty()) {
- throw new IllegalStateException("No Lin-Check test configuration to run");
- }
- testConfigurations.forEach(testConfiguration -> {
- try {
- checkImpl(testConfiguration);
- } catch (RuntimeException | AssertionError e) {
- throw e;
- } catch (Exception e) {
- throw new IllegalStateException(e);
- }
- });
- }
-
- private void checkImpl(CTestConfiguration testCfg) throws AssertionError, Exception {
- ExecutionGenerator exGen = createExecutionGenerator(testCfg.generatorClass, testCfg);
- // Run iterations
- for (int iteration = 1; iteration <= testCfg.iterations; iteration++) {
- ExecutionScenario scenario = exGen.nextExecution();
- // Check correctness of the generated scenario
- reporter.logIteration(iteration, testCfg.iterations, scenario);
- try {
- runScenario(scenario, testCfg);
- } catch (AssertionError e) {
- if (!testCfg.minimizeFailedScenario) throw e;
- minimizeScenario(scenario, testCfg, e);
- }
- }
- }
-
- // 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 void minimizeScenario(ExecutionScenario scenario, CTestConfiguration testCfg, AssertionError currentError) throws AssertionError, Exception {
- reporter.logScenarioMinimization(scenario);
- for (int i = 0; i < scenario.parallelExecution.size(); i++) {
- for (int j = 0; j < scenario.parallelExecution.get(i).size(); j++) {
- ExecutionScenario newScenario = copyScenario(scenario);
- newScenario.parallelExecution.get(i).remove(j);
- if (newScenario.parallelExecution.get(i).isEmpty())
- newScenario.parallelExecution.remove(i); // remove empty thread
- minimizeNewScenarioAttempt(newScenario, testCfg);
- }
- }
- for (int i = 0; i < scenario.initExecution.size(); i++) {
- ExecutionScenario newScenario = copyScenario(scenario);
- newScenario.initExecution.remove(i);
- minimizeNewScenarioAttempt(newScenario, testCfg);
- }
- for (int i = 0; i < scenario.postExecution.size(); i++) {
- ExecutionScenario newScenario = copyScenario(scenario);
- newScenario.postExecution.remove(i);
- minimizeNewScenarioAttempt(newScenario, testCfg);
- }
- throw currentError;
- }
-
- private void minimizeNewScenarioAttempt(ExecutionScenario newScenario, CTestConfiguration testCfg) throws AssertionError, Exception {
- try {
- runScenario(newScenario, testCfg);
- } catch (IllegalArgumentException e) {
- // Ignore incorrect scenarios
- } catch (AssertionError e) {
- // Scenario has been minimized! Try to minimize more!
- minimizeScenario(newScenario, testCfg, e);
- throw new IllegalStateException("Never reaches, the previous `minimizeScenario` call always throw an exception");
- }
- }
-
- private ExecutionScenario copyScenario(ExecutionScenario scenario) {
- List initExecution = new ArrayList<>(scenario.initExecution);
- List> parallelExecution = new ArrayList<>();
- for (int i = 0; i < scenario.parallelExecution.size(); i++) {
- parallelExecution.add(new ArrayList<>(scenario.parallelExecution.get(i)));
- }
- List postExecution = new ArrayList<>(scenario.postExecution);
- return new ExecutionScenario(initExecution, parallelExecution, postExecution);
- }
-
- private void runScenario(ExecutionScenario scenario, CTestConfiguration testCfg) throws AssertionError, Exception {
- validateScenario(testCfg, scenario);
- Verifier verifier = createVerifier(testCfg.verifierClass, scenario, testCfg.sequentialSpecification);
- if (testCfg.requireStateEquivalenceImplCheck) verifier.checkStateEquivalenceImplementation();
- Strategy strategy = Strategy.createStrategy(testCfg, testClass, scenario, verifier, reporter);
- strategy.run();
- }
-
- private void validateScenario(CTestConfiguration testCfg, ExecutionScenario scenario) {
- if (scenario.hasSuspendableActors()) {
- if (scenario.initExecution.stream().anyMatch(Actor::isSuspendable))
- throw new IllegalArgumentException("Generated execution scenario for the test class with suspendable methods contains suspendable actors in initial part");
- if (scenario.parallelExecution.stream().anyMatch(actors -> actors.stream().anyMatch(Actor::isSuspendable)) && scenario.postExecution.size() > 0)
- throw new IllegalArgumentException("Generated execution scenario for the test class with suspendable methods has non-empty post part");
- }
- }
-
- private Verifier createVerifier(Class extends Verifier> verifierClass, ExecutionScenario scenario,
- Class> sequentialSpecification) throws Exception {
- return verifierClass.getConstructor(ExecutionScenario.class, Class.class).newInstance(scenario, sequentialSpecification);
- }
-
- private ExecutionGenerator createExecutionGenerator(Class extends ExecutionGenerator> generatorClass,
- CTestConfiguration testConfiguration) throws Exception {
- return generatorClass.getConstructor(CTestConfiguration.class, CTestStructure.class).newInstance(testConfiguration, testStructure);
- }
-
- private LoggingLevel getLogLevelFromAnnotation() {
- LogLevel logLevelAnn = testClass.getAnnotation(LogLevel.class);
- if (logLevelAnn == null)
- return DEFAULT_LOG_LEVEL;
- return logLevelAnn.value();
- }
-}
\ No newline at end of file
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.kt b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.kt
new file mode 100644
index 000000000..daba1ec0e
--- /dev/null
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/LinChecker.kt
@@ -0,0 +1,197 @@
+/*
+ * #%L
+ * Lincheck
+ * %%
+ * Copyright (C) 2015 - 2018 Devexperts, LLC
+ * Copyright (C) 2019 JetBrains s.r.o.
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+package org.jetbrains.kotlinx.lincheck
+
+import org.jetbrains.kotlinx.lincheck.annotations.*
+import org.jetbrains.kotlinx.lincheck.execution.*
+import org.jetbrains.kotlinx.lincheck.runner.*
+import org.jetbrains.kotlinx.lincheck.strategy.*
+import org.jetbrains.kotlinx.lincheck.verifier.*
+import java.util.*
+
+/**
+ * This class runs concurrent tests.
+ * See [.check] and [.check] methods for details.
+ */
+private class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) {
+ private val testConfigurations: List
+ private val testStructure: CTestStructure
+ private val reporter: Reporter
+ /**
+ * @throws AssertionError if algorithm or data structure is not correct
+ */
+ @Throws(AssertionError::class)
+ fun check() {
+ val resultsByConfiguration = checkDetailed()
+ for (results in resultsByConfiguration.values) {
+ val lastResult = results[results.size - 1]
+ if (lastResult.isError) throw AssertionError(reporter.generateReport(lastResult))
+ }
+ }
+
+ init {
+ testStructure = CTestStructure.getFromTestClass(testClass)
+ val logLevel: LoggingLevel
+ if (options != null) {
+ logLevel = options.logLevel
+ testConfigurations = listOf(options.createTestConfigurations(testClass))
+ } else {
+ logLevel = logLevelFromAnnotation
+ testConfigurations = CTestConfiguration.createFromTestClass(testClass)
+ }
+ reporter = Reporter(logLevel)
+ }
+
+ /**
+ * @return TestReport with information about concurrent test run.
+ */
+ @Throws(AssertionError::class)
+ fun checkDetailed(): Map> {
+ check(!testConfigurations!!.isEmpty()) { "No Lin-Check test configuration to run" }
+ val resultsByConfiguration: MutableMap> = HashMap()
+ for (testConfiguration in testConfigurations) {
+ try {
+ val results: List = checkDetailedImpl(testConfiguration)
+ resultsByConfiguration[testConfiguration] = results
+ if (results[results.size - 1].isError) return resultsByConfiguration
+ } catch (e: Exception) { // an Exception in LinCheck
+ throw IllegalStateException(e)
+ }
+ }
+ return resultsByConfiguration
+ }
+
+ /**
+ * Generates the specified in the configuration number of scenarios, invokes them,
+ * and returns the corresponding invocation results.
+ */
+ @Throws(Exception::class)
+ private fun generateScenariosAndInvoke(testCfg: CTestConfiguration): Map> {
+ val exGen = createExecutionGenerator(testCfg.generatorClass, testCfg)
+ val allResults: MutableMap> = HashMap()
+ // Run iterations
+ for (iteration in 1..testCfg.iterations) {
+ val scenario = exGen.nextExecution()
+ validateScenario(scenario)
+ val strategy = Strategy.createStrategy(testCfg, testClass, scenario, reporter)
+ val scenarioResults = strategy.run()
+ allResults[scenario] = scenarioResults
+ // Check whether an error is already detected
+ if (scenarioResults.stream().anyMatch(InvocationResult::isError)) break
+ }
+ return allResults
+ }
+
+ // 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.
+ @Throws(AssertionError::class, Exception::class)
+ private fun minimizeScenario(scenario: ExecutionScenario, testCfg: CTestConfiguration, currentReport: IterationResult): IterationResult {
+ reporter.logScenarioMinimization(scenario)
+ for (i in scenario.parallelExecution.indices) {
+ for (j in scenario.parallelExecution[i].indices) {
+ val newScenario = copyScenario(scenario)
+ newScenario.parallelExecution[i].removeAt(j)
+ if (newScenario.parallelExecution[i].isEmpty()) newScenario.parallelExecution.removeAt(i) // remove empty thread
+ val result = minimizeNewScenarioAttempt(newScenario, testCfg)
+ if (result.isError) return result
+ }
+ }
+ for (i in scenario.initExecution.indices) {
+ val newScenario = copyScenario(scenario)
+ newScenario.initExecution.removeAt(i)
+ val result = minimizeNewScenarioAttempt(newScenario, testCfg)
+ if (result.isError) return result
+ }
+ for (i in scenario.postExecution.indices) {
+ val newScenario = copyScenario(scenario)
+ newScenario.postExecution.removeAt(i)
+ val result = minimizeNewScenarioAttempt(newScenario, testCfg)
+ if (result.isError) return result
+ }
+ return currentReport
+ }
+
+ @Throws(AssertionError::class, Exception::class)
+ private fun minimizeNewScenarioAttempt(newScenario: ExecutionScenario, testCfg: CTestConfiguration): IterationResult {
+ try {
+ val result: IterationResult = runScenario(newScenario, testCfg)
+ if (result.isError) return minimizeScenario(newScenario, testCfg, result)
+ } catch (e: IllegalArgumentException) { // Ignore incorrect scenarios
+ }
+ return SuccessIterationResult(newScenario, testCfg.iterations)
+ }
+
+ private fun copyScenario(scenario: ExecutionScenario): ExecutionScenario {
+ val initExecution: List = ArrayList(scenario.initExecution)
+ val parallelExecution: MutableList> = ArrayList()
+ for (i in scenario.parallelExecution.indices) {
+ parallelExecution.add(ArrayList(scenario.parallelExecution[i]))
+ }
+ val postExecution: List = ArrayList(scenario.postExecution)
+ return ExecutionScenario(initExecution, parallelExecution, postExecution)
+ }
+
+ private fun validateScenario(scenario: ExecutionScenario) {
+ if (scenario.hasSuspendableActors()) {
+ require(!scenario.initExecution.stream().anyMatch(Actor::isSuspendable)) { "Generated execution scenario for the test class with suspendable methods contains suspendable actors in initial part" }
+ require(!(scenario.parallelExecution.stream().anyMatch { actors: List -> actors.stream().anyMatch(Actor::isSuspendable) } && scenario.postExecution.size > 0)) { "Generated execution scenario for the test class with suspendable methods has non-empty post part" }
+ }
+ }
+
+ private fun createVerifier(verifierClass: Class, scenario: ExecutionScenario, sequentialSpecification: Class<*>) =
+ verifierClass.getConstructor(ExecutionScenario::class.java, Class::class.java).newInstance(scenario, sequentialSpecification)
+
+ private fun createExecutionGenerator(generatorClass: Class, testConfiguration: CTestConfiguration) =
+ generatorClass.getConstructor(CTestConfiguration::class.java, CTestStructure::class.java).newInstance(testConfiguration, testStructure)
+
+ private val logLevelFromAnnotation = testClass.getAnnotation(LogLevel::class.java)?.value ?: DEFAULT_LOG_LEVEL
+
+ // We use this companion object for backwards compatibility.
+ companion object {
+ /**
+ * Runs the specified concurrent tests. If [options] is null, the provided on
+ * the testing class `@...CTest` annotations are used to specify the test parameters.
+ *
+ * @throws AssertionError if any of the tests fails.
+ */
+ @JvmStatic
+ @JvmOverloads
+ fun ?> check(testClass: Class<*>, options: O? = null) {
+ LinChecker(testClass, options).check()
+ }
+ }
+}
+
+
+/**
+ * This is a short-cut for the following code:
+ * ```
+ * val options = ...
+ * LinChecker.check(testClass, options)
+ * ```
+ */
+fun > O.check(testClass: Class<*>) = LinChecker.check(testClass, this)
\ No newline at end of file
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Options.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Options.java
index 0a5faafb5..e1b7e34e7 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Options.java
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Options.java
@@ -164,5 +164,4 @@ public OPT sequentialSpecification(Class> clazz) {
this.sequentialSpecification = clazz;
return (OPT) this;
}
-
}
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 f6a6b4f5f..ac93f8fbb 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/Reporter.kt
@@ -3,6 +3,7 @@
* Lincheck
* %%
* Copyright (C) 2015 - 2018 Devexperts, LLC
+ * Copyright (C) 2019 JetBrains s.r.o.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
@@ -19,12 +20,11 @@
* .
* #L%
*/
-
package org.jetbrains.kotlinx.lincheck
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario
-import java.io.PrintStream
+import org.jetbrains.kotlinx.lincheck.execution.*
+import org.jetbrains.kotlinx.lincheck.runner.*
+import java.io.*
class Reporter @JvmOverloads constructor(val logLevel: LoggingLevel, val out: PrintStream = System.out) {
fun logIteration(iteration: Int, maxIterations: Int, scenario: ExecutionScenario) = synchronized(this) {
@@ -43,6 +43,36 @@ class Reporter @JvmOverloads constructor(val logLevel: LoggingLevel, val out: Pr
}
}
+ fun generateReport(result: IterationResult): String = StringBuilder().apply {
+ when (result) {
+ is IncorrectIterationResult -> {
+ appendln("Incorrect interleaving found:")
+ appendIncorrectResults(result.scenario, result.results)
+ }
+ is UnexpectedExceptionIterationResult -> {
+ appendln("Illegal exception was thrown:")
+ appendExecutionScenario(result.scenario)
+ appendln()
+ appendExceptionStackTrace(result.exception)
+ }
+ is DeadlockIterationResult -> {
+ appendln("The execution has hung, see the thread dump:")
+ for ((t, stackTrace) in result.threadDump) {
+ t as ParallelThreadsRunner.TestThread
+ appendln("Thread-${t.iThread}:")
+ for (ste in stackTrace) {
+ if (logLevel > LoggingLevel.DEBUG && ste.className.startsWith("org.jetbrains.kotlinx.lincheck.runner.")) break
+ appendln("\t$ste")
+ }
+ appendln()
+ }
+ appendExecutionScenario(result.scenario)
+ }
+ is SuccessIterationResult -> append("No error was found.")
+ else -> throw IllegalStateException("Unsupported IterationResult to be reported.")
+ }
+ }.toString()
+
inline fun log(logLevel: LoggingLevel, crossinline msg: () -> String) {
if (this.logLevel > logLevel) return
out.println(msg())
@@ -116,7 +146,7 @@ private fun StringBuilder.appendExecutionScenario(scenario: ExecutionScenario) {
}
}
-fun StringBuilder.appendIncorrectResults(scenario: ExecutionScenario, results: ExecutionResult) {
+private fun StringBuilder.appendIncorrectResults(scenario: ExecutionScenario, results: ExecutionResult) {
appendln("= Invalid execution results: =")
if (scenario.initExecution.isNotEmpty()) {
appendln("Init part:")
@@ -130,4 +160,17 @@ fun StringBuilder.appendIncorrectResults(scenario: ExecutionScenario, results: E
appendln("Post part:")
append(uniteActorsAndResults(scenario.postExecution, results.postResults))
}
+}
+
+private fun StringBuilder.appendExceptionStackTrace(e: Throwable) {
+ val sw = StringWriter()
+ val pw = PrintWriter(sw)
+ val reducedStackTrace = mutableListOf()
+ for (ste in e.stackTrace) {
+ if (ste.className.startsWith("org.jetbrains.kotlinx.lincheck.runner.")) break
+ reducedStackTrace.add(ste)
+ }
+ e.stackTrace = reducedStackTrace.toTypedArray()
+ e.printStackTrace(pw)
+ append(sw.toString())
}
\ No newline at end of file
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.java
deleted file mode 100644
index 0a1ff8718..000000000
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.jetbrains.kotlinx.lincheck.execution;
-
-/*
- * #%L
- * Lincheck
- * %%
- * Copyright (C) 2015 - 2018 Devexperts, LLC
- * %%
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Lesser Public License for more details.
- *
- * You should have received a copy of the GNU General Lesser Public
- * License along with this program. If not, see
- * .
- * #L%
- */
-
-import org.jetbrains.kotlinx.lincheck.Result;
-
-import java.util.List;
-import java.util.Objects;
-
-/**
- * This class represents a result corresponding to
- * the specified {@link ExecutionScenario scenario} execution.
- *
- * All the result parts should have the same dimensions as the scenario.
- */
-public class ExecutionResult {
- /**
- * Results of the initial sequential part of the execution.
- * @see ExecutionScenario#initExecution
- */
- public final List initResults;
- /**
- * Results of the parallel part of the execution.
- * @see ExecutionScenario#parallelExecution
- */
- public final List> parallelResults;
- /**
- * Results of the last sequential part of the execution.
- * @see ExecutionScenario#postExecution
- */
- public final List postResults;
-
- public ExecutionResult(List initResults, List> parallelResults, List postResults) {
- this.initResults = initResults;
- this.parallelResults = parallelResults;
- this.postResults = postResults;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- ExecutionResult that = (ExecutionResult) o;
- return Objects.equals(initResults, that.initResults) &&
- Objects.equals(parallelResults, that.parallelResults) &&
- Objects.equals(postResults, that.postResults);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(initResults, parallelResults, postResults);
- }
-}
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt
new file mode 100644
index 000000000..3a1042634
--- /dev/null
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionResult.kt
@@ -0,0 +1,27 @@
+package org.jetbrains.kotlinx.lincheck.execution
+
+import org.jetbrains.kotlinx.lincheck.*
+
+/**
+ * This class represents a result corresponding to
+ * the specified [scenario][ExecutionScenario] execution.
+ *
+ * All the result parts should have the same dimensions as the scenario.
+ */
+data class ExecutionResult(
+ /**
+ * Results of the initial sequential part of the execution.
+ * @see ExecutionScenario.initExecution
+ */
+ val initResults: List,
+ /**
+ * Results of the parallel part of the execution.
+ * @see ExecutionScenario.parallelExecution
+ */
+ val parallelResults: List>,
+ /**
+ * Results of the last sequential part of the execution.
+ * @see ExecutionScenario.postExecution
+ */
+ val postResults: List
+)
\ No newline at end of file
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.java
index a52874a11..0e21fe7a9 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.java
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/execution/ExecutionScenario.java
@@ -1,5 +1,3 @@
-package org.jetbrains.kotlinx.lincheck.execution;
-
/*
* #%L
* Lincheck
@@ -10,17 +8,18 @@
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
+package org.jetbrains.kotlinx.lincheck.execution;
import org.jetbrains.kotlinx.lincheck.Actor;
import org.jetbrains.kotlinx.lincheck.strategy.Strategy;
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt
new file mode 100644
index 000000000..6549a2d52
--- /dev/null
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/InvocationResult.kt
@@ -0,0 +1,66 @@
+/*-
+ * #%L
+ * Lincheck
+ * %%
+ * Copyright (C) 2019 JetBrains s.r.o.
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+package org.jetbrains.kotlinx.lincheck.runner
+
+import org.jetbrains.kotlinx.lincheck.execution.*
+
+/**
+ * Represents results for invocations, see [Runner.run].
+ * Marked as error (see [isError]) if this invocation has been failed.
+ */
+internal sealed class InvocationResult {
+ abstract val isError: Boolean
+}
+
+/**
+ * The invocation completed successfully with correct results.
+ */
+internal object SuccessfulInvocationResult: InvocationResult() {
+ override val isError get() = false
+}
+
+/**
+ * The invocation completed successfully, but the [results] are incorrect.
+ */
+internal data class IncorrectInvocationResult(
+ val results: ExecutionResult
+) : InvocationResult() {
+ override val isError get() = true
+}
+
+/**
+ * Indicates that the invocation has get into deadlock or livelock.
+ */
+internal data class DeadlockInvocationResult(
+ val threadDump: Map>
+) : InvocationResult() {
+ override val isError get() = true
+}
+
+/**
+ * The invocation has finished with an unexpected exception.
+ */
+internal class UnexpectedExceptionInvocationResult(
+ val exception: Throwable
+) : InvocationResult() {
+ override val isError get() = true
+}
\ No newline at end of file
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/IterationResult.kt b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/IterationResult.kt
new file mode 100644
index 000000000..de0623388
--- /dev/null
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/IterationResult.kt
@@ -0,0 +1,22 @@
+package org.jetbrains.kotlinx.lincheck.runner
+
+import org.jetbrains.kotlinx.lincheck.execution.*
+
+sealed class IterationResult {
+ abstract val isError: Boolean
+}
+
+internal object SuccessfulIterationResult : IterationResult() {
+ override val isError: Boolean get() = false
+}
+
+internal class IncorrectIterationResult(val scenario: ExecutionResult, val result: ExecutionResult) : IterationResult() {
+ override val isError: Boolean get() = true
+}
+
+internal class FailedIterationResult(val failedInvocationResult: InvocationResult) : IterationResult() {
+ init {
+ require(failedInvocationResult.isError) { "The provided invocation result should be a failure" }
+ }
+ override val isError: Boolean get() = true
+}
\ No newline at end of file
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt
index 03629b644..c02ffc163 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt
@@ -40,7 +40,7 @@ private typealias SuspensionPointResultWithContinuation = AtomicReference,
@@ -48,6 +48,7 @@ open class ParallelThreadsRunner(
) : Runner(scenario, strategy, testClass) {
private lateinit var testInstance: Any
private val executor = newFixedThreadPool(scenario.threads, ParallelThreadsRunner::TestThread)
+ private val completedOrSuspendedThreads = AtomicInteger(0)
private val completions = List(scenario.threads) { threadId ->
List(scenario.parallelExecution[threadId].size) { Completion(threadId) }
@@ -163,41 +164,34 @@ open class ParallelThreadsRunner(
return suspensionPointResults[threadId]
}
- override fun run(): ExecutionResult? {
+ override fun run(): InvocationResult {
reset()
- val initResults = scenario.initExecution.map { initActor -> executeActor(testInstance, initActor) }
- val parallelResults = testThreadExecutions.map { executor.submit(it) }.map { future ->
- try {
- future.get(10, TimeUnit.SECONDS).toList()
- } catch (e: TimeoutException) {
- val stackTraces = Thread.getAllStackTraces().filter { (t, _) -> t is TestThread }
- val msgBuilder = StringBuilder()
- msgBuilder.appendln("The execution has hung, see the thread dump:")
- for ((t, stackTrace) in stackTraces) {
- t as TestThread
- msgBuilder.appendln("Thread-${t.iThread}:")
- for (ste in stackTrace) {
- if (ste.className.startsWith("org.jetbrains.kotlinx.lincheck.runner.")) break
- msgBuilder.appendln("\t$ste")
- }
- msgBuilder.appendln()
+ try {
+ val initResults = scenario.initExecution.map { initActor -> executeActor(testInstance, initActor) }
+ val parallelResults = testThreadExecutions.map { executor.submit(it) }.map { future ->
+ try {
+ future.get(10, TimeUnit.SECONDS).toList()
+ } catch (e: TimeoutException) {
+ val threadDump = Thread.getAllStackTraces().filter { (t, _) -> t is TestThread }
+ threadDump.keys.forEach { it.stop() }
+ return DeadlockInvocationResult(threadDump)
}
- Thread.getAllStackTraces().map { it.key }.filterIsInstance().forEach { it.stop() }
- throw AssertionError(msgBuilder.toString())
}
- }
- val dummyCompletion = Continuation(EmptyCoroutineContext) {}
- var postPartSuspended = false
- val postResults = scenario.postExecution.map { postActor ->
- // no actors are executed after suspension of a post part
- if (postPartSuspended) {
- NoResult
- } else {
- // post part may contain suspendable actors if there aren't any in the parallel part, invoke with dummy continuation
- executeActor(testInstance, postActor, dummyCompletion).also { postPartSuspended = it.wasSuspended }
+ val dummyCompletion = Continuation(EmptyCoroutineContext) {}
+ var postPartSuspended = false
+ val postResults = scenario.postExecution.map { postActor ->
+ // no actors are executed after suspension of a post part
+ if (postPartSuspended) {
+ NoResult
+ } else {
+ // post part may contain suspendable actors if there aren't any in the parallel part, invoke with dummy continuation
+ executeActor(testInstance, postActor, dummyCompletion).also { postPartSuspended = it.wasSuspended }
+ }
}
+ return CompletedInvocationResult(ExecutionResult(initResults, parallelResults, postResults))
+ } catch (e: Throwable) {
+ return UnexpectedExceptionInvocationResult(e)
}
- return ExecutionResult(initResults, parallelResults, postResults)
}
override fun onStart(iThread: Int) {
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/Runner.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/Runner.java
index b2a8686ea..1e65bc18f 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/Runner.java
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/runner/Runner.java
@@ -1,34 +1,32 @@
-package org.jetbrains.kotlinx.lincheck.runner;
-
/*
* #%L
* Lincheck
* %%
* Copyright (C) 2015 - 2018 Devexperts, LLC
+ * Copyright (C) 2019 JetBrains s.r.o.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
-import org.jetbrains.kotlinx.lincheck.ExecutionClassLoader;
-import org.jetbrains.kotlinx.lincheck.TransformationClassLoader;
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult;
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario;
-import org.jetbrains.kotlinx.lincheck.strategy.Strategy;
-import org.objectweb.asm.ClassVisitor;
-import java.util.concurrent.atomic.AtomicInteger;
+package org.jetbrains.kotlinx.lincheck.runner;
+
+import org.jetbrains.kotlinx.lincheck.*;
+import org.jetbrains.kotlinx.lincheck.execution.*;
+import org.jetbrains.kotlinx.lincheck.strategy.*;
+import org.objectweb.asm.*;
/**
* Runner determines how to run your concurrent test. In order to support techniques
@@ -40,7 +38,6 @@ public abstract class Runner {
protected final ExecutionScenario scenario;
protected final Class> testClass;
public final ExecutionClassLoader classLoader;
- protected final AtomicInteger completedOrSuspendedThreads = new AtomicInteger(0);
protected Runner(ExecutionScenario scenario, Strategy strategy, Class> testClass) {
this.scenario = scenario;
@@ -79,10 +76,9 @@ public boolean needsTransformation() {
}
/**
- * Runs next invocation
- * @return the obtained results
+ * Runs the next invocation.
*/
- public abstract ExecutionResult run() throws InterruptedException;
+ public abstract InvocationResult run() throws InterruptedException;
/**
* This method is invoked by every test thread as the first operation.
@@ -101,11 +97,4 @@ public void onFinish(int iThread) {}
* Closes used for this runner resources.
*/
public void close() {}
-
- /**
- * @return whether all scenario threads are completed or suspended
- */
- public boolean isParallelExecutionCompleted() {
- return completedOrSuspendedThreads.get() == scenario.getThreads();
- }
}
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java
index bcb771c9c..137c4c52a 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/ManagedStrategy.java
@@ -23,13 +23,14 @@
*/
import org.jetbrains.kotlinx.lincheck.Reporter;
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult;
import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario;
+import org.jetbrains.kotlinx.lincheck.runner.InvocationResult;
import org.jetbrains.kotlinx.lincheck.runner.ParallelThreadsRunner;
import org.jetbrains.kotlinx.lincheck.runner.Runner;
-import org.jetbrains.kotlinx.lincheck.verifier.Verifier;
import org.objectweb.asm.ClassVisitor;
+import java.util.*;
+
/**
* This is an abstract class for all managed strategies.
* This abstraction helps to choose a proper {@link Runner},
@@ -45,8 +46,8 @@ public abstract class ManagedStrategy extends Strategy {
private final Runner runner;
private ManagedStrategyTransformer transformer;
- protected ManagedStrategy(Class> testClass, ExecutionScenario scenario, Verifier verifier, Reporter reporter) {
- super(scenario, verifier, reporter);
+ protected ManagedStrategy(Class> testClass, ExecutionScenario scenario, Reporter reporter) {
+ super(scenario, reporter);
nThreads = scenario.parallelExecution.size();
runner = new ParallelThreadsRunner(scenario, this, testClass, null) {
@Override
@@ -75,9 +76,9 @@ public boolean needsTransformation() {
}
@Override
- public final void run() throws Exception {
+ public final List run() throws Exception {
try {
- runImpl();
+ return runImpl();
} finally {
runner.close();
}
@@ -88,7 +89,7 @@ public final void run() throws Exception {
*
* @return invocation results for each executed actor.
*/
- protected final ExecutionResult runInvocation() throws InterruptedException {
+ protected final InvocationResult runInvocation() throws InterruptedException {
return runner.run();
}
@@ -103,10 +104,8 @@ protected final StackTraceElement getLocationDescription(int codeLocation) {
/**
* This method implements the strategy logic
- *
- * @throws Exception an occurred exception (at least by {@link Verifier}) during the testing
*/
- protected abstract void runImpl() throws Exception;
+ protected abstract List runImpl() throws Exception;
// == LISTENING EVENTS ==
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.java
index cee57d59e..7a43aec6c 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.java
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/Strategy.java
@@ -22,18 +22,14 @@
* #L%
*/
-import org.jetbrains.kotlinx.lincheck.CTestConfiguration;
-import org.jetbrains.kotlinx.lincheck.Reporter;
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult;
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario;
-import org.jetbrains.kotlinx.lincheck.strategy.randomswitch.RandomSwitchCTestConfiguration;
-import org.jetbrains.kotlinx.lincheck.strategy.randomswitch.RandomSwitchStrategy;
-import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTestConfiguration;
-import org.jetbrains.kotlinx.lincheck.strategy.stress.StressStrategy;
-import org.jetbrains.kotlinx.lincheck.verifier.Verifier;
-import org.objectweb.asm.ClassVisitor;
+import org.jetbrains.kotlinx.lincheck.*;
+import org.jetbrains.kotlinx.lincheck.execution.*;
+import org.jetbrains.kotlinx.lincheck.runner.*;
+import org.jetbrains.kotlinx.lincheck.strategy.randomswitch.*;
+import org.jetbrains.kotlinx.lincheck.strategy.stress.*;
+import org.objectweb.asm.*;
-import static org.jetbrains.kotlinx.lincheck.ReporterKt.appendIncorrectResults;
+import java.util.*;
/**
* Implementation of this class describes how to run the generated execution.
@@ -45,22 +41,12 @@
public abstract class Strategy {
protected final ExecutionScenario scenario;
protected final Reporter reporter;
- private final Verifier verifier;
- protected Strategy(ExecutionScenario scenario, Verifier verifier, Reporter reporter) {
+ protected Strategy(ExecutionScenario scenario, Reporter reporter) {
this.scenario = scenario;
- this.verifier = verifier;
this.reporter = reporter;
}
- protected void verifyResults(ExecutionResult results) {
- if (!verifier.verifyResults(results)) {
- StringBuilder msgBuilder = new StringBuilder("Invalid interleaving found:\n");
- appendIncorrectResults(msgBuilder, scenario, results);
- throw new AssertionError(msgBuilder.toString());
- }
- }
-
public ClassVisitor createTransformer(ClassVisitor cv) {
throw new UnsupportedOperationException(getClass() + " runner does not transform classes");
}
@@ -73,17 +59,16 @@ public boolean needsTransformation() {
* Creates {@link Strategy} based on {@code testCfg} type.
*/
public static Strategy createStrategy(CTestConfiguration testCfg, Class> testClass,
- ExecutionScenario scenario, Verifier verifier, Reporter reporter)
- {
+ ExecutionScenario scenario, Reporter reporter) {
if (testCfg instanceof StressCTestConfiguration) {
- return new StressStrategy(testClass, scenario, verifier,
+ return new StressStrategy(testClass, scenario,
(StressCTestConfiguration) testCfg, reporter);
} else if (testCfg instanceof RandomSwitchCTestConfiguration) {
- return new RandomSwitchStrategy(testClass, scenario, verifier,
+ return new RandomSwitchStrategy(testClass, scenario,
(RandomSwitchCTestConfiguration) testCfg, reporter);
}
throw new IllegalArgumentException("Unknown strategy configuration type: " + testCfg.getClass());
}
- public abstract void run() throws Exception;
-}
+ public abstract List run() throws Exception;
+}
\ No newline at end of file
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java
index ca2aee2c2..8ddab75b0 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/randomswitch/RandomSwitchStrategy.java
@@ -22,10 +22,12 @@
* #L%
*/
-import org.jetbrains.kotlinx.lincheck.Reporter;
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario;
-import org.jetbrains.kotlinx.lincheck.strategy.ManagedStrategy;
-import org.jetbrains.kotlinx.lincheck.verifier.Verifier;
+import org.jetbrains.kotlinx.lincheck.*;
+import org.jetbrains.kotlinx.lincheck.execution.*;
+import org.jetbrains.kotlinx.lincheck.runner.*;
+import org.jetbrains.kotlinx.lincheck.strategy.*;
+
+import java.util.*;
/**
* This managed strategy switches current thread to a random one with the specified probability.
@@ -37,16 +39,19 @@ public class RandomSwitchStrategy extends ManagedStrategy {
private final int invocations;
public RandomSwitchStrategy(Class> testClass, ExecutionScenario scenario,
- Verifier verifier, RandomSwitchCTestConfiguration testCfg, Reporter reporter)
+ RandomSwitchCTestConfiguration testCfg, Reporter reporter)
{
- super(testClass, scenario, verifier, reporter);
+ super(testClass, scenario, reporter);
this.invocations = testCfg.invocationsPerIteration;
}
@Override
- protected void runImpl() throws Exception {
- for (int i = 0; i < invocations; i++)
- verifyResults(runInvocation());
+ protected List runImpl() throws Exception {
+ List results = new ArrayList<>();
+ for (int i = 1; i < invocations; i++) {
+ results.add(runInvocation());
+ }
+ return results;
}
@Override
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.java
index 52183b5c9..a83abcb2e 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.java
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/strategy/stress/StressStrategy.java
@@ -22,19 +22,13 @@
* #L%
*/
-import org.jetbrains.kotlinx.lincheck.Actor;
-import org.jetbrains.kotlinx.lincheck.Reporter;
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario;
-import org.jetbrains.kotlinx.lincheck.runner.ParallelThreadsRunner;
-import org.jetbrains.kotlinx.lincheck.runner.Runner;
-import org.jetbrains.kotlinx.lincheck.strategy.Strategy;
-import org.jetbrains.kotlinx.lincheck.verifier.Verifier;
+import org.jetbrains.kotlinx.lincheck.*;
+import org.jetbrains.kotlinx.lincheck.execution.*;
+import org.jetbrains.kotlinx.lincheck.runner.*;
+import org.jetbrains.kotlinx.lincheck.strategy.*;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.Phaser;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.*;
+import java.util.concurrent.atomic.*;
/**
* This strategy
@@ -50,8 +44,8 @@ public class StressStrategy extends Strategy {
private final AtomicInteger uninitializedThreads = new AtomicInteger(0); // for threads synchronization
public StressStrategy(Class> testClass, ExecutionScenario scenario,
- Verifier verifier, StressCTestConfiguration testCfg, Reporter reporter) {
- super(scenario, verifier, reporter);
+ StressCTestConfiguration testCfg, Reporter reporter) {
+ super(scenario, reporter);
this.invocations = testCfg.invocationsPerIteration;
// Create waits if needed
waits = testCfg.addWaits ? new ArrayList<>() : null;
@@ -61,7 +55,6 @@ public StressStrategy(Class> testClass, ExecutionScenario scenario,
}
}
// Create runner
- Phaser phaser = new Phaser(testCfg.threads);
runner = new ParallelThreadsRunner(scenario, this, testClass, waits) {
@Override
public void onStart(int iThread) {
@@ -73,10 +66,11 @@ public void onStart(int iThread) {
}
@Override
- public void run() throws InterruptedException {
+ public List run() throws InterruptedException {
try {
+ List results = new ArrayList<>();
// Run invocations
- for (int invocation = 0; invocation < invocations; invocation++) {
+ for (int invocation = 1; invocation <= invocations; invocation++) {
// Specify waits if needed
if (waits != null) {
int maxWait = (int) ((float) invocation * MAX_WAIT / invocations) + 1;
@@ -87,8 +81,11 @@ public void run() throws InterruptedException {
}
}
uninitializedThreads.set(scenario.parallelExecution.size()); // reinit synchronization
- verifyResults(runner.run());
+ InvocationResult r = runner.run();
+ results.add(r);
+ if (r.isError()) break;
}
+ return results;
} finally {
runner.close();
}
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt
index 9ca4bc42d..98ff146bf 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/AbstractLTSVerifier.kt
@@ -33,11 +33,11 @@ import org.jetbrains.kotlinx.lincheck.execution.*
* the next possible transitions using [VerifierContext.nextContext] function. This verifier
* uses depth-first search to find a proper path.
*/
-abstract class AbstractLTSVerifier(protected val scenario: ExecutionScenario, protected val sequentialSpecification: Class<*>) : CachedVerifier() {
+abstract class AbstractLTSVerifier(protected val sequentialSpecification: Class<*>) : CachedVerifier() {
abstract val lts: LTS
- abstract fun createInitialContext(results: ExecutionResult): VerifierContext
+ abstract fun createInitialContext(scenario: ExecutionScenario, results: ExecutionResult): VerifierContext
- override fun verifyResultsImpl(results: ExecutionResult) = createInitialContext(results).verify()
+ override fun verifyResultsImpl(scenario: ExecutionScenario, results: ExecutionResult) = createInitialContext(scenario, results).verify()
private fun VerifierContext.verify(): Boolean {
// Check if a possible path is found.
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java
index 990f62a8d..412fe387a 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/CachedVerifier.java
@@ -1,5 +1,3 @@
-package org.jetbrains.kotlinx.lincheck.verifier;
-
/*
* #%L
* Lincheck
@@ -10,22 +8,24 @@
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult;
+package org.jetbrains.kotlinx.lincheck.verifier;
+
+import org.jetbrains.kotlinx.lincheck.execution.*;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.*;
+import java.util.concurrent.*;
/**
* This verifier cached the already verified results in a hash table,
@@ -34,14 +34,13 @@
* phase significantly.
*/
public abstract class CachedVerifier implements Verifier {
- private final Set previousResults = new HashSet<>();
+ private final ConcurrentHashMap> previousResults = new ConcurrentHashMap<>();
@Override
- public final boolean verifyResults(ExecutionResult results) {
- if (!previousResults.add(results))
- return true;
- return verifyResultsImpl(results);
+ public final boolean verifyResults(ExecutionScenario scenario, ExecutionResult results) {
+ Map previousScenarioResults = previousResults.computeIfAbsent(scenario, s -> new HashMap<>());
+ return previousScenarioResults.computeIfAbsent(results, r -> verifyResultsImpl(scenario, r));
}
- public abstract boolean verifyResultsImpl(ExecutionResult results);
+ public abstract boolean verifyResultsImpl(ExecutionScenario scenario, ExecutionResult results);
}
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.java
index 578b56d6c..234da2f33 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.java
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/EpsilonVerifier.java
@@ -1,5 +1,3 @@
-package org.jetbrains.kotlinx.lincheck.verifier;
-
/*
* #%L
* Lincheck
@@ -10,30 +8,30 @@
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
+package org.jetbrains.kotlinx.lincheck.verifier;
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult;
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario;
+import org.jetbrains.kotlinx.lincheck.execution.*;
/**
* This verifier does nothing and could be used for performance benchmarking.
*/
public class EpsilonVerifier implements Verifier {
- public EpsilonVerifier(ExecutionScenario scenario, Class> sequentialSpecification) {}
+ public EpsilonVerifier(Class> sequentialSpecification) {}
@Override
- public boolean verifyResults(ExecutionResult results) {
+ public boolean verifyResults(ExecutionScenario scenario, ExecutionResult results) {
return true; // Always correct results :)
}
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt
index 4ce604c79..9478ec400 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt
@@ -82,6 +82,7 @@ class LTS(sequentialSpecification: Class<*>) {
/**
* Computes or gets the existing transition from the current state by the given [actor].
*/
+ @Synchronized
fun next(actor: Actor, expectedResult: Result, ticket: Int) =
if (ticket == NO_TICKET) nextByRequest(actor, expectedResult) else nextByFollowUp(actor, ticket, expectedResult)
@@ -111,6 +112,7 @@ class LTS(sequentialSpecification: Class<*>) {
return if (transitionInfo.isLegalByFollowUp(expectedResult)) transitionInfo else null
}
+ @Synchronized
fun nextByCancellation(actor: Actor, ticket: Int): TransitionInfo = transitionsByCancellations.computeIfAbsent(ticket) {
generateNextState { instance, suspendedOperations, resumedTicketsWithResults, continuationsMap ->
// Invoke the given operation to count the next transition.
@@ -231,7 +233,8 @@ class LTS(sequentialSpecification: Class<*>) {
/**
* Creates and stores the new LTS state or gets the one if already exists.
*/
- private inline fun StateInfo.intern(
+ @Synchronized
+ private fun StateInfo.intern(
curOperation: Operation?,
block: (StateInfo, RemappingFunction?) -> T
): T {
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.java
index 8692dda21..1ae772750 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.java
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/SerializabilityVerifier.java
@@ -22,7 +22,6 @@
* #L%
*/
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult;
import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario;
import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier;
@@ -38,8 +37,8 @@
public class SerializabilityVerifier extends CachedVerifier {
private final LinearizabilityVerifier linearizabilityVerifier;
- public SerializabilityVerifier(ExecutionScenario scenario, Class> sequentialSpecification) {
- this.linearizabilityVerifier = new LinearizabilityVerifier(convertScenario(scenario), sequentialSpecification);
+ public SerializabilityVerifier(Class> sequentialSpecification) {
+ this.linearizabilityVerifier = new LinearizabilityVerifier(sequentialSpecification);
}
private static List> convert(List initPart, List> parallelPart, List postPart) {
@@ -66,8 +65,8 @@ private static ExecutionResult convertResult(ExecutionResult scenario) {
}
@Override
- public boolean verifyResultsImpl(ExecutionResult results) {
- return linearizabilityVerifier.verifyResultsImpl(convertResult(results));
+ public boolean verifyResultsImpl(ExecutionScenario scenario, ExecutionResult results) {
+ return linearizabilityVerifier.verifyResultsImpl(convertScenario(scenario), convertResult(results));
}
@Override
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/Verifier.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/Verifier.java
index 13b9e7470..03146030d 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/Verifier.java
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/Verifier.java
@@ -22,7 +22,7 @@
* #L%
*/
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult;
+import org.jetbrains.kotlinx.lincheck.execution.*;
import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier;
/**
@@ -30,7 +30,7 @@
* By default, it checks for linearizability (see {@link LinearizabilityVerifier}).
*
* IMPORTANT!
- * All implementations should have {@code (ExecutionScenario scenario, Class> sequentialSpecification)} constructor,
+ * All implementations should have {@code (Class> sequentialSpecification)} constructor,
* which takes the scenario to be tested and the correct sequential implementation of the testing data structure.
*/
public interface Verifier {
@@ -38,7 +38,7 @@ public interface Verifier {
* Verifies the specified results for correctness.
* Returns {@code true} if results are possible, {@code false} otherwise.
*/
- boolean verifyResults(ExecutionResult results);
+ boolean verifyResults(ExecutionScenario scenario, ExecutionResult results);
/**
* Verifiers which use sequential implementation instances as states (or parts of them)
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt
index 9d4dc7f63..a5746e752 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/linearizability/LinearizabilityVerifier.kt
@@ -35,12 +35,11 @@ import org.jetbrains.kotlinx.lincheck.verifier.*
* for performance improvement (see [CachedVerifier]).
*/
class LinearizabilityVerifier(
- scenario: ExecutionScenario,
sequentialSpecification: Class<*>
-) : AbstractLTSVerifier(scenario, sequentialSpecification) {
+) : AbstractLTSVerifier(sequentialSpecification) {
override val lts: LTS = LTS(sequentialSpecification = sequentialSpecification)
- override fun createInitialContext(results: ExecutionResult) =
+ override fun createInitialContext(scenario: ExecutionScenario, results: ExecutionResult) =
LinearizabilityContext(scenario, results, lts.initialState)
}
diff --git a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.java b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.java
index 20b1b1083..951365c77 100644
--- a/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.java
+++ b/lincheck/src/main/java/org/jetbrains/kotlinx/lincheck/verifier/quiescent/QuiescentConsistencyVerifier.java
@@ -1,5 +1,3 @@
-package org.jetbrains.kotlinx.lincheck.verifier.quiescent;
-
/*
* #%L
* Lincheck
@@ -10,35 +8,31 @@
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
+package org.jetbrains.kotlinx.lincheck.verifier.quiescent;
-import org.jetbrains.kotlinx.lincheck.Actor;
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult;
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario;
-import org.jetbrains.kotlinx.lincheck.verifier.CachedVerifier;
-import org.jetbrains.kotlinx.lincheck.verifier.linearizability.LinearizabilityVerifier;
+import org.jetbrains.kotlinx.lincheck.*;
+import org.jetbrains.kotlinx.lincheck.execution.*;
+import org.jetbrains.kotlinx.lincheck.verifier.*;
+import org.jetbrains.kotlinx.lincheck.verifier.linearizability.*;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
public class QuiescentConsistencyVerifier extends CachedVerifier {
- private final ExecutionScenario originalScenario;
private final LinearizabilityVerifier linearizabilityVerifier;
- public QuiescentConsistencyVerifier(ExecutionScenario scenario, Class> sequentialSpecification) {
- this.originalScenario = scenario;
- this.linearizabilityVerifier = new LinearizabilityVerifier(convertScenario(scenario), sequentialSpecification);
+ public QuiescentConsistencyVerifier(Class> sequentialSpecification) {
+ this.linearizabilityVerifier = new LinearizabilityVerifier(sequentialSpecification);
}
private static ExecutionScenario convertScenario(ExecutionScenario scenario) {
@@ -69,11 +63,13 @@ private static boolean isQuiescentConsistent(Actor actor) {
}
@Override
- public boolean verifyResultsImpl(ExecutionResult results) {
- return linearizabilityVerifier.verifyResultsImpl(new ExecutionResult(
- results.initResults,
- convertAccordingToScenario(originalScenario.parallelExecution, results.parallelResults),
- results.postResults)
+ public boolean verifyResultsImpl(ExecutionScenario scenario, ExecutionResult results) {
+ return linearizabilityVerifier.verifyResultsImpl(convertScenario(scenario),
+ new ExecutionResult(
+ results.initResults,
+ convertAccordingToScenario(scenario.parallelExecution, results.parallelResults),
+ results.postResults
+ )
);
}
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLinCheckTest.kt b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLinCheckTest.kt
new file mode 100644
index 000000000..c2ebd3ddc
--- /dev/null
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/AbstractLinCheckTest.kt
@@ -0,0 +1,54 @@
+/*-
+ * #%L
+ * Lincheck
+ * %%
+ * Copyright (C) 2019 JetBrains s.r.o.
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+package org.jetbrains.kotlinx.lincheck.test
+
+import org.jetbrains.kotlinx.lincheck.*
+import org.jetbrains.kotlinx.lincheck.strategy.IterationResult
+import org.jetbrains.kotlinx.lincheck.strategy.SuccessIterationResult
+import org.jetbrains.kotlinx.lincheck.strategy.stress.StressOptions
+import org.jetbrains.kotlinx.lincheck.verifier.VerifierState
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import kotlin.reflect.KClass
+import kotlin.reflect.full.isSubclassOf
+
+/**
+ * An abstraction for testing all lincheck strategies
+ */
+abstract class AbstractLinCheckTest(vararg expectedErrors: KClass) : VerifierState() {
+ private val expectedErrors = if (expectedErrors.isEmpty()) listOf(SuccessIterationResult::class) else expectedErrors.toList()
+ private val reporter = Reporter(LoggingLevel.INFO)
+
+ override fun extractState() = error("Not implemented")
+
+ open fun > O.customizeOptions(): O = this
+
+ private fun runTest(createOptions: () -> Options<*, *>) {
+ val result = LinChecker.checkDetailed(this.javaClass, createOptions()).values.first().last()
+ assertTrue(reporter.generateReport(result), expectedErrors.any { expectedError -> result::class.isSubclassOf(expectedError)})
+ }
+
+ @Test(timeout = 100_000)
+ fun testWithStressStrategy() = runTest {
+ StressOptions().iterations(30).customizeOptions()
+ }
+}
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/ExceptionAsResultTest.java b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/ExceptionAsResultTest.java
index 1d85ba724..78f4adcd5 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/ExceptionAsResultTest.java
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/ExceptionAsResultTest.java
@@ -22,7 +22,6 @@
* #L%
*/
-import org.jetbrains.kotlinx.lincheck.LinChecker;
import org.jetbrains.kotlinx.lincheck.annotations.Operation;
import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest;
import org.junit.Test;
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt
index c29126ec6..12f78673d 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/HangingTest.kt
@@ -23,17 +23,17 @@ package org.jetbrains.kotlinx.lincheck.test
import org.jetbrains.kotlinx.lincheck.*
import org.jetbrains.kotlinx.lincheck.annotations.*
-import org.jetbrains.kotlinx.lincheck.strategy.stress.*
-import org.junit.*
+import org.jetbrains.kotlinx.lincheck.strategy.*
-@StressCTest(iterations = 1, actorsBefore = 0, actorsAfter = 0,
- requireStateEquivalenceImplCheck = false, minimizeFailedScenario = false)
-class HangingTest {
+class HangingTest : AbstractLinCheckTest(DeadlockIterationResult::class) {
@Operation
fun badOperation() {
while (true) {}
}
- @Test(expected = AssertionError::class)
- fun test() = LinChecker.check(this::class.java)
+ override fun extractState() = Unit
+
+ override fun > O.customizeOptions(): O =
+ iterations(1).actorsBefore(0).actorsAfter(0)
+ .requireStateEquivalenceImplCheck(false).minimizeFailedScenario(false)
}
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/NonParallelOpGroupTest.java b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/NonParallelOpGroupTest.java
index 73e2a0000..478b5a734 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/NonParallelOpGroupTest.java
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/NonParallelOpGroupTest.java
@@ -22,28 +22,25 @@
* #L%
*/
-import org.jetbrains.kotlinx.lincheck.LinChecker;
-import org.jetbrains.kotlinx.lincheck.LoggingLevel;
-import org.jetbrains.kotlinx.lincheck.annotations.LogLevel;
-import org.jetbrains.kotlinx.lincheck.annotations.OpGroupConfig;
+import org.jctools.queues.atomic.*;
+import org.jetbrains.annotations.*;
+import org.jetbrains.kotlinx.lincheck.*;
import org.jetbrains.kotlinx.lincheck.annotations.Operation;
-import org.jetbrains.kotlinx.lincheck.annotations.Param;
-import org.jetbrains.kotlinx.lincheck.paramgen.IntGen;
-import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest;
-import org.jctools.queues.atomic.SpscLinkedAtomicQueue;
-import org.junit.Test;
+import org.jetbrains.kotlinx.lincheck.annotations.*;
+import org.jetbrains.kotlinx.lincheck.strategy.stress.*;
+import org.jetbrains.kotlinx.lincheck.verifier.*;
+import org.junit.*;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.*;
@OpGroupConfig(name = "producer", nonParallel = true)
@OpGroupConfig(name = "consumer", nonParallel = true)
-@StressCTest(requireStateEquivalenceImplCheck = false)
-public class NonParallelOpGroupTest {
+@StressCTest(iterations = 50, actorsPerThread = 3)
+public class NonParallelOpGroupTest extends VerifierState {
private SpscLinkedAtomicQueue queue = new SpscLinkedAtomicQueue<>();
- private AtomicInteger i = new AtomicInteger();
@Operation(group = "producer")
- public void offer(@Param(gen = IntGen.class) Integer x) {
+ public void offer(Integer x) {
queue.offer(x);
}
@@ -52,9 +49,16 @@ public Integer poll() {
return queue.poll();
}
- @Operation
- public int incAndGet() {
- return i.incrementAndGet();
+ @NotNull
+ @Override
+ protected Object extractState() {
+ List elements = new ArrayList<>();
+ while (true) {
+ Integer el = poll();
+ if (el == null) break;
+ elements.add(el);
+ }
+ return elements;
}
@Test
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/ResultAsParameterTest.java b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/ResultAsParameterTest.java
index e93294f24..1862fe3d1 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/ResultAsParameterTest.java
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/ResultAsParameterTest.java
@@ -22,7 +22,6 @@
* #L%
*/
-import org.jetbrains.kotlinx.lincheck.LinChecker;
import org.jetbrains.kotlinx.lincheck.annotations.OpGroupConfig;
import org.jetbrains.kotlinx.lincheck.annotations.Operation;
import org.jetbrains.kotlinx.lincheck.annotations.Param;
@@ -35,7 +34,7 @@
import java.util.List;
@OpGroupConfig(name = "push_remove", nonParallel = true)
-@StressCTest
+@StressCTest(iterations = 10, actorsBefore = 0, actorsPerThread = 2, actorsAfter = 0)
public class ResultAsParameterTest extends VerifierState {
private Stack stack = new Stack();
private Node lastPushNode = null;
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/RunOnceTest.java b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/RunOnceTest.java
index 67f1addf8..b49e8fe8a 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/RunOnceTest.java
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/RunOnceTest.java
@@ -22,14 +22,13 @@
* #L%
*/
-import org.jetbrains.kotlinx.lincheck.LinChecker;
import org.jetbrains.kotlinx.lincheck.annotations.Operation;
import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest;
import org.junit.Test;
-@StressCTest(threads = 3, iterations = 100, invocationsPerIteration = 10, requireStateEquivalenceImplCheck = false)
+@StressCTest(threads = 3, iterations = 10, invocationsPerIteration = 10, requireStateEquivalenceImplCheck = false)
public class RunOnceTest {
- private A a = new A();;
+ private A a = new A();
@Operation(runOnce = true)
public void a() {
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt
index bd3bacd5b..40cb46720 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/ParallelThreadsRunnerEasyExceptionTest.kt
@@ -25,6 +25,8 @@ import org.jetbrains.kotlinx.lincheck.runner.ParallelThreadsRunner
import org.junit.Test
import kotlin.coroutines.intrinsics.*
import org.jetbrains.kotlinx.lincheck.*
+import org.jetbrains.kotlinx.lincheck.runner.CompletedInvocationResult
+import org.jetbrains.kotlinx.lincheck.strategy.IterationResult
import org.jetbrains.kotlinx.lincheck.strategy.Strategy
import org.jetbrains.kotlinx.lincheck.test.verifier.*
import org.junit.Assert.assertEquals
@@ -86,7 +88,7 @@ class SuspendResumeScenarios {
class ParallelThreadsRunnerExceptionTest {
val mockStrategy = object : Strategy(null, null, null) {
- override fun run() {
+ override fun run(): IterationResult {
throw UnsupportedOperationException()
}
}
@@ -114,10 +116,9 @@ class ParallelThreadsRunnerExceptionTest {
}
}
}
- val runner =
- ParallelThreadsRunner(scenario, mockStrategy, testClass, null)
- val results = runner.run()
- assertEquals(results, expectedResults)
+ val runner = ParallelThreadsRunner(scenario, mockStrategy, testClass, null)
+ val invocationResult = runner.run()
+ assertEquals((invocationResult as CompletedInvocationResult).results, expectedResults)
}
@Test
@@ -135,8 +136,8 @@ class ParallelThreadsRunnerExceptionTest {
}
}
val runner = ParallelThreadsRunner(scenario, mockStrategy, testClass, null)
- val results = runner.run()
- assertEquals(results, expectedResults)
+ val invocationResult = runner.run()
+ assertEquals((invocationResult as CompletedInvocationResult).results, expectedResults)
}
@Test
@@ -148,10 +149,9 @@ class ParallelThreadsRunnerExceptionTest {
}
}
}
- val runner =
- ParallelThreadsRunner(scenario, mockStrategy, testClass, null)
- val results = runner.run()
- assertEquals(results, expectedResults)
+ val runner = ParallelThreadsRunner(scenario, mockStrategy, testClass, null)
+ val invocationResult = runner.run()
+ assertEquals((invocationResult as CompletedInvocationResult).results, expectedResults)
}
}
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java
index c59e72176..f844b0287 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/runner/TestThreadExecutionHelperTest.java
@@ -1,5 +1,3 @@
-package org.jetbrains.kotlinx.lincheck.test.runner;
-
/*
* #%L
* Lincheck
@@ -10,23 +8,25 @@
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
+package org.jetbrains.kotlinx.lincheck.test.runner;
import org.jetbrains.kotlinx.lincheck.*;
-import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult;
+import org.jetbrains.kotlinx.lincheck.runner.InvocationResult;
import org.jetbrains.kotlinx.lincheck.runner.Runner;
import org.jetbrains.kotlinx.lincheck.runner.TestThreadExecution;
import org.jetbrains.kotlinx.lincheck.runner.TestThreadExecutionGenerator;
+import org.jetbrains.kotlinx.lincheck.strategy.IterationResult;
import org.jetbrains.kotlinx.lincheck.strategy.Strategy;
import org.junit.Assert;
import org.junit.Before;
@@ -45,13 +45,13 @@ public class TestThreadExecutionHelperTest {
public void setUp() {
Strategy mockStrategy = new Strategy(null, null, null) {
@Override
- public void run(){
+ public IterationResult run(){
throw new UnsupportedOperationException();
}
};
runner = new Runner(null, mockStrategy, ArrayDeque.class) {
@Override
- public ExecutionResult run() {
+ public InvocationResult run() {
throw new UnsupportedOperationException();
}
};
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchCTestAnnTest.java b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchCTestAnnTest.java
index 72fb8a5a5..9259f53db 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchCTestAnnTest.java
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchCTestAnnTest.java
@@ -22,7 +22,6 @@
* #L%
*/
-import org.jetbrains.kotlinx.lincheck.LinChecker;
import org.jetbrains.kotlinx.lincheck.annotations.Operation;
import org.jetbrains.kotlinx.lincheck.execution.RandomExecutionGenerator;
import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest;
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchOptionsTest.java b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchOptionsTest.java
index 980d87f71..380ef3793 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchOptionsTest.java
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/randomswitch/RandomSwitchOptionsTest.java
@@ -22,7 +22,6 @@
* #L%
*/
-import org.jetbrains.kotlinx.lincheck.LinChecker;
import org.jetbrains.kotlinx.lincheck.Options;
import org.jetbrains.kotlinx.lincheck.annotations.Operation;
import org.jetbrains.kotlinx.lincheck.execution.RandomExecutionGenerator;
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressCTestAnnTest.java b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressCTestAnnTest.java
index 80f545b17..c0da59290 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressCTestAnnTest.java
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressCTestAnnTest.java
@@ -22,8 +22,6 @@
* #L%
*/
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.kotlinx.lincheck.LinChecker;
import org.jetbrains.kotlinx.lincheck.annotations.Operation;
import org.jetbrains.kotlinx.lincheck.execution.RandomExecutionGenerator;
import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest;
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java
index edbb6bccb..c362a388b 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/strategy/stress/StressOptionsTest.java
@@ -1,5 +1,3 @@
-package org.jetbrains.kotlinx.lincheck.test.strategy.stress;
-
/*
* #%L
* Lincheck
@@ -10,19 +8,19 @@
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
+package org.jetbrains.kotlinx.lincheck.test.strategy.stress;
-import org.jetbrains.kotlinx.lincheck.LinChecker;
import org.jetbrains.kotlinx.lincheck.LoggingLevel;
import org.jetbrains.kotlinx.lincheck.Options;
import org.jetbrains.kotlinx.lincheck.annotations.Operation;
@@ -51,7 +49,7 @@ public void test() {
.verifier(LinearizabilityVerifier.class)
.threads(2)
.actorsPerThread(3)
- .logLevel(LoggingLevel.INFO)
+ .logLevel(LoggingLevel.ERROR)
.requireStateEquivalenceImplCheck(false)
.minimizeFailedScenario(false);
LinChecker.check(StressOptionsTest.class, opts);
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt
index 187ffd98f..433316cd2 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/CustomScenarioDSL.kt
@@ -60,7 +60,7 @@ fun verify(
val (scenario, results) = scenarioWithResults(block)
val verifier = verifierClass.getConstructor(ExecutionScenario::class.java, Class::class.java)
.newInstance(scenario, testClass)
- val res = verifier.verifyResults(results)
+ val res = verifier.verifyResults(scenario, results)
assert(res == correct)
}
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/EpsilonVerifierTest.kt b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/EpsilonVerifierTest.kt
index 19593cf59..3fa08d373 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/EpsilonVerifierTest.kt
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/EpsilonVerifierTest.kt
@@ -1,5 +1,3 @@
-package org.jetbrains.kotlinx.lincheck.test.verifier
-
/*
* #%L
* Lincheck
@@ -21,6 +19,7 @@ package org.jetbrains.kotlinx.lincheck.test.verifier
* .
* #L%
*/
+package org.jetbrains.kotlinx.lincheck.test.verifier
import org.jetbrains.kotlinx.lincheck.LinChecker
import org.jetbrains.kotlinx.lincheck.annotations.Operation
@@ -29,7 +28,7 @@ import org.jetbrains.kotlinx.lincheck.verifier.EpsilonVerifier
import org.jetbrains.kotlinx.lincheck.verifier.VerifierState
import org.junit.Test
-@StressCTest(verifier = EpsilonVerifier::class)
+@StressCTest(iterations = 10, threads = 2, actorsPerThread = 2, verifier = EpsilonVerifier::class)
class EpsilonVerifierTest : VerifierState() {
private var i = 0
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelStressTest.kt b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelStressTest.kt
index 552cce0aa..caf6af3a6 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelStressTest.kt
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/BufferedChannelStressTest.kt
@@ -21,16 +21,18 @@
*/
package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability
+import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import org.jetbrains.kotlinx.lincheck.*
import org.jetbrains.kotlinx.lincheck.annotations.*
import org.jetbrains.kotlinx.lincheck.paramgen.*
import org.jetbrains.kotlinx.lincheck.strategy.stress.*
+import org.jetbrains.kotlinx.lincheck.test.*
import org.junit.*
+@InternalCoroutinesApi
@Param(name = "value", gen = IntGen::class, conf = "1:5")
-@StressCTest(actorsPerThread = 3, sequentialSpecification = SequentiaBuffered2IntChannel::class)
-class BufferedChannelStressTest {
+class BufferedChannelStressTest : AbstractLinCheckTest() {
private val c = Channel(2)
@Operation(cancellableOnSuspension = false)
@@ -45,8 +47,9 @@ class BufferedChannelStressTest {
@Operation
fun offer(@Param(name = "value") value: Int) = c.offer(value)
- @Test
- fun test() = LinChecker.check(BufferedChannelStressTest::class.java)
+ override fun > O.customizeOptions(): O =
+ sequentialSpecification(SequentiaBuffered2IntChannel::class.java)
}
+@InternalCoroutinesApi
class SequentiaBuffered2IntChannel : SequentialIntChannel(capacity = 2)
\ No newline at end of file
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelStressTest.kt b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelStressTest.kt
index 76ab40111..50b96aaa1 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelStressTest.kt
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/RendezvousChannelStressTest.kt
@@ -21,34 +21,36 @@
*/
package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability
+import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import org.jetbrains.kotlinx.lincheck.*
import org.jetbrains.kotlinx.lincheck.annotations.*
import org.jetbrains.kotlinx.lincheck.paramgen.*
import org.jetbrains.kotlinx.lincheck.strategy.stress.*
+import org.jetbrains.kotlinx.lincheck.test.*
import org.jetbrains.kotlinx.lincheck.verifier.linearizability.*
import org.junit.*
-@LogLevel(LoggingLevel.DEBUG)
+@InternalCoroutinesApi
@Param(name = "value", gen = IntGen::class, conf = "1:5")
-@StressCTest(sequentialSpecification = SequentialRendezvousIntChannel::class, verifier = LinearizabilityVerifier::class)
-class RendezvousChannelStressTest {
+class RendezvousChannelStressTest : AbstractLinCheckTest() {
private val ch = Channel()
@Operation(handleExceptionsAsResult = [ClosedSendChannelException::class])
suspend fun send(@Param(name = "value") value: Int) = ch.send(value)
@Operation(handleExceptionsAsResult = [ClosedReceiveChannelException::class])
- suspend fun receive() = ch.receive() + 100
+ suspend fun receive() = ch.receive()
@Operation(handleExceptionsAsResult = [ClosedReceiveChannelException::class])
- suspend fun receiveOrNull() = ch.receiveOrNull()?.plus(100)
+ suspend fun receiveOrNull() = ch.receiveOrNull()
@Operation
fun close() = ch.close()
- @Test
- fun test() = LinChecker.check(RendezvousChannelStressTest::class.java)
+ override fun > O.customizeOptions(): O =
+ sequentialSpecification(SequentialRendezvousIntChannel::class.java)
}
+@InternalCoroutinesApi
class SequentialRendezvousIntChannel : SequentialIntChannel(capacity = 0)
\ No newline at end of file
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SequentialIntChannel.kt b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SequentialIntChannel.kt
index 994121762..fe8fdbbf3 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SequentialIntChannel.kt
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/linearizability/SequentialIntChannel.kt
@@ -24,102 +24,74 @@ package org.jetbrains.kotlinx.lincheck.test.verifier.linearizability
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import org.jetbrains.kotlinx.lincheck.verifier.*
-import java.util.concurrent.locks.*
-import kotlin.coroutines.*
+@InternalCoroutinesApi
open class SequentialIntChannel(private val capacity: Int) : VerifierState() {
- private val lock = ReentrantLock()
private val senders = ArrayList, Int>>()
- private val receivers = ArrayList>()
- private val buffer = ArrayList(capacity)
+ private val receivers = ArrayList>()
+ private val buffer = ArrayList()
private var closed = false
- @InternalCoroutinesApi
suspend fun send(x: Int) {
- lock.lock()
- try {
- if (offer(x)) return
- suspendAtomicCancellableCoroutine { cont ->
- senders.add(cont to x)
- }
- } finally {
- lock.unlock()
+ if (offer(x)) return
+ suspendAtomicCancellableCoroutine { cont ->
+ senders.add(cont to x)
}
}
- @InternalCoroutinesApi
fun offer(x: Int): Boolean {
- lock.lock()
- try {
- while (true) {
- if (closed) throw ClosedSendChannelException("")
- if (receivers.isEmpty() && buffer.size == capacity) return false
- if (receivers.isNotEmpty()) {
- val r = receivers.removeAt(0)
- if (r.tryResume0(x)) return true
- } else {
- buffer.add(x)
- return true
- }
+ while (true) {
+ if (closed) throw ClosedSendChannelException("")
+ if (receivers.isNotEmpty()) {
+ val r = receivers.removeAt(0)
+ if (r.tryResume0(x)) return true
+ } else {
+ if (buffer.size == capacity) return false
+ buffer.add(x)
+ return true
}
- } finally {
- lock.unlock()
}
}
- @InternalCoroutinesApi
suspend fun receive(): Int {
- lock.lock()
- try {
- val pollResult = poll()
- if (pollResult !== null) return pollResult
- return suspendAtomicCancellableCoroutine { cont ->
- receivers.add(cont)
- }
- } finally {
- lock.unlock()
- }
+ val res = receiveImpl()
+ if (res === CLOSED) throw ClosedReceiveChannelException("")
+ return res as Int
}
@InternalCoroutinesApi
suspend fun receiveOrNull(): Int? {
- lock.lock()
- try {
- if (senders.isEmpty() && closed) return null
- val pollResult = poll()
- if (pollResult !== null) return pollResult
- return suspendAtomicCancellableCoroutine { cont ->
- receivers.add(cont)
- }
- } finally {
- lock.unlock()
+ val res = receiveImpl()
+ return if (res === CLOSED) null
+ else res as Int
+ }
+
+ suspend fun receiveImpl(): Any {
+ if (buffer.isEmpty() && senders.isEmpty() && closed) return CLOSED
+ val pollResult = poll()
+ if (pollResult !== null) return pollResult
+ return suspendAtomicCancellableCoroutine { cont ->
+ receivers.add(cont)
}
}
- @InternalCoroutinesApi
fun poll(): Int? {
- lock.lock()
- try {
- if (buffer.size > 0) {
- val res = buffer.removeAt(0)
- while (true) {
- if (senders.isEmpty()) break
- val (s, x) = senders.removeAt(0)
- if (s.tryResume0(Unit)) {
- buffer.add(x)
- break
- }
- }
- return res
- }
+ if (buffer.size > 0) {
+ val res = buffer.removeAt(0)
while (true) {
- if (senders.isEmpty() && closed) throw ClosedReceiveChannelException("")
- if (senders.isEmpty()) return null
+ if (senders.isEmpty()) break
val (s, x) = senders.removeAt(0)
- if (s.tryResume0(Unit)) return x
+ if (s.tryResume0(Unit)) {
+ buffer.add(x)
+ break
+ }
}
- } finally {
- lock.unlock()
+ return res
+ }
+ while (true) {
+ if (senders.isEmpty()) return null
+ val (s, x) = senders.removeAt(0)
+ if (s.tryResume0(Unit)) return x
}
}
@@ -127,7 +99,7 @@ open class SequentialIntChannel(private val capacity: Int) : VerifierState() {
if (closed) return false
closed = true
receivers.forEach {
- it.resumeWithException(ClosedReceiveChannelException(""))
+ it.tryResume0(CLOSED)
}
receivers.clear()
return true
@@ -142,3 +114,5 @@ private fun CancellableContinuation.tryResume0(res: T): Boolean {
completeResume(token)
return true
}
+
+private val CLOSED = Any()
\ No newline at end of file
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeMPSCQueueTest.java b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeMPSCQueueTest.java
index a69833d81..0e6fad09c 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeMPSCQueueTest.java
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/quiescent/LockFreeMPSCQueueTest.java
@@ -22,20 +22,25 @@
* #L%
*/
-import org.jetbrains.kotlinx.lincheck.LinChecker;
+import kotlin.*;
+import org.jetbrains.annotations.*;
import org.jetbrains.kotlinx.lincheck.annotations.OpGroupConfig;
import org.jetbrains.kotlinx.lincheck.annotations.Operation;
import org.jetbrains.kotlinx.lincheck.annotations.Param;
import org.jetbrains.kotlinx.lincheck.paramgen.IntGen;
import org.jetbrains.kotlinx.lincheck.strategy.stress.StressCTest;
+import org.jetbrains.kotlinx.lincheck.verifier.*;
import org.jetbrains.kotlinx.lincheck.verifier.quiescent.QuiescentConsistencyVerifier;
import org.jetbrains.kotlinx.lincheck.verifier.quiescent.QuiescentConsistent;
import org.junit.Test;
-@StressCTest(verifier = QuiescentConsistencyVerifier.class, requireStateEquivalenceImplCheck = false)
+import java.util.*;
+
+@StressCTest(iterations = 50, actorsBefore = 0, actorsPerThread = 2, verifier = QuiescentConsistencyVerifier.class)
@OpGroupConfig(name = "consumer", nonParallel = true)
-public class LockFreeMPSCQueueTest {
+public class LockFreeMPSCQueueTest extends VerifierState {
private LockFreeMPSCQueue q = new LockFreeMPSCQueue<>();
+ private boolean closed = false;
@Operation(group = "consumer")
@QuiescentConsistent
@@ -52,6 +57,19 @@ public boolean addLast(@Param(gen = IntGen.class) Integer val) {
@QuiescentConsistent
public void close() {
q.close();
+ closed = true;
+ }
+
+ @NotNull
+ @Override
+ protected Object extractState() {
+ List elements = new ArrayList<>();
+ while (true) {
+ Integer el = removeFirstOrNull();
+ if (el == null) break;
+ elements.add(el);
+ }
+ return new Pair<>(elements, closed);
}
@Test
diff --git a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueSimulationTest.kt b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueSimulationTest.kt
index 3cb38f093..54787d259 100644
--- a/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueSimulationTest.kt
+++ b/lincheck/src/test/java/org/jetbrains/kotlinx/lincheck/test/verifier/serializability/SerializableQueueSimulationTest.kt
@@ -24,18 +24,17 @@ package org.jetbrains.kotlinx.lincheck.test.verifier.serializability
import org.jetbrains.kotlinx.lincheck.*
import org.jetbrains.kotlinx.lincheck.annotations.*
+import org.jetbrains.kotlinx.lincheck.annotations.Operation
import org.jetbrains.kotlinx.lincheck.paramgen.*
import org.jetbrains.kotlinx.lincheck.strategy.stress.*
-import org.jetbrains.kotlinx.lincheck.verifier.SerializabilityVerifier
+import org.jetbrains.kotlinx.lincheck.verifier.*
import org.junit.Test
-@StressCTest(actorsBefore = 2,
- threads = 2, actorsPerThread = 4,
- actorsAfter = 2,
- verifier = SerializabilityVerifier::class)
-class SerializableQueueSimulationTest {
- val q = SerializableQueueSimulation()
+@StressCTest(iterations = 30, actorsBefore = 1, threads = 2, actorsPerThread = 2, actorsAfter = 2,
+ verifier = SerializabilityVerifier::class, requireStateEquivalenceImplCheck = false)
+class SerializableQueueSimulationTest : VerifierState() {
+ private val q = SerializableQueueSimulation()
@Operation
fun push(@Param(gen = IntGen::class) item: Int) = q.push(item)
@@ -45,19 +44,4 @@ class SerializableQueueSimulationTest {
@Test
fun test() = LinChecker.check(SerializableQueueSimulationTest::class.java)
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as SerializableQueueSimulationTest
-
- if (q != other.q) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- return q.hashCode()
- }
}