Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2765782f authored by Anton Potapov's avatar Anton Potapov
Browse files

Media output domain layer tests

Flag: aconfig new_volume_panel TEAMFOOD
Test: atest MediaControllerRepositoryImplTest LocalMediaRepositoryImplTest
Test: atest SpatialAudioComponentInteractorTest SpatialAudioAvailabilityCriteriaTest MediaOutputViewModelTest MediaDeviceSessionInteractorTest
Fixes: 329641812
Fixes: 329561499
Change-Id: I2dc05cb5ffe7c97673c705ba27ac41a211102a04
parent cac75836
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.launch

/** [Flow] for [MediaSessionManager.OnActiveSessionsChangedListener]. */
val MediaSessionManager.activeMediaChanges: Flow<Collection<MediaController>?>
val MediaSessionManager.activeMediaChanges: Flow<List<MediaController>?>
    get() =
        callbackFlow {
                val listener =
+14 −11
Original line number Diff line number Diff line
@@ -39,8 +39,14 @@ import kotlinx.coroutines.flow.stateIn
/** Provides controllers for currently active device media sessions. */
interface MediaControllerRepository {

    /** Current [MediaController]. Null is emitted when there is no active [MediaController]. */
    val activeMediaControllers: StateFlow<Collection<MediaController>>
    /**
     * Get a list of controllers for all ongoing sessions. The controllers will be provided in
     * priority order with the most important controller at index 0.
     *
     * This requires the [android.Manifest.permission.MEDIA_CONTENT_CONTROL] permission be held by
     * the calling app.
     */
    val activeSessions: StateFlow<List<MediaController>>
}

class MediaControllerRepositoryImpl(
@@ -51,20 +57,17 @@ class MediaControllerRepositoryImpl(
    backgroundContext: CoroutineContext,
) : MediaControllerRepository {

    override val activeMediaControllers: StateFlow<Collection<MediaController>> =
    override val activeSessions: StateFlow<List<MediaController>> =
        merge(
                mediaSessionManager.activeMediaChanges
                    .onStart { emit(mediaSessionManager.getActiveSessions(null)) }
                    .filterNotNull(),
                localBluetoothManager
                    ?.headsetAudioModeChanges
                    ?.onStart { emit(Unit) }
                    ?.map { mediaSessionManager.getActiveSessions(null) } ?: emptyFlow(),
                mediaSessionManager.activeMediaChanges.filterNotNull(),
                localBluetoothManager?.headsetAudioModeChanges?.map {
                    mediaSessionManager.getActiveSessions(null)
                } ?: emptyFlow(),
                audioManagerEventsReceiver.events
                    .filterIsInstance(AudioManagerEvent.StreamDevicesChanged::class)
                    .onStart { emit(AudioManagerEvent.StreamDevicesChanged) }
                    .map { mediaSessionManager.getActiveSessions(null) },
            )
            .onStart { emit(mediaSessionManager.getActiveSessions(null)) }
            .flowOn(backgroundContext)
            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyList())
}
+0 −103
Original line number Diff line number Diff line
@@ -15,17 +15,12 @@
 */
package com.android.settingslib.volume.data.repository

import android.media.MediaRoute2Info
import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.model.RoutingSession
import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -37,15 +32,10 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyString
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
class LocalMediaRepositoryImplTest {
@@ -53,7 +43,6 @@ class LocalMediaRepositoryImplTest {
    @Mock private lateinit var localMediaManager: LocalMediaManager
    @Mock private lateinit var mediaDevice1: MediaDevice
    @Mock private lateinit var mediaDevice2: MediaDevice
    @Mock private lateinit var mediaRouter2Manager: MediaRouter2Manager

    @Captor
    private lateinit var deviceCallbackCaptor: ArgumentCaptor<LocalMediaManager.DeviceCallback>
@@ -71,28 +60,10 @@ class LocalMediaRepositoryImplTest {
            LocalMediaRepositoryImpl(
                eventsReceiver,
                localMediaManager,
                mediaRouter2Manager,
                testScope.backgroundScope,
                testScope.testScheduler,
            )
    }

    @Test
    fun mediaDevices_areUpdated() {
        testScope.runTest {
            var mediaDevices: Collection<MediaDevice>? = null
            underTest.mediaDevices.onEach { mediaDevices = it }.launchIn(backgroundScope)
            runCurrent()
            verify(localMediaManager).registerCallback(deviceCallbackCaptor.capture())
            deviceCallbackCaptor.value.onDeviceListUpdate(listOf(mediaDevice1, mediaDevice2))
            runCurrent()

            assertThat(mediaDevices).hasSize(2)
            assertThat(mediaDevices).contains(mediaDevice1)
            assertThat(mediaDevices).contains(mediaDevice2)
        }
    }

    @Test
    fun deviceListUpdated_currentConnectedDeviceUpdated() {
        testScope.runTest {
@@ -110,78 +81,4 @@ class LocalMediaRepositoryImplTest {
            assertThat(currentConnectedDevice).isEqualTo(mediaDevice1)
        }
    }

    @Test
    fun kek() {
        testScope.runTest {
            `when`(localMediaManager.remoteRoutingSessions)
                .thenReturn(
                    listOf(
                        testRoutingSessionInfo1,
                        testRoutingSessionInfo2,
                        testRoutingSessionInfo3,
                    )
                )
            `when`(localMediaManager.shouldEnableVolumeSeekBar(any())).then {
                (it.arguments[0] as RoutingSessionInfo) == testRoutingSessionInfo1
            }
            `when`(mediaRouter2Manager.getTransferableRoutes(any<RoutingSessionInfo>())).then {
                if ((it.arguments[0] as RoutingSessionInfo) == testRoutingSessionInfo2) {
                    return@then listOf(mock(MediaRoute2Info::class.java))
                }
                emptyList<MediaRoute2Info>()
            }
            var remoteRoutingSessions: Collection<RoutingSession>? = null
            underTest.remoteRoutingSessions
                .onEach { remoteRoutingSessions = it }
                .launchIn(backgroundScope)

            runCurrent()

            assertThat(remoteRoutingSessions)
                .containsExactlyElementsIn(
                    listOf(
                        RoutingSession(
                            routingSessionInfo = testRoutingSessionInfo1,
                            isVolumeSeekBarEnabled = true,
                            isMediaOutputDisabled = true,
                        ),
                        RoutingSession(
                            routingSessionInfo = testRoutingSessionInfo2,
                            isVolumeSeekBarEnabled = false,
                            isMediaOutputDisabled = false,
                        ),
                        RoutingSession(
                            routingSessionInfo = testRoutingSessionInfo3,
                            isVolumeSeekBarEnabled = false,
                            isMediaOutputDisabled = true,
                        )
                    )
                )
        }
    }

    @Test
    fun adjustSessionVolume_adjusts() {
        testScope.runTest {
            var volume = 0
            `when`(localMediaManager.adjustSessionVolume(anyString(), anyInt())).then {
                volume = it.arguments[1] as Int
                Unit
            }

            underTest.adjustSessionVolume("test_session", 10)

            assertThat(volume).isEqualTo(10)
        }
    }

    private companion object {
        val testRoutingSessionInfo1 =
            RoutingSessionInfo.Builder("id_1", "test.pkg.1").addSelectedRoute("route_1").build()
        val testRoutingSessionInfo2 =
            RoutingSessionInfo.Builder("id_2", "test.pkg.2").addSelectedRoute("route_2").build()
        val testRoutingSessionInfo3 =
            RoutingSessionInfo.Builder("id_3", "test.pkg.3").addSelectedRoute("route_3").build()
    }
}
+11 −48
Original line number Diff line number Diff line
@@ -22,13 +22,10 @@ import android.media.session.MediaSessionManager
import android.media.session.PlaybackState
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.BluetoothCallback
import com.android.settingslib.bluetooth.BluetoothEventManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -37,21 +34,15 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
class MediaControllerRepositoryImplTest {

    @Captor private lateinit var callbackCaptor: ArgumentCaptor<BluetoothCallback>

    @Mock private lateinit var mediaSessionManager: MediaSessionManager
    @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
    @Mock private lateinit var eventManager: BluetoothEventManager
@@ -103,7 +94,7 @@ class MediaControllerRepositoryImplTest {
    }

    @Test
    fun playingMediaDevicesAvailable_sessionIsActive() {
    fun mediaDevicesAvailable_returnsAllActiveOnes() {
        testScope.runTest {
            `when`(mediaSessionManager.getActiveSessions(any()))
                .thenReturn(
@@ -112,53 +103,25 @@ class MediaControllerRepositoryImplTest {
                        statelessMediaController,
                        errorMediaController,
                        remoteMediaController,
                        localMediaController
                        localMediaController,
                    )
                )
            var mediaController: MediaController? = null
            underTest.activeMediaController
                .onEach { mediaController = it }
                .launchIn(backgroundScope)
            runCurrent()

            eventsReceiver.triggerEvent(AudioManagerEvent.StreamDevicesChanged)
            triggerOnAudioModeChanged()
            var mediaControllers: Collection<MediaController>? = null
            underTest.activeSessions.onEach { mediaControllers = it }.launchIn(backgroundScope)
            runCurrent()

            assertThat(mediaController).isSameInstanceAs(localMediaController)
        }
    }

    @Test
    fun noPlayingMediaDevicesAvailable_sessionIsInactive() {
        testScope.runTest {
            `when`(mediaSessionManager.getActiveSessions(any()))
                .thenReturn(
                    listOf(
            assertThat(mediaControllers)
                .containsExactly(
                    stoppedMediaController,
                    statelessMediaController,
                    errorMediaController,
                    remoteMediaController,
                    localMediaController,
                )
                )
            var mediaController: MediaController? = null
            underTest.activeMediaController
                .onEach { mediaController = it }
                .launchIn(backgroundScope)
            runCurrent()

            eventsReceiver.triggerEvent(AudioManagerEvent.StreamDevicesChanged)
            triggerOnAudioModeChanged()
            runCurrent()

            assertThat(mediaController).isNull()
        }
    }

    private fun triggerOnAudioModeChanged() {
        verify(eventManager).registerCallback(callbackCaptor.capture())
        callbackCaptor.value.onAudioModeChanged()
    }

    private companion object {
        val statePlaying: PlaybackState =
            PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0, 0f).build()
+93 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor

import android.os.Handler
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.android.systemui.volume.localMediaController
import com.android.systemui.volume.mediaControllerRepository
import com.android.systemui.volume.mediaOutputInteractor
import com.android.systemui.volume.remoteMediaController
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class MediaDeviceSessionInteractorTest : SysuiTestCase() {

    private val kosmos = testKosmos()

    private lateinit var underTest: MediaDeviceSessionInteractor

    @Before
    fun setup() {
        with(kosmos) {
            mediaControllerRepository.setActiveSessions(
                listOf(localMediaController, remoteMediaController)
            )

            underTest =
                MediaDeviceSessionInteractor(
                    testScope.testScheduler,
                    Handler(TestableLooper.get(kosmos.testCase).looper),
                    mediaControllerRepository,
                )
        }
    }

    @Test
    fun playbackInfo_returnsPlaybackInfo() {
        with(kosmos) {
            testScope.runTest {
                val session by collectLastValue(mediaOutputInteractor.defaultActiveMediaSession)
                runCurrent()
                val info by collectLastValue(underTest.playbackInfo(session!!))
                runCurrent()

                assertThat(info).isEqualTo(localMediaController.playbackInfo)
            }
        }
    }

    @Test
    fun playbackState_returnsPlaybackState() {
        with(kosmos) {
            testScope.runTest {
                val session by collectLastValue(mediaOutputInteractor.defaultActiveMediaSession)
                runCurrent()
                val state by collectLastValue(underTest.playbackState(session!!))
                runCurrent()

                assertThat(state).isEqualTo(localMediaController.playbackState)
            }
        }
    }
}
Loading