Make RuntimeAsyncCommandsTest more reliable#12602
Conversation
Tests are doing more harm than good when they fail randomly. Also, test suite shouldn't take minutes. Using monitors instead of relaying on flaky `Thread.sleep`. Changed the output of one test which was relying on sometimes odd interaction with safepoints. This change should make `RuntimeAsyncCommandsTest` more reliable while still giving us some feedback about interrupts.
JaroslavTulach
left a comment
There was a problem hiding this comment.
waitis better thansleep- thanks for improving the code
- suggestion: wrap the awaiting code into a single helper method
- enter
synchronizedfirst and only then loop - check condition first and only if not satisfied
wait
...ion-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeAsyncCommandsTest.scala
Outdated
Show resolved
Hide resolved
...ion-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeAsyncCommandsTest.scala
Outdated
Show resolved
Hide resolved
...ion-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeAsyncCommandsTest.scala
Outdated
Show resolved
Hide resolved
...ion-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeAsyncCommandsTest.scala
Outdated
Show resolved
Hide resolved
...ion-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeAsyncCommandsTest.scala
Outdated
Show resolved
Hide resolved
|
|
||
| val failures = responses.filter(_.payload.isInstanceOf[Api.ExecutionFailed]) | ||
| val failures = | ||
| responses.filter(_.payload.isInstanceOf[Api.ExecutionComplete]) |
There was a problem hiding this comment.
Is this change deliberate?
There was a problem hiding this comment.
Yes, see my comment in the PR
Maybe Windows is just slow by definition.
JaroslavTulach
left a comment
There was a problem hiding this comment.
- possibly there is problem calling
resettoo early - otherwise I like the simplification of the testing code
| val expectedList = expected.toList | ||
| monitor.synchronized { | ||
| while (!receivedExpected && iteration < 20) { | ||
| out = readAndReset() |
There was a problem hiding this comment.
readAndReset() is destructive
- it cleans the already collected text
- it is suspicious to
reset()before the condition is met - what if two lines are expected and ...
- ...
outcontains just the first one and the check fails - ... then another line is added to
out, but ... - ... the first line is already
reset()?
| isProgramStarted = out == List("started") | ||
| iteration += 1 | ||
| } | ||
| val isProgramStarted = context.out.awaitOnText("started") |
There was a problem hiding this comment.
Simpler.
context.out.assertAwaitOnText("started", "Program start time out");Having an assert like check would remove one local variable and three subsequent lines reporting the failure.
...ion-tests/src/test/scala/org/enso/interpreter/test/instrument/RuntimeAsyncCommandsTest.scala
Show resolved
Hide resolved
| iteration += 1 | ||
| } | ||
|
|
||
| val reallyFinished = context.out.awaitOnText(exact = false, "True") |
There was a problem hiding this comment.
| val reallyFinished = context.out.awaitOnText(exact = false, "True") | |
| context.out.awaitOnText(exact = false, "True") shouldBe true |
is relatively short as well. I still like assertAwaitOnText a bit more. Probably because it feels more JUnit4 like...
|
The changes in this PR improve robustness. But: I am debugging RuntimeAsyncCommandsTest for last few days and alas, the changes in this PR are unlikely to help its stability: The great variability of the results comes from the randomness when this Try(
executeProgram(contextId, stackItem, localCalls)
).toEither.left
.map(onExecutionError(stackItem.item, _))which (in my opinion) may not be catching try {
lockTimestamp = acquireWriteCompilationLock(where)
callable.call()
} catch {
case _: InterruptedException =>
logger.debug(
"Failed [{}] to acquire lock: interrupted",
Array[Any](where.getSimpleName)
)
null.asInstanceOf[T]
} finally {if |
|
Another problem is in def onFailure(): Option[Api.ExecutionResult] = error match {
case _: ThreadInterruptedException =>
val message = s"Execution of function $itemName interrupted."
logger.trace(message)
None
case _ =>
val message = s"Execution of function $itemName failed ($reason)."
logger.trace(message, error)
Some(ExecutionResult.Failure(message, None))if the interrupt happens in guest code (like in Enso calling |
Pull Request Description
Tests are doing more harm than good when they fail randomly. Also, test suite shouldn't take minutes.
Using monitors instead of relaying on flaky
Thread.sleep. Changed the output of one test which was relying on sometimes odd interaction with safepoints.This change should make
RuntimeAsyncCommandsTestmore reliable while still giving us some feedback about interrupts.The whole test suite should also run in a couple of seconds.
Closes #11576 🤞
Checklist
Please ensure that the following checklist has been satisfied before submitting the PR:
Scala,
Java,
TypeScript,
and
Rust
style guides. In case you are using a language not listed above, follow the Rust style guide.