正在显示
12 个修改的文件
包含
115 行增加
和
117 行删除
| @@ -2,8 +2,9 @@ | @@ -2,8 +2,9 @@ | ||
| 2 | 2 | ||
| 3 | buildscript { | 3 | buildscript { |
| 4 | ext { | 4 | ext { |
| 5 | - compose_version = '1.0.5' | ||
| 6 | - kotlin_version = '1.5.31' | 5 | + compose_version = '1.1.0-rc01' |
| 6 | + compose_compiler_version = '1.1.0-rc02' | ||
| 7 | + kotlin_version = '1.6.10' | ||
| 7 | java_version = JavaVersion.VERSION_1_8 | 8 | java_version = JavaVersion.VERSION_1_8 |
| 8 | dokka_version = '1.5.0' | 9 | dokka_version = '1.5.0' |
| 9 | } | 10 | } |
| @@ -13,7 +14,7 @@ buildscript { | @@ -13,7 +14,7 @@ buildscript { | ||
| 13 | jcenter() | 14 | jcenter() |
| 14 | } | 15 | } |
| 15 | dependencies { | 16 | dependencies { |
| 16 | - classpath 'com.android.tools.build:gradle:7.0.3' | 17 | + classpath 'com.android.tools.build:gradle:7.0.4' |
| 17 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | 18 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" |
| 18 | classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" | 19 | classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" |
| 19 | classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" | 20 | classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" |
| @@ -69,7 +70,7 @@ ext { | @@ -69,7 +70,7 @@ ext { | ||
| 69 | 'service' : "com.google.auto.service:auto-service:${versions.autoService}", | 70 | 'service' : "com.google.auto.service:auto-service:${versions.autoService}", |
| 70 | 'serviceAnnotations': "com.google.auto.service:auto-service-annotations:${versions.autoService}", | 71 | 'serviceAnnotations': "com.google.auto.service:auto-service-annotations:${versions.autoService}", |
| 71 | ], | 72 | ], |
| 72 | - kotlinx_coroutines: "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2", | 73 | + kotlinx_coroutines: "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0", |
| 73 | timber : "com.github.ajalt:timberkt:1.5.1", | 74 | timber : "com.github.ajalt:timberkt:1.5.1", |
| 74 | // lint | 75 | // lint |
| 75 | lint : "com.android.tools.lint:lint:${versions.lint}", | 76 | lint : "com.android.tools.lint:lint:${versions.lint}", |
| 1 | package io.livekit.android.events | 1 | package io.livekit.android.events |
| 2 | 2 | ||
| 3 | import kotlinx.coroutines.flow.SharedFlow | 3 | import kotlinx.coroutines.flow.SharedFlow |
| 4 | -import kotlinx.coroutines.flow.collect | ||
| 5 | 4 | ||
| 6 | interface EventListenable<out T> { | 5 | interface EventListenable<out T> { |
| 7 | val events: SharedFlow<T> | 6 | val events: SharedFlow<T> |
| 8 | } | 7 | } |
| 9 | 8 | ||
| 10 | suspend inline fun <T> EventListenable<T>.collect(crossinline action: suspend (value: T) -> Unit) { | 9 | suspend inline fun <T> EventListenable<T>.collect(crossinline action: suspend (value: T) -> Unit) { |
| 11 | - return events.collect(action) | 10 | + events.collect { value -> action(value) } |
| 12 | } | 11 | } |
| 1 | +package io.livekit.android | ||
| 2 | + | ||
| 3 | +import io.livekit.android.coroutines.TestCoroutineRule | ||
| 4 | +import kotlinx.coroutines.ExperimentalCoroutinesApi | ||
| 5 | +import kotlinx.coroutines.test.TestScope | ||
| 6 | +import kotlinx.coroutines.test.runTest | ||
| 7 | +import org.junit.Rule | ||
| 8 | +import org.mockito.junit.MockitoJUnit | ||
| 9 | + | ||
| 10 | +@ExperimentalCoroutinesApi | ||
| 11 | +abstract class BaseTest { | ||
| 12 | + | ||
| 13 | + @get:Rule | ||
| 14 | + var mockitoRule = MockitoJUnit.rule() | ||
| 15 | + | ||
| 16 | + @get:Rule | ||
| 17 | + var coroutineRule = TestCoroutineRule() | ||
| 18 | + | ||
| 19 | + @ExperimentalCoroutinesApi | ||
| 20 | + fun runTest(testBody: suspend TestScope.() -> Unit) = coroutineRule.scope.runTest(testBody = testBody) | ||
| 21 | +} |
| @@ -2,7 +2,6 @@ package io.livekit.android | @@ -2,7 +2,6 @@ package io.livekit.android | ||
| 2 | 2 | ||
| 3 | import android.content.Context | 3 | import android.content.Context |
| 4 | import androidx.test.core.app.ApplicationProvider | 4 | import androidx.test.core.app.ApplicationProvider |
| 5 | -import io.livekit.android.coroutines.TestCoroutineRule | ||
| 6 | import io.livekit.android.mock.MockWebSocketFactory | 5 | import io.livekit.android.mock.MockWebSocketFactory |
| 7 | import io.livekit.android.mock.dagger.DaggerTestLiveKitComponent | 6 | import io.livekit.android.mock.dagger.DaggerTestLiveKitComponent |
| 8 | import io.livekit.android.mock.dagger.TestCoroutinesModule | 7 | import io.livekit.android.mock.dagger.TestCoroutinesModule |
| @@ -12,22 +11,13 @@ import io.livekit.android.room.SignalClientTest | @@ -12,22 +11,13 @@ import io.livekit.android.room.SignalClientTest | ||
| 12 | import io.livekit.android.util.toOkioByteString | 11 | import io.livekit.android.util.toOkioByteString |
| 13 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 12 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 14 | import kotlinx.coroutines.launch | 13 | import kotlinx.coroutines.launch |
| 15 | -import kotlinx.coroutines.test.runBlockingTest | ||
| 16 | import okhttp3.Protocol | 14 | import okhttp3.Protocol |
| 17 | import okhttp3.Request | 15 | import okhttp3.Request |
| 18 | import okhttp3.Response | 16 | import okhttp3.Response |
| 19 | import org.junit.Before | 17 | import org.junit.Before |
| 20 | -import org.junit.Rule | ||
| 21 | -import org.mockito.junit.MockitoJUnit | ||
| 22 | 18 | ||
| 23 | @ExperimentalCoroutinesApi | 19 | @ExperimentalCoroutinesApi |
| 24 | -abstract class MockE2ETest { | ||
| 25 | - | ||
| 26 | - @get:Rule | ||
| 27 | - var mockitoRule = MockitoJUnit.rule() | ||
| 28 | - | ||
| 29 | - @get:Rule | ||
| 30 | - var coroutineRule = TestCoroutineRule() | 20 | +abstract class MockE2ETest : BaseTest() { |
| 31 | 21 | ||
| 32 | internal lateinit var component: TestLiveKitComponent | 22 | internal lateinit var component: TestLiveKitComponent |
| 33 | lateinit var context: Context | 23 | lateinit var context: Context |
| @@ -46,7 +36,7 @@ abstract class MockE2ETest { | @@ -46,7 +36,7 @@ abstract class MockE2ETest { | ||
| 46 | wsFactory = component.websocketFactory() | 36 | wsFactory = component.websocketFactory() |
| 47 | } | 37 | } |
| 48 | 38 | ||
| 49 | - fun connect() { | 39 | + suspend fun connect() { |
| 50 | val job = coroutineRule.scope.launch { | 40 | val job = coroutineRule.scope.launch { |
| 51 | room.connect( | 41 | room.connect( |
| 52 | url = SignalClientTest.EXAMPLE_URL, | 42 | url = SignalClientTest.EXAMPLE_URL, |
| @@ -56,11 +46,7 @@ abstract class MockE2ETest { | @@ -56,11 +46,7 @@ abstract class MockE2ETest { | ||
| 56 | wsFactory.listener.onOpen(wsFactory.ws, createOpenResponse(wsFactory.request)) | 46 | wsFactory.listener.onOpen(wsFactory.ws, createOpenResponse(wsFactory.request)) |
| 57 | wsFactory.listener.onMessage(wsFactory.ws, SignalClientTest.JOIN.toOkioByteString()) | 47 | wsFactory.listener.onMessage(wsFactory.ws, SignalClientTest.JOIN.toOkioByteString()) |
| 58 | 48 | ||
| 59 | - // PeerTransport negotiation is on a debounce delay. | ||
| 60 | - coroutineRule.dispatcher.advanceTimeBy(1000L) | ||
| 61 | - runBlockingTest { | ||
| 62 | - job.join() | ||
| 63 | - } | 49 | + job.join() |
| 64 | } | 50 | } |
| 65 | 51 | ||
| 66 | fun createOpenResponse(request: Request): Response { | 52 | fun createOpenResponse(request: Request): Response { |
| 1 | package io.livekit.android.coroutines | 1 | package io.livekit.android.coroutines |
| 2 | 2 | ||
| 3 | +import kotlinx.coroutines.Dispatchers | ||
| 3 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 4 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 4 | -import kotlinx.coroutines.test.TestCoroutineDispatcher | ||
| 5 | -import kotlinx.coroutines.test.TestCoroutineScope | 5 | +import kotlinx.coroutines.test.TestScope |
| 6 | +import kotlinx.coroutines.test.UnconfinedTestDispatcher | ||
| 7 | +import kotlinx.coroutines.test.resetMain | ||
| 8 | +import kotlinx.coroutines.test.setMain | ||
| 9 | +import org.junit.Assert | ||
| 6 | import org.junit.rules.TestRule | 10 | import org.junit.rules.TestRule |
| 7 | import org.junit.runner.Description | 11 | import org.junit.runner.Description |
| 8 | import org.junit.runners.model.Statement | 12 | import org.junit.runners.model.Statement |
| 9 | 13 | ||
| 10 | @OptIn(ExperimentalCoroutinesApi::class) | 14 | @OptIn(ExperimentalCoroutinesApi::class) |
| 11 | class TestCoroutineRule : TestRule { | 15 | class TestCoroutineRule : TestRule { |
| 12 | - val dispatcher = TestCoroutineDispatcher() | ||
| 13 | - val scope = TestCoroutineScope(dispatcher) | 16 | + val dispatcher = UnconfinedTestDispatcher() |
| 17 | + val scope = TestScope(dispatcher) | ||
| 14 | 18 | ||
| 15 | override fun apply(base: Statement, description: Description?) = object : Statement() { | 19 | override fun apply(base: Statement, description: Description?) = object : Statement() { |
| 16 | @Throws(Throwable::class) | 20 | @Throws(Throwable::class) |
| 17 | override fun evaluate() { | 21 | override fun evaluate() { |
| 22 | + Dispatchers.setMain(dispatcher) | ||
| 18 | base.evaluate() | 23 | base.evaluate() |
| 19 | - scope.cleanupTestCoroutines() | 24 | + val timeAfterTest = dispatcher.scheduler.currentTime |
| 25 | + dispatcher.scheduler.advanceUntilIdle() // run the remaining tasks | ||
| 26 | + Assert.assertEquals( | ||
| 27 | + timeAfterTest, | ||
| 28 | + dispatcher.scheduler.currentTime | ||
| 29 | + ) // will fail if there were tasks scheduled at a later moment | ||
| 30 | + Dispatchers.resetMain() | ||
| 20 | } | 31 | } |
| 21 | } | 32 | } |
| 22 | } | 33 | } |
| @@ -6,7 +6,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi | @@ -6,7 +6,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi | ||
| 6 | import kotlinx.coroutines.async | 6 | import kotlinx.coroutines.async |
| 7 | import kotlinx.coroutines.flow.Flow | 7 | import kotlinx.coroutines.flow.Flow |
| 8 | import kotlinx.coroutines.flow.MutableStateFlow | 8 | import kotlinx.coroutines.flow.MutableStateFlow |
| 9 | -import kotlinx.coroutines.test.runBlockingTest | ||
| 10 | 9 | ||
| 11 | open class FlowCollector<T>( | 10 | open class FlowCollector<T>( |
| 12 | private val flow: Flow<T>, | 11 | private val flow: Flow<T>, |
| @@ -21,13 +20,9 @@ open class FlowCollector<T>( | @@ -21,13 +20,9 @@ open class FlowCollector<T>( | ||
| 21 | * Stop collecting events. returns the events collected. | 20 | * Stop collecting events. returns the events collected. |
| 22 | */ | 21 | */ |
| 23 | @OptIn(ExperimentalCoroutinesApi::class) | 22 | @OptIn(ExperimentalCoroutinesApi::class) |
| 24 | - fun stopCollecting(): List<T> { | 23 | + suspend fun stopCollecting(): List<T> { |
| 25 | signal.compareAndSet(null, Unit) | 24 | signal.compareAndSet(null, Unit) |
| 26 | - var events: List<T> = emptyList() | ||
| 27 | - runBlockingTest { | ||
| 28 | - events = collectEventsDeferred.await() | ||
| 29 | - } | ||
| 30 | - return events | 25 | + return collectEventsDeferred.await() |
| 31 | } | 26 | } |
| 32 | 27 | ||
| 33 | } | 28 | } |
| @@ -6,7 +6,7 @@ import io.livekit.android.mock.MockWebSocket | @@ -6,7 +6,7 @@ import io.livekit.android.mock.MockWebSocket | ||
| 6 | import io.livekit.android.util.LoggingRule | 6 | import io.livekit.android.util.LoggingRule |
| 7 | import io.livekit.android.util.toPBByteString | 7 | import io.livekit.android.util.toPBByteString |
| 8 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 8 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 9 | -import kotlinx.coroutines.test.runBlockingTest | 9 | +import kotlinx.coroutines.test.runTest |
| 10 | import livekit.LivekitRtc | 10 | import livekit.LivekitRtc |
| 11 | import org.junit.Assert | 11 | import org.junit.Assert |
| 12 | import org.junit.Before | 12 | import org.junit.Before |
| @@ -34,7 +34,7 @@ class RTCEngineMockE2ETest : MockE2ETest() { | @@ -34,7 +34,7 @@ class RTCEngineMockE2ETest : MockE2ETest() { | ||
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | @Test | 36 | @Test |
| 37 | - fun iceSubscriberConnect() = runBlockingTest { | 37 | + fun iceSubscriberConnect() = runTest { |
| 38 | connect() | 38 | connect() |
| 39 | 39 | ||
| 40 | val remoteOffer = SessionDescription(SessionDescription.Type.OFFER, "remote_offer") | 40 | val remoteOffer = SessionDescription(SessionDescription.Type.OFFER, "remote_offer") |
| @@ -59,7 +59,7 @@ class RTCEngineMockE2ETest : MockE2ETest() { | @@ -59,7 +59,7 @@ class RTCEngineMockE2ETest : MockE2ETest() { | ||
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | @Test | 61 | @Test |
| 62 | - fun reconnectOnFailure() = runBlockingTest { | 62 | + fun reconnectOnFailure() = runTest { |
| 63 | connect() | 63 | connect() |
| 64 | val oldWs = wsFactory.ws | 64 | val oldWs = wsFactory.ws |
| 65 | wsFactory.listener.onFailure(oldWs, Exception(), null) | 65 | wsFactory.listener.onFailure(oldWs, Exception(), null) |
| @@ -13,7 +13,6 @@ import io.livekit.android.room.track.Track | @@ -13,7 +13,6 @@ import io.livekit.android.room.track.Track | ||
| 13 | import io.livekit.android.util.toOkioByteString | 13 | import io.livekit.android.util.toOkioByteString |
| 14 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 14 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 15 | import kotlinx.coroutines.launch | 15 | import kotlinx.coroutines.launch |
| 16 | -import kotlinx.coroutines.test.runBlockingTest | ||
| 17 | import org.junit.Assert | 16 | import org.junit.Assert |
| 18 | import org.junit.Test | 17 | import org.junit.Test |
| 19 | import org.junit.runner.RunWith | 18 | import org.junit.runner.RunWith |
| @@ -25,12 +24,12 @@ import org.robolectric.RobolectricTestRunner | @@ -25,12 +24,12 @@ import org.robolectric.RobolectricTestRunner | ||
| 25 | class RoomMockE2ETest : MockE2ETest() { | 24 | class RoomMockE2ETest : MockE2ETest() { |
| 26 | 25 | ||
| 27 | @Test | 26 | @Test |
| 28 | - fun connectTest() { | 27 | + fun connectTest() = runTest { |
| 29 | connect() | 28 | connect() |
| 30 | } | 29 | } |
| 31 | 30 | ||
| 32 | @Test | 31 | @Test |
| 33 | - fun connectFailureProperlyContinues() { | 32 | + fun connectFailureProperlyContinues() = runTest { |
| 34 | 33 | ||
| 35 | var didThrowException = false | 34 | var didThrowException = false |
| 36 | val job = coroutineRule.scope.launch { | 35 | val job = coroutineRule.scope.launch { |
| @@ -46,15 +45,13 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -46,15 +45,13 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 46 | 45 | ||
| 47 | wsFactory.listener.onFailure(wsFactory.ws, Exception(), null) | 46 | wsFactory.listener.onFailure(wsFactory.ws, Exception(), null) |
| 48 | 47 | ||
| 49 | - runBlockingTest { | ||
| 50 | - job.join() | ||
| 51 | - } | 48 | + job.join() |
| 52 | 49 | ||
| 53 | Assert.assertTrue(didThrowException) | 50 | Assert.assertTrue(didThrowException) |
| 54 | } | 51 | } |
| 55 | 52 | ||
| 56 | @Test | 53 | @Test |
| 57 | - fun roomUpdateTest() { | 54 | + fun roomUpdateTest() = runTest { |
| 58 | connect() | 55 | connect() |
| 59 | val eventCollector = EventCollector(room.events, coroutineRule.scope) | 56 | val eventCollector = EventCollector(room.events, coroutineRule.scope) |
| 60 | wsFactory.listener.onMessage(wsFactory.ws, SignalClientTest.ROOM_UPDATE.toOkioByteString()) | 57 | wsFactory.listener.onMessage(wsFactory.ws, SignalClientTest.ROOM_UPDATE.toOkioByteString()) |
| @@ -69,7 +66,7 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -69,7 +66,7 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 69 | } | 66 | } |
| 70 | 67 | ||
| 71 | @Test | 68 | @Test |
| 72 | - fun connectionQualityUpdateTest() { | 69 | + fun connectionQualityUpdateTest() = runTest { |
| 73 | connect() | 70 | connect() |
| 74 | val eventCollector = EventCollector(room.events, coroutineRule.scope) | 71 | val eventCollector = EventCollector(room.events, coroutineRule.scope) |
| 75 | wsFactory.listener.onMessage( | 72 | wsFactory.listener.onMessage( |
| @@ -84,7 +81,7 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -84,7 +81,7 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 84 | } | 81 | } |
| 85 | 82 | ||
| 86 | @Test | 83 | @Test |
| 87 | - fun participantConnected() { | 84 | + fun participantConnected() = runTest { |
| 88 | connect() | 85 | connect() |
| 89 | 86 | ||
| 90 | val eventCollector = EventCollector(room.events, coroutineRule.scope) | 87 | val eventCollector = EventCollector(room.events, coroutineRule.scope) |
| @@ -99,7 +96,7 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -99,7 +96,7 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 99 | } | 96 | } |
| 100 | 97 | ||
| 101 | @Test | 98 | @Test |
| 102 | - fun participantDisconnected() { | 99 | + fun participantDisconnected() = runTest { |
| 103 | connect() | 100 | connect() |
| 104 | wsFactory.listener.onMessage( | 101 | wsFactory.listener.onMessage( |
| 105 | wsFactory.ws, | 102 | wsFactory.ws, |
| @@ -118,7 +115,7 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -118,7 +115,7 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 118 | } | 115 | } |
| 119 | 116 | ||
| 120 | @Test | 117 | @Test |
| 121 | - fun onActiveSpeakersChanged() { | 118 | + fun onActiveSpeakersChanged() = runTest { |
| 122 | connect() | 119 | connect() |
| 123 | 120 | ||
| 124 | val eventCollector = EventCollector(room.events, coroutineRule.scope) | 121 | val eventCollector = EventCollector(room.events, coroutineRule.scope) |
| @@ -133,7 +130,7 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -133,7 +130,7 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 133 | } | 130 | } |
| 134 | 131 | ||
| 135 | @Test | 132 | @Test |
| 136 | - fun participantMetadataChanged() { | 133 | + fun participantMetadataChanged() = runTest { |
| 137 | connect() | 134 | connect() |
| 138 | 135 | ||
| 139 | wsFactory.listener.onMessage( | 136 | wsFactory.listener.onMessage( |
| @@ -153,7 +150,7 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -153,7 +150,7 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 153 | } | 150 | } |
| 154 | 151 | ||
| 155 | @Test | 152 | @Test |
| 156 | - fun trackStreamStateChanged() { | 153 | + fun trackStreamStateChanged() = runTest { |
| 157 | connect() | 154 | connect() |
| 158 | 155 | ||
| 159 | wsFactory.listener.onMessage( | 156 | wsFactory.listener.onMessage( |
| @@ -189,7 +186,7 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -189,7 +186,7 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 189 | } | 186 | } |
| 190 | 187 | ||
| 191 | @Test | 188 | @Test |
| 192 | - fun trackSubscriptionPermissionChanged() { | 189 | + fun trackSubscriptionPermissionChanged() = runTest { |
| 193 | connect() | 190 | connect() |
| 194 | 191 | ||
| 195 | wsFactory.listener.onMessage( | 192 | wsFactory.listener.onMessage( |
| @@ -224,7 +221,7 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -224,7 +221,7 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 224 | } | 221 | } |
| 225 | 222 | ||
| 226 | @Test | 223 | @Test |
| 227 | - fun onConnectionAvailableWillReconnect() { | 224 | + fun onConnectionAvailableWillReconnect() = runTest { |
| 228 | connect() | 225 | connect() |
| 229 | val eventCollector = EventCollector(room.events, coroutineRule.scope) | 226 | val eventCollector = EventCollector(room.events, coroutineRule.scope) |
| 230 | val network = Mockito.mock(Network::class.java) | 227 | val network = Mockito.mock(Network::class.java) |
| @@ -237,7 +234,7 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -237,7 +234,7 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 237 | } | 234 | } |
| 238 | 235 | ||
| 239 | @Test | 236 | @Test |
| 240 | - fun leave() { | 237 | + fun leave() = runTest { |
| 241 | connect() | 238 | connect() |
| 242 | val eventCollector = EventCollector(room.events, coroutineRule.scope) | 239 | val eventCollector = EventCollector(room.events, coroutineRule.scope) |
| 243 | wsFactory.listener.onMessage( | 240 | wsFactory.listener.onMessage( |
| @@ -8,8 +8,8 @@ import io.livekit.android.events.EventCollector | @@ -8,8 +8,8 @@ import io.livekit.android.events.EventCollector | ||
| 8 | import io.livekit.android.events.RoomEvent | 8 | import io.livekit.android.events.RoomEvent |
| 9 | import io.livekit.android.mock.MockEglBase | 9 | import io.livekit.android.mock.MockEglBase |
| 10 | import io.livekit.android.room.participant.LocalParticipant | 10 | import io.livekit.android.room.participant.LocalParticipant |
| 11 | -import kotlinx.coroutines.* | ||
| 12 | -import kotlinx.coroutines.test.runBlockingTest | 11 | +import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 12 | +import kotlinx.coroutines.test.runTest | ||
| 13 | import livekit.LivekitModels | 13 | import livekit.LivekitModels |
| 14 | import org.junit.Assert | 14 | import org.junit.Assert |
| 15 | import org.junit.Before | 15 | import org.junit.Before |
| @@ -32,6 +32,7 @@ class RoomTest { | @@ -32,6 +32,7 @@ class RoomTest { | ||
| 32 | 32 | ||
| 33 | @get:Rule | 33 | @get:Rule |
| 34 | var mockitoRule = MockitoJUnit.rule() | 34 | var mockitoRule = MockitoJUnit.rule() |
| 35 | + | ||
| 35 | @get:Rule | 36 | @get:Rule |
| 36 | var coroutineRule = TestCoroutineRule() | 37 | var coroutineRule = TestCoroutineRule() |
| 37 | 38 | ||
| @@ -64,7 +65,7 @@ class RoomTest { | @@ -64,7 +65,7 @@ class RoomTest { | ||
| 64 | ) | 65 | ) |
| 65 | } | 66 | } |
| 66 | 67 | ||
| 67 | - fun connect() { | 68 | + suspend fun connect() { |
| 68 | rtcEngine.stub { | 69 | rtcEngine.stub { |
| 69 | onBlocking { rtcEngine.join(any(), any(), anyOrNull()) } | 70 | onBlocking { rtcEngine.join(any(), any(), anyOrNull()) } |
| 70 | .doReturn(SignalClientTest.JOIN.join) | 71 | .doReturn(SignalClientTest.JOIN.join) |
| @@ -73,24 +74,20 @@ class RoomTest { | @@ -73,24 +74,20 @@ class RoomTest { | ||
| 73 | onBlocking { rtcEngine.client } | 74 | onBlocking { rtcEngine.client } |
| 74 | .doReturn(Mockito.mock(SignalClient::class.java)) | 75 | .doReturn(Mockito.mock(SignalClient::class.java)) |
| 75 | } | 76 | } |
| 76 | - val job = coroutineRule.scope.launch { | ||
| 77 | - room.connect( | ||
| 78 | - url = SignalClientTest.EXAMPLE_URL, | ||
| 79 | - token = "", | ||
| 80 | - ) | ||
| 81 | - } | ||
| 82 | - runBlockingTest { | ||
| 83 | - job.join() | ||
| 84 | - } | 77 | + |
| 78 | + room.connect( | ||
| 79 | + url = SignalClientTest.EXAMPLE_URL, | ||
| 80 | + token = "", | ||
| 81 | + ) | ||
| 85 | } | 82 | } |
| 86 | 83 | ||
| 87 | @Test | 84 | @Test |
| 88 | - fun connectTest() { | 85 | + fun connectTest() = runTest { |
| 89 | connect() | 86 | connect() |
| 90 | } | 87 | } |
| 91 | 88 | ||
| 92 | @Test | 89 | @Test |
| 93 | - fun onConnectionAvailableWillReconnect() { | 90 | + fun onConnectionAvailableWillReconnect() = runTest { |
| 94 | connect() | 91 | connect() |
| 95 | 92 | ||
| 96 | val network = Mockito.mock(Network::class.java) | 93 | val network = Mockito.mock(Network::class.java) |
| @@ -100,7 +97,7 @@ class RoomTest { | @@ -100,7 +97,7 @@ class RoomTest { | ||
| 100 | } | 97 | } |
| 101 | 98 | ||
| 102 | @Test | 99 | @Test |
| 103 | - fun onDisconnect() { | 100 | + fun onDisconnect() = runTest { |
| 104 | connect() | 101 | connect() |
| 105 | 102 | ||
| 106 | val eventCollector = EventCollector(room.events, coroutineRule.scope) | 103 | val eventCollector = EventCollector(room.events, coroutineRule.scope) |
| 1 | package io.livekit.android.room | 1 | package io.livekit.android.room |
| 2 | 2 | ||
| 3 | import com.google.protobuf.util.JsonFormat | 3 | import com.google.protobuf.util.JsonFormat |
| 4 | +import io.livekit.android.BaseTest | ||
| 4 | import io.livekit.android.mock.MockWebSocketFactory | 5 | import io.livekit.android.mock.MockWebSocketFactory |
| 5 | import io.livekit.android.mock.TestData | 6 | import io.livekit.android.mock.TestData |
| 6 | import io.livekit.android.util.toOkioByteString | 7 | import io.livekit.android.util.toOkioByteString |
| 7 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 8 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 8 | import kotlinx.coroutines.async | 9 | import kotlinx.coroutines.async |
| 9 | -import kotlinx.coroutines.test.TestCoroutineDispatcher | ||
| 10 | -import kotlinx.coroutines.test.TestCoroutineScope | ||
| 11 | -import kotlinx.coroutines.test.runBlockingTest | ||
| 12 | import kotlinx.serialization.json.Json | 10 | import kotlinx.serialization.json.Json |
| 13 | import livekit.LivekitModels | 11 | import livekit.LivekitModels |
| 14 | import livekit.LivekitRtc | 12 | import livekit.LivekitRtc |
| 15 | import okhttp3.* | 13 | import okhttp3.* |
| 16 | -import org.junit.After | ||
| 17 | import org.junit.Assert | 14 | import org.junit.Assert |
| 18 | import org.junit.Before | 15 | import org.junit.Before |
| 19 | import org.junit.Test | 16 | import org.junit.Test |
| 17 | +import org.mockito.Mock | ||
| 20 | import org.mockito.Mockito | 18 | import org.mockito.Mockito |
| 21 | import org.mockito.kotlin.any | 19 | import org.mockito.kotlin.any |
| 22 | import org.mockito.kotlin.argThat | 20 | import org.mockito.kotlin.argThat |
| 23 | import org.webrtc.SessionDescription | 21 | import org.webrtc.SessionDescription |
| 24 | 22 | ||
| 25 | @ExperimentalCoroutinesApi | 23 | @ExperimentalCoroutinesApi |
| 26 | -class SignalClientTest { | 24 | +class SignalClientTest : BaseTest() { |
| 27 | 25 | ||
| 28 | lateinit var wsFactory: MockWebSocketFactory | 26 | lateinit var wsFactory: MockWebSocketFactory |
| 29 | lateinit var client: SignalClient | 27 | lateinit var client: SignalClient |
| 28 | + | ||
| 29 | + @Mock | ||
| 30 | lateinit var listener: SignalClient.Listener | 30 | lateinit var listener: SignalClient.Listener |
| 31 | - lateinit var okHttpClient: OkHttpClient | ||
| 32 | 31 | ||
| 33 | - lateinit var coroutineDispatcher: TestCoroutineDispatcher | ||
| 34 | - lateinit var coroutineScope: TestCoroutineScope | 32 | + @Mock |
| 33 | + lateinit var okHttpClient: OkHttpClient | ||
| 35 | 34 | ||
| 36 | @Before | 35 | @Before |
| 37 | fun setup() { | 36 | fun setup() { |
| 38 | - coroutineDispatcher = TestCoroutineDispatcher() | ||
| 39 | - coroutineScope = TestCoroutineScope(coroutineDispatcher) | ||
| 40 | wsFactory = MockWebSocketFactory() | 37 | wsFactory = MockWebSocketFactory() |
| 41 | - okHttpClient = Mockito.mock(OkHttpClient::class.java) | ||
| 42 | client = SignalClient( | 38 | client = SignalClient( |
| 43 | wsFactory, | 39 | wsFactory, |
| 44 | JsonFormat.parser(), | 40 | JsonFormat.parser(), |
| @@ -46,17 +42,11 @@ class SignalClientTest { | @@ -46,17 +42,11 @@ class SignalClientTest { | ||
| 46 | Json, | 42 | Json, |
| 47 | useJson = false, | 43 | useJson = false, |
| 48 | okHttpClient = okHttpClient, | 44 | okHttpClient = okHttpClient, |
| 49 | - ioDispatcher = coroutineDispatcher | 45 | + ioDispatcher = coroutineRule.dispatcher |
| 50 | ) | 46 | ) |
| 51 | - listener = Mockito.mock(SignalClient.Listener::class.java) | ||
| 52 | client.listener = listener | 47 | client.listener = listener |
| 53 | } | 48 | } |
| 54 | 49 | ||
| 55 | - @After | ||
| 56 | - fun tearDown() { | ||
| 57 | - coroutineScope.cleanupTestCoroutines() | ||
| 58 | - } | ||
| 59 | - | ||
| 60 | private fun createOpenResponse(request: Request): Response { | 50 | private fun createOpenResponse(request: Request): Response { |
| 61 | return Response.Builder() | 51 | return Response.Builder() |
| 62 | .request(request) | 52 | .request(request) |
| @@ -75,36 +65,35 @@ class SignalClientTest { | @@ -75,36 +65,35 @@ class SignalClientTest { | ||
| 75 | } | 65 | } |
| 76 | 66 | ||
| 77 | @Test | 67 | @Test |
| 78 | - fun joinAndResponse() { | ||
| 79 | - val job = coroutineScope.async { | 68 | + fun joinAndResponse() = runTest { |
| 69 | + println("dispatcher = ${this.coroutineContext}") | ||
| 70 | + val job = async { | ||
| 80 | client.join(EXAMPLE_URL, "") | 71 | client.join(EXAMPLE_URL, "") |
| 81 | } | 72 | } |
| 82 | 73 | ||
| 83 | connectWebsocketAndJoin() | 74 | connectWebsocketAndJoin() |
| 84 | 75 | ||
| 85 | - runBlockingTest { | ||
| 86 | - val response = job.await() | ||
| 87 | - Assert.assertEquals(response, JOIN.join) | ||
| 88 | - } | 76 | + val response = job.await() |
| 77 | + Assert.assertEquals(true, client.isConnected) | ||
| 78 | + Assert.assertEquals(response, JOIN.join) | ||
| 89 | } | 79 | } |
| 90 | 80 | ||
| 91 | @Test | 81 | @Test |
| 92 | - fun reconnect() { | ||
| 93 | - val job = coroutineScope.async { | 82 | + fun reconnect() = runTest { |
| 83 | + val job = async { | ||
| 94 | client.reconnect(EXAMPLE_URL, "") | 84 | client.reconnect(EXAMPLE_URL, "") |
| 95 | } | 85 | } |
| 96 | 86 | ||
| 97 | client.onOpen(wsFactory.ws, createOpenResponse(wsFactory.request)) | 87 | client.onOpen(wsFactory.ws, createOpenResponse(wsFactory.request)) |
| 98 | 88 | ||
| 99 | - runBlockingTest { | ||
| 100 | - job.await() | ||
| 101 | - } | 89 | + job.await() |
| 90 | + Assert.assertEquals(true, client.isConnected) | ||
| 102 | } | 91 | } |
| 103 | 92 | ||
| 104 | @Test | 93 | @Test |
| 105 | - fun joinFailure() { | 94 | + fun joinFailure() = runTest { |
| 106 | var failed = false | 95 | var failed = false |
| 107 | - val job = coroutineScope.async { | 96 | + val job = async { |
| 108 | try { | 97 | try { |
| 109 | client.join(EXAMPLE_URL, "") | 98 | client.join(EXAMPLE_URL, "") |
| 110 | } catch (e: Exception) { | 99 | } catch (e: Exception) { |
| @@ -113,34 +102,34 @@ class SignalClientTest { | @@ -113,34 +102,34 @@ class SignalClientTest { | ||
| 113 | } | 102 | } |
| 114 | 103 | ||
| 115 | client.onFailure(wsFactory.ws, Exception(), null) | 104 | client.onFailure(wsFactory.ws, Exception(), null) |
| 116 | - runBlockingTest { job.await() } | 105 | + job.await() |
| 117 | 106 | ||
| 118 | Assert.assertTrue(failed) | 107 | Assert.assertTrue(failed) |
| 119 | } | 108 | } |
| 120 | 109 | ||
| 121 | @Test | 110 | @Test |
| 122 | - fun listenerNotCalledUntilOnReady() { | ||
| 123 | - val job = coroutineScope.async { | 111 | + fun listenerNotCalledUntilOnReady() = runTest { |
| 112 | + val job = async { | ||
| 124 | client.join(EXAMPLE_URL, "") | 113 | client.join(EXAMPLE_URL, "") |
| 125 | } | 114 | } |
| 126 | 115 | ||
| 127 | connectWebsocketAndJoin() | 116 | connectWebsocketAndJoin() |
| 128 | client.onMessage(wsFactory.ws, OFFER.toOkioByteString()) | 117 | client.onMessage(wsFactory.ws, OFFER.toOkioByteString()) |
| 129 | 118 | ||
| 130 | - runBlockingTest { job.await() } | 119 | + job.await() |
| 131 | 120 | ||
| 132 | Mockito.verifyNoInteractions(listener) | 121 | Mockito.verifyNoInteractions(listener) |
| 133 | } | 122 | } |
| 134 | 123 | ||
| 135 | @Test | 124 | @Test |
| 136 | - fun listenerCalledAfterOnReady() { | ||
| 137 | - val job = coroutineScope.async { | 125 | + fun listenerCalledAfterOnReady() = runTest { |
| 126 | + val job = async { | ||
| 138 | client.join(EXAMPLE_URL, "") | 127 | client.join(EXAMPLE_URL, "") |
| 139 | } | 128 | } |
| 140 | connectWebsocketAndJoin() | 129 | connectWebsocketAndJoin() |
| 141 | client.onMessage(wsFactory.ws, OFFER.toOkioByteString()) | 130 | client.onMessage(wsFactory.ws, OFFER.toOkioByteString()) |
| 142 | 131 | ||
| 143 | - runBlockingTest { job.await() } | 132 | + job.await() |
| 144 | client.onReady() | 133 | client.onReady() |
| 145 | Mockito.verify(listener) | 134 | Mockito.verify(listener) |
| 146 | .onOffer(argThat { type == SessionDescription.Type.OFFER && description == OFFER.offer.sdp }) | 135 | .onOffer(argThat { type == SessionDescription.Type.OFFER && description == OFFER.offer.sdp }) |
| @@ -151,12 +140,12 @@ class SignalClientTest { | @@ -151,12 +140,12 @@ class SignalClientTest { | ||
| 151 | * [WebSocketListener.onClosed]. Ensure that listener is called properly. | 140 | * [WebSocketListener.onClosed]. Ensure that listener is called properly. |
| 152 | */ | 141 | */ |
| 153 | @Test | 142 | @Test |
| 154 | - fun listenerNotifiedAfterFailure() { | ||
| 155 | - val job = coroutineScope.async { | 143 | + fun listenerNotifiedAfterFailure() = runTest { |
| 144 | + val job = async { | ||
| 156 | client.join(EXAMPLE_URL, "") | 145 | client.join(EXAMPLE_URL, "") |
| 157 | } | 146 | } |
| 158 | connectWebsocketAndJoin() | 147 | connectWebsocketAndJoin() |
| 159 | - runBlockingTest { job.await() } | 148 | + job.await() |
| 160 | 149 | ||
| 161 | client.onFailure(wsFactory.ws, Exception(), null) | 150 | client.onFailure(wsFactory.ws, Exception(), null) |
| 162 | 151 |
| @@ -4,6 +4,8 @@ import io.livekit.android.coroutines.TestCoroutineRule | @@ -4,6 +4,8 @@ import io.livekit.android.coroutines.TestCoroutineRule | ||
| 4 | import io.livekit.android.events.EventCollector | 4 | import io.livekit.android.events.EventCollector |
| 5 | import io.livekit.android.events.ParticipantEvent | 5 | import io.livekit.android.events.ParticipantEvent |
| 6 | import io.livekit.android.room.track.TrackPublication | 6 | import io.livekit.android.room.track.TrackPublication |
| 7 | +import kotlinx.coroutines.ExperimentalCoroutinesApi | ||
| 8 | +import kotlinx.coroutines.test.runTest | ||
| 7 | import livekit.LivekitModels | 9 | import livekit.LivekitModels |
| 8 | import org.junit.Assert.assertEquals | 10 | import org.junit.Assert.assertEquals |
| 9 | import org.junit.Assert.assertTrue | 11 | import org.junit.Assert.assertTrue |
| @@ -11,6 +13,7 @@ import org.junit.Before | @@ -11,6 +13,7 @@ import org.junit.Before | ||
| 11 | import org.junit.Rule | 13 | import org.junit.Rule |
| 12 | import org.junit.Test | 14 | import org.junit.Test |
| 13 | 15 | ||
| 16 | +@ExperimentalCoroutinesApi | ||
| 14 | class ParticipantTest { | 17 | class ParticipantTest { |
| 15 | 18 | ||
| 16 | @get:Rule | 19 | @get:Rule |
| @@ -24,7 +27,7 @@ class ParticipantTest { | @@ -24,7 +27,7 @@ class ParticipantTest { | ||
| 24 | } | 27 | } |
| 25 | 28 | ||
| 26 | @Test | 29 | @Test |
| 27 | - fun updateFromInfo() { | 30 | + fun updateFromInfo() = runTest { |
| 28 | participant.updateFromInfo(INFO) | 31 | participant.updateFromInfo(INFO) |
| 29 | 32 | ||
| 30 | assertTrue(participant.hasInfo) | 33 | assertTrue(participant.hasInfo) |
| @@ -36,7 +39,7 @@ class ParticipantTest { | @@ -36,7 +39,7 @@ class ParticipantTest { | ||
| 36 | } | 39 | } |
| 37 | 40 | ||
| 38 | @Test | 41 | @Test |
| 39 | - fun setMetadataCallsListeners() { | 42 | + fun setMetadataCallsListeners() = runTest { |
| 40 | class MetadataListener : ParticipantListener { | 43 | class MetadataListener : ParticipantListener { |
| 41 | var wasCalled = false | 44 | var wasCalled = false |
| 42 | lateinit var participantValue: Participant | 45 | lateinit var participantValue: Participant |
| @@ -69,7 +72,7 @@ class ParticipantTest { | @@ -69,7 +72,7 @@ class ParticipantTest { | ||
| 69 | } | 72 | } |
| 70 | 73 | ||
| 71 | @Test | 74 | @Test |
| 72 | - fun setMetadataChangedEvent() { | 75 | + fun setMetadataChangedEvent() = runTest { |
| 73 | val eventCollector = EventCollector(participant.events, coroutineRule.scope) | 76 | val eventCollector = EventCollector(participant.events, coroutineRule.scope) |
| 74 | val prevMetadata = participant.metadata | 77 | val prevMetadata = participant.metadata |
| 75 | val metadata = "metadata" | 78 | val metadata = "metadata" |
| @@ -87,7 +90,7 @@ class ParticipantTest { | @@ -87,7 +90,7 @@ class ParticipantTest { | ||
| 87 | } | 90 | } |
| 88 | 91 | ||
| 89 | @Test | 92 | @Test |
| 90 | - fun setIsSpeakingChangedEvent() { | 93 | + fun setIsSpeakingChangedEvent() = runTest { |
| 91 | val eventCollector = EventCollector(participant.events, coroutineRule.scope) | 94 | val eventCollector = EventCollector(participant.events, coroutineRule.scope) |
| 92 | val newIsSpeaking = !participant.isSpeaking | 95 | val newIsSpeaking = !participant.isSpeaking |
| 93 | participant.isSpeaking = newIsSpeaking | 96 | participant.isSpeaking = newIsSpeaking |
| @@ -104,7 +107,7 @@ class ParticipantTest { | @@ -104,7 +107,7 @@ class ParticipantTest { | ||
| 104 | } | 107 | } |
| 105 | 108 | ||
| 106 | @Test | 109 | @Test |
| 107 | - fun addTrackPublication() { | 110 | + fun addTrackPublication() = runTest { |
| 108 | val audioPublication = TrackPublication(TRACK_INFO, null, participant) | 111 | val audioPublication = TrackPublication(TRACK_INFO, null, participant) |
| 109 | participant.addTrackPublication(audioPublication) | 112 | participant.addTrackPublication(audioPublication) |
| 110 | 113 |
| @@ -38,8 +38,7 @@ android { | @@ -38,8 +38,7 @@ android { | ||
| 38 | compose true | 38 | compose true |
| 39 | } | 39 | } |
| 40 | composeOptions { | 40 | composeOptions { |
| 41 | - kotlinCompilerExtensionVersion compose_version | ||
| 42 | - kotlinCompilerVersion kotlin_version | 41 | + kotlinCompilerExtensionVersion compose_compiler_version |
| 43 | } | 42 | } |
| 44 | packagingOptions { | 43 | packagingOptions { |
| 45 | resources { | 44 | resources { |
-
请 注册 或 登录 后发表评论