Andrey Stepankov
04/06/2020, 2:34 PM@Test
fun test_ConflatedBroadcastChannel_runBlocking() {
runBlocking {
val channel = ConflatedBroadcastChannel<Int>()
val collect = async {
channel.asFlow().collect {
println("collected $it")
}
}
val send = async {
(0 until 5).forEach {
println("send $it")
channel.send(it)
}
channel.cancel()
}
listOf(collect, send).awaitAll()
}
// console out
// send 0
// send 1
// send 2
// send 3
// send 4
// collected 0
// collected 4
}
@Test
fun test_ConflatedBroadcastChannel_runBlockingTest() {
runBlockingTest {
val channel = ConflatedBroadcastChannel<Int>()
val collect = async {
channel.asFlow().collect {
println("collected $it")
}
}
val send = async {
(0 until 5).forEach {
println("send $it")
channel.send(it)
}
channel.cancel()
}
listOf(collect, send).awaitAll()
}
// console out
// send 0
// collected 0
// send 1
// collected 1
// send 2
// collected 2
// send 3
// collected 3
// send 4
// collected 4
}tseisel
04/06/2020, 3:15 PMrunBlockingTest is designed for simplifying unit testing of coroutine code. To do so, it offers some simplifications of the coroutine execution model:
• coroutines started with launch or async are executed eagerly and synchronously, right after their declaration
• functions that involve time such as delay or withTimeout are fast-forwarded (virtual time) to make tests execute way faster.
Therefore, runBlockingTest does not behave like its "production code" implementation runBlocking .
When you call async, its block is not executed immediately, but scheduled for execution. This means that if you start multiple coroutines at the same time, there is no guarantee that coroutine#1 will run before coroutine#2. This is likely what's happening here:
• ConflatedBroadcastChannel.send is called for each number, and it never suspends by nature, hence all numbers are sent at once (1, 2, 3 are lost due to conflation).
• Your consumer block suspends until 0 is received, then only receive 4 because other numbers have been overwritten.tseisel
04/06/2020, 3:19 PMlaunch instead of async, since there are no results to be awaited. Because you have launched those coroutines in the scope of runBlocking (or runBlockingTest), you don't have to use awaitAll (ou joinAll when using launch) to wait for both coroutines to complete. This is a benefit from Structured Concurrency.Andrey Stepankov
04/06/2020, 3:31 PMKroppeb
04/06/2020, 5:17 PMConflated in ConflatedBroadcastChannel stands forAndrey Stepankov
04/06/2020, 7:54 PM