diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index d78a7c8..e07ffab 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -18,7 +18,7 @@ jobs: java-version: 1.11 - name: Run tests - run: ./gradlew testDebugUnitTest + run: ./gradlew testDebugUnitTest --stacktrace - name: Run static analysis run: ./gradlew ktlintCheck detekt diff --git a/README.md b/README.md index ceeb744..ad39c3b 100644 --- a/README.md +++ b/README.md @@ -39,10 +39,10 @@ repositories { dependencies { // LifecycleObserverKtx - implementation "com.adamkobus:lifecycle-observer-ktx:0.0.1-SNAPSHOT" + implementation "com.adamkobus:lifecycle-observer-ktx:1.0.0-SNAPSHOT" // LifecycleObserverKtx + LifecycleAwareViewModel + ViewParam - implementation "com.adamkobus:lifecycle-observer-viewmodel-ktx:0.0.1-SNAPSHOT" + implementation "com.adamkobus:lifecycle-observer-viewmodel-ktx:1.0.0-SNAPSHOT" } ``` diff --git a/build.gradle b/build.gradle index 2d48978..bbaae5b 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,10 @@ task clean(type: Delete) { allprojects { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { kotlinOptions { - freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" + freeCompilerArgs += [ + '-progressive', + "-opt-in=kotlin.RequiresOptIn" + ] } } } diff --git a/gradle/android-setup.gradle b/gradle/android-setup.gradle index a93b9a2..8916480 100644 --- a/gradle/android-setup.gradle +++ b/gradle/android-setup.gradle @@ -37,6 +37,12 @@ def configureAndroidProject(Project androidProject, boolean isApplication) { } } + buildTypes { + debug { + testCoverageEnabled true + } + } + compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 @@ -47,6 +53,10 @@ def configureAndroidProject(Project androidProject, boolean isApplication) { excludes += "/META-INF/{AL2.0,LGPL2.1}" } } + + testOptions.unitTests { + returnDefaultValues = true + } } } diff --git a/gradle/publishing-root.gradle b/gradle/publishing-root.gradle index a007492..15b2aea 100644 --- a/gradle/publishing-root.gradle +++ b/gradle/publishing-root.gradle @@ -4,7 +4,7 @@ rootProject.ext["adamkobusSonatypeStagingProfileId"] = "" rootProject.ext["snapshot"] = System.getenv().getOrDefault("MAVEN_SNAPSHOT", "true") -def VERSION_BASE = "0.0.1" +def VERSION_BASE = "1.0.0" def VERSION_SUFFIX = "" if (rootProject.ext["snapshot"] == "true") { VERSION_SUFFIX = "-SNAPSHOT" diff --git a/lifecycle-observer-ktx/build.gradle b/lifecycle-observer-ktx/build.gradle index 0f87274..9420673 100644 --- a/lifecycle-observer-ktx/build.gradle +++ b/lifecycle-observer-ktx/build.gradle @@ -16,6 +16,7 @@ dependencies { // testing testImplementation Libs.Test.JUnit testImplementation Libs.Test.Mockk + testImplementation Libs.Test.CoroutinesTest } ext { diff --git a/lifecycle-observer-ktx/src/test/java/com/adamkobus/lifecycle/LifecycleObserverKtxTest.kt b/lifecycle-observer-ktx/src/test/java/com/adamkobus/lifecycle/LifecycleObserverKtxTest.kt index 5884747..6564338 100644 --- a/lifecycle-observer-ktx/src/test/java/com/adamkobus/lifecycle/LifecycleObserverKtxTest.kt +++ b/lifecycle-observer-ktx/src/test/java/com/adamkobus/lifecycle/LifecycleObserverKtxTest.kt @@ -6,48 +6,53 @@ import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every import io.mockk.mockk -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.Timeout import java.util.concurrent.TimeUnit +@OptIn(ExperimentalCoroutinesApi::class) class LifecycleObserverKtxTest { @JvmField @Rule val timeoutRule = Timeout(10, TimeUnit.SECONDS) + private val deferred = CompletableDeferred() private val lifecycleOwner: LifecycleOwner = mockk() private val suspendTask: suspend () -> Unit = mockk() private val task: () -> Unit = mockk() @Before fun setUp() { - coEvery { suspendTask.invoke() } coAnswers {} + coEvery { suspendTask.invoke() } coAnswers { deferred.complete(Unit) } every { task.invoke() } answers {} } @Test - fun `GIVEN start task registered with runOnCreateDestroy WHEN ON_CREATE THEN task is executed`() = runBlocking { + fun `GIVEN start task registered with runOnCreateDestroy WHEN ON_CREATE THEN task is executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnCreateDestroy { onCreate = suspendTask } // when testSubject.onStateChanged(lifecycleOwner, Lifecycle.Event.ON_CREATE) // then + deferred.await() coVerify { suspendTask.invoke() } } @Test - fun `GIVEN start task registered with runOnCreateDestroy WHEN not ON_CREATE THEN task not executed`() = runBlocking { + fun `GIVEN start task registered with runOnCreateDestroy WHEN not ON_CREATE THEN task not executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnCreateDestroy { onCreate = suspendTask } Lifecycle.Event.values().filter { it != Lifecycle.Event.ON_CREATE }.forEach { event -> // when @@ -59,9 +64,9 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN stop task registered with runOnCreateDestroy WHEN ON_DESTROY THEN task is executed`() = runBlocking { + fun `GIVEN stop task registered with runOnCreateDestroy WHEN ON_DESTROY THEN task is executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnCreateDestroy { onDestroy = task } // when @@ -72,9 +77,9 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN stop task registered with runOnCreateDestroy WHEN not ON_DESTROY THEN task not executed`() = runBlocking { + fun `GIVEN stop task registered with runOnCreateDestroy WHEN not ON_DESTROY THEN task not executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnCreateDestroy { onDestroy = task } Lifecycle.Event.values().filter { it != Lifecycle.Event.ON_DESTROY }.forEach { event -> // when @@ -86,22 +91,23 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN start task registered with runOnStartStop WHEN ON_START THEN task is executed`() = runBlocking { + fun `GIVEN start task registered with runOnStartStop WHEN ON_START THEN task is executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnStartStop { onStart = suspendTask } // when testSubject.onStateChanged(lifecycleOwner, Lifecycle.Event.ON_START) // then + deferred.await() coVerify { suspendTask.invoke() } } @Test - fun `GIVEN start task registered with runOnStartStop WHEN not ON_START THEN task not executed`() = runBlocking { + fun `GIVEN start task registered with runOnStartStop WHEN not ON_START THEN task not executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnStartStop { onStart = suspendTask } Lifecycle.Event.values().filter { it != Lifecycle.Event.ON_START }.forEach { event -> // when @@ -113,9 +119,9 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN stop task registered with runOnStartStop WHEN ON_STOP THEN task is executed`() = runBlocking { + fun `GIVEN stop task registered with runOnStartStop WHEN ON_STOP THEN task is executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnStartStop { onStop = task } // when @@ -126,9 +132,9 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN stop task registered with runOnStartStop WHEN not ON_STOP THEN task not executed`() = runBlocking { + fun `GIVEN stop task registered with runOnStartStop WHEN not ON_STOP THEN task not executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnStartStop { onStop = task } Lifecycle.Event.values().filter { it != Lifecycle.Event.ON_STOP }.forEach { event -> // when @@ -140,22 +146,23 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN start task registered with runOnResumePause WHEN ON_RESUME THEN task is executed`() = runBlocking { + fun `GIVEN start task registered with runOnResumePause WHEN ON_RESUME THEN task is executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnResumePause { onResume = suspendTask } // when testSubject.onStateChanged(lifecycleOwner, Lifecycle.Event.ON_RESUME) // then + deferred.await() coVerify { suspendTask.invoke() } } @Test - fun `GIVEN start task registered with runOnResumePause WHEN not ON_RESUME THEN task not executed`() = runBlocking { + fun `GIVEN start task registered with runOnResumePause WHEN not ON_RESUME THEN task not executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnResumePause { onResume = suspendTask } Lifecycle.Event.values().filter { it != Lifecycle.Event.ON_RESUME }.forEach { event -> // when @@ -167,9 +174,9 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN stop task registered with runOnStartStop WHEN ON_PAUSE THEN task is executed`() = runBlocking { + fun `GIVEN stop task registered with runOnStartStop WHEN ON_PAUSE THEN task is executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnResumePause { onPause = task } // when @@ -180,9 +187,9 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN stop task registered with runOnStartStop WHEN not ON_PAUSE THEN task not executed`() = runBlocking { + fun `GIVEN stop task registered with runOnStartStop WHEN not ON_PAUSE THEN task not executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnResumePause { onPause = task } Lifecycle.Event.values().filter { it != Lifecycle.Event.ON_PAUSE }.forEach { event -> // when @@ -196,22 +203,23 @@ class LifecycleObserverKtxTest { // single event registration @Test - fun `GIVEN start task registered with runOnCreate WHEN ON_CREATE THEN task is executed`() = runBlocking { + fun `GIVEN start task registered with runOnCreate WHEN ON_CREATE THEN task is executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnCreate(suspendTask) // when testSubject.onStateChanged(lifecycleOwner, Lifecycle.Event.ON_CREATE) // then + deferred.await() coVerify { suspendTask.invoke() } } @Test - fun `GIVEN start task registered with runOnCreate WHEN not ON_CREATE THEN task not executed`() = runBlocking { + fun `GIVEN start task registered with runOnCreate WHEN not ON_CREATE THEN task not executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnCreate(suspendTask) Lifecycle.Event.values().filter { it != Lifecycle.Event.ON_CREATE }.forEach { event -> // when @@ -223,9 +231,9 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN stop task registered with runOnDestroy WHEN ON_DESTOY THEN task is executed`() = runBlocking { + fun `GIVEN stop task registered with runOnDestroy WHEN ON_DESTOY THEN task is executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnDestroy(task) // when @@ -236,9 +244,9 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN stop task registered with runOnDestroy WHEN not ON_DESTOY THEN task not executed`() = runBlocking { + fun `GIVEN stop task registered with runOnDestroy WHEN not ON_DESTOY THEN task not executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnDestroy(task) Lifecycle.Event.values().filter { it != Lifecycle.Event.ON_DESTROY }.forEach { event -> // when @@ -250,22 +258,23 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN start task registered with runOnStart WHEN ON_START THEN task is executed`() = runBlocking { + fun `GIVEN start task registered with runOnStart WHEN ON_START THEN task is executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnStart(suspendTask) // when testSubject.onStateChanged(lifecycleOwner, Lifecycle.Event.ON_START) // then + deferred.await() coVerify { suspendTask.invoke() } } @Test - fun `GIVEN start task registered with runOnStart WHEN not ON_START THEN task not executed`() = runBlocking { + fun `GIVEN start task registered with runOnStart WHEN not ON_START THEN task not executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnStart(suspendTask) Lifecycle.Event.values().filter { it != Lifecycle.Event.ON_START }.forEach { event -> // when @@ -277,9 +286,9 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN stop task registered with runOnStop WHEN ON_STOP THEN task is executed`() = runBlocking { + fun `GIVEN stop task registered with runOnStop WHEN ON_STOP THEN task is executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnStop(task) // when @@ -290,9 +299,9 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN stop task registered with runOnDestroy WHEN not ON_STOP THEN task not executed`() = runBlocking { + fun `GIVEN stop task registered with runOnDestroy WHEN not ON_STOP THEN task not executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnStop(task) Lifecycle.Event.values().filter { it != Lifecycle.Event.ON_STOP }.forEach { event -> // when @@ -304,22 +313,23 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN start task registered with runOnResume WHEN ON_RESUME THEN task is executed`() = runBlocking { + fun `GIVEN start task registered with runOnResume WHEN ON_RESUME THEN task is executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnResume(suspendTask) // when testSubject.onStateChanged(lifecycleOwner, Lifecycle.Event.ON_RESUME) // then + deferred.await() coVerify { suspendTask.invoke() } } @Test - fun `GIVEN start task registered with runOnResume WHEN not ON_RESUME THEN task not executed`() = runBlocking { + fun `GIVEN start task registered with runOnResume WHEN not ON_RESUME THEN task not executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnResume(suspendTask) Lifecycle.Event.values().filter { it != Lifecycle.Event.ON_RESUME }.forEach { event -> // when @@ -331,9 +341,9 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN stop task registered with runOnPause WHEN ON_PAUSE THEN task is executed`() = runBlocking { + fun `GIVEN stop task registered with runOnPause WHEN ON_PAUSE THEN task is executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnPause(task) // when @@ -344,9 +354,9 @@ class LifecycleObserverKtxTest { } @Test - fun `GIVEN stop task registered with runOnPause WHEN not ON_PAUSE THEN task not executed`() = runBlocking { + fun `GIVEN stop task registered with runOnPause WHEN not ON_PAUSE THEN task not executed`() = runTest { // given - val testSubject = testSubject(this) + val testSubject = testSubject() testSubject.runOnPause(task) Lifecycle.Event.values().filter { it != Lifecycle.Event.ON_PAUSE }.forEach { event -> // when @@ -357,5 +367,5 @@ class LifecycleObserverKtxTest { } } - private fun testSubject(scope: CoroutineScope) = LifecycleObserverKtx(scope, Dispatchers.Default) + private fun TestScope.testSubject() = LifecycleObserverKtx(this, StandardTestDispatcher(testScheduler)) } diff --git a/lifecycle-observer-viewmodel-ktx/build.gradle b/lifecycle-observer-viewmodel-ktx/build.gradle index bede71f..c4d4084 100644 --- a/lifecycle-observer-viewmodel-ktx/build.gradle +++ b/lifecycle-observer-viewmodel-ktx/build.gradle @@ -20,6 +20,7 @@ dependencies { // testing testImplementation Libs.Test.JUnit testImplementation Libs.Test.Mockk + testImplementation Libs.Test.CoroutinesTest } ext { diff --git a/lifecycle-observer-viewmodel-ktx/src/test/java/com/adamkobus/android/vm/ViewParamTest.kt b/lifecycle-observer-viewmodel-ktx/src/test/java/com/adamkobus/android/vm/ViewParamTest.kt index 980bb95..c364ed6 100644 --- a/lifecycle-observer-viewmodel-ktx/src/test/java/com/adamkobus/android/vm/ViewParamTest.kt +++ b/lifecycle-observer-viewmodel-ktx/src/test/java/com/adamkobus/android/vm/ViewParamTest.kt @@ -1,31 +1,33 @@ package com.adamkobus.android.vm +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import org.junit.Assert.assertEquals import org.junit.Rule import org.junit.Test import org.junit.rules.Timeout -import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import kotlin.random.Random +@OptIn(ExperimentalCoroutinesApi::class) class ViewParamTest { @JvmField @Rule val timeoutRule = Timeout(10, TimeUnit.SECONDS) + private val deferred = CompletableDeferred() private val testSubject = ViewParam() @Test - fun `GIVEN bound value WHEN collect THEN offered value is collected`() = runBlocking { + fun `GIVEN bound value WHEN collect THEN offered value is collected`() = runTest { // given val expectedValue = Random.nextInt() testSubject.bind(expectedValue) - val latch = CountDownLatch(1) // when val job = launch(Dispatchers.IO) { @@ -33,15 +35,15 @@ class ViewParamTest { // then assertEquals(expectedValue, obtained) - latch.countDown() + deferred.complete(Unit) } } - latch.await() + deferred.await() job.cancel() } @Test - fun `GIVEN bound value WHEN observe THEN offered value is collected`() = runBlocking { + fun `GIVEN bound value WHEN observe THEN offered value is collected`() = runTest { // given val expectedValue = Random.nextInt() testSubject.bind(expectedValue) diff --git a/scripts/cleanup-secrets.sh b/scripts/cleanup-secrets.sh old mode 100644 new mode 100755 diff --git a/scripts/decrypt-secrets.sh b/scripts/decrypt-secrets.sh old mode 100644 new mode 100755 diff --git a/scripts/encrypt-secrets.sh b/scripts/encrypt-secrets.sh old mode 100644 new mode 100755