diff --git a/.gitignore b/.gitignore index 5b9a0dc8..0b0185d3 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,9 @@ example-android-app/ # generated **/src-gen/commonTest/kotlin/**/config/TestConfig.kt +# kotlin-js +kotlin-js-store/ + # Editors *.suo *.ntvs* diff --git a/build.gradle.kts b/build.gradle.kts index 9a3a5be4..a14cbe36 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,6 +30,7 @@ allprojects { addCustomRepositories() mavenCentral() google() + jcenter() } configurations.all { @@ -48,6 +49,6 @@ allprojects { } tasks.named("wrapper") { - gradleVersion = "7.3.3" + gradleVersion = "7.2" distributionType = Wrapper.DistributionType.ALL } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f1031183..4adeca47 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -6,6 +6,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlePlugin/kmock-dependency/src/main/kotlin/tech/antibytes/gradle/kmock/dependency/Version.kt b/gradlePlugin/kmock-dependency/src/main/kotlin/tech/antibytes/gradle/kmock/dependency/Version.kt index 04fb7eb9..b86fad50 100644 --- a/gradlePlugin/kmock-dependency/src/main/kotlin/tech/antibytes/gradle/kmock/dependency/Version.kt +++ b/gradlePlugin/kmock-dependency/src/main/kotlin/tech/antibytes/gradle/kmock/dependency/Version.kt @@ -14,7 +14,7 @@ object Version { /** * [AnitBytes GradlePlugins](https://github.com/bitPogo/gradle-plugins) */ - const val antibytes = "c81afcd" + const val antibytes = "99eff34" /** * [Spotless](https://plugins.gradle.org/plugin/com.diffplug.gradle.spotless) @@ -28,7 +28,8 @@ object Version { } val antibytes = Antibytes + object Antibytes { - val test = "f0f5eec" + val test = "7b11c9d-add-ios-SNAPSHOT" } } diff --git a/kmock/build.gradle.kts b/kmock/build.gradle.kts index 38ba9e13..4fe32fcf 100644 --- a/kmock/build.gradle.kts +++ b/kmock/build.gradle.kts @@ -30,21 +30,34 @@ antiBytesPublishing { kotlin { android() + js(IR) { + nodejs() + browser() + } + jvm() + ios() + + linuxX64() + sourceSets { val commonMain by getting { dependencies { implementation(Dependency.multiplatform.kotlin.common) + implementation(Dependency.multiplatform.stately.isolate) + implementation(Dependency.multiplatform.stately.concurrency) + + implementation(LocalDependency.antibytes.test.core) } } val commonTest by getting { dependencies { implementation(Dependency.multiplatform.test.common) implementation(Dependency.multiplatform.test.annotations) + implementation(Dependency.multiplatform.coroutines.common) implementation(LocalDependency.antibytes.test.annotations) - implementation(LocalDependency.antibytes.test.core) implementation(LocalDependency.antibytes.test.coroutine) implementation(LocalDependency.antibytes.test.fixture) } @@ -66,6 +79,21 @@ kotlin { } } + val jsMain by getting { + dependencies { + dependsOn(commonMain) + implementation(Dependency.multiplatform.kotlin.js) + implementation(Dependency.js.nodejs) + } + } + val jsTest by getting { + dependencies { + dependsOn(commonTest) + + implementation(Dependency.multiplatform.test.js) + } + } + val jvmMain by getting { dependencies { dependsOn(commonMain) @@ -80,5 +108,63 @@ kotlin { implementation(Dependency.multiplatform.test.junit) } } + + val nativeMain by creating { + dependencies { + dependsOn(commonMain) + } + } + + val nativeTest by creating { + dependencies { + dependsOn(commonTest) + } + } + + val darwinMain by creating { + dependencies { + dependsOn(nativeMain) + } + } + val darwinTest by creating { + dependencies { + dependsOn(nativeTest) + } + } + + val otherMain by creating { + dependencies { + dependsOn(nativeMain) + } + } + + val otherTest by creating { + dependencies { + dependsOn(nativeTest) + } + } + + val linuxX64Main by getting { + dependencies { + dependsOn(otherMain) + } + } + + val linuxX64Test by getting { + dependencies { + dependsOn(otherTest) + } + } + + val iosMain by getting { + dependencies { + dependsOn(darwinMain) + } + } + val iosTest by getting { + dependencies { + dependsOn(darwinTest) + } + } } } diff --git a/kmock/src/commonMain/kotlin/tech/antibytes/kmock/ArgumentMatcher.kt b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/ArgumentMatcher.kt new file mode 100644 index 00000000..c0f9bec4 --- /dev/null +++ b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/ArgumentMatcher.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import tech.antibytes.kmock.KMockContract.GetOrSet + +internal fun Array?.withArguments(vararg values: Any?): Boolean { + return when { + this == null -> values.isEmpty() + values.isEmpty() -> true + else -> values.all { value -> this.contains(value) } + } +} + +internal fun Array?.withSameArguments(vararg values: Any?): Boolean { + return this?.contentDeepEquals(values) ?: values.isEmpty() +} + +internal fun Array?.withoutArguments(vararg values: Any?): Boolean { + return if (this == null) { + values.isNotEmpty() + } else { + values.none { value -> this.contains(value) } + } +} + +internal fun GetOrSet.wasGotten(): Boolean = this is GetOrSet.Get + +internal fun GetOrSet.wasSet(): Boolean = this is GetOrSet.Set + +internal fun GetOrSet.wasSetTo(value: Any?): Boolean { + return when (this) { + !is GetOrSet.Set -> false + else -> this.value == value + } +} diff --git a/kmock/src/commonMain/kotlin/tech/antibytes/kmock/AsyncFunMockery.kt b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/AsyncFunMockery.kt new file mode 100644 index 00000000..41250057 --- /dev/null +++ b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/AsyncFunMockery.kt @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import tech.antibytes.kmock.KMockContract.Collector +import tech.antibytes.util.test.MockError + +class AsyncFunMockery>( + id: String, + collector: Collector = Collector { _, _ -> Unit } +) : KMockContract.AsyncFunMockery, FunMockery(id, collector) { + private suspend fun execute( + function: suspend () -> ReturnValue, + vararg arguments: Any? + ): ReturnValue { + onEvent(arguments) + + return when (provider.get()) { + PROVIDER.RETURN_VALUE -> retrieveValue() + PROVIDER.RETURN_VALUES -> retrieveFromValues() + PROVIDER.SIDE_EFFECT -> function() + else -> throw MockError.MissingStub("Missing stub value for $id") + } + } + + @Suppress("UNCHECKED_CAST") + override suspend fun invoke(): ReturnValue { + val invocation = suspend { + (retrieveSideEffect() as suspend () -> ReturnValue) + .invoke() + } + + return execute(invocation) + } + + @Suppress("UNCHECKED_CAST") + override suspend fun invoke(arg0: Arg0): ReturnValue { + val invocation = suspend { + (retrieveSideEffect() as suspend (Arg0) -> ReturnValue) + .invoke(arg0) + } + + return execute(invocation, arg0) + } + + @Suppress("UNCHECKED_CAST") + override suspend fun invoke(arg0: Arg0, arg1: Arg1): ReturnValue { + val invocation = suspend { + (retrieveSideEffect() as suspend (Arg0, Arg1) -> ReturnValue) + .invoke(arg0, arg1) + } + + return execute(invocation, arg0, arg1) + } + + @Suppress("UNCHECKED_CAST") + override suspend fun invoke(arg0: Arg0, arg1: Arg1, arg2: Arg2): ReturnValue { + val invocation = suspend { + (retrieveSideEffect() as suspend (Arg0, Arg1, Arg2) -> ReturnValue) + .invoke(arg0, arg1, arg2) + } + + return execute(invocation, arg0, arg1, arg2) + } + + @Suppress("UNCHECKED_CAST") + override suspend fun invoke(arg0: Arg0, arg1: Arg1, arg2: Arg2, arg3: Arg3): ReturnValue { + val invocation = suspend { + (retrieveSideEffect() as suspend (Arg0, Arg1, Arg2, Arg3) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3) + } + + return execute(invocation, arg0, arg1, arg2, arg3) + } + + @Suppress("UNCHECKED_CAST") + override suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4 + ): ReturnValue { + val invocation = suspend { + (retrieveSideEffect() as suspend (Arg0, Arg1, Arg2, Arg3, Arg4) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4) + } + + @Suppress("UNCHECKED_CAST") + override suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5 + ): ReturnValue { + val invocation = suspend { + (retrieveSideEffect() as suspend (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5) + } + + @Suppress("UNCHECKED_CAST") + override suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6 + ): ReturnValue { + val invocation = suspend { + (retrieveSideEffect() as suspend (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5, arg6) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5, arg6) + } + + @Suppress("UNCHECKED_CAST") + override suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7 + ): ReturnValue { + val invocation = suspend { + (retrieveSideEffect() as suspend (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) + } + + @Suppress("UNCHECKED_CAST") + override suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8 + ): ReturnValue { + val invocation = suspend { + (retrieveSideEffect() as suspend (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) + } + + @Suppress("UNCHECKED_CAST") + override suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9 + ): ReturnValue { + val invocation = suspend { + (retrieveSideEffect() as suspend (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + } + + @Suppress("UNCHECKED_CAST") + override suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10 + ): ReturnValue { + val invocation = suspend { + (retrieveSideEffect() as suspend (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9, Arg10) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) + } + + @Suppress("UNCHECKED_CAST") + override suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10, + arg11: Arg11 + ): ReturnValue { + val invocation = suspend { + (retrieveSideEffect() as suspend (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9, Arg10, Arg11) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11) + } + + @Suppress("UNCHECKED_CAST") + override suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10, + arg11: Arg11, + arg12: Arg12 + ): ReturnValue { + val invocation = suspend { + (retrieveSideEffect() as suspend (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9, Arg10, Arg11, Arg12) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12) + } +} diff --git a/kmock/src/commonMain/kotlin/tech/antibytes/kmock/FunMockery.kt b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/FunMockery.kt new file mode 100644 index 00000000..2d185606 --- /dev/null +++ b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/FunMockery.kt @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import co.touchlab.stately.concurrency.AtomicReference +import co.touchlab.stately.concurrency.value +import co.touchlab.stately.isolate.IsolateState +import tech.antibytes.kmock.KMockContract.Collector +import tech.antibytes.util.test.MockError +import kotlin.math.max + +abstract class FunMockery>( + override val id: String, + collector: Collector = Collector { _, _ -> Unit } +) : KMockContract.FunMockery { + private val _returnValue: AtomicReference = AtomicReference(null) + private val _returnValues: AtomicReference?> = AtomicReference(null) + private val _sideEffect: AtomicReference = AtomicReference(null) + private val _calls: AtomicReference = AtomicReference(0) + protected val provider: AtomicReference = AtomicReference(PROVIDER.NO_PROVIDER) + private val arguments: IsolateState?>> = IsolateState { mutableListOf() } + private val collector = AtomicReference(collector) + + protected enum class PROVIDER(val value: Int) { + NO_PROVIDER(0), + RETURN_VALUE(1), + RETURN_VALUES(2), + SIDE_EFFECT(3) + } + + private fun setProvider(provider: PROVIDER) { + val activeProvider = max( + provider.value, + this.provider.get().value + ) + + if (activeProvider == provider.value) { + this.provider.set(provider) + } + } + + override var returnValue: ReturnValue + @Suppress("UNCHECKED_CAST") + get() = _returnValue.get() as ReturnValue + set(value) { + setProvider(PROVIDER.RETURN_VALUE) + _returnValue.set(value) + } + + override var returnValues: List + get() = _returnValues.get() as List + set(values) { + if (values.isEmpty()) { + throw MockError.MissingStub("Empty Lists are not valid as value provider.") + } else { + setProvider(PROVIDER.RETURN_VALUES) + _returnValues.set(values) + } + } + + override var sideEffect: SideEffect + get() = _sideEffect.get() as SideEffect + set(value) { + setProvider(PROVIDER.SIDE_EFFECT) + _sideEffect.set(value) + } + + override val calls: Int + get() = _calls.get() + + private fun determineNewValues(currentValues: List): List { + return if (currentValues.size == 1) { + currentValues + } else { + currentValues.drop(1) + } + } + + protected fun retrieveValue(): ReturnValue = _returnValue.get()!! + + protected fun retrieveFromValues(): ReturnValue { + val currentValues = _returnValues.value + val value = currentValues!!.first() + + val newValues = determineNewValues(currentValues) + + _returnValues.compareAndSet(currentValues, newValues) + + return value + } + + protected fun retrieveSideEffect(): SideEffect = _sideEffect.get()!! + + private fun guardArguments(arguments: Array): Array? { + return if (arguments.isEmpty()) { + null + } else { + arguments + } + } + + private fun captureArguments(arguments: Array) { + this.arguments.access { it.add(guardArguments(arguments)) } + } + + private fun incrementInvocations() { + val calls = this._calls.get() + + this._calls.compareAndSet( + calls, + calls + 1 + ) + } + + private fun notifyCollector() { + collector.get().addReference( + this, + this._calls.get() + ) + } + + protected fun onEvent(arguments: Array) { + notifyCollector() + captureArguments(arguments) + incrementInvocations() + } + + override fun getArgumentsForCall(callIndex: Int): Array? = arguments.access { it[callIndex] } +} diff --git a/kmock/src/commonMain/kotlin/tech/antibytes/kmock/KMockContract.kt b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/KMockContract.kt new file mode 100644 index 00000000..a9c7fd73 --- /dev/null +++ b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/KMockContract.kt @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +interface KMockContract { + sealed interface Mockery { + val id: String + val calls: Int + + fun getArgumentsForCall(callIndex: Int): Arguments + } + + interface FunMockery> : Mockery?> { + var returnValue: ReturnValue + var returnValues: List + var sideEffect: SideEffect + } + + interface SyncFunMockery> : FunMockery { + + fun invoke(): ReturnValue + + fun invoke(arg0: Arg0): ReturnValue + + fun invoke( + arg0: Arg0, + arg1: Arg1 + ): ReturnValue + + fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2 + ): ReturnValue + + fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3 + ): ReturnValue + + fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4 + ): ReturnValue + + fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5 + ): ReturnValue + + fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6 + ): ReturnValue + + fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7 + ): ReturnValue + + fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8 + ): ReturnValue + + fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9 + ): ReturnValue + + fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10 + ): ReturnValue + + fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10, + arg11: Arg11 + ): ReturnValue + + fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10, + arg11: Arg11, + arg12: Arg12, + ): ReturnValue + } + + interface AsyncFunMockery> : FunMockery { + + suspend fun invoke(): ReturnValue + + suspend fun invoke(arg0: Arg0): ReturnValue + + suspend fun invoke( + arg0: Arg0, + arg1: Arg1 + ): ReturnValue + + suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2 + ): ReturnValue + + suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3 + ): ReturnValue + + suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4 + ): ReturnValue + + suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5 + ): ReturnValue + + suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6 + ): ReturnValue + + suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7 + ): ReturnValue + + suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8 + ): ReturnValue + + suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9 + ): ReturnValue + + suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10 + ): ReturnValue + + suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10, + arg11: Arg11 + ): ReturnValue + + suspend fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10, + arg11: Arg11, + arg12: Arg12, + ): ReturnValue + } + + sealed class GetOrSet(val value: Any?) { + object Get : GetOrSet(null) + class Set(newValue: Any?) : GetOrSet(newValue) + } + + interface PropMockery : Mockery { + var get: Value + var getMany: List + var set: (Value) -> Unit + + fun onGet(): Value + fun onSet(value: Value) + } + + interface VerificationHandle { + val id: String + val callIndices: List + } + + data class Reference( + val mockery: Mockery<*, *>, + val callIndex: Int + ) + + fun interface Collector { + fun addReference(referredMock: Mockery<*, *>, referredCall: Int) + } + + interface VerificationHandleContainer { + fun add(handle: VerificationHandle) + + fun toList(): List + } + + interface Verifier { + val references: List + } + + companion object { + const val NOT_CALLED = "Call not found." + const val TOO_LESS_CALLS = "Expected at least \$1 calls, but found only \$2." + const val TOO_MANY_CALLS = "Expected at most \$1 calls, but exceeded with \$2." + const val NOTHING_TO_STRICTLY_VERIFY = "The given verification chain (has \$1 items) does not match the captured calls (\$2 were captured)." + const val NOTHING_TO_VERIFY = "The given verification chain (has \$1 items) is exceeding the captured calls (\$2 were captured)." + const val NO_MATCHING_CALL_IDX = "The captured calls of \$1 exceeds the captured calls." + const val MISMATCHING_FUNCTION = "Excepted \$1, but got \$2." + const val MISMATCHING_CALL_IDX = "Excepted the \$1, but the \$2 was referenced." + const val CALL_NOT_FOUND = "Last referred invocation of \$1 was not found." + } +} diff --git a/kmock/src/commonMain/kotlin/tech/antibytes/kmock/Operators.kt b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/Operators.kt new file mode 100644 index 00000000..5864ba09 --- /dev/null +++ b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/Operators.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +infix fun KMockContract.VerificationHandle.union( + other: KMockContract.VerificationHandle +): KMockContract.VerificationHandle { + val multiSet = this.callIndices.toMutableSet() + multiSet.addAll(other.callIndices) + + return VerificationHandle( + this.id, + multiSet.sorted() + ) +} + +infix fun KMockContract.VerificationHandle.and( + other: KMockContract.VerificationHandle +): KMockContract.VerificationHandle = this.union(other) + +infix fun KMockContract.VerificationHandle.intersect( + other: KMockContract.VerificationHandle +): KMockContract.VerificationHandle { + val set = this.callIndices + .filter { value -> value in other.callIndices } + .sorted() + + return VerificationHandle( + this.id, + set + ) +} + +infix fun KMockContract.VerificationHandle.or( + other: KMockContract.VerificationHandle +): KMockContract.VerificationHandle = this.intersect(other) + +infix fun KMockContract.VerificationHandle.diff( + other: KMockContract.VerificationHandle +): KMockContract.VerificationHandle { + val set = this.callIndices + .toMutableSet() + .also { it.addAll(other.callIndices) } + .filterNot { value -> value in this.callIndices && value in other.callIndices } + .sorted() + + return VerificationHandle( + this.id, + set + ) +} + +infix fun KMockContract.VerificationHandle.xor( + other: KMockContract.VerificationHandle +): KMockContract.VerificationHandle = this.diff(other) diff --git a/kmock/src/commonMain/kotlin/tech/antibytes/kmock/PropertyMockery.kt b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/PropertyMockery.kt new file mode 100644 index 00000000..757b4569 --- /dev/null +++ b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/PropertyMockery.kt @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import co.touchlab.stately.concurrency.AtomicReference +import co.touchlab.stately.concurrency.value +import co.touchlab.stately.isolate.IsolateState +import tech.antibytes.kmock.KMockContract.Collector +import tech.antibytes.kmock.KMockContract.GetOrSet +import tech.antibytes.util.test.MockError + +class PropertyMockery( + override val id: String, + collector: Collector = Collector { _, _ -> Unit } +) : KMockContract.PropMockery { + private val provider: AtomicReference = AtomicReference(null) + private val _get: AtomicReference = AtomicReference(null) + private val _getMany: AtomicReference?> = AtomicReference(null) + private val _set: AtomicReference<((Value) -> Unit)> = AtomicReference { /*Do Nothing on Default*/ } + private val _calls: AtomicReference = AtomicReference(0) + private val arguments: IsolateState> = IsolateState { mutableListOf() } + private val collector = AtomicReference(collector) + + override var get: Value + @Suppress("UNCHECKED_CAST") + get() = _get.get() as Value + set(value) { + provider.set(false) + _get.set(value) + } + + override var getMany: List + get() = _getMany.get() as List + set(values) { + if (values.isEmpty()) { + throw MockError.MissingStub("Empty Lists are not valid as value provider.") + } else { + provider.set(true) + _getMany.set(values) + } + } + + override var set: (Value) -> Unit + get() = _set.get() + set(value) { + _set.set(value) + } + + override val calls: Int + get() = _calls.get() + + private fun determineNewValues(currentValues: List): List { + return if (currentValues.size == 1) { + currentValues + } else { + currentValues.drop(1) + } + } + + private fun retrieveValue(): Value { + val currentValues = _getMany.value + val value = currentValues!!.first() + + val newValues = determineNewValues(currentValues) + + _getMany.compareAndSet(currentValues, newValues) + + return value + } + + private fun incrementInvocations() { + val calls = this._calls.get() + + this._calls.compareAndSet( + calls, + calls + 1 + ) + } + + private fun captureArguments(argument: GetOrSet) { + this.arguments.access { it.add(argument) } + } + + private fun notifyCollector() { + collector.get().addReference( + this, + this._calls.get() + ) + } + + private fun onEvent(argument: GetOrSet) { + captureArguments(argument) + notifyCollector() + incrementInvocations() + } + + override fun onGet(): Value { + onEvent(GetOrSet.Get) + + return when (provider.get()) { + false -> _get.get()!! + true -> retrieveValue() + else -> throw MockError.MissingStub("Missing stub value for $id") + } + } + + override fun onSet(value: Value) { + onEvent(GetOrSet.Set(value)) + + set(value) + } + + override fun getArgumentsForCall(callIndex: Int): GetOrSet = arguments.access { it[callIndex] } +} diff --git a/kmock/src/commonMain/kotlin/tech/antibytes/kmock/SyncFunMockery.kt b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/SyncFunMockery.kt new file mode 100644 index 00000000..14965e01 --- /dev/null +++ b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/SyncFunMockery.kt @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import tech.antibytes.kmock.KMockContract.Collector +import tech.antibytes.util.test.MockError + +class SyncFunMockery>( + id: String, + collector: Collector = Collector { _, _ -> Unit } +) : KMockContract.SyncFunMockery, FunMockery(id, collector) { + private fun execute( + function: () -> ReturnValue, + vararg arguments: Any? + ): ReturnValue { + onEvent(arguments) + + return when (provider.get()) { + PROVIDER.RETURN_VALUE -> retrieveValue() + PROVIDER.RETURN_VALUES -> retrieveFromValues() + PROVIDER.SIDE_EFFECT -> function() + else -> throw MockError.MissingStub("Missing stub value for $id") + } + } + + @Suppress("UNCHECKED_CAST") + override fun invoke(): ReturnValue { + val invocation = { + (retrieveSideEffect() as () -> ReturnValue) + .invoke() + } + + return execute(invocation) + } + + @Suppress("UNCHECKED_CAST") + override fun invoke(arg0: Arg0): ReturnValue { + val invocation = { + (retrieveSideEffect() as (Arg0) -> ReturnValue) + .invoke(arg0) + } + + return execute(invocation, arg0) + } + + @Suppress("UNCHECKED_CAST") + override fun invoke(arg0: Arg0, arg1: Arg1): ReturnValue { + val invocation = { + (retrieveSideEffect() as (Arg0, Arg1) -> ReturnValue) + .invoke(arg0, arg1) + } + + return execute(invocation, arg0, arg1) + } + + @Suppress("UNCHECKED_CAST") + override fun invoke(arg0: Arg0, arg1: Arg1, arg2: Arg2): ReturnValue { + val invocation = { + (retrieveSideEffect() as (Arg0, Arg1, Arg2) -> ReturnValue) + .invoke(arg0, arg1, arg2) + } + + return execute(invocation, arg0, arg1, arg2) + } + + @Suppress("UNCHECKED_CAST") + override fun invoke(arg0: Arg0, arg1: Arg1, arg2: Arg2, arg3: Arg3): ReturnValue { + val invocation = { + (retrieveSideEffect() as (Arg0, Arg1, Arg2, Arg3) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3) + } + + return execute(invocation, arg0, arg1, arg2, arg3) + } + + @Suppress("UNCHECKED_CAST") + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4 + ): ReturnValue { + val invocation = { + (retrieveSideEffect() as (Arg0, Arg1, Arg2, Arg3, Arg4) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4) + } + + @Suppress("UNCHECKED_CAST") + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5 + ): ReturnValue { + val invocation = { + (retrieveSideEffect() as (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5) + } + + @Suppress("UNCHECKED_CAST") + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6 + ): ReturnValue { + val invocation = { + (retrieveSideEffect() as (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5, arg6) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5, arg6) + } + + @Suppress("UNCHECKED_CAST") + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7 + ): ReturnValue { + val invocation = { + (retrieveSideEffect() as (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) + } + + @Suppress("UNCHECKED_CAST") + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8 + ): ReturnValue { + val invocation = { + (retrieveSideEffect() as (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) + } + + @Suppress("UNCHECKED_CAST") + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9 + ): ReturnValue { + val invocation = { + (retrieveSideEffect() as (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + } + + @Suppress("UNCHECKED_CAST") + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10 + ): ReturnValue { + val invocation = { + (retrieveSideEffect() as (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9, Arg10) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) + } + + @Suppress("UNCHECKED_CAST") + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10, + arg11: Arg11 + ): ReturnValue { + val invocation = { + (retrieveSideEffect() as (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9, Arg10, Arg11) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11) + } + + @Suppress("UNCHECKED_CAST") + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10, + arg11: Arg11, + arg12: Arg12 + ): ReturnValue { + val invocation = { + (retrieveSideEffect() as (Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9, Arg10, Arg11, Arg12) -> ReturnValue) + .invoke(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12) + } + + return execute(invocation, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12) + } +} diff --git a/kmock/src/commonMain/kotlin/tech/antibytes/kmock/Verification.kt b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/Verification.kt new file mode 100644 index 00000000..1cd7ef24 --- /dev/null +++ b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/Verification.kt @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import tech.antibytes.kmock.KMockContract.Companion.CALL_NOT_FOUND +import tech.antibytes.kmock.KMockContract.Companion.MISMATCHING_CALL_IDX +import tech.antibytes.kmock.KMockContract.Companion.MISMATCHING_FUNCTION +import tech.antibytes.kmock.KMockContract.Companion.NOTHING_TO_STRICTLY_VERIFY +import tech.antibytes.kmock.KMockContract.Companion.NOTHING_TO_VERIFY +import tech.antibytes.kmock.KMockContract.Companion.NOT_CALLED +import tech.antibytes.kmock.KMockContract.Companion.NO_MATCHING_CALL_IDX +import tech.antibytes.kmock.KMockContract.Companion.TOO_LESS_CALLS +import tech.antibytes.kmock.KMockContract.Companion.TOO_MANY_CALLS +import tech.antibytes.kmock.KMockContract.Reference +import tech.antibytes.kmock.KMockContract.VerificationHandle +import tech.antibytes.kmock.KMockContract.Verifier + +private fun formatMessage( + message: String, + actual: Int, + expected: Int +): String = formatMessage(message, actual.toString(), expected.toString()) + +private fun formatMessage( + message: String, + actual: Int, + expected: String +): String = formatMessage(message, actual.toString(), expected) + +private fun formatMessage( + message: String, + arg: String +): String = formatMessage(message, "", arg) + +private fun formatMessage(message: String, actual: String, expected: String): String { + return message + .replace("\$1", expected) + .replace("\$2", actual) +} + +private fun determineAtLeastMessage(actual: Int, expected: Int): String { + return if (actual == 0) { + NOT_CALLED + } else { + formatMessage(TOO_LESS_CALLS, actual, expected) + } +} + +private infix fun VerificationHandle.mustBeAtLeast(value: Int) { + if (this.callIndices.size < value) { + val message = determineAtLeastMessage(this.callIndices.size, value) + + throw AssertionError(message) + } +} + +private infix fun VerificationHandle.mustBeAtMost(value: Int) { + if (this.callIndices.size > value) { + val message = formatMessage(TOO_MANY_CALLS, this.callIndices.size, value) + + throw AssertionError(message) + } +} + +fun verify( + exactly: Int? = null, + atLeast: Int = 1, + atMost: Int? = null, + action: () -> VerificationHandle +) { + val handle = action() + val minimum = exactly ?: atLeast + val maximum = exactly ?: atMost + + handle mustBeAtLeast minimum + + if (maximum != null) { + handle mustBeAtMost maximum + } +} + +private fun initChainVerification( + scope: VerificationChainBuilder.() -> Unit +): List { + val container = VerificationChainBuilder() + + scope(container) + + return container.toList() +} + +private fun guardStrictChain(references: List, handles: List) { + if (handles.size != references.size) { + val message = formatMessage(NOTHING_TO_STRICTLY_VERIFY, handles.size, references.size) + + throw AssertionError(message) + } +} + +private fun scanHandleStrictly( + latestCall: Int, + applicableIdx: List +): Int? { + val next = latestCall + 1 + return applicableIdx.firstOrNull { value -> value == next } +} + +private fun evaluateStrictReference( + reference: Reference, + functionName: String, + call: Int? +) { + if (reference.mockery.id != functionName) { + val message = formatMessage(MISMATCHING_FUNCTION, reference.mockery.id, functionName) + throw AssertionError(message) + } + + if (call == null) { + val message = formatMessage( + NO_MATCHING_CALL_IDX, + reference.mockery.id + ) + + throw AssertionError(message) + } + + if (reference.callIndex != call) { + val message = formatMessage( + MISMATCHING_CALL_IDX, + reference.callIndex, + "$call of ${reference.mockery.id}" + ) + + throw AssertionError(message) + } +} + +fun Verifier.verifyStrictOrder( + scope: VerificationChainBuilder.() -> Unit +) { + val handleCalls: MutableMap = mutableMapOf() + val handles = initChainVerification(scope) + + guardStrictChain(this.references, handles) + + this.references.forEachIndexed { idx, reference -> + val functionName = handles[idx].id + val lastCall = handleCalls[functionName] ?: -1 + val call = scanHandleStrictly(lastCall, handles[idx].callIndices) + + evaluateStrictReference(reference, functionName, call) + + handleCalls[functionName] = call ?: Int.MAX_VALUE + } +} + +private fun guardChain(references: List, handles: List) { + if (handles.size > references.size) { + val message = formatMessage(NOTHING_TO_VERIFY, handles.size, references.size) + + throw AssertionError(message) + } +} + +private fun scanHandle( + latestCall: Int, + applicableIdx: List +): Int? = applicableIdx.firstOrNull { value -> value > latestCall } + +private fun evaluateReference( + reference: Reference, + functionName: String, + call: Int? +): Boolean { + return when { + call == null -> false + reference.mockery.id != functionName -> false + reference.callIndex != call -> false + else -> true + } +} + +private fun ensureAllHandlesAreDone( + handles: List, + handleOffset: Int +) { + if (handleOffset != handles.size) { + val message = formatMessage( + CALL_NOT_FOUND, + handles[handleOffset].id + ) + + throw AssertionError(message) + } +} + +fun Verifier.verifyOrder( + scope: VerificationChainBuilder.() -> Unit +) { + val handleCalls: MutableMap = mutableMapOf() + val handles = initChainVerification(scope) + var handleOffset = 0 + + guardChain(this.references, handles) + + this.references.forEach { reference -> + val functionName = handles[handleOffset].id + val lastCall = handleCalls[functionName] ?: -1 + val call = scanHandle(lastCall, handles[handleOffset].callIndices) + + if (evaluateReference(reference, functionName, call)) { + handleCalls[functionName] = call!! + handleOffset += 1 + } + + if (handleOffset == handles.size) { + return@forEach + } + } + + ensureAllHandlesAreDone(handles, handleOffset) +} diff --git a/kmock/src/commonMain/kotlin/tech/antibytes/kmock/VerificationChainBuilder.kt b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/VerificationChainBuilder.kt new file mode 100644 index 00000000..c9596f04 --- /dev/null +++ b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/VerificationChainBuilder.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import tech.antibytes.kmock.KMockContract.FunMockery +import tech.antibytes.kmock.KMockContract.VerificationHandle + +class VerificationChainBuilder : KMockContract.VerificationHandleContainer { + private val handles = mutableListOf() + + override fun add(handle: VerificationHandle) { + handles.add(handle) + } + + override fun toList(): List = handles.toList() +} + +fun VerificationChainBuilder.withArguments(mockery: FunMockery<*, *>, vararg arguments: Any?) { + this.add(mockery.withArguments(*arguments)) +} + +fun VerificationChainBuilder.withSameArguments(mockery: FunMockery<*, *>, vararg arguments: Any?) { + this.add(mockery.withSameArguments(*arguments)) +} + +fun VerificationChainBuilder.withoutArguments(mockery: FunMockery<*, *>, vararg arguments: Any?) { + this.add(mockery.withoutArguments(*arguments)) +} + +fun VerificationChainBuilder.wasGotten(mockery: KMockContract.PropMockery<*>) { + this.add(mockery.wasGotten()) +} + +fun VerificationChainBuilder.wasSet(mockery: KMockContract.PropMockery<*>) { + this.add(mockery.wasSet()) +} + +fun VerificationChainBuilder.wasSetTo(mockery: KMockContract.PropMockery<*>, value: Any?) { + this.add(mockery.wasSetTo(value)) +} diff --git a/kmock/src/commonMain/kotlin/tech/antibytes/kmock/ExampleClass.kt b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/VerificationHandle.kt similarity index 54% rename from kmock/src/commonMain/kotlin/tech/antibytes/kmock/ExampleClass.kt rename to kmock/src/commonMain/kotlin/tech/antibytes/kmock/VerificationHandle.kt index ef023db0..d0bca2e0 100644 --- a/kmock/src/commonMain/kotlin/tech/antibytes/kmock/ExampleClass.kt +++ b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/VerificationHandle.kt @@ -6,6 +6,7 @@ package tech.antibytes.kmock -class ExampleClass { - fun doSomething() = "something" -} +data class VerificationHandle( + override val id: String, + override val callIndices: List +) : KMockContract.VerificationHandle diff --git a/kmock/src/commonMain/kotlin/tech/antibytes/kmock/VerificationHandleFactory.kt b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/VerificationHandleFactory.kt new file mode 100644 index 00000000..112f2260 --- /dev/null +++ b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/VerificationHandleFactory.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import tech.antibytes.kmock.KMockContract.FunMockery +import tech.antibytes.kmock.KMockContract.Mockery +import tech.antibytes.kmock.KMockContract.PropMockery +import tech.antibytes.kmock.KMockContract.VerificationHandle + +private fun traverseMock( + mock: Mockery<*, T>, + action: T.() -> Boolean +): VerificationHandle { + val callIndices = mutableListOf() + + for (idx in 0 until mock.calls) { + if (action(mock.getArgumentsForCall(idx))) { + callIndices.add(idx) + } + } + + return VerificationHandle(mock.id, callIndices) +} + +fun FunMockery<*, *>.withArguments( + vararg values: Any? +): VerificationHandle = traverseMock(this) { withArguments(*values) } + +fun FunMockery<*, *>.withSameArguments( + vararg values: Any? +): VerificationHandle = traverseMock(this) { withSameArguments(*values) } + +fun FunMockery<*, *>.withoutArguments( + vararg values: Any? +): VerificationHandle = traverseMock(this) { withoutArguments(*values) } + +fun PropMockery<*>.wasGotten(): VerificationHandle = traverseMock(this) { wasGotten() } + +fun PropMockery<*>.wasSet(): VerificationHandle = traverseMock(this) { wasSet() } + +fun PropMockery<*>.wasSetTo( + value: Any? +): VerificationHandle = traverseMock(this) { wasSetTo(value) } diff --git a/kmock/src/commonMain/kotlin/tech/antibytes/kmock/Verifier.kt b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/Verifier.kt new file mode 100644 index 00000000..d4b42490 --- /dev/null +++ b/kmock/src/commonMain/kotlin/tech/antibytes/kmock/Verifier.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import co.touchlab.stately.isolate.IsolateState +import tech.antibytes.kmock.KMockContract.Mockery +import tech.antibytes.kmock.KMockContract.Reference + +class Verifier : KMockContract.Verifier, KMockContract.Collector { + private val _references: IsolateState> = IsolateState { mutableListOf() } + + override val references: List + get() = _references.access { it.toList() } + + override fun addReference(referredMock: Mockery<*, *>, referredCall: Int) { + _references.access { references -> + references.add(Reference(referredMock, referredCall)) + } + } +} diff --git a/kmock/src/commonTest/kotlin/tech/antibytes/kmock/ArgumentMatcherSpec.kt b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/ArgumentMatcherSpec.kt new file mode 100644 index 00000000..f8e3be10 --- /dev/null +++ b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/ArgumentMatcherSpec.kt @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import tech.antibytes.util.test.fixture.fixture +import tech.antibytes.util.test.fixture.kotlinFixture +import tech.antibytes.util.test.fixture.listFixture +import tech.antibytes.util.test.mustBe +import kotlin.js.JsName +import kotlin.test.Test + +class ArgumentMatcherSpec { + private val fixture = kotlinFixture() + + @Test + @JsName("fn0") + fun `Given withArguments is called with an Argument it returns false if the Array contains not the given Argument`() { + // Given + val array = fixture.listFixture().toTypedArray() + + // When + val actual = array.withArguments(fixture.fixture()) + + // Then + actual mustBe false + } + + @Test + @JsName("fn1") + fun `Given withArguments is called with an Argument it returns false if the Array contains the given Argument`() { + // Given + val array = fixture.listFixture().toTypedArray() + + // When + val actual = array.withArguments(array.first()) + + // Then + actual mustBe true + } + + @Test + @JsName("fn2") + fun `Given withArguments is called without Arguments void it returns true if the Array contains Arguments`() { + // Given + val array = fixture.listFixture().toTypedArray() + + // When + val actual = array.withArguments() + + // Then + actual mustBe true + } + + @Test + @JsName("fn3") + fun `Given withArguments is called without Arguments void it returns true if the Array is null`() { + // Given + val array = null + + // When + val actual = array.withArguments() + + // Then + actual mustBe true + } + + @Test + @JsName("fn4") + fun `Given withArguments is called with Arguments it returns false if the Array is null`() { + // Given + val array = null + + // When + val actual = array.withArguments(fixture.listFixture(), fixture.listFixture()) + + // Then + actual mustBe false + } + + @Test + @JsName("fn5") + fun `Given withArguments is called with Arguments it returns false if the Array contains not all of given Arguments`() { + // Given + val array = fixture.listFixture(size = 8).toTypedArray() + + // When + val actual = array.withArguments(array[0], array[1], array[2], fixture.fixture()) + + // Then + actual mustBe false + } + + @Test + @JsName("fn6") + fun `Given withArguments is called with Arguments it returns true if the Array contains all of given Arguments`() { + // Given + val array = fixture.listFixture(size = 8).toTypedArray() + + // When + val actual = array.withArguments(array[0], array[1], array[2], array[3]) + + // Then + actual mustBe true + } + + @Test + @JsName("fn7") + fun `Given withSameArguments is called with an Argument it returns false if the Array matches not the given Argument`() { + // Given + val array = fixture.listFixture().toTypedArray() + + // When + val actual = array.withSameArguments(fixture.fixture()) + + // Then + actual mustBe false + } + + @Test + @JsName("fn8") + fun `Given withSameArguments is called with an Argument it returns true if the Array matches the given Argument`() { + // Given + val array = fixture.listFixture(size = 1).toTypedArray() + + // When + val actual = array.withSameArguments(array.first()) + + // Then + actual mustBe true + } + + @Test + @JsName("fn9") + fun `Given withSameArguments is called without Arguments void it returns false if the Array has Arguments`() { + // Given + val array = fixture.listFixture(size = 1).toTypedArray() + + // When + val actual = array.withSameArguments() + + // Then + actual mustBe false + } + + @Test + @JsName("fn10") + fun `Given withSameArguments is called witg Arguments void it returns false if the Array is null`() { + // Given + val array = null + + // When + val actual = array.withSameArguments(fixture.fixture()) + + // Then + actual mustBe false + } + + @Test + @JsName("fn11") + fun `Given withSameArguments is called without Arguments void it returns false if the Array is null`() { + // Given + val array = null + + // When + val actual = array.withSameArguments() + + // Then + actual mustBe true + } + + @Test + @JsName("fn12") + fun `Given withSameArguments is called with Arguments it returns false if the Array matches not exactly the given Arguments`() { + // Given + val array = fixture.listFixture(size = 8).toTypedArray() + + // When + val actual = array.withSameArguments(array[0], array[1], array[2], array[3]) + + // Then + actual mustBe false + } + + @Test + @JsName("fn13") + fun `Given withSameArguments is called with Arguments it returns true if the Array matches exactly the given Arguments`() { + // Given + val array = fixture.listFixture(size = 8).toTypedArray() + + // When + val actual = array.withSameArguments(*array) + + // Then + actual mustBe true + } + + @Test + @JsName("fn14") + fun `Given withoutArgument is called with an Argument it returns true if the Array contains not the given Argument`() { + // Given + val array = fixture.listFixture().toTypedArray() + + // When + val actual = array.withoutArguments(fixture.fixture()) + + // Then + actual mustBe true + } + + @Test + @JsName("fn15") + fun `Given withoutArgument is called with an Argument it returns false if the Array contains the given Argument`() { + // Given + val array = fixture.listFixture().toTypedArray() + + // When + val actual = array.withoutArguments(array.first()) + + // Then + actual mustBe false + } + + @Test + @JsName("fn16") + fun `Given withoutArgument is called without Argument it returns true if the Array contains Argument`() { + // Given + val array = fixture.listFixture().toTypedArray() + + // When + val actual = array.withoutArguments() + + // Then + actual mustBe true + } + + @Test + @JsName("fn17") + fun `Given withoutArgument is called with Arguments it returns false if the Array is null`() { + // Given + val array = fixture.listFixture().toTypedArray() + + // When + val actual = array.withoutArguments() + + // Then + actual mustBe true + } + + @Test + @JsName("fn18") + fun `Given withoutArgument is called with Arguments it returns true if the Array none of given Arguments matches`() { + // Given + val array = fixture.listFixture(size = 8).toTypedArray() + + // When + val actual = + array.withoutArguments(fixture.fixture(), fixture.fixture(), fixture.fixture()) + + // Then + actual mustBe true + } + + @Test + @JsName("fn19") + fun `Given withoutArgument is called with Arguments it returns false if the Array matches at least one of given Arguments`() { + // Given + val array = fixture.listFixture(size = 8).toTypedArray() + + // When + val actual = array.withoutArguments( + array[0], + fixture.fixture(), + fixture.fixture(), + fixture.fixture() + ) + + // Then + actual mustBe false + } + + @Test + @JsName("fn20") + fun `Given wasGotten is called it returns false if it is attacht to Set`() { + KMockContract.GetOrSet.Set(null).wasGotten() mustBe false + } + + @Test + @JsName("fn21") + fun `Given wasGotten is called it returns true if it is attacht to Get`() { + KMockContract.GetOrSet.Get.wasGotten() mustBe true + } + + @Test + @JsName("fn22") + fun `Given wasSet is called it returns false if it is attacht to Get`() { + KMockContract.GetOrSet.Get.wasSet() mustBe false + } + + @Test + @JsName("fn23") + fun `Given wasGotten is called it returns true if it is attacht to Set`() { + KMockContract.GetOrSet.Set(null).wasSet() mustBe true + } + + @Test + @JsName("fn24") + fun `Given wasSetTo is called it returns false if it is attacht to Get`() { + KMockContract.GetOrSet.Get.wasSetTo(null) mustBe false + } + + @Test + @JsName("fn25") + fun `Given wasSetTo is called it returns false if the values do not match`() { + KMockContract.GetOrSet.Set(fixture.fixture()).wasSetTo(fixture.fixture()) mustBe false + } + + @Test + @JsName("fn26") + fun `Given wasSetTo is called it returns true if the values do math`() { + val value: Any = fixture.fixture() + KMockContract.GetOrSet.Set(value).wasSetTo(value) mustBe true + } +} diff --git a/kmock/src/commonTest/kotlin/tech/antibytes/kmock/AsyncFunMockerySpec.kt b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/AsyncFunMockerySpec.kt new file mode 100644 index 00000000..8b297393 --- /dev/null +++ b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/AsyncFunMockerySpec.kt @@ -0,0 +1,1386 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import co.touchlab.stately.concurrency.AtomicReference +import tech.antibytes.kmock.KMockContract.Collector +import tech.antibytes.util.test.MockError +import tech.antibytes.util.test.coroutine.AsyncTestReturnValue +import tech.antibytes.util.test.coroutine.TestScopeDispatcher +import tech.antibytes.util.test.coroutine.asyncMultiBlock +import tech.antibytes.util.test.coroutine.runBlockingTest +import tech.antibytes.util.test.coroutine.runBlockingTestInContext +import tech.antibytes.util.test.coroutine.runBlockingTestWithTimeoutInScope +import tech.antibytes.util.test.fixture.fixture +import tech.antibytes.util.test.fixture.kotlinFixture +import tech.antibytes.util.test.fixture.listFixture +import tech.antibytes.util.test.fulfils +import tech.antibytes.util.test.mustBe +import tech.antibytes.util.test.sameAs +import kotlin.js.JsName +import kotlin.test.Test +import kotlin.test.assertFailsWith + +class AsyncFunMockerySpec { + private val fixture = kotlinFixture() + private val testScope1 = TestScopeDispatcher.dispatch("test1") + private val testScope2 = TestScopeDispatcher.dispatch("test2") + + @Test + @JsName("fn0") + fun `It fulfils FunMockery`() { + AsyncFunMockery Unit>(fixture.fixture()) fulfils KMockContract.FunMockery::class + } + + @Test + @JsName("fn0_a") + fun `It fulfils AsyncFunMockery`() { + AsyncFunMockery Unit>(fixture.fixture()) fulfils KMockContract.AsyncFunMockery::class + } + + @Test + @JsName("fn1") + fun `Given a returnValue is set it is threadsafe retrievable`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val value: Any = fixture.fixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValue = value + } + + // Then + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.returnValue mustBe value + } + + return asyncMultiBlock + } + + @Test + @JsName("fn2") + fun `Given a returnValue is set with nullable value it is threadsafe retrievable`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any?>(fixture.fixture()) + val value: Any? = null + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValue = value + } + + // Then + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.returnValue mustBe value + } + + return asyncMultiBlock + } + + @Test + @JsName("fn3") + fun `Given a returnValues is set with an emptyList it fails`() { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + + // Then + val error = assertFailsWith { + mockery.returnValues = emptyList() + } + + error.message mustBe "Empty Lists are not valid as value provider." + } + + @Test + @JsName("fn4") + fun `Given a returnValues is set it is threadsafe retrievable`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val values: List = fixture.listFixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValues = values + } + + // Then + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.returnValues mustBe values + } + + return asyncMultiBlock + } + + @Test + @JsName("fn5") + fun `Given a sideEffect is set it is threadsafe retrievable`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val effect: suspend () -> Any = { fixture.fixture() } + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = effect + } + + // Then + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.sideEffect sameAs effect + } + + return asyncMultiBlock + } + + @Test + @JsName("fn6") + fun `Given invoke is called it fails if no ReturnValue Provider is set`(): AsyncTestReturnValue { + // Given + val name: String = fixture.fixture() + val mockery = AsyncFunMockery Any>(name) + + return runBlockingTestInContext(testScope1.coroutineContext) { + // Then + val error = assertFailsWith { + // When + mockery.invoke() + } + + error.message mustBe "Missing stub value for $name" + } + } + + @Test + @JsName("fn7") + fun `Given invoke is called it returns the ReturnValue threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val value: String = fixture.fixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValue = value + } + + runBlockingTestInContext(testScope2.coroutineContext) { + val actual = mockery.invoke() + + // Then + actual mustBe value + } + + return asyncMultiBlock + } + + @Test + @JsName("fn8") + fun `Given invoke is called it returns the ReturnValues threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val values: List = fixture.listFixture(size = 5) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValues = values.toList() + } + + runBlockingTestInContext(testScope2.coroutineContext) { + values.forEach { value -> + val actual = mockery.invoke() + + // Then + actual mustBe value + } + } + + return asyncMultiBlock + } + + @Test + @JsName("fn9") + fun `Given invoke is called it returns the last ReturnValue if the given List is down to one value threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val values: List = fixture.listFixture(size = 1) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValues = values.toList() + } + + runBlockingTestInContext(testScope2.coroutineContext) { + for (x in 0 until 10) { + val actual = mockery.invoke() + + // Then + actual mustBe values.first() + } + } + + return asyncMultiBlock + } + + @Test + @JsName("fn10") + fun `Given invoke is called it calls the given SideEffect and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = AtomicReference(null) + val actualArgument1 = AtomicReference(null) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1 -> + actualArgument0.set(givenArg0) + actualArgument1.set(givenArg1) + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + val actual = mockery.invoke(argument0, argument1) + + // Then + actual mustBe expected + actualArgument0.get() mustBe argument0 + actualArgument1.get() mustBe argument1 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn11") + fun `Given invoke is called it uses ReturnValues over ReturnValue`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val value: Any = fixture.fixture() + val values: List = fixture.listFixture(size = 2) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValue = value + mockery.returnValues = values + } + + runBlockingTestInContext(testScope2.coroutineContext) { + val actual = mockery.invoke() + + // Then + actual mustBe values.first() + } + + return asyncMultiBlock + } + + @Test + @JsName("fn12") + fun `Given invoke is called it uses SideEffect over ReturnValues`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val expected: Any = fixture.fixture() + val values: List = fixture.listFixture(size = 2) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { expected } + mockery.returnValues = values + } + + runBlockingTestInContext(testScope2.coroutineContext) { + val actual = mockery.invoke() + + // Then + actual mustBe expected + } + + return asyncMultiBlock + } + + @Test + @JsName("fn13") + fun `Given invoke is called it captures Arguments threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val values: List = fixture.listFixture(size = 5) + val argument: String = fixture.fixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValues = values.toList() + } + + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.invoke(argument) + } + + runBlockingTest { + val actual = mockery.getArgumentsForCall(0) + + actual!!.size mustBe 1 + actual[0] mustBe argument + } + + return asyncMultiBlock + } + + @Test + @JsName("fn14") + fun `Given invoke is called it captures void Arguments threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val values: List = fixture.listFixture(size = 5) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValues = values + } + + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.invoke() + } + + runBlockingTest { + val actual = mockery.getArgumentsForCall(0) + + actual mustBe null + } + + return asyncMultiBlock + } + + @Test + @JsName("fn15") + fun `It reflects the given id`() { + // Given + val name: String = fixture.fixture() + + // When + val actual = AsyncFunMockery Any>(name).id + + // Then + actual mustBe name + } + + @Test + @JsName("fn16") + fun `Its default call count is 0`() { + AsyncFunMockery Any>(fixture.fixture()).calls mustBe 0 + } + + @Test + @JsName("fn17") + fun `Given invoke is called it increments the call counter threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val values: List = fixture.listFixture(size = 5) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValues = values + } + + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.invoke() + } + + runBlockingTest { + mockery.calls mustBe 1 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn18") + fun `Given the mockery has a Collector and invoke is called it calls the Collect`(): AsyncTestReturnValue { + // Given + val values: List = fixture.listFixture(size = 5) + + val capturedMock = AtomicReference?>(null) + val capturedCalledIdx = AtomicReference(null) + + val collector = Collector { referredMock, referredCall -> + capturedMock.set(referredMock) + capturedCalledIdx.set(referredCall) + } + + // When + val mockery = AsyncFunMockery Any>(fixture.fixture(), collector) + + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValues = values + } + + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.invoke() + } + + runBlockingTest { + capturedMock.get()?.id mustBe mockery.id + capturedCalledIdx.get() mustBe 0 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn34") + fun `Given invoke is called it calls the given SideEffect with 0 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val expected: Any = fixture.fixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { + expected + } + } + + runBlockingTestWithTimeoutInScope(testScope2.coroutineContext) { + val actual = mockery.invoke() + + // Then + actual mustBe expected + mockery.getArgumentsForCall(0) mustBe null + } + + return asyncMultiBlock + } + + @Test + @JsName("fn35") + fun `Given invoke is called it calls the given SideEffect with 1 Argument and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val expected: Any = fixture.fixture() + + val actualArgument0 = AtomicReference(null) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0 -> + actualArgument0.set(givenArg0) + + expected + } + } + + runBlockingTestWithTimeoutInScope(testScope2.coroutineContext) { + // When + val actual = mockery.invoke(argument0) + + // Then + actual mustBe expected + actualArgument0.get() mustBe argument0 + + val arguments = mockery.getArgumentsForCall(0) + arguments?.size mustBe 1 + arguments!![0] mustBe argument0 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn36") + fun `Given invoke is called it calls the given SideEffect with 2 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val expected: Any = fixture.fixture() + + val actualArgument0 = AtomicReference(null) + val actualArgument1 = AtomicReference(null) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1 -> + + actualArgument0.set(givenArg0) + actualArgument1.set(givenArg1) + + expected + } + } + + runBlockingTestWithTimeoutInScope(testScope2.coroutineContext) { + // When + val actual = mockery.invoke(argument0, argument1) + + // Then + actual mustBe expected + actualArgument0.get() mustBe argument0 + actualArgument1.get() mustBe argument1 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 2 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn37") + fun `Given invoke is called it calls the given SideEffect with 3 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val expected: Any = fixture.fixture() + + val actualArgument0 = AtomicReference(null) + val actualArgument1 = AtomicReference(null) + val actualArgument2 = AtomicReference(null) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2 -> + actualArgument0.set(givenArg0) + actualArgument1.set(givenArg1) + actualArgument2.set(givenArg2) + + expected + } + } + + runBlockingTestWithTimeoutInScope(testScope2.coroutineContext) { + // When + val actual = mockery.invoke(argument0, argument1, argument2) + + // Then + actual mustBe expected + actualArgument0.get() mustBe argument0 + actualArgument1.get() mustBe argument1 + actualArgument2.get() mustBe argument2 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 3 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn38") + fun `Given invoke is called it calls the given SideEffect with 4 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val expected: Any = fixture.fixture() + + val actualArgument0 = AtomicReference(null) + val actualArgument1 = AtomicReference(null) + val actualArgument2 = AtomicReference(null) + val actualArgument3 = AtomicReference(null) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3 -> + actualArgument0.set(givenArg0) + actualArgument1.set(givenArg1) + actualArgument2.set(givenArg2) + actualArgument3.set(givenArg3) + + expected + } + } + + runBlockingTestWithTimeoutInScope(testScope2.coroutineContext) { + // When + val actual = mockery.invoke(argument0, argument1, argument2, argument3) + + // Then + actual mustBe expected + actualArgument0.get() mustBe argument0 + actualArgument1.get() mustBe argument1 + actualArgument2.get() mustBe argument2 + actualArgument3.get() mustBe argument3 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 4 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn39") + fun `Given invoke is called it calls the given SideEffect with 5 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = AtomicReference(null) + val actualArgument1 = AtomicReference(null) + val actualArgument2 = AtomicReference(null) + val actualArgument3 = AtomicReference(null) + val actualArgument4 = AtomicReference(null) + + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4 -> + actualArgument0.set(givenArg0) + actualArgument1.set(givenArg1) + actualArgument2.set(givenArg2) + actualArgument3.set(givenArg3) + actualArgument4.set(givenArg4) + + expected + } + } + + runBlockingTest { + // When + val actual = mockery.invoke(argument0, argument1, argument2, argument3, argument4) + actual mustBe expected + + // Then + actualArgument0.get() mustBe argument0 + actualArgument1.get() mustBe argument1 + actualArgument2.get() mustBe argument2 + actualArgument3.get() mustBe argument3 + actualArgument4.get() mustBe argument4 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 5 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn40") + fun `Given invoke is called it calls the given SideEffect with 6 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = AtomicReference(null) + val actualArgument1 = AtomicReference(null) + val actualArgument2 = AtomicReference(null) + val actualArgument3 = AtomicReference(null) + val actualArgument4 = AtomicReference(null) + val actualArgument5 = AtomicReference(null) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5 -> + actualArgument0.set(givenArg0) + actualArgument1.set(givenArg1) + actualArgument2.set(givenArg2) + actualArgument3.set(givenArg3) + actualArgument4.set(givenArg4) + actualArgument5.set(givenArg5) + + expected + } + } + + runBlockingTestWithTimeoutInScope(testScope2.coroutineContext) { + // When + val actual = mockery.invoke(argument0, argument1, argument2, argument3, argument4, argument5) + + // Then + actual mustBe expected + actualArgument0.get() mustBe argument0 + actualArgument1.get() mustBe argument1 + actualArgument2.get() mustBe argument2 + actualArgument3.get() mustBe argument3 + actualArgument4.get() mustBe argument4 + actualArgument5.get() mustBe argument5 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 6 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn41") + fun `Given invoke is called it calls the given SideEffect with 7 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + val argument6: String = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = AtomicReference(null) + val actualArgument1 = AtomicReference(null) + val actualArgument2 = AtomicReference(null) + val actualArgument3 = AtomicReference(null) + val actualArgument4 = AtomicReference(null) + val actualArgument5 = AtomicReference(null) + val actualArgument6 = AtomicReference(null) + + // When + runBlockingTestWithTimeoutInScope(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5, givenArg6 -> + actualArgument0.set(givenArg0) + actualArgument1.set(givenArg1) + actualArgument2.set(givenArg2) + actualArgument3.set(givenArg3) + actualArgument4.set(givenArg4) + actualArgument5.set(givenArg5) + actualArgument6.set(givenArg6) + + expected + } + } + + runBlockingTestWithTimeoutInScope(testScope2.coroutineContext) { + // When + val actual = mockery.invoke(argument0, argument1, argument2, argument3, argument4, argument5, argument6) + + // Then + actual mustBe expected + actualArgument0.get() mustBe argument0 + actualArgument1.get() mustBe argument1 + actualArgument2.get() mustBe argument2 + actualArgument3.get() mustBe argument3 + actualArgument4.get() mustBe argument4 + actualArgument5.get() mustBe argument5 + actualArgument6.get() mustBe argument6 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 7 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + arguments[6] mustBe argument6 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn42") + fun `Given invoke is called it calls the given SideEffect with 8 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + val argument6: String = fixture.fixture() + val argument7: Int = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = AtomicReference(null) + val actualArgument1 = AtomicReference(null) + val actualArgument2 = AtomicReference(null) + val actualArgument3 = AtomicReference(null) + val actualArgument4 = AtomicReference(null) + val actualArgument5 = AtomicReference(null) + val actualArgument6 = AtomicReference(null) + val actualArgument7 = AtomicReference(null) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5, givenArg6, givenArg7 -> + actualArgument0.set(givenArg0) + actualArgument1.set(givenArg1) + actualArgument2.set(givenArg2) + actualArgument3.set(givenArg3) + actualArgument4.set(givenArg4) + actualArgument5.set(givenArg5) + actualArgument6.set(givenArg6) + actualArgument7.set(givenArg7) + + expected + } + } + + runBlockingTestWithTimeoutInScope(testScope2.coroutineContext) { + // When + val actual = mockery.invoke( + argument0, + argument1, + argument2, + argument3, + argument4, + argument5, + argument6, + argument7 + ) + + // Then + actual mustBe expected + actualArgument0.get() mustBe argument0 + actualArgument1.get() mustBe argument1 + actualArgument2.get() mustBe argument2 + actualArgument3.get() mustBe argument3 + actualArgument4.get() mustBe argument4 + actualArgument5.get() mustBe argument5 + actualArgument6.get() mustBe argument6 + actualArgument7.get() mustBe argument7 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 8 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + arguments[6] mustBe argument6 + arguments[7] mustBe argument7 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn43") + fun `Given invoke is called it calls the given SideEffect with 9 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + val argument6: String = fixture.fixture() + val argument7: Int = fixture.fixture() + val argument8: String = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = AtomicReference(null) + val actualArgument1 = AtomicReference(null) + val actualArgument2 = AtomicReference(null) + val actualArgument3 = AtomicReference(null) + val actualArgument4 = AtomicReference(null) + val actualArgument5 = AtomicReference(null) + val actualArgument6 = AtomicReference(null) + val actualArgument7 = AtomicReference(null) + val actualArgument8 = AtomicReference(null) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5, givenArg6, givenArg7, givenArg8 -> + actualArgument0.set(givenArg0) + actualArgument1.set(givenArg1) + actualArgument2.set(givenArg2) + actualArgument3.set(givenArg3) + actualArgument4.set(givenArg4) + actualArgument5.set(givenArg5) + actualArgument6.set(givenArg6) + actualArgument7.set(givenArg7) + actualArgument8.set(givenArg8) + + expected + } + } + + runBlockingTestWithTimeoutInScope(testScope2.coroutineContext) { + // When + val actual = mockery.invoke( + argument0, + argument1, + argument2, + argument3, + argument4, + argument5, + argument6, + argument7, + argument8 + ) + + // Then + actual mustBe expected + actualArgument0.get() mustBe argument0 + actualArgument1.get() mustBe argument1 + actualArgument2.get() mustBe argument2 + actualArgument3.get() mustBe argument3 + actualArgument4.get() mustBe argument4 + actualArgument5.get() mustBe argument5 + actualArgument6.get() mustBe argument6 + actualArgument7.get() mustBe argument7 + actualArgument8.get() mustBe argument8 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 9 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + arguments[6] mustBe argument6 + arguments[7] mustBe argument7 + arguments[8] mustBe argument8 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn44") + fun `Given invoke is called it calls the given SideEffect with 10 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + val argument6: String = fixture.fixture() + val argument7: Int = fixture.fixture() + val argument8: String = fixture.fixture() + val argument9: Int = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = AtomicReference(null) + val actualArgument1 = AtomicReference(null) + val actualArgument2 = AtomicReference(null) + val actualArgument3 = AtomicReference(null) + val actualArgument4 = AtomicReference(null) + val actualArgument5 = AtomicReference(null) + val actualArgument6 = AtomicReference(null) + val actualArgument7 = AtomicReference(null) + val actualArgument8 = AtomicReference(null) + val actualArgument9 = AtomicReference(null) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5, givenArg6, givenArg7, givenArg8, givenArg9 -> + actualArgument0.set(givenArg0) + actualArgument1.set(givenArg1) + actualArgument2.set(givenArg2) + actualArgument3.set(givenArg3) + actualArgument4.set(givenArg4) + actualArgument5.set(givenArg5) + actualArgument6.set(givenArg6) + actualArgument7.set(givenArg7) + actualArgument8.set(givenArg8) + actualArgument9.set(givenArg9) + + expected + } + } + + runBlockingTestWithTimeoutInScope(testScope2.coroutineContext) { + // When + val actual = mockery.invoke( + argument0, + argument1, + argument2, + argument3, + argument4, + argument5, + argument6, + argument7, + argument8, + argument9 + ) + + // Then + actual mustBe expected + actualArgument0.get() mustBe argument0 + actualArgument1.get() mustBe argument1 + actualArgument2.get() mustBe argument2 + actualArgument3.get() mustBe argument3 + actualArgument4.get() mustBe argument4 + actualArgument5.get() mustBe argument5 + actualArgument6.get() mustBe argument6 + actualArgument7.get() mustBe argument7 + actualArgument8.get() mustBe argument8 + actualArgument9.get() mustBe argument9 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 10 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + arguments[6] mustBe argument6 + arguments[7] mustBe argument7 + arguments[8] mustBe argument8 + arguments[9] mustBe argument9 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn45") + fun `Given invoke is called it calls the given SideEffect with 11 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + val argument6: String = fixture.fixture() + val argument7: Int = fixture.fixture() + val argument8: String = fixture.fixture() + val argument9: Int = fixture.fixture() + val argument10: String = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = AtomicReference(null) + val actualArgument1 = AtomicReference(null) + val actualArgument2 = AtomicReference(null) + val actualArgument3 = AtomicReference(null) + val actualArgument4 = AtomicReference(null) + val actualArgument5 = AtomicReference(null) + val actualArgument6 = AtomicReference(null) + val actualArgument7 = AtomicReference(null) + val actualArgument8 = AtomicReference(null) + val actualArgument9 = AtomicReference(null) + val actualArgument10 = AtomicReference(null) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5, givenArg6, givenArg7, givenArg8, givenArg9, givenArg10 -> + actualArgument0.set(givenArg0) + actualArgument1.set(givenArg1) + actualArgument2.set(givenArg2) + actualArgument3.set(givenArg3) + actualArgument4.set(givenArg4) + actualArgument5.set(givenArg5) + actualArgument6.set(givenArg6) + actualArgument7.set(givenArg7) + actualArgument8.set(givenArg8) + actualArgument9.set(givenArg9) + actualArgument10.set(givenArg10) + + expected + } + } + + runBlockingTestWithTimeoutInScope(testScope2.coroutineContext) { + // When + + val actual = mockery.invoke( + argument0, + argument1, + argument2, + argument3, + argument4, + argument5, + argument6, + argument7, + argument8, + argument9, + argument10 + ) + + // Then + actual mustBe expected + actualArgument0.get() mustBe argument0 + actualArgument1.get() mustBe argument1 + actualArgument2.get() mustBe argument2 + actualArgument3.get() mustBe argument3 + actualArgument4.get() mustBe argument4 + actualArgument5.get() mustBe argument5 + actualArgument6.get() mustBe argument6 + actualArgument7.get() mustBe argument7 + actualArgument8.get() mustBe argument8 + actualArgument9.get() mustBe argument9 + actualArgument10.get() mustBe argument10 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 11 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + arguments[6] mustBe argument6 + arguments[7] mustBe argument7 + arguments[8] mustBe argument8 + arguments[9] mustBe argument9 + arguments[10] mustBe argument10 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn46") + fun `Given invoke is called it calls the given SideEffect with 12 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + val argument6: String = fixture.fixture() + val argument7: Int = fixture.fixture() + val argument8: String = fixture.fixture() + val argument9: Int = fixture.fixture() + val argument10: String = fixture.fixture() + val argument11: Int = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = AtomicReference(null) + val actualArgument1 = AtomicReference(null) + val actualArgument2 = AtomicReference(null) + val actualArgument3 = AtomicReference(null) + val actualArgument4 = AtomicReference(null) + val actualArgument5 = AtomicReference(null) + val actualArgument6 = AtomicReference(null) + val actualArgument7 = AtomicReference(null) + val actualArgument8 = AtomicReference(null) + val actualArgument9 = AtomicReference(null) + val actualArgument10 = AtomicReference(null) + val actualArgument11 = AtomicReference(null) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5, givenArg6, givenArg7, givenArg8, givenArg9, givenArg10, givenArg11 -> + actualArgument0.set(givenArg0) + actualArgument1.set(givenArg1) + actualArgument2.set(givenArg2) + actualArgument3.set(givenArg3) + actualArgument4.set(givenArg4) + actualArgument5.set(givenArg5) + actualArgument6.set(givenArg6) + actualArgument7.set(givenArg7) + actualArgument8.set(givenArg8) + actualArgument9.set(givenArg9) + actualArgument10.set(givenArg10) + actualArgument11.set(givenArg11) + + expected + } + } + + runBlockingTestWithTimeoutInScope(testScope2.coroutineContext) { + // When + val actual = mockery.invoke( + argument0, + argument1, + argument2, + argument3, + argument4, + argument5, + argument6, + argument7, + argument8, + argument9, + argument10, + argument11 + ) + + // Then + actual mustBe expected + actualArgument0.get() mustBe argument0 + actualArgument1.get() mustBe argument1 + actualArgument2.get() mustBe argument2 + actualArgument3.get() mustBe argument3 + actualArgument4.get() mustBe argument4 + actualArgument5.get() mustBe argument5 + actualArgument6.get() mustBe argument6 + actualArgument7.get() mustBe argument7 + actualArgument8.get() mustBe argument8 + actualArgument9.get() mustBe argument9 + actualArgument10.get() mustBe argument10 + actualArgument11.get() mustBe argument11 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 12 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + arguments[6] mustBe argument6 + arguments[7] mustBe argument7 + arguments[8] mustBe argument8 + arguments[9] mustBe argument9 + arguments[10] mustBe argument10 + arguments[11] mustBe argument11 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn47") + fun `Given invoke is called it calls the given SideEffect with 13 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = AsyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + val argument6: String = fixture.fixture() + val argument7: Int = fixture.fixture() + val argument8: String = fixture.fixture() + val argument9: Int = fixture.fixture() + val argument10: String = fixture.fixture() + val argument11: Int = fixture.fixture() + val argument12: String = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = AtomicReference(null) + val actualArgument1 = AtomicReference(null) + val actualArgument2 = AtomicReference(null) + val actualArgument3 = AtomicReference(null) + val actualArgument4 = AtomicReference(null) + val actualArgument5 = AtomicReference(null) + val actualArgument6 = AtomicReference(null) + val actualArgument7 = AtomicReference(null) + val actualArgument8 = AtomicReference(null) + val actualArgument9 = AtomicReference(null) + val actualArgument10 = AtomicReference(null) + val actualArgument11 = AtomicReference(null) + val actualArgument12 = AtomicReference(null) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5, givenArg6, givenArg7, givenArg8, givenArg9, givenArg10, givenArg11, givenArg12 -> + actualArgument0.set(givenArg0) + actualArgument1.set(givenArg1) + actualArgument2.set(givenArg2) + actualArgument3.set(givenArg3) + actualArgument4.set(givenArg4) + actualArgument5.set(givenArg5) + actualArgument6.set(givenArg6) + actualArgument7.set(givenArg7) + actualArgument8.set(givenArg8) + actualArgument9.set(givenArg9) + actualArgument10.set(givenArg10) + actualArgument11.set(givenArg11) + actualArgument12.set(givenArg12) + + expected + } + } + + runBlockingTestWithTimeoutInScope(testScope2.coroutineContext) { + // When + val actual = mockery.invoke( + argument0, + argument1, + argument2, + argument3, + argument4, + argument5, + argument6, + argument7, + argument8, + argument9, + argument10, + argument11, + argument12 + ) + + // Then + actual mustBe expected + actualArgument0.get() mustBe argument0 + actualArgument1.get() mustBe argument1 + actualArgument2.get() mustBe argument2 + actualArgument3.get() mustBe argument3 + actualArgument4.get() mustBe argument4 + actualArgument5.get() mustBe argument5 + actualArgument6.get() mustBe argument6 + actualArgument7.get() mustBe argument7 + actualArgument8.get() mustBe argument8 + actualArgument9.get() mustBe argument9 + actualArgument10.get() mustBe argument10 + actualArgument11.get() mustBe argument11 + actualArgument12.get() mustBe argument12 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 13 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + arguments[6] mustBe argument6 + arguments[7] mustBe argument7 + arguments[8] mustBe argument8 + arguments[9] mustBe argument9 + arguments[10] mustBe argument10 + arguments[11] mustBe argument11 + arguments[12] mustBe argument12 + } + + return asyncMultiBlock + } +} diff --git a/kmock/src/commonTest/kotlin/tech/antibytes/kmock/ExampleClassSpec.kt b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/ExampleClassSpec.kt deleted file mode 100644 index f4e2b794..00000000 --- a/kmock/src/commonTest/kotlin/tech/antibytes/kmock/ExampleClassSpec.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. - * - * Use of this source code is governed by Apache v2.0 - */ - -package tech.antibytes.kmock - -import tech.antibytes.util.test.mustBe -import kotlin.test.Test - -class ExampleClassSpec { - @Test - fun test() { - ExampleClass().doSomething() mustBe "something" - } -} diff --git a/kmock/src/commonTest/kotlin/tech/antibytes/kmock/OperationSpec.kt b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/OperationSpec.kt new file mode 100644 index 00000000..3aa74f49 --- /dev/null +++ b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/OperationSpec.kt @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import tech.antibytes.util.test.fixture.fixture +import tech.antibytes.util.test.fixture.kotlinFixture +import tech.antibytes.util.test.fixture.listFixture +import tech.antibytes.util.test.mustBe +import kotlin.js.JsName +import kotlin.test.Test + +class OperationSpec { + private val fixture = kotlinFixture() + + @Test + @JsName("fn0") + fun `Given union is called with different VerificationHandles it merges both`() { + // Given + val name: String = fixture.fixture() + val values1: List = fixture.listFixture() + val values2: List = fixture.listFixture() + + val handle1 = VerificationHandle(name, values1) + val handle2 = VerificationHandle(name, values2) + + // When + val actualUnion = handle1 union handle2 + val actualAnd = handle1 and handle2 + + // Then + val expected = values1.toMutableList() + .also { it.addAll(values2) } + .toSet() + .toList() + .sorted() + + actualAnd mustBe actualUnion + + actualUnion.id mustBe handle1.id + actualUnion.callIndices.forEachIndexed { idx, value -> + value mustBe expected[idx] + } + } + + @Test + @JsName("fn1") + fun `Given intersect is called with different VerificationHandles it makes the intersection of both`() { + // Given + val name: String = fixture.fixture() + val base: List = fixture.listFixture() + val values1: List = fixture.listFixture() + .toMutableList() + .also { it.addAll(base) } + .also { it.shuffle() } + val values2: List = fixture.listFixture() + .toMutableList() + .also { it.addAll(base) } + .also { it.shuffle() } + + val handle1 = VerificationHandle(name, values1) + val handle2 = VerificationHandle(name, values2) + + // When + val actualIntersect = handle1 intersect handle2 + val actualOr = handle1 or handle2 + + // Then + val expected = base.sorted() + + actualOr mustBe actualIntersect + + actualIntersect.id mustBe handle1.id + actualIntersect.callIndices.forEachIndexed { idx, value -> + value mustBe expected[idx] + } + } + + @Test + @JsName("fn2") + fun `Given diif is called with different VerificationHandles it makes the difference of both`() { + // Given + val name: String = fixture.fixture() + val base: List = fixture.listFixture() + val values1: List = fixture.listFixture() + .toMutableList() + .also { it.addAll(base) } + .also { it.shuffle() } + val values2: List = fixture.listFixture() + .toMutableList() + .also { it.addAll(base) } + .also { it.shuffle() } + + val handle1 = VerificationHandle(name, values1) + val handle2 = VerificationHandle(name, values2) + + // When + val actualDiff = handle1 diff handle2 + val actualXOr = handle1 xor handle2 + + // Then + val expected = values1 + .toMutableSet() + .also { it.addAll(values2) } + .filterNot { value -> base.contains(value) } + .sorted() + + actualXOr mustBe actualDiff + + actualDiff.id mustBe handle1.id + actualDiff.callIndices.forEachIndexed { idx, value -> + value mustBe expected[idx] + } + } +} diff --git a/kmock/src/commonTest/kotlin/tech/antibytes/kmock/PropertyMockerySpec.kt b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/PropertyMockerySpec.kt new file mode 100644 index 00000000..3253e555 --- /dev/null +++ b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/PropertyMockerySpec.kt @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import co.touchlab.stately.concurrency.AtomicReference +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.launch +import tech.antibytes.util.test.MockError +import tech.antibytes.util.test.coroutine.AsyncTestReturnValue +import tech.antibytes.util.test.coroutine.TestScopeDispatcher +import tech.antibytes.util.test.coroutine.asyncMultiBlock +import tech.antibytes.util.test.coroutine.runBlockingTest +import tech.antibytes.util.test.coroutine.runBlockingTestInContext +import tech.antibytes.util.test.fixture.fixture +import tech.antibytes.util.test.fixture.kotlinFixture +import tech.antibytes.util.test.fixture.listFixture +import tech.antibytes.util.test.fulfils +import tech.antibytes.util.test.mustBe +import tech.antibytes.util.test.sameAs +import kotlin.js.JsName +import kotlin.test.Test +import kotlin.test.assertFailsWith + +class PropertyMockerySpec { + private val fixture = kotlinFixture() + private val testScope1 = TestScopeDispatcher.dispatch("test1") + private val testScope2 = TestScopeDispatcher.dispatch("test2") + + @Test + @JsName("fn0") + fun `It fulfils PropMockery`() { + PropertyMockery(fixture.fixture()) fulfils KMockContract.PropMockery::class + } + + @Test + @JsName("fn1") + fun `Given a get is set it is threadsafe retrievable`(): AsyncTestReturnValue { + // Given + val mockery = PropertyMockery(fixture.fixture()) + val value: Any = fixture.fixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.get = value + } + + // Then + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.get mustBe value + } + + return asyncMultiBlock + } + + @Test + @JsName("fn2") + fun `Given a get is set with nullable value it is threadsafe retrievable`(): AsyncTestReturnValue { + // Given + val mockery = PropertyMockery(fixture.fixture()) + val value: Any? = null + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.get = value + } + + // Then + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.get mustBe value + } + + return asyncMultiBlock + } + + @Test + @JsName("fn3") + fun `Given a getMany is set with an emptyList it fails`() { + // Given + val mockery = PropertyMockery(fixture.fixture()) + + // Then + val error = assertFailsWith { + mockery.getMany = emptyList() + } + + error.message mustBe "Empty Lists are not valid as value provider." + } + + @Test + @JsName("fn4") + fun `Given a getMany is set it is threadsafe retrievable`(): AsyncTestReturnValue { + // Given + val mockery = PropertyMockery(fixture.fixture()) + val values: List = fixture.listFixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.getMany = values + } + + // Then + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.getMany mustBe values + } + + return asyncMultiBlock + } + + @Test + @JsName("fn5") + fun `Given a set is set it is threadsafe retrievable`(): AsyncTestReturnValue { + // Given + val mockery = PropertyMockery(fixture.fixture()) + val effect: (Any) -> Unit = { } + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.set = effect + } + + // Then + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.set sameAs effect + } + + return asyncMultiBlock + } + + @Test + @JsName("fn6") + fun `Given onGet is called it fails if no ReturnValue Provider is set`(): AsyncTestReturnValue { + // Given + val name: String = fixture.fixture() + val mockery = PropertyMockery(name) + + return runBlockingTestInContext(testScope1.coroutineContext) { + // Then + val error = assertFailsWith { + // When + mockery.onGet() + } + + error.message mustBe "Missing stub value for $name" + } + } + + @Test + @JsName("fn7") + fun `Given onGet is called it returns the Get Value threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = PropertyMockery(fixture.fixture()) + val value: String = fixture.fixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.get = value + } + + runBlockingTestInContext(testScope2.coroutineContext) { + val actual = mockery.onGet() + + // Then + actual mustBe value + } + + return asyncMultiBlock + } + + @Test + @JsName("fn8") + fun `Given onGet is called it returns the GetMany Value threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = PropertyMockery(fixture.fixture()) + val values: List = fixture.listFixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.getMany = values + } + + runBlockingTestInContext(testScope2.coroutineContext) { + values.forEach { value -> + val actual = mockery.onGet() + + // Then + actual mustBe value + } + } + + return asyncMultiBlock + } + + @Test + @JsName("fn9") + fun `Given onGet is called it returns the last GetMany Value if the given List is down to one value threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = PropertyMockery(fixture.fixture()) + val values: List = fixture.listFixture(size = 1) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.getMany = values.toList() + } + + runBlockingTestInContext(testScope2.coroutineContext) { + for (x in 0 until 10) { + val actual = mockery.onGet() + + // Then + actual mustBe values.first() + } + } + + return asyncMultiBlock + } + + @Test + @JsName("fn10") + fun `Given onGet is called it uses GetMany over Get`(): AsyncTestReturnValue { + // Given + val mockery = PropertyMockery(fixture.fixture()) + val value: Any = fixture.fixture() + val values: List = fixture.listFixture(size = 2) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.get = value + mockery.getMany = values + } + + runBlockingTestInContext(testScope2.coroutineContext) { + val actual = mockery.onGet() + + // Then + actual mustBe values.first() + } + + return asyncMultiBlock + } + + @Test + @JsName("fn11") + fun `Given onSet is called it calls the given SideEffect and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = PropertyMockery(fixture.fixture()) + val newValue: Any = fixture.fixture() + + val actualNew = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.set = { givenNew -> + testScope1.launch { + actualNew.send(givenNew) + } + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + val actual = mockery.onSet(newValue) + + // Then + actual mustBe Unit + actualNew.receive() mustBe newValue + } + + return asyncMultiBlock + } + + @Test + @JsName("fn12") + fun `Given onGet is called it sets an Arguments to capture the call threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = PropertyMockery(fixture.fixture()) + val values: List = fixture.listFixture(size = 5) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.getMany = values.toList() + } + + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.onGet() + } + + runBlockingTest { + val actual = mockery.getArgumentsForCall(0) + + actual fulfils KMockContract.GetOrSet.Get::class + actual.value mustBe null + } + + return asyncMultiBlock + } + + @Test + @JsName("fn13") + fun `Given onSet is called it sets an Arguments to capture the call threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = PropertyMockery(fixture.fixture()) + val value: Any = fixture.fixture() + + // When + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.onSet(value) + } + + runBlockingTest { + val actual = mockery.getArgumentsForCall(0) + + actual fulfils KMockContract.GetOrSet.Set::class + actual.value mustBe value + } + + return asyncMultiBlock + } + + @Test + @JsName("fn14") + fun `It reflects the given id`() { + // Given + val name: String = fixture.fixture() + + // When + val actual = PropertyMockery(name).id + + // Then + actual mustBe name + } + + @Test + @JsName("fn15") + fun `Its default call count is 0`() { + PropertyMockery(fixture.fixture()).calls mustBe 0 + } + + @Test + @JsName("fn17") + fun `Given onGet is called it increments the call counter threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = PropertyMockery(fixture.fixture()) + val values: List = fixture.listFixture(size = 5) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.getMany = values + } + + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.onGet() + } + + runBlockingTest { + mockery.calls mustBe 1 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn18") + fun `Given onSet is called it increments the call counter threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = PropertyMockery(fixture.fixture()) + val value: Any = fixture.fixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.onSet(value) + } + + runBlockingTest { + mockery.calls mustBe 1 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn19") + fun `Given the mockery has a Collector and onGet is called it calls the Collect`(): AsyncTestReturnValue { + // Given + val values: List = fixture.listFixture(size = 5) + + val capturedMock = AtomicReference?>(null) + val capturedCalledIdx = AtomicReference(null) + + val collector = KMockContract.Collector { referredMock, referredCall -> + capturedMock.set(referredMock) + capturedCalledIdx.set(referredCall) + } + + // When + val mockery = PropertyMockery(fixture.fixture(), collector) + + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.getMany = values + } + + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.onGet() + } + + runBlockingTest { + capturedMock.get()?.id mustBe mockery.id + capturedCalledIdx.get() mustBe 0 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn20") + fun `Given the mockery has a Collector and onSet is called it calls the Collect`(): AsyncTestReturnValue { + // Given + val value: Any = fixture.fixture() + + val capturedMock = AtomicReference?>(null) + val capturedCalledIdx = AtomicReference(null) + + val collector = KMockContract.Collector { referredMock, referredCall -> + capturedMock.set(referredMock) + capturedCalledIdx.set(referredCall) + } + + // When + val mockery = PropertyMockery(fixture.fixture(), collector) + + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.onSet(value) + } + + runBlockingTest { + capturedMock.get()?.id mustBe mockery.id + capturedCalledIdx.get() mustBe 0 + } + + return asyncMultiBlock + } +} diff --git a/kmock/src/commonTest/kotlin/tech/antibytes/kmock/SyncFunMockerySpec.kt b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/SyncFunMockerySpec.kt new file mode 100644 index 00000000..f3382077 --- /dev/null +++ b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/SyncFunMockerySpec.kt @@ -0,0 +1,1406 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import co.touchlab.stately.concurrency.AtomicReference +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.launch +import tech.antibytes.kmock.KMockContract.Collector +import tech.antibytes.util.test.MockError +import tech.antibytes.util.test.coroutine.AsyncTestReturnValue +import tech.antibytes.util.test.coroutine.TestScopeDispatcher +import tech.antibytes.util.test.coroutine.asyncMultiBlock +import tech.antibytes.util.test.coroutine.runBlockingTest +import tech.antibytes.util.test.coroutine.runBlockingTestInContext +import tech.antibytes.util.test.fixture.fixture +import tech.antibytes.util.test.fixture.kotlinFixture +import tech.antibytes.util.test.fixture.listFixture +import tech.antibytes.util.test.fulfils +import tech.antibytes.util.test.mustBe +import tech.antibytes.util.test.sameAs +import kotlin.js.JsName +import kotlin.test.Test +import kotlin.test.assertFailsWith + +class SyncFunMockerySpec { + private val fixture = kotlinFixture() + private val testScope1 = TestScopeDispatcher.dispatch("test1") + private val testScope2 = TestScopeDispatcher.dispatch("test2") + + @Test + @JsName("fn0") + fun `It fulfils FunMockery`() { + SyncFunMockery Unit>(fixture.fixture()) fulfils KMockContract.FunMockery::class + } + + @Test + @JsName("fn0_a") + fun `It fulfils SyncFunMockery`() { + SyncFunMockery Unit>(fixture.fixture()) fulfils KMockContract.SyncFunMockery::class + } + + @Test + @JsName("fn1") + fun `Given a returnValue is set it is threadsafe retrievable`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val value: Any = fixture.fixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValue = value + } + + // Then + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.returnValue mustBe value + } + + return asyncMultiBlock + } + + @Test + @JsName("fn2") + fun `Given a returnValue is set with nullable value it is threadsafe retrievable`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any?>(fixture.fixture()) + val value: Any? = null + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValue = value + } + + // Then + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.returnValue mustBe value + } + + return asyncMultiBlock + } + + @Test + @JsName("fn3") + fun `Given a returnValues is set with an emptyList it fails`() { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + + // Then + val error = assertFailsWith { + mockery.returnValues = emptyList() + } + + error.message mustBe "Empty Lists are not valid as value provider." + } + + @Test + @JsName("fn4") + fun `Given a returnValues is set it is threadsafe retrievable`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val values: List = fixture.listFixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValues = values + } + + // Then + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.returnValues mustBe values + } + + return asyncMultiBlock + } + + @Test + @JsName("fn5") + fun `Given a sideEffect is set it is threadsafe retrievable`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val effect: () -> Any = { fixture.fixture() } + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = effect + } + + // Then + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.sideEffect sameAs effect + } + + return asyncMultiBlock + } + + @Test + @JsName("fn6") + fun `Given invoke is called it fails if no ReturnValue Provider is set`(): AsyncTestReturnValue { + // Given + val name: String = fixture.fixture() + val mockery = SyncFunMockery Any>(name) + + // When + return runBlockingTestInContext(testScope1.coroutineContext) { + // Then + val error = assertFailsWith { + mockery.invoke() + } + + error.message mustBe "Missing stub value for $name" + } + } + + @Test + @JsName("fn7") + fun `Given invoke is called it returns the ReturnValue threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val value: String = fixture.fixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValue = value + } + + runBlockingTestInContext(testScope2.coroutineContext) { + val actual = mockery.invoke() + + // Then + actual mustBe value + } + + return asyncMultiBlock + } + + @Test + @JsName("fn8") + fun `Given invoke is called it returns the ReturnValues threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val values: List = fixture.listFixture(size = 5) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValues = values.toList() + } + + runBlockingTestInContext(testScope2.coroutineContext) { + values.forEach { value -> + val actual = mockery.invoke() + + // Then + actual mustBe value + } + } + + return asyncMultiBlock + } + + @Test + @JsName("fn9") + fun `Given invoke is called it returns the last ReturnValue if the given List is down to one value threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val values: List = fixture.listFixture(size = 1) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValues = values.toList() + } + + runBlockingTestInContext(testScope2.coroutineContext) { + for (x in 0 until 10) { + val actual = mockery.invoke() + + // Then + actual mustBe values.first() + } + } + + return asyncMultiBlock + } + + @Test + @JsName("fn10") + fun `Given invoke is called it calls the given SideEffect and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val expected: Any = fixture.fixture() + + val actualArgument0 = Channel() + val actualArgument1 = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1 -> + testScope1.launch { + actualArgument0.send(givenArg0) + actualArgument1.send(givenArg1) + } + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + val actual = mockery.invoke(argument0, argument1) + + // Then + actual mustBe expected + actualArgument0.receive() mustBe argument0 + actualArgument1.receive() mustBe argument1 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn11") + fun `Given invoke is called it uses ReturnValues over ReturnValue`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val value: Any = fixture.fixture() + val values: List = fixture.listFixture(size = 2) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValue = value + mockery.returnValues = values + } + + runBlockingTestInContext(testScope2.coroutineContext) { + val actual = mockery.invoke() + + // Then + actual mustBe values.first() + } + + return asyncMultiBlock + } + + @Test + @JsName("fn12") + fun `Given invoke is called it uses SideEffect over ReturnValues`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val expected: Any = fixture.fixture() + val values: List = fixture.listFixture(size = 2) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { expected } + mockery.returnValues = values + } + + runBlockingTestInContext(testScope2.coroutineContext) { + val actual = mockery.invoke() + + // Then + actual mustBe expected + } + + return asyncMultiBlock + } + + @Test + @JsName("fn13") + fun `Given invoke is called it captures Arguments threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val values: List = fixture.listFixture(size = 5) + val argument: String = fixture.fixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValues = values.toList() + } + + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.invoke(argument) + } + + runBlockingTest { + val actual = mockery.getArgumentsForCall(0) + + actual!!.size mustBe 1 + actual[0] mustBe argument + } + + return asyncMultiBlock + } + + @Test + @JsName("fn14") + fun `Given invoke is called it captures void Arguments threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val values: List = fixture.listFixture(size = 5) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValues = values + } + + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.invoke() + } + + runBlockingTest { + val actual = mockery.getArgumentsForCall(0) + + actual mustBe null + } + + return asyncMultiBlock + } + + @Test + @JsName("fn15") + fun `It reflects the given id`() { + // Given + val name: String = fixture.fixture() + + // When + val actual = SyncFunMockery Any>(name).id + + // Then + actual mustBe name + } + + @Test + @JsName("fn16") + fun `Its default call count is 0`() { + SyncFunMockery Any>(fixture.fixture()).calls mustBe 0 + } + + @Test + @JsName("fn17") + fun `Given invoke is called it increments the call counter threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val values: List = fixture.listFixture(size = 5) + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValues = values + } + + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.invoke() + } + + runBlockingTest { + mockery.calls mustBe 1 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn18") + fun `Given the mockery has a Collector and invoke is called it calls the Collect`(): AsyncTestReturnValue { + // Given + val values: List = fixture.listFixture(size = 5) + + val capturedMock = AtomicReference?>(null) + val capturedCalledIdx = AtomicReference(null) + + val collector = Collector { referredMock, referredCall -> + capturedMock.set(referredMock) + capturedCalledIdx.set(referredCall) + } + + // When + val mockery = SyncFunMockery Any>(fixture.fixture(), collector) + + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.returnValues = values + } + + runBlockingTestInContext(testScope2.coroutineContext) { + mockery.invoke() + } + + runBlockingTest { + capturedMock.get()?.id mustBe mockery.id + capturedCalledIdx.get() mustBe 0 + } + + return asyncMultiBlock + } + + // Functions + @Test + @JsName("fn19") + fun `Given invoke is called it calls the given SideEffect with 0 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val expected: Any = fixture.fixture() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + val actual = mockery.invoke() + + // Then + actual mustBe expected + mockery.getArgumentsForCall(0) mustBe null + } + + return asyncMultiBlock + } + + @Test + @JsName("fn20") + fun `Given invoke is called it calls the given SideEffect with 1 Argument and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val expected: Any = fixture.fixture() + + val actualArgument0 = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0 -> + testScope1.launch { + actualArgument0.send(givenArg0) + } + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + val actual = mockery.invoke(argument0) + + // Then + actual mustBe expected + actualArgument0.receive() mustBe argument0 + + val arguments = mockery.getArgumentsForCall(0) + arguments?.size mustBe 1 + arguments!![0] mustBe argument0 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn21") + fun `Given invoke is called it calls the given SideEffect with 2 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val expected: Any = fixture.fixture() + + val actualArgument0 = Channel() + val actualArgument1 = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1 -> + testScope1.launch { + actualArgument0.send(givenArg0) + actualArgument1.send(givenArg1) + } + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + val actual = mockery.invoke(argument0, argument1) + + // Then + actual mustBe expected + actualArgument0.receive() mustBe argument0 + actualArgument1.receive() mustBe argument1 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 2 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn22") + fun `Given invoke is called it calls the given SideEffect with 3 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val expected: Any = fixture.fixture() + + val actualArgument0 = Channel() + val actualArgument1 = Channel() + val actualArgument2 = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2 -> + testScope1.launch { + actualArgument0.send(givenArg0) + actualArgument1.send(givenArg1) + actualArgument2.send(givenArg2) + } + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + val actual = mockery.invoke(argument0, argument1, argument2) + + // Then + actual mustBe expected + actualArgument0.receive() mustBe argument0 + actualArgument1.receive() mustBe argument1 + actualArgument2.receive() mustBe argument2 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 3 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn23") + fun `Given invoke is called it calls the given SideEffect with 4 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val expected: Any = fixture.fixture() + + val actualArgument0 = Channel() + val actualArgument1 = Channel() + val actualArgument2 = Channel() + val actualArgument3 = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3 -> + testScope1.launch { + actualArgument0.send(givenArg0) + actualArgument1.send(givenArg1) + actualArgument2.send(givenArg2) + actualArgument3.send(givenArg3) + } + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + val actual = mockery.invoke(argument0, argument1, argument2, argument3) + + // Then + actual mustBe expected + actualArgument0.receive() mustBe argument0 + actualArgument1.receive() mustBe argument1 + actualArgument2.receive() mustBe argument2 + actualArgument3.receive() mustBe argument3 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 4 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn24") + fun `Given invoke is called it calls the given SideEffect with 5 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = Channel() + val actualArgument1 = Channel() + val actualArgument2 = Channel() + val actualArgument3 = Channel() + val actualArgument4 = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4 -> + testScope1.launch { + actualArgument0.send(givenArg0) + actualArgument1.send(givenArg1) + actualArgument2.send(givenArg2) + actualArgument3.send(givenArg3) + actualArgument4.send(givenArg4) + } + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + val actual = mockery.invoke(argument0, argument1, argument2, argument3, argument4) + + // Then + actual mustBe expected + actualArgument0.receive() mustBe argument0 + actualArgument1.receive() mustBe argument1 + actualArgument2.receive() mustBe argument2 + actualArgument3.receive() mustBe argument3 + actualArgument4.receive() mustBe argument4 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 5 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn25") + fun `Given invoke is called it calls the given SideEffect with 6 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = Channel() + val actualArgument1 = Channel() + val actualArgument2 = Channel() + val actualArgument3 = Channel() + val actualArgument4 = Channel() + val actualArgument5 = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5 -> + testScope1.launch { + actualArgument0.send(givenArg0) + actualArgument1.send(givenArg1) + actualArgument2.send(givenArg2) + actualArgument3.send(givenArg3) + actualArgument4.send(givenArg4) + actualArgument5.send(givenArg5) + } + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + val actual = mockery.invoke(argument0, argument1, argument2, argument3, argument4, argument5) + + // Then + actual mustBe expected + actualArgument0.receive() mustBe argument0 + actualArgument1.receive() mustBe argument1 + actualArgument2.receive() mustBe argument2 + actualArgument3.receive() mustBe argument3 + actualArgument4.receive() mustBe argument4 + actualArgument5.receive() mustBe argument5 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 6 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn27") + fun `Given invoke is called it calls the given SideEffect with 7 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + val argument6: String = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = Channel() + val actualArgument1 = Channel() + val actualArgument2 = Channel() + val actualArgument3 = Channel() + val actualArgument4 = Channel() + val actualArgument5 = Channel() + val actualArgument6 = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5, givenArg6 -> + testScope1.launch { + actualArgument0.send(givenArg0) + actualArgument1.send(givenArg1) + actualArgument2.send(givenArg2) + actualArgument3.send(givenArg3) + actualArgument4.send(givenArg4) + actualArgument5.send(givenArg5) + actualArgument6.send(givenArg6) + } + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + val actual = mockery.invoke(argument0, argument1, argument2, argument3, argument4, argument5, argument6) + + // Then + actual mustBe expected + actualArgument0.receive() mustBe argument0 + actualArgument1.receive() mustBe argument1 + actualArgument2.receive() mustBe argument2 + actualArgument3.receive() mustBe argument3 + actualArgument4.receive() mustBe argument4 + actualArgument5.receive() mustBe argument5 + actualArgument6.receive() mustBe argument6 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 7 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + arguments[6] mustBe argument6 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn28") + fun `Given invoke is called it calls the given SideEffect with 8 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + val argument6: String = fixture.fixture() + val argument7: Int = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = Channel() + val actualArgument1 = Channel() + val actualArgument2 = Channel() + val actualArgument3 = Channel() + val actualArgument4 = Channel() + val actualArgument5 = Channel() + val actualArgument6 = Channel() + val actualArgument7 = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5, givenArg6, givenArg7 -> + testScope1.launch { + actualArgument0.send(givenArg0) + actualArgument1.send(givenArg1) + actualArgument2.send(givenArg2) + actualArgument3.send(givenArg3) + actualArgument4.send(givenArg4) + actualArgument5.send(givenArg5) + actualArgument6.send(givenArg6) + actualArgument7.send(givenArg7) + } + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + val actual = + mockery.invoke(argument0, argument1, argument2, argument3, argument4, argument5, argument6, argument7) + + // Then + actual mustBe expected + actualArgument0.receive() mustBe argument0 + actualArgument1.receive() mustBe argument1 + actualArgument2.receive() mustBe argument2 + actualArgument3.receive() mustBe argument3 + actualArgument4.receive() mustBe argument4 + actualArgument5.receive() mustBe argument5 + actualArgument6.receive() mustBe argument6 + actualArgument7.receive() mustBe argument7 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 8 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + arguments[6] mustBe argument6 + arguments[7] mustBe argument7 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn29") + fun `Given invoke is called it calls the given SideEffect with 9 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + val argument6: String = fixture.fixture() + val argument7: Int = fixture.fixture() + val argument8: String = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = Channel() + val actualArgument1 = Channel() + val actualArgument2 = Channel() + val actualArgument3 = Channel() + val actualArgument4 = Channel() + val actualArgument5 = Channel() + val actualArgument6 = Channel() + val actualArgument7 = Channel() + val actualArgument8 = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5, givenArg6, givenArg7, givenArg8 -> + testScope1.launch { + actualArgument0.send(givenArg0) + actualArgument1.send(givenArg1) + actualArgument2.send(givenArg2) + actualArgument3.send(givenArg3) + actualArgument4.send(givenArg4) + actualArgument5.send(givenArg5) + actualArgument6.send(givenArg6) + actualArgument7.send(givenArg7) + actualArgument8.send(givenArg8) + } + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + val actual = mockery.invoke( + argument0, + argument1, + argument2, + argument3, + argument4, + argument5, + argument6, + argument7, + argument8 + ) + + // Then + actual mustBe expected + actualArgument0.receive() mustBe argument0 + actualArgument1.receive() mustBe argument1 + actualArgument2.receive() mustBe argument2 + actualArgument3.receive() mustBe argument3 + actualArgument4.receive() mustBe argument4 + actualArgument5.receive() mustBe argument5 + actualArgument6.receive() mustBe argument6 + actualArgument7.receive() mustBe argument7 + actualArgument8.receive() mustBe argument8 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 9 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + arguments[6] mustBe argument6 + arguments[7] mustBe argument7 + arguments[8] mustBe argument8 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn30") + fun `Given invoke is called it calls the given SideEffect with 10 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + val argument6: String = fixture.fixture() + val argument7: Int = fixture.fixture() + val argument8: String = fixture.fixture() + val argument9: Int = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = Channel() + val actualArgument1 = Channel() + val actualArgument2 = Channel() + val actualArgument3 = Channel() + val actualArgument4 = Channel() + val actualArgument5 = Channel() + val actualArgument6 = Channel() + val actualArgument7 = Channel() + val actualArgument8 = Channel() + val actualArgument9 = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5, givenArg6, givenArg7, givenArg8, givenArg9 -> + testScope1.launch { + actualArgument0.send(givenArg0) + actualArgument1.send(givenArg1) + actualArgument2.send(givenArg2) + actualArgument3.send(givenArg3) + actualArgument4.send(givenArg4) + actualArgument5.send(givenArg5) + actualArgument6.send(givenArg6) + actualArgument7.send(givenArg7) + actualArgument8.send(givenArg8) + actualArgument9.send(givenArg9) + } + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + val actual = mockery.invoke( + argument0, + argument1, + argument2, + argument3, + argument4, + argument5, + argument6, + argument7, + argument8, + argument9 + ) + + // Then + actual mustBe expected + actualArgument0.receive() mustBe argument0 + actualArgument1.receive() mustBe argument1 + actualArgument2.receive() mustBe argument2 + actualArgument3.receive() mustBe argument3 + actualArgument4.receive() mustBe argument4 + actualArgument5.receive() mustBe argument5 + actualArgument6.receive() mustBe argument6 + actualArgument7.receive() mustBe argument7 + actualArgument8.receive() mustBe argument8 + actualArgument9.receive() mustBe argument9 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 10 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + arguments[6] mustBe argument6 + arguments[7] mustBe argument7 + arguments[8] mustBe argument8 + arguments[9] mustBe argument9 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn31") + fun `Given invoke is called it calls the given SideEffect with 11 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + val argument6: String = fixture.fixture() + val argument7: Int = fixture.fixture() + val argument8: String = fixture.fixture() + val argument9: Int = fixture.fixture() + val argument10: String = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = Channel() + val actualArgument1 = Channel() + val actualArgument2 = Channel() + val actualArgument3 = Channel() + val actualArgument4 = Channel() + val actualArgument5 = Channel() + val actualArgument6 = Channel() + val actualArgument7 = Channel() + val actualArgument8 = Channel() + val actualArgument9 = Channel() + val actualArgument10 = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5, givenArg6, givenArg7, givenArg8, givenArg9, givenArg10 -> + testScope1.launch { + actualArgument0.send(givenArg0) + actualArgument1.send(givenArg1) + actualArgument2.send(givenArg2) + actualArgument3.send(givenArg3) + actualArgument4.send(givenArg4) + actualArgument5.send(givenArg5) + actualArgument6.send(givenArg6) + actualArgument7.send(givenArg7) + actualArgument8.send(givenArg8) + actualArgument9.send(givenArg9) + actualArgument10.send(givenArg10) + } + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + + val actual = mockery.invoke( + argument0, + argument1, + argument2, + argument3, + argument4, + argument5, + argument6, + argument7, + argument8, + argument9, + argument10 + ) + + // Then + actual mustBe expected + actualArgument0.receive() mustBe argument0 + actualArgument1.receive() mustBe argument1 + actualArgument2.receive() mustBe argument2 + actualArgument3.receive() mustBe argument3 + actualArgument4.receive() mustBe argument4 + actualArgument5.receive() mustBe argument5 + actualArgument6.receive() mustBe argument6 + actualArgument7.receive() mustBe argument7 + actualArgument8.receive() mustBe argument8 + actualArgument9.receive() mustBe argument9 + actualArgument10.receive() mustBe argument10 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 11 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + arguments[6] mustBe argument6 + arguments[7] mustBe argument7 + arguments[8] mustBe argument8 + arguments[9] mustBe argument9 + arguments[10] mustBe argument10 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn32") + fun `Given invoke is called it calls the given SideEffect with 12 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + val argument6: String = fixture.fixture() + val argument7: Int = fixture.fixture() + val argument8: String = fixture.fixture() + val argument9: Int = fixture.fixture() + val argument10: String = fixture.fixture() + val argument11: Int = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = Channel() + val actualArgument1 = Channel() + val actualArgument2 = Channel() + val actualArgument3 = Channel() + val actualArgument4 = Channel() + val actualArgument5 = Channel() + val actualArgument6 = Channel() + val actualArgument7 = Channel() + val actualArgument8 = Channel() + val actualArgument9 = Channel() + val actualArgument10 = Channel() + val actualArgument11 = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5, givenArg6, givenArg7, givenArg8, givenArg9, givenArg10, givenArg11 -> + testScope1.launch { + actualArgument0.send(givenArg0) + actualArgument1.send(givenArg1) + actualArgument2.send(givenArg2) + actualArgument3.send(givenArg3) + actualArgument4.send(givenArg4) + actualArgument5.send(givenArg5) + actualArgument6.send(givenArg6) + actualArgument7.send(givenArg7) + actualArgument8.send(givenArg8) + actualArgument9.send(givenArg9) + actualArgument10.send(givenArg10) + actualArgument11.send(givenArg11) + } + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + val actual = mockery.invoke( + argument0, + argument1, + argument2, + argument3, + argument4, + argument5, + argument6, + argument7, + argument8, + argument9, + argument10, + argument11 + ) + + // Then + actual mustBe expected + actualArgument0.receive() mustBe argument0 + actualArgument1.receive() mustBe argument1 + actualArgument2.receive() mustBe argument2 + actualArgument3.receive() mustBe argument3 + actualArgument4.receive() mustBe argument4 + actualArgument5.receive() mustBe argument5 + actualArgument6.receive() mustBe argument6 + actualArgument7.receive() mustBe argument7 + actualArgument8.receive() mustBe argument8 + actualArgument9.receive() mustBe argument9 + actualArgument10.receive() mustBe argument10 + actualArgument11.receive() mustBe argument11 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 12 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + arguments[6] mustBe argument6 + arguments[7] mustBe argument7 + arguments[8] mustBe argument8 + arguments[9] mustBe argument9 + arguments[10] mustBe argument10 + arguments[11] mustBe argument11 + } + + return asyncMultiBlock + } + + @Test + @JsName("fn33") + fun `Given invoke is called it calls the given SideEffect with 13 Arguments and delegates values threadsafe`(): AsyncTestReturnValue { + // Given + val mockery = SyncFunMockery Any>(fixture.fixture()) + val argument0: String = fixture.fixture() + val argument1: Int = fixture.fixture() + val argument2: String = fixture.fixture() + val argument3: Int = fixture.fixture() + val argument4: String = fixture.fixture() + val argument5: Int = fixture.fixture() + val argument6: String = fixture.fixture() + val argument7: Int = fixture.fixture() + val argument8: String = fixture.fixture() + val argument9: Int = fixture.fixture() + val argument10: String = fixture.fixture() + val argument11: Int = fixture.fixture() + val argument12: String = fixture.fixture() + + val expected: Any = fixture.fixture() + + val actualArgument0 = Channel() + val actualArgument1 = Channel() + val actualArgument2 = Channel() + val actualArgument3 = Channel() + val actualArgument4 = Channel() + val actualArgument5 = Channel() + val actualArgument6 = Channel() + val actualArgument7 = Channel() + val actualArgument8 = Channel() + val actualArgument9 = Channel() + val actualArgument10 = Channel() + val actualArgument11 = Channel() + val actualArgument12 = Channel() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + mockery.sideEffect = { givenArg0, givenArg1, givenArg2, givenArg3, givenArg4, givenArg5, givenArg6, givenArg7, givenArg8, givenArg9, givenArg10, givenArg11, givenArg12 -> + testScope1.launch { + actualArgument0.send(givenArg0) + actualArgument1.send(givenArg1) + actualArgument2.send(givenArg2) + actualArgument3.send(givenArg3) + actualArgument4.send(givenArg4) + actualArgument5.send(givenArg5) + actualArgument6.send(givenArg6) + actualArgument7.send(givenArg7) + actualArgument8.send(givenArg8) + actualArgument9.send(givenArg9) + actualArgument10.send(givenArg10) + actualArgument11.send(givenArg11) + actualArgument12.send(givenArg12) + } + + expected + } + } + + runBlockingTestInContext(testScope2.coroutineContext) { + // When + val actual = mockery.invoke( + argument0, + argument1, + argument2, + argument3, + argument4, + argument5, + argument6, + argument7, + argument8, + argument9, + argument10, + argument11, + argument12 + ) + + // Then + actual mustBe expected + actualArgument0.receive() mustBe argument0 + actualArgument1.receive() mustBe argument1 + actualArgument2.receive() mustBe argument2 + actualArgument3.receive() mustBe argument3 + actualArgument4.receive() mustBe argument4 + actualArgument5.receive() mustBe argument5 + actualArgument6.receive() mustBe argument6 + actualArgument7.receive() mustBe argument7 + actualArgument8.receive() mustBe argument8 + actualArgument9.receive() mustBe argument9 + actualArgument10.receive() mustBe argument10 + actualArgument11.receive() mustBe argument11 + actualArgument12.receive() mustBe argument12 + + val arguments = mockery.getArgumentsForCall(0) + arguments!!.size mustBe 13 + arguments[0] mustBe argument0 + arguments[1] mustBe argument1 + arguments[2] mustBe argument2 + arguments[3] mustBe argument3 + arguments[4] mustBe argument4 + arguments[5] mustBe argument5 + arguments[6] mustBe argument6 + arguments[7] mustBe argument7 + arguments[8] mustBe argument8 + arguments[9] mustBe argument9 + arguments[10] mustBe argument10 + arguments[11] mustBe argument11 + arguments[12] mustBe argument12 + } + + return asyncMultiBlock + } +} diff --git a/kmock/src/commonTest/kotlin/tech/antibytes/kmock/VerificationChainBuilderSpec.kt b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/VerificationChainBuilderSpec.kt new file mode 100644 index 00000000..fae433ab --- /dev/null +++ b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/VerificationChainBuilderSpec.kt @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import tech.antibytes.mock.FunMockeryStub +import tech.antibytes.mock.PropertyMockeryStub +import tech.antibytes.util.test.fixture.PublicApi +import tech.antibytes.util.test.fixture.fixture +import tech.antibytes.util.test.fixture.kotlinFixture +import tech.antibytes.util.test.fixture.listFixture +import tech.antibytes.util.test.fulfils +import tech.antibytes.util.test.mustBe +import kotlin.js.JsName +import kotlin.test.Test + +class VerificationChainBuilderSpec { + private val fixture = kotlinFixture() + + private fun PublicApi.Fixture.handleFixtures(size: Int): List { + val fixtures = mutableListOf() + + for (x in 0 until size) { + fixtures.add( + VerificationHandle( + fixture.fixture(), + fixture.listFixture() + ) + ) + } + + return fixtures + } + + @Test + @JsName("fn0") + fun `It fulfils HandleContainer`() { + VerificationChainBuilder() fulfils KMockContract.VerificationHandleContainer::class + } + + @Test + @JsName("fn1") + fun `Given add and toList is called it adds the given Handles and return them as List`() { + // Given + val size = 5 + val handles = fixture.handleFixtures(size) + + // When + val container = VerificationChainBuilder() + handles.forEach { handle -> + container.add(handle) + } + + val actual = container.toList() + + // Then + actual mustBe handles + } + + @Test + @JsName("fn2") + fun `Given withArguments is called it uses the extension of FunMockery`() { + // Given + val name: String = fixture.fixture() + val mock = FunMockeryStub(name, 1) + val values = fixture.listFixture().toTypedArray() + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + values + } + + // When + val container = VerificationChainBuilder() + container.withArguments(mock, *(values.sorted()).toTypedArray()) + + // Then + val actual = container.toList()[0] + actual mustBe VerificationHandle(name, listOf(0)) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn3") + fun `Given withSameArguments is called it uses the extension of FunMockery`() { + // Given + val name: String = fixture.fixture() + val mock = FunMockeryStub(name, 1) + val values = fixture.listFixture().toTypedArray() + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + values + } + + // When + val container = VerificationChainBuilder() + container.withSameArguments(mock, *values) + + // Then + val actual = container.toList()[0] + actual mustBe VerificationHandle(name, listOf(0)) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn4") + fun `Given withoutArguments is called it uses the extension of FunMockery`() { + // Given + val name: String = fixture.fixture() + val mock = FunMockeryStub(name, 1) + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + fixture.listFixture().toTypedArray() + } + + // When + val container = VerificationChainBuilder() + container.withoutArguments(mock, fixture.fixture()) + + // Then + val actual = container.toList()[0] + actual mustBe VerificationHandle(name, listOf(0)) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn5") + fun `Given wasGotten is called it uses the extension of PropMockery`() { + // Given + val name: String = fixture.fixture() + val mock = PropertyMockeryStub(name, 1) + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + KMockContract.GetOrSet.Get + } + + // When + val container = VerificationChainBuilder() + container.wasGotten(mock) + + // Then + val actual = container.toList()[0] + actual mustBe VerificationHandle(name, listOf(0)) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn6") + fun `Given wasSet is called it uses the extension of PropMockery`() { + // Given + val name: String = fixture.fixture() + val mock = PropertyMockeryStub(name, 1) + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + KMockContract.GetOrSet.Set(null) + } + + // When + val container = VerificationChainBuilder() + container.wasSet(mock) + + // Then + val actual = container.toList()[0] + actual mustBe VerificationHandle(name, listOf(0)) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn7") + fun `Given wasSetTo is called it uses the extension of PropMockery`() { + // Given + val name: String = fixture.fixture() + val mock = PropertyMockeryStub(name, 1) + val value: Any = fixture.fixture() + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + KMockContract.GetOrSet.Set(value) + } + + // When + val container = VerificationChainBuilder() + container.wasSetTo(mock, value) + + // Then + val actual = container.toList()[0] + actual mustBe VerificationHandle(name, listOf(0)) + capturedIndex mustBe 0 + } +} diff --git a/kmock/src/commonTest/kotlin/tech/antibytes/kmock/VerificationFactorySpec.kt b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/VerificationFactorySpec.kt new file mode 100644 index 00000000..bde80f03 --- /dev/null +++ b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/VerificationFactorySpec.kt @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import tech.antibytes.mock.FunMockeryStub +import tech.antibytes.mock.PropertyMockeryStub +import tech.antibytes.util.test.fixture.fixture +import tech.antibytes.util.test.fixture.kotlinFixture +import tech.antibytes.util.test.fixture.listFixture +import tech.antibytes.util.test.mustBe +import kotlin.js.JsName +import kotlin.test.Test + +class VerificationFactorySpec { + private val fixture = kotlinFixture() + + @Test + @JsName("fn0") + fun `Given withArguments is called with a FunMockery it returns a VerficationHandle which contains no matches if nothing matches`() { + // Given + val name: String = fixture.fixture() + val mock = FunMockeryStub(name, 0) + + // When + val actual = mock.withArguments() + + // Then + actual mustBe VerificationHandle(name, emptyList()) + } + + @Test + @JsName("fn1") + fun `Given withArguments is called with a FunMockery it returns a VerficationHandle which contains no matches if nothing matches while delegating the captured values`() { + // Given + val name: String = fixture.fixture() + val mock = FunMockeryStub(name, 1) + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + fixture.listFixture().toTypedArray() + } + + // When + val actual = mock.withArguments(fixture.fixture()) + + // Then + actual mustBe VerificationHandle(name, emptyList()) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn2") + fun `Given withArguments is called with a FunMockery it returns a VerficationHandle which contains matches if something matches while delegating the captured values`() { + // Given + val name: String = fixture.fixture() + val mock = FunMockeryStub(name, 1) + val values = fixture.listFixture().toTypedArray() + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + values + } + + // When + val actual = mock.withArguments(*(values.sorted()).toTypedArray()) + + // Then + actual mustBe VerificationHandle(name, listOf(0)) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn4") + fun `Given withSameArguments is called with a FunMockery it returns a VerficationHandle which contains no matches if nothing matches`() { + // Given + val name: String = fixture.fixture() + val mock = FunMockeryStub(name, 0) + + // When + val actual = mock.withSameArguments() + + // Then + actual mustBe VerificationHandle(name, emptyList()) + } + + @Test + @JsName("fn5") + fun `Given withSameArguments is called with a FunMockery it returns a VerficationHandle which contains no matches if nothing matches while delegating the captured values`() { + // Given + val name: String = fixture.fixture() + val mock = FunMockeryStub(name, 1) + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + fixture.listFixture().toTypedArray() + } + + // When + val actual = mock.withSameArguments(fixture.fixture()) + + // Then + actual mustBe VerificationHandle(name, emptyList()) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn6") + fun `Given withSameArguments is called with a FunMockery it returns a VerficationHandle which contains matches if something matches while delegating the captured values`() { + // Given + val name: String = fixture.fixture() + val mock = FunMockeryStub(name, 1) + val values = fixture.listFixture().toTypedArray() + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + values + } + + // When + val actual = mock.withSameArguments(*values) + + // Then + actual mustBe VerificationHandle(name, listOf(0)) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn7") + fun `Given withoutArguments is called with a FunMockery it returns a VerficationHandle which contains no matches if nothing matches`() { + // Given + val name: String = fixture.fixture() + val mock = FunMockeryStub(name, 0) + + // When + val actual = mock.withoutArguments() + + // Then + actual mustBe VerificationHandle(name, emptyList()) + } + + @Test + @JsName("fn8") + fun `Given withoutArguments is called with a FunMockery it returns a VerficationHandle which contains no matches if nothing matches while delegating the captured values`() { + // Given + val name: String = fixture.fixture() + val mock = FunMockeryStub(name, 1) + val values = fixture.listFixture().toTypedArray() + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + values + } + + // When + val actual = mock.withoutArguments(*values) + + // Then + actual mustBe VerificationHandle(name, emptyList()) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn9") + fun `Given withoutArguments is called with a FunMockery it returns a VerficationHandle which contains matches if something matches while delegating the captured values`() { + // Given + val name: String = fixture.fixture() + val mock = FunMockeryStub(name, 1) + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + fixture.listFixture().toTypedArray() + } + + // When + val actual = mock.withoutArguments(fixture.fixture()) + + // Then + actual mustBe VerificationHandle(name, listOf(0)) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn10") + fun `Given wasGotten is called with a PropMockery it returns a VerficationHandle while filtering mismatches`() { + // Given + val name: String = fixture.fixture() + val mock = PropertyMockeryStub(name, 1) + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + KMockContract.GetOrSet.Set(null) + } + + // When + val actual = mock.wasGotten() + + // Then + actual mustBe VerificationHandle(name, emptyList()) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn11") + fun `Given wasGotten is called with a PropMockery it returns a VerficationHandle which contains matches`() { + // Given + val name: String = fixture.fixture() + val mock = PropertyMockeryStub(name, 1) + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + KMockContract.GetOrSet.Get + } + + // When + val actual = mock.wasGotten() + + // Then + actual mustBe VerificationHandle(name, listOf(0)) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn12") + fun `Given wasSet is called with a PropMockery it returns a VerficationHandle while filtering mismatches`() { + // Given + val name: String = fixture.fixture() + val mock = PropertyMockeryStub(name, 1) + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + KMockContract.GetOrSet.Get + } + + // When + val actual = mock.wasSet() + + // Then + actual mustBe VerificationHandle(name, emptyList()) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn13") + fun `Given wasSet is called with a PropMockery it returns a VerficationHandle which contains matches`() { + // Given + val name: String = fixture.fixture() + val mock = PropertyMockeryStub(name, 1) + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + KMockContract.GetOrSet.Set(null) + } + + // When + val actual = mock.wasSet() + + // Then + actual mustBe VerificationHandle(name, listOf(0)) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn14") + fun `Given wasSetTo is called with a PropMockery it returns a VerficationHandle while filtering mismatches`() { + // Given + val name: String = fixture.fixture() + val mock = PropertyMockeryStub(name, 1) + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + KMockContract.GetOrSet.Get + } + + // When + val actual = mock.wasSetTo(fixture.fixture()) + + // Then + actual mustBe VerificationHandle(name, emptyList()) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn15") + fun `Given wasSetTo is called with a PropMockery it returns a VerficationHandle while filtering mismatching Values`() { + // Given + val name: String = fixture.fixture() + val mock = PropertyMockeryStub(name, 1) + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + KMockContract.GetOrSet.Set(fixture.fixture()) + } + + // When + val actual = mock.wasSetTo(fixture.fixture()) + + // Then + actual mustBe VerificationHandle(name, emptyList()) + capturedIndex mustBe 0 + } + + @Test + @JsName("fn16") + fun `Given wasSetTo is called with a PropMockery it returns a VerficationHandle which contains matches`() { + // Given + val name: String = fixture.fixture() + val value: Any = fixture.fixture() + val mock = PropertyMockeryStub(name, 1) + + var capturedIndex: Int? = null + mock.getArgumentsForCall = { givenIndex -> + capturedIndex = givenIndex + + KMockContract.GetOrSet.Set(value) + } + + // When + val actual = mock.wasSetTo(value) + + // Then + actual mustBe VerificationHandle(name, listOf(0)) + capturedIndex mustBe 0 + } +} diff --git a/kmock/src/commonTest/kotlin/tech/antibytes/kmock/VerificationSpec.kt b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/VerificationSpec.kt new file mode 100644 index 00000000..4782eb5c --- /dev/null +++ b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/VerificationSpec.kt @@ -0,0 +1,825 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import tech.antibytes.kmock.KMockContract.Reference +import tech.antibytes.mock.FunMockeryStub +import tech.antibytes.mock.VerifierStub +import tech.antibytes.util.test.fixture.fixture +import tech.antibytes.util.test.fixture.kotlinFixture +import tech.antibytes.util.test.fixture.listFixture +import tech.antibytes.util.test.mustBe +import kotlin.js.JsName +import kotlin.test.Test +import kotlin.test.assertFailsWith + +class VerificationSpec { + private val fixture = kotlinFixture() + + @Test + @JsName("fn0") + fun `Given verify is called it fails if the covered mock does not contain any call`() { + // Given + val mockery = FunMockeryStub(fixture.fixture(), fixture.fixture()) + + // When + val error = assertFailsWith { + verify { + VerificationHandle(mockery.id, emptyList()) + } + } + + error.message mustBe "Call not found." + } + + @Test + @JsName("fn1") + fun `Given verify is called it fails if the covered mock does not have the minimum amount of calls`() { + // Given + val mockery = FunMockeryStub(fixture.fixture(), fixture.fixture()) + val givenCalls = 1 + val expectedCalls = 3 + + // When + val error = assertFailsWith { + verify(atLeast = expectedCalls) { + VerificationHandle(mockery.id, fixture.listFixture(size = givenCalls)) + } + } + + error.message mustBe "Expected at least $expectedCalls calls, but found only $givenCalls." + } + + @Test + @JsName("fn2") + fun `Given verify is called it fails if the covered mock does exceeds the maximum amount of calls`() { + // Given + val mockery = FunMockeryStub(fixture.fixture(), fixture.fixture()) + val givenCalls = 3 + val expectedCalls = 1 + + // When + val error = assertFailsWith { + verify(atMost = expectedCalls) { + VerificationHandle(mockery.id, fixture.listFixture(size = givenCalls)) + } + } + + error.message mustBe "Expected at most $expectedCalls calls, but exceeded with $givenCalls." + } + + @Test + @JsName("fn3") + fun `Given verify is called it fails if the covered mock does not have the exact minimum amount of calls`() { + // Given + val mockery = FunMockeryStub(fixture.fixture(), fixture.fixture()) + val givenCalls = 1 + val expectedCalls = 3 + + // When + val error = assertFailsWith { + verify(exactly = expectedCalls, atLeast = 0) { + VerificationHandle(mockery.id, fixture.listFixture(size = givenCalls)) + } + } + + error.message mustBe "Expected at least $expectedCalls calls, but found only $givenCalls." + } + + @Test + @JsName("fn4") + fun `Given verify is called it fails if the covered mock does exceeds the exact maximum amount of calls`() { + // Given + val mockery = FunMockeryStub(fixture.fixture(), fixture.fixture()) + val givenCalls = 3 + val expectedCalls = 1 + + // When + val error = assertFailsWith { + verify(exactly = expectedCalls, atMost = 0) { + VerificationHandle(mockery.id, fixture.listFixture(size = givenCalls)) + } + } + + error.message mustBe "Expected at most $expectedCalls calls, but exceeded with $givenCalls." + } + + @Test + @JsName("fn5") + fun `Given verify is called it passes if the covered mock matches the requirements`() { + // Given + val mockery = FunMockeryStub(fixture.fixture(), fixture.fixture()) + val givenCalls = 3 + + // When + verify(exactly = givenCalls) { + VerificationHandle(mockery.id, fixture.listFixture(size = givenCalls)) + } + } + + @Test + @JsName("fn6") + fun `Given verifyStrictOrder is called it fails if the amount captured calls does not match the given Order`() { + // Given + val verifierLower = VerifierStub(emptyList()) + val verifierUpper = VerifierStub( + listOf( + Reference(FunMockeryStub(fixture.fixture(), fixture.fixture()), fixture.fixture()), + Reference(FunMockeryStub(fixture.fixture(), fixture.fixture()), fixture.fixture()) + ) + ) + val handle = VerificationHandle(fixture.fixture(), fixture.listFixture()) + + // Then + val errorLowerBound = assertFailsWith { + // When + verifierLower.verifyStrictOrder { + add(handle) + } + } + + errorLowerBound.message mustBe "The given verification chain (has ${verifierLower.references.size} items) does not match the captured calls (1 were captured)." + + // Then + val errorUpperBound = assertFailsWith { + // When + verifierUpper.verifyStrictOrder { + add(handle) + } + } + + errorUpperBound.message mustBe "The given verification chain (has ${verifierUpper.references.size} items) does not match the captured calls (1 were captured)." + } + + @Test + @JsName("fn7") + fun `Given verifyStrictOrder is called it fails if the referenced Functions do not match`() { + // Given + val handleMockery = FunMockeryStub( + fixture.fixture(), + calls = fixture.fixture() + ) + val referenceMockery = FunMockeryStub( + fixture.fixture(), + calls = fixture.fixture() + ) + + val handle = VerificationHandle( + handleMockery.id, + listOf(1) + ) + + val verifier = VerifierStub( + listOf(Reference(referenceMockery, 0)) + ) + + // Then + val error = assertFailsWith { + // When + verifier.verifyStrictOrder { + add(handle) + } + } + + error.message mustBe "Excepted ${handleMockery.id}, but got ${referenceMockery.id}." + } + + @Test + @JsName("fn8") + fun `Given verifyStrictOrder is called it fails if the referenced CallIndicies do not match`() { + // Given + val name: String = fixture.fixture() + val expectedCallIdx = 0 + val actualCallIdx = 1 + val referenceMockery = FunMockeryStub( + name, + calls = fixture.fixture() + ) + + val handle = VerificationHandle( + name, + listOf(expectedCallIdx) + ) + + val verifier = VerifierStub( + listOf(Reference(referenceMockery, actualCallIdx)) + ) + + // Then + val error = assertFailsWith { + // When + verifier.verifyStrictOrder { + add(handle) + } + } + + error.message mustBe "Excepted the $expectedCallIdx of $name, but the $actualCallIdx was referenced." + } + + @Test + @JsName("fn9") + fun `Given verifyStrictOrder is called it fails if the referenced CallIndicies do not match on multiple values`() { + // Given + val name: String = fixture.fixture() + val expectedCallIdx = 1 + val actualCallIdx = 2 + val referenceMockery = FunMockeryStub( + name, + calls = fixture.fixture() + ) + + val handle = VerificationHandle( + name, + listOf( + 0, + expectedCallIdx + ) + ) + + val verifier = VerifierStub( + listOf( + Reference(referenceMockery, 0), + Reference(referenceMockery, actualCallIdx) + ) + ) + + // Then + val error = assertFailsWith { + // When + verifier.verifyStrictOrder { + add(handle) + add(handle) + } + } + + error.message mustBe "Excepted the $expectedCallIdx of $name, but the $actualCallIdx was referenced." + } + + @Test + @JsName("fn10") + fun `Given verifyStrictOrder is called it fails if the referenced CallIndicies do not match on multiple values with various length`() { + // Given + val name: String = fixture.fixture() + val expectedCallIdx = 1 + val actualCallIdx = 2 + val referenceMockery = FunMockeryStub( + name, + calls = fixture.fixture() + ) + + val handle1 = VerificationHandle( + name, + listOf( + 0, + ) + ) + + val handle2 = VerificationHandle( + name, + listOf( + expectedCallIdx, + ) + ) + + val verifier = VerifierStub( + listOf( + Reference(referenceMockery, 0), + Reference(referenceMockery, actualCallIdx) + ) + ) + + // Then + val error = assertFailsWith { + // When + verifier.verifyStrictOrder { + add(handle1) + add(handle2) + } + } + + error.message mustBe "Excepted the $expectedCallIdx of $name, but the $actualCallIdx was referenced." + } + + @Test + @JsName("fn11") + fun `Given verifyStrictOrder is called it fails if the referenced CallIndicies do not match on multiple values with various length while exceeding the range`() { + // Given + val name: String = fixture.fixture() + val expectedCallIdx = 2 + val referenceMockery = FunMockeryStub( + name, + calls = fixture.fixture() + ) + + val handle1 = VerificationHandle( + name, + listOf( + 0, + ) + ) + + val handle2 = VerificationHandle( + name, + listOf( + expectedCallIdx + 1, + ) + ) + + val handle3 = VerificationHandle( + name, + listOf( + expectedCallIdx, + ) + ) + + val verifier = VerifierStub( + listOf( + Reference(referenceMockery, 0), + Reference(referenceMockery, expectedCallIdx - 1), + Reference(referenceMockery, expectedCallIdx), + ) + ) + + // Then + val error = assertFailsWith { + // When + verifier.verifyStrictOrder { + add(handle1) + add(handle2) + add(handle3) + } + } + + error.message mustBe "The captured calls of $name exceeds the captured calls." + } + + @Test + @JsName("fn12") + fun `Given verifyStrictOrder is called it fails if the referenced CallIndicies do not match on multiple values with mixed References`() { + // Given + val name1: String = fixture.fixture() + val name2: String = fixture.fixture() + val expectedCallIdx = 2 + val referenceMockery1 = FunMockeryStub( + name1, + calls = fixture.fixture() + ) + val referenceMockery2 = FunMockeryStub( + name2, + calls = fixture.fixture() + ) + + val handle1 = VerificationHandle( + name1, + listOf( + 0, + ) + ) + + val handle2 = VerificationHandle( + name2, + listOf( + 0, + ) + ) + + val handle3 = VerificationHandle( + name1, + listOf( + expectedCallIdx, + ) + ) + + val verifier = VerifierStub( + listOf( + Reference(referenceMockery1, 0), + Reference(referenceMockery2, 0), + Reference(referenceMockery1, expectedCallIdx), + ) + ) + + // Then + val error = assertFailsWith { + // When + verifier.verifyStrictOrder { + add(handle1) + add(handle2) + add(handle3) + } + } + + error.message mustBe "The captured calls of $name1 exceeds the captured calls." + } + + @Test + @JsName("fn13") + fun `Given verifyStrictOrder is called it passes if the referenced CallIndicies match on multiple values with mixed References`() { + // Given + val name1: String = fixture.fixture() + val name2: String = fixture.fixture() + val expectedCallIdx = 1 + val referenceMockery1 = FunMockeryStub( + name1, + calls = fixture.fixture() + ) + val referenceMockery2 = FunMockeryStub( + name2, + calls = fixture.fixture() + ) + + val handle1 = VerificationHandle( + name1, + listOf( + 0, + ) + ) + + val handle2 = VerificationHandle( + name2, + listOf( + 0, + ) + ) + + val handle3 = VerificationHandle( + name1, + listOf( + expectedCallIdx, + ) + ) + + val verifier = VerifierStub( + listOf( + Reference(referenceMockery1, 0), + Reference(referenceMockery2, 0), + Reference(referenceMockery1, expectedCallIdx), + ) + ) + + // When + verifier.verifyStrictOrder { + add(handle1) + add(handle2) + add(handle3) + } + } + + @Test + @JsName("fn14") + fun `Given verifyOrder is called it fails if the amount captured calls is smaller than the given Order`() { + // Given + val verifier = VerifierStub(emptyList()) + val handle = VerificationHandle(fixture.fixture(), fixture.listFixture()) + + // Then + val error = assertFailsWith { + // When + verifier.verifyOrder { + add(handle) + } + } + + error.message mustBe "The given verification chain (has ${verifier.references.size} items) is exceeding the captured calls (1 were captured)." + } + + @Test + @JsName("fn15") + fun `Given verifyOrder is called it fails if the captured calls does not contain the mentioned function call`() { + val handleMockery = FunMockeryStub( + fixture.fixture(), + calls = fixture.fixture() + ) + val referenceMockery = FunMockeryStub( + fixture.fixture(), + calls = fixture.fixture() + ) + + val handle = VerificationHandle( + handleMockery.id, + listOf(1) + ) + + val verifier = VerifierStub( + listOf(Reference(referenceMockery, 0)) + ) + + // Then + val error = assertFailsWith { + // When + verifier.verifyOrder { + add(handle) + } + } + + error.message mustBe "Last referred invocation of ${handleMockery.id} was not found." + } + + @Test + @JsName("fn16") + fun `Given verifyOrder is called it passes if the referenced CallIndicies was found`() { + // Given + val name: String = fixture.fixture() + val expectedCallIdx = 0 + val referenceMockery = FunMockeryStub( + name, + calls = fixture.fixture() + ) + + val handle = VerificationHandle( + name, + listOf(expectedCallIdx) + ) + + val verifier = VerifierStub( + listOf(Reference(referenceMockery, expectedCallIdx)) + ) + + // When + verifier.verifyStrictOrder { + add(handle) + } + } + + @Test + @JsName("fn17") + fun `Given verifyOrder is called it fails if the referenced CallIndicies was not found for multiple calls`() { + // Given + val name: String = fixture.fixture() + val expectedCallIdx1 = 1 + val expectedCallIdx2 = 0 + val referenceMockery = FunMockeryStub( + name, + calls = fixture.fixture() + ) + + val handle1 = VerificationHandle( + name, + listOf(expectedCallIdx1) + ) + + val handle2 = VerificationHandle( + name, + listOf(expectedCallIdx2) + ) + + val verifier = VerifierStub( + listOf( + Reference(referenceMockery, expectedCallIdx1), + Reference(referenceMockery, expectedCallIdx2) + ) + ) + + // Then + val error = assertFailsWith { + // When + verifier.verifyOrder { + add(handle1) + add(handle2) + } + } + + error.message mustBe "Last referred invocation of $name was not found." + } + + @Test + @JsName("fn18") + fun `Given verifyOrder is called it passes if the referenced CallIndicies was found for multiple calls`() { + // Given + val name: String = fixture.fixture() + val expectedCallIdx1 = 0 + val expectedCallIdx2 = 1 + val referenceMockery = FunMockeryStub( + name, + calls = fixture.fixture() + ) + + val handle1 = VerificationHandle( + name, + listOf(expectedCallIdx1) + ) + + val handle2 = VerificationHandle( + name, + listOf(expectedCallIdx2) + ) + + val verifier = VerifierStub( + listOf( + Reference(referenceMockery, expectedCallIdx1), + Reference(referenceMockery, expectedCallIdx2) + ) + ) + + // When + verifier.verifyOrder { + add(handle1) + add(handle2) + } + } + + @Test + @JsName("fn19") + fun `Given verifyOrder is called it passes if the referenced CallIndicies was found for multiple calls with various length`() { + // Given + val name: String = fixture.fixture() + val expectedCallIdx1 = 2 + val expectedCallIdx2 = 3 + + val referenceMockery = FunMockeryStub( + name, + calls = fixture.fixture() + ) + + val handle1 = VerificationHandle( + name, + listOf( + 0, + 1, + expectedCallIdx1 + ) + ) + + val handle2 = VerificationHandle( + name, + listOf( + 0, + expectedCallIdx2 + ) + ) + + val verifier = VerifierStub( + listOf( + Reference(referenceMockery, 0), + Reference(referenceMockery, 1), + Reference(referenceMockery, expectedCallIdx1), + Reference(referenceMockery, expectedCallIdx2) + ) + ) + + // When + verifier.verifyOrder { + add(handle1) + add(handle2) + } + } + + @Test + @JsName("fn20") + fun `Given verifyOrder is called it fails if the referenced CallIndicies was not found for multiple calls with various length`() { + // Given + val name: String = fixture.fixture() + val expectedCallIdx1 = 3 + val expectedCallIdx2 = 2 + val referenceMockery = FunMockeryStub( + name, + calls = fixture.fixture() + ) + + val handle1 = VerificationHandle( + name, + listOf( + expectedCallIdx1 + ) + ) + + val handle2 = VerificationHandle( + name, + listOf( + expectedCallIdx2 + ) + ) + + val verifier = VerifierStub( + listOf( + Reference(referenceMockery, expectedCallIdx1), + Reference(referenceMockery, expectedCallIdx2) + ) + ) + + // Then + val error = assertFailsWith { + // When + verifier.verifyOrder { + add(handle1) + add(handle2) + } + } + + error.message mustBe "Last referred invocation of $name was not found." + } + + @Test + @JsName("fn21") + fun `Given verifyOrder is called it passes if the referenced CallIndicies match on multiple values with mixed References`() { + // Given + val name1: String = fixture.fixture() + val name2: String = fixture.fixture() + val expectedCallIdx = 2 + val referenceMockery1 = FunMockeryStub( + name1, + calls = fixture.fixture() + ) + val referenceMockery2 = FunMockeryStub( + name2, + calls = fixture.fixture() + ) + + val handle1 = VerificationHandle( + name1, + listOf( + 0, + ) + ) + + val handle2 = VerificationHandle( + name2, + listOf( + 0, + ) + ) + + val handle3 = VerificationHandle( + name1, + listOf( + expectedCallIdx, + ) + ) + + val verifier = VerifierStub( + listOf( + Reference(referenceMockery1, 0), + Reference(referenceMockery2, 0), + Reference(referenceMockery1, expectedCallIdx), + ) + ) + + // When + verifier.verifyOrder { + add(handle1) + add(handle2) + add(handle3) + } + } + + @Test + @JsName("fn22") + fun `Given verifyOrder is called it fails if the referenced CallIndicies does not match on multiple values with mixed References`() { + // Given + val name1: String = fixture.fixture() + val name2: String = fixture.fixture() + val expectedCallIdx = 2 + val referenceMockery1 = FunMockeryStub( + name1, + calls = fixture.fixture() + ) + val referenceMockery2 = FunMockeryStub( + name2, + calls = fixture.fixture() + ) + + val handle1 = VerificationHandle( + name1, + listOf( + 0, + ) + ) + + val handle2 = VerificationHandle( + name2, + listOf( + 0, + ) + ) + + val handle3 = VerificationHandle( + name1, + listOf( + expectedCallIdx + 1, + ) + ) + + val verifier = VerifierStub( + listOf( + Reference(referenceMockery1, 0), + Reference(referenceMockery2, 0), + Reference(referenceMockery1, expectedCallIdx), + ) + ) + + // Then + val error = assertFailsWith { + // When + verifier.verifyOrder { + add(handle1) + add(handle2) + add(handle3) + } + } + + error.message mustBe "Last referred invocation of $name1 was not found." + } +} diff --git a/kmock/src/commonTest/kotlin/tech/antibytes/kmock/VerifierSpec.kt b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/VerifierSpec.kt new file mode 100644 index 00000000..782a3a18 --- /dev/null +++ b/kmock/src/commonTest/kotlin/tech/antibytes/kmock/VerifierSpec.kt @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.kmock + +import tech.antibytes.mock.FunMockeryStub +import tech.antibytes.util.test.coroutine.AsyncTestReturnValue +import tech.antibytes.util.test.coroutine.TestScopeDispatcher +import tech.antibytes.util.test.coroutine.asyncMultiBlock +import tech.antibytes.util.test.coroutine.runBlockingTestInContext +import tech.antibytes.util.test.fixture.fixture +import tech.antibytes.util.test.fixture.kotlinFixture +import tech.antibytes.util.test.fulfils +import tech.antibytes.util.test.mustBe +import tech.antibytes.util.test.sameAs +import kotlin.js.JsName +import kotlin.math.absoluteValue +import kotlin.test.Test + +class VerifierSpec { + private val fixture = kotlinFixture() + private val testScope1 = TestScopeDispatcher.dispatch("test1") + private val testScope2 = TestScopeDispatcher.dispatch("test2") + + @Test + @JsName("fn0") + fun `It fulfils Verifier`() { + Verifier() fulfils KMockContract.Verifier::class + } + + @Test + @JsName("fn1") + fun `It fulfils Collector`() { + Verifier() fulfils KMockContract.Collector::class + } + + @Test + @JsName("fn2") + fun `It has a emptyMap of references by default`() { + Verifier().references mustBe emptyList() + } + + @Test + @JsName("fn3") + fun `Given add reference is called it adds a refrenence entry threadsafe`(): AsyncTestReturnValue { + // Given + val index: Int = fixture.fixture().absoluteValue + val mockery = FunMockeryStub(fixture.fixture(), fixture.fixture()) + + val verifier = Verifier() + + // When + runBlockingTestInContext(testScope1.coroutineContext) { + verifier.addReference(mockery, index) + } + + // Then + runBlockingTestInContext(testScope2.coroutineContext) { + verifier.references.first().mockery sameAs mockery + verifier.references.first().callIndex mustBe index + } + + return asyncMultiBlock + } +} diff --git a/kmock/src/commonTest/kotlin/tech/antibytes/mock/FunMockeryStub.kt b/kmock/src/commonTest/kotlin/tech/antibytes/mock/FunMockeryStub.kt new file mode 100644 index 00000000..ff0adb2c --- /dev/null +++ b/kmock/src/commonTest/kotlin/tech/antibytes/mock/FunMockeryStub.kt @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.mock + +import tech.antibytes.kmock.KMockContract +import tech.antibytes.util.test.MockError + +class FunMockeryStub( + override val id: String, + override val calls: Int, + var getArgumentsForCall: ((Int) -> Array?)? = null +) : KMockContract.SyncFunMockery Any> { + override var returnValue: Any + get() = TODO("Not yet implemented") + set(_) = TODO("Not yet implemented") + override var returnValues: List + get() = TODO("Not yet implemented") + set(_) = TODO("Not yet implemented") + override var sideEffect: () -> Any + get() = TODO("Not yet implemented") + set(_) = TODO("Not yet implemented") + + override fun getArgumentsForCall(callIndex: Int): Array? { + return if (getArgumentsForCall == null) { + throw MockError.MissingStub("Missing sideeffect getArgumentsForCall") + } else { + getArgumentsForCall!!.invoke(callIndex) + } + } + + override fun invoke(): Any { + TODO("Not yet implemented") + } + + override fun invoke(arg0: Arg0): Any { + TODO("Not yet implemented") + } + + override fun invoke(arg0: Arg0, arg1: Arg1): Any { + TODO("Not yet implemented") + } + + override fun invoke(arg0: Arg0, arg1: Arg1, arg2: Arg2): Any { + TODO("Not yet implemented") + } + + override fun invoke(arg0: Arg0, arg1: Arg1, arg2: Arg2, arg3: Arg3): Any { + TODO("Not yet implemented") + } + + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4 + ): Any { + TODO("Not yet implemented") + } + + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5 + ): Any { + TODO("Not yet implemented") + } + + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6 + ): Any { + TODO("Not yet implemented") + } + + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7 + ): Any { + TODO("Not yet implemented") + } + + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8 + ): Any { + TODO("Not yet implemented") + } + + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9 + ): Any { + TODO("Not yet implemented") + } + + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10 + ): Any { + TODO("Not yet implemented") + } + + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10, + arg11: Arg11 + ): Any { + TODO("Not yet implemented") + } + + override fun invoke( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + arg10: Arg10, + arg11: Arg11, + arg12: Arg12 + ): Any { + TODO("Not yet implemented") + } +} diff --git a/kmock/src/commonTest/kotlin/tech/antibytes/mock/PropertyMockeryStub.kt b/kmock/src/commonTest/kotlin/tech/antibytes/mock/PropertyMockeryStub.kt new file mode 100644 index 00000000..3c58bbdd --- /dev/null +++ b/kmock/src/commonTest/kotlin/tech/antibytes/mock/PropertyMockeryStub.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.mock + +import tech.antibytes.kmock.KMockContract +import tech.antibytes.kmock.KMockContract.GetOrSet +import tech.antibytes.util.test.MockError + +class PropertyMockeryStub( + override val id: String, + override val calls: Int, + var getArgumentsForCall: ((Int) -> GetOrSet)? = null +) : KMockContract.PropMockery { + override fun getArgumentsForCall(callIndex: Int): GetOrSet { + return if (getArgumentsForCall == null) { + throw MockError.MissingStub("Missing sideeffect getArgumentsForCall") + } else { + getArgumentsForCall!!.invoke(callIndex) + } + } + + override var get: Any + get() = TODO("Not yet implemented") + set(_) = TODO("Not yet implemented") + override var getMany: List + get() = TODO("Not yet implemented") + set(_) = TODO("Not yet implemented") + override var set: (Any) -> Unit + get() = TODO("Not yet implemented") + set(_) = TODO("Not yet implemented") + + override fun onGet(): Any { + TODO("Not yet implemented") + } + + override fun onSet(value: Any) { + TODO("Not yet implemented") + } +} diff --git a/kmock/src/commonTest/kotlin/tech/antibytes/mock/VerifierStub.kt b/kmock/src/commonTest/kotlin/tech/antibytes/mock/VerifierStub.kt new file mode 100644 index 00000000..52ba1900 --- /dev/null +++ b/kmock/src/commonTest/kotlin/tech/antibytes/mock/VerifierStub.kt @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved. + * + * Use of this source code is governed by Apache v2.0 + */ + +package tech.antibytes.mock + +import tech.antibytes.kmock.KMockContract + +class VerifierStub( + override val references: List +) : KMockContract.Verifier