-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Test timeout does not work when background scope loops forever and test uses runCurrent #3917
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Yes, that's because |
Huh, but the test does complete when launching in regular scope instead of |
Yes, because a timeout cancels the main test scope, but the background scope is independent from it cancellation-wise: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-scope/background-scope.html
When you just So, at least this behavior is documented and can be predicted. Though maybe we could do better. It would be helpful if you showed the actual test where you encountered this issue. Maybe it is sensible, and you have a point, in which case, yes, we will need to change |
My test includes test subject, a pretty complicated stack of corourines (lots of nested coroutine scopes etc.) that is collecting flows in infinite loop, so I have to run it in To ensure that things actually execute in that background scope, I have to call For this case, I've managed to screwed up something inside test subject, causing the infinite loop and thus hanging the test. This would likely still be caught by tests, since they would hang forever, but I would much prefer if test would fail in this case, alerting us to the bug in subject's code. |
I still don't understand this: Since you just want to process background tasks, instead of |
Also, you don't have to run it in In any case, some code would be welcome. |
Here is an example on why we need @Test
fun `Demo`() = runTest {
var value: Boolean = false
launch {
launch {
value = true
}
}
runCurrent()
assertEquals(value, true)
} where We generally follow this kind of pattern in all our tests:
But isn't that essentially equal to
But that is extra boilerplate. Isn't the entire premise of the |
Your example still doesn't explain the issue you have. The most important part is,
In what test do you want infinite loops,
Background scope is for background work, it has several pieces to its behavior. Being cancelled on test finish is one of them, so it's not the entire premise. For one, neither the test itself nor
|
Our code was essentially something like this: suspend fun subjectUnderTest(
infiniteFlowA: Flow<Boolean>,
infiniteFlowB: Flow<Boolean>
) = coroutineScope {
val channelForTheFlowA = infiniteFlowA.produceIn(this)
val channelForTheFlowB = infiniteFlowB.produceIn(this)
var timeoutValue = 1_000
try {
while (isActive) {
select {
channelForTheFlowA.onReceive {
timeoutValue = someLogicBasedOnFlowAOutput(it)
}
channelForTheFlowB.onReceive {
timeoutValue = someLogicBasedOnFlowBOutput(it)
}
onTimeout(timeoutValue) {
setSomethingHereThatWeWantToAssert()
timeoutValue = resetTimeoutToSomeValue()
}
}
}
} finally {
channelForTheFlowA.cancel()
channelForTheFlowB.cancel()
}
} Code collects data from infinite flows (via select) and then performs value after some timeout. We had a bug in the code where
That leaves us with the following options:
|
Ok, I think I got it! Is it correct that the problem is only that, with Still, your point is valid. Thank you for elaborating on it! This is one more argument for us to provide a |
Ah yes, sorry, I forgot to mention that the infinite loop is accidental, yes. Thanks for looking into it. If you want, you can close this and merge it into #3919 |
Describe the bug
When there is an infinite loop in a background scope coroutine, calling
runCurrent()
in test will cause test to lock up, ignoring the timeout.Provide a Reproducer
Run following test:
this test will never complete despite having timeout set as one second.
The text was updated successfully, but these errors were encountered: