From 83f6caff6d64d2314034a8da668f56ab472bccc2 Mon Sep 17 00:00:00 2001 From: Adam Kobus Date: Sat, 5 Feb 2022 21:45:27 +0100 Subject: [PATCH 1/4] Fixed scripts permissions --- scripts/cleanup-secrets.sh | 0 scripts/decrypt-secrets.sh | 0 scripts/encrypt-secrets.sh | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/cleanup-secrets.sh mode change 100644 => 100755 scripts/decrypt-secrets.sh mode change 100644 => 100755 scripts/encrypt-secrets.sh 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 From cd07cff2092b992263f311372676ff085745b096 Mon Sep 17 00:00:00 2001 From: Adam Kobus Date: Sat, 5 Feb 2022 22:06:01 +0100 Subject: [PATCH 2/4] Version 1.0.0 --- README.md | 4 ++-- gradle/publishing-root.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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/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" From 2bf16112106524bc1d46beec0b15c0e935c9a7c1 Mon Sep 17 00:00:00 2001 From: Adam Kobus Date: Sat, 5 Feb 2022 22:48:58 +0100 Subject: [PATCH 3/4] Tests config fix --- .github/workflows/pull-request.yml | 2 +- build.gradle | 5 +- gradle/android-setup.gradle | 10 ++ lifecycle-observer-ktx/build.gradle | 1 + .../lifecycle/LifecycleObserverKtxTest.kt | 116 ++++++++++-------- lifecycle-observer-viewmodel-ktx/build.gradle | 1 + .../com/adamkobus/android/vm/ViewParamTest.kt | 8 +- 7 files changed, 85 insertions(+), 58 deletions(-) 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/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/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..3fc209e 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,9 +1,10 @@ package com.adamkobus.android.vm 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 @@ -12,6 +13,7 @@ import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import kotlin.random.Random +@OptIn(ExperimentalCoroutinesApi::class) class ViewParamTest { @JvmField @@ -21,7 +23,7 @@ class ViewParamTest { 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) @@ -41,7 +43,7 @@ class ViewParamTest { } @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) From 916a973398004b19c38d4ed649ec3a76f9d35872 Mon Sep 17 00:00:00 2001 From: Adam Kobus Date: Sat, 5 Feb 2022 23:18:53 +0100 Subject: [PATCH 4/4] Further tests improvements --- .../test/java/com/adamkobus/android/vm/ViewParamTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 3fc209e..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,5 +1,6 @@ package com.adamkobus.android.vm +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first @@ -9,7 +10,6 @@ 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 @@ -20,6 +20,7 @@ class ViewParamTest { @Rule val timeoutRule = Timeout(10, TimeUnit.SECONDS) + private val deferred = CompletableDeferred() private val testSubject = ViewParam() @Test @@ -27,7 +28,6 @@ class ViewParamTest { // given val expectedValue = Random.nextInt() testSubject.bind(expectedValue) - val latch = CountDownLatch(1) // when val job = launch(Dispatchers.IO) { @@ -35,10 +35,10 @@ class ViewParamTest { // then assertEquals(expectedValue, obtained) - latch.countDown() + deferred.complete(Unit) } } - latch.await() + deferred.await() job.cancel() }