Skip to content
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

Update ChannelManager.kt #637

Merged
merged 4 commits into from
Jun 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions multicast/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ kotlin {
implementation(libs.kotlinx.coroutines.core)
}
}

val commonTest by getting {
dependencies {
implementation(kotlin("test"))
implementation(libs.junit)
implementation(libs.kotlinx.coroutines.test)
implementation(libs.turbine)
}
}

val jvmMain by getting
val androidMain by getting
val nativeMain by creating {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.flow.Flow
import org.mobilenativefoundation.store.multicast5.ChannelManager.Message
import kotlin.coroutines.cancellation.CancellationException

internal interface ChannelManager<T> {

Expand Down Expand Up @@ -55,7 +56,11 @@ internal interface ChannelManager<T> {

suspend fun dispatchValue(value: Message.Dispatch.Value<T>) {
_awaitsDispatch = false
channel.send(value)
try {
channel.send(value)
} catch (e: CancellationException) {
// ignore
}
}

fun dispatchError(error: Throwable) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.mobilenativefoundation.store.multicast5


import app.cash.turbine.test
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals

class StoreChannelManagerTests {

@Test
fun cancelledDownstreamChannelShouldNotCancelOtherChannels() = runTest {
val coroutineScope = CoroutineScope(Dispatchers.Default)
val lockUpstream = Mutex(true)
val testMessages = listOf(1, 2, 3)
val numChannels = 20
val upstreamFlow = flow {
lockUpstream.withLock {
testMessages.onEach { emit(it) }
}
}
val channelManager = StoreChannelManager(
scope = coroutineScope,
bufferSize = 0,
upstream = upstreamFlow,
piggybackingDownstream = false,
keepUpstreamAlive = false,
onEach = { }
)
val channels = createChannels(numChannels)
val channelToBeCancelled = Channel<ChannelManager.Message.Dispatch<Int>>(Channel.UNLIMITED)
.also { channel ->
coroutineScope.launch {
channel.consumeAsFlow().test {
cancelAndIgnoreRemainingEvents()
}
}
}
coroutineScope.launch {
channels.forEach { channelManager.addDownstream(it) }
lockUpstream.unlock()
}
coroutineScope.launch {
channelManager.addDownstream(channelToBeCancelled)
}

channels.forEach { channel ->
val messagesFlow = channel.consumeAsFlow()
.filterIsInstance<ChannelManager.Message.Dispatch.Value<Int>>()
.onEach { it.delivered.complete(Unit) }

messagesFlow.test {
for (message in testMessages) {
val dispatchValue = awaitItem()
assertEquals(message, dispatchValue.value)
}
awaitComplete()
}
}
}

private fun createChannels(count: Int): List<Channel<ChannelManager.Message.Dispatch<Int>>> {
return (1..count).map { Channel(Channel.UNLIMITED) }
}
}