Relax Kotlin contracts for Executable parameters#4481
Relax Kotlin contracts for Executable parameters#4481marcphilipp wants to merge 4 commits intodevelop/6.xfrom
Executable parameters#4481Conversation
Since `Executable` is allowed/expected to throw exceptions, the semantics of being called `EXACTLY_ONCE` are blurry because it might not be executed entirely when throwing an exception. Since the Kotlin 2.1 compiler emits a warning for these parameters, we're relaxing their contracts to be called `AT_MOST_ONCE` instead.
Now that #4371 is resolved.
79993fe to
ab26f26
Compare
awelless
left a comment
There was a problem hiding this comment.
The tests prevent me from approval.
The decision about assertDoesNotThrow I leave to you
| @Test | ||
| fun `assertTimeout with value initialization in lambda`() { | ||
| val value: Int | ||
| fun `assertTimeout with value assignment in lambda`() { |
There was a problem hiding this comment.
The problem is that this test will pass even when no contract is specified. The AT_LEAST_ONE contract allows us to assign vals in lamda body. A test like this would be a better candidate, as it will pass only with AT_LEAST_ONCE or EXACTLY_ONCE contracts. This applies to other changes in this file too.
@Test
fun `assertTimeout with value assignment in lambda`() {
val value: Int
assertTimeout(ofMillis(500)) {
value = 10 // Val can be assigned in the message supplier lambda.
assertEquals(10, value)
}
}There was a problem hiding this comment.
Thanks for the suggestion. Applied in 5cac4e3
| internal inline fun <R> evaluateAndWrap(executable: () -> R): ThrowingSupplier<R> { | ||
| contract { | ||
| callsInPlace(executable, EXACTLY_ONCE) | ||
| callsInPlace(executable, AT_MOST_ONCE) |
There was a problem hiding this comment.
Although this makes the code compile, I'm still not entirely happy that we must mark this as AT_MOST_ONCE only to make kotlin assertDoesNotThrow compatible with its java counterpart.
If we were able to just run lambda in kotlin, we'd be able to mark all assertDoesNotThrow methods as EXACTLY_ONCE. That being set, this will cause duplication of AssertDoesNotThrow logic which doesn't sound optimal either.
internal inline fun <R> evaluateAndWrap(executable: () -> R): ThrowingSupplier<R> {
contract {
callsInPlace(executable, EXACTLY_ONCE)
}
return try {
executable()
} catch (throwable: Throwable) {
// logic from java's AssertDoesNotThrow
UnrecoverableExceptions.rethrowIfUnrecoverable(t)
throw AssertDoesNotThrow.createAssertionFailedError(messageOrSupplier, t)
}
}|
@awelless Thanks for the review! Do you have time to take another look at my latest changes? |
awelless
left a comment
There was a problem hiding this comment.
The changes look good, besides the contract in one assertDoesNotThrow.
Approved, assuming the comment will be addressed
| executable: () -> R | ||
| ): R { | ||
| contract { | ||
| callsInPlace(executable, EXACTLY_ONCE) |
There was a problem hiding this comment.
This executable should be called EXACTLY_ONCE, as other assertDoesNotThrow methods.
|
Superseded by #4551 |
ExecutableparametersOverview
I hereby agree to the terms of the JUnit Contributor License Agreement.
Definition of Done
@APIannotations