diff --git a/radioplayer/build.gradle.kts b/radioplayer/build.gradle.kts index 1a87f8f..455cc6b 100644 --- a/radioplayer/build.gradle.kts +++ b/radioplayer/build.gradle.kts @@ -15,10 +15,9 @@ swiftklib { } kotlin { - androidTarget { + androidTarget {} + jvm() - } - val xcf = XCFramework() listOf( iosX64(), diff --git a/radioplayer/src/jvmMain/kotlin/dev/markturnip/radioplayer/PlatformMediaPlayer.jvm.kt b/radioplayer/src/jvmMain/kotlin/dev/markturnip/radioplayer/PlatformMediaPlayer.jvm.kt new file mode 100644 index 0000000..273f20e --- /dev/null +++ b/radioplayer/src/jvmMain/kotlin/dev/markturnip/radioplayer/PlatformMediaPlayer.jvm.kt @@ -0,0 +1,47 @@ +package dev.markturnip.radioplayer + +actual final class PlatformMediaPlayer actual constructor() { + + private val stateCallbacks = mutableListOf<(PlaybackState) -> Unit>() + private val progressCallbacks = mutableListOf<(Progress) -> Unit>() + + var currentState: PlaybackState = PlaybackState.STOPPED + private set + + actual fun playItem(mediaPlayerItem: MediaPlayerItem) { + updateState(PlaybackState.PLAYING) + } + + actual fun play() { + updateState(PlaybackState.PLAYING) + } + + actual fun pause() { + updateState(PlaybackState.PAUSED) + } + + actual fun stop() { + updateState(PlaybackState.STOPPED) + } + + actual fun skip(delta: Double) {} + + actual fun seekWithPosition(position: Double) {} + + actual fun subscribeState(callback: (PlaybackState) -> Unit) { + stateCallbacks += callback + } + + actual fun subscribeProgress(callback: (Progress) -> Unit) { + progressCallbacks += callback + } + + fun emitProgress(progress: Progress) { + progressCallbacks.forEach { it(progress) } + } + + private fun updateState(newState: PlaybackState) { + currentState = newState + stateCallbacks.forEach { it(newState) } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 55a8d81..70e14c3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,4 +18,5 @@ dependencyResolutionManagement { } rootProject.name = "radioplayer-kt" -include(":radioplayer") \ No newline at end of file +include(":radioplayer") +include(":tests") \ No newline at end of file diff --git a/tests/build.gradle.kts b/tests/build.gradle.kts new file mode 100644 index 0000000..9b12303 --- /dev/null +++ b/tests/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + alias(libs.plugins.kotlinMultiplatform) +} + +kotlin { + jvm() + + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64() + ) + + sourceSets { + commonTest.dependencies { + implementation(libs.kotlin.test) + implementation(projects.radioplayer) + } + } +} diff --git a/tests/src/commonTest/kotlin/dev/markturnip/radioplayer/MediaPlayerItemTest.kt b/tests/src/commonTest/kotlin/dev/markturnip/radioplayer/MediaPlayerItemTest.kt new file mode 100644 index 0000000..ab94cb4 --- /dev/null +++ b/tests/src/commonTest/kotlin/dev/markturnip/radioplayer/MediaPlayerItemTest.kt @@ -0,0 +1,53 @@ +package dev.markturnip.radioplayer + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNull +import kotlin.test.assertTrue + +class MediaPlayerItemTest { + + private class TestMediaPlayerItem( + override val id: String, + override val title: String, + override val artist: String?, + override val url: String, + override val isLive: Boolean, + override val artworkUrl: String? + ) : MediaPlayerItem + + @Test + fun testLiveRadioStation() { + val station = TestMediaPlayerItem( + id = "station-1", + title = "BBC Radio 1", + artist = null, + url = "https://stream.example.com/radio1.mp3", + isLive = true, + artworkUrl = "https://example.com/artwork.png" + ) + assertEquals("station-1", station.id) + assertEquals("BBC Radio 1", station.title) + assertNull(station.artist) + assertTrue(station.isLive) + assertEquals("https://stream.example.com/radio1.mp3", station.url) + assertEquals("https://example.com/artwork.png", station.artworkUrl) + } + + @Test + fun testOnDemandTrack() { + val track = TestMediaPlayerItem( + id = "track-42", + title = "Blue (Da Ba Dee)", + artist = "Eiffel 65", + url = "https://stream.example.com/track42.mp3", + isLive = false, + artworkUrl = null + ) + assertEquals("track-42", track.id) + assertEquals("Eiffel 65", track.artist) + assertFalse(track.isLive) + assertNull(track.artworkUrl) + } +} diff --git a/tests/src/commonTest/kotlin/dev/markturnip/radioplayer/PlatformMediaPlayerTest.kt b/tests/src/commonTest/kotlin/dev/markturnip/radioplayer/PlatformMediaPlayerTest.kt new file mode 100644 index 0000000..9ea9b66 --- /dev/null +++ b/tests/src/commonTest/kotlin/dev/markturnip/radioplayer/PlatformMediaPlayerTest.kt @@ -0,0 +1,62 @@ +package dev.markturnip.radioplayer + +import kotlin.test.Test +import kotlin.test.assertEquals + +class PlatformMediaPlayerTest { + + private class TestMediaPlayerItem( + override val id: String = "test-id", + override val title: String = "Test Track", + override val artist: String? = "Test Artist", + override val url: String = "https://stream.example.com/test.mp3", + override val isLive: Boolean = false, + override val artworkUrl: String? = null + ) : MediaPlayerItem + + @Test + fun testPlayTransitionsToPlayingState() { + val player = PlatformMediaPlayer() + val states = mutableListOf() + player.subscribeState { states += it } + + player.play() + + assertEquals(listOf(PlaybackState.PLAYING), states) + } + + @Test + fun testPauseTransitionsToPausedState() { + val player = PlatformMediaPlayer() + val states = mutableListOf() + player.subscribeState { states += it } + + player.play() + player.pause() + + assertEquals(listOf(PlaybackState.PLAYING, PlaybackState.PAUSED), states) + } + + @Test + fun testStopTransitionsToStoppedState() { + val player = PlatformMediaPlayer() + val states = mutableListOf() + player.subscribeState { states += it } + + player.play() + player.stop() + + assertEquals(listOf(PlaybackState.PLAYING, PlaybackState.STOPPED), states) + } + + @Test + fun testPlayItemTransitionsToPlayingState() { + val player = PlatformMediaPlayer() + val states = mutableListOf() + player.subscribeState { states += it } + + player.playItem(TestMediaPlayerItem()) + + assertEquals(listOf(PlaybackState.PLAYING), states) + } +} diff --git a/tests/src/commonTest/kotlin/dev/markturnip/radioplayer/PlaybackStateTest.kt b/tests/src/commonTest/kotlin/dev/markturnip/radioplayer/PlaybackStateTest.kt new file mode 100644 index 0000000..30c299a --- /dev/null +++ b/tests/src/commonTest/kotlin/dev/markturnip/radioplayer/PlaybackStateTest.kt @@ -0,0 +1,39 @@ +package dev.markturnip.radioplayer + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.assertTrue + +class PlaybackStateTest { + + @Test + fun testAllStatesExist() { + val states = PlaybackState.entries + assertTrue(states.contains(PlaybackState.PLAYING)) + assertTrue(states.contains(PlaybackState.PAUSED)) + assertTrue(states.contains(PlaybackState.STOPPED)) + assertTrue(states.contains(PlaybackState.BUFFERING)) + assertTrue(states.contains(PlaybackState.ERROR)) + } + + @Test + fun testStateEquality() { + assertEquals(PlaybackState.PLAYING, PlaybackState.PLAYING) + assertNotEquals(PlaybackState.PLAYING, PlaybackState.PAUSED) + } + + @Test + fun testStateNames() { + assertEquals("PLAYING", PlaybackState.PLAYING.name) + assertEquals("PAUSED", PlaybackState.PAUSED.name) + assertEquals("STOPPED", PlaybackState.STOPPED.name) + assertEquals("BUFFERING", PlaybackState.BUFFERING.name) + assertEquals("ERROR", PlaybackState.ERROR.name) + } + + @Test + fun testStateCount() { + assertEquals(5, PlaybackState.entries.size) + } +} diff --git a/tests/src/commonTest/kotlin/dev/markturnip/radioplayer/ProgressTest.kt b/tests/src/commonTest/kotlin/dev/markturnip/radioplayer/ProgressTest.kt new file mode 100644 index 0000000..d0b3e29 --- /dev/null +++ b/tests/src/commonTest/kotlin/dev/markturnip/radioplayer/ProgressTest.kt @@ -0,0 +1,43 @@ +package dev.markturnip.radioplayer + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +class ProgressTest { + + @Test + fun testEquality() { + val p1 = Progress(elapsed = 30.0, duration = 120.0) + val p2 = Progress(elapsed = 30.0, duration = 120.0) + assertEquals(p1, p2) + } + + @Test + fun testInequality() { + val p1 = Progress(elapsed = 30.0, duration = 120.0) + val p2 = Progress(elapsed = 60.0, duration = 120.0) + assertNotEquals(p1, p2) + } + + @Test + fun testCopy() { + val original = Progress(elapsed = 30.0, duration = 120.0) + val updated = original.copy(elapsed = 60.0) + assertEquals(60.0, updated.elapsed) + assertEquals(120.0, updated.duration) + } + + @Test + fun testElapsedDurationRatio() { + val progress = Progress(elapsed = 30.0, duration = 120.0) + assertEquals(0.25, progress.elapsed / progress.duration) + } + + @Test + fun testZeroElapsedWithNonZeroDuration() { + val progress = Progress(elapsed = 0.0, duration = 120.0) + assertEquals(0.0, progress.elapsed) + assertEquals(0.0, progress.elapsed / progress.duration) + } +} diff --git a/tests/src/jvmTest/kotlin/dev/markturnip/radioplayer/PlatformMediaPlayerJvmTest.kt b/tests/src/jvmTest/kotlin/dev/markturnip/radioplayer/PlatformMediaPlayerJvmTest.kt new file mode 100644 index 0000000..3d37888 --- /dev/null +++ b/tests/src/jvmTest/kotlin/dev/markturnip/radioplayer/PlatformMediaPlayerJvmTest.kt @@ -0,0 +1,38 @@ +package dev.markturnip.radioplayer + +import kotlin.test.Test +import kotlin.test.assertEquals + +class PlatformMediaPlayerJvmTest { + + @Test + fun testInitialStateIsStopped() { + val player = PlatformMediaPlayer() + assertEquals(PlaybackState.STOPPED, player.currentState) + } + + @Test + fun testCurrentStateReflectsPlayPauseStop() { + val player = PlatformMediaPlayer() + player.play() + assertEquals(PlaybackState.PLAYING, player.currentState) + player.pause() + assertEquals(PlaybackState.PAUSED, player.currentState) + player.stop() + assertEquals(PlaybackState.STOPPED, player.currentState) + } + + @Test + fun testSubscribeProgressReceivesEmittedUpdates() { + val player = PlatformMediaPlayer() + val received = mutableListOf() + player.subscribeProgress { received += it } + + player.emitProgress(Progress(elapsed = 10.0, duration = 180.0)) + player.emitProgress(Progress(elapsed = 20.0, duration = 180.0)) + + assertEquals(2, received.size) + assertEquals(10.0, received[0].elapsed) + assertEquals(20.0, received[1].elapsed) + } +}