Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate last Android Tests to Unit Tests #477

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 0 additions & 40 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,43 +152,3 @@ jobs:
title: Code Coverage
pass-emoji: 🟢
fail-emoji: 🔴

android-tests:
name: Android Tests
runs-on: ubuntu-latest
needs: build
env:
USERNAME: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
strategy:
matrix:
api-level: [ 26 ]
steps:
- name: Enable KVM
# https://github.blog/changelog/2023-02-23-hardware-accelerated-android-virtualization-on-actions-windows-and-linux-larger-hosted-runners/
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v4
with:
cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
- name: Copy CI gradle.properties
run: mkdir -p ~/.gradle; cp .github/gradle-ci.properties ~/.gradle/gradle.properties
- name: Run Android Tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
arch: x86_64
# Supported tasks per module
# :pillarbox-analytics:connectedDebugAndroidTest
# :pillarbox-core-business:connectedDebugAndroidTest
# :pillarbox-player:connectedDebugAndroidTest
# :pillarbox-ui:connectedDebugAndroidTest
script: ./gradlew :pillarbox-player:connectedDebugAndroidTest
2 changes: 0 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ androidx-paging = "3.3.2"
androidx-test-core = "1.6.1"
androidx-test-ext-junit = "1.2.1"
androidx-test-monitor = "1.7.2"
androidx-test-runner = "1.6.2"
androidx-tv-material = "1.0.0"
coil = "2.7.0"
comscore = "6.11.1"
Expand Down Expand Up @@ -58,7 +57,6 @@ androidx-paging-common = { module = "androidx.paging:paging-common", version.ref
androidx-paging-compose = { module = "androidx.paging:paging-compose", version.ref = "androidx-paging" }
androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test-core" }
androidx-test-monitor = { module = "androidx.test:monitor", version.ref = "androidx-test-monitor" }
androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidx-test-runner" }
androidx-tv-material = { module = "androidx.tv:tv-material", version.ref = "androidx-tv-material" }
coil = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" }
coil-base = { module = "io.coil-kt:coil-compose-base", version.ref = "coil" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ object TestPillarboxRunHelper {
* Runs tasks of the main Looper until [Player.Listener.onEvents] matches the
* expected state or a playback error occurs.
*
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.
* If a playback error occurs, it will be thrown wrapped in an [IllegalStateException].
*
* @param player The [Player].
* @param expectedEvent The expected [Player.Event] if null wait until first [Player.Listener.onEvents].
Expand Down Expand Up @@ -60,7 +60,7 @@ object TestPillarboxRunHelper {
/**
* Runs tasks of the main Looper until [Player.Listener.onPlaybackParametersChanged] is called or a playback error occurs.
*
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.
* If a playback error occurs, it will be thrown wrapped in an [IllegalStateException].
*
* @param player The [Player].
* @throws TimeoutException If the [RobolectricUtil.DEFAULT_TIMEOUT_MS] is exceeded.
Expand Down Expand Up @@ -103,4 +103,36 @@ object TestPillarboxRunHelper {
player.currentPosition >= position.inWholeMilliseconds
}
}

/**
* Run and wait until [Player.isPlaying] is [isPlaying].

* If a playback error occurs, it will be thrown wrapped in an [IllegalStateException].
*
* @param player The [Player].
* @param isPlaying The expected value of [Player.isPlaying].

* @throws TimeoutException If the [RobolectricUtil.DEFAULT_TIMEOUT_MS] is exceeded.
*/
@Throws(TimeoutException::class)
fun runUntilIsPlaying(player: Player, isPlaying: Boolean) {
verifyMainTestThread(player)
if (player is ExoPlayer) {
verifyPlaybackThreadIsAlive(player)
}
val receivedCallback = AtomicBoolean(false)
val listener = object : Player.Listener {
override fun onIsPlayingChanged(actual: Boolean) {
if (actual == isPlaying) {
receivedCallback.set(true)
}
}
}
player.addListener(listener)
RobolectricUtil.runMainLooperUntil { receivedCallback.get() || player.playerError != null }
player.removeListener(listener)
if (player.playerError != null) {
throw IllegalStateException(player.playerError)
}
}
}
18 changes: 1 addition & 17 deletions pillarbox-player/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,6 @@ android {
buildFeatures {
buildConfig = true
}

// Mockk includes some licenses information, which may conflict with other license files. This block merges all licenses together.
// Mockk excludes all licenses instead:
// https://github.com/mockk/mockk/blob/f879502a044c83c2a5fd52992f20903209eb34f3/modules/mockk-android/build.gradle.kts#L14-L19
packaging {
resources {
merges += "META-INF/LICENSE.md"
merges += "META-INF/LICENSE-notice.md"
}
}
}

dependencies {
Expand Down Expand Up @@ -69,14 +59,8 @@ dependencies {
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.mockk)
testImplementation(libs.mockk.dsl)
testRuntimeOnly(libs.robolectric)
testImplementation(libs.robolectric)
testImplementation(libs.robolectric.annotations)
testImplementation(libs.robolectric.shadows.framework)
testImplementation(libs.turbine)

androidTestImplementation(libs.androidx.test.monitor)
androidTestRuntimeOnly(libs.androidx.test.runner)
androidTestImplementation(libs.junit)
androidTestRuntimeOnly(libs.kotlinx.coroutines.android)
androidTestImplementation(libs.mockk)
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright (c) SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.player

import android.net.Uri
import android.os.Looper
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.test.utils.FakeClock
import androidx.test.core.app.ApplicationProvider
import ch.srgssr.pillarbox.player.test.utils.TestPillarboxRunHelper
import org.junit.runner.RunWith
import org.robolectric.ParameterizedRobolectricTestRunner
import org.robolectric.ParameterizedRobolectricTestRunner.Parameters
import org.robolectric.Shadows.shadowOf
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

@RunWith(ParameterizedRobolectricTestRunner::class)
class IsPlayingAllTypeOfContentTest(
private val urlToTest: String,
) {
private lateinit var player: PillarboxExoPlayer

@BeforeTest
fun setUp() {
player = PillarboxExoPlayer(
context = ApplicationProvider.getApplicationContext(),
clock = FakeClock(true),
coroutineContext = EmptyCoroutineContext,
)
}

@AfterTest
fun tearDown() {
player.release()

shadowOf(Looper.getMainLooper()).idle()
}

@Test
fun `is playing`() {
player.setMediaItem(MediaItem.fromUri(urlToTest))
player.prepare()
player.play()

TestPillarboxRunHelper.runUntilIsPlaying(player, isPlaying = true)

assertEquals(Player.STATE_READY, player.playbackState)
assertTrue(player.isPlaying)
assertNotNull(player.currentMediaItem)
assertEquals(player.currentMediaItem?.localConfiguration?.uri, Uri.parse(urlToTest))
}

companion object {
@JvmStatic
@Suppress("unused")
@Parameters(name = "{index}: {0}")
fun parameters(): Iterable<Any> {
return listOf(
// From urn:swi:video:48940210
"https://cdn.prod.swi-services.ch/video-projects/141b30ce-3850-424b-9063-a20d5619d342/localised-videos/ENG/renditions/ENG.mp4",
"https://rts-vod-amd.akamaized.net/ww/14970442/7510ee63-05a4-3d48-8d26-1f1b3a82f6be/master.m3u8",
"https://srfaudio-a.akamaihd.net/delivery/world/af671f12-6f17-415a-9dd8-b8aee24cce8b.mp3",
"https://storage.googleapis.com/wvmedia/clear/h264/tears/tears.mpd",
"https://storage.googleapis.com/wvmedia/clear/hevc/tears/tears.mpd",
"https://rtsc3video.akamaized.net/hls/live/2042837/c3video/3/playlist.m3u8?dw=0",
"https://rtsc3video.akamaized.net/hls/live/2042837/c3video/3/playlist.m3u8",
"https://stream.srg-ssr.ch/m/la-1ere/mp3_128",
"https://lsaplus.swisstxt.ch/audio/couleur3_96.stream/playlist.m3u8",
)
}
}
}
Loading