Add eventually utility - #1162
Conversation
|
Looks like a sensible addition to me, but maybe we should ask for more opinions? I can raise it on Scala contributors and discord to get more eyes on it. What do you think? |
One way hand yes, but I find tinkering with stack trace might cause more issues and is a bit magical. |
Sure, let's try to find some common ground. Can you point me to the Scala Contributors thread/Discord where the discussion happens? I am happy to keep an eye on those and in this PR's thread. |
|
I've posted on the general Scala Discord and on Scalameta one. I think ideally people would comment here. I just wanted some additional opinions, since I mostly maintain munit in a minimal capacity 😓 (didn't work on the library to extensively) |
i didn't understand this part. I'm pretty sure everything we do in this library is a Future (or you wouldn't have had to add an the user needs to assert inside the body, or worry about await themselves. you just need to avoid retrying failures due to failed assertions (this looks like skipping a particular exception type). |
I am not getting your point here. munit assertions are by design "synchronous" and run wherever the user wants them to run. It is perfectly wise to do test("do something"):
assertEquals(1,1)
assert(1 == 1)And none of these return a The whole transformer mechanism is of course evident when test("do something"):
for
v <- fut()
yield assertEquals(v, 1)But then again, the first example is also perfectly valid. The goal of this proposal is for
Is that if def eventually[A](body: Any) = withRetries(() =>munitValueTransform(body))Then we lose the capacity to easily retry synchronous code as the test examples in this PR show. Also, what framework integrations consider their preferred way to convert to the Future world may necessarily be their preferred way of retrying failures. This design allows them to do it inside of their effect world before the final Future conversion runs.
The goal of this is to retry failed assertions, to wait until something that we expect to happen eventually happens. Eg, something is listening in a Docker container port, etc. This is an alternative take of specs2 eventually. Of course, we can catch only assertion errors, instead of most exceptions, that's a decision choice. Please, explain your point further. |
Blocks for the given duration on JVM and Native; Scala.js is single-threaded and cannot block, so there it does nothing. Unused until the next commit.
i saw your |
| transform: EventuallyTransform[A], | ||
| ): A = options.eventually(body) | ||
|
|
||
| def munitEventuallyOptions: EventuallyOptions = EventuallyOptions.disabled |
There was a problem hiding this comment.
What's the point of this default? Doesn't this render eventually a noop?
There was a problem hiding this comment.
correct. there's no "reasonable" default, in my opinion.
There was a problem hiding this comment.
In that case, why try to provide one? This makes eventually similar to identity, right? I think it's reasonable to require the user to provide retry settings, either via implicit or explicitly.
There was a problem hiding this comment.
sounds good, let's do it this way. @tgodzik please chime in (since i revised it, i guess it's my implicit "ok".)
There was a problem hiding this comment.
Thanks. This looks okay to me.
`eventually(body)` re-evaluates the body until its assertions stop failing, sleeping between attempts; a `Future` retries without blocking. The retry count and sleep come from an implicit `EventuallyOptions`, which a suite or a narrower scope must provide; both must be positive. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A failing `eventually` reports the frames of a plain failing assertion: `StackTraces.dropOutside` marks every attempt and trimming cuts everything outside the innermost marker, so the retry loop never shows up. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Hi! As discussed in #1161, here's a proposal for
eventually. Some things to think about:munitValueTransform(body)and returned aFuture. However, that wouldn't work for sync assertions before the end of a test example, and wouldn't return composable effect types for the interested downstream effect adapter libraries.I am open to all kinds of suggestions.