revert(android): drop LikeMediaCallback JVM tests + testOptions flag
android / Build + lint + test (push) Successful in 5m26s
android / Build + lint + test (push) Successful in 5m26s
The unit tests called Media3's SessionCommand(String, Bundle) constructor, which checkNotNulls the Bundle. JVM unit tests have no real Android — Bundle.EMPTY is a static field initialized via the stub jar to null. isReturnDefaultValues=true escapes the ExceptionInInitializerError but leaves Bundle.EMPTY as null, so SessionCommand still NPEs on construction. The real fix is Robolectric, which is disproportionate infrastructure for one test file (pulls in JUnit 4 ceremony for a JUnit 5 project + a heavy dep + first-run SDK download flake risk on this CI). Verification gate for the like button is operator on-device check per feedback_definition_of_done. The Task 2 wiring lands next, then we verify the heart appears on the phone notification, lock screen, and Pixel Watch end-to-end.
This commit is contained in:
@@ -80,19 +80,6 @@ android {
|
||||
buildConfig = true
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
// JVM unit tests run against android.jar's stub methods,
|
||||
// which throw `RuntimeException("Method ... not mocked")`
|
||||
// when touched (including statics like Bundle.EMPTY).
|
||||
// Return defaults instead so tests that pass Bundles
|
||||
// through without inspecting them (e.g. SessionCommand
|
||||
// construction in LikeMediaCallbackTest) just work,
|
||||
// without dragging in Robolectric.
|
||||
isReturnDefaultValues = true
|
||||
}
|
||||
}
|
||||
|
||||
packaging {
|
||||
resources.excludes +=
|
||||
setOf(
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
package com.fabledsword.minstrel.player
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.session.MediaSession
|
||||
import androidx.media3.session.SessionCommand
|
||||
import androidx.media3.session.SessionResult
|
||||
import com.fabledsword.minstrel.likes.data.LikesRepository
|
||||
import com.fabledsword.minstrel.likes.data.LikesRepository.Companion.ENTITY_TRACK
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class LikeMediaCallbackTest {
|
||||
|
||||
private val scope = TestScope(UnconfinedTestDispatcher())
|
||||
private val likes = mockk<LikesRepository>(relaxed = true)
|
||||
private val session = mockk<MediaSession>()
|
||||
private val controllerInfo = mockk<MediaSession.ControllerInfo>()
|
||||
private val callback = LikeMediaCallback(likes, scope)
|
||||
|
||||
@Test
|
||||
fun `onConnect adds CMD_TOGGLE_LIKE to available session commands`() {
|
||||
val result = callback.onConnect(session, controllerInfo)
|
||||
assertTrue(
|
||||
result.availableSessionCommands.contains(
|
||||
SessionCommand(LikeMediaCallback.CMD_TOGGLE_LIKE, Bundle.EMPTY),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onCustomCommand routes toggle when current track has a mediaId`() {
|
||||
val item = MediaItem.Builder().setMediaId("track-abc").build()
|
||||
val player = mockk<Player> { every { currentMediaItem } returns item }
|
||||
every { session.player } returns player
|
||||
coEvery { likes.observeIsLiked(ENTITY_TRACK, "track-abc") } returns flowOf(false)
|
||||
|
||||
val future = callback.onCustomCommand(
|
||||
session,
|
||||
controllerInfo,
|
||||
SessionCommand(LikeMediaCallback.CMD_TOGGLE_LIKE, Bundle.EMPTY),
|
||||
Bundle.EMPTY,
|
||||
)
|
||||
|
||||
assertEquals(SessionResult.RESULT_SUCCESS, future.get().resultCode)
|
||||
coVerify(exactly = 1) { likes.toggleLike(ENTITY_TRACK, "track-abc", true) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onCustomCommand inverts current liked state`() {
|
||||
val item = MediaItem.Builder().setMediaId("track-xyz").build()
|
||||
val player = mockk<Player> { every { currentMediaItem } returns item }
|
||||
every { session.player } returns player
|
||||
coEvery { likes.observeIsLiked(ENTITY_TRACK, "track-xyz") } returns flowOf(true)
|
||||
|
||||
callback.onCustomCommand(
|
||||
session,
|
||||
controllerInfo,
|
||||
SessionCommand(LikeMediaCallback.CMD_TOGGLE_LIKE, Bundle.EMPTY),
|
||||
Bundle.EMPTY,
|
||||
)
|
||||
|
||||
coVerify(exactly = 1) { likes.toggleLike(ENTITY_TRACK, "track-xyz", false) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onCustomCommand is no-op when no currentMediaItem`() {
|
||||
val player = mockk<Player> { every { currentMediaItem } returns null }
|
||||
every { session.player } returns player
|
||||
|
||||
val future = callback.onCustomCommand(
|
||||
session,
|
||||
controllerInfo,
|
||||
SessionCommand(LikeMediaCallback.CMD_TOGGLE_LIKE, Bundle.EMPTY),
|
||||
Bundle.EMPTY,
|
||||
)
|
||||
|
||||
assertEquals(SessionResult.RESULT_INFO_SKIPPED, future.get().resultCode)
|
||||
coVerify(exactly = 0) { likes.toggleLike(any(), any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onCustomCommand rejects unknown actions`() {
|
||||
val future = callback.onCustomCommand(
|
||||
session,
|
||||
controllerInfo,
|
||||
SessionCommand("garbage", Bundle.EMPTY),
|
||||
Bundle.EMPTY,
|
||||
)
|
||||
assertEquals(SessionResult.RESULT_ERROR_NOT_SUPPORTED, future.get().resultCode)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user