@@ -19,18 +19,21 @@ package com.google.firebase.ai.type
1919import android.Manifest
2020import android.content.Context
2121import android.content.pm.PackageManager
22+ import android.os.Build
2223import com.google.firebase.ai.common.PermissionMissingException
2324import io.ktor.client.plugins.websocket.ClientWebSocketSession
2425import kotlin.coroutines.CoroutineContext
2526import kotlinx.coroutines.ExperimentalCoroutinesApi
2627import kotlinx.coroutines.test.UnconfinedTestDispatcher
2728import kotlinx.coroutines.test.runTest
29+ import org.junit.After
2830import org.junit.Assert.assertEquals
2931import org.junit.Assert.assertThrows
3032import org.junit.Before
3133import org.junit.Test
3234import org.junit.runner.RunWith
3335import org.mockito.Mock
36+ import org.mockito.MockedStatic
3437import org.mockito.Mockito.mockStatic
3538import org.mockito.Mockito.`when`
3639import org.mockito.junit.MockitoJUnitRunner
@@ -44,25 +47,39 @@ class LiveSessionTest {
4447 @Mock private lateinit var mockSession: ClientWebSocketSession
4548 @Mock private lateinit var mockAudioHelper: AudioHelper
4649
50+ private lateinit var mockedBuildVersion: MockedStatic <Build .VERSION >
4751 private lateinit var testDispatcher: CoroutineContext
4852 private lateinit var liveSession: LiveSession
4953
5054 @Before
5155 fun setUp () {
5256 testDispatcher = UnconfinedTestDispatcher ()
5357 `when `(mockContext.packageManager).thenReturn(mockPackageManager)
58+ mockedBuildVersion = mockStatic(Build .VERSION ::class .java)
5459
5560 // Mock AudioHelper.build() to return our mockAudioHelper
5661 // Need to use mockStatic for static methods
57- mockStatic(AudioHelper ::class .java).use { mockedAudioHelper ->
58- mockedAudioHelper.`when `<AudioHelper > { AudioHelper .build() }.thenReturn(mockAudioHelper)
62+ // Note: It's generally better to manage static mocks with try-with-resources or @ExtendWith if
63+ // the runner supports it well, but for this structure, @Before/@After is common.
64+ // AudioHelper static mock is managed with try-with-resources where it's used for instance
65+ // creation.
66+ mockStatic(AudioHelper ::class .java).use { mockedAudioHelperStatic ->
67+ mockedAudioHelperStatic
68+ .`when `<AudioHelper > { AudioHelper .build() }
69+ .thenReturn(mockAudioHelper)
5970 liveSession = LiveSession (mockContext, mockSession, testDispatcher, null )
6071 }
6172 }
6273
74+ @After
75+ fun tearDown () {
76+ mockedBuildVersion.close()
77+ }
78+
6379 @Test
64- fun `startAudioConversation with RECORD_AUDIO permission proceeds normally` () = runTest {
80+ fun `startAudioConversation on API M+ with permission proceeds normally` () = runTest {
6581 // Arrange
82+ mockedBuildVersion.`when ` { Build .VERSION .SDK_INT }.thenReturn(Build .VERSION_CODES .M )
6683 `when `(mockContext.checkSelfPermission(Manifest .permission.RECORD_AUDIO ))
6784 .thenReturn(PackageManager .PERMISSION_GRANTED )
6885
@@ -72,9 +89,10 @@ class LiveSessionTest {
7289 }
7390
7491 @Test
75- fun `startAudioConversation without RECORD_AUDIO permission throws PermissionMissingException` () =
92+ fun `startAudioConversation on API M+ without permission throws PermissionMissingException` () =
7693 runTest {
7794 // Arrange
95+ mockedBuildVersion.`when ` { Build .VERSION .SDK_INT }.thenReturn(Build .VERSION_CODES .M )
7896 `when `(mockContext.checkSelfPermission(Manifest .permission.RECORD_AUDIO ))
7997 .thenReturn(PackageManager .PERMISSION_DENIED )
8098
@@ -85,4 +103,28 @@ class LiveSessionTest {
85103 }
86104 assertEquals(" Missing RECORD_AUDIO" , exception.message)
87105 }
106+
107+ @Test
108+ fun `startAudioConversation on API Pre-M with denied permission proceeds normally` () = runTest {
109+ // Arrange
110+ mockedBuildVersion.`when ` { Build .VERSION .SDK_INT }.thenReturn(Build .VERSION_CODES .LOLLIPOP )
111+ `when `(mockContext.checkSelfPermission(Manifest .permission.RECORD_AUDIO ))
112+ .thenReturn(PackageManager .PERMISSION_DENIED ) // This shouldn't be checked
113+
114+ // Act & Assert
115+ // No exception should be thrown
116+ liveSession.startAudioConversation()
117+ }
118+
119+ @Test
120+ fun `startAudioConversation on API Pre-M with granted permission proceeds normally` () = runTest {
121+ // Arrange
122+ mockedBuildVersion.`when ` { Build .VERSION .SDK_INT }.thenReturn(Build .VERSION_CODES .LOLLIPOP )
123+ `when `(mockContext.checkSelfPermission(Manifest .permission.RECORD_AUDIO ))
124+ .thenReturn(PackageManager .PERMISSION_GRANTED ) // This shouldn't be checked
125+
126+ // Act & Assert
127+ // No exception should be thrown
128+ liveSession.startAudioConversation()
129+ }
88130}
0 commit comments