davidliu
Committed by GitHub

Separate mocks & tests into a separate publishable package (#395)

* Separate mocks and tests into separate package

* publishing for testing package

* Make NetworkCallbackManager injectable

* various fixes and upgrades to test package

* fix tests
正在显示 88 个修改的文件 包含 908 行增加528 行删除
@@ -55,7 +55,7 @@ jobs: @@ -55,7 +55,7 @@ jobs:
55 ./gradlew spotlessCheck 55 ./gradlew spotlessCheck
56 56
57 - name: Build with Gradle 57 - name: Build with Gradle
58 - run: ./gradlew assembleRelease livekit-android-sdk:testRelease 58 + run: ./gradlew assembleRelease livekit-android-test:testRelease
59 59
60 # TODO: Figure out appropriate place to run this. Takes ~3 mins, so pretty slow. 60 # TODO: Figure out appropriate place to run this. Takes ~3 mins, so pretty slow.
61 # # generates coverage-report.md and publishes as checkrun 61 # # generates coverage-report.md and publishes as checkrun
@@ -40,7 +40,7 @@ jobs: @@ -40,7 +40,7 @@ jobs:
40 AWS_DEFAULT_REGION: "us-east-1" 40 AWS_DEFAULT_REGION: "us-east-1"
41 41
42 - name: Build with Gradle 42 - name: Build with Gradle
43 - run: ./gradlew livekit-android-sdk:assembleRelease livekit-android-sdk:testRelease 43 + run: ./gradlew livekit-android-sdk:assembleRelease livekit-android-test:testRelease
44 44
45 - name: Create gpg key and import into gradle properties 45 - name: Create gpg key and import into gradle properties
46 run: | 46 run: |
@@ -17,6 +17,9 @@ okhttp = "4.12.0" @@ -17,6 +17,9 @@ okhttp = "4.12.0"
17 protobuf = "3.22.0" 17 protobuf = "3.22.0"
18 protobufJavalite = "3.22.0" 18 protobufJavalite = "3.22.0"
19 semver4j = "3.1.0" 19 semver4j = "3.1.0"
  20 +core-ktx = "1.12.0"
  21 +appcompat = "1.6.1"
  22 +material = "1.11.0"
20 23
21 [libraries] 24 [libraries]
22 android-jain-sip-ri = { module = "javax.sip:android-jain-sip-ri", version.ref = "androidJainSipRi" } 25 android-jain-sip-ri = { module = "javax.sip:android-jain-sip-ri", version.ref = "androidJainSipRi" }
@@ -58,10 +61,20 @@ espresso = { module = "androidx.test.espresso:espresso-core", version = "3.5.1" @@ -58,10 +61,20 @@ espresso = { module = "androidx.test.espresso:espresso-core", version = "3.5.1"
58 junit = { module = "junit:junit", version.ref = "junit-lib" } 61 junit = { module = "junit:junit", version.ref = "junit-lib" }
59 junitJupiterApi = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit-jupiter" } 62 junitJupiterApi = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit-jupiter" }
60 junitJupiterEngine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit-jupiter" } 63 junitJupiterEngine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit-jupiter" }
61 -mockito-core = { module = "org.mockito:mockito-core", version = "4.0.0" }  
62 -mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version = "4.0.0" }  
63 -robolectric = { module = "org.robolectric:robolectric", version = "4.10.2" } 64 +
  65 +# Mockito 5 requires JVM 11.
  66 +#noinspection GradleDependency
  67 +mockito-core = { module = "org.mockito:mockito-core", version = "4.11.0" }
  68 +#noinspection GradleDependency
  69 +mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version = "4.1.0" }
  70 +#noinspection GradleDependency
  71 +mockito-inline = { module = "org.mockito:mockito-inline", version = "4.11.0" }
  72 +
  73 +robolectric = { module = "org.robolectric:robolectric", version = "4.11.1" }
64 turbine = { module = "app.cash.turbine:turbine", version = "1.0.0" } 74 turbine = { module = "app.cash.turbine:turbine", version = "1.0.0" }
  75 +core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
  76 +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
  77 +material = { group = "com.google.android.material", name = "material", version.ref = "material" }
65 78
66 [plugins] 79 [plugins]
67 80
@@ -16,44 +16,47 @@ @@ -16,44 +16,47 @@
16 16
17 package io.livekit.android.dagger 17 package io.livekit.android.dagger
18 18
19 -internal object InjectionNames { 19 +/**
  20 + * @suppress
  21 + */
  22 +object InjectionNames {
20 23
21 /** 24 /**
22 * @see [kotlinx.coroutines.Dispatchers.Default] 25 * @see [kotlinx.coroutines.Dispatchers.Default]
23 */ 26 */
24 - internal const val DISPATCHER_DEFAULT = "dispatcher_default" 27 + const val DISPATCHER_DEFAULT = "dispatcher_default"
25 28
26 /** 29 /**
27 * @see [kotlinx.coroutines.Dispatchers.IO] 30 * @see [kotlinx.coroutines.Dispatchers.IO]
28 */ 31 */
29 - internal const val DISPATCHER_IO = "dispatcher_io" 32 + const val DISPATCHER_IO = "dispatcher_io"
30 33
31 /** 34 /**
32 * @see [kotlinx.coroutines.Dispatchers.Main] 35 * @see [kotlinx.coroutines.Dispatchers.Main]
33 */ 36 */
34 - internal const val DISPATCHER_MAIN = "dispatcher_main" 37 + const val DISPATCHER_MAIN = "dispatcher_main"
35 38
36 /** 39 /**
37 * @see [kotlinx.coroutines.Dispatchers.Unconfined] 40 * @see [kotlinx.coroutines.Dispatchers.Unconfined]
38 */ 41 */
39 - internal const val DISPATCHER_UNCONFINED = "dispatcher_unconfined" 42 + const val DISPATCHER_UNCONFINED = "dispatcher_unconfined"
40 43
41 - internal const val SENDER = "sender" 44 + const val SENDER = "sender"
42 45
43 - internal const val OPTIONS_VIDEO_HW_ACCEL = "options_video_hw_accel" 46 + const val OPTIONS_VIDEO_HW_ACCEL = "options_video_hw_accel"
44 47
45 - internal const val LIB_WEBRTC_INITIALIZATION = "lib_webrtc_initialization" 48 + const val LIB_WEBRTC_INITIALIZATION = "lib_webrtc_initialization"
46 49
47 // Overrides 50 // Overrides
48 - internal const val OVERRIDE_OKHTTP = "override_okhttp"  
49 - internal const val OVERRIDE_AUDIO_DEVICE_MODULE = "override_audio_device_module"  
50 - internal const val OVERRIDE_AUDIO_PROCESSOR_OPTIONS = "override_audio_processor_options"  
51 - internal const val OVERRIDE_JAVA_AUDIO_DEVICE_MODULE_CUSTOMIZER = "override_java_audio_device_module_customizer"  
52 - internal const val OVERRIDE_VIDEO_ENCODER_FACTORY = "override_video_encoder_factory"  
53 - internal const val OVERRIDE_VIDEO_DECODER_FACTORY = "override_video_decoder_factory"  
54 - internal const val OVERRIDE_AUDIO_HANDLER = "override_audio_handler"  
55 - internal const val OVERRIDE_AUDIO_OUTPUT_TYPE = "override_audio_output_type"  
56 - internal const val OVERRIDE_DISABLE_COMMUNICATION_WORKAROUND = "override_disable_communication_workaround"  
57 - internal const val OVERRIDE_EGL_BASE = "override_egl_base"  
58 - internal const val OVERRIDE_PEER_CONNECTION_FACTORY_OPTIONS = "override_peer_connection_factory_options" 51 + const val OVERRIDE_OKHTTP = "override_okhttp"
  52 + const val OVERRIDE_AUDIO_DEVICE_MODULE = "override_audio_device_module"
  53 + const val OVERRIDE_AUDIO_PROCESSOR_OPTIONS = "override_audio_processor_options"
  54 + const val OVERRIDE_JAVA_AUDIO_DEVICE_MODULE_CUSTOMIZER = "override_java_audio_device_module_customizer"
  55 + const val OVERRIDE_VIDEO_ENCODER_FACTORY = "override_video_encoder_factory"
  56 + const val OVERRIDE_VIDEO_DECODER_FACTORY = "override_video_decoder_factory"
  57 + const val OVERRIDE_AUDIO_HANDLER = "override_audio_handler"
  58 + const val OVERRIDE_AUDIO_OUTPUT_TYPE = "override_audio_output_type"
  59 + const val OVERRIDE_DISABLE_COMMUNICATION_WORKAROUND = "override_disable_communication_workaround"
  60 + const val OVERRIDE_EGL_BASE = "override_egl_base"
  61 + const val OVERRIDE_PEER_CONNECTION_FACTORY_OPTIONS = "override_peer_connection_factory_options"
59 } 62 }
@@ -21,8 +21,11 @@ import dagger.Provides @@ -21,8 +21,11 @@ import dagger.Provides
21 import dagger.Reusable 21 import dagger.Reusable
22 import kotlinx.serialization.json.Json 22 import kotlinx.serialization.json.Json
23 23
  24 +/**
  25 + * @suppress
  26 + */
24 @Module 27 @Module
25 -internal object JsonFormatModule { 28 +object JsonFormatModule {
26 @Provides 29 @Provides
27 @Reusable 30 @Reusable
28 fun kotlinSerializationJson(): Json = 31 fun kotlinSerializationJson(): Json =
@@ -25,6 +25,9 @@ import livekit.org.webrtc.EglBase @@ -25,6 +25,9 @@ import livekit.org.webrtc.EglBase
25 import livekit.org.webrtc.PeerConnectionFactory 25 import livekit.org.webrtc.PeerConnectionFactory
26 import javax.inject.Singleton 26 import javax.inject.Singleton
27 27
  28 +/**
  29 + * @suppress
  30 + */
28 @Singleton 31 @Singleton
29 @Component( 32 @Component(
30 modules = [ 33 modules = [
@@ -37,7 +40,7 @@ import javax.inject.Singleton @@ -37,7 +40,7 @@ import javax.inject.Singleton
37 MemoryModule::class, 40 MemoryModule::class,
38 ], 41 ],
39 ) 42 )
40 -internal interface LiveKitComponent { 43 +interface LiveKitComponent {
41 44
42 fun roomFactory(): Room.Factory 45 fun roomFactory(): Room.Factory
43 46
@@ -21,8 +21,11 @@ import dagger.Provides @@ -21,8 +21,11 @@ import dagger.Provides
21 import io.livekit.android.memory.CloseableManager 21 import io.livekit.android.memory.CloseableManager
22 import javax.inject.Singleton 22 import javax.inject.Singleton
23 23
  24 +/**
  25 + * @suppress
  26 + */
24 @Module 27 @Module
25 -internal object MemoryModule { 28 +object MemoryModule {
26 29
27 @Singleton 30 @Singleton
28 @Provides 31 @Provides
@@ -23,9 +23,12 @@ import dagger.Provides @@ -23,9 +23,12 @@ import dagger.Provides
23 import io.livekit.android.LiveKitOverrides 23 import io.livekit.android.LiveKitOverrides
24 import javax.inject.Named 24 import javax.inject.Named
25 25
  26 +/**
  27 + * @suppress
  28 + */
26 @SuppressLint("KotlinNullnessAnnotation") 29 @SuppressLint("KotlinNullnessAnnotation")
27 @Module 30 @Module
28 -internal class OverridesModule(private val overrides: LiveKitOverrides) { 31 +class OverridesModule(private val overrides: LiveKitOverrides) {
29 32
30 @Provides 33 @Provides
31 @Named(InjectionNames.OVERRIDE_OKHTTP) 34 @Named(InjectionNames.OVERRIDE_OKHTTP)
@@ -41,8 +41,14 @@ import timber.log.Timber @@ -41,8 +41,14 @@ import timber.log.Timber
41 import javax.inject.Named 41 import javax.inject.Named
42 import javax.inject.Singleton 42 import javax.inject.Singleton
43 43
44 -internal typealias CapabilitiesGetter = @JvmSuppressWildcards (MediaStreamTrack.MediaType) -> RtpCapabilities 44 +/**
  45 + * @suppress
  46 + */
  47 +typealias CapabilitiesGetter = @JvmSuppressWildcards (MediaStreamTrack.MediaType) -> RtpCapabilities
45 48
  49 +/**
  50 + * @suppress
  51 + */
46 @Module 52 @Module
47 internal object RTCModule { 53 internal object RTCModule {
48 54
@@ -17,10 +17,16 @@ @@ -17,10 +17,16 @@
17 package io.livekit.android.dagger 17 package io.livekit.android.dagger
18 18
19 import android.content.Context 19 import android.content.Context
  20 +import android.net.ConnectivityManager
20 import androidx.annotation.Nullable 21 import androidx.annotation.Nullable
21 import dagger.Module 22 import dagger.Module
22 import dagger.Provides 23 import dagger.Provides
23 import dagger.Reusable 24 import dagger.Reusable
  25 +import io.livekit.android.memory.CloseableManager
  26 +import io.livekit.android.room.network.NetworkCallbackManagerFactory
  27 +import io.livekit.android.room.network.NetworkCallbackManagerImpl
  28 +import io.livekit.android.room.network.NetworkCallbackRegistry
  29 +import io.livekit.android.room.network.NetworkCallbackRegistryImpl
24 import io.livekit.android.stats.AndroidNetworkInfo 30 import io.livekit.android.stats.AndroidNetworkInfo
25 import io.livekit.android.stats.NetworkInfo 31 import io.livekit.android.stats.NetworkInfo
26 import okhttp3.OkHttpClient 32 import okhttp3.OkHttpClient
@@ -50,4 +56,23 @@ internal object WebModule { @@ -50,4 +56,23 @@ internal object WebModule {
50 fun networkInfo(context: Context): NetworkInfo { 56 fun networkInfo(context: Context): NetworkInfo {
51 return AndroidNetworkInfo(context) 57 return AndroidNetworkInfo(context)
52 } 58 }
  59 +
  60 + @Provides
  61 + fun connectivityManager(context: Context): ConnectivityManager {
  62 + return context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
  63 + }
  64 +
  65 + @Provides
  66 + fun networkCallbackRegistrar(connectivityManager: ConnectivityManager): NetworkCallbackRegistry {
  67 + return NetworkCallbackRegistryImpl(connectivityManager)
  68 + }
  69 +
  70 + @Provides
  71 + @Reusable
  72 + fun networkCallbackManagerFactory(closeableManager: CloseableManager, registrar: NetworkCallbackRegistry): NetworkCallbackManagerFactory {
  73 + return { networkCallback: ConnectivityManager.NetworkCallback ->
  74 + NetworkCallbackManagerImpl(networkCallback, registrar)
  75 + .apply { closeableManager.registerClosable(this) }
  76 + }
  77 + }
53 } 78 }
@@ -232,7 +232,10 @@ enum class DisconnectReason { @@ -232,7 +232,10 @@ enum class DisconnectReason {
232 JOIN_FAILURE, 232 JOIN_FAILURE,
233 } 233 }
234 234
235 -internal fun LivekitModels.DisconnectReason?.convert(): DisconnectReason { 235 +/**
  236 + * @suppress
  237 + */
  238 +fun LivekitModels.DisconnectReason?.convert(): DisconnectReason {
236 return when (this) { 239 return when (this) {
237 LivekitModels.DisconnectReason.CLIENT_INITIATED -> DisconnectReason.CLIENT_INITIATED 240 LivekitModels.DisconnectReason.CLIENT_INITIATED -> DisconnectReason.CLIENT_INITIATED
238 LivekitModels.DisconnectReason.DUPLICATE_IDENTITY -> DisconnectReason.DUPLICATE_IDENTITY 241 LivekitModels.DisconnectReason.DUPLICATE_IDENTITY -> DisconnectReason.DUPLICATE_IDENTITY
@@ -354,7 +354,11 @@ constructor( @@ -354,7 +354,11 @@ constructor(
354 354
355 private const val DD_EXTENSION_URI = "https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension" 355 private const val DD_EXTENSION_URI = "https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension"
356 356
357 -internal fun ensureVideoDDExtensionForSVC(mediaDesc: MediaDescription) { 357 +/**
  358 + * @suppress
  359 + */
  360 +@VisibleForTesting
  361 +fun ensureVideoDDExtensionForSVC(mediaDesc: MediaDescription) {
358 val codec = mediaDesc.getRtps() 362 val codec = mediaDesc.getRtps()
359 .firstOrNull() 363 .firstOrNull()
360 ?.second 364 ?.second
@@ -397,7 +401,11 @@ eliminate this issue. @@ -397,7 +401,11 @@ eliminate this issue.
397 */ 401 */
398 private const val startBitrateForSVC = 0.7 402 private const val startBitrateForSVC = 0.7
399 403
400 -internal fun ensureCodecBitrates( 404 +/**
  405 + * @suppress
  406 + */
  407 +@VisibleForTesting
  408 +fun ensureCodecBitrates(
401 media: MediaDescription, 409 media: MediaDescription,
402 trackBitrates: Map<TrackBitrateInfoKey, TrackBitrateInfo>, 410 trackBitrates: Map<TrackBitrateInfoKey, TrackBitrateInfo>,
403 ) { 411 ) {
@@ -454,12 +462,20 @@ internal fun isSVCCodec(codec: String?): Boolean { @@ -454,12 +462,20 @@ internal fun isSVCCodec(codec: String?): Boolean {
454 "vp9".equals(codec, ignoreCase = true)) 462 "vp9".equals(codec, ignoreCase = true))
455 } 463 }
456 464
457 -internal data class TrackBitrateInfo( 465 +/**
  466 + * @suppress
  467 + */
  468 +@VisibleForTesting
  469 +data class TrackBitrateInfo(
458 val codec: String, 470 val codec: String,
459 val maxBitrate: Long, 471 val maxBitrate: Long,
460 ) 472 )
461 473
462 -internal sealed class TrackBitrateInfoKey { 474 +/**
  475 + * @suppress
  476 + */
  477 +@VisibleForTesting
  478 +sealed class TrackBitrateInfoKey {
463 data class Cid(val value: String) : TrackBitrateInfoKey() 479 data class Cid(val value: String) : TrackBitrateInfoKey()
464 data class Transceiver(val value: RtpTransceiver) : TrackBitrateInfoKey() 480 data class Transceiver(val value: RtpTransceiver) : TrackBitrateInfoKey()
465 } 481 }
@@ -80,7 +80,8 @@ internal constructor( @@ -80,7 +80,8 @@ internal constructor(
80 * Reflects the combined connection state of SignalClient and primary PeerConnection. 80 * Reflects the combined connection state of SignalClient and primary PeerConnection.
81 */ 81 */
82 @FlowObservable 82 @FlowObservable
83 - internal var connectionState: ConnectionState by flowDelegate(ConnectionState.DISCONNECTED) { newVal, oldVal -> 83 + @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
  84 + var connectionState: ConnectionState by flowDelegate(ConnectionState.DISCONNECTED) { newVal, oldVal ->
84 if (newVal == oldVal) { 85 if (newVal == oldVal) {
85 return@flowDelegate 86 return@flowDelegate
86 } 87 }
@@ -383,7 +384,8 @@ internal constructor( @@ -383,7 +384,8 @@ internal constructor(
383 * reconnect Signal and PeerConnections 384 * reconnect Signal and PeerConnections
384 */ 385 */
385 @Synchronized 386 @Synchronized
386 - internal fun reconnect() { 387 + @VisibleForTesting
  388 + fun reconnect() {
387 if (reconnectingJob?.isActive == true) { 389 if (reconnectingJob?.isActive == true) {
388 LKLog.d { "Reconnection is already in progress" } 390 LKLog.d { "Reconnection is already in progress" }
389 return 391 return
@@ -740,11 +742,18 @@ internal constructor( @@ -740,11 +742,18 @@ internal constructor(
740 } 742 }
741 743
742 companion object { 744 companion object {
  745 +
  746 + /**
  747 + * @suppress
  748 + */
743 @VisibleForTesting 749 @VisibleForTesting
744 - internal const val RELIABLE_DATA_CHANNEL_LABEL = "_reliable" 750 + const val RELIABLE_DATA_CHANNEL_LABEL = "_reliable"
745 751
  752 + /**
  753 + * @suppress
  754 + */
746 @VisibleForTesting 755 @VisibleForTesting
747 - internal const val LOSSY_DATA_CHANNEL_LABEL = "_lossy" 756 + const val LOSSY_DATA_CHANNEL_LABEL = "_lossy"
748 internal const val MAX_DATA_PACKET_SIZE = 15000 757 internal const val MAX_DATA_PACKET_SIZE = 15000
749 private const val MAX_RECONNECT_RETRIES = 10 758 private const val MAX_RECONNECT_RETRIES = 10
750 private const val MAX_RECONNECT_TIMEOUT = 60 * 1000 759 private const val MAX_RECONNECT_TIMEOUT = 60 * 1000
@@ -1056,11 +1065,11 @@ internal constructor( @@ -1056,11 +1065,11 @@ internal constructor(
1056 } 1065 }
1057 1066
1058 @VisibleForTesting 1067 @VisibleForTesting
1059 - internal fun getPublisherPeerConnection() = 1068 + fun getPublisherPeerConnection() =
1060 publisher!!.peerConnection 1069 publisher!!.peerConnection
1061 1070
1062 @VisibleForTesting 1071 @VisibleForTesting
1063 - internal fun getSubscriberPeerConnection() = 1072 + fun getSubscriberPeerConnection() =
1064 subscriber!!.peerConnection 1073 subscriber!!.peerConnection
1065 } 1074 }
1066 1075
@@ -1073,7 +1082,7 @@ enum class ReconnectType { @@ -1073,7 +1082,7 @@ enum class ReconnectType {
1073 FORCE_FULL_RECONNECT, 1082 FORCE_FULL_RECONNECT,
1074 } 1083 }
1075 1084
1076 -internal fun LivekitRtc.ICEServer.toWebrtc() = PeerConnection.IceServer.builder(urlsList) 1085 +fun LivekitRtc.ICEServer.toWebrtc(): PeerConnection.IceServer = PeerConnection.IceServer.builder(urlsList)
1077 .setUsername(username ?: "") 1086 .setUsername(username ?: "")
1078 .setPassword(credential ?: "") 1087 .setPassword(credential ?: "")
1079 .setTlsAlpnProtocols(emptyList()) 1088 .setTlsAlpnProtocols(emptyList())
@@ -19,7 +19,6 @@ @@ -19,7 +19,6 @@
19 package io.livekit.android.room 19 package io.livekit.android.room
20 20
21 import android.content.Context 21 import android.content.Context
22 -import android.net.ConnectivityManager  
23 import android.net.ConnectivityManager.NetworkCallback 22 import android.net.ConnectivityManager.NetworkCallback
24 import android.net.Network 23 import android.net.Network
25 import androidx.annotation.VisibleForTesting 24 import androidx.annotation.VisibleForTesting
@@ -39,7 +38,7 @@ import io.livekit.android.e2ee.E2EEOptions @@ -39,7 +38,7 @@ import io.livekit.android.e2ee.E2EEOptions
39 import io.livekit.android.events.* 38 import io.livekit.android.events.*
40 import io.livekit.android.memory.CloseableManager 39 import io.livekit.android.memory.CloseableManager
41 import io.livekit.android.renderer.TextureViewRenderer 40 import io.livekit.android.renderer.TextureViewRenderer
42 -import io.livekit.android.room.network.NetworkCallbackManager 41 +import io.livekit.android.room.network.NetworkCallbackManagerFactory
43 import io.livekit.android.room.participant.* 42 import io.livekit.android.room.participant.*
44 import io.livekit.android.room.provisions.LKObjects 43 import io.livekit.android.room.provisions.LKObjects
45 import io.livekit.android.room.track.* 44 import io.livekit.android.room.track.*
@@ -78,6 +77,7 @@ constructor( @@ -78,6 +77,7 @@ constructor(
78 private val communicationWorkaround: CommunicationWorkaround, 77 private val communicationWorkaround: CommunicationWorkaround,
79 val audioProcessingController: AudioProcessingController, 78 val audioProcessingController: AudioProcessingController,
80 val lkObjects: LKObjects, 79 val lkObjects: LKObjects,
  80 + networkCallbackManagerFactory: NetworkCallbackManagerFactory,
81 ) : RTCEngine.Listener, ParticipantListener { 81 ) : RTCEngine.Listener, ParticipantListener {
82 82
83 private lateinit var coroutineScope: CoroutineScope 83 private lateinit var coroutineScope: CoroutineScope
@@ -753,7 +753,7 @@ constructor( @@ -753,7 +753,7 @@ constructor(
753 } 753 }
754 754
755 // ------------------------------------- NetworkCallback -------------------------------------// 755 // ------------------------------------- NetworkCallback -------------------------------------//
756 - private val networkCallbackManager = NetworkCallbackManager( 756 + private val networkCallbackManager = networkCallbackManagerFactory.invoke(
757 object : NetworkCallback() { 757 object : NetworkCallback() {
758 override fun onLost(network: Network) { 758 override fun onLost(network: Network) {
759 // lost connection, flip to reconnecting 759 // lost connection, flip to reconnecting
@@ -770,8 +770,7 @@ constructor( @@ -770,8 +770,7 @@ constructor(
770 hasLostConnectivity = false 770 hasLostConnectivity = false
771 } 771 }
772 }, 772 },
773 - connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager,  
774 - ).apply { closeableManager.registerClosable(this) } 773 + )
775 774
776 // ----------------------------------- RTCEngine.Listener ------------------------------------// 775 // ----------------------------------- RTCEngine.Listener ------------------------------------//
777 776
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 16
17 package io.livekit.android.room 17 package io.livekit.android.room
18 18
  19 +import androidx.annotation.VisibleForTesting
19 import com.vdurmont.semver4j.Semver 20 import com.vdurmont.semver4j.Semver
20 import io.livekit.android.ConnectOptions 21 import io.livekit.android.ConnectOptions
21 import io.livekit.android.RoomOptions 22 import io.livekit.android.RoomOptions
@@ -122,7 +123,8 @@ constructor( @@ -122,7 +123,8 @@ constructor(
122 /** 123 /**
123 * @throws Exception if fails to connect. 124 * @throws Exception if fails to connect.
124 */ 125 */
125 - internal suspend fun reconnect(url: String, token: String, participantSid: String?): Either<ReconnectResponse, Unit> { 126 + @VisibleForTesting
  127 + suspend fun reconnect(url: String, token: String, participantSid: String?): Either<ReconnectResponse, Unit> {
126 val reconnectResponse = connect( 128 val reconnectResponse = connect(
127 url, 129 url,
128 token, 130 token,
@@ -17,12 +17,40 @@ @@ -17,12 +17,40 @@
17 package io.livekit.android.room.network 17 package io.livekit.android.room.network
18 18
19 import android.net.ConnectivityManager 19 import android.net.ConnectivityManager
  20 +import android.net.ConnectivityManager.NetworkCallback
20 import android.net.NetworkCapabilities 21 import android.net.NetworkCapabilities
21 import android.net.NetworkRequest 22 import android.net.NetworkRequest
22 import io.livekit.android.util.LKLog 23 import io.livekit.android.util.LKLog
23 import java.io.Closeable 24 import java.io.Closeable
24 import java.util.concurrent.atomic.AtomicBoolean 25 import java.util.concurrent.atomic.AtomicBoolean
25 26
  27 +typealias NetworkCallbackManagerFactory = @JvmSuppressWildcards (
  28 + networkCallback: NetworkCallback,
  29 +) -> NetworkCallbackManager
  30 +
  31 +/**
  32 + * @suppress
  33 + */
  34 +interface NetworkCallbackRegistry {
  35 + fun registerNetworkCallback(networkRequest: NetworkRequest, networkCallback: NetworkCallback)
  36 + fun unregisterNetworkCallback(networkCallback: NetworkCallback)
  37 +}
  38 +
  39 +internal class NetworkCallbackRegistryImpl(val connectivityManager: ConnectivityManager) : NetworkCallbackRegistry {
  40 + override fun registerNetworkCallback(networkRequest: NetworkRequest, networkCallback: NetworkCallback) {
  41 + connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
  42 + }
  43 +
  44 + override fun unregisterNetworkCallback(networkCallback: NetworkCallback) {
  45 + connectivityManager.unregisterNetworkCallback(networkCallback)
  46 + }
  47 +}
  48 +
  49 +interface NetworkCallbackManager : Closeable {
  50 + fun registerCallback()
  51 + fun unregisterCallback()
  52 +}
  53 +
26 /** 54 /**
27 * Manages a [ConnectivityManager.NetworkCallback] so that it is never 55 * Manages a [ConnectivityManager.NetworkCallback] so that it is never
28 * registered multiple times. A NetworkCallback is allowed to be registered 56 * registered multiple times. A NetworkCallback is allowed to be registered
@@ -30,16 +58,18 @@ import java.util.concurrent.atomic.AtomicBoolean @@ -30,16 +58,18 @@ import java.util.concurrent.atomic.AtomicBoolean
30 * requests will leak on 8.0 and earlier. 58 * requests will leak on 8.0 and earlier.
31 * 59 *
32 * There's a 100 request hard limit, so leaks here are particularly dangerous. 60 * There's a 100 request hard limit, so leaks here are particularly dangerous.
  61 + *
  62 + * @suppress
33 */ 63 */
34 -class NetworkCallbackManager(  
35 - private val networkCallback: ConnectivityManager.NetworkCallback,  
36 - private val connectivityManager: ConnectivityManager,  
37 -) : Closeable { 64 +class NetworkCallbackManagerImpl(
  65 + private val networkCallback: NetworkCallback,
  66 + private val connectivityManager: NetworkCallbackRegistry,
  67 +) : NetworkCallbackManager {
38 private val isRegistered = AtomicBoolean(false) 68 private val isRegistered = AtomicBoolean(false)
39 private val isClosed = AtomicBoolean(false) 69 private val isClosed = AtomicBoolean(false)
40 70
41 @Synchronized 71 @Synchronized
42 - fun registerCallback() { 72 + override fun registerCallback() {
43 if (!isClosed.get() && isRegistered.compareAndSet(false, true)) { 73 if (!isClosed.get() && isRegistered.compareAndSet(false, true)) {
44 try { 74 try {
45 val networkRequest = NetworkRequest.Builder() 75 val networkRequest = NetworkRequest.Builder()
@@ -53,7 +83,7 @@ class NetworkCallbackManager( @@ -53,7 +83,7 @@ class NetworkCallbackManager(
53 } 83 }
54 84
55 @Synchronized 85 @Synchronized
56 - fun unregisterCallback() { 86 + override fun unregisterCallback() {
57 if (!isClosed.get() && isRegistered.compareAndSet(true, false)) { 87 if (!isClosed.get() && isRegistered.compareAndSet(true, false)) {
58 try { 88 try {
59 connectivityManager.unregisterNetworkCallback(networkCallback) 89 connectivityManager.unregisterNetworkCallback(networkCallback)
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 16
17 package io.livekit.android.room.participant 17 package io.livekit.android.room.participant
18 18
  19 +import androidx.annotation.VisibleForTesting
19 import io.livekit.android.dagger.InjectionNames 20 import io.livekit.android.dagger.InjectionNames
20 import io.livekit.android.events.BroadcastEventBus 21 import io.livekit.android.events.BroadcastEventBus
21 import io.livekit.android.events.ParticipantEvent 22 import io.livekit.android.events.ParticipantEvent
@@ -75,7 +76,7 @@ open class Participant( @@ -75,7 +76,7 @@ open class Participant(
75 @FlowObservable 76 @FlowObservable
76 @get:FlowObservable 77 @get:FlowObservable
77 var identity: Identity? by flowDelegate(identity) 78 var identity: Identity? by flowDelegate(identity)
78 - internal set 79 + @VisibleForTesting set
79 80
80 /** 81 /**
81 * Changes can be observed by using [io.livekit.android.util.flow] 82 * Changes can be observed by using [io.livekit.android.util.flow]
@@ -83,7 +84,7 @@ open class Participant( @@ -83,7 +84,7 @@ open class Participant(
83 @FlowObservable 84 @FlowObservable
84 @get:FlowObservable 85 @get:FlowObservable
85 var audioLevel: Float by flowDelegate(0f) 86 var audioLevel: Float by flowDelegate(0f)
86 - internal set 87 + @VisibleForTesting set
87 88
88 /** 89 /**
89 * Changes can be observed by using [io.livekit.android.util.flow] 90 * Changes can be observed by using [io.livekit.android.util.flow]
@@ -99,7 +100,7 @@ open class Participant( @@ -99,7 +100,7 @@ open class Participant(
99 } 100 }
100 } 101 }
101 } 102 }
102 - internal set 103 + @VisibleForTesting set
103 104
104 @FlowObservable 105 @FlowObservable
105 @get:FlowObservable 106 @get:FlowObservable
@@ -108,7 +109,7 @@ open class Participant( @@ -108,7 +109,7 @@ open class Participant(
108 eventBus.postEvent(ParticipantEvent.NameChanged(this, newValue), scope) 109 eventBus.postEvent(ParticipantEvent.NameChanged(this, newValue), scope)
109 } 110 }
110 } 111 }
111 - internal set 112 + @VisibleForTesting set
112 113
113 /** 114 /**
114 * Changes can be observed by using [io.livekit.android.util.flow] 115 * Changes can be observed by using [io.livekit.android.util.flow]
@@ -121,7 +122,7 @@ open class Participant( @@ -121,7 +122,7 @@ open class Participant(
121 eventBus.postEvent(ParticipantEvent.MetadataChanged(this, oldMetadata), scope) 122 eventBus.postEvent(ParticipantEvent.MetadataChanged(this, oldMetadata), scope)
122 } 123 }
123 } 124 }
124 - internal set 125 + @VisibleForTesting set
125 126
126 /** 127 /**
127 * 128 *
@@ -168,7 +169,8 @@ open class Participant( @@ -168,7 +169,8 @@ open class Participant(
168 * @suppress 169 * @suppress
169 */ 170 */
170 @Deprecated("Use events instead") 171 @Deprecated("Use events instead")
171 - internal var internalListener: ParticipantListener? = null 172 + @VisibleForTesting
  173 + var internalListener: ParticipantListener? = null
172 174
173 val hasInfo 175 val hasInfo
174 get() = participantInfo != null 176 get() = participantInfo != null
@@ -300,7 +302,8 @@ open class Participant( @@ -300,7 +302,8 @@ open class Participant(
300 /** 302 /**
301 * @suppress 303 * @suppress
302 */ 304 */
303 - internal open fun updateFromInfo(info: LivekitModels.ParticipantInfo) { 305 + @VisibleForTesting
  306 + open fun updateFromInfo(info: LivekitModels.ParticipantInfo) {
304 sid = Sid(info.sid) 307 sid = Sid(info.sid)
305 identity = Identity(info.identity) 308 identity = Identity(info.identity)
306 participantInfo = info 309 participantInfo = info
@@ -351,7 +354,11 @@ open class Participant( @@ -351,7 +354,11 @@ open class Participant(
351 } 354 }
352 } 355 }
353 356
354 - internal open fun dispose() { 357 + /**
  358 + * @suppress
  359 + */
  360 + @VisibleForTesting
  361 + open fun dispose() {
355 scope.cancel() 362 scope.cancel()
356 363
357 sid = Sid("") 364 sid = Sid("")
@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 package io.livekit.android.room.track 17 package io.livekit.android.room.track
18 18
19 import android.view.View 19 import android.view.View
  20 +import androidx.annotation.VisibleForTesting
20 import io.livekit.android.dagger.InjectionNames 21 import io.livekit.android.dagger.InjectionNames
21 import io.livekit.android.events.TrackEvent 22 import io.livekit.android.events.TrackEvent
22 import io.livekit.android.room.track.video.VideoSinkVisibility 23 import io.livekit.android.room.track.video.VideoSinkVisibility
@@ -41,9 +42,18 @@ class RemoteVideoTrack( @@ -41,9 +42,18 @@ class RemoteVideoTrack(
41 private val sinkVisibilityMap = mutableMapOf<VideoSink, VideoSinkVisibility>() 42 private val sinkVisibilityMap = mutableMapOf<VideoSink, VideoSinkVisibility>()
42 private val visibilities = sinkVisibilityMap.values 43 private val visibilities = sinkVisibilityMap.values
43 44
44 - internal var lastVisibility = false 45 + /**
  46 + * @suppress
  47 + */
  48 + @VisibleForTesting
  49 + var lastVisibility = false
45 private set 50 private set
46 - internal var lastDimensions: Dimensions = Dimensions(0, 0) 51 +
  52 + /**
  53 + * @suppress
  54 + */
  55 + @VisibleForTesting
  56 + var lastDimensions: Dimensions = Dimensions(0, 0)
47 private set 57 private set
48 58
49 internal var receiver: RtpReceiver 59 internal var receiver: RtpReceiver
@@ -16,7 +16,10 @@ @@ -16,7 +16,10 @@
16 16
17 package io.livekit.android.util 17 package io.livekit.android.util
18 18
19 -internal sealed class Either<out A, out B> { 19 +/**
  20 + * @suppress
  21 + */
  22 +sealed class Either<out A, out B> {
20 class Left<out A>(val value: A) : Either<A, Nothing>() 23 class Left<out A>(val value: A) : Either<A, Nothing>()
21 class Right<out B>(val value: B) : Either<Nothing, B>() 24 class Right<out B>(val value: B) : Either<Nothing, B>()
22 } 25 }
@@ -20,7 +20,7 @@ import com.google.protobuf.MessageLite @@ -20,7 +20,7 @@ import com.google.protobuf.MessageLite
20 import okio.ByteString 20 import okio.ByteString
21 import okio.ByteString.Companion.toByteString 21 import okio.ByteString.Companion.toByteString
22 22
23 -internal fun MessageLite.toOkioByteString(): ByteString { 23 +fun MessageLite.toOkioByteString(): ByteString {
24 val byteArray = toByteArray() 24 val byteArray = toByteArray()
25 return byteArray.toByteString(0, byteArray.size) 25 return byteArray.toByteString(0, byteArray.size)
26 } 26 }
@@ -20,9 +20,15 @@ import android.gov.nist.javax.sdp.fields.AttributeField @@ -20,9 +20,15 @@ import android.gov.nist.javax.sdp.fields.AttributeField
20 import android.javax.sdp.MediaDescription 20 import android.javax.sdp.MediaDescription
21 import io.livekit.android.util.LKLog 21 import io.livekit.android.util.LKLog
22 22
23 -internal data class SdpRtp(val payload: Long, val codec: String, val rate: Long?, val encoding: String?) 23 +/**
  24 + * @suppress
  25 + */
  26 +data class SdpRtp(val payload: Long, val codec: String, val rate: Long?, val encoding: String?)
24 27
25 -internal fun MediaDescription.getRtps(): List<Pair<AttributeField, SdpRtp>> { 28 +/**
  29 + * @suppress
  30 + */
  31 +fun MediaDescription.getRtps(): List<Pair<AttributeField, SdpRtp>> {
26 return getAttributes(true) 32 return getAttributes(true)
27 .filterIsInstance<AttributeField>() 33 .filterIsInstance<AttributeField>()
28 .filter { it.attribute.name == "rtpmap" } 34 .filter { it.attribute.name == "rtpmap" }
@@ -43,17 +49,26 @@ internal fun tryParseRtp(string: String): SdpRtp? { @@ -43,17 +49,26 @@ internal fun tryParseRtp(string: String): SdpRtp? {
43 return SdpRtp(payload.toLong(), codec, toOptionalLong(rate), toOptionalString(encoding)) 49 return SdpRtp(payload.toLong(), codec, toOptionalLong(rate), toOptionalString(encoding))
44 } 50 }
45 51
46 -internal data class SdpMsid( 52 +/**
  53 + * @suppress
  54 + */
  55 +data class SdpMsid(
47 /** holds the msid-id (and msid-appdata if available) */ 56 /** holds the msid-id (and msid-appdata if available) */
48 val value: String, 57 val value: String,
49 ) 58 )
50 59
51 -internal fun MediaDescription.getMsid(): SdpMsid? { 60 +/**
  61 + * @suppress
  62 + */
  63 +fun MediaDescription.getMsid(): SdpMsid? {
52 val attribute = getAttribute("msid") ?: return null 64 val attribute = getAttribute("msid") ?: return null
53 return SdpMsid(attribute) 65 return SdpMsid(attribute)
54 } 66 }
55 67
56 -internal data class SdpFmtp(val payload: Long, val config: String) { 68 +/**
  69 + * @suppress
  70 + */
  71 +data class SdpFmtp(val payload: Long, val config: String) {
57 fun toAttributeField(): AttributeField { 72 fun toAttributeField(): AttributeField {
58 return AttributeField().apply { 73 return AttributeField().apply {
59 name = "fmtp" 74 name = "fmtp"
@@ -62,7 +77,10 @@ internal data class SdpFmtp(val payload: Long, val config: String) { @@ -62,7 +77,10 @@ internal data class SdpFmtp(val payload: Long, val config: String) {
62 } 77 }
63 } 78 }
64 79
65 -internal fun MediaDescription.getFmtps(): List<Pair<AttributeField, SdpFmtp>> { 80 +/**
  81 + * @suppress
  82 + */
  83 +fun MediaDescription.getFmtps(): List<Pair<AttributeField, SdpFmtp>> {
66 return getAttributes(true) 84 return getAttributes(true)
67 .filterIsInstance<AttributeField>() 85 .filterIsInstance<AttributeField>()
68 .filter { it.attribute.name == "fmtp" } 86 .filter { it.attribute.name == "fmtp" }
@@ -83,7 +101,10 @@ internal fun tryParseFmtp(string: String): SdpFmtp? { @@ -83,7 +101,10 @@ internal fun tryParseFmtp(string: String): SdpFmtp? {
83 return SdpFmtp(payload.toLong(), config) 101 return SdpFmtp(payload.toLong(), config)
84 } 102 }
85 103
86 -internal data class SdpExt(val value: Long, val direction: String?, val encryptUri: String?, val uri: String, val config: String?) { 104 +/**
  105 + * @suppress
  106 + */
  107 +data class SdpExt(val value: Long, val direction: String?, val encryptUri: String?, val uri: String, val config: String?) {
87 fun toAttributeField(): AttributeField { 108 fun toAttributeField(): AttributeField {
88 return AttributeField().apply { 109 return AttributeField().apply {
89 name = "extmap" 110 name = "extmap"
@@ -104,7 +125,10 @@ internal data class SdpExt(val value: Long, val direction: String?, val encryptU @@ -104,7 +125,10 @@ internal data class SdpExt(val value: Long, val direction: String?, val encryptU
104 } 125 }
105 } 126 }
106 127
107 -internal fun MediaDescription.getExts(): List<Pair<AttributeField, SdpExt>> { 128 +/**
  129 + * @suppress
  130 + */
  131 +fun MediaDescription.getExts(): List<Pair<AttributeField, SdpExt>> {
108 return getAttributes(true) 132 return getAttributes(true)
109 .filterIsInstance<AttributeField>() 133 .filterIsInstance<AttributeField>()
110 .filter { it.attribute.name == "extmap" } 134 .filter { it.attribute.name == "extmap" }
@@ -43,13 +43,13 @@ internal fun PeerConnection.PeerConnectionState.isDisconnected(): Boolean { @@ -43,13 +43,13 @@ internal fun PeerConnection.PeerConnectionState.isDisconnected(): Boolean {
43 } 43 }
44 } 44 }
45 45
46 -internal fun RTCConfiguration.copy(): RTCConfiguration { 46 +fun RTCConfiguration.copy(): RTCConfiguration {
47 val newConfig = RTCConfiguration(emptyList()) 47 val newConfig = RTCConfiguration(emptyList())
48 newConfig.copyFrom(this) 48 newConfig.copyFrom(this)
49 return newConfig 49 return newConfig
50 } 50 }
51 51
52 -internal fun RTCConfiguration.copyFrom(config: RTCConfiguration) { 52 +fun RTCConfiguration.copyFrom(config: RTCConfiguration) {
53 iceTransportsType = config.iceTransportsType 53 iceTransportsType = config.iceTransportsType
54 iceServers = config.iceServers 54 iceServers = config.iceServers
55 bundlePolicy = config.bundlePolicy 55 bundlePolicy = config.bundlePolicy
@@ -46,8 +46,14 @@ private val threadFactory = object : ThreadFactory { @@ -46,8 +46,14 @@ private val threadFactory = object : ThreadFactory {
46 private var executor = Executors.newSingleThreadExecutor(threadFactory) 46 private var executor = Executors.newSingleThreadExecutor(threadFactory)
47 private var rtcDispatcher: CoroutineDispatcher = executor.asCoroutineDispatcher() 47 private var rtcDispatcher: CoroutineDispatcher = executor.asCoroutineDispatcher()
48 48
  49 +/**
  50 + * Overrides how RTC thread calls are executed and dispatched.
  51 + *
  52 + * This should absolutely not be used in production environments and is
  53 + * only to be used for testing.
  54 + */
49 @VisibleForTesting 55 @VisibleForTesting
50 -internal fun overrideExecutorAndDispatcher(executorService: ExecutorService, dispatcher: CoroutineDispatcher) { 56 +fun overrideExecutorAndDispatcher(executorService: ExecutorService, dispatcher: CoroutineDispatcher) {
51 executor = executorService 57 executor = executorService
52 rtcDispatcher = dispatcher 58 rtcDispatcher = dispatcher
53 } 59 }
@@ -56,8 +62,10 @@ internal fun overrideExecutorAndDispatcher(executorService: ExecutorService, dis @@ -56,8 +62,10 @@ internal fun overrideExecutorAndDispatcher(executorService: ExecutorService, dis
56 * Execute [action] on the RTC thread. The PeerConnection API 62 * Execute [action] on the RTC thread. The PeerConnection API
57 * is generally not thread safe, so all actions relating to 63 * is generally not thread safe, so all actions relating to
58 * peer connection objects should go through the RTC thread. 64 * peer connection objects should go through the RTC thread.
  65 + *
  66 + * @suppress
59 */ 67 */
60 -fun <T> executeOnRTCThread(action: () -> T) { 68 +internal fun <T> executeOnRTCThread(action: () -> T) {
61 if (Thread.currentThread().name.startsWith(EXECUTOR_THREADNAME_PREFIX)) { 69 if (Thread.currentThread().name.startsWith(EXECUTOR_THREADNAME_PREFIX)) {
62 action() 70 action()
63 } else { 71 } else {
@@ -69,8 +77,10 @@ fun <T> executeOnRTCThread(action: () -> T) { @@ -69,8 +77,10 @@ fun <T> executeOnRTCThread(action: () -> T) {
69 * Execute [action] synchronously on the RTC thread. The PeerConnection API 77 * Execute [action] synchronously on the RTC thread. The PeerConnection API
70 * is generally not thread safe, so all actions relating to 78 * is generally not thread safe, so all actions relating to
71 * peer connection objects should go through the RTC thread. 79 * peer connection objects should go through the RTC thread.
  80 + *
  81 + * @suppress
72 */ 82 */
73 -fun <T> executeBlockingOnRTCThread(action: () -> T): T { 83 +internal fun <T> executeBlockingOnRTCThread(action: () -> T): T {
74 return if (Thread.currentThread().name.startsWith(EXECUTOR_THREADNAME_PREFIX)) { 84 return if (Thread.currentThread().name.startsWith(EXECUTOR_THREADNAME_PREFIX)) {
75 action() 85 action()
76 } else { 86 } else {
@@ -83,7 +93,7 @@ fun <T> executeBlockingOnRTCThread(action: () -> T): T { @@ -83,7 +93,7 @@ fun <T> executeBlockingOnRTCThread(action: () -> T): T {
83 * is generally not thread safe, so all actions relating to 93 * is generally not thread safe, so all actions relating to
84 * peer connection objects should go through the RTC thread. 94 * peer connection objects should go through the RTC thread.
85 */ 95 */
86 -suspend fun <T> launchBlockingOnRTCThread(action: suspend CoroutineScope.() -> T): T = coroutineScope { 96 +internal suspend fun <T> launchBlockingOnRTCThread(action: suspend CoroutineScope.() -> T): T = coroutineScope {
87 return@coroutineScope if (Thread.currentThread().name.startsWith(EXECUTOR_THREADNAME_PREFIX)) { 97 return@coroutineScope if (Thread.currentThread().name.startsWith(EXECUTOR_THREADNAME_PREFIX)) {
88 this.action() 98 this.action()
89 } else { 99 } else {
  1 +plugins {
  2 + id "org.jetbrains.dokka"
  3 + id 'com.android.library'
  4 + id 'kotlin-android'
  5 + id 'kotlin-kapt'
  6 +}
  7 +
  8 +android {
  9 + namespace 'io.livekit.android.test'
  10 + compileSdkVersion androidSdk.compileVersion
  11 +
  12 + defaultConfig {
  13 + minSdkVersion androidSdk.minVersion
  14 + targetSdkVersion androidSdk.targetVersion
  15 +
  16 + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
  17 + consumerProguardFiles "consumer-rules.pro"
  18 + }
  19 +
  20 + lintOptions {
  21 + disable 'VisibleForTests'
  22 + }
  23 + buildTypes {
  24 + release {
  25 + minifyEnabled false
  26 + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
  27 + }
  28 + }
  29 + compileOptions {
  30 + sourceCompatibility java_version
  31 + targetCompatibility java_version
  32 + }
  33 + kotlinOptions {
  34 + freeCompilerArgs = ["-Xinline-classes", "-opt-in=kotlin.RequiresOptIn"]
  35 + jvmTarget = java_version
  36 + }
  37 + testOptions {
  38 + unitTests {
  39 + includeAndroidResources = true
  40 + }
  41 + }
  42 +}
  43 +
  44 +dokkaHtml {
  45 + moduleName.set("livekit-android-test")
  46 + dokkaSourceSets {
  47 + configureEach {
  48 + skipEmptyPackages.set(true)
  49 + includeNonPublic.set(false)
  50 + includes.from("module.md")
  51 + displayName.set("LiveKit Mocks")
  52 + sourceLink {
  53 + localDirectory.set(file("src/main/java"))
  54 +
  55 + // URL showing where the source code can be accessed through the web browser
  56 + remoteUrl.set(new URL(
  57 + "https://github.com/livekit/client-sdk-android/tree/master/livekit-android-test/src/main/java"))
  58 + // Suffix which is used to append the line number to the URL. Use #L for GitHub
  59 + remoteLineSuffix.set("#L")
  60 + }
  61 +
  62 + perPackageOption {
  63 + matchingRegex.set(".*\\.dagger.*")
  64 + suppress.set(true)
  65 + }
  66 +
  67 + perPackageOption {
  68 + matchingRegex.set(".*\\.util.*")
  69 + suppress.set(true)
  70 + }
  71 + }
  72 + }
  73 +}
  74 +
  75 +dependencies {
  76 +
  77 + implementation(project(":livekit-android-sdk"))
  78 + implementation libs.timber
  79 + implementation libs.coroutines.lib
  80 + implementation libs.kotlinx.serialization.json
  81 + api libs.webrtc
  82 + api libs.okhttp
  83 + api libs.audioswitch
  84 + implementation libs.androidx.annotation
  85 + api libs.protobuf.javalite
  86 + implementation libs.android.jain.sip.ri
  87 + implementation libs.junit
  88 + implementation libs.robolectric
  89 + implementation libs.mockito.core
  90 + implementation libs.mockito.kotlin
  91 + implementation libs.mockito.inline
  92 + implementation libs.androidx.test.core
  93 + implementation libs.coroutines.test
  94 + implementation libs.dagger.lib
  95 + kapt libs.dagger.compiler
  96 +
  97 + testImplementation libs.junit
  98 + testImplementation libs.robolectric
  99 + kaptTest libs.dagger.compiler
  100 +
  101 + androidTestImplementation libs.androidx.test.junit
  102 + androidTestImplementation libs.espresso
  103 +}
  104 +tasks.withType(Test).configureEach {
  105 + systemProperty "robolectric.logging.enabled", true
  106 +}
  107 +
  108 +apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
  109 +
  110 +afterEvaluate {
  111 + publishing {
  112 + publications {
  113 + // Creates a Maven publication called "release".
  114 + release(MavenPublication) {
  115 + // Applies the component for the release build variant.
  116 + from components.release
  117 +
  118 + // You can then customize attributes of the publication as shown below.
  119 + groupId = GROUP
  120 + artifactId = POM_ARTIFACT_ID
  121 + version = VERSION_NAME
  122 + }
  123 + }
  124 + }
  125 +}
  1 +POM_NAME=LiveKit Client Android Mocks for Testing
  2 +POM_ARTIFACT_ID=livekit-android-test
  3 +POM_PACKAGING=aar
  1 +# Add project specific ProGuard rules here.
  2 +# You can control the set of applied configuration files using the
  3 +# proguardFiles setting in build.gradle.
  4 +#
  5 +# For more details, see
  6 +# http://developer.android.com/guide/developing/tools/proguard.html
  7 +
  8 +# If your project uses WebView with JS, uncomment the following
  9 +# and specify the fully qualified class name to the JavaScript interface
  10 +# class:
  11 +#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
  12 +# public *;
  13 +#}
  14 +
  15 +# Uncomment this to preserve the line number information for
  16 +# debugging stack traces.
  17 +#-keepattributes SourceFile,LineNumberTable
  18 +
  19 +# If you keep the line number information, uncomment this to
  20 +# hide the original source file name.
  21 +#-renamesourcefileattribute SourceFile
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  3 +
  4 +</manifest>
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@ @@ -14,11 +14,11 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android 17 +package io.livekit.android.test
18 18
19 import com.google.common.util.concurrent.MoreExecutors 19 import com.google.common.util.concurrent.MoreExecutors
20 -import io.livekit.android.coroutines.TestCoroutineRule  
21 -import io.livekit.android.util.LoggingRule 20 +import io.livekit.android.test.coroutines.TestCoroutineRule
  21 +import io.livekit.android.test.util.LoggingRule
22 import io.livekit.android.webrtc.peerconnection.overrideExecutorAndDispatcher 22 import io.livekit.android.webrtc.peerconnection.overrideExecutorAndDispatcher
23 import kotlinx.coroutines.ExperimentalCoroutinesApi 23 import kotlinx.coroutines.ExperimentalCoroutinesApi
24 import kotlinx.coroutines.test.TestScope 24 import kotlinx.coroutines.test.TestScope
@@ -14,18 +14,18 @@ @@ -14,18 +14,18 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android 17 +package io.livekit.android.test
18 18
19 import android.content.Context 19 import android.content.Context
20 import androidx.test.core.app.ApplicationProvider 20 import androidx.test.core.app.ApplicationProvider
21 import com.google.protobuf.MessageLite 21 import com.google.protobuf.MessageLite
22 -import io.livekit.android.mock.MockPeerConnection  
23 -import io.livekit.android.mock.MockWebSocketFactory  
24 -import io.livekit.android.mock.dagger.DaggerTestLiveKitComponent  
25 -import io.livekit.android.mock.dagger.TestCoroutinesModule  
26 -import io.livekit.android.mock.dagger.TestLiveKitComponent  
27 import io.livekit.android.room.Room 22 import io.livekit.android.room.Room
28 -import io.livekit.android.room.SignalClientTest 23 +import io.livekit.android.test.mock.MockPeerConnection
  24 +import io.livekit.android.test.mock.MockWebSocketFactory
  25 +import io.livekit.android.test.mock.TestData
  26 +import io.livekit.android.test.mock.dagger.DaggerTestLiveKitComponent
  27 +import io.livekit.android.test.mock.dagger.TestCoroutinesModule
  28 +import io.livekit.android.test.mock.dagger.TestLiveKitComponent
29 import io.livekit.android.util.toOkioByteString 29 import io.livekit.android.util.toOkioByteString
30 import kotlinx.coroutines.ExperimentalCoroutinesApi 30 import kotlinx.coroutines.ExperimentalCoroutinesApi
31 import kotlinx.coroutines.launch 31 import kotlinx.coroutines.launch
@@ -44,10 +44,10 @@ import org.robolectric.RobolectricTestRunner @@ -44,10 +44,10 @@ import org.robolectric.RobolectricTestRunner
44 @RunWith(RobolectricTestRunner::class) 44 @RunWith(RobolectricTestRunner::class)
45 abstract class MockE2ETest : BaseTest() { 45 abstract class MockE2ETest : BaseTest() {
46 46
47 - internal lateinit var component: TestLiveKitComponent  
48 - internal lateinit var context: Context  
49 - internal lateinit var room: Room  
50 - internal lateinit var wsFactory: MockWebSocketFactory 47 + lateinit var component: TestLiveKitComponent
  48 + lateinit var context: Context
  49 + lateinit var room: Room
  50 + lateinit var wsFactory: MockWebSocketFactory
51 51
52 @Before 52 @Before
53 fun mocksSetup() { 53 fun mocksSetup() {
@@ -66,7 +66,7 @@ abstract class MockE2ETest : BaseTest() { @@ -66,7 +66,7 @@ abstract class MockE2ETest : BaseTest() {
66 room.release() 66 room.release()
67 } 67 }
68 68
69 - suspend fun connect(joinResponse: LivekitRtc.SignalResponse = SignalClientTest.JOIN) { 69 + suspend fun connect(joinResponse: LivekitRtc.SignalResponse = TestData.JOIN) {
70 connectSignal(joinResponse) 70 connectSignal(joinResponse)
71 connectPeerConnection() 71 connectPeerConnection()
72 } 72 }
@@ -74,7 +74,7 @@ abstract class MockE2ETest : BaseTest() { @@ -74,7 +74,7 @@ abstract class MockE2ETest : BaseTest() {
74 suspend fun connectSignal(joinResponse: LivekitRtc.SignalResponse) { 74 suspend fun connectSignal(joinResponse: LivekitRtc.SignalResponse) {
75 val job = coroutineRule.scope.launch { 75 val job = coroutineRule.scope.launch {
76 room.connect( 76 room.connect(
77 - url = SignalClientTest.EXAMPLE_URL, 77 + url = TestData.EXAMPLE_URL,
78 token = "", 78 token = "",
79 ) 79 )
80 } 80 }
@@ -84,23 +84,23 @@ abstract class MockE2ETest : BaseTest() { @@ -84,23 +84,23 @@ abstract class MockE2ETest : BaseTest() {
84 job.join() 84 job.join()
85 } 85 }
86 86
87 - suspend fun getSubscriberPeerConnection() = 87 + fun getSubscriberPeerConnection() =
88 component 88 component
89 .rtcEngine() 89 .rtcEngine()
90 .getSubscriberPeerConnection() as MockPeerConnection 90 .getSubscriberPeerConnection() as MockPeerConnection
91 91
92 - suspend fun getPublisherPeerConnection() = 92 + fun getPublisherPeerConnection() =
93 component 93 component
94 .rtcEngine() 94 .rtcEngine()
95 .getPublisherPeerConnection() as MockPeerConnection 95 .getPublisherPeerConnection() as MockPeerConnection
96 96
97 - suspend fun connectPeerConnection() {  
98 - simulateMessageFromServer(SignalClientTest.OFFER) 97 + fun connectPeerConnection() {
  98 + simulateMessageFromServer(TestData.OFFER)
99 val subPeerConnection = getSubscriberPeerConnection() 99 val subPeerConnection = getSubscriberPeerConnection()
100 subPeerConnection.moveToIceConnectionState(PeerConnection.IceConnectionState.CONNECTED) 100 subPeerConnection.moveToIceConnectionState(PeerConnection.IceConnectionState.CONNECTED)
101 } 101 }
102 102
103 - suspend fun disconnectPeerConnection() { 103 + fun disconnectPeerConnection() {
104 val subPeerConnection = component 104 val subPeerConnection = component
105 .rtcEngine() 105 .rtcEngine()
106 .getSubscriberPeerConnection() as MockPeerConnection 106 .getSubscriberPeerConnection() as MockPeerConnection
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -14,12 +14,16 @@ @@ -14,12 +14,16 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.assert 17 +package io.livekit.android.test.assert
18 18
19 import org.junit.Assert 19 import org.junit.Assert
20 20
21 -fun assertIsClass(expectedClass: Class<*>, actual: Any) {  
22 - val klazz = actual::class.java 21 +fun assertIsClass(expectedClass: Class<*>, actual: Any?) {
  22 + val klazz = if (actual == null) {
  23 + Nothing::class.java
  24 + } else {
  25 + actual::class.java
  26 + }
23 27
24 Assert.assertEquals(expectedClass, klazz) 28 Assert.assertEquals(expectedClass, klazz)
25 } 29 }
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.coroutines 17 +package io.livekit.android.test.coroutines
18 18
19 import kotlinx.coroutines.CancellationException 19 import kotlinx.coroutines.CancellationException
20 import kotlinx.coroutines.cancel 20 import kotlinx.coroutines.cancel
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.coroutines 17 +package io.livekit.android.test.coroutines
18 18
19 import kotlinx.coroutines.Dispatchers 19 import kotlinx.coroutines.Dispatchers
20 import kotlinx.coroutines.ExperimentalCoroutinesApi 20 import kotlinx.coroutines.ExperimentalCoroutinesApi
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -14,8 +14,10 @@ @@ -14,8 +14,10 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.events 17 +package io.livekit.android.test.events
18 18
  19 +import io.livekit.android.events.Event
  20 +import io.livekit.android.events.EventListenable
19 import kotlinx.coroutines.CoroutineScope 21 import kotlinx.coroutines.CoroutineScope
20 22
21 class EventCollector<T : Event>( 23 class EventCollector<T : Event>(
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -14,11 +14,10 @@ @@ -14,11 +14,10 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.events 17 +package io.livekit.android.test.events
18 18
19 -import io.livekit.android.coroutines.toListUntilSignal 19 +import io.livekit.android.test.coroutines.toListUntilSignal
20 import kotlinx.coroutines.CoroutineScope 20 import kotlinx.coroutines.CoroutineScope
21 -import kotlinx.coroutines.ExperimentalCoroutinesApi  
22 import kotlinx.coroutines.async 21 import kotlinx.coroutines.async
23 import kotlinx.coroutines.flow.Flow 22 import kotlinx.coroutines.flow.Flow
24 import kotlinx.coroutines.flow.MutableStateFlow 23 import kotlinx.coroutines.flow.MutableStateFlow
@@ -35,7 +34,6 @@ open class FlowCollector<T>( @@ -35,7 +34,6 @@ open class FlowCollector<T>(
35 /** 34 /**
36 * Stop collecting events. returns the events collected. 35 * Stop collecting events. returns the events collected.
37 */ 36 */
38 - @OptIn(ExperimentalCoroutinesApi::class)  
39 suspend fun stopCollecting(): List<T> { 37 suspend fun stopCollecting(): List<T> {
40 signal.compareAndSet(null, Unit) 38 signal.compareAndSet(null, Unit)
41 return collectEventsDeferred.await() 39 return collectEventsDeferred.await()
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import io.livekit.android.audio.AudioProcessingController 19 import io.livekit.android.audio.AudioProcessingController
20 import io.livekit.android.audio.AudioProcessorInterface 20 import io.livekit.android.audio.AudioProcessorInterface
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import livekit.org.webrtc.AudioTrack 19 import livekit.org.webrtc.AudioTrack
20 20
@@ -14,13 +14,14 @@ @@ -14,13 +14,14 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import livekit.org.webrtc.DataChannel 19 import livekit.org.webrtc.DataChannel
20 20
21 class MockDataChannel(private val label: String?) : DataChannel(1L) { 21 class MockDataChannel(private val label: String?) : DataChannel(1L) {
22 22
23 - var observer: DataChannel.Observer? = null 23 + var observer: Observer? = null
  24 + var sentBuffers = mutableListOf<Buffer?>()
24 override fun registerObserver(observer: Observer?) { 25 override fun registerObserver(observer: Observer?) {
25 this.observer = observer 26 this.observer = observer
26 } 27 }
@@ -46,6 +47,7 @@ class MockDataChannel(private val label: String?) : DataChannel(1L) { @@ -46,6 +47,7 @@ class MockDataChannel(private val label: String?) : DataChannel(1L) {
46 } 47 }
47 48
48 override fun send(buffer: Buffer?): Boolean { 49 override fun send(buffer: Buffer?): Boolean {
  50 + sentBuffers.add(buffer)
49 return true 51 return true
50 } 52 }
51 53
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import android.graphics.SurfaceTexture 19 import android.graphics.SurfaceTexture
20 import android.view.Surface 20 import android.view.Surface
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import io.livekit.android.room.provisions.LKObjects 19 import io.livekit.android.room.provisions.LKObjects
20 20
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import livekit.org.webrtc.AudioTrack 19 import livekit.org.webrtc.AudioTrack
20 import livekit.org.webrtc.MediaStream 20 import livekit.org.webrtc.MediaStream
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import livekit.org.webrtc.MediaStreamTrack 19 import livekit.org.webrtc.MediaStreamTrack
20 20
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -14,25 +14,19 @@ @@ -14,25 +14,19 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android 17 +package io.livekit.android.test.mock
18 18
19 -import androidx.test.ext.junit.runners.AndroidJUnit4  
20 -import androidx.test.platform.app.InstrumentationRegistry  
21 -import org.junit.Assert.assertEquals  
22 -import org.junit.Test  
23 -import org.junit.runner.RunWith 19 +import android.net.ConnectivityManager.NetworkCallback
  20 +import android.net.NetworkRequest
  21 +import io.livekit.android.room.network.NetworkCallbackRegistry
24 22
25 -/**  
26 - * Instrumented test, which will execute on an Android device.  
27 - *  
28 - * See [testing documentation](http://d.android.com/tools/testing).  
29 - */  
30 -@RunWith(AndroidJUnit4::class)  
31 -class ExampleInstrumentedTest {  
32 - @Test  
33 - fun useAppContext() {  
34 - // Context of the app under test.  
35 - val appContext = InstrumentationRegistry.getInstrumentation().targetContext  
36 - assertEquals("io.livekit.android.test", appContext.packageName) 23 +class MockNetworkCallbackRegistry : NetworkCallbackRegistry {
  24 + val networkCallbacks = mutableSetOf<NetworkCallback>()
  25 + override fun registerNetworkCallback(networkRequest: NetworkRequest, networkCallback: NetworkCallback) {
  26 + networkCallbacks.add(networkCallback)
  27 + }
  28 +
  29 + override fun unregisterNetworkCallback(networkCallback: NetworkCallback) {
  30 + networkCallbacks.remove(networkCallback)
37 } 31 }
38 } 32 }
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import livekit.org.webrtc.DataChannel 19 import livekit.org.webrtc.DataChannel
20 import livekit.org.webrtc.IceCandidate 20 import livekit.org.webrtc.IceCandidate
@@ -64,8 +64,13 @@ class MockPeerConnection( @@ -64,8 +64,13 @@ class MockPeerConnection(
64 return null 64 return null
65 } 65 }
66 66
  67 + val dataChannels = mutableMapOf<String, DataChannel>()
67 override fun createDataChannel(label: String?, init: DataChannel.Init?): DataChannel { 68 override fun createDataChannel(label: String?, init: DataChannel.Init?): DataChannel {
68 - return MockDataChannel(label) 69 + val dataChannel = MockDataChannel(label)
  70 + if (label != null) {
  71 + dataChannels[label] = dataChannel
  72 + }
  73 + return dataChannel
69 } 74 }
70 75
71 override fun createOffer(observer: SdpObserver?, constraints: MediaConstraints?) { 76 override fun createOffer(observer: SdpObserver?, constraints: MediaConstraints?) {
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import livekit.org.webrtc.RtpReceiver 19 import livekit.org.webrtc.RtpReceiver
20 import org.mockito.Mockito 20 import org.mockito.Mockito
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import livekit.org.webrtc.RtpSender 19 import livekit.org.webrtc.RtpSender
20 import org.mockito.Mockito 20 import org.mockito.Mockito
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import android.content.Context 19 import android.content.Context
20 import livekit.org.webrtc.CapturerObserver 20 import livekit.org.webrtc.CapturerObserver
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import livekit.org.webrtc.VideoSource 19 import livekit.org.webrtc.VideoSource
20 20
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import livekit.org.webrtc.VideoSink 19 import livekit.org.webrtc.VideoSink
20 import livekit.org.webrtc.VideoTrack 20 import livekit.org.webrtc.VideoTrack
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import okhttp3.Request 19 import okhttp3.Request
20 import okhttp3.WebSocket 20 import okhttp3.WebSocket
@@ -14,11 +14,12 @@ @@ -14,11 +14,12 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import com.google.protobuf.MessageLite 19 import com.google.protobuf.MessageLite
  20 +import io.livekit.android.test.util.toOkioByteString
  21 +import io.livekit.android.test.util.toPBByteString
20 import io.livekit.android.util.toOkioByteString 22 import io.livekit.android.util.toOkioByteString
21 -import io.livekit.android.util.toPBByteString  
22 import livekit.LivekitModels 23 import livekit.LivekitModels
23 import livekit.LivekitRtc.LeaveRequest 24 import livekit.LivekitRtc.LeaveRequest
24 import livekit.LivekitRtc.SignalRequest 25 import livekit.LivekitRtc.SignalRequest
@@ -14,32 +14,39 @@ @@ -14,32 +14,39 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock 17 +package io.livekit.android.test.mock
18 18
19 import livekit.LivekitModels 19 import livekit.LivekitModels
  20 +import livekit.LivekitRtc
20 21
21 object TestData { 22 object TestData {
22 23
  24 + const val EXAMPLE_URL = "ws://www.example.com"
  25 +
23 val LOCAL_AUDIO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) { 26 val LOCAL_AUDIO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) {
24 sid = "TR_local_audio_track_sid" 27 sid = "TR_local_audio_track_sid"
25 type = LivekitModels.TrackType.AUDIO 28 type = LivekitModels.TrackType.AUDIO
  29 + source = LivekitModels.TrackSource.MICROPHONE
26 build() 30 build()
27 } 31 }
28 val LOCAL_VIDEO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) { 32 val LOCAL_VIDEO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) {
29 sid = "TR_local_video_track_sid" 33 sid = "TR_local_video_track_sid"
30 type = LivekitModels.TrackType.VIDEO 34 type = LivekitModels.TrackType.VIDEO
  35 + source = LivekitModels.TrackSource.CAMERA
31 build() 36 build()
32 } 37 }
33 38
34 val REMOTE_AUDIO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) { 39 val REMOTE_AUDIO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) {
35 sid = "TR_remote_audio_track_sid" 40 sid = "TR_remote_audio_track_sid"
36 type = LivekitModels.TrackType.AUDIO 41 type = LivekitModels.TrackType.AUDIO
  42 + source = LivekitModels.TrackSource.MICROPHONE
37 build() 43 build()
38 } 44 }
39 45
40 val REMOTE_VIDEO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) { 46 val REMOTE_VIDEO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) {
41 sid = "TR_remote_video_track_sid" 47 sid = "TR_remote_video_track_sid"
42 type = LivekitModels.TrackType.VIDEO 48 type = LivekitModels.TrackType.VIDEO
  49 + source = LivekitModels.TrackSource.CAMERA
43 build() 50 build()
44 } 51 }
45 52
@@ -81,4 +88,218 @@ object TestData { @@ -81,4 +88,218 @@ object TestData {
81 active = true 88 active = true
82 build() 89 build()
83 } 90 }
  91 +
  92 + // Signal Responses
  93 + // /////////////////////////////////
  94 +
  95 + val JOIN = with(LivekitRtc.SignalResponse.newBuilder()) {
  96 + join = with(LivekitRtc.JoinResponse.newBuilder()) {
  97 + room = with(LivekitModels.Room.newBuilder()) {
  98 + name = "roomname"
  99 + build()
  100 + }
  101 + participant = LOCAL_PARTICIPANT
  102 + subscriberPrimary = true
  103 + addIceServers(
  104 + with(LivekitRtc.ICEServer.newBuilder()) {
  105 + addUrls("stun:stun.join.com:19302")
  106 + username = "username"
  107 + credential = "credential"
  108 + build()
  109 + },
  110 + )
  111 + serverVersion = "0.15.2"
  112 + build()
  113 + }
  114 + build()
  115 + }
  116 +
  117 + val RECONNECT = with(LivekitRtc.SignalResponse.newBuilder()) {
  118 + reconnect = with(LivekitRtc.ReconnectResponse.newBuilder()) {
  119 + addIceServers(
  120 + with(LivekitRtc.ICEServer.newBuilder()) {
  121 + addUrls("stun:stun.reconnect.com:19302")
  122 + username = "username"
  123 + credential = "credential"
  124 + build()
  125 + },
  126 + )
  127 + clientConfiguration = with(LivekitModels.ClientConfiguration.newBuilder()) {
  128 + forceRelay = LivekitModels.ClientConfigSetting.ENABLED
  129 + build()
  130 + }
  131 + build()
  132 + }
  133 + build()
  134 + }
  135 +
  136 + val OFFER = with(LivekitRtc.SignalResponse.newBuilder()) {
  137 + offer = with(LivekitRtc.SessionDescription.newBuilder()) {
  138 + sdp = "remote_offer"
  139 + type = "offer"
  140 + build()
  141 + }
  142 + build()
  143 + }
  144 +
  145 + val ROOM_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {
  146 + roomUpdate = with(LivekitRtc.RoomUpdate.newBuilder()) {
  147 + room = with(LivekitModels.Room.newBuilder()) {
  148 + sid = "room_sid"
  149 + metadata = "metadata"
  150 + activeRecording = true
  151 + build()
  152 + }
  153 + build()
  154 + }
  155 + build()
  156 + }
  157 +
  158 + val LOCAL_TRACK_PUBLISHED = with(LivekitRtc.SignalResponse.newBuilder()) {
  159 + trackPublished = with(LivekitRtc.TrackPublishedResponse.newBuilder()) {
  160 + cid = "local_cid"
  161 + track = LOCAL_AUDIO_TRACK
  162 + build()
  163 + }
  164 + build()
  165 + }
  166 +
  167 + val LOCAL_TRACK_UNPUBLISHED = with(LivekitRtc.SignalResponse.newBuilder()) {
  168 + trackUnpublished = with(LivekitRtc.TrackUnpublishedResponse.newBuilder()) {
  169 + trackSid = LOCAL_AUDIO_TRACK.sid
  170 + build()
  171 + }
  172 + build()
  173 + }
  174 +
  175 + val PERMISSION_CHANGE = with(LivekitRtc.SignalResponse.newBuilder()) {
  176 + update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
  177 + addParticipants(
  178 + LOCAL_PARTICIPANT.toBuilder()
  179 + .setPermission(
  180 + LivekitModels.ParticipantPermission.newBuilder()
  181 + .setCanPublish(false)
  182 + .setCanSubscribe(false)
  183 + .setCanPublishData(false)
  184 + .setHidden(false)
  185 + .setRecorder(false)
  186 + .build(),
  187 + )
  188 + .build(),
  189 + )
  190 + build()
  191 + }
  192 + build()
  193 + }
  194 +
  195 + val PARTICIPANT_JOIN = with(LivekitRtc.SignalResponse.newBuilder()) {
  196 + update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
  197 + addParticipants(REMOTE_PARTICIPANT)
  198 + build()
  199 + }
  200 + build()
  201 + }
  202 +
  203 + val PARTICIPANT_DISCONNECT = with(LivekitRtc.SignalResponse.newBuilder()) {
  204 + update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
  205 + val disconnectedParticipant = REMOTE_PARTICIPANT.toBuilder()
  206 + .setState(LivekitModels.ParticipantInfo.State.DISCONNECTED)
  207 + .build()
  208 +
  209 + addParticipants(disconnectedParticipant)
  210 + build()
  211 + }
  212 + build()
  213 + }
  214 +
  215 + val ACTIVE_SPEAKER_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {
  216 + speakersChanged = with(LivekitRtc.SpeakersChanged.newBuilder()) {
  217 + addSpeakers(REMOTE_SPEAKER_INFO)
  218 + build()
  219 + }
  220 + build()
  221 + }
  222 +
  223 + val LOCAL_PARTICIPANT_METADATA_CHANGED = with(LivekitRtc.SignalResponse.newBuilder()) {
  224 + update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
  225 + val participantMetadataChanged = LOCAL_PARTICIPANT.toBuilder()
  226 + .setMetadata("changed_metadata")
  227 + .setName("changed_name")
  228 + .build()
  229 +
  230 + addParticipants(participantMetadataChanged)
  231 + build()
  232 + }
  233 + build()
  234 + }
  235 +
  236 + val REMOTE_PARTICIPANT_METADATA_CHANGED = with(LivekitRtc.SignalResponse.newBuilder()) {
  237 + update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
  238 + val participantMetadataChanged = REMOTE_PARTICIPANT.toBuilder()
  239 + .setMetadata("changed_metadata")
  240 + .setName("changed_name")
  241 + .build()
  242 +
  243 + addParticipants(participantMetadataChanged)
  244 + build()
  245 + }
  246 + build()
  247 + }
  248 +
  249 + val CONNECTION_QUALITY = with(LivekitRtc.SignalResponse.newBuilder()) {
  250 + connectionQuality = with(LivekitRtc.ConnectionQualityUpdate.newBuilder()) {
  251 + addUpdates(
  252 + with(LivekitRtc.ConnectionQualityInfo.newBuilder()) {
  253 + participantSid = JOIN.join.participant.sid
  254 + quality = LivekitModels.ConnectionQuality.EXCELLENT
  255 + build()
  256 + },
  257 + )
  258 + build()
  259 + }
  260 + build()
  261 + }
  262 +
  263 + val STREAM_STATE_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {
  264 + streamStateUpdate = with(LivekitRtc.StreamStateUpdate.newBuilder()) {
  265 + addStreamStates(
  266 + with(LivekitRtc.StreamStateInfo.newBuilder()) {
  267 + participantSid = REMOTE_PARTICIPANT.sid
  268 + trackSid = REMOTE_AUDIO_TRACK.sid
  269 + state = LivekitRtc.StreamState.ACTIVE
  270 + build()
  271 + },
  272 + )
  273 + build()
  274 + }
  275 + build()
  276 + }
  277 +
  278 + val SUBSCRIPTION_PERMISSION_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {
  279 + subscriptionPermissionUpdate = with(LivekitRtc.SubscriptionPermissionUpdate.newBuilder()) {
  280 + participantSid = REMOTE_PARTICIPANT.sid
  281 + trackSid = REMOTE_AUDIO_TRACK.sid
  282 + allowed = false
  283 + build()
  284 + }
  285 + build()
  286 + }
  287 +
  288 + val REFRESH_TOKEN = with(LivekitRtc.SignalResponse.newBuilder()) {
  289 + refreshToken = "refresh_token"
  290 + build()
  291 + }
  292 +
  293 + val PONG = with(LivekitRtc.SignalResponse.newBuilder()) {
  294 + pong = 1L
  295 + build()
  296 + }
  297 +
  298 + val LEAVE = with(LivekitRtc.SignalResponse.newBuilder()) {
  299 + leave = with(LivekitRtc.LeaveRequest.newBuilder()) {
  300 + reason = LivekitModels.DisconnectReason.SERVER_SHUTDOWN
  301 + build()
  302 + }
  303 + build()
  304 + }
84 } 305 }
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock.dagger 17 +package io.livekit.android.test.mock.dagger
18 18
19 import dagger.Binds 19 import dagger.Binds
20 import dagger.Module 20 import dagger.Module
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock.dagger 17 +package io.livekit.android.test.mock.dagger
18 18
19 import dagger.Module 19 import dagger.Module
20 import dagger.Provides 20 import dagger.Provides
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock.dagger 17 +package io.livekit.android.test.mock.dagger
18 18
19 import android.content.Context 19 import android.content.Context
20 import dagger.BindsInstance 20 import dagger.BindsInstance
@@ -22,8 +22,9 @@ import dagger.Component @@ -22,8 +22,9 @@ import dagger.Component
22 import io.livekit.android.dagger.JsonFormatModule 22 import io.livekit.android.dagger.JsonFormatModule
23 import io.livekit.android.dagger.LiveKitComponent 23 import io.livekit.android.dagger.LiveKitComponent
24 import io.livekit.android.dagger.MemoryModule 24 import io.livekit.android.dagger.MemoryModule
25 -import io.livekit.android.mock.MockWebSocketFactory  
26 import io.livekit.android.room.RTCEngine 25 import io.livekit.android.room.RTCEngine
  26 +import io.livekit.android.test.mock.MockNetworkCallbackRegistry
  27 +import io.livekit.android.test.mock.MockWebSocketFactory
27 import javax.inject.Singleton 28 import javax.inject.Singleton
28 29
29 @Singleton 30 @Singleton
@@ -37,12 +38,14 @@ import javax.inject.Singleton @@ -37,12 +38,14 @@ import javax.inject.Singleton
37 MemoryModule::class, 38 MemoryModule::class,
38 ], 39 ],
39 ) 40 )
40 -internal interface TestLiveKitComponent : LiveKitComponent { 41 +interface TestLiveKitComponent : LiveKitComponent {
41 42
42 fun websocketFactory(): MockWebSocketFactory 43 fun websocketFactory(): MockWebSocketFactory
43 44
44 fun rtcEngine(): RTCEngine 45 fun rtcEngine(): RTCEngine
45 46
  47 + fun networkCallbackRegistry(): MockNetworkCallbackRegistry
  48 +
46 @Component.Factory 49 @Component.Factory
47 interface Factory { 50 interface Factory {
48 fun create( 51 fun create(
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock.dagger 17 +package io.livekit.android.test.mock.dagger
18 18
19 import android.content.Context 19 import android.content.Context
20 import android.javax.sdp.SdpFactory 20 import android.javax.sdp.SdpFactory
@@ -23,8 +23,8 @@ import dagger.Provides @@ -23,8 +23,8 @@ import dagger.Provides
23 import io.livekit.android.audio.AudioProcessingController 23 import io.livekit.android.audio.AudioProcessingController
24 import io.livekit.android.dagger.CapabilitiesGetter 24 import io.livekit.android.dagger.CapabilitiesGetter
25 import io.livekit.android.dagger.InjectionNames 25 import io.livekit.android.dagger.InjectionNames
26 -import io.livekit.android.mock.MockAudioProcessingController  
27 -import io.livekit.android.mock.MockEglBase 26 +import io.livekit.android.test.mock.MockAudioProcessingController
  27 +import io.livekit.android.test.mock.MockEglBase
28 import livekit.org.webrtc.* 28 import livekit.org.webrtc.*
29 import javax.inject.Named 29 import javax.inject.Named
30 import javax.inject.Singleton 30 import javax.inject.Singleton
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -14,14 +14,19 @@ @@ -14,14 +14,19 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.mock.dagger 17 +package io.livekit.android.test.mock.dagger
18 18
  19 +import android.net.ConnectivityManager
19 import dagger.Module 20 import dagger.Module
20 import dagger.Provides 21 import dagger.Provides
21 import dagger.Reusable 22 import dagger.Reusable
22 -import io.livekit.android.mock.MockWebSocketFactory 23 +import io.livekit.android.memory.CloseableManager
  24 +import io.livekit.android.room.network.NetworkCallbackManagerFactory
  25 +import io.livekit.android.room.network.NetworkCallbackManagerImpl
23 import io.livekit.android.stats.NetworkInfo 26 import io.livekit.android.stats.NetworkInfo
24 import io.livekit.android.stats.NetworkType 27 import io.livekit.android.stats.NetworkType
  28 +import io.livekit.android.test.mock.MockNetworkCallbackRegistry
  29 +import io.livekit.android.test.mock.MockWebSocketFactory
25 import okhttp3.OkHttpClient 30 import okhttp3.OkHttpClient
26 import okhttp3.Response 31 import okhttp3.Response
27 import okhttp3.WebSocket 32 import okhttp3.WebSocket
@@ -62,4 +67,22 @@ object TestWebModule { @@ -62,4 +67,22 @@ object TestWebModule {
62 override fun getNetworkType() = NetworkType.WIFI 67 override fun getNetworkType() = NetworkType.WIFI
63 } 68 }
64 } 69 }
  70 +
  71 + @Provides
  72 + @Singleton
  73 + fun networkCallbackRegistrar(): MockNetworkCallbackRegistry {
  74 + return MockNetworkCallbackRegistry()
  75 + }
  76 +
  77 + @Provides
  78 + @Reusable
  79 + fun networkCallbackManagerFactory(
  80 + closeableManager: CloseableManager,
  81 + registrar: MockNetworkCallbackRegistry,
  82 + ): NetworkCallbackManagerFactory {
  83 + return { networkCallback: ConnectivityManager.NetworkCallback ->
  84 + NetworkCallbackManagerImpl(networkCallback, registrar)
  85 + .apply { closeableManager.registerClosable(this) }
  86 + }
  87 + }
65 } 88 }
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.util 17 +package io.livekit.android.test.util
18 18
19 import com.google.protobuf.ByteString 19 import com.google.protobuf.ByteString
20 import okio.ByteString.Companion.toByteString 20 import okio.ByteString.Companion.toByteString
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -14,10 +14,11 @@ @@ -14,10 +14,11 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package io.livekit.android.util 17 +package io.livekit.android.test.util
18 18
19 import android.util.Log 19 import android.util.Log
20 import io.livekit.android.LiveKit 20 import io.livekit.android.LiveKit
  21 +import io.livekit.android.util.LoggingLevel
21 import org.junit.rules.TestRule 22 import org.junit.rules.TestRule
22 import org.junit.runner.Description 23 import org.junit.runner.Description
23 import org.junit.runners.model.Statement 24 import org.junit.runners.model.Statement
@@ -16,9 +16,9 @@ @@ -16,9 +16,9 @@
16 16
17 package livekit.org.webrtc 17 package livekit.org.webrtc
18 18
19 -import io.livekit.android.mock.MockPeerConnection  
20 -import io.livekit.android.mock.MockVideoSource  
21 -import io.livekit.android.mock.MockVideoStreamTrack 19 +import io.livekit.android.test.mock.MockPeerConnection
  20 +import io.livekit.android.test.mock.MockVideoSource
  21 +import io.livekit.android.test.mock.MockVideoStreamTrack
22 22
23 class MockPeerConnectionFactory : PeerConnectionFactory(1L) { 23 class MockPeerConnectionFactory : PeerConnectionFactory(1L) {
24 override fun createPeerConnectionInternal( 24 override fun createPeerConnectionInternal(
@@ -16,8 +16,8 @@ @@ -16,8 +16,8 @@
16 16
17 package livekit.org.webrtc 17 package livekit.org.webrtc
18 18
19 -import io.livekit.android.mock.MockRtpReceiver  
20 -import io.livekit.android.mock.MockRtpSender 19 +import io.livekit.android.test.mock.MockRtpReceiver
  20 +import io.livekit.android.test.mock.MockRtpSender
21 import livekit.org.webrtc.RtpTransceiver.RtpTransceiverDirection 21 import livekit.org.webrtc.RtpTransceiver.RtpTransceiverDirection
22 import org.mockito.Mockito 22 import org.mockito.Mockito
23 23
@@ -16,9 +16,11 @@ @@ -16,9 +16,11 @@
16 16
17 package io.livekit.android.room 17 package io.livekit.android.room
18 18
19 -import io.livekit.android.MockE2ETest 19 +import io.livekit.android.test.MockE2ETest
  20 +import io.livekit.android.test.mock.TestData
  21 +import io.livekit.android.test.util.toOkioByteString
  22 +import io.livekit.android.test.util.toPBByteString
20 import io.livekit.android.util.toOkioByteString 23 import io.livekit.android.util.toOkioByteString
21 -import io.livekit.android.util.toPBByteString  
22 import kotlinx.coroutines.ExperimentalCoroutinesApi 24 import kotlinx.coroutines.ExperimentalCoroutinesApi
23 import livekit.LivekitModels 25 import livekit.LivekitModels
24 import livekit.LivekitRtc 26 import livekit.LivekitRtc
@@ -44,7 +46,7 @@ class RTCEngineMockE2ETest : MockE2ETest() { @@ -44,7 +46,7 @@ class RTCEngineMockE2ETest : MockE2ETest() {
44 @Test 46 @Test
45 fun iceServersSetOnJoin() = runTest { 47 fun iceServersSetOnJoin() = runTest {
46 connect() 48 connect()
47 - val sentIceServers = SignalClientTest.JOIN.join.iceServersList 49 + val sentIceServers = TestData.JOIN.join.iceServersList
48 .map { it.toWebrtc() } 50 .map { it.toWebrtc() }
49 val subPeerConnection = getSubscriberPeerConnection() 51 val subPeerConnection = getSubscriberPeerConnection()
50 52
@@ -55,7 +57,7 @@ class RTCEngineMockE2ETest : MockE2ETest() { @@ -55,7 +57,7 @@ class RTCEngineMockE2ETest : MockE2ETest() {
55 fun iceSubscriberConnect() = runTest { 57 fun iceSubscriberConnect() = runTest {
56 connect() 58 connect()
57 assertEquals( 59 assertEquals(
58 - SignalClientTest.OFFER.offer.sdp, 60 + TestData.OFFER.offer.sdp,
59 getSubscriberPeerConnection().remoteDescription?.description, 61 getSubscriberPeerConnection().remoteDescription?.description,
60 ) 62 )
61 63
@@ -113,19 +115,19 @@ class RTCEngineMockE2ETest : MockE2ETest() { @@ -113,19 +115,19 @@ class RTCEngineMockE2ETest : MockE2ETest() {
113 connect() 115 connect()
114 116
115 val oldToken = wsFactory.request.url.queryParameter(SignalClient.CONNECT_QUERY_TOKEN) 117 val oldToken = wsFactory.request.url.queryParameter(SignalClient.CONNECT_QUERY_TOKEN)
116 - wsFactory.listener.onMessage(wsFactory.ws, SignalClientTest.REFRESH_TOKEN.toOkioByteString()) 118 + wsFactory.listener.onMessage(wsFactory.ws, TestData.REFRESH_TOKEN.toOkioByteString())
117 wsFactory.listener.onFailure(wsFactory.ws, Exception(), null) 119 wsFactory.listener.onFailure(wsFactory.ws, Exception(), null)
118 120
119 testScheduler.advanceUntilIdle() 121 testScheduler.advanceUntilIdle()
120 val newToken = wsFactory.request.url.queryParameter(SignalClient.CONNECT_QUERY_TOKEN) 122 val newToken = wsFactory.request.url.queryParameter(SignalClient.CONNECT_QUERY_TOKEN)
121 Assert.assertNotEquals(oldToken, newToken) 123 Assert.assertNotEquals(oldToken, newToken)
122 - assertEquals(SignalClientTest.REFRESH_TOKEN.refreshToken, newToken) 124 + assertEquals(TestData.REFRESH_TOKEN.refreshToken, newToken)
123 } 125 }
124 126
125 @Test 127 @Test
126 fun relayConfiguration() = runTest { 128 fun relayConfiguration() = runTest {
127 connect( 129 connect(
128 - with(SignalClientTest.JOIN.toBuilder()) { 130 + with(TestData.JOIN.toBuilder()) {
129 join = with(join.toBuilder()) { 131 join = with(join.toBuilder()) {
130 clientConfiguration = with(LivekitModels.ClientConfiguration.newBuilder()) { 132 clientConfiguration = with(LivekitModels.ClientConfiguration.newBuilder()) {
131 forceRelay = LivekitModels.ClientConfigSetting.ENABLED 133 forceRelay = LivekitModels.ClientConfigSetting.ENABLED
@@ -147,6 +149,6 @@ class RTCEngineMockE2ETest : MockE2ETest() { @@ -147,6 +149,6 @@ class RTCEngineMockE2ETest : MockE2ETest() {
147 149
148 testScheduler.advanceUntilIdle() 150 testScheduler.advanceUntilIdle()
149 val sid = wsFactory.request.url.queryParameter(SignalClient.CONNECT_QUERY_PARTICIPANT_SID) 151 val sid = wsFactory.request.url.queryParameter(SignalClient.CONNECT_QUERY_PARTICIPANT_SID)
150 - assertEquals(SignalClientTest.JOIN.join.participant.sid, sid) 152 + assertEquals(TestData.JOIN.join.participant.sid, sid)
151 } 153 }
152 } 154 }
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -17,12 +17,12 @@ @@ -17,12 +17,12 @@
17 package io.livekit.android.room 17 package io.livekit.android.room
18 18
19 import com.google.protobuf.ByteString 19 import com.google.protobuf.ByteString
20 -import io.livekit.android.MockE2ETest  
21 -import io.livekit.android.assert.assertIsClass  
22 -import io.livekit.android.events.EventCollector  
23 import io.livekit.android.events.RoomEvent 20 import io.livekit.android.events.RoomEvent
24 -import io.livekit.android.mock.MockDataChannel  
25 -import io.livekit.android.mock.MockPeerConnection 21 +import io.livekit.android.test.MockE2ETest
  22 +import io.livekit.android.test.assert.assertIsClass
  23 +import io.livekit.android.test.events.EventCollector
  24 +import io.livekit.android.test.mock.MockDataChannel
  25 +import io.livekit.android.test.mock.MockPeerConnection
26 import kotlinx.coroutines.ExperimentalCoroutinesApi 26 import kotlinx.coroutines.ExperimentalCoroutinesApi
27 import livekit.LivekitModels.DataPacket 27 import livekit.LivekitModels.DataPacket
28 import livekit.LivekitModels.UserPacket 28 import livekit.LivekitModels.UserPacket
@@ -16,21 +16,20 @@ @@ -16,21 +16,20 @@
16 16
17 package io.livekit.android.room 17 package io.livekit.android.room
18 18
19 -import android.content.Context  
20 -import android.net.ConnectivityManager  
21 import android.net.Network 19 import android.net.Network
22 -import androidx.test.platform.app.InstrumentationRegistry  
23 -import io.livekit.android.MockE2ETest  
24 -import io.livekit.android.assert.assertIsClassList  
25 import io.livekit.android.events.* 20 import io.livekit.android.events.*
26 -import io.livekit.android.mock.MockAudioStreamTrack  
27 -import io.livekit.android.mock.MockMediaStream  
28 -import io.livekit.android.mock.MockRtpReceiver  
29 -import io.livekit.android.mock.TestData  
30 -import io.livekit.android.mock.createMediaStreamId  
31 import io.livekit.android.room.participant.ConnectionQuality 21 import io.livekit.android.room.participant.ConnectionQuality
32 import io.livekit.android.room.track.LocalAudioTrack 22 import io.livekit.android.room.track.LocalAudioTrack
33 import io.livekit.android.room.track.Track 23 import io.livekit.android.room.track.Track
  24 +import io.livekit.android.test.MockE2ETest
  25 +import io.livekit.android.test.assert.assertIsClassList
  26 +import io.livekit.android.test.events.EventCollector
  27 +import io.livekit.android.test.events.FlowCollector
  28 +import io.livekit.android.test.mock.MockAudioStreamTrack
  29 +import io.livekit.android.test.mock.MockMediaStream
  30 +import io.livekit.android.test.mock.MockRtpReceiver
  31 +import io.livekit.android.test.mock.TestData
  32 +import io.livekit.android.test.mock.createMediaStreamId
34 import io.livekit.android.util.flow 33 import io.livekit.android.util.flow
35 import io.livekit.android.util.toOkioByteString 34 import io.livekit.android.util.toOkioByteString
36 import junit.framework.Assert.assertEquals 35 import junit.framework.Assert.assertEquals
@@ -45,8 +44,6 @@ import org.junit.Test @@ -45,8 +44,6 @@ import org.junit.Test
45 import org.junit.runner.RunWith 44 import org.junit.runner.RunWith
46 import org.mockito.Mockito 45 import org.mockito.Mockito
47 import org.robolectric.RobolectricTestRunner 46 import org.robolectric.RobolectricTestRunner
48 -import org.robolectric.Shadows.shadowOf  
49 -import org.robolectric.shadows.ShadowConnectivityManager  
50 47
51 @ExperimentalCoroutinesApi 48 @ExperimentalCoroutinesApi
52 @RunWith(RobolectricTestRunner::class) 49 @RunWith(RobolectricTestRunner::class)
@@ -79,8 +76,8 @@ class RoomMockE2ETest : MockE2ETest() { @@ -79,8 +76,8 @@ class RoomMockE2ETest : MockE2ETest() {
79 76
80 @Test 77 @Test
81 fun connectNoEventsWithRemoteParticipant() = runTest { 78 fun connectNoEventsWithRemoteParticipant() = runTest {
82 - val joinResponse = with(SignalClientTest.JOIN.toBuilder()) {  
83 - join = with(SignalClientTest.JOIN.join.toBuilder()) { 79 + val joinResponse = with(TestData.JOIN.toBuilder()) {
  80 + join = with(TestData.JOIN.join.toBuilder()) {
84 addOtherParticipants(TestData.REMOTE_PARTICIPANT) 81 addOtherParticipants(TestData.REMOTE_PARTICIPANT)
85 build() 82 build()
86 } 83 }
@@ -104,7 +101,7 @@ class RoomMockE2ETest : MockE2ETest() { @@ -104,7 +101,7 @@ class RoomMockE2ETest : MockE2ETest() {
104 val job = coroutineRule.scope.launch { 101 val job = coroutineRule.scope.launch {
105 try { 102 try {
106 room.connect( 103 room.connect(
107 - url = SignalClientTest.EXAMPLE_URL, 104 + url = TestData.EXAMPLE_URL,
108 token = "", 105 token = "",
109 ) 106 )
110 } catch (e: Throwable) { 107 } catch (e: Throwable) {
@@ -123,8 +120,8 @@ class RoomMockE2ETest : MockE2ETest() { @@ -123,8 +120,8 @@ class RoomMockE2ETest : MockE2ETest() {
123 fun roomUpdateTest() = runTest { 120 fun roomUpdateTest() = runTest {
124 connect() 121 connect()
125 val eventCollector = EventCollector(room.events, coroutineRule.scope) 122 val eventCollector = EventCollector(room.events, coroutineRule.scope)
126 - val roomUpdate = SignalClientTest.ROOM_UPDATE  
127 - wsFactory.listener.onMessage(wsFactory.ws, SignalClientTest.ROOM_UPDATE.toOkioByteString()) 123 + val roomUpdate = TestData.ROOM_UPDATE
  124 + wsFactory.listener.onMessage(wsFactory.ws, TestData.ROOM_UPDATE.toOkioByteString())
128 val events = eventCollector.stopCollecting() 125 val events = eventCollector.stopCollecting()
129 126
130 assertEquals( 127 assertEquals(
@@ -150,7 +147,7 @@ class RoomMockE2ETest : MockE2ETest() { @@ -150,7 +147,7 @@ class RoomMockE2ETest : MockE2ETest() {
150 val eventCollector = EventCollector(room.events, coroutineRule.scope) 147 val eventCollector = EventCollector(room.events, coroutineRule.scope)
151 wsFactory.listener.onMessage( 148 wsFactory.listener.onMessage(
152 wsFactory.ws, 149 wsFactory.ws,
153 - SignalClientTest.CONNECTION_QUALITY.toOkioByteString(), 150 + TestData.CONNECTION_QUALITY.toOkioByteString(),
154 ) 151 )
155 val events = eventCollector.stopCollecting() 152 val events = eventCollector.stopCollecting()
156 153
@@ -164,7 +161,7 @@ class RoomMockE2ETest : MockE2ETest() { @@ -164,7 +161,7 @@ class RoomMockE2ETest : MockE2ETest() {
164 connect() 161 connect()
165 162
166 val eventCollector = EventCollector(room.events, coroutineRule.scope) 163 val eventCollector = EventCollector(room.events, coroutineRule.scope)
167 - simulateMessageFromServer(SignalClientTest.PARTICIPANT_JOIN) 164 + simulateMessageFromServer(TestData.PARTICIPANT_JOIN)
168 val events = eventCollector.stopCollecting() 165 val events = eventCollector.stopCollecting()
169 166
170 assertIsClassList( 167 assertIsClassList(
@@ -182,13 +179,13 @@ class RoomMockE2ETest : MockE2ETest() { @@ -182,13 +179,13 @@ class RoomMockE2ETest : MockE2ETest() {
182 connect() 179 connect()
183 wsFactory.listener.onMessage( 180 wsFactory.listener.onMessage(
184 wsFactory.ws, 181 wsFactory.ws,
185 - SignalClientTest.PARTICIPANT_JOIN.toOkioByteString(), 182 + TestData.PARTICIPANT_JOIN.toOkioByteString(),
186 ) 183 )
187 184
188 val eventCollector = EventCollector(room.events, coroutineRule.scope) 185 val eventCollector = EventCollector(room.events, coroutineRule.scope)
189 wsFactory.listener.onMessage( 186 wsFactory.listener.onMessage(
190 wsFactory.ws, 187 wsFactory.ws,
191 - SignalClientTest.PARTICIPANT_DISCONNECT.toOkioByteString(), 188 + TestData.PARTICIPANT_DISCONNECT.toOkioByteString(),
192 ) 189 )
193 val events = eventCollector.stopCollecting() 190 val events = eventCollector.stopCollecting()
194 191
@@ -209,7 +206,7 @@ class RoomMockE2ETest : MockE2ETest() { @@ -209,7 +206,7 @@ class RoomMockE2ETest : MockE2ETest() {
209 val eventCollector = EventCollector(room.events, coroutineRule.scope) 206 val eventCollector = EventCollector(room.events, coroutineRule.scope)
210 wsFactory.listener.onMessage( 207 wsFactory.listener.onMessage(
211 wsFactory.ws, 208 wsFactory.ws,
212 - SignalClientTest.ACTIVE_SPEAKER_UPDATE.toOkioByteString(), 209 + TestData.ACTIVE_SPEAKER_UPDATE.toOkioByteString(),
213 ) 210 )
214 val events = eventCollector.stopCollecting() 211 val events = eventCollector.stopCollecting()
215 212
@@ -223,7 +220,7 @@ class RoomMockE2ETest : MockE2ETest() { @@ -223,7 +220,7 @@ class RoomMockE2ETest : MockE2ETest() {
223 220
224 wsFactory.listener.onMessage( 221 wsFactory.listener.onMessage(
225 wsFactory.ws, 222 wsFactory.ws,
226 - SignalClientTest.PARTICIPANT_JOIN.toOkioByteString(), 223 + TestData.PARTICIPANT_JOIN.toOkioByteString(),
227 ) 224 )
228 225
229 // We intentionally don't emit if the track isn't subscribed, so need to 226 // We intentionally don't emit if the track isn't subscribed, so need to
@@ -243,7 +240,7 @@ class RoomMockE2ETest : MockE2ETest() { @@ -243,7 +240,7 @@ class RoomMockE2ETest : MockE2ETest() {
243 val eventCollector = EventCollector(room.events, coroutineRule.scope) 240 val eventCollector = EventCollector(room.events, coroutineRule.scope)
244 wsFactory.listener.onMessage( 241 wsFactory.listener.onMessage(
245 wsFactory.ws, 242 wsFactory.ws,
246 - SignalClientTest.STREAM_STATE_UPDATE.toOkioByteString(), 243 + TestData.STREAM_STATE_UPDATE.toOkioByteString(),
247 ) 244 )
248 val events = eventCollector.stopCollecting() 245 val events = eventCollector.stopCollecting()
249 246
@@ -260,7 +257,7 @@ class RoomMockE2ETest : MockE2ETest() { @@ -260,7 +257,7 @@ class RoomMockE2ETest : MockE2ETest() {
260 257
261 wsFactory.listener.onMessage( 258 wsFactory.listener.onMessage(
262 wsFactory.ws, 259 wsFactory.ws,
263 - SignalClientTest.PARTICIPANT_JOIN.toOkioByteString(), 260 + TestData.PARTICIPANT_JOIN.toOkioByteString(),
264 ) 261 )
265 room.onAddTrack( 262 room.onAddTrack(
266 MockRtpReceiver.create(), 263 MockRtpReceiver.create(),
@@ -277,7 +274,7 @@ class RoomMockE2ETest : MockE2ETest() { @@ -277,7 +274,7 @@ class RoomMockE2ETest : MockE2ETest() {
277 val eventCollector = EventCollector(room.events, coroutineRule.scope) 274 val eventCollector = EventCollector(room.events, coroutineRule.scope)
278 wsFactory.listener.onMessage( 275 wsFactory.listener.onMessage(
279 wsFactory.ws, 276 wsFactory.ws,
280 - SignalClientTest.SUBSCRIPTION_PERMISSION_UPDATE.toOkioByteString(), 277 + TestData.SUBSCRIPTION_PERMISSION_UPDATE.toOkioByteString(),
281 ) 278 )
282 val events = eventCollector.stopCollecting() 279 val events = eventCollector.stopCollecting()
283 280
@@ -297,15 +294,12 @@ class RoomMockE2ETest : MockE2ETest() { @@ -297,15 +294,12 @@ class RoomMockE2ETest : MockE2ETest() {
297 val eventCollector = FlowCollector(engine::connectionState.flow, coroutineRule.scope) 294 val eventCollector = FlowCollector(engine::connectionState.flow, coroutineRule.scope)
298 val network = Mockito.mock(Network::class.java) 295 val network = Mockito.mock(Network::class.java)
299 296
300 - val connectivityManager = InstrumentationRegistry.getInstrumentation()  
301 - .context  
302 - .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager  
303 - val shadowConnectivityManager: ShadowConnectivityManager = shadowOf(connectivityManager) 297 + val networkCallbackRegistry = component.networkCallbackRegistry()
304 298
305 - shadowConnectivityManager.networkCallbacks.forEach { callback -> 299 + networkCallbackRegistry.networkCallbacks.forEach { callback ->
306 callback.onLost(network) 300 callback.onLost(network)
307 } 301 }
308 - shadowConnectivityManager.networkCallbacks.forEach { callback -> 302 + networkCallbackRegistry.networkCallbacks.forEach { callback ->
309 callback.onAvailable(network) 303 callback.onAvailable(network)
310 } 304 }
311 305
@@ -327,7 +321,7 @@ class RoomMockE2ETest : MockE2ETest() { @@ -327,7 +321,7 @@ class RoomMockE2ETest : MockE2ETest() {
327 val eventCollector = EventCollector(room.events, coroutineRule.scope) 321 val eventCollector = EventCollector(room.events, coroutineRule.scope)
328 wsFactory.listener.onMessage( 322 wsFactory.listener.onMessage(
329 wsFactory.ws, 323 wsFactory.ws,
330 - SignalClientTest.LEAVE.toOkioByteString(), 324 + TestData.LEAVE.toOkioByteString(),
331 ) 325 )
332 val events = eventCollector.stopCollecting() 326 val events = eventCollector.stopCollecting()
333 327
@@ -342,7 +336,7 @@ class RoomMockE2ETest : MockE2ETest() { @@ -342,7 +336,7 @@ class RoomMockE2ETest : MockE2ETest() {
342 room.localParticipant.publishAudioTrack( 336 room.localParticipant.publishAudioTrack(
343 LocalAudioTrack( 337 LocalAudioTrack(
344 "", 338 "",
345 - MockAudioStreamTrack(id = SignalClientTest.LOCAL_TRACK_PUBLISHED.trackPublished.cid), 339 + MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
346 ), 340 ),
347 ) 341 )
348 342
@@ -350,7 +344,7 @@ class RoomMockE2ETest : MockE2ETest() { @@ -350,7 +344,7 @@ class RoomMockE2ETest : MockE2ETest() {
350 344
351 wsFactory.listener.onMessage( 345 wsFactory.listener.onMessage(
352 wsFactory.ws, 346 wsFactory.ws,
353 - SignalClientTest.LEAVE.toOkioByteString(), 347 + TestData.LEAVE.toOkioByteString(),
354 ) 348 )
355 room.disconnect() 349 room.disconnect()
356 val events = eventCollector.stopCollecting() 350 val events = eventCollector.stopCollecting()
@@ -388,7 +382,7 @@ class RoomMockE2ETest : MockE2ETest() { @@ -388,7 +382,7 @@ class RoomMockE2ETest : MockE2ETest() {
388 room.localParticipant.publishAudioTrack( 382 room.localParticipant.publishAudioTrack(
389 LocalAudioTrack( 383 LocalAudioTrack(
390 "", 384 "",
391 - MockAudioStreamTrack(id = SignalClientTest.LOCAL_TRACK_PUBLISHED.trackPublished.cid), 385 + MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
392 ), 386 ),
393 ) 387 )
394 388
@@ -405,7 +399,7 @@ class RoomMockE2ETest : MockE2ETest() { @@ -405,7 +399,7 @@ class RoomMockE2ETest : MockE2ETest() {
405 fun disconnectCleansUpParticipants() = runTest { 399 fun disconnectCleansUpParticipants() = runTest {
406 connect() 400 connect()
407 401
408 - room.onUpdateParticipants(SignalClientTest.PARTICIPANT_JOIN.update.participantsList) 402 + room.onUpdateParticipants(TestData.PARTICIPANT_JOIN.update.participantsList)
409 room.onAddTrack( 403 room.onAddTrack(
410 MockRtpReceiver.create(), 404 MockRtpReceiver.create(),
411 MockAudioStreamTrack(), 405 MockAudioStreamTrack(),
@@ -441,11 +435,11 @@ class RoomMockE2ETest : MockE2ETest() { @@ -441,11 +435,11 @@ class RoomMockE2ETest : MockE2ETest() {
441 connect() 435 connect()
442 436
443 val eventCollector = EventCollector(room.events, coroutineRule.scope) 437 val eventCollector = EventCollector(room.events, coroutineRule.scope)
444 - wsFactory.listener.onMessage(wsFactory.ws, SignalClientTest.LEAVE.toOkioByteString()) 438 + wsFactory.listener.onMessage(wsFactory.ws, TestData.LEAVE.toOkioByteString())
445 val events = eventCollector.stopCollecting() 439 val events = eventCollector.stopCollecting()
446 assertEquals(1, events.size) 440 assertEquals(1, events.size)
447 assertEquals(true, events[0] is RoomEvent.Disconnected) 441 assertEquals(true, events[0] is RoomEvent.Disconnected)
448 - assertEquals(SignalClientTest.LEAVE.leave.reason.convert(), (events[0] as RoomEvent.Disconnected).reason) 442 + assertEquals(TestData.LEAVE.leave.reason.convert(), (events[0] as RoomEvent.Disconnected).reason)
449 } 443 }
450 444
451 @Test 445 @Test
@@ -16,10 +16,11 @@ @@ -16,10 +16,11 @@
16 16
17 package io.livekit.android.room 17 package io.livekit.android.room
18 18
19 -import io.livekit.android.MockE2ETest  
20 -import io.livekit.android.mock.MockAudioStreamTrack  
21 import io.livekit.android.room.track.LocalAudioTrack 19 import io.livekit.android.room.track.LocalAudioTrack
22 -import io.livekit.android.util.toPBByteString 20 +import io.livekit.android.test.MockE2ETest
  21 +import io.livekit.android.test.mock.MockAudioStreamTrack
  22 +import io.livekit.android.test.mock.TestData
  23 +import io.livekit.android.test.util.toPBByteString
23 import kotlinx.coroutines.ExperimentalCoroutinesApi 24 import kotlinx.coroutines.ExperimentalCoroutinesApi
24 import livekit.LivekitRtc 25 import livekit.LivekitRtc
25 import livekit.org.webrtc.PeerConnection 26 import livekit.org.webrtc.PeerConnection
@@ -46,9 +47,9 @@ class RoomReconnectionMockE2ETest : MockE2ETest() { @@ -46,9 +47,9 @@ class RoomReconnectionMockE2ETest : MockE2ETest() {
46 ?: 0 47 ?: 0
47 48
48 if (softReconnectParam == 0) { 49 if (softReconnectParam == 0) {
49 - simulateMessageFromServer(SignalClientTest.JOIN) 50 + simulateMessageFromServer(TestData.JOIN)
50 } else { 51 } else {
51 - simulateMessageFromServer(SignalClientTest.RECONNECT) 52 + simulateMessageFromServer(TestData.RECONNECT)
52 } 53 }
53 } 54 }
54 55
@@ -89,7 +90,7 @@ class RoomReconnectionMockE2ETest : MockE2ETest() { @@ -89,7 +90,7 @@ class RoomReconnectionMockE2ETest : MockE2ETest() {
89 val rtcConfig = getSubscriberPeerConnection().rtcConfig 90 val rtcConfig = getSubscriberPeerConnection().rtcConfig
90 assertEquals(PeerConnection.IceTransportsType.RELAY, rtcConfig.iceTransportsType) 91 assertEquals(PeerConnection.IceTransportsType.RELAY, rtcConfig.iceTransportsType)
91 92
92 - val sentIceServers = SignalClientTest.RECONNECT.reconnect.iceServersList 93 + val sentIceServers = TestData.RECONNECT.reconnect.iceServersList
93 .map { server -> server.toWebrtc() } 94 .map { server -> server.toWebrtc() }
94 assertEquals(sentIceServers, rtcConfig.iceServers) 95 assertEquals(sentIceServers, rtcConfig.iceServers)
95 } 96 }
@@ -103,7 +104,7 @@ class RoomReconnectionMockE2ETest : MockE2ETest() { @@ -103,7 +104,7 @@ class RoomReconnectionMockE2ETest : MockE2ETest() {
103 room.localParticipant.publishAudioTrack( 104 room.localParticipant.publishAudioTrack(
104 LocalAudioTrack( 105 LocalAudioTrack(
105 "", 106 "",
106 - MockAudioStreamTrack(id = SignalClientTest.LOCAL_TRACK_PUBLISHED.trackPublished.cid), 107 + MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
107 ), 108 ),
108 ) 109 )
109 110
@@ -16,11 +16,12 @@ @@ -16,11 +16,12 @@
16 16
17 package io.livekit.android.room 17 package io.livekit.android.room
18 18
19 -import io.livekit.android.MockE2ETest  
20 -import io.livekit.android.assert.assertIsClassList  
21 -import io.livekit.android.events.EventCollector  
22 -import io.livekit.android.events.FlowCollector  
23 import io.livekit.android.events.RoomEvent 19 import io.livekit.android.events.RoomEvent
  20 +import io.livekit.android.test.MockE2ETest
  21 +import io.livekit.android.test.assert.assertIsClassList
  22 +import io.livekit.android.test.events.EventCollector
  23 +import io.livekit.android.test.events.FlowCollector
  24 +import io.livekit.android.test.mock.TestData
24 import io.livekit.android.util.flow 25 import io.livekit.android.util.flow
25 import junit.framework.Assert.assertEquals 26 import junit.framework.Assert.assertEquals
26 import kotlinx.coroutines.ExperimentalCoroutinesApi 27 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -53,9 +54,9 @@ class RoomReconnectionTypesMockE2ETest( @@ -53,9 +54,9 @@ class RoomReconnectionTypesMockE2ETest(
53 ?: 0 54 ?: 0
54 55
55 if (softReconnectParam == 0) { 56 if (softReconnectParam == 0) {
56 - simulateMessageFromServer(SignalClientTest.JOIN) 57 + simulateMessageFromServer(TestData.JOIN)
57 } else { 58 } else {
58 - simulateMessageFromServer(SignalClientTest.RECONNECT) 59 + simulateMessageFromServer(TestData.RECONNECT)
59 } 60 }
60 } 61 }
61 62
@@ -17,19 +17,24 @@ @@ -17,19 +17,24 @@
17 package io.livekit.android.room 17 package io.livekit.android.room
18 18
19 import android.content.Context 19 import android.content.Context
20 -import android.net.ConnectivityManager 20 +import android.net.ConnectivityManager.NetworkCallback
21 import android.net.Network 21 import android.net.Network
22 import androidx.test.core.app.ApplicationProvider 22 import androidx.test.core.app.ApplicationProvider
23 -import androidx.test.platform.app.InstrumentationRegistry  
24 -import io.livekit.android.assert.assertIsClassList  
25 import io.livekit.android.audio.NoAudioHandler 23 import io.livekit.android.audio.NoAudioHandler
26 import io.livekit.android.audio.NoopCommunicationWorkaround 24 import io.livekit.android.audio.NoopCommunicationWorkaround
27 -import io.livekit.android.coroutines.TestCoroutineRule  
28 import io.livekit.android.e2ee.E2EEManager 25 import io.livekit.android.e2ee.E2EEManager
29 import io.livekit.android.events.* 26 import io.livekit.android.events.*
30 import io.livekit.android.memory.CloseableManager 27 import io.livekit.android.memory.CloseableManager
31 -import io.livekit.android.mock.* 28 +import io.livekit.android.room.network.NetworkCallbackManagerImpl
32 import io.livekit.android.room.participant.LocalParticipant 29 import io.livekit.android.room.participant.LocalParticipant
  30 +import io.livekit.android.test.assert.assertIsClassList
  31 +import io.livekit.android.test.coroutines.TestCoroutineRule
  32 +import io.livekit.android.test.events.EventCollector
  33 +import io.livekit.android.test.mock.MockAudioProcessingController
  34 +import io.livekit.android.test.mock.MockEglBase
  35 +import io.livekit.android.test.mock.MockLKObjects
  36 +import io.livekit.android.test.mock.MockNetworkCallbackRegistry
  37 +import io.livekit.android.test.mock.TestData
33 import kotlinx.coroutines.ExperimentalCoroutinesApi 38 import kotlinx.coroutines.ExperimentalCoroutinesApi
34 import kotlinx.coroutines.async 39 import kotlinx.coroutines.async
35 import kotlinx.coroutines.flow.MutableSharedFlow 40 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -48,8 +53,6 @@ import org.mockito.Mockito @@ -48,8 +53,6 @@ import org.mockito.Mockito
48 import org.mockito.junit.MockitoJUnit 53 import org.mockito.junit.MockitoJUnit
49 import org.mockito.kotlin.* 54 import org.mockito.kotlin.*
50 import org.robolectric.RobolectricTestRunner 55 import org.robolectric.RobolectricTestRunner
51 -import org.robolectric.Shadows  
52 -import org.robolectric.shadows.ShadowConnectivityManager  
53 56
54 @ExperimentalCoroutinesApi 57 @ExperimentalCoroutinesApi
55 @RunWith(RobolectricTestRunner::class) 58 @RunWith(RobolectricTestRunner::class)
@@ -69,6 +72,8 @@ class RoomTest { @@ -69,6 +72,8 @@ class RoomTest {
69 @Mock 72 @Mock
70 lateinit var e2EEManagerFactory: E2EEManager.Factory 73 lateinit var e2EEManagerFactory: E2EEManager.Factory
71 74
  75 + lateinit var networkCallbackRegistry: MockNetworkCallbackRegistry
  76 +
72 var eglBase: EglBase = MockEglBase() 77 var eglBase: EglBase = MockEglBase()
73 78
74 val localParticipantFactory = object : LocalParticipant.Factory { 79 val localParticipantFactory = object : LocalParticipant.Factory {
@@ -89,6 +94,7 @@ class RoomTest { @@ -89,6 +94,7 @@ class RoomTest {
89 @Before 94 @Before
90 fun setup() { 95 fun setup() {
91 context = ApplicationProvider.getApplicationContext() 96 context = ApplicationProvider.getApplicationContext()
  97 + networkCallbackRegistry = MockNetworkCallbackRegistry()
92 room = Room( 98 room = Room(
93 context = context, 99 context = context,
94 engine = rtcEngine, 100 engine = rtcEngine,
@@ -103,10 +109,13 @@ class RoomTest { @@ -103,10 +109,13 @@ class RoomTest {
103 communicationWorkaround = NoopCommunicationWorkaround(), 109 communicationWorkaround = NoopCommunicationWorkaround(),
104 audioProcessingController = MockAudioProcessingController(), 110 audioProcessingController = MockAudioProcessingController(),
105 lkObjects = MockLKObjects.get(), 111 lkObjects = MockLKObjects.get(),
  112 + networkCallbackManagerFactory = { networkCallback: NetworkCallback ->
  113 + NetworkCallbackManagerImpl(networkCallback, networkCallbackRegistry)
  114 + },
106 ) 115 )
107 } 116 }
108 117
109 - suspend fun connect(joinResponse: JoinResponse = SignalClientTest.JOIN.join) { 118 + suspend fun connect(joinResponse: JoinResponse = TestData.JOIN.join) {
110 rtcEngine.stub { 119 rtcEngine.stub {
111 onBlocking { rtcEngine.join(any(), any(), anyOrNull(), anyOrNull()) } 120 onBlocking { rtcEngine.join(any(), any(), anyOrNull(), anyOrNull()) }
112 .doSuspendableAnswer { 121 .doSuspendableAnswer {
@@ -120,7 +129,7 @@ class RoomTest { @@ -120,7 +129,7 @@ class RoomTest {
120 } 129 }
121 130
122 room.connect( 131 room.connect(
123 - url = SignalClientTest.EXAMPLE_URL, 132 + url = TestData.EXAMPLE_URL,
124 token = "", 133 token = "",
125 ) 134 )
126 } 135 }
@@ -128,7 +137,7 @@ class RoomTest { @@ -128,7 +137,7 @@ class RoomTest {
128 @Test 137 @Test
129 fun connectTest() = runTest { 138 fun connectTest() = runTest {
130 connect() 139 connect()
131 - val roomInfo = SignalClientTest.JOIN.join.room 140 + val roomInfo = TestData.JOIN.join.room
132 141
133 assertEquals(roomInfo.name, room.name) 142 assertEquals(roomInfo.name, room.name)
134 assertEquals(Room.Sid(roomInfo.sid), room.sid) 143 assertEquals(Room.Sid(roomInfo.sid), room.sid)
@@ -139,7 +148,7 @@ class RoomTest { @@ -139,7 +148,7 @@ class RoomTest {
139 @Test 148 @Test
140 fun roomUpdate() = runTest { 149 fun roomUpdate() = runTest {
141 connect() 150 connect()
142 - val update = SignalClientTest.ROOM_UPDATE.roomUpdate.room 151 + val update = TestData.ROOM_UPDATE.roomUpdate.room
143 152
144 val eventCollector = EventCollector(room.events, coroutineRule.scope) 153 val eventCollector = EventCollector(room.events, coroutineRule.scope)
145 room.onRoomUpdate(update) 154 room.onRoomUpdate(update)
@@ -164,15 +173,10 @@ class RoomTest { @@ -164,15 +173,10 @@ class RoomTest {
164 173
165 val network = Mockito.mock(Network::class.java) 174 val network = Mockito.mock(Network::class.java)
166 175
167 - val connectivityManager = InstrumentationRegistry.getInstrumentation()  
168 - .context  
169 - .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager  
170 - val shadowConnectivityManager: ShadowConnectivityManager = Shadows.shadowOf(connectivityManager)  
171 -  
172 - shadowConnectivityManager.networkCallbacks.forEach { callback -> 176 + networkCallbackRegistry.networkCallbacks.forEach { callback ->
173 callback.onLost(network) 177 callback.onLost(network)
174 } 178 }
175 - shadowConnectivityManager.networkCallbacks.forEach { callback -> 179 + networkCallbackRegistry.networkCallbacks.forEach { callback ->
176 callback.onAvailable(network) 180 callback.onAvailable(network)
177 } 181 }
178 182
@@ -227,7 +231,7 @@ class RoomTest { @@ -227,7 +231,7 @@ class RoomTest {
227 assertFalse(job.isCompleted) 231 assertFalse(job.isCompleted)
228 connect() 232 connect()
229 assertFalse(job.isCompleted) 233 assertFalse(job.isCompleted)
230 - val update = SignalClientTest.ROOM_UPDATE.roomUpdate.room 234 + val update = TestData.ROOM_UPDATE.roomUpdate.room
231 room.onRoomUpdate(update) 235 room.onRoomUpdate(update)
232 236
233 advanceUntilIdle() 237 advanceUntilIdle()
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -16,20 +16,22 @@ @@ -16,20 +16,22 @@
16 16
17 package io.livekit.android.room 17 package io.livekit.android.room
18 18
19 -import io.livekit.android.BaseTest  
20 -import io.livekit.android.mock.MockWebSocketFactory  
21 -import io.livekit.android.mock.TestData  
22 import io.livekit.android.stats.NetworkInfo 19 import io.livekit.android.stats.NetworkInfo
23 import io.livekit.android.stats.NetworkType 20 import io.livekit.android.stats.NetworkType
  21 +import io.livekit.android.test.BaseTest
  22 +import io.livekit.android.test.mock.MockWebSocketFactory
  23 +import io.livekit.android.test.mock.TestData.EXAMPLE_URL
  24 +import io.livekit.android.test.mock.TestData.JOIN
  25 +import io.livekit.android.test.mock.TestData.OFFER
  26 +import io.livekit.android.test.mock.TestData.PONG
  27 +import io.livekit.android.test.mock.TestData.RECONNECT
  28 +import io.livekit.android.test.mock.TestData.ROOM_UPDATE
  29 +import io.livekit.android.test.util.toPBByteString
24 import io.livekit.android.util.toOkioByteString 30 import io.livekit.android.util.toOkioByteString
25 -import io.livekit.android.util.toPBByteString  
26 import kotlinx.coroutines.ExperimentalCoroutinesApi 31 import kotlinx.coroutines.ExperimentalCoroutinesApi
27 import kotlinx.coroutines.async 32 import kotlinx.coroutines.async
28 import kotlinx.serialization.json.Json 33 import kotlinx.serialization.json.Json
29 -import livekit.LivekitModels  
30 -import livekit.LivekitModels.ClientConfiguration  
31 import livekit.LivekitRtc 34 import livekit.LivekitRtc
32 -import livekit.LivekitRtc.ICEServer  
33 import livekit.org.webrtc.SessionDescription 35 import livekit.org.webrtc.SessionDescription
34 import okhttp3.OkHttpClient 36 import okhttp3.OkHttpClient
35 import okhttp3.Protocol 37 import okhttp3.Protocol
@@ -43,9 +45,9 @@ import org.junit.Before @@ -43,9 +45,9 @@ import org.junit.Before
43 import org.junit.Test 45 import org.junit.Test
44 import org.mockito.Mock 46 import org.mockito.Mock
45 import org.mockito.Mockito 47 import org.mockito.Mockito
  48 +import org.mockito.Mockito.inOrder
46 import org.mockito.kotlin.any 49 import org.mockito.kotlin.any
47 import org.mockito.kotlin.argThat 50 import org.mockito.kotlin.argThat
48 -import org.mockito.kotlin.inOrder  
49 import org.mockito.kotlin.times 51 import org.mockito.kotlin.times
50 52
51 @ExperimentalCoroutinesApi 53 @ExperimentalCoroutinesApi
@@ -329,218 +331,5 @@ class SignalClientTest : BaseTest() { @@ -329,218 +331,5 @@ class SignalClientTest : BaseTest() {
329 } 331 }
330 332
331 // mock data 333 // mock data
332 - companion object {  
333 - const val EXAMPLE_URL = "ws://www.example.com"  
334 -  
335 - val JOIN = with(LivekitRtc.SignalResponse.newBuilder()) {  
336 - join = with(LivekitRtc.JoinResponse.newBuilder()) {  
337 - room = with(LivekitModels.Room.newBuilder()) {  
338 - name = "roomname"  
339 - build()  
340 - }  
341 - participant = TestData.LOCAL_PARTICIPANT  
342 - subscriberPrimary = true  
343 - addIceServers(  
344 - with(ICEServer.newBuilder()) {  
345 - addUrls("stun:stun.join.com:19302")  
346 - username = "username"  
347 - credential = "credential"  
348 - build()  
349 - },  
350 - )  
351 - serverVersion = "0.15.2"  
352 - build()  
353 - }  
354 - build()  
355 - }  
356 -  
357 - val RECONNECT = with(LivekitRtc.SignalResponse.newBuilder()) {  
358 - reconnect = with(LivekitRtc.ReconnectResponse.newBuilder()) {  
359 - addIceServers(  
360 - with(ICEServer.newBuilder()) {  
361 - addUrls("stun:stun.reconnect.com:19302")  
362 - username = "username"  
363 - credential = "credential"  
364 - build()  
365 - },  
366 - )  
367 - clientConfiguration = with(ClientConfiguration.newBuilder()) {  
368 - forceRelay = LivekitModels.ClientConfigSetting.ENABLED  
369 - build()  
370 - }  
371 - build()  
372 - }  
373 - build()  
374 - }  
375 -  
376 - val OFFER = with(LivekitRtc.SignalResponse.newBuilder()) {  
377 - offer = with(LivekitRtc.SessionDescription.newBuilder()) {  
378 - sdp = "remote_offer"  
379 - type = "offer"  
380 - build()  
381 - }  
382 - build()  
383 - }  
384 -  
385 - val ROOM_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {  
386 - roomUpdate = with(LivekitRtc.RoomUpdate.newBuilder()) {  
387 - room = with(LivekitModels.Room.newBuilder()) {  
388 - sid = "room_sid"  
389 - metadata = "metadata"  
390 - activeRecording = true  
391 - build()  
392 - }  
393 - build()  
394 - }  
395 - build()  
396 - }  
397 -  
398 - val LOCAL_TRACK_PUBLISHED = with(LivekitRtc.SignalResponse.newBuilder()) {  
399 - trackPublished = with(LivekitRtc.TrackPublishedResponse.newBuilder()) {  
400 - cid = "local_cid"  
401 - track = TestData.LOCAL_AUDIO_TRACK  
402 - build()  
403 - }  
404 - build()  
405 - }  
406 -  
407 - val LOCAL_TRACK_UNPUBLISHED = with(LivekitRtc.SignalResponse.newBuilder()) {  
408 - trackUnpublished = with(LivekitRtc.TrackUnpublishedResponse.newBuilder()) {  
409 - trackSid = TestData.LOCAL_AUDIO_TRACK.sid  
410 - build()  
411 - }  
412 - build()  
413 - }  
414 -  
415 - val PERMISSION_CHANGE = with(LivekitRtc.SignalResponse.newBuilder()) {  
416 - update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {  
417 - addParticipants(  
418 - TestData.LOCAL_PARTICIPANT.toBuilder()  
419 - .setPermission(  
420 - LivekitModels.ParticipantPermission.newBuilder()  
421 - .setCanPublish(false)  
422 - .setCanSubscribe(false)  
423 - .setCanPublishData(false)  
424 - .setHidden(false)  
425 - .setRecorder(false)  
426 - .build(),  
427 - )  
428 - .build(),  
429 - )  
430 - build()  
431 - }  
432 - build()  
433 - }  
434 -  
435 - val PARTICIPANT_JOIN = with(LivekitRtc.SignalResponse.newBuilder()) {  
436 - update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {  
437 - addParticipants(TestData.REMOTE_PARTICIPANT)  
438 - build()  
439 - }  
440 - build()  
441 - }  
442 -  
443 - val PARTICIPANT_DISCONNECT = with(LivekitRtc.SignalResponse.newBuilder()) {  
444 - update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {  
445 - val disconnectedParticipant = TestData.REMOTE_PARTICIPANT.toBuilder()  
446 - .setState(LivekitModels.ParticipantInfo.State.DISCONNECTED)  
447 - .build()  
448 -  
449 - addParticipants(disconnectedParticipant)  
450 - build()  
451 - }  
452 - build()  
453 - }  
454 -  
455 - val ACTIVE_SPEAKER_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {  
456 - speakersChanged = with(LivekitRtc.SpeakersChanged.newBuilder()) {  
457 - addSpeakers(TestData.REMOTE_SPEAKER_INFO)  
458 - build()  
459 - }  
460 - build()  
461 - }  
462 -  
463 - val LOCAL_PARTICIPANT_METADATA_CHANGED = with(LivekitRtc.SignalResponse.newBuilder()) {  
464 - update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {  
465 - val participantMetadataChanged = TestData.LOCAL_PARTICIPANT.toBuilder()  
466 - .setMetadata("changed_metadata")  
467 - .setName("changed_name")  
468 - .build()  
469 -  
470 - addParticipants(participantMetadataChanged)  
471 - build()  
472 - }  
473 - build()  
474 - }  
475 -  
476 - val REMOTE_PARTICIPANT_METADATA_CHANGED = with(LivekitRtc.SignalResponse.newBuilder()) {  
477 - update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {  
478 - val participantMetadataChanged = TestData.REMOTE_PARTICIPANT.toBuilder()  
479 - .setMetadata("changed_metadata")  
480 - .setName("changed_name")  
481 - .build()  
482 -  
483 - addParticipants(participantMetadataChanged)  
484 - build()  
485 - }  
486 - build()  
487 - }  
488 -  
489 - val CONNECTION_QUALITY = with(LivekitRtc.SignalResponse.newBuilder()) {  
490 - connectionQuality = with(LivekitRtc.ConnectionQualityUpdate.newBuilder()) {  
491 - addUpdates(  
492 - with(LivekitRtc.ConnectionQualityInfo.newBuilder()) {  
493 - participantSid = JOIN.join.participant.sid  
494 - quality = LivekitModels.ConnectionQuality.EXCELLENT  
495 - build()  
496 - },  
497 - )  
498 - build()  
499 - }  
500 - build()  
501 - }  
502 -  
503 - val STREAM_STATE_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {  
504 - streamStateUpdate = with(LivekitRtc.StreamStateUpdate.newBuilder()) {  
505 - addStreamStates(  
506 - with(LivekitRtc.StreamStateInfo.newBuilder()) {  
507 - participantSid = TestData.REMOTE_PARTICIPANT.sid  
508 - trackSid = TestData.REMOTE_AUDIO_TRACK.sid  
509 - state = LivekitRtc.StreamState.ACTIVE  
510 - build()  
511 - },  
512 - )  
513 - build()  
514 - }  
515 - build()  
516 - }  
517 -  
518 - val SUBSCRIPTION_PERMISSION_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {  
519 - subscriptionPermissionUpdate = with(LivekitRtc.SubscriptionPermissionUpdate.newBuilder()) {  
520 - participantSid = TestData.REMOTE_PARTICIPANT.sid  
521 - trackSid = TestData.REMOTE_AUDIO_TRACK.sid  
522 - allowed = false  
523 - build()  
524 - }  
525 - build()  
526 - }  
527 -  
528 - val REFRESH_TOKEN = with(LivekitRtc.SignalResponse.newBuilder()) {  
529 - refreshToken = "refresh_token"  
530 - build()  
531 - }  
532 -  
533 - val PONG = with(LivekitRtc.SignalResponse.newBuilder()) {  
534 - pong = 1L  
535 - build()  
536 - }  
537 -  
538 - val LEAVE = with(LivekitRtc.SignalResponse.newBuilder()) {  
539 - leave = with(LivekitRtc.LeaveRequest.newBuilder()) {  
540 - reason = LivekitModels.DisconnectReason.SERVER_SHUTDOWN  
541 - build()  
542 - }  
543 - build()  
544 - }  
545 - } 334 + companion object
546 } 335 }
@@ -16,25 +16,25 @@ @@ -16,25 +16,25 @@
16 16
17 package io.livekit.android.room.participant 17 package io.livekit.android.room.participant
18 18
19 -import io.livekit.android.MockE2ETest  
20 -import io.livekit.android.assert.assertIsClassList  
21 -import io.livekit.android.events.EventCollector  
22 import io.livekit.android.events.ParticipantEvent 19 import io.livekit.android.events.ParticipantEvent
23 import io.livekit.android.events.RoomEvent 20 import io.livekit.android.events.RoomEvent
24 -import io.livekit.android.mock.MockAudioStreamTrack  
25 -import io.livekit.android.mock.MockEglBase  
26 -import io.livekit.android.mock.MockVideoCapturer  
27 -import io.livekit.android.mock.MockVideoStreamTrack  
28 import io.livekit.android.room.DefaultsManager 21 import io.livekit.android.room.DefaultsManager
29 -import io.livekit.android.room.SignalClientTest  
30 import io.livekit.android.room.track.LocalAudioTrack 22 import io.livekit.android.room.track.LocalAudioTrack
31 import io.livekit.android.room.track.LocalVideoTrack 23 import io.livekit.android.room.track.LocalVideoTrack
32 import io.livekit.android.room.track.LocalVideoTrackOptions 24 import io.livekit.android.room.track.LocalVideoTrackOptions
33 import io.livekit.android.room.track.Track 25 import io.livekit.android.room.track.Track
34 import io.livekit.android.room.track.VideoCaptureParameter 26 import io.livekit.android.room.track.VideoCaptureParameter
35 import io.livekit.android.room.track.VideoCodec 27 import io.livekit.android.room.track.VideoCodec
  28 +import io.livekit.android.test.MockE2ETest
  29 +import io.livekit.android.test.assert.assertIsClassList
  30 +import io.livekit.android.test.events.EventCollector
  31 +import io.livekit.android.test.mock.MockAudioStreamTrack
  32 +import io.livekit.android.test.mock.MockEglBase
  33 +import io.livekit.android.test.mock.MockVideoCapturer
  34 +import io.livekit.android.test.mock.MockVideoStreamTrack
  35 +import io.livekit.android.test.mock.TestData
  36 +import io.livekit.android.test.util.toPBByteString
36 import io.livekit.android.util.toOkioByteString 37 import io.livekit.android.util.toOkioByteString
37 -import io.livekit.android.util.toPBByteString  
38 import kotlinx.coroutines.ExperimentalCoroutinesApi 38 import kotlinx.coroutines.ExperimentalCoroutinesApi
39 import livekit.LivekitModels 39 import livekit.LivekitModels
40 import livekit.LivekitRtc 40 import livekit.LivekitRtc
@@ -60,7 +60,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() { @@ -60,7 +60,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() {
60 room.localParticipant.publishAudioTrack( 60 room.localParticipant.publishAudioTrack(
61 LocalAudioTrack( 61 LocalAudioTrack(
62 "", 62 "",
63 - MockAudioStreamTrack(id = SignalClientTest.LOCAL_TRACK_PUBLISHED.trackPublished.cid), 63 + MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
64 ), 64 ),
65 ) 65 )
66 66
@@ -147,14 +147,14 @@ class LocalParticipantMockE2ETest : MockE2ETest() { @@ -147,14 +147,14 @@ class LocalParticipantMockE2ETest : MockE2ETest() {
147 147
148 wsFactory.listener.onMessage( 148 wsFactory.listener.onMessage(
149 wsFactory.ws, 149 wsFactory.ws,
150 - SignalClientTest.LOCAL_PARTICIPANT_METADATA_CHANGED.toOkioByteString(), 150 + TestData.LOCAL_PARTICIPANT_METADATA_CHANGED.toOkioByteString(),
151 ) 151 )
152 152
153 val roomEvents = roomEventsCollector.stopCollecting() 153 val roomEvents = roomEventsCollector.stopCollecting()
154 val participantEvents = participantEventsCollector.stopCollecting() 154 val participantEvents = participantEventsCollector.stopCollecting()
155 155
156 val localParticipant = room.localParticipant 156 val localParticipant = room.localParticipant
157 - val updateData = SignalClientTest.REMOTE_PARTICIPANT_METADATA_CHANGED.update.getParticipants(0) 157 + val updateData = TestData.REMOTE_PARTICIPANT_METADATA_CHANGED.update.getParticipants(0)
158 assertEquals(updateData.metadata, localParticipant.metadata) 158 assertEquals(updateData.metadata, localParticipant.metadata)
159 assertEquals(updateData.name, localParticipant.name) 159 assertEquals(updateData.name, localParticipant.name)
160 160
@@ -16,14 +16,15 @@ @@ -16,14 +16,15 @@
16 16
17 package io.livekit.android.room.participant 17 package io.livekit.android.room.participant
18 18
19 -import io.livekit.android.MockE2ETest  
20 -import io.livekit.android.assert.assertIsClassList  
21 -import io.livekit.android.events.EventCollector  
22 import io.livekit.android.events.ParticipantEvent 19 import io.livekit.android.events.ParticipantEvent
23 import io.livekit.android.events.RoomEvent 20 import io.livekit.android.events.RoomEvent
24 -import io.livekit.android.mock.MockAudioStreamTrack  
25 -import io.livekit.android.room.SignalClientTest  
26 import io.livekit.android.room.track.LocalAudioTrack 21 import io.livekit.android.room.track.LocalAudioTrack
  22 +import io.livekit.android.test.MockE2ETest
  23 +import io.livekit.android.test.assert.assertIsClassList
  24 +import io.livekit.android.test.events.EventCollector
  25 +import io.livekit.android.test.mock.MockAudioStreamTrack
  26 +import io.livekit.android.test.mock.TestData
  27 +import io.livekit.android.test.util.toOkioByteString
27 import io.livekit.android.util.toOkioByteString 28 import io.livekit.android.util.toOkioByteString
28 import kotlinx.coroutines.ExperimentalCoroutinesApi 29 import kotlinx.coroutines.ExperimentalCoroutinesApi
29 import org.junit.Assert.assertEquals 30 import org.junit.Assert.assertEquals
@@ -43,13 +44,13 @@ class ParticipantMockE2ETest : MockE2ETest() { @@ -43,13 +44,13 @@ class ParticipantMockE2ETest : MockE2ETest() {
43 room.localParticipant.publishAudioTrack( 44 room.localParticipant.publishAudioTrack(
44 LocalAudioTrack( 45 LocalAudioTrack(
45 "", 46 "",
46 - MockAudioStreamTrack(id = SignalClientTest.LOCAL_TRACK_PUBLISHED.trackPublished.cid), 47 + MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
47 ), 48 ),
48 ) 49 )
49 50
50 val eventCollector = EventCollector(room.events, coroutineRule.scope) 51 val eventCollector = EventCollector(room.events, coroutineRule.scope)
51 // remote unpublish 52 // remote unpublish
52 - simulateMessageFromServer(SignalClientTest.LOCAL_TRACK_UNPUBLISHED) 53 + simulateMessageFromServer(TestData.LOCAL_TRACK_UNPUBLISHED)
53 val events = eventCollector.stopCollecting() 54 val events = eventCollector.stopCollecting()
54 55
55 assertEquals(1, events.size) 56 assertEquals(1, events.size)
@@ -62,7 +63,7 @@ class ParticipantMockE2ETest : MockE2ETest() { @@ -62,7 +63,7 @@ class ParticipantMockE2ETest : MockE2ETest() {
62 connect() 63 connect()
63 64
64 val eventCollector = EventCollector(room.events, coroutineRule.scope) 65 val eventCollector = EventCollector(room.events, coroutineRule.scope)
65 - simulateMessageFromServer(SignalClientTest.PERMISSION_CHANGE) 66 + simulateMessageFromServer(TestData.PERMISSION_CHANGE)
66 val events = eventCollector.stopCollecting() 67 val events = eventCollector.stopCollecting()
67 68
68 assertEquals(1, events.size) 69 assertEquals(1, events.size)
@@ -75,7 +76,7 @@ class ParticipantMockE2ETest : MockE2ETest() { @@ -75,7 +76,7 @@ class ParticipantMockE2ETest : MockE2ETest() {
75 76
76 wsFactory.listener.onMessage( 77 wsFactory.listener.onMessage(
77 wsFactory.ws, 78 wsFactory.ws,
78 - SignalClientTest.PARTICIPANT_JOIN.toOkioByteString(), 79 + TestData.PARTICIPANT_JOIN.toOkioByteString(),
79 ) 80 )
80 81
81 val remoteParticipant = room.remoteParticipants.values.first() 82 val remoteParticipant = room.remoteParticipants.values.first()
@@ -83,12 +84,12 @@ class ParticipantMockE2ETest : MockE2ETest() { @@ -83,12 +84,12 @@ class ParticipantMockE2ETest : MockE2ETest() {
83 val participantEventsCollector = EventCollector(remoteParticipant.events, coroutineRule.scope) 84 val participantEventsCollector = EventCollector(remoteParticipant.events, coroutineRule.scope)
84 wsFactory.listener.onMessage( 85 wsFactory.listener.onMessage(
85 wsFactory.ws, 86 wsFactory.ws,
86 - SignalClientTest.REMOTE_PARTICIPANT_METADATA_CHANGED.toOkioByteString(), 87 + TestData.REMOTE_PARTICIPANT_METADATA_CHANGED.toOkioByteString(),
87 ) 88 )
88 val roomEvents = roomEventsCollector.stopCollecting() 89 val roomEvents = roomEventsCollector.stopCollecting()
89 val participantEvents = participantEventsCollector.stopCollecting() 90 val participantEvents = participantEventsCollector.stopCollecting()
90 91
91 - val updateData = SignalClientTest.REMOTE_PARTICIPANT_METADATA_CHANGED.update.getParticipants(0) 92 + val updateData = TestData.REMOTE_PARTICIPANT_METADATA_CHANGED.update.getParticipants(0)
92 assertEquals(updateData.metadata, remoteParticipant.metadata) 93 assertEquals(updateData.metadata, remoteParticipant.metadata)
93 assertEquals(updateData.name, remoteParticipant.name) 94 assertEquals(updateData.name, remoteParticipant.name)
94 95
@@ -16,10 +16,10 @@ @@ -16,10 +16,10 @@
16 16
17 package io.livekit.android.room.participant 17 package io.livekit.android.room.participant
18 18
19 -import io.livekit.android.coroutines.TestCoroutineRule  
20 -import io.livekit.android.events.EventCollector  
21 import io.livekit.android.events.ParticipantEvent 19 import io.livekit.android.events.ParticipantEvent
22 import io.livekit.android.room.track.TrackPublication 20 import io.livekit.android.room.track.TrackPublication
  21 +import io.livekit.android.test.coroutines.TestCoroutineRule
  22 +import io.livekit.android.test.events.EventCollector
23 import kotlinx.coroutines.ExperimentalCoroutinesApi 23 import kotlinx.coroutines.ExperimentalCoroutinesApi
24 import kotlinx.coroutines.test.runTest 24 import kotlinx.coroutines.test.runTest
25 import livekit.LivekitModels 25 import livekit.LivekitModels
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -16,12 +16,12 @@ @@ -16,12 +16,12 @@
16 16
17 package io.livekit.android.room.participant 17 package io.livekit.android.room.participant
18 18
19 -import io.livekit.android.BaseTest  
20 -import io.livekit.android.events.EventCollector  
21 -import io.livekit.android.events.FlowCollector  
22 import io.livekit.android.events.ParticipantEvent 19 import io.livekit.android.events.ParticipantEvent
23 import io.livekit.android.room.SignalClient 20 import io.livekit.android.room.SignalClient
24 import io.livekit.android.room.track.TrackPublication 21 import io.livekit.android.room.track.TrackPublication
  22 +import io.livekit.android.test.BaseTest
  23 +import io.livekit.android.test.events.EventCollector
  24 +import io.livekit.android.test.events.FlowCollector
25 import io.livekit.android.util.flow 25 import io.livekit.android.util.flow
26 import kotlinx.coroutines.ExperimentalCoroutinesApi 26 import kotlinx.coroutines.ExperimentalCoroutinesApi
27 import livekit.LivekitModels 27 import livekit.LivekitModels
@@ -16,11 +16,14 @@ @@ -16,11 +16,14 @@
16 16
17 package io.livekit.android.room.track 17 package io.livekit.android.room.track
18 18
19 -import io.livekit.android.MockE2ETest  
20 -import io.livekit.android.mock.*  
21 -import io.livekit.android.room.SignalClientTest 19 +import io.livekit.android.test.MockE2ETest
  20 +import io.livekit.android.test.mock.MockMediaStream
  21 +import io.livekit.android.test.mock.MockRtpReceiver
  22 +import io.livekit.android.test.mock.MockVideoStreamTrack
  23 +import io.livekit.android.test.mock.TestData
  24 +import io.livekit.android.test.mock.createMediaStreamId
  25 +import io.livekit.android.test.util.toPBByteString
22 import io.livekit.android.util.toOkioByteString 26 import io.livekit.android.util.toOkioByteString
23 -import io.livekit.android.util.toPBByteString  
24 import kotlinx.coroutines.ExperimentalCoroutinesApi 27 import kotlinx.coroutines.ExperimentalCoroutinesApi
25 import kotlinx.coroutines.test.advanceUntilIdle 28 import kotlinx.coroutines.test.advanceUntilIdle
26 import livekit.LivekitModels.VideoQuality 29 import livekit.LivekitModels.VideoQuality
@@ -43,7 +46,7 @@ class RemoteTrackPublicationTest : MockE2ETest() { @@ -43,7 +46,7 @@ class RemoteTrackPublicationTest : MockE2ETest() {
43 46
44 wsFactory.listener.onMessage( 47 wsFactory.listener.onMessage(
45 wsFactory.ws, 48 wsFactory.ws,
46 - SignalClientTest.PARTICIPANT_JOIN.toOkioByteString(), 49 + TestData.PARTICIPANT_JOIN.toOkioByteString(),
47 ) 50 )
48 51
49 room.onAddTrack( 52 room.onAddTrack(
@@ -16,12 +16,12 @@ @@ -16,12 +16,12 @@
16 16
17 package io.livekit.android.room.track 17 package io.livekit.android.room.track
18 18
19 -import io.livekit.android.BaseTest  
20 -import io.livekit.android.events.EventCollector  
21 import io.livekit.android.events.TrackEvent 19 import io.livekit.android.events.TrackEvent
22 -import io.livekit.android.mock.MockRtpReceiver  
23 -import io.livekit.android.mock.MockVideoStreamTrack  
24 import io.livekit.android.room.track.video.VideoSinkVisibility 20 import io.livekit.android.room.track.video.VideoSinkVisibility
  21 +import io.livekit.android.test.BaseTest
  22 +import io.livekit.android.test.events.EventCollector
  23 +import io.livekit.android.test.mock.MockRtpReceiver
  24 +import io.livekit.android.test.mock.MockVideoStreamTrack
25 import kotlinx.coroutines.ExperimentalCoroutinesApi 25 import kotlinx.coroutines.ExperimentalCoroutinesApi
26 import livekit.org.webrtc.VideoFrame 26 import livekit.org.webrtc.VideoFrame
27 import livekit.org.webrtc.VideoSink 27 import livekit.org.webrtc.VideoSink
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
1 /* 1 /*
2 - * Copyright 2023 LiveKit, Inc. 2 + * Copyright 2023-2024 LiveKit, Inc.
3 * 3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 5 * you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 16
17 package io.livekit.android.webrtc 17 package io.livekit.android.webrtc
18 18
19 -import io.livekit.android.BaseTest 19 +import io.livekit.android.test.BaseTest
20 import livekit.org.webrtc.PeerConnection.RTCConfiguration 20 import livekit.org.webrtc.PeerConnection.RTCConfiguration
21 import org.junit.Assert.assertEquals 21 import org.junit.Assert.assertEquals
22 import org.junit.Assert.assertTrue 22 import org.junit.Assert.assertTrue
@@ -25,6 +25,9 @@ import org.mockito.Mockito @@ -25,6 +25,9 @@ import org.mockito.Mockito
25 25
26 class RTCConfigurationTest : BaseTest() { 26 class RTCConfigurationTest : BaseTest() {
27 27
  28 + /**
  29 + * Make sure that copy handles every declared field.
  30 + */
28 @Test 31 @Test
29 fun copyTest() { 32 fun copyTest() {
30 val originalConfig = RTCConfiguration(mutableListOf()) 33 val originalConfig = RTCConfiguration(mutableListOf())
@@ -40,7 +43,9 @@ class RTCConfigurationTest : BaseTest() { @@ -40,7 +43,9 @@ class RTCConfigurationTest : BaseTest() {
40 } 43 }
41 } 44 }
42 45
43 - // Test to make sure the copy test is actually checking properly 46 + /**
  47 + * Make sure the copy test is actually checking properly
  48 + */
44 @Test 49 @Test
45 fun copyFailureCheckTest() { 50 fun copyFailureCheckTest() {
46 val originalConfig = RTCConfiguration(mutableListOf()) 51 val originalConfig = RTCConfiguration(mutableListOf())
@@ -11,3 +11,4 @@ include ':video-encode-decode-test' @@ -11,3 +11,4 @@ include ':video-encode-decode-test'
11 include ':sample-app-basic' 11 include ':sample-app-basic'
12 include ':sample-app-record-local' 12 include ':sample-app-record-local'
13 include ':examples:selfie-segmentation' 13 include ':examples:selfie-segmentation'
  14 +include ':livekit-android-test'