From 8db0b67ce9ba4a5e56c04c1bea3a738eacb176cf Mon Sep 17 00:00:00 2001 From: Harsh Shandilya Date: Thu, 9 Dec 2021 10:07:54 +0530 Subject: Refactor coroutine testing setup (#1583) * coroutine-utils: init * coroutine-utils-testing: init * format-common: switch over to using DispatcherProvider * Convert Binds method to an extension function * Add Dispatcher module --- format-common/api/format-common.api | 2 +- format-common/build.gradle.kts | 2 ++ .../msfjarvis/aps/data/passfile/PasswordEntry.kt | 5 ++++- .../aps/data/passfile/PasswordEntryTest.kt | 25 ++++++++++++++-------- 4 files changed, 23 insertions(+), 11 deletions(-) (limited to 'format-common') diff --git a/format-common/api/format-common.api b/format-common/api/format-common.api index d0caaf78..629644e3 100644 --- a/format-common/api/format-common.api +++ b/format-common/api/format-common.api @@ -1,5 +1,5 @@ public final class dev/msfjarvis/aps/data/passfile/PasswordEntry { - public fun (Ldev/msfjarvis/aps/util/time/UserClock;Ldev/msfjarvis/aps/util/totp/TotpFinder;Lkotlinx/coroutines/CoroutineScope;[B)V + public fun (Ldev/msfjarvis/aps/util/time/UserClock;Ldev/msfjarvis/aps/util/totp/TotpFinder;Ldev/msfjarvis/aps/util/coroutines/DispatcherProvider;Lkotlinx/coroutines/CoroutineScope;[B)V public final fun getExtraContent ()Ljava/util/Map; public final fun getExtraContentString ()Ljava/lang/String; public final fun getExtraContentWithoutAuthData ()Ljava/lang/String; diff --git a/format-common/build.gradle.kts b/format-common/build.gradle.kts index e5400bcc..3b940573 100644 --- a/format-common/build.gradle.kts +++ b/format-common/build.gradle.kts @@ -9,11 +9,13 @@ plugins { } dependencies { + implementation(projects.coroutineUtils) implementation(libs.androidx.annotation) implementation(libs.dagger.hilt.core) implementation(libs.thirdparty.commons.codec) implementation(libs.thirdparty.kotlinResult) implementation(libs.kotlin.coroutines.core) + testImplementation(projects.coroutineUtilsTesting) testImplementation(libs.bundles.testDependencies) testImplementation(libs.kotlin.coroutines.test) } diff --git a/format-common/src/main/kotlin/dev/msfjarvis/aps/data/passfile/PasswordEntry.kt b/format-common/src/main/kotlin/dev/msfjarvis/aps/data/passfile/PasswordEntry.kt index 408069a2..81398b45 100644 --- a/format-common/src/main/kotlin/dev/msfjarvis/aps/data/passfile/PasswordEntry.kt +++ b/format-common/src/main/kotlin/dev/msfjarvis/aps/data/passfile/PasswordEntry.kt @@ -9,6 +9,7 @@ import com.github.michaelbull.result.mapBoth import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import dev.msfjarvis.aps.util.coroutines.DispatcherProvider import dev.msfjarvis.aps.util.time.UserClock import dev.msfjarvis.aps.util.totp.Otp import dev.msfjarvis.aps.util.totp.TotpFinder @@ -32,6 +33,8 @@ constructor( clock: UserClock, /** [TotpFinder] implementation to extract data from a TOTP URI */ totpFinder: TotpFinder, + /** Instance of [DispatcherProvider] to select an IO dispatcher for emitting TOTP values. */ + dispatcherProvider: DispatcherProvider, /** * A cancellable [CoroutineScope] inside which we constantly emit new TOTP values as time elapses */ @@ -81,7 +84,7 @@ constructor( username = findUsername() totpSecret = totpFinder.findSecret(content) if (totpSecret != null) { - scope.launch { + scope.launch(dispatcherProvider.io()) { val digits = totpFinder.findDigits(content) val totpPeriod = totpFinder.findPeriod(content) val totpAlgorithm = totpFinder.findAlgorithm(content) diff --git a/format-common/src/test/kotlin/dev/msfjarvis/aps/data/passfile/PasswordEntryTest.kt b/format-common/src/test/kotlin/dev/msfjarvis/aps/data/passfile/PasswordEntryTest.kt index 2923946e..32066cc3 100644 --- a/format-common/src/test/kotlin/dev/msfjarvis/aps/data/passfile/PasswordEntryTest.kt +++ b/format-common/src/test/kotlin/dev/msfjarvis/aps/data/passfile/PasswordEntryTest.kt @@ -5,6 +5,7 @@ package dev.msfjarvis.aps.data.passfile +import dev.msfjarvis.aps.test.CoroutineTestRule import dev.msfjarvis.aps.util.time.TestUserClock import dev.msfjarvis.aps.util.totp.TotpFinder import java.util.Locale @@ -16,15 +17,22 @@ import kotlin.test.assertNull import kotlin.test.assertTrue import kotlin.time.ExperimentalTime import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest +import org.junit.Rule @OptIn(ExperimentalCoroutinesApi::class, ExperimentalTime::class) class PasswordEntryTest { + @get:Rule val coroutineTestRule: CoroutineTestRule = CoroutineTestRule() private fun makeEntry(content: String) = - PasswordEntry(fakeClock, testFinder, scope, content.encodeToByteArray()) + PasswordEntry( + fakeClock, + testFinder, + coroutineTestRule.testDispatcherProvider, + TestScope(coroutineTestRule.testDispatcher), + content.encodeToByteArray(), + ) @Test fun testGetPassword() { @@ -125,19 +133,20 @@ class PasswordEntryTest { @Test @Ignore("Timing with runTest seems hard to implement right now") - fun testGeneratesOtpFromTotpUri() = - scope.runTest { + fun testGeneratesOtpFromTotpUri() { + runTest { val entry = makeEntry("secret\nextra\n$TOTP_URI") assertTrue(entry.hasTotp()) val code = entry.totp.value assertNotNull(code) { "Generated OTP cannot be null" } assertEquals("818800", code) } + } @Test @Ignore("Timing with runTest seems hard to implement right now") - fun testGeneratesOtpWithOnlyUriInFile() = - scope.runTest { + fun testGeneratesOtpWithOnlyUriInFile() { + runTest { val entry = makeEntry(TOTP_URI) assertNull(entry.password) assertTrue(entry.hasTotp()) @@ -145,6 +154,7 @@ class PasswordEntryTest { assertNotNull(code) { "Generated OTP cannot be null" } assertEquals("818800", code) } + } @Test fun testOnlyLooksForUriInFirstLine() { @@ -171,9 +181,6 @@ class PasswordEntryTest { const val TOTP_URI = "otpauth://totp/ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30" - val dispatcher = StandardTestDispatcher() - val scope = TestScope(dispatcher) - val fakeClock = TestUserClock() // This implementation is hardcoded for the URI above. -- cgit v1.2.3