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:
./gradlew spotlessCheck
- name: Build with Gradle
run: ./gradlew assembleRelease livekit-android-sdk:testRelease
run: ./gradlew assembleRelease livekit-android-test:testRelease
# TODO: Figure out appropriate place to run this. Takes ~3 mins, so pretty slow.
# # generates coverage-report.md and publishes as checkrun
... ...
... ... @@ -40,7 +40,7 @@ jobs:
AWS_DEFAULT_REGION: "us-east-1"
- name: Build with Gradle
run: ./gradlew livekit-android-sdk:assembleRelease livekit-android-sdk:testRelease
run: ./gradlew livekit-android-sdk:assembleRelease livekit-android-test:testRelease
- name: Create gpg key and import into gradle properties
run: |
... ...
... ... @@ -17,6 +17,9 @@ okhttp = "4.12.0"
protobuf = "3.22.0"
protobufJavalite = "3.22.0"
semver4j = "3.1.0"
core-ktx = "1.12.0"
appcompat = "1.6.1"
material = "1.11.0"
[libraries]
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"
junit = { module = "junit:junit", version.ref = "junit-lib" }
junitJupiterApi = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit-jupiter" }
junitJupiterEngine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit-jupiter" }
mockito-core = { module = "org.mockito:mockito-core", version = "4.0.0" }
mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version = "4.0.0" }
robolectric = { module = "org.robolectric:robolectric", version = "4.10.2" }
# Mockito 5 requires JVM 11.
#noinspection GradleDependency
mockito-core = { module = "org.mockito:mockito-core", version = "4.11.0" }
#noinspection GradleDependency
mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version = "4.1.0" }
#noinspection GradleDependency
mockito-inline = { module = "org.mockito:mockito-inline", version = "4.11.0" }
robolectric = { module = "org.robolectric:robolectric", version = "4.11.1" }
turbine = { module = "app.cash.turbine:turbine", version = "1.0.0" }
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
[plugins]
... ...
... ... @@ -16,44 +16,47 @@
package io.livekit.android.dagger
internal object InjectionNames {
/**
* @suppress
*/
object InjectionNames {
/**
* @see [kotlinx.coroutines.Dispatchers.Default]
*/
internal const val DISPATCHER_DEFAULT = "dispatcher_default"
const val DISPATCHER_DEFAULT = "dispatcher_default"
/**
* @see [kotlinx.coroutines.Dispatchers.IO]
*/
internal const val DISPATCHER_IO = "dispatcher_io"
const val DISPATCHER_IO = "dispatcher_io"
/**
* @see [kotlinx.coroutines.Dispatchers.Main]
*/
internal const val DISPATCHER_MAIN = "dispatcher_main"
const val DISPATCHER_MAIN = "dispatcher_main"
/**
* @see [kotlinx.coroutines.Dispatchers.Unconfined]
*/
internal const val DISPATCHER_UNCONFINED = "dispatcher_unconfined"
const val DISPATCHER_UNCONFINED = "dispatcher_unconfined"
internal const val SENDER = "sender"
const val SENDER = "sender"
internal const val OPTIONS_VIDEO_HW_ACCEL = "options_video_hw_accel"
const val OPTIONS_VIDEO_HW_ACCEL = "options_video_hw_accel"
internal const val LIB_WEBRTC_INITIALIZATION = "lib_webrtc_initialization"
const val LIB_WEBRTC_INITIALIZATION = "lib_webrtc_initialization"
// Overrides
internal const val OVERRIDE_OKHTTP = "override_okhttp"
internal const val OVERRIDE_AUDIO_DEVICE_MODULE = "override_audio_device_module"
internal const val OVERRIDE_AUDIO_PROCESSOR_OPTIONS = "override_audio_processor_options"
internal const val OVERRIDE_JAVA_AUDIO_DEVICE_MODULE_CUSTOMIZER = "override_java_audio_device_module_customizer"
internal const val OVERRIDE_VIDEO_ENCODER_FACTORY = "override_video_encoder_factory"
internal const val OVERRIDE_VIDEO_DECODER_FACTORY = "override_video_decoder_factory"
internal const val OVERRIDE_AUDIO_HANDLER = "override_audio_handler"
internal const val OVERRIDE_AUDIO_OUTPUT_TYPE = "override_audio_output_type"
internal const val OVERRIDE_DISABLE_COMMUNICATION_WORKAROUND = "override_disable_communication_workaround"
internal const val OVERRIDE_EGL_BASE = "override_egl_base"
internal const val OVERRIDE_PEER_CONNECTION_FACTORY_OPTIONS = "override_peer_connection_factory_options"
const val OVERRIDE_OKHTTP = "override_okhttp"
const val OVERRIDE_AUDIO_DEVICE_MODULE = "override_audio_device_module"
const val OVERRIDE_AUDIO_PROCESSOR_OPTIONS = "override_audio_processor_options"
const val OVERRIDE_JAVA_AUDIO_DEVICE_MODULE_CUSTOMIZER = "override_java_audio_device_module_customizer"
const val OVERRIDE_VIDEO_ENCODER_FACTORY = "override_video_encoder_factory"
const val OVERRIDE_VIDEO_DECODER_FACTORY = "override_video_decoder_factory"
const val OVERRIDE_AUDIO_HANDLER = "override_audio_handler"
const val OVERRIDE_AUDIO_OUTPUT_TYPE = "override_audio_output_type"
const val OVERRIDE_DISABLE_COMMUNICATION_WORKAROUND = "override_disable_communication_workaround"
const val OVERRIDE_EGL_BASE = "override_egl_base"
const val OVERRIDE_PEER_CONNECTION_FACTORY_OPTIONS = "override_peer_connection_factory_options"
}
... ...
... ... @@ -21,8 +21,11 @@ import dagger.Provides
import dagger.Reusable
import kotlinx.serialization.json.Json
/**
* @suppress
*/
@Module
internal object JsonFormatModule {
object JsonFormatModule {
@Provides
@Reusable
fun kotlinSerializationJson(): Json =
... ...
... ... @@ -25,6 +25,9 @@ import livekit.org.webrtc.EglBase
import livekit.org.webrtc.PeerConnectionFactory
import javax.inject.Singleton
/**
* @suppress
*/
@Singleton
@Component(
modules = [
... ... @@ -37,7 +40,7 @@ import javax.inject.Singleton
MemoryModule::class,
],
)
internal interface LiveKitComponent {
interface LiveKitComponent {
fun roomFactory(): Room.Factory
... ...
... ... @@ -21,8 +21,11 @@ import dagger.Provides
import io.livekit.android.memory.CloseableManager
import javax.inject.Singleton
/**
* @suppress
*/
@Module
internal object MemoryModule {
object MemoryModule {
@Singleton
@Provides
... ...
... ... @@ -23,9 +23,12 @@ import dagger.Provides
import io.livekit.android.LiveKitOverrides
import javax.inject.Named
/**
* @suppress
*/
@SuppressLint("KotlinNullnessAnnotation")
@Module
internal class OverridesModule(private val overrides: LiveKitOverrides) {
class OverridesModule(private val overrides: LiveKitOverrides) {
@Provides
@Named(InjectionNames.OVERRIDE_OKHTTP)
... ...
... ... @@ -41,8 +41,14 @@ import timber.log.Timber
import javax.inject.Named
import javax.inject.Singleton
internal typealias CapabilitiesGetter = @JvmSuppressWildcards (MediaStreamTrack.MediaType) -> RtpCapabilities
/**
* @suppress
*/
typealias CapabilitiesGetter = @JvmSuppressWildcards (MediaStreamTrack.MediaType) -> RtpCapabilities
/**
* @suppress
*/
@Module
internal object RTCModule {
... ...
... ... @@ -17,10 +17,16 @@
package io.livekit.android.dagger
import android.content.Context
import android.net.ConnectivityManager
import androidx.annotation.Nullable
import dagger.Module
import dagger.Provides
import dagger.Reusable
import io.livekit.android.memory.CloseableManager
import io.livekit.android.room.network.NetworkCallbackManagerFactory
import io.livekit.android.room.network.NetworkCallbackManagerImpl
import io.livekit.android.room.network.NetworkCallbackRegistry
import io.livekit.android.room.network.NetworkCallbackRegistryImpl
import io.livekit.android.stats.AndroidNetworkInfo
import io.livekit.android.stats.NetworkInfo
import okhttp3.OkHttpClient
... ... @@ -50,4 +56,23 @@ internal object WebModule {
fun networkInfo(context: Context): NetworkInfo {
return AndroidNetworkInfo(context)
}
@Provides
fun connectivityManager(context: Context): ConnectivityManager {
return context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
}
@Provides
fun networkCallbackRegistrar(connectivityManager: ConnectivityManager): NetworkCallbackRegistry {
return NetworkCallbackRegistryImpl(connectivityManager)
}
@Provides
@Reusable
fun networkCallbackManagerFactory(closeableManager: CloseableManager, registrar: NetworkCallbackRegistry): NetworkCallbackManagerFactory {
return { networkCallback: ConnectivityManager.NetworkCallback ->
NetworkCallbackManagerImpl(networkCallback, registrar)
.apply { closeableManager.registerClosable(this) }
}
}
}
... ...
... ... @@ -232,7 +232,10 @@ enum class DisconnectReason {
JOIN_FAILURE,
}
internal fun LivekitModels.DisconnectReason?.convert(): DisconnectReason {
/**
* @suppress
*/
fun LivekitModels.DisconnectReason?.convert(): DisconnectReason {
return when (this) {
LivekitModels.DisconnectReason.CLIENT_INITIATED -> DisconnectReason.CLIENT_INITIATED
LivekitModels.DisconnectReason.DUPLICATE_IDENTITY -> DisconnectReason.DUPLICATE_IDENTITY
... ...
... ... @@ -354,7 +354,11 @@ constructor(
private const val DD_EXTENSION_URI = "https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension"
internal fun ensureVideoDDExtensionForSVC(mediaDesc: MediaDescription) {
/**
* @suppress
*/
@VisibleForTesting
fun ensureVideoDDExtensionForSVC(mediaDesc: MediaDescription) {
val codec = mediaDesc.getRtps()
.firstOrNull()
?.second
... ... @@ -397,7 +401,11 @@ eliminate this issue.
*/
private const val startBitrateForSVC = 0.7
internal fun ensureCodecBitrates(
/**
* @suppress
*/
@VisibleForTesting
fun ensureCodecBitrates(
media: MediaDescription,
trackBitrates: Map<TrackBitrateInfoKey, TrackBitrateInfo>,
) {
... ... @@ -454,12 +462,20 @@ internal fun isSVCCodec(codec: String?): Boolean {
"vp9".equals(codec, ignoreCase = true))
}
internal data class TrackBitrateInfo(
/**
* @suppress
*/
@VisibleForTesting
data class TrackBitrateInfo(
val codec: String,
val maxBitrate: Long,
)
internal sealed class TrackBitrateInfoKey {
/**
* @suppress
*/
@VisibleForTesting
sealed class TrackBitrateInfoKey {
data class Cid(val value: String) : TrackBitrateInfoKey()
data class Transceiver(val value: RtpTransceiver) : TrackBitrateInfoKey()
}
... ...
... ... @@ -80,7 +80,8 @@ internal constructor(
* Reflects the combined connection state of SignalClient and primary PeerConnection.
*/
@FlowObservable
internal var connectionState: ConnectionState by flowDelegate(ConnectionState.DISCONNECTED) { newVal, oldVal ->
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
var connectionState: ConnectionState by flowDelegate(ConnectionState.DISCONNECTED) { newVal, oldVal ->
if (newVal == oldVal) {
return@flowDelegate
}
... ... @@ -383,7 +384,8 @@ internal constructor(
* reconnect Signal and PeerConnections
*/
@Synchronized
internal fun reconnect() {
@VisibleForTesting
fun reconnect() {
if (reconnectingJob?.isActive == true) {
LKLog.d { "Reconnection is already in progress" }
return
... ... @@ -740,11 +742,18 @@ internal constructor(
}
companion object {
/**
* @suppress
*/
@VisibleForTesting
internal const val RELIABLE_DATA_CHANNEL_LABEL = "_reliable"
const val RELIABLE_DATA_CHANNEL_LABEL = "_reliable"
/**
* @suppress
*/
@VisibleForTesting
internal const val LOSSY_DATA_CHANNEL_LABEL = "_lossy"
const val LOSSY_DATA_CHANNEL_LABEL = "_lossy"
internal const val MAX_DATA_PACKET_SIZE = 15000
private const val MAX_RECONNECT_RETRIES = 10
private const val MAX_RECONNECT_TIMEOUT = 60 * 1000
... ... @@ -1056,11 +1065,11 @@ internal constructor(
}
@VisibleForTesting
internal fun getPublisherPeerConnection() =
fun getPublisherPeerConnection() =
publisher!!.peerConnection
@VisibleForTesting
internal fun getSubscriberPeerConnection() =
fun getSubscriberPeerConnection() =
subscriber!!.peerConnection
}
... ... @@ -1073,7 +1082,7 @@ enum class ReconnectType {
FORCE_FULL_RECONNECT,
}
internal fun LivekitRtc.ICEServer.toWebrtc() = PeerConnection.IceServer.builder(urlsList)
fun LivekitRtc.ICEServer.toWebrtc(): PeerConnection.IceServer = PeerConnection.IceServer.builder(urlsList)
.setUsername(username ?: "")
.setPassword(credential ?: "")
.setTlsAlpnProtocols(emptyList())
... ...
... ... @@ -19,7 +19,6 @@
package io.livekit.android.room
import android.content.Context
import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback
import android.net.Network
import androidx.annotation.VisibleForTesting
... ... @@ -39,7 +38,7 @@ import io.livekit.android.e2ee.E2EEOptions
import io.livekit.android.events.*
import io.livekit.android.memory.CloseableManager
import io.livekit.android.renderer.TextureViewRenderer
import io.livekit.android.room.network.NetworkCallbackManager
import io.livekit.android.room.network.NetworkCallbackManagerFactory
import io.livekit.android.room.participant.*
import io.livekit.android.room.provisions.LKObjects
import io.livekit.android.room.track.*
... ... @@ -78,6 +77,7 @@ constructor(
private val communicationWorkaround: CommunicationWorkaround,
val audioProcessingController: AudioProcessingController,
val lkObjects: LKObjects,
networkCallbackManagerFactory: NetworkCallbackManagerFactory,
) : RTCEngine.Listener, ParticipantListener {
private lateinit var coroutineScope: CoroutineScope
... ... @@ -753,7 +753,7 @@ constructor(
}
// ------------------------------------- NetworkCallback -------------------------------------//
private val networkCallbackManager = NetworkCallbackManager(
private val networkCallbackManager = networkCallbackManagerFactory.invoke(
object : NetworkCallback() {
override fun onLost(network: Network) {
// lost connection, flip to reconnecting
... ... @@ -770,8 +770,7 @@ constructor(
hasLostConnectivity = false
}
},
connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager,
).apply { closeableManager.registerClosable(this) }
)
// ----------------------------------- RTCEngine.Listener ------------------------------------//
... ...
... ... @@ -16,6 +16,7 @@
package io.livekit.android.room
import androidx.annotation.VisibleForTesting
import com.vdurmont.semver4j.Semver
import io.livekit.android.ConnectOptions
import io.livekit.android.RoomOptions
... ... @@ -122,7 +123,8 @@ constructor(
/**
* @throws Exception if fails to connect.
*/
internal suspend fun reconnect(url: String, token: String, participantSid: String?): Either<ReconnectResponse, Unit> {
@VisibleForTesting
suspend fun reconnect(url: String, token: String, participantSid: String?): Either<ReconnectResponse, Unit> {
val reconnectResponse = connect(
url,
token,
... ...
... ... @@ -17,12 +17,40 @@
package io.livekit.android.room.network
import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import io.livekit.android.util.LKLog
import java.io.Closeable
import java.util.concurrent.atomic.AtomicBoolean
typealias NetworkCallbackManagerFactory = @JvmSuppressWildcards (
networkCallback: NetworkCallback,
) -> NetworkCallbackManager
/**
* @suppress
*/
interface NetworkCallbackRegistry {
fun registerNetworkCallback(networkRequest: NetworkRequest, networkCallback: NetworkCallback)
fun unregisterNetworkCallback(networkCallback: NetworkCallback)
}
internal class NetworkCallbackRegistryImpl(val connectivityManager: ConnectivityManager) : NetworkCallbackRegistry {
override fun registerNetworkCallback(networkRequest: NetworkRequest, networkCallback: NetworkCallback) {
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
}
override fun unregisterNetworkCallback(networkCallback: NetworkCallback) {
connectivityManager.unregisterNetworkCallback(networkCallback)
}
}
interface NetworkCallbackManager : Closeable {
fun registerCallback()
fun unregisterCallback()
}
/**
* Manages a [ConnectivityManager.NetworkCallback] so that it is never
* registered multiple times. A NetworkCallback is allowed to be registered
... ... @@ -30,16 +58,18 @@ import java.util.concurrent.atomic.AtomicBoolean
* requests will leak on 8.0 and earlier.
*
* There's a 100 request hard limit, so leaks here are particularly dangerous.
*
* @suppress
*/
class NetworkCallbackManager(
private val networkCallback: ConnectivityManager.NetworkCallback,
private val connectivityManager: ConnectivityManager,
) : Closeable {
class NetworkCallbackManagerImpl(
private val networkCallback: NetworkCallback,
private val connectivityManager: NetworkCallbackRegistry,
) : NetworkCallbackManager {
private val isRegistered = AtomicBoolean(false)
private val isClosed = AtomicBoolean(false)
@Synchronized
fun registerCallback() {
override fun registerCallback() {
if (!isClosed.get() && isRegistered.compareAndSet(false, true)) {
try {
val networkRequest = NetworkRequest.Builder()
... ... @@ -53,7 +83,7 @@ class NetworkCallbackManager(
}
@Synchronized
fun unregisterCallback() {
override fun unregisterCallback() {
if (!isClosed.get() && isRegistered.compareAndSet(true, false)) {
try {
connectivityManager.unregisterNetworkCallback(networkCallback)
... ...
... ... @@ -16,6 +16,7 @@
package io.livekit.android.room.participant
import androidx.annotation.VisibleForTesting
import io.livekit.android.dagger.InjectionNames
import io.livekit.android.events.BroadcastEventBus
import io.livekit.android.events.ParticipantEvent
... ... @@ -75,7 +76,7 @@ open class Participant(
@FlowObservable
@get:FlowObservable
var identity: Identity? by flowDelegate(identity)
internal set
@VisibleForTesting set
/**
* Changes can be observed by using [io.livekit.android.util.flow]
... ... @@ -83,7 +84,7 @@ open class Participant(
@FlowObservable
@get:FlowObservable
var audioLevel: Float by flowDelegate(0f)
internal set
@VisibleForTesting set
/**
* Changes can be observed by using [io.livekit.android.util.flow]
... ... @@ -99,7 +100,7 @@ open class Participant(
}
}
}
internal set
@VisibleForTesting set
@FlowObservable
@get:FlowObservable
... ... @@ -108,7 +109,7 @@ open class Participant(
eventBus.postEvent(ParticipantEvent.NameChanged(this, newValue), scope)
}
}
internal set
@VisibleForTesting set
/**
* Changes can be observed by using [io.livekit.android.util.flow]
... ... @@ -121,7 +122,7 @@ open class Participant(
eventBus.postEvent(ParticipantEvent.MetadataChanged(this, oldMetadata), scope)
}
}
internal set
@VisibleForTesting set
/**
*
... ... @@ -168,7 +169,8 @@ open class Participant(
* @suppress
*/
@Deprecated("Use events instead")
internal var internalListener: ParticipantListener? = null
@VisibleForTesting
var internalListener: ParticipantListener? = null
val hasInfo
get() = participantInfo != null
... ... @@ -300,7 +302,8 @@ open class Participant(
/**
* @suppress
*/
internal open fun updateFromInfo(info: LivekitModels.ParticipantInfo) {
@VisibleForTesting
open fun updateFromInfo(info: LivekitModels.ParticipantInfo) {
sid = Sid(info.sid)
identity = Identity(info.identity)
participantInfo = info
... ... @@ -351,7 +354,11 @@ open class Participant(
}
}
internal open fun dispose() {
/**
* @suppress
*/
@VisibleForTesting
open fun dispose() {
scope.cancel()
sid = Sid("")
... ...
... ... @@ -17,6 +17,7 @@
package io.livekit.android.room.track
import android.view.View
import androidx.annotation.VisibleForTesting
import io.livekit.android.dagger.InjectionNames
import io.livekit.android.events.TrackEvent
import io.livekit.android.room.track.video.VideoSinkVisibility
... ... @@ -41,9 +42,18 @@ class RemoteVideoTrack(
private val sinkVisibilityMap = mutableMapOf<VideoSink, VideoSinkVisibility>()
private val visibilities = sinkVisibilityMap.values
internal var lastVisibility = false
/**
* @suppress
*/
@VisibleForTesting
var lastVisibility = false
private set
internal var lastDimensions: Dimensions = Dimensions(0, 0)
/**
* @suppress
*/
@VisibleForTesting
var lastDimensions: Dimensions = Dimensions(0, 0)
private set
internal var receiver: RtpReceiver
... ...
... ... @@ -16,7 +16,10 @@
package io.livekit.android.util
internal sealed class Either<out A, out B> {
/**
* @suppress
*/
sealed class Either<out A, out B> {
class Left<out A>(val value: A) : Either<A, Nothing>()
class Right<out B>(val value: B) : Either<Nothing, B>()
}
... ...
... ... @@ -20,7 +20,7 @@ import com.google.protobuf.MessageLite
import okio.ByteString
import okio.ByteString.Companion.toByteString
internal fun MessageLite.toOkioByteString(): ByteString {
fun MessageLite.toOkioByteString(): ByteString {
val byteArray = toByteArray()
return byteArray.toByteString(0, byteArray.size)
}
... ...
... ... @@ -20,9 +20,15 @@ import android.gov.nist.javax.sdp.fields.AttributeField
import android.javax.sdp.MediaDescription
import io.livekit.android.util.LKLog
internal data class SdpRtp(val payload: Long, val codec: String, val rate: Long?, val encoding: String?)
/**
* @suppress
*/
data class SdpRtp(val payload: Long, val codec: String, val rate: Long?, val encoding: String?)
internal fun MediaDescription.getRtps(): List<Pair<AttributeField, SdpRtp>> {
/**
* @suppress
*/
fun MediaDescription.getRtps(): List<Pair<AttributeField, SdpRtp>> {
return getAttributes(true)
.filterIsInstance<AttributeField>()
.filter { it.attribute.name == "rtpmap" }
... ... @@ -43,17 +49,26 @@ internal fun tryParseRtp(string: String): SdpRtp? {
return SdpRtp(payload.toLong(), codec, toOptionalLong(rate), toOptionalString(encoding))
}
internal data class SdpMsid(
/**
* @suppress
*/
data class SdpMsid(
/** holds the msid-id (and msid-appdata if available) */
val value: String,
)
internal fun MediaDescription.getMsid(): SdpMsid? {
/**
* @suppress
*/
fun MediaDescription.getMsid(): SdpMsid? {
val attribute = getAttribute("msid") ?: return null
return SdpMsid(attribute)
}
internal data class SdpFmtp(val payload: Long, val config: String) {
/**
* @suppress
*/
data class SdpFmtp(val payload: Long, val config: String) {
fun toAttributeField(): AttributeField {
return AttributeField().apply {
name = "fmtp"
... ... @@ -62,7 +77,10 @@ internal data class SdpFmtp(val payload: Long, val config: String) {
}
}
internal fun MediaDescription.getFmtps(): List<Pair<AttributeField, SdpFmtp>> {
/**
* @suppress
*/
fun MediaDescription.getFmtps(): List<Pair<AttributeField, SdpFmtp>> {
return getAttributes(true)
.filterIsInstance<AttributeField>()
.filter { it.attribute.name == "fmtp" }
... ... @@ -83,7 +101,10 @@ internal fun tryParseFmtp(string: String): SdpFmtp? {
return SdpFmtp(payload.toLong(), config)
}
internal data class SdpExt(val value: Long, val direction: String?, val encryptUri: String?, val uri: String, val config: String?) {
/**
* @suppress
*/
data class SdpExt(val value: Long, val direction: String?, val encryptUri: String?, val uri: String, val config: String?) {
fun toAttributeField(): AttributeField {
return AttributeField().apply {
name = "extmap"
... ... @@ -104,7 +125,10 @@ internal data class SdpExt(val value: Long, val direction: String?, val encryptU
}
}
internal fun MediaDescription.getExts(): List<Pair<AttributeField, SdpExt>> {
/**
* @suppress
*/
fun MediaDescription.getExts(): List<Pair<AttributeField, SdpExt>> {
return getAttributes(true)
.filterIsInstance<AttributeField>()
.filter { it.attribute.name == "extmap" }
... ...
... ... @@ -43,13 +43,13 @@ internal fun PeerConnection.PeerConnectionState.isDisconnected(): Boolean {
}
}
internal fun RTCConfiguration.copy(): RTCConfiguration {
fun RTCConfiguration.copy(): RTCConfiguration {
val newConfig = RTCConfiguration(emptyList())
newConfig.copyFrom(this)
return newConfig
}
internal fun RTCConfiguration.copyFrom(config: RTCConfiguration) {
fun RTCConfiguration.copyFrom(config: RTCConfiguration) {
iceTransportsType = config.iceTransportsType
iceServers = config.iceServers
bundlePolicy = config.bundlePolicy
... ...
... ... @@ -46,8 +46,14 @@ private val threadFactory = object : ThreadFactory {
private var executor = Executors.newSingleThreadExecutor(threadFactory)
private var rtcDispatcher: CoroutineDispatcher = executor.asCoroutineDispatcher()
/**
* Overrides how RTC thread calls are executed and dispatched.
*
* This should absolutely not be used in production environments and is
* only to be used for testing.
*/
@VisibleForTesting
internal fun overrideExecutorAndDispatcher(executorService: ExecutorService, dispatcher: CoroutineDispatcher) {
fun overrideExecutorAndDispatcher(executorService: ExecutorService, dispatcher: CoroutineDispatcher) {
executor = executorService
rtcDispatcher = dispatcher
}
... ... @@ -56,8 +62,10 @@ internal fun overrideExecutorAndDispatcher(executorService: ExecutorService, dis
* Execute [action] on the RTC thread. The PeerConnection API
* is generally not thread safe, so all actions relating to
* peer connection objects should go through the RTC thread.
*
* @suppress
*/
fun <T> executeOnRTCThread(action: () -> T) {
internal fun <T> executeOnRTCThread(action: () -> T) {
if (Thread.currentThread().name.startsWith(EXECUTOR_THREADNAME_PREFIX)) {
action()
} else {
... ... @@ -69,8 +77,10 @@ fun <T> executeOnRTCThread(action: () -> T) {
* Execute [action] synchronously on the RTC thread. The PeerConnection API
* is generally not thread safe, so all actions relating to
* peer connection objects should go through the RTC thread.
*
* @suppress
*/
fun <T> executeBlockingOnRTCThread(action: () -> T): T {
internal fun <T> executeBlockingOnRTCThread(action: () -> T): T {
return if (Thread.currentThread().name.startsWith(EXECUTOR_THREADNAME_PREFIX)) {
action()
} else {
... ... @@ -83,7 +93,7 @@ fun <T> executeBlockingOnRTCThread(action: () -> T): T {
* is generally not thread safe, so all actions relating to
* peer connection objects should go through the RTC thread.
*/
suspend fun <T> launchBlockingOnRTCThread(action: suspend CoroutineScope.() -> T): T = coroutineScope {
internal suspend fun <T> launchBlockingOnRTCThread(action: suspend CoroutineScope.() -> T): T = coroutineScope {
return@coroutineScope if (Thread.currentThread().name.startsWith(EXECUTOR_THREADNAME_PREFIX)) {
this.action()
} else {
... ...
/build
\ No newline at end of file
... ...
plugins {
id "org.jetbrains.dokka"
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
}
android {
namespace 'io.livekit.android.test'
compileSdkVersion androidSdk.compileVersion
defaultConfig {
minSdkVersion androidSdk.minVersion
targetSdkVersion androidSdk.targetVersion
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
lintOptions {
disable 'VisibleForTests'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility java_version
targetCompatibility java_version
}
kotlinOptions {
freeCompilerArgs = ["-Xinline-classes", "-opt-in=kotlin.RequiresOptIn"]
jvmTarget = java_version
}
testOptions {
unitTests {
includeAndroidResources = true
}
}
}
dokkaHtml {
moduleName.set("livekit-android-test")
dokkaSourceSets {
configureEach {
skipEmptyPackages.set(true)
includeNonPublic.set(false)
includes.from("module.md")
displayName.set("LiveKit Mocks")
sourceLink {
localDirectory.set(file("src/main/java"))
// URL showing where the source code can be accessed through the web browser
remoteUrl.set(new URL(
"https://github.com/livekit/client-sdk-android/tree/master/livekit-android-test/src/main/java"))
// Suffix which is used to append the line number to the URL. Use #L for GitHub
remoteLineSuffix.set("#L")
}
perPackageOption {
matchingRegex.set(".*\\.dagger.*")
suppress.set(true)
}
perPackageOption {
matchingRegex.set(".*\\.util.*")
suppress.set(true)
}
}
}
}
dependencies {
implementation(project(":livekit-android-sdk"))
implementation libs.timber
implementation libs.coroutines.lib
implementation libs.kotlinx.serialization.json
api libs.webrtc
api libs.okhttp
api libs.audioswitch
implementation libs.androidx.annotation
api libs.protobuf.javalite
implementation libs.android.jain.sip.ri
implementation libs.junit
implementation libs.robolectric
implementation libs.mockito.core
implementation libs.mockito.kotlin
implementation libs.mockito.inline
implementation libs.androidx.test.core
implementation libs.coroutines.test
implementation libs.dagger.lib
kapt libs.dagger.compiler
testImplementation libs.junit
testImplementation libs.robolectric
kaptTest libs.dagger.compiler
androidTestImplementation libs.androidx.test.junit
androidTestImplementation libs.espresso
}
tasks.withType(Test).configureEach {
systemProperty "robolectric.logging.enabled", true
}
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
afterEvaluate {
publishing {
publications {
// Creates a Maven publication called "release".
release(MavenPublication) {
// Applies the component for the release build variant.
from components.release
// You can then customize attributes of the publication as shown below.
groupId = GROUP
artifactId = POM_ARTIFACT_ID
version = VERSION_NAME
}
}
}
}
... ...
POM_NAME=LiveKit Client Android Mocks for Testing
POM_ARTIFACT_ID=livekit-android-test
POM_PACKAGING=aar
... ...
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
... ...
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ... @@ -14,11 +14,11 @@
* limitations under the License.
*/
package io.livekit.android
package io.livekit.android.test
import com.google.common.util.concurrent.MoreExecutors
import io.livekit.android.coroutines.TestCoroutineRule
import io.livekit.android.util.LoggingRule
import io.livekit.android.test.coroutines.TestCoroutineRule
import io.livekit.android.test.util.LoggingRule
import io.livekit.android.webrtc.peerconnection.overrideExecutorAndDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
... ...
... ... @@ -14,18 +14,18 @@
* limitations under the License.
*/
package io.livekit.android
package io.livekit.android.test
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.google.protobuf.MessageLite
import io.livekit.android.mock.MockPeerConnection
import io.livekit.android.mock.MockWebSocketFactory
import io.livekit.android.mock.dagger.DaggerTestLiveKitComponent
import io.livekit.android.mock.dagger.TestCoroutinesModule
import io.livekit.android.mock.dagger.TestLiveKitComponent
import io.livekit.android.room.Room
import io.livekit.android.room.SignalClientTest
import io.livekit.android.test.mock.MockPeerConnection
import io.livekit.android.test.mock.MockWebSocketFactory
import io.livekit.android.test.mock.TestData
import io.livekit.android.test.mock.dagger.DaggerTestLiveKitComponent
import io.livekit.android.test.mock.dagger.TestCoroutinesModule
import io.livekit.android.test.mock.dagger.TestLiveKitComponent
import io.livekit.android.util.toOkioByteString
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
... ... @@ -44,10 +44,10 @@ import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
abstract class MockE2ETest : BaseTest() {
internal lateinit var component: TestLiveKitComponent
internal lateinit var context: Context
internal lateinit var room: Room
internal lateinit var wsFactory: MockWebSocketFactory
lateinit var component: TestLiveKitComponent
lateinit var context: Context
lateinit var room: Room
lateinit var wsFactory: MockWebSocketFactory
@Before
fun mocksSetup() {
... ... @@ -66,7 +66,7 @@ abstract class MockE2ETest : BaseTest() {
room.release()
}
suspend fun connect(joinResponse: LivekitRtc.SignalResponse = SignalClientTest.JOIN) {
suspend fun connect(joinResponse: LivekitRtc.SignalResponse = TestData.JOIN) {
connectSignal(joinResponse)
connectPeerConnection()
}
... ... @@ -74,7 +74,7 @@ abstract class MockE2ETest : BaseTest() {
suspend fun connectSignal(joinResponse: LivekitRtc.SignalResponse) {
val job = coroutineRule.scope.launch {
room.connect(
url = SignalClientTest.EXAMPLE_URL,
url = TestData.EXAMPLE_URL,
token = "",
)
}
... ... @@ -84,23 +84,23 @@ abstract class MockE2ETest : BaseTest() {
job.join()
}
suspend fun getSubscriberPeerConnection() =
fun getSubscriberPeerConnection() =
component
.rtcEngine()
.getSubscriberPeerConnection() as MockPeerConnection
suspend fun getPublisherPeerConnection() =
fun getPublisherPeerConnection() =
component
.rtcEngine()
.getPublisherPeerConnection() as MockPeerConnection
suspend fun connectPeerConnection() {
simulateMessageFromServer(SignalClientTest.OFFER)
fun connectPeerConnection() {
simulateMessageFromServer(TestData.OFFER)
val subPeerConnection = getSubscriberPeerConnection()
subPeerConnection.moveToIceConnectionState(PeerConnection.IceConnectionState.CONNECTED)
}
suspend fun disconnectPeerConnection() {
fun disconnectPeerConnection() {
val subPeerConnection = component
.rtcEngine()
.getSubscriberPeerConnection() as MockPeerConnection
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ... @@ -14,12 +14,16 @@
* limitations under the License.
*/
package io.livekit.android.assert
package io.livekit.android.test.assert
import org.junit.Assert
fun assertIsClass(expectedClass: Class<*>, actual: Any) {
val klazz = actual::class.java
fun assertIsClass(expectedClass: Class<*>, actual: Any?) {
val klazz = if (actual == null) {
Nothing::class.java
} else {
actual::class.java
}
Assert.assertEquals(expectedClass, klazz)
}
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.coroutines
package io.livekit.android.test.coroutines
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.cancel
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.coroutines
package io.livekit.android.test.coroutines
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ... @@ -14,8 +14,10 @@
* limitations under the License.
*/
package io.livekit.android.events
package io.livekit.android.test.events
import io.livekit.android.events.Event
import io.livekit.android.events.EventListenable
import kotlinx.coroutines.CoroutineScope
class EventCollector<T : Event>(
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ... @@ -14,11 +14,10 @@
* limitations under the License.
*/
package io.livekit.android.events
package io.livekit.android.test.events
import io.livekit.android.coroutines.toListUntilSignal
import io.livekit.android.test.coroutines.toListUntilSignal
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
... ... @@ -35,7 +34,6 @@ open class FlowCollector<T>(
/**
* Stop collecting events. returns the events collected.
*/
@OptIn(ExperimentalCoroutinesApi::class)
suspend fun stopCollecting(): List<T> {
signal.compareAndSet(null, Unit)
return collectEventsDeferred.await()
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import io.livekit.android.audio.AudioProcessingController
import io.livekit.android.audio.AudioProcessorInterface
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import livekit.org.webrtc.AudioTrack
... ...
... ... @@ -14,13 +14,14 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import livekit.org.webrtc.DataChannel
class MockDataChannel(private val label: String?) : DataChannel(1L) {
var observer: DataChannel.Observer? = null
var observer: Observer? = null
var sentBuffers = mutableListOf<Buffer?>()
override fun registerObserver(observer: Observer?) {
this.observer = observer
}
... ... @@ -46,6 +47,7 @@ class MockDataChannel(private val label: String?) : DataChannel(1L) {
}
override fun send(buffer: Buffer?): Boolean {
sentBuffers.add(buffer)
return true
}
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import android.graphics.SurfaceTexture
import android.view.Surface
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import io.livekit.android.room.provisions.LKObjects
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import livekit.org.webrtc.AudioTrack
import livekit.org.webrtc.MediaStream
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import livekit.org.webrtc.MediaStreamTrack
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ... @@ -14,25 +14,19 @@
* limitations under the License.
*/
package io.livekit.android
package io.livekit.android.test.mock
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import android.net.ConnectivityManager.NetworkCallback
import android.net.NetworkRequest
import io.livekit.android.room.network.NetworkCallbackRegistry
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.livekit.android.test", appContext.packageName)
class MockNetworkCallbackRegistry : NetworkCallbackRegistry {
val networkCallbacks = mutableSetOf<NetworkCallback>()
override fun registerNetworkCallback(networkRequest: NetworkRequest, networkCallback: NetworkCallback) {
networkCallbacks.add(networkCallback)
}
override fun unregisterNetworkCallback(networkCallback: NetworkCallback) {
networkCallbacks.remove(networkCallback)
}
}
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import livekit.org.webrtc.DataChannel
import livekit.org.webrtc.IceCandidate
... ... @@ -64,8 +64,13 @@ class MockPeerConnection(
return null
}
val dataChannels = mutableMapOf<String, DataChannel>()
override fun createDataChannel(label: String?, init: DataChannel.Init?): DataChannel {
return MockDataChannel(label)
val dataChannel = MockDataChannel(label)
if (label != null) {
dataChannels[label] = dataChannel
}
return dataChannel
}
override fun createOffer(observer: SdpObserver?, constraints: MediaConstraints?) {
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import livekit.org.webrtc.RtpReceiver
import org.mockito.Mockito
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import livekit.org.webrtc.RtpSender
import org.mockito.Mockito
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import android.content.Context
import livekit.org.webrtc.CapturerObserver
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import livekit.org.webrtc.VideoSource
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import livekit.org.webrtc.VideoSink
import livekit.org.webrtc.VideoTrack
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import okhttp3.Request
import okhttp3.WebSocket
... ...
... ... @@ -14,11 +14,12 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import com.google.protobuf.MessageLite
import io.livekit.android.test.util.toOkioByteString
import io.livekit.android.test.util.toPBByteString
import io.livekit.android.util.toOkioByteString
import io.livekit.android.util.toPBByteString
import livekit.LivekitModels
import livekit.LivekitRtc.LeaveRequest
import livekit.LivekitRtc.SignalRequest
... ...
... ... @@ -14,32 +14,39 @@
* limitations under the License.
*/
package io.livekit.android.mock
package io.livekit.android.test.mock
import livekit.LivekitModels
import livekit.LivekitRtc
object TestData {
const val EXAMPLE_URL = "ws://www.example.com"
val LOCAL_AUDIO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) {
sid = "TR_local_audio_track_sid"
type = LivekitModels.TrackType.AUDIO
source = LivekitModels.TrackSource.MICROPHONE
build()
}
val LOCAL_VIDEO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) {
sid = "TR_local_video_track_sid"
type = LivekitModels.TrackType.VIDEO
source = LivekitModels.TrackSource.CAMERA
build()
}
val REMOTE_AUDIO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) {
sid = "TR_remote_audio_track_sid"
type = LivekitModels.TrackType.AUDIO
source = LivekitModels.TrackSource.MICROPHONE
build()
}
val REMOTE_VIDEO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) {
sid = "TR_remote_video_track_sid"
type = LivekitModels.TrackType.VIDEO
source = LivekitModels.TrackSource.CAMERA
build()
}
... ... @@ -81,4 +88,218 @@ object TestData {
active = true
build()
}
// Signal Responses
// /////////////////////////////////
val JOIN = with(LivekitRtc.SignalResponse.newBuilder()) {
join = with(LivekitRtc.JoinResponse.newBuilder()) {
room = with(LivekitModels.Room.newBuilder()) {
name = "roomname"
build()
}
participant = LOCAL_PARTICIPANT
subscriberPrimary = true
addIceServers(
with(LivekitRtc.ICEServer.newBuilder()) {
addUrls("stun:stun.join.com:19302")
username = "username"
credential = "credential"
build()
},
)
serverVersion = "0.15.2"
build()
}
build()
}
val RECONNECT = with(LivekitRtc.SignalResponse.newBuilder()) {
reconnect = with(LivekitRtc.ReconnectResponse.newBuilder()) {
addIceServers(
with(LivekitRtc.ICEServer.newBuilder()) {
addUrls("stun:stun.reconnect.com:19302")
username = "username"
credential = "credential"
build()
},
)
clientConfiguration = with(LivekitModels.ClientConfiguration.newBuilder()) {
forceRelay = LivekitModels.ClientConfigSetting.ENABLED
build()
}
build()
}
build()
}
val OFFER = with(LivekitRtc.SignalResponse.newBuilder()) {
offer = with(LivekitRtc.SessionDescription.newBuilder()) {
sdp = "remote_offer"
type = "offer"
build()
}
build()
}
val ROOM_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {
roomUpdate = with(LivekitRtc.RoomUpdate.newBuilder()) {
room = with(LivekitModels.Room.newBuilder()) {
sid = "room_sid"
metadata = "metadata"
activeRecording = true
build()
}
build()
}
build()
}
val LOCAL_TRACK_PUBLISHED = with(LivekitRtc.SignalResponse.newBuilder()) {
trackPublished = with(LivekitRtc.TrackPublishedResponse.newBuilder()) {
cid = "local_cid"
track = LOCAL_AUDIO_TRACK
build()
}
build()
}
val LOCAL_TRACK_UNPUBLISHED = with(LivekitRtc.SignalResponse.newBuilder()) {
trackUnpublished = with(LivekitRtc.TrackUnpublishedResponse.newBuilder()) {
trackSid = LOCAL_AUDIO_TRACK.sid
build()
}
build()
}
val PERMISSION_CHANGE = with(LivekitRtc.SignalResponse.newBuilder()) {
update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
addParticipants(
LOCAL_PARTICIPANT.toBuilder()
.setPermission(
LivekitModels.ParticipantPermission.newBuilder()
.setCanPublish(false)
.setCanSubscribe(false)
.setCanPublishData(false)
.setHidden(false)
.setRecorder(false)
.build(),
)
.build(),
)
build()
}
build()
}
val PARTICIPANT_JOIN = with(LivekitRtc.SignalResponse.newBuilder()) {
update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
addParticipants(REMOTE_PARTICIPANT)
build()
}
build()
}
val PARTICIPANT_DISCONNECT = with(LivekitRtc.SignalResponse.newBuilder()) {
update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
val disconnectedParticipant = REMOTE_PARTICIPANT.toBuilder()
.setState(LivekitModels.ParticipantInfo.State.DISCONNECTED)
.build()
addParticipants(disconnectedParticipant)
build()
}
build()
}
val ACTIVE_SPEAKER_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {
speakersChanged = with(LivekitRtc.SpeakersChanged.newBuilder()) {
addSpeakers(REMOTE_SPEAKER_INFO)
build()
}
build()
}
val LOCAL_PARTICIPANT_METADATA_CHANGED = with(LivekitRtc.SignalResponse.newBuilder()) {
update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
val participantMetadataChanged = LOCAL_PARTICIPANT.toBuilder()
.setMetadata("changed_metadata")
.setName("changed_name")
.build()
addParticipants(participantMetadataChanged)
build()
}
build()
}
val REMOTE_PARTICIPANT_METADATA_CHANGED = with(LivekitRtc.SignalResponse.newBuilder()) {
update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
val participantMetadataChanged = REMOTE_PARTICIPANT.toBuilder()
.setMetadata("changed_metadata")
.setName("changed_name")
.build()
addParticipants(participantMetadataChanged)
build()
}
build()
}
val CONNECTION_QUALITY = with(LivekitRtc.SignalResponse.newBuilder()) {
connectionQuality = with(LivekitRtc.ConnectionQualityUpdate.newBuilder()) {
addUpdates(
with(LivekitRtc.ConnectionQualityInfo.newBuilder()) {
participantSid = JOIN.join.participant.sid
quality = LivekitModels.ConnectionQuality.EXCELLENT
build()
},
)
build()
}
build()
}
val STREAM_STATE_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {
streamStateUpdate = with(LivekitRtc.StreamStateUpdate.newBuilder()) {
addStreamStates(
with(LivekitRtc.StreamStateInfo.newBuilder()) {
participantSid = REMOTE_PARTICIPANT.sid
trackSid = REMOTE_AUDIO_TRACK.sid
state = LivekitRtc.StreamState.ACTIVE
build()
},
)
build()
}
build()
}
val SUBSCRIPTION_PERMISSION_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {
subscriptionPermissionUpdate = with(LivekitRtc.SubscriptionPermissionUpdate.newBuilder()) {
participantSid = REMOTE_PARTICIPANT.sid
trackSid = REMOTE_AUDIO_TRACK.sid
allowed = false
build()
}
build()
}
val REFRESH_TOKEN = with(LivekitRtc.SignalResponse.newBuilder()) {
refreshToken = "refresh_token"
build()
}
val PONG = with(LivekitRtc.SignalResponse.newBuilder()) {
pong = 1L
build()
}
val LEAVE = with(LivekitRtc.SignalResponse.newBuilder()) {
leave = with(LivekitRtc.LeaveRequest.newBuilder()) {
reason = LivekitModels.DisconnectReason.SERVER_SHUTDOWN
build()
}
build()
}
}
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock.dagger
package io.livekit.android.test.mock.dagger
import dagger.Binds
import dagger.Module
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock.dagger
package io.livekit.android.test.mock.dagger
import dagger.Module
import dagger.Provides
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock.dagger
package io.livekit.android.test.mock.dagger
import android.content.Context
import dagger.BindsInstance
... ... @@ -22,8 +22,9 @@ import dagger.Component
import io.livekit.android.dagger.JsonFormatModule
import io.livekit.android.dagger.LiveKitComponent
import io.livekit.android.dagger.MemoryModule
import io.livekit.android.mock.MockWebSocketFactory
import io.livekit.android.room.RTCEngine
import io.livekit.android.test.mock.MockNetworkCallbackRegistry
import io.livekit.android.test.mock.MockWebSocketFactory
import javax.inject.Singleton
@Singleton
... ... @@ -37,12 +38,14 @@ import javax.inject.Singleton
MemoryModule::class,
],
)
internal interface TestLiveKitComponent : LiveKitComponent {
interface TestLiveKitComponent : LiveKitComponent {
fun websocketFactory(): MockWebSocketFactory
fun rtcEngine(): RTCEngine
fun networkCallbackRegistry(): MockNetworkCallbackRegistry
@Component.Factory
interface Factory {
fun create(
... ...
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.mock.dagger
package io.livekit.android.test.mock.dagger
import android.content.Context
import android.javax.sdp.SdpFactory
... ... @@ -23,8 +23,8 @@ import dagger.Provides
import io.livekit.android.audio.AudioProcessingController
import io.livekit.android.dagger.CapabilitiesGetter
import io.livekit.android.dagger.InjectionNames
import io.livekit.android.mock.MockAudioProcessingController
import io.livekit.android.mock.MockEglBase
import io.livekit.android.test.mock.MockAudioProcessingController
import io.livekit.android.test.mock.MockEglBase
import livekit.org.webrtc.*
import javax.inject.Named
import javax.inject.Singleton
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ... @@ -14,14 +14,19 @@
* limitations under the License.
*/
package io.livekit.android.mock.dagger
package io.livekit.android.test.mock.dagger
import android.net.ConnectivityManager
import dagger.Module
import dagger.Provides
import dagger.Reusable
import io.livekit.android.mock.MockWebSocketFactory
import io.livekit.android.memory.CloseableManager
import io.livekit.android.room.network.NetworkCallbackManagerFactory
import io.livekit.android.room.network.NetworkCallbackManagerImpl
import io.livekit.android.stats.NetworkInfo
import io.livekit.android.stats.NetworkType
import io.livekit.android.test.mock.MockNetworkCallbackRegistry
import io.livekit.android.test.mock.MockWebSocketFactory
import okhttp3.OkHttpClient
import okhttp3.Response
import okhttp3.WebSocket
... ... @@ -62,4 +67,22 @@ object TestWebModule {
override fun getNetworkType() = NetworkType.WIFI
}
}
@Provides
@Singleton
fun networkCallbackRegistrar(): MockNetworkCallbackRegistry {
return MockNetworkCallbackRegistry()
}
@Provides
@Reusable
fun networkCallbackManagerFactory(
closeableManager: CloseableManager,
registrar: MockNetworkCallbackRegistry,
): NetworkCallbackManagerFactory {
return { networkCallback: ConnectivityManager.NetworkCallback ->
NetworkCallbackManagerImpl(networkCallback, registrar)
.apply { closeableManager.registerClosable(this) }
}
}
}
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ... @@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.livekit.android.util
package io.livekit.android.test.util
import com.google.protobuf.ByteString
import okio.ByteString.Companion.toByteString
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ... @@ -14,10 +14,11 @@
* limitations under the License.
*/
package io.livekit.android.util
package io.livekit.android.test.util
import android.util.Log
import io.livekit.android.LiveKit
import io.livekit.android.util.LoggingLevel
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
... ...
... ... @@ -16,9 +16,9 @@
package livekit.org.webrtc
import io.livekit.android.mock.MockPeerConnection
import io.livekit.android.mock.MockVideoSource
import io.livekit.android.mock.MockVideoStreamTrack
import io.livekit.android.test.mock.MockPeerConnection
import io.livekit.android.test.mock.MockVideoSource
import io.livekit.android.test.mock.MockVideoStreamTrack
class MockPeerConnectionFactory : PeerConnectionFactory(1L) {
override fun createPeerConnectionInternal(
... ...
... ... @@ -16,8 +16,8 @@
package livekit.org.webrtc
import io.livekit.android.mock.MockRtpReceiver
import io.livekit.android.mock.MockRtpSender
import io.livekit.android.test.mock.MockRtpReceiver
import io.livekit.android.test.mock.MockRtpSender
import livekit.org.webrtc.RtpTransceiver.RtpTransceiverDirection
import org.mockito.Mockito
... ...
... ... @@ -16,9 +16,11 @@
package io.livekit.android.room
import io.livekit.android.MockE2ETest
import io.livekit.android.test.MockE2ETest
import io.livekit.android.test.mock.TestData
import io.livekit.android.test.util.toOkioByteString
import io.livekit.android.test.util.toPBByteString
import io.livekit.android.util.toOkioByteString
import io.livekit.android.util.toPBByteString
import kotlinx.coroutines.ExperimentalCoroutinesApi
import livekit.LivekitModels
import livekit.LivekitRtc
... ... @@ -44,7 +46,7 @@ class RTCEngineMockE2ETest : MockE2ETest() {
@Test
fun iceServersSetOnJoin() = runTest {
connect()
val sentIceServers = SignalClientTest.JOIN.join.iceServersList
val sentIceServers = TestData.JOIN.join.iceServersList
.map { it.toWebrtc() }
val subPeerConnection = getSubscriberPeerConnection()
... ... @@ -55,7 +57,7 @@ class RTCEngineMockE2ETest : MockE2ETest() {
fun iceSubscriberConnect() = runTest {
connect()
assertEquals(
SignalClientTest.OFFER.offer.sdp,
TestData.OFFER.offer.sdp,
getSubscriberPeerConnection().remoteDescription?.description,
)
... ... @@ -113,19 +115,19 @@ class RTCEngineMockE2ETest : MockE2ETest() {
connect()
val oldToken = wsFactory.request.url.queryParameter(SignalClient.CONNECT_QUERY_TOKEN)
wsFactory.listener.onMessage(wsFactory.ws, SignalClientTest.REFRESH_TOKEN.toOkioByteString())
wsFactory.listener.onMessage(wsFactory.ws, TestData.REFRESH_TOKEN.toOkioByteString())
wsFactory.listener.onFailure(wsFactory.ws, Exception(), null)
testScheduler.advanceUntilIdle()
val newToken = wsFactory.request.url.queryParameter(SignalClient.CONNECT_QUERY_TOKEN)
Assert.assertNotEquals(oldToken, newToken)
assertEquals(SignalClientTest.REFRESH_TOKEN.refreshToken, newToken)
assertEquals(TestData.REFRESH_TOKEN.refreshToken, newToken)
}
@Test
fun relayConfiguration() = runTest {
connect(
with(SignalClientTest.JOIN.toBuilder()) {
with(TestData.JOIN.toBuilder()) {
join = with(join.toBuilder()) {
clientConfiguration = with(LivekitModels.ClientConfiguration.newBuilder()) {
forceRelay = LivekitModels.ClientConfigSetting.ENABLED
... ... @@ -147,6 +149,6 @@ class RTCEngineMockE2ETest : MockE2ETest() {
testScheduler.advanceUntilIdle()
val sid = wsFactory.request.url.queryParameter(SignalClient.CONNECT_QUERY_PARTICIPANT_SID)
assertEquals(SignalClientTest.JOIN.join.participant.sid, sid)
assertEquals(TestData.JOIN.join.participant.sid, sid)
}
}
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ...
... ... @@ -17,12 +17,12 @@
package io.livekit.android.room
import com.google.protobuf.ByteString
import io.livekit.android.MockE2ETest
import io.livekit.android.assert.assertIsClass
import io.livekit.android.events.EventCollector
import io.livekit.android.events.RoomEvent
import io.livekit.android.mock.MockDataChannel
import io.livekit.android.mock.MockPeerConnection
import io.livekit.android.test.MockE2ETest
import io.livekit.android.test.assert.assertIsClass
import io.livekit.android.test.events.EventCollector
import io.livekit.android.test.mock.MockDataChannel
import io.livekit.android.test.mock.MockPeerConnection
import kotlinx.coroutines.ExperimentalCoroutinesApi
import livekit.LivekitModels.DataPacket
import livekit.LivekitModels.UserPacket
... ...
... ... @@ -16,21 +16,20 @@
package io.livekit.android.room
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import androidx.test.platform.app.InstrumentationRegistry
import io.livekit.android.MockE2ETest
import io.livekit.android.assert.assertIsClassList
import io.livekit.android.events.*
import io.livekit.android.mock.MockAudioStreamTrack
import io.livekit.android.mock.MockMediaStream
import io.livekit.android.mock.MockRtpReceiver
import io.livekit.android.mock.TestData
import io.livekit.android.mock.createMediaStreamId
import io.livekit.android.room.participant.ConnectionQuality
import io.livekit.android.room.track.LocalAudioTrack
import io.livekit.android.room.track.Track
import io.livekit.android.test.MockE2ETest
import io.livekit.android.test.assert.assertIsClassList
import io.livekit.android.test.events.EventCollector
import io.livekit.android.test.events.FlowCollector
import io.livekit.android.test.mock.MockAudioStreamTrack
import io.livekit.android.test.mock.MockMediaStream
import io.livekit.android.test.mock.MockRtpReceiver
import io.livekit.android.test.mock.TestData
import io.livekit.android.test.mock.createMediaStreamId
import io.livekit.android.util.flow
import io.livekit.android.util.toOkioByteString
import junit.framework.Assert.assertEquals
... ... @@ -45,8 +44,6 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.robolectric.RobolectricTestRunner
import org.robolectric.Shadows.shadowOf
import org.robolectric.shadows.ShadowConnectivityManager
@ExperimentalCoroutinesApi
@RunWith(RobolectricTestRunner::class)
... ... @@ -79,8 +76,8 @@ class RoomMockE2ETest : MockE2ETest() {
@Test
fun connectNoEventsWithRemoteParticipant() = runTest {
val joinResponse = with(SignalClientTest.JOIN.toBuilder()) {
join = with(SignalClientTest.JOIN.join.toBuilder()) {
val joinResponse = with(TestData.JOIN.toBuilder()) {
join = with(TestData.JOIN.join.toBuilder()) {
addOtherParticipants(TestData.REMOTE_PARTICIPANT)
build()
}
... ... @@ -104,7 +101,7 @@ class RoomMockE2ETest : MockE2ETest() {
val job = coroutineRule.scope.launch {
try {
room.connect(
url = SignalClientTest.EXAMPLE_URL,
url = TestData.EXAMPLE_URL,
token = "",
)
} catch (e: Throwable) {
... ... @@ -123,8 +120,8 @@ class RoomMockE2ETest : MockE2ETest() {
fun roomUpdateTest() = runTest {
connect()
val eventCollector = EventCollector(room.events, coroutineRule.scope)
val roomUpdate = SignalClientTest.ROOM_UPDATE
wsFactory.listener.onMessage(wsFactory.ws, SignalClientTest.ROOM_UPDATE.toOkioByteString())
val roomUpdate = TestData.ROOM_UPDATE
wsFactory.listener.onMessage(wsFactory.ws, TestData.ROOM_UPDATE.toOkioByteString())
val events = eventCollector.stopCollecting()
assertEquals(
... ... @@ -150,7 +147,7 @@ class RoomMockE2ETest : MockE2ETest() {
val eventCollector = EventCollector(room.events, coroutineRule.scope)
wsFactory.listener.onMessage(
wsFactory.ws,
SignalClientTest.CONNECTION_QUALITY.toOkioByteString(),
TestData.CONNECTION_QUALITY.toOkioByteString(),
)
val events = eventCollector.stopCollecting()
... ... @@ -164,7 +161,7 @@ class RoomMockE2ETest : MockE2ETest() {
connect()
val eventCollector = EventCollector(room.events, coroutineRule.scope)
simulateMessageFromServer(SignalClientTest.PARTICIPANT_JOIN)
simulateMessageFromServer(TestData.PARTICIPANT_JOIN)
val events = eventCollector.stopCollecting()
assertIsClassList(
... ... @@ -182,13 +179,13 @@ class RoomMockE2ETest : MockE2ETest() {
connect()
wsFactory.listener.onMessage(
wsFactory.ws,
SignalClientTest.PARTICIPANT_JOIN.toOkioByteString(),
TestData.PARTICIPANT_JOIN.toOkioByteString(),
)
val eventCollector = EventCollector(room.events, coroutineRule.scope)
wsFactory.listener.onMessage(
wsFactory.ws,
SignalClientTest.PARTICIPANT_DISCONNECT.toOkioByteString(),
TestData.PARTICIPANT_DISCONNECT.toOkioByteString(),
)
val events = eventCollector.stopCollecting()
... ... @@ -209,7 +206,7 @@ class RoomMockE2ETest : MockE2ETest() {
val eventCollector = EventCollector(room.events, coroutineRule.scope)
wsFactory.listener.onMessage(
wsFactory.ws,
SignalClientTest.ACTIVE_SPEAKER_UPDATE.toOkioByteString(),
TestData.ACTIVE_SPEAKER_UPDATE.toOkioByteString(),
)
val events = eventCollector.stopCollecting()
... ... @@ -223,7 +220,7 @@ class RoomMockE2ETest : MockE2ETest() {
wsFactory.listener.onMessage(
wsFactory.ws,
SignalClientTest.PARTICIPANT_JOIN.toOkioByteString(),
TestData.PARTICIPANT_JOIN.toOkioByteString(),
)
// We intentionally don't emit if the track isn't subscribed, so need to
... ... @@ -243,7 +240,7 @@ class RoomMockE2ETest : MockE2ETest() {
val eventCollector = EventCollector(room.events, coroutineRule.scope)
wsFactory.listener.onMessage(
wsFactory.ws,
SignalClientTest.STREAM_STATE_UPDATE.toOkioByteString(),
TestData.STREAM_STATE_UPDATE.toOkioByteString(),
)
val events = eventCollector.stopCollecting()
... ... @@ -260,7 +257,7 @@ class RoomMockE2ETest : MockE2ETest() {
wsFactory.listener.onMessage(
wsFactory.ws,
SignalClientTest.PARTICIPANT_JOIN.toOkioByteString(),
TestData.PARTICIPANT_JOIN.toOkioByteString(),
)
room.onAddTrack(
MockRtpReceiver.create(),
... ... @@ -277,7 +274,7 @@ class RoomMockE2ETest : MockE2ETest() {
val eventCollector = EventCollector(room.events, coroutineRule.scope)
wsFactory.listener.onMessage(
wsFactory.ws,
SignalClientTest.SUBSCRIPTION_PERMISSION_UPDATE.toOkioByteString(),
TestData.SUBSCRIPTION_PERMISSION_UPDATE.toOkioByteString(),
)
val events = eventCollector.stopCollecting()
... ... @@ -297,15 +294,12 @@ class RoomMockE2ETest : MockE2ETest() {
val eventCollector = FlowCollector(engine::connectionState.flow, coroutineRule.scope)
val network = Mockito.mock(Network::class.java)
val connectivityManager = InstrumentationRegistry.getInstrumentation()
.context
.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val shadowConnectivityManager: ShadowConnectivityManager = shadowOf(connectivityManager)
val networkCallbackRegistry = component.networkCallbackRegistry()
shadowConnectivityManager.networkCallbacks.forEach { callback ->
networkCallbackRegistry.networkCallbacks.forEach { callback ->
callback.onLost(network)
}
shadowConnectivityManager.networkCallbacks.forEach { callback ->
networkCallbackRegistry.networkCallbacks.forEach { callback ->
callback.onAvailable(network)
}
... ... @@ -327,7 +321,7 @@ class RoomMockE2ETest : MockE2ETest() {
val eventCollector = EventCollector(room.events, coroutineRule.scope)
wsFactory.listener.onMessage(
wsFactory.ws,
SignalClientTest.LEAVE.toOkioByteString(),
TestData.LEAVE.toOkioByteString(),
)
val events = eventCollector.stopCollecting()
... ... @@ -342,7 +336,7 @@ class RoomMockE2ETest : MockE2ETest() {
room.localParticipant.publishAudioTrack(
LocalAudioTrack(
"",
MockAudioStreamTrack(id = SignalClientTest.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
),
)
... ... @@ -350,7 +344,7 @@ class RoomMockE2ETest : MockE2ETest() {
wsFactory.listener.onMessage(
wsFactory.ws,
SignalClientTest.LEAVE.toOkioByteString(),
TestData.LEAVE.toOkioByteString(),
)
room.disconnect()
val events = eventCollector.stopCollecting()
... ... @@ -388,7 +382,7 @@ class RoomMockE2ETest : MockE2ETest() {
room.localParticipant.publishAudioTrack(
LocalAudioTrack(
"",
MockAudioStreamTrack(id = SignalClientTest.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
),
)
... ... @@ -405,7 +399,7 @@ class RoomMockE2ETest : MockE2ETest() {
fun disconnectCleansUpParticipants() = runTest {
connect()
room.onUpdateParticipants(SignalClientTest.PARTICIPANT_JOIN.update.participantsList)
room.onUpdateParticipants(TestData.PARTICIPANT_JOIN.update.participantsList)
room.onAddTrack(
MockRtpReceiver.create(),
MockAudioStreamTrack(),
... ... @@ -441,11 +435,11 @@ class RoomMockE2ETest : MockE2ETest() {
connect()
val eventCollector = EventCollector(room.events, coroutineRule.scope)
wsFactory.listener.onMessage(wsFactory.ws, SignalClientTest.LEAVE.toOkioByteString())
wsFactory.listener.onMessage(wsFactory.ws, TestData.LEAVE.toOkioByteString())
val events = eventCollector.stopCollecting()
assertEquals(1, events.size)
assertEquals(true, events[0] is RoomEvent.Disconnected)
assertEquals(SignalClientTest.LEAVE.leave.reason.convert(), (events[0] as RoomEvent.Disconnected).reason)
assertEquals(TestData.LEAVE.leave.reason.convert(), (events[0] as RoomEvent.Disconnected).reason)
}
@Test
... ...
... ... @@ -16,10 +16,11 @@
package io.livekit.android.room
import io.livekit.android.MockE2ETest
import io.livekit.android.mock.MockAudioStreamTrack
import io.livekit.android.room.track.LocalAudioTrack
import io.livekit.android.util.toPBByteString
import io.livekit.android.test.MockE2ETest
import io.livekit.android.test.mock.MockAudioStreamTrack
import io.livekit.android.test.mock.TestData
import io.livekit.android.test.util.toPBByteString
import kotlinx.coroutines.ExperimentalCoroutinesApi
import livekit.LivekitRtc
import livekit.org.webrtc.PeerConnection
... ... @@ -46,9 +47,9 @@ class RoomReconnectionMockE2ETest : MockE2ETest() {
?: 0
if (softReconnectParam == 0) {
simulateMessageFromServer(SignalClientTest.JOIN)
simulateMessageFromServer(TestData.JOIN)
} else {
simulateMessageFromServer(SignalClientTest.RECONNECT)
simulateMessageFromServer(TestData.RECONNECT)
}
}
... ... @@ -89,7 +90,7 @@ class RoomReconnectionMockE2ETest : MockE2ETest() {
val rtcConfig = getSubscriberPeerConnection().rtcConfig
assertEquals(PeerConnection.IceTransportsType.RELAY, rtcConfig.iceTransportsType)
val sentIceServers = SignalClientTest.RECONNECT.reconnect.iceServersList
val sentIceServers = TestData.RECONNECT.reconnect.iceServersList
.map { server -> server.toWebrtc() }
assertEquals(sentIceServers, rtcConfig.iceServers)
}
... ... @@ -103,7 +104,7 @@ class RoomReconnectionMockE2ETest : MockE2ETest() {
room.localParticipant.publishAudioTrack(
LocalAudioTrack(
"",
MockAudioStreamTrack(id = SignalClientTest.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
),
)
... ...
... ... @@ -16,11 +16,12 @@
package io.livekit.android.room
import io.livekit.android.MockE2ETest
import io.livekit.android.assert.assertIsClassList
import io.livekit.android.events.EventCollector
import io.livekit.android.events.FlowCollector
import io.livekit.android.events.RoomEvent
import io.livekit.android.test.MockE2ETest
import io.livekit.android.test.assert.assertIsClassList
import io.livekit.android.test.events.EventCollector
import io.livekit.android.test.events.FlowCollector
import io.livekit.android.test.mock.TestData
import io.livekit.android.util.flow
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
... ... @@ -53,9 +54,9 @@ class RoomReconnectionTypesMockE2ETest(
?: 0
if (softReconnectParam == 0) {
simulateMessageFromServer(SignalClientTest.JOIN)
simulateMessageFromServer(TestData.JOIN)
} else {
simulateMessageFromServer(SignalClientTest.RECONNECT)
simulateMessageFromServer(TestData.RECONNECT)
}
}
... ...
... ... @@ -17,19 +17,24 @@
package io.livekit.android.room
import android.content.Context
import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback
import android.net.Network
import androidx.test.core.app.ApplicationProvider
import androidx.test.platform.app.InstrumentationRegistry
import io.livekit.android.assert.assertIsClassList
import io.livekit.android.audio.NoAudioHandler
import io.livekit.android.audio.NoopCommunicationWorkaround
import io.livekit.android.coroutines.TestCoroutineRule
import io.livekit.android.e2ee.E2EEManager
import io.livekit.android.events.*
import io.livekit.android.memory.CloseableManager
import io.livekit.android.mock.*
import io.livekit.android.room.network.NetworkCallbackManagerImpl
import io.livekit.android.room.participant.LocalParticipant
import io.livekit.android.test.assert.assertIsClassList
import io.livekit.android.test.coroutines.TestCoroutineRule
import io.livekit.android.test.events.EventCollector
import io.livekit.android.test.mock.MockAudioProcessingController
import io.livekit.android.test.mock.MockEglBase
import io.livekit.android.test.mock.MockLKObjects
import io.livekit.android.test.mock.MockNetworkCallbackRegistry
import io.livekit.android.test.mock.TestData
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableSharedFlow
... ... @@ -48,8 +53,6 @@ import org.mockito.Mockito
import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.*
import org.robolectric.RobolectricTestRunner
import org.robolectric.Shadows
import org.robolectric.shadows.ShadowConnectivityManager
@ExperimentalCoroutinesApi
@RunWith(RobolectricTestRunner::class)
... ... @@ -69,6 +72,8 @@ class RoomTest {
@Mock
lateinit var e2EEManagerFactory: E2EEManager.Factory
lateinit var networkCallbackRegistry: MockNetworkCallbackRegistry
var eglBase: EglBase = MockEglBase()
val localParticipantFactory = object : LocalParticipant.Factory {
... ... @@ -89,6 +94,7 @@ class RoomTest {
@Before
fun setup() {
context = ApplicationProvider.getApplicationContext()
networkCallbackRegistry = MockNetworkCallbackRegistry()
room = Room(
context = context,
engine = rtcEngine,
... ... @@ -103,10 +109,13 @@ class RoomTest {
communicationWorkaround = NoopCommunicationWorkaround(),
audioProcessingController = MockAudioProcessingController(),
lkObjects = MockLKObjects.get(),
networkCallbackManagerFactory = { networkCallback: NetworkCallback ->
NetworkCallbackManagerImpl(networkCallback, networkCallbackRegistry)
},
)
}
suspend fun connect(joinResponse: JoinResponse = SignalClientTest.JOIN.join) {
suspend fun connect(joinResponse: JoinResponse = TestData.JOIN.join) {
rtcEngine.stub {
onBlocking { rtcEngine.join(any(), any(), anyOrNull(), anyOrNull()) }
.doSuspendableAnswer {
... ... @@ -120,7 +129,7 @@ class RoomTest {
}
room.connect(
url = SignalClientTest.EXAMPLE_URL,
url = TestData.EXAMPLE_URL,
token = "",
)
}
... ... @@ -128,7 +137,7 @@ class RoomTest {
@Test
fun connectTest() = runTest {
connect()
val roomInfo = SignalClientTest.JOIN.join.room
val roomInfo = TestData.JOIN.join.room
assertEquals(roomInfo.name, room.name)
assertEquals(Room.Sid(roomInfo.sid), room.sid)
... ... @@ -139,7 +148,7 @@ class RoomTest {
@Test
fun roomUpdate() = runTest {
connect()
val update = SignalClientTest.ROOM_UPDATE.roomUpdate.room
val update = TestData.ROOM_UPDATE.roomUpdate.room
val eventCollector = EventCollector(room.events, coroutineRule.scope)
room.onRoomUpdate(update)
... ... @@ -164,15 +173,10 @@ class RoomTest {
val network = Mockito.mock(Network::class.java)
val connectivityManager = InstrumentationRegistry.getInstrumentation()
.context
.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val shadowConnectivityManager: ShadowConnectivityManager = Shadows.shadowOf(connectivityManager)
shadowConnectivityManager.networkCallbacks.forEach { callback ->
networkCallbackRegistry.networkCallbacks.forEach { callback ->
callback.onLost(network)
}
shadowConnectivityManager.networkCallbacks.forEach { callback ->
networkCallbackRegistry.networkCallbacks.forEach { callback ->
callback.onAvailable(network)
}
... ... @@ -227,7 +231,7 @@ class RoomTest {
assertFalse(job.isCompleted)
connect()
assertFalse(job.isCompleted)
val update = SignalClientTest.ROOM_UPDATE.roomUpdate.room
val update = TestData.ROOM_UPDATE.roomUpdate.room
room.onRoomUpdate(update)
advanceUntilIdle()
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ...
... ... @@ -16,20 +16,22 @@
package io.livekit.android.room
import io.livekit.android.BaseTest
import io.livekit.android.mock.MockWebSocketFactory
import io.livekit.android.mock.TestData
import io.livekit.android.stats.NetworkInfo
import io.livekit.android.stats.NetworkType
import io.livekit.android.test.BaseTest
import io.livekit.android.test.mock.MockWebSocketFactory
import io.livekit.android.test.mock.TestData.EXAMPLE_URL
import io.livekit.android.test.mock.TestData.JOIN
import io.livekit.android.test.mock.TestData.OFFER
import io.livekit.android.test.mock.TestData.PONG
import io.livekit.android.test.mock.TestData.RECONNECT
import io.livekit.android.test.mock.TestData.ROOM_UPDATE
import io.livekit.android.test.util.toPBByteString
import io.livekit.android.util.toOkioByteString
import io.livekit.android.util.toPBByteString
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.serialization.json.Json
import livekit.LivekitModels
import livekit.LivekitModels.ClientConfiguration
import livekit.LivekitRtc
import livekit.LivekitRtc.ICEServer
import livekit.org.webrtc.SessionDescription
import okhttp3.OkHttpClient
import okhttp3.Protocol
... ... @@ -43,9 +45,9 @@ import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.inOrder
import org.mockito.kotlin.any
import org.mockito.kotlin.argThat
import org.mockito.kotlin.inOrder
import org.mockito.kotlin.times
@ExperimentalCoroutinesApi
... ... @@ -329,218 +331,5 @@ class SignalClientTest : BaseTest() {
}
// mock data
companion object {
const val EXAMPLE_URL = "ws://www.example.com"
val JOIN = with(LivekitRtc.SignalResponse.newBuilder()) {
join = with(LivekitRtc.JoinResponse.newBuilder()) {
room = with(LivekitModels.Room.newBuilder()) {
name = "roomname"
build()
}
participant = TestData.LOCAL_PARTICIPANT
subscriberPrimary = true
addIceServers(
with(ICEServer.newBuilder()) {
addUrls("stun:stun.join.com:19302")
username = "username"
credential = "credential"
build()
},
)
serverVersion = "0.15.2"
build()
}
build()
}
val RECONNECT = with(LivekitRtc.SignalResponse.newBuilder()) {
reconnect = with(LivekitRtc.ReconnectResponse.newBuilder()) {
addIceServers(
with(ICEServer.newBuilder()) {
addUrls("stun:stun.reconnect.com:19302")
username = "username"
credential = "credential"
build()
},
)
clientConfiguration = with(ClientConfiguration.newBuilder()) {
forceRelay = LivekitModels.ClientConfigSetting.ENABLED
build()
}
build()
}
build()
}
val OFFER = with(LivekitRtc.SignalResponse.newBuilder()) {
offer = with(LivekitRtc.SessionDescription.newBuilder()) {
sdp = "remote_offer"
type = "offer"
build()
}
build()
}
val ROOM_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {
roomUpdate = with(LivekitRtc.RoomUpdate.newBuilder()) {
room = with(LivekitModels.Room.newBuilder()) {
sid = "room_sid"
metadata = "metadata"
activeRecording = true
build()
}
build()
}
build()
}
val LOCAL_TRACK_PUBLISHED = with(LivekitRtc.SignalResponse.newBuilder()) {
trackPublished = with(LivekitRtc.TrackPublishedResponse.newBuilder()) {
cid = "local_cid"
track = TestData.LOCAL_AUDIO_TRACK
build()
}
build()
}
val LOCAL_TRACK_UNPUBLISHED = with(LivekitRtc.SignalResponse.newBuilder()) {
trackUnpublished = with(LivekitRtc.TrackUnpublishedResponse.newBuilder()) {
trackSid = TestData.LOCAL_AUDIO_TRACK.sid
build()
}
build()
}
val PERMISSION_CHANGE = with(LivekitRtc.SignalResponse.newBuilder()) {
update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
addParticipants(
TestData.LOCAL_PARTICIPANT.toBuilder()
.setPermission(
LivekitModels.ParticipantPermission.newBuilder()
.setCanPublish(false)
.setCanSubscribe(false)
.setCanPublishData(false)
.setHidden(false)
.setRecorder(false)
.build(),
)
.build(),
)
build()
}
build()
}
val PARTICIPANT_JOIN = with(LivekitRtc.SignalResponse.newBuilder()) {
update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
addParticipants(TestData.REMOTE_PARTICIPANT)
build()
}
build()
}
val PARTICIPANT_DISCONNECT = with(LivekitRtc.SignalResponse.newBuilder()) {
update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
val disconnectedParticipant = TestData.REMOTE_PARTICIPANT.toBuilder()
.setState(LivekitModels.ParticipantInfo.State.DISCONNECTED)
.build()
addParticipants(disconnectedParticipant)
build()
}
build()
}
val ACTIVE_SPEAKER_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {
speakersChanged = with(LivekitRtc.SpeakersChanged.newBuilder()) {
addSpeakers(TestData.REMOTE_SPEAKER_INFO)
build()
}
build()
}
val LOCAL_PARTICIPANT_METADATA_CHANGED = with(LivekitRtc.SignalResponse.newBuilder()) {
update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
val participantMetadataChanged = TestData.LOCAL_PARTICIPANT.toBuilder()
.setMetadata("changed_metadata")
.setName("changed_name")
.build()
addParticipants(participantMetadataChanged)
build()
}
build()
}
val REMOTE_PARTICIPANT_METADATA_CHANGED = with(LivekitRtc.SignalResponse.newBuilder()) {
update = with(LivekitRtc.ParticipantUpdate.newBuilder()) {
val participantMetadataChanged = TestData.REMOTE_PARTICIPANT.toBuilder()
.setMetadata("changed_metadata")
.setName("changed_name")
.build()
addParticipants(participantMetadataChanged)
build()
}
build()
}
val CONNECTION_QUALITY = with(LivekitRtc.SignalResponse.newBuilder()) {
connectionQuality = with(LivekitRtc.ConnectionQualityUpdate.newBuilder()) {
addUpdates(
with(LivekitRtc.ConnectionQualityInfo.newBuilder()) {
participantSid = JOIN.join.participant.sid
quality = LivekitModels.ConnectionQuality.EXCELLENT
build()
},
)
build()
}
build()
}
val STREAM_STATE_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {
streamStateUpdate = with(LivekitRtc.StreamStateUpdate.newBuilder()) {
addStreamStates(
with(LivekitRtc.StreamStateInfo.newBuilder()) {
participantSid = TestData.REMOTE_PARTICIPANT.sid
trackSid = TestData.REMOTE_AUDIO_TRACK.sid
state = LivekitRtc.StreamState.ACTIVE
build()
},
)
build()
}
build()
}
val SUBSCRIPTION_PERMISSION_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) {
subscriptionPermissionUpdate = with(LivekitRtc.SubscriptionPermissionUpdate.newBuilder()) {
participantSid = TestData.REMOTE_PARTICIPANT.sid
trackSid = TestData.REMOTE_AUDIO_TRACK.sid
allowed = false
build()
}
build()
}
val REFRESH_TOKEN = with(LivekitRtc.SignalResponse.newBuilder()) {
refreshToken = "refresh_token"
build()
}
val PONG = with(LivekitRtc.SignalResponse.newBuilder()) {
pong = 1L
build()
}
val LEAVE = with(LivekitRtc.SignalResponse.newBuilder()) {
leave = with(LivekitRtc.LeaveRequest.newBuilder()) {
reason = LivekitModels.DisconnectReason.SERVER_SHUTDOWN
build()
}
build()
}
}
companion object
}
... ...
... ... @@ -16,25 +16,25 @@
package io.livekit.android.room.participant
import io.livekit.android.MockE2ETest
import io.livekit.android.assert.assertIsClassList
import io.livekit.android.events.EventCollector
import io.livekit.android.events.ParticipantEvent
import io.livekit.android.events.RoomEvent
import io.livekit.android.mock.MockAudioStreamTrack
import io.livekit.android.mock.MockEglBase
import io.livekit.android.mock.MockVideoCapturer
import io.livekit.android.mock.MockVideoStreamTrack
import io.livekit.android.room.DefaultsManager
import io.livekit.android.room.SignalClientTest
import io.livekit.android.room.track.LocalAudioTrack
import io.livekit.android.room.track.LocalVideoTrack
import io.livekit.android.room.track.LocalVideoTrackOptions
import io.livekit.android.room.track.Track
import io.livekit.android.room.track.VideoCaptureParameter
import io.livekit.android.room.track.VideoCodec
import io.livekit.android.test.MockE2ETest
import io.livekit.android.test.assert.assertIsClassList
import io.livekit.android.test.events.EventCollector
import io.livekit.android.test.mock.MockAudioStreamTrack
import io.livekit.android.test.mock.MockEglBase
import io.livekit.android.test.mock.MockVideoCapturer
import io.livekit.android.test.mock.MockVideoStreamTrack
import io.livekit.android.test.mock.TestData
import io.livekit.android.test.util.toPBByteString
import io.livekit.android.util.toOkioByteString
import io.livekit.android.util.toPBByteString
import kotlinx.coroutines.ExperimentalCoroutinesApi
import livekit.LivekitModels
import livekit.LivekitRtc
... ... @@ -60,7 +60,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() {
room.localParticipant.publishAudioTrack(
LocalAudioTrack(
"",
MockAudioStreamTrack(id = SignalClientTest.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
),
)
... ... @@ -147,14 +147,14 @@ class LocalParticipantMockE2ETest : MockE2ETest() {
wsFactory.listener.onMessage(
wsFactory.ws,
SignalClientTest.LOCAL_PARTICIPANT_METADATA_CHANGED.toOkioByteString(),
TestData.LOCAL_PARTICIPANT_METADATA_CHANGED.toOkioByteString(),
)
val roomEvents = roomEventsCollector.stopCollecting()
val participantEvents = participantEventsCollector.stopCollecting()
val localParticipant = room.localParticipant
val updateData = SignalClientTest.REMOTE_PARTICIPANT_METADATA_CHANGED.update.getParticipants(0)
val updateData = TestData.REMOTE_PARTICIPANT_METADATA_CHANGED.update.getParticipants(0)
assertEquals(updateData.metadata, localParticipant.metadata)
assertEquals(updateData.name, localParticipant.name)
... ...
... ... @@ -16,14 +16,15 @@
package io.livekit.android.room.participant
import io.livekit.android.MockE2ETest
import io.livekit.android.assert.assertIsClassList
import io.livekit.android.events.EventCollector
import io.livekit.android.events.ParticipantEvent
import io.livekit.android.events.RoomEvent
import io.livekit.android.mock.MockAudioStreamTrack
import io.livekit.android.room.SignalClientTest
import io.livekit.android.room.track.LocalAudioTrack
import io.livekit.android.test.MockE2ETest
import io.livekit.android.test.assert.assertIsClassList
import io.livekit.android.test.events.EventCollector
import io.livekit.android.test.mock.MockAudioStreamTrack
import io.livekit.android.test.mock.TestData
import io.livekit.android.test.util.toOkioByteString
import io.livekit.android.util.toOkioByteString
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Assert.assertEquals
... ... @@ -43,13 +44,13 @@ class ParticipantMockE2ETest : MockE2ETest() {
room.localParticipant.publishAudioTrack(
LocalAudioTrack(
"",
MockAudioStreamTrack(id = SignalClientTest.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid),
),
)
val eventCollector = EventCollector(room.events, coroutineRule.scope)
// remote unpublish
simulateMessageFromServer(SignalClientTest.LOCAL_TRACK_UNPUBLISHED)
simulateMessageFromServer(TestData.LOCAL_TRACK_UNPUBLISHED)
val events = eventCollector.stopCollecting()
assertEquals(1, events.size)
... ... @@ -62,7 +63,7 @@ class ParticipantMockE2ETest : MockE2ETest() {
connect()
val eventCollector = EventCollector(room.events, coroutineRule.scope)
simulateMessageFromServer(SignalClientTest.PERMISSION_CHANGE)
simulateMessageFromServer(TestData.PERMISSION_CHANGE)
val events = eventCollector.stopCollecting()
assertEquals(1, events.size)
... ... @@ -75,7 +76,7 @@ class ParticipantMockE2ETest : MockE2ETest() {
wsFactory.listener.onMessage(
wsFactory.ws,
SignalClientTest.PARTICIPANT_JOIN.toOkioByteString(),
TestData.PARTICIPANT_JOIN.toOkioByteString(),
)
val remoteParticipant = room.remoteParticipants.values.first()
... ... @@ -83,12 +84,12 @@ class ParticipantMockE2ETest : MockE2ETest() {
val participantEventsCollector = EventCollector(remoteParticipant.events, coroutineRule.scope)
wsFactory.listener.onMessage(
wsFactory.ws,
SignalClientTest.REMOTE_PARTICIPANT_METADATA_CHANGED.toOkioByteString(),
TestData.REMOTE_PARTICIPANT_METADATA_CHANGED.toOkioByteString(),
)
val roomEvents = roomEventsCollector.stopCollecting()
val participantEvents = participantEventsCollector.stopCollecting()
val updateData = SignalClientTest.REMOTE_PARTICIPANT_METADATA_CHANGED.update.getParticipants(0)
val updateData = TestData.REMOTE_PARTICIPANT_METADATA_CHANGED.update.getParticipants(0)
assertEquals(updateData.metadata, remoteParticipant.metadata)
assertEquals(updateData.name, remoteParticipant.name)
... ...
... ... @@ -16,10 +16,10 @@
package io.livekit.android.room.participant
import io.livekit.android.coroutines.TestCoroutineRule
import io.livekit.android.events.EventCollector
import io.livekit.android.events.ParticipantEvent
import io.livekit.android.room.track.TrackPublication
import io.livekit.android.test.coroutines.TestCoroutineRule
import io.livekit.android.test.events.EventCollector
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import livekit.LivekitModels
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ...
... ... @@ -16,12 +16,12 @@
package io.livekit.android.room.participant
import io.livekit.android.BaseTest
import io.livekit.android.events.EventCollector
import io.livekit.android.events.FlowCollector
import io.livekit.android.events.ParticipantEvent
import io.livekit.android.room.SignalClient
import io.livekit.android.room.track.TrackPublication
import io.livekit.android.test.BaseTest
import io.livekit.android.test.events.EventCollector
import io.livekit.android.test.events.FlowCollector
import io.livekit.android.util.flow
import kotlinx.coroutines.ExperimentalCoroutinesApi
import livekit.LivekitModels
... ...
... ... @@ -16,11 +16,14 @@
package io.livekit.android.room.track
import io.livekit.android.MockE2ETest
import io.livekit.android.mock.*
import io.livekit.android.room.SignalClientTest
import io.livekit.android.test.MockE2ETest
import io.livekit.android.test.mock.MockMediaStream
import io.livekit.android.test.mock.MockRtpReceiver
import io.livekit.android.test.mock.MockVideoStreamTrack
import io.livekit.android.test.mock.TestData
import io.livekit.android.test.mock.createMediaStreamId
import io.livekit.android.test.util.toPBByteString
import io.livekit.android.util.toOkioByteString
import io.livekit.android.util.toPBByteString
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceUntilIdle
import livekit.LivekitModels.VideoQuality
... ... @@ -43,7 +46,7 @@ class RemoteTrackPublicationTest : MockE2ETest() {
wsFactory.listener.onMessage(
wsFactory.ws,
SignalClientTest.PARTICIPANT_JOIN.toOkioByteString(),
TestData.PARTICIPANT_JOIN.toOkioByteString(),
)
room.onAddTrack(
... ...
... ... @@ -16,12 +16,12 @@
package io.livekit.android.room.track
import io.livekit.android.BaseTest
import io.livekit.android.events.EventCollector
import io.livekit.android.events.TrackEvent
import io.livekit.android.mock.MockRtpReceiver
import io.livekit.android.mock.MockVideoStreamTrack
import io.livekit.android.room.track.video.VideoSinkVisibility
import io.livekit.android.test.BaseTest
import io.livekit.android.test.events.EventCollector
import io.livekit.android.test.mock.MockRtpReceiver
import io.livekit.android.test.mock.MockVideoStreamTrack
import kotlinx.coroutines.ExperimentalCoroutinesApi
import livekit.org.webrtc.VideoFrame
import livekit.org.webrtc.VideoSink
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ...
/*
* Copyright 2023 LiveKit, Inc.
* Copyright 2023-2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
... ...
... ... @@ -16,7 +16,7 @@
package io.livekit.android.webrtc
import io.livekit.android.BaseTest
import io.livekit.android.test.BaseTest
import livekit.org.webrtc.PeerConnection.RTCConfiguration
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
... ... @@ -25,6 +25,9 @@ import org.mockito.Mockito
class RTCConfigurationTest : BaseTest() {
/**
* Make sure that copy handles every declared field.
*/
@Test
fun copyTest() {
val originalConfig = RTCConfiguration(mutableListOf())
... ... @@ -40,7 +43,9 @@ class RTCConfigurationTest : BaseTest() {
}
}
// Test to make sure the copy test is actually checking properly
/**
* Make sure the copy test is actually checking properly
*/
@Test
fun copyFailureCheckTest() {
val originalConfig = RTCConfiguration(mutableListOf())
... ...
... ... @@ -11,3 +11,4 @@ include ':video-encode-decode-test'
include ':sample-app-basic'
include ':sample-app-record-local'
include ':examples:selfie-segmentation'
include ':livekit-android-test'
... ...