@@ -42,23 +42,32 @@ class LinChecker (private val testClass: Class<*>, options: Options<*, *>?) {
42
42
* @throws LincheckAssertionError if the testing data structure is incorrect.
43
43
*/
44
44
fun check () {
45
- val failure = checkImpl() ? : return
46
- throw LincheckAssertionError (failure)
45
+ checkImpl { failure ->
46
+ if (failure != null ) throw LincheckAssertionError (failure)
47
+ }
47
48
}
48
49
49
50
/* *
50
- * @return TestReport with information about concurrent test run.
51
+ * Runs Lincheck to check the tested class under given configurations.
52
+ *
53
+ * @param cont Optional continuation taking [LincheckFailure] as an argument.
54
+ * The continuation is run in the context when Lincheck java-agent is still attached.
55
+ * @return [LincheckFailure] if a failure is discovered, null otherwise.
51
56
*/
52
57
@Synchronized // never run Lincheck tests in parallel
53
- internal fun checkImpl (): LincheckFailure ? {
58
+ internal fun checkImpl (cont : LincheckFailureContinuation ? = null ): LincheckFailure ? {
54
59
check(testConfigurations.isNotEmpty()) { " No Lincheck test configuration to run" }
55
60
lincheckVerificationStarted()
56
61
for (testCfg in testConfigurations) {
57
62
withLincheckJavaAgent(testCfg.instrumentationMode) {
58
63
val failure = testCfg.checkImpl()
59
- if (failure != null ) return failure
64
+ if (failure != null ) {
65
+ if (cont != null ) cont(failure)
66
+ return failure
67
+ }
60
68
}
61
69
}
70
+ if (cont != null ) cont(null )
62
71
return null
63
72
}
64
73
@@ -203,6 +212,36 @@ fun <O : Options<O, *>> O.check(testClass: Class<*>) = LinChecker.check(testClas
203
212
*/
204
213
fun <O : Options <O , * >> O.check (testClass : KClass <* >) = this .check(testClass.java)
205
214
206
- internal fun <O : Options <O , * >> O.checkImpl (testClass : Class <* >) = LinChecker (testClass, this ).checkImpl()
215
+ /* *
216
+ * Runs Lincheck to check the tested class under given configurations.
217
+ *
218
+ * @param testClass Tested class.
219
+ * @return [LincheckFailure] if a failure is discovered, null otherwise.
220
+ */
221
+ internal fun <O : Options <O , * >> O.checkImpl (testClass : Class <* >): LincheckFailure ? =
222
+ LinChecker (testClass, this ).checkImpl()
223
+
224
+ /* *
225
+ * Runs Lincheck to check the tested class under given configurations.
226
+ *
227
+ * Takes the [LincheckFailureContinuation] as an argument.
228
+ * This is required due to current limitations of our testing infrastructure.
229
+ * Some tests need to inspect the internals of the failure object
230
+ * (for example, the stack traces of exceptions thrown during the execution).
231
+ * However, because Lincheck dynamically installs java-agent and then uninstalls it,
232
+ * this process can invalidate some internal state of the failure object
233
+ * (for example, the source code mapping information in the stack traces is typically lost).
234
+ * To overcome this problem, we run the continuation in the context when Lincheck java-agent is still attached.
235
+ *
236
+ * @param testClass Tested class.
237
+ * @param cont Continuation taking [LincheckFailure] as an argument.
238
+ * The continuation is run in the context when Lincheck java-agent is still attached.
239
+ * @return [LincheckFailure] if a failure is discovered, null otherwise.
240
+ */
241
+ internal fun <O : Options <O , * >> O.checkImpl (testClass : Class <* >, cont : LincheckFailureContinuation ) {
242
+ LinChecker (testClass, this ).checkImpl(cont)
243
+ }
244
+
245
+ internal typealias LincheckFailureContinuation = (LincheckFailure ? ) -> Unit
207
246
208
247
internal const val NO_OPERATION_ERROR_MESSAGE = " You must specify at least one operation to test. Please refer to the user guide: https://kotlinlang.org/docs/introduction.html"
0 commit comments