From 4c936b18ed73a12fe44ca56b7bdc26e53dfc7a0f Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Wed, 20 Jul 2022 21:42:15 +0200 Subject: [PATCH 01/17] #152 add failing tests for value classes --- .../test/kotlin/io/mockk/it/ValueClassTest.kt | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt index 6acf64f9f..66fa6bec6 100644 --- a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt +++ b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt @@ -1,6 +1,7 @@ package io.mockk.it import io.mockk.* +import kotlin.jvm.JvmInline import kotlin.test.Test import kotlin.test.assertEquals @@ -43,6 +44,43 @@ class ValueClassTest { verify { mock.processValue(DummyValue(1)) } } + + @Test + fun `any matcher for value class`() { + val mock = mockk(relaxed = true) + val givenResult = 1 + every { mock.doSomething(any()) } returns givenResult + + val result = mock.doSomething(ValueDummy("moin")) + + assertEquals(givenResult, result) + } + + @Test + fun `slot for value class`() { + val mock = mockk(relaxed = true) + val slot = slot() + val givenResult = 1 + every { mock.doSomething(capture(slot)) } returns givenResult + + val givenParameter = ValueDummy("s") + + val result = mock.doSomething(givenParameter) + + assertEquals(givenResult, result) + assertEquals(givenParameter, slot.captured) + } + + @Test + fun `value class as return value`() { + val mock = mockk(relaxed = true) + val givenResult = ValueDummy("moin") + every { mock.getSomething() } returns givenResult + + val result = mock.getSomething() + + assertEquals(givenResult, result) + } } // TODO should be value class in kotlin 1.5+ @@ -54,3 +92,11 @@ private class DummyService { fun processValue(value: DummyValue) = DummyValue(0) } + +@JvmInline +value class ValueDummy(val value: String) + +interface ValueServiceDummy { + fun doSomething(value: ValueDummy): Int + fun getSomething(): ValueDummy +} From 514e01420db260f67803fd14ef46ef62db1bd210 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 22 Jul 2022 18:41:06 +0200 Subject: [PATCH 02/17] add support for value class any() matcher --- .../main/kotlin/io/mockk/ValueClassSupport.kt | 11 ---- .../kotlin/io/mockk/InternalPlatformDsl.kt | 23 ++++++++- .../src/main/kotlin/io/mockk/Matchers.kt | 6 +-- .../kotlin/io/mockk/InternalPlatformDsl.kt | 50 ++++++++++++++++++- .../impl/recording/SignatureValueGenerator.kt | 10 +++- .../impl/recording/states/RecordingState.kt | 16 +++--- .../test/kotlin/io/mockk/it/ValueClassTest.kt | 6 ++- .../recording/JvmSignatureValueGenerator.kt | 31 ++++++++++-- 8 files changed, 121 insertions(+), 32 deletions(-) diff --git a/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt b/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt index 1781141b5..2496d1e19 100644 --- a/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt +++ b/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt @@ -72,14 +72,3 @@ private fun KClass.isValueClass() = try { } catch (_: Throwable) { false } - -/** - * POLYFILL for kotlin version < 1.5 - * will be shadowed by implementation in kotlin SDK 1.5+ - * - * @return true if this is an inline class, else false - */ -private val KClass.isValue: Boolean - get() = !isData && - primaryConstructor?.parameters?.size == 1 && - java.declaredMethods.any { it.name == "box-impl" } diff --git a/dsl/common/src/main/kotlin/io/mockk/InternalPlatformDsl.kt b/dsl/common/src/main/kotlin/io/mockk/InternalPlatformDsl.kt index ddea66820..44dfb3682 100644 --- a/dsl/common/src/main/kotlin/io/mockk/InternalPlatformDsl.kt +++ b/dsl/common/src/main/kotlin/io/mockk/InternalPlatformDsl.kt @@ -1,6 +1,7 @@ package io.mockk import kotlin.coroutines.Continuation +import kotlin.reflect.KClass expect object InternalPlatformDsl { fun identityHashCode(obj: Any): Int @@ -35,6 +36,26 @@ expect object InternalPlatformDsl { fun counter(): InternalCounter fun coroutineCall(lambda: suspend () -> T): CoroutineCall + + /** + * Get the [KClass] of the single value that a `value class` contains. + * + * The result might also be a value class! So check recursively, if necessary. + * + * @return [KClass] of boxed value, if this is `value class`, else [cls]. + */ + fun unboxClass(cls: KClass<*>): KClass<*> + + /** + * Normally this simply casts [arg] to `T` + * + * However, if `T` is a `value class`es (of type [cls]) it will construct a new instance of the + * class, and set [arg] as the value. + */ + actual fun boxCast( + cls: KClass<*>, + arg: Any, + ): T } interface CoroutineCall { @@ -50,4 +71,4 @@ interface InternalCounter { val value: Long fun increment(): Long -} \ No newline at end of file +} diff --git a/dsl/common/src/main/kotlin/io/mockk/Matchers.kt b/dsl/common/src/main/kotlin/io/mockk/Matchers.kt index c9666ee08..100cad605 100644 --- a/dsl/common/src/main/kotlin/io/mockk/Matchers.kt +++ b/dsl/common/src/main/kotlin/io/mockk/Matchers.kt @@ -140,17 +140,16 @@ data class CaptureNullableMatcher( */ data class CapturingSlotMatcher( val captureSlot: CapturingSlot, - override val argumentType: KClass<*> + override val argumentType: KClass<*>, ) : Matcher, CapturingMatcher, TypedMatcher, EquivalentMatcher { override fun equivalent(): Matcher = ConstantMatcher(true) - @Suppress("UNCHECKED_CAST") override fun capture(arg: Any?) { if (arg == null) { captureSlot.isNull = true } else { captureSlot.isNull = false - captureSlot.captured = arg as T + captureSlot.captured = InternalPlatformDsl.boxCast(argumentType, arg) } captureSlot.isCaptured = true } @@ -473,4 +472,3 @@ fun CompositeMatcher<*>.captureSubMatchers(arg: Any?) { .forEach { it.capture(arg) } } } - diff --git a/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt b/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt index 6854e59d9..fdf1228b9 100644 --- a/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt +++ b/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt @@ -1,6 +1,5 @@ package io.mockk -import kotlinx.coroutines.runBlocking import java.lang.reflect.AccessibleObject import java.lang.reflect.InvocationTargetException import java.lang.reflect.Method @@ -13,10 +12,13 @@ import kotlin.reflect.KProperty1 import kotlin.reflect.KType import kotlin.reflect.KTypeParameter import kotlin.reflect.full.allSuperclasses +import kotlin.reflect.full.declaredMemberProperties import kotlin.reflect.full.functions import kotlin.reflect.full.memberProperties +import kotlin.reflect.full.primaryConstructor import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.javaMethod +import kotlinx.coroutines.runBlocking actual object InternalPlatformDsl { actual fun identityHashCode(obj: Any): Int = System.identityHashCode(obj) @@ -214,6 +216,29 @@ actual object InternalPlatformDsl { } actual fun coroutineCall(lambda: suspend () -> T): CoroutineCall = JvmCoroutineCall(lambda) + + actual fun unboxClass(cls: KClass<*>): KClass<*> { + if (!cls.isValue) return cls + + // get backing field + val backingField = cls.valueField() + + // get boxed value + return backingField.returnType.classifier as KClass<*> + } + + @Suppress("UNCHECKED_CAST") + actual fun boxCast( + cls: KClass<*>, + arg: Any, + ): T { + return if (cls.isValue) { + val constructor = cls.primaryConstructor!!.apply { isAccessible = true } + constructor.call(arg) as T + } else { + arg as T + } + } } class JvmCoroutineCall(private val lambda: suspend () -> T) : CoroutineCall { @@ -232,3 +257,26 @@ class JvmCoroutineCall(private val lambda: suspend () -> T) : CoroutineCall, KProperty1>() + +private fun KClass.valueField(): KProperty1 { + @Suppress("UNCHECKED_CAST") + return valueClassFieldCache.getOrPut(this) { + require(isValue) { "$this is not a value class" } + + // value classes always have a primary constructor... + val constructor = primaryConstructor!!.apply { isAccessible = true } + // ...and exactly one constructor parameter + val constructorParameter = constructor.parameters.first() + // ...with a backing field + val backingField = declaredMemberProperties + .first { it.name == constructorParameter.name } + .apply { isAccessible = true } + + backingField + } as KProperty1 +} diff --git a/mockk/common/src/main/kotlin/io/mockk/impl/recording/SignatureValueGenerator.kt b/mockk/common/src/main/kotlin/io/mockk/impl/recording/SignatureValueGenerator.kt index 53f21cc14..5605a413e 100644 --- a/mockk/common/src/main/kotlin/io/mockk/impl/recording/SignatureValueGenerator.kt +++ b/mockk/common/src/main/kotlin/io/mockk/impl/recording/SignatureValueGenerator.kt @@ -1,7 +1,13 @@ package io.mockk.impl.recording +import io.mockk.impl.instantiation.AbstractInstantiator +import io.mockk.impl.instantiation.AnyValueGenerator import kotlin.reflect.KClass interface SignatureValueGenerator { - fun signatureValue(cls: KClass, orInstantiateVia: () -> T): T -} \ No newline at end of file + fun signatureValue( + cls: KClass, + anyValueGeneratorProvider: () -> AnyValueGenerator, + instantiator: AbstractInstantiator, + ): T +} diff --git a/mockk/common/src/main/kotlin/io/mockk/impl/recording/states/RecordingState.kt b/mockk/common/src/main/kotlin/io/mockk/impl/recording/states/RecordingState.kt index b58b45b23..e78bb1b94 100644 --- a/mockk/common/src/main/kotlin/io/mockk/impl/recording/states/RecordingState.kt +++ b/mockk/common/src/main/kotlin/io/mockk/impl/recording/states/RecordingState.kt @@ -42,15 +42,17 @@ abstract class RecordingState(recorder: CommonCallRecorder) : CallRecordingState recorder.calls.addAll(detector.calls) } - @Suppress("UNCHECKED_CAST") override fun matcher(matcher: Matcher<*>, cls: KClass): T { - val signatureValue = recorder.signatureValueGenerator.signatureValue(cls) { - recorder.anyValueGenerator().anyValue(cls, isNullable = false) { - recorder.instantiator.instantiate(cls) - } as T - } + val signatureValue = recorder.signatureValueGenerator.signatureValue( + cls, + recorder.anyValueGenerator, + recorder.instantiator, + ) + + val packRef: Any = InternalPlatform.packRef(signatureValue) + ?: error("null packRef for $cls signature $signatureValue") - builder().addMatcher(matcher, InternalPlatform.packRef(signatureValue)!!) + builder().addMatcher(matcher, packRef) return signatureValue } diff --git a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt index 66fa6bec6..cb1d4869e 100644 --- a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt +++ b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt @@ -2,6 +2,7 @@ package io.mockk.it import io.mockk.* import kotlin.jvm.JvmInline +import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertEquals @@ -57,6 +58,7 @@ class ValueClassTest { } @Test + @Ignore // TODO I had this working at one point... commenting it out now to see if the tests pass on CI/CD fun `slot for value class`() { val mock = mockk(relaxed = true) val slot = slot() @@ -84,9 +86,9 @@ class ValueClassTest { } // TODO should be value class in kotlin 1.5+ -private inline class DummyValue(val value: Int) +inline class DummyValue(val value: Int) -private class DummyService { +class DummyService { fun requestValue() = DummyValue(0) diff --git a/mockk/jvm/src/main/kotlin/io/mockk/impl/recording/JvmSignatureValueGenerator.kt b/mockk/jvm/src/main/kotlin/io/mockk/impl/recording/JvmSignatureValueGenerator.kt index 07b1023ae..6428d98e7 100644 --- a/mockk/jvm/src/main/kotlin/io/mockk/impl/recording/JvmSignatureValueGenerator.kt +++ b/mockk/jvm/src/main/kotlin/io/mockk/impl/recording/JvmSignatureValueGenerator.kt @@ -1,11 +1,29 @@ package io.mockk.impl.recording -import java.util.* +import io.mockk.InternalPlatformDsl +import io.mockk.impl.instantiation.AbstractInstantiator +import io.mockk.impl.instantiation.AnyValueGenerator +import java.util.Random import kotlin.reflect.KClass import kotlin.reflect.full.cast +import kotlin.reflect.full.primaryConstructor +import kotlin.reflect.jvm.isAccessible class JvmSignatureValueGenerator(val rnd: Random) : SignatureValueGenerator { - override fun signatureValue(cls: KClass, orInstantiateVia: () -> T): T { + override fun signatureValue( + cls: KClass, + anyValueGeneratorProvider: () -> AnyValueGenerator, + instantiator: AbstractInstantiator, + ): T { + + if (cls.isValue) { + val valueCls = InternalPlatformDsl.unboxClass(cls) + val valueSig = signatureValue(valueCls, anyValueGeneratorProvider, instantiator) + + val constructor = cls.primaryConstructor!!.apply { isAccessible = true } + return constructor.call(valueSig) + } + return cls.cast( when (cls) { java.lang.Boolean::class -> rnd.nextBoolean() @@ -17,8 +35,13 @@ class JvmSignatureValueGenerator(val rnd: Random) : SignatureValueGenerator { java.lang.Float::class -> rnd.nextFloat() java.lang.Double::class -> rnd.nextDouble() java.lang.String::class -> rnd.nextLong().toString(16) - else -> orInstantiateVia() + + else -> + @Suppress("UNCHECKED_CAST") + anyValueGeneratorProvider().anyValue(cls, isNullable = false) { + instantiator.instantiate(cls) + } as T } ) } -} \ No newline at end of file +} From cd368da269d56da179a409ae54d7990bcf04fd8b Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 22 Jul 2022 18:49:02 +0200 Subject: [PATCH 03/17] fix value classes & slots --- dsl/common/src/main/kotlin/io/mockk/API.kt | 10 ++++++---- .../src/test/kotlin/io/mockk/it/ValueClassTest.kt | 1 - 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/dsl/common/src/main/kotlin/io/mockk/API.kt b/dsl/common/src/main/kotlin/io/mockk/API.kt index 2c095eaff..0c802aceb 100644 --- a/dsl/common/src/main/kotlin/io/mockk/API.kt +++ b/dsl/common/src/main/kotlin/io/mockk/API.kt @@ -3557,11 +3557,13 @@ interface TypedMatcher { val argumentType: KClass<*> fun checkType(arg: Any?): Boolean { - if (argumentType.simpleName === null) { - return true + return when { + argumentType.simpleName === null -> true + else -> { + val unboxedClass = InternalPlatformDsl.unboxClass(argumentType) + return unboxedClass.isInstance(arg) + } } - - return argumentType.isInstance(arg) } } diff --git a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt index cb1d4869e..d09b162a1 100644 --- a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt +++ b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt @@ -58,7 +58,6 @@ class ValueClassTest { } @Test - @Ignore // TODO I had this working at one point... commenting it out now to see if the tests pass on CI/CD fun `slot for value class`() { val mock = mockk(relaxed = true) val slot = slot() From 32ade90a2e7dba3f65a891b3bccc2c3e99b5c132 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 22 Jul 2022 19:11:26 +0200 Subject: [PATCH 04/17] update 'inline class' to 'value class' --- mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt index d09b162a1..a066ef2cd 100644 --- a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt +++ b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt @@ -2,7 +2,6 @@ package io.mockk.it import io.mockk.* import kotlin.jvm.JvmInline -import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertEquals @@ -84,8 +83,8 @@ class ValueClassTest { } } -// TODO should be value class in kotlin 1.5+ -inline class DummyValue(val value: Int) +@JvmInline +value class DummyValue(val value: Int) class DummyService { From 0ffba57163a687a04acb72d4c7d2b36abd03b677 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Fri, 22 Jul 2022 19:16:46 +0200 Subject: [PATCH 05/17] update TODO, apply some auto-fixes to remove warnings --- dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt b/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt index fdf1228b9..b9c1e7d4f 100644 --- a/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt +++ b/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt @@ -46,7 +46,7 @@ actual object InternalPlatformDsl { kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED -> "SUSPEND_MARKER" is Continuation<*> -> "continuation {}" is KClass<*> -> this.simpleName ?: "" - is Method -> name + "(" + parameterTypes.map { it.simpleName }.joinToString() + ")" + is Method -> name + "(" + parameterTypes.joinToString { it.simpleName } + ")" is Function<*> -> "lambda {}" else -> toString() } @@ -243,7 +243,7 @@ actual object InternalPlatformDsl { class JvmCoroutineCall(private val lambda: suspend () -> T) : CoroutineCall { companion object { - val callMethod = JvmCoroutineCall::class.java.getMethod("callCoroutine", Continuation::class.java) + val callMethod: Method = JvmCoroutineCall::class.java.getMethod("callCoroutine", Continuation::class.java) } suspend fun callCoroutine() = lambda() @@ -258,8 +258,7 @@ class JvmCoroutineCall(private val lambda: suspend () -> T) : CoroutineCall, KProperty1>() From f2e6c7ad9ee3fd553dd958cace459c6e634b2a60 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sat, 23 Jul 2022 11:04:45 +0200 Subject: [PATCH 06/17] refactor mock-dsl-jvm to re-use ValueClassSupport code, and add TODOs to de-dupe them #857 --- .../main/kotlin/io/mockk/ValueClassSupport.kt | 3 + .../main/kotlin/io/mockk/ValueClassSupport.kt | 3 + .../kotlin/io/mockk/InternalPlatformDsl.kt | 33 +------ .../main/kotlin/io/mockk/ValueClassSupport.kt | 85 +++++++++++++++++++ 4 files changed, 93 insertions(+), 31 deletions(-) create mode 100644 dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt diff --git a/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt b/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt index 1781141b5..a3de5d4fd 100644 --- a/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt +++ b/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt @@ -6,6 +6,9 @@ import kotlin.reflect.full.declaredMemberProperties import kotlin.reflect.full.primaryConstructor import kotlin.reflect.jvm.isAccessible +// TODO this class is copy-pasted and should be de-duplicated +// see https://github.com/mockk/mockk/issues/857 + private val valueClassFieldCache = mutableMapOf, KProperty1>() /** diff --git a/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt b/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt index 2496d1e19..fb0982cef 100644 --- a/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt +++ b/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt @@ -6,6 +6,9 @@ import kotlin.reflect.full.declaredMemberProperties import kotlin.reflect.full.primaryConstructor import kotlin.reflect.jvm.isAccessible +// TODO this class is copy-pasted and should be de-duplicated +// see https://github.com/mockk/mockk/issues/857 + private val valueClassFieldCache = mutableMapOf, KProperty1>() /** diff --git a/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt b/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt index b9c1e7d4f..59a248c97 100644 --- a/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt +++ b/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt @@ -1,5 +1,6 @@ package io.mockk +import io.mockk.ValueClassSupportDsl.boxedClass import java.lang.reflect.AccessibleObject import java.lang.reflect.InvocationTargetException import java.lang.reflect.Method @@ -217,15 +218,7 @@ actual object InternalPlatformDsl { actual fun coroutineCall(lambda: suspend () -> T): CoroutineCall = JvmCoroutineCall(lambda) - actual fun unboxClass(cls: KClass<*>): KClass<*> { - if (!cls.isValue) return cls - - // get backing field - val backingField = cls.valueField() - - // get boxed value - return backingField.returnType.classifier as KClass<*> - } + actual fun unboxClass(cls: KClass<*>): KClass<*> = cls.boxedClass() @Suppress("UNCHECKED_CAST") actual fun boxCast( @@ -257,25 +250,3 @@ class JvmCoroutineCall(private val lambda: suspend () -> T) : CoroutineCall, KProperty1>() - -private fun KClass.valueField(): KProperty1 { - @Suppress("UNCHECKED_CAST") - return valueClassFieldCache.getOrPut(this) { - require(isValue) { "$this is not a value class" } - - // value classes always have a primary constructor... - val constructor = primaryConstructor!!.apply { isAccessible = true } - // ...and exactly one constructor parameter - val constructorParameter = constructor.parameters.first() - // ...with a backing field - val backingField = declaredMemberProperties - .first { it.name == constructorParameter.name } - .apply { isAccessible = true } - - backingField - } as KProperty1 -} diff --git a/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt b/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt new file mode 100644 index 000000000..626cf9fe2 --- /dev/null +++ b/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt @@ -0,0 +1,85 @@ +package io.mockk + +import kotlin.reflect.KClass +import kotlin.reflect.KProperty1 +import kotlin.reflect.full.declaredMemberProperties +import kotlin.reflect.full.primaryConstructor +import kotlin.reflect.jvm.isAccessible + +/** + * Provides value class support in the `mockk-dsl-jvm` subproject. + * + * This is marked as internal so that it won't clash with the another class in `mockk-agent-jvm`. + * + * TODO this class is copy-pasted and should be de-duplicated, see https://github.com/mockk/mockk/issues/857 + */ +internal object ValueClassSupportDsl { + + private val valueClassFieldCache = mutableMapOf, KProperty1>() + + /** + * Get boxed value of any value class + * + * @return boxed value of value class, if this is value class, else just itself + */ + fun T.boxedValue(): Any? { + if (!this::class.isValueClass()) return this + + // get backing field + val backingField = this::class.valueField() + + // get boxed value + @Suppress("UNCHECKED_CAST") + return (backingField as KProperty1).get(this) + } + + /** + * Get class of boxed value of any value class + * + * @return class of boxed value, if this is value class, else just class of itself + */ + fun T.boxedClass(): KClass<*> { + return this::class.boxedClass() + } + + /** + * Get the KClass of boxed value if this is a value class. + * + * @return class of boxed value, if this is value class, else just class of itself + */ + fun KClass<*>.boxedClass(): KClass<*> { + if (!this.isValueClass()) return this + + // get backing field + val backingField = this.valueField() + + // get boxed value + return backingField.returnType.classifier as KClass<*> + } + + + private fun KClass.valueField(): KProperty1 { + @Suppress("UNCHECKED_CAST") + return valueClassFieldCache.getOrPut(this) { + require(isValue) { "$this is not a value class" } + + // value classes always have a primary constructor... + val constructor = primaryConstructor!! + // ...and exactly one constructor parameter + val constructorParameter = constructor.parameters.first() + // ...with a backing field + val backingField = declaredMemberProperties + .first { it.name == constructorParameter.name } + .apply { isAccessible = true } + + backingField + } as KProperty1 + } + + private fun KClass.isValueClass() = try { + this.isValue + } catch (_: Throwable) { + false + } + +} From 1c5e442e47aa0ec4f9fc903011aa6592a8ed657e Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sat, 23 Jul 2022 11:10:13 +0200 Subject: [PATCH 07/17] test for #729 --- .../test/kotlin/io/mockk/it/ValueClassTest.kt | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt index a066ef2cd..fd5988bb1 100644 --- a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt +++ b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt @@ -1,6 +1,9 @@ package io.mockk.it -import io.mockk.* +import io.mockk.every +import io.mockk.mockk +import io.mockk.slot +import io.mockk.verify import kotlin.jvm.JvmInline import kotlin.test.Test import kotlin.test.assertEquals @@ -81,6 +84,17 @@ class ValueClassTest { assertEquals(givenResult, result) } + + @Test + fun `verify function with UInt return can be stubbed`() { + val mock = mockk { + every { getUInt() } returns 999u + } + + val result = mock.getUInt() + + assertEquals(999u, result) + } } @JvmInline @@ -88,9 +102,11 @@ value class DummyValue(val value: Int) class DummyService { - fun requestValue() = DummyValue(0) + fun requestValue() = DummyValue(0) + + fun processValue(value: DummyValue) = DummyValue(0) - fun processValue(value: DummyValue) = DummyValue(0) + fun getUInt(): UInt = 123u } @JvmInline From 9cc892fafc4d5b461d632d1aaae07998131143b9 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sat, 23 Jul 2022 11:26:55 +0200 Subject: [PATCH 08/17] test #729, extension fun with UInt return --- .../src/test/kotlin/io/mockk/it/ValueClassTest.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt index fd5988bb1..a0681b77d 100644 --- a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt +++ b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt @@ -85,6 +85,7 @@ class ValueClassTest { assertEquals(givenResult, result) } + /** https://github.com/mockk/mockk/issues/729 */ @Test fun `verify function with UInt return can be stubbed`() { val mock = mockk { @@ -95,6 +96,19 @@ class ValueClassTest { assertEquals(999u, result) } + + /** https://github.com/mockk/mockk/issues/729 */ + @Test + fun `verify extension function with UInt return can be stubbed`() { + + val fn = mockk UInt>() + + every { "string".fn() } returns 777u + + val result = "string".fn() + + assertEquals(777u, result) + } } @JvmInline From bcd427efb63c7bcad30655c03c9410165cb6ff40 Mon Sep 17 00:00:00 2001 From: aSemy <897017+aSemy@users.noreply.github.com> Date: Sat, 23 Jul 2022 11:43:42 +0200 Subject: [PATCH 09/17] rm 'actual' from 'expect' object --- dsl/common/src/main/kotlin/io/mockk/InternalPlatformDsl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsl/common/src/main/kotlin/io/mockk/InternalPlatformDsl.kt b/dsl/common/src/main/kotlin/io/mockk/InternalPlatformDsl.kt index 44dfb3682..f9ac847bc 100644 --- a/dsl/common/src/main/kotlin/io/mockk/InternalPlatformDsl.kt +++ b/dsl/common/src/main/kotlin/io/mockk/InternalPlatformDsl.kt @@ -52,7 +52,7 @@ expect object InternalPlatformDsl { * However, if `T` is a `value class`es (of type [cls]) it will construct a new instance of the * class, and set [arg] as the value. */ - actual fun boxCast( + fun boxCast( cls: KClass<*>, arg: Any, ): T From 201b43d3d27f4058d6e628d2b07c13af6dc9fdf3 Mon Sep 17 00:00:00 2001 From: aSemy <897017+aSemy@users.noreply.github.com> Date: Sat, 23 Jul 2022 14:44:45 +0200 Subject: [PATCH 10/17] fix kdoc typo --- dsl/common/src/main/kotlin/io/mockk/InternalPlatformDsl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dsl/common/src/main/kotlin/io/mockk/InternalPlatformDsl.kt b/dsl/common/src/main/kotlin/io/mockk/InternalPlatformDsl.kt index f9ac847bc..c280a87cc 100644 --- a/dsl/common/src/main/kotlin/io/mockk/InternalPlatformDsl.kt +++ b/dsl/common/src/main/kotlin/io/mockk/InternalPlatformDsl.kt @@ -49,8 +49,8 @@ expect object InternalPlatformDsl { /** * Normally this simply casts [arg] to `T` * - * However, if `T` is a `value class`es (of type [cls]) it will construct a new instance of the - * class, and set [arg] as the value. + * However, if `T` is a `value class` (of type [cls]) this will construct a new instance of the + * value class, and set [arg] as the value. */ fun boxCast( cls: KClass<*>, From 65e864348848831ff2f6c3678e5c891a730af5fa Mon Sep 17 00:00:00 2001 From: aSemy <897017+aSemy@users.noreply.github.com> Date: Sat, 23 Jul 2022 14:45:24 +0200 Subject: [PATCH 11/17] fix kdoc typo --- dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt b/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt index 626cf9fe2..7aa2b90b6 100644 --- a/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt +++ b/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt @@ -9,7 +9,7 @@ import kotlin.reflect.jvm.isAccessible /** * Provides value class support in the `mockk-dsl-jvm` subproject. * - * This is marked as internal so that it won't clash with the another class in `mockk-agent-jvm`. + * This is marked as internal so that it won't clash with the other class in `mockk-agent-jvm`. * * TODO this class is copy-pasted and should be de-duplicated, see https://github.com/mockk/mockk/issues/857 */ From 5e7851bea66bf348cdb1b5152baf7917ca587e1f Mon Sep 17 00:00:00 2001 From: Bengt Brodersen Date: Sun, 24 Jul 2022 00:41:39 +0200 Subject: [PATCH 12/17] refactor: simplify ValueClassSupport --- .../main/kotlin/io/mockk/ValueClassSupport.kt | 92 ++++--------------- .../io/mockk/proxy/android/advice/Advice.kt | 2 +- .../main/kotlin/io/mockk/ValueClassSupport.kt | 81 ++++------------ .../io/mockk/proxy/jvm/advice/Interceptor.kt | 2 +- .../kotlin/io/mockk/impl/InternalPlatform.kt | 2 +- .../instantiation/JvmMockFactoryHelper.kt | 2 +- 6 files changed, 44 insertions(+), 137 deletions(-) diff --git a/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt b/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt index a3de5d4fd..cea2d7b42 100644 --- a/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt +++ b/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt @@ -3,86 +3,34 @@ package io.mockk import kotlin.reflect.KClass import kotlin.reflect.KProperty1 import kotlin.reflect.full.declaredMemberProperties -import kotlin.reflect.full.primaryConstructor import kotlin.reflect.jvm.isAccessible -// TODO this class is copy-pasted and should be de-duplicated -// see https://github.com/mockk/mockk/issues/857 - -private val valueClassFieldCache = mutableMapOf, KProperty1>() - /** - * Get boxed value of any value class - * - * @return boxed value of value class, if this is value class, else just itself + * Underlying property value of a **`value class`** or self */ -fun T.boxedValue(): Any? { - if (!this::class.isValueClass()) return this - - // get backing field - val backingField = this::class.valueField() - - // get boxed value +val T.boxedValue: Any? @Suppress("UNCHECKED_CAST") - return (backingField as KProperty1).get(this) -} + get() = if (!this::class.isValue_safe) this + else (this::class as KClass).boxedProperty.get(this) /** - * Get class of boxed value of any value class - * - * @return class of boxed value, if this is value class, else just class of itself + * Underlying property class of a **`value class`** or self */ -fun T.boxedClass(): KClass<*> { - return this::class.boxedClass() -} - -/** - * Get the KClass of boxed value if this is a value class. - * - * @return class of boxed value, if this is value class, else just class of itself - */ -fun KClass<*>.boxedClass(): KClass<*> { - if (!this.isValueClass()) return this - - // get backing field - val backingField = this.valueField() - - // get boxed value - return backingField.returnType.classifier as KClass<*> -} - - -private fun KClass.valueField(): KProperty1 { - @Suppress("UNCHECKED_CAST") - return valueClassFieldCache.getOrPut(this) { - require(isValue) { "$this is not a value class" } - - // value classes always have a primary constructor... - val constructor = primaryConstructor!! - // ...and exactly one constructor parameter - val constructorParameter = constructor.parameters.first() - // ...with a backing field - val backingField = declaredMemberProperties - .first { it.name == constructorParameter.name } - .apply { isAccessible = true } - - backingField - } as KProperty1 -} - -private fun KClass.isValueClass() = try { - this.isValue -} catch (_: Throwable) { - false -} +val KClass<*>.boxedClass: KClass<*> + get() = if (!this.isValue_safe) this + else this.boxedProperty.returnType.classifier as KClass<*> /** - * POLYFILL for kotlin version < 1.5 - * will be shadowed by implementation in kotlin SDK 1.5+ - * - * @return true if this is an inline class, else false + * Underlying property of a **`value class`** */ -private val KClass.isValue: Boolean - get() = !isData && - primaryConstructor?.parameters?.size == 1 && - java.declaredMethods.any { it.name == "box-impl" } +private val KClass.boxedProperty: KProperty1 + get() = if (!this.isValue_safe) throw UnsupportedOperationException("$this is not a value class") + // value classes always have exactly one property + else this.declaredMemberProperties.first().apply { isAccessible = true } + +private val KClass.isValue_safe: Boolean + get() = try { + this.isValue + } catch (_: UnsupportedOperationException) { + false + } diff --git a/agent/android/src/main/kotlin/io/mockk/proxy/android/advice/Advice.kt b/agent/android/src/main/kotlin/io/mockk/proxy/android/advice/Advice.kt index e1b9e18ab..4c4dd594f 100644 --- a/agent/android/src/main/kotlin/io/mockk/proxy/android/advice/Advice.kt +++ b/agent/android/src/main/kotlin/io/mockk/proxy/android/advice/Advice.kt @@ -81,7 +81,7 @@ internal class Advice( superMethodCall, arguments ) - ?.boxedValue() // unbox value class objects + ?.boxedValue // unbox value class objects } } diff --git a/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt b/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt index fb0982cef..cea2d7b42 100644 --- a/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt +++ b/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt @@ -3,75 +3,34 @@ package io.mockk import kotlin.reflect.KClass import kotlin.reflect.KProperty1 import kotlin.reflect.full.declaredMemberProperties -import kotlin.reflect.full.primaryConstructor import kotlin.reflect.jvm.isAccessible -// TODO this class is copy-pasted and should be de-duplicated -// see https://github.com/mockk/mockk/issues/857 - -private val valueClassFieldCache = mutableMapOf, KProperty1>() - /** - * Get boxed value of any value class - * - * @return boxed value of value class, if this is value class, else just itself + * Underlying property value of a **`value class`** or self */ -fun T.boxedValue(): Any? { - if (!this::class.isValueClass()) return this - - // get backing field - val backingField = this::class.valueField() - - // get boxed value +val T.boxedValue: Any? @Suppress("UNCHECKED_CAST") - return (backingField as KProperty1).get(this) -} + get() = if (!this::class.isValue_safe) this + else (this::class as KClass).boxedProperty.get(this) /** - * Get class of boxed value of any value class - * - * @return class of boxed value, if this is value class, else just class of itself + * Underlying property class of a **`value class`** or self */ -fun T.boxedClass(): KClass<*> { - return this::class.boxedClass() -} +val KClass<*>.boxedClass: KClass<*> + get() = if (!this.isValue_safe) this + else this.boxedProperty.returnType.classifier as KClass<*> /** - * Get the KClass of boxed value if this is a value class. - * - * @return class of boxed value, if this is value class, else just class of itself + * Underlying property of a **`value class`** */ -fun KClass<*>.boxedClass(): KClass<*> { - if (!this.isValueClass()) return this - - // get backing field - val backingField = this.valueField() - - // get boxed value - return backingField.returnType.classifier as KClass<*> -} - - -private fun KClass.valueField(): KProperty1 { - @Suppress("UNCHECKED_CAST") - return valueClassFieldCache.getOrPut(this) { - require(isValue) { "$this is not a value class" } - - // value classes always have a primary constructor... - val constructor = primaryConstructor!! - // ...and exactly one constructor parameter - val constructorParameter = constructor.parameters.first() - // ...with a backing field - val backingField = declaredMemberProperties - .first { it.name == constructorParameter.name } - .apply { isAccessible = true } - - backingField - } as KProperty1 -} - -private fun KClass.isValueClass() = try { - this.isValue -} catch (_: Throwable) { - false -} +private val KClass.boxedProperty: KProperty1 + get() = if (!this.isValue_safe) throw UnsupportedOperationException("$this is not a value class") + // value classes always have exactly one property + else this.declaredMemberProperties.first().apply { isAccessible = true } + +private val KClass.isValue_safe: Boolean + get() = try { + this.isValue + } catch (_: UnsupportedOperationException) { + false + } diff --git a/agent/jvm/src/main/kotlin/io/mockk/proxy/jvm/advice/Interceptor.kt b/agent/jvm/src/main/kotlin/io/mockk/proxy/jvm/advice/Interceptor.kt index dcb8019c5..1ce1673cf 100644 --- a/agent/jvm/src/main/kotlin/io/mockk/proxy/jvm/advice/Interceptor.kt +++ b/agent/jvm/src/main/kotlin/io/mockk/proxy/jvm/advice/Interceptor.kt @@ -19,7 +19,7 @@ internal class Interceptor( method ) return handler.invocation(self, method, callOriginalMethod, arguments) - ?.boxedValue() // unbox value class objects + ?.boxedValue // unbox value class objects } } diff --git a/mockk/jvm/src/main/kotlin/io/mockk/impl/InternalPlatform.kt b/mockk/jvm/src/main/kotlin/io/mockk/impl/InternalPlatform.kt index cb586d936..4b2dc598a 100644 --- a/mockk/jvm/src/main/kotlin/io/mockk/impl/InternalPlatform.kt +++ b/mockk/jvm/src/main/kotlin/io/mockk/impl/InternalPlatform.kt @@ -63,7 +63,7 @@ actual object InternalPlatform { actual fun packRef(arg: Any?): Any? { return when { arg == null -> null - isPassedByValue(arg.boxedClass()) -> arg.boxedValue() + isPassedByValue(arg::class.boxedClass) -> arg.boxedValue else -> ref(arg) } } diff --git a/mockk/jvm/src/main/kotlin/io/mockk/impl/instantiation/JvmMockFactoryHelper.kt b/mockk/jvm/src/main/kotlin/io/mockk/impl/instantiation/JvmMockFactoryHelper.kt index 71552c367..b08b9250d 100644 --- a/mockk/jvm/src/main/kotlin/io/mockk/impl/instantiation/JvmMockFactoryHelper.kt +++ b/mockk/jvm/src/main/kotlin/io/mockk/impl/instantiation/JvmMockFactoryHelper.kt @@ -151,7 +151,7 @@ object JvmMockFactoryHelper { is KType -> kotlinReturnType.classifier as? KClass<*> ?: returnType.kotlin is KClass<*> -> kotlinReturnType else -> returnType.kotlin - }.boxedClass() + }.boxedClass val androidCompatibleReturnType = if (returnType.qualifiedName in androidUnsupportedTypes) { this@toDescription.returnType.kotlin From aafe7afd6512606f88f5057b79cb37663e91952d Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 24 Jul 2022 10:42:30 +0200 Subject: [PATCH 13/17] more testing for value classes - pre-merge in https://github.com/aSemy/mockk/pull/1 --- .../main/kotlin/io/mockk/ValueClassSupport.kt | 99 ++-- .../io/mockk/proxy/android/advice/Advice.kt | 2 +- .../main/kotlin/io/mockk/ValueClassSupport.kt | 94 ++-- .../io/mockk/proxy/jvm/advice/Interceptor.kt | 2 +- .../kotlin/io/mockk/InternalPlatformDsl.kt | 2 +- .../main/kotlin/io/mockk/ValueClassSupport.kt | 96 ++-- .../test/kotlin/io/mockk/it/ValueClassTest.kt | 478 +++++++++++++++--- .../kotlin/io/mockk/impl/InternalPlatform.kt | 2 +- .../instantiation/JvmMockFactoryHelper.kt | 2 +- 9 files changed, 545 insertions(+), 232 deletions(-) diff --git a/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt b/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt index a3de5d4fd..961634ead 100644 --- a/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt +++ b/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt @@ -9,80 +9,57 @@ import kotlin.reflect.jvm.isAccessible // TODO this class is copy-pasted and should be de-duplicated // see https://github.com/mockk/mockk/issues/857 -private val valueClassFieldCache = mutableMapOf, KProperty1>() - /** - * Get boxed value of any value class + * Underlying property value of a **`value class`** or self. * - * @return boxed value of value class, if this is value class, else just itself + * The type of the return might also be a `value class`! */ -fun T.boxedValue(): Any? { - if (!this::class.isValueClass()) return this - - // get backing field - val backingField = this::class.valueField() - - // get boxed value +val T.boxedValue: Any? @Suppress("UNCHECKED_CAST") - return (backingField as KProperty1).get(this) -} + get() = if (!this::class.isValue_safe) { + this + } else { + (this::class as KClass).boxedProperty.get(this) + } /** - * Get class of boxed value of any value class + * Underlying property class of a **`value class`** or self. * - * @return class of boxed value, if this is value class, else just class of itself + * The returned class might also be a `value class`! */ -fun T.boxedClass(): KClass<*> { - return this::class.boxedClass() -} +val KClass<*>.boxedClass: KClass<*> + get() = if (!this.isValue_safe) { + this + } else { + this.boxedProperty.returnType.classifier as KClass<*> + } + +private val valueClassFieldCache = mutableMapOf, KProperty1>() /** - * Get the KClass of boxed value if this is a value class. + * Underlying property of a **`value class`**. * - * @return class of boxed value, if this is value class, else just class of itself + * The underlying property might also be a `value class`! */ -fun KClass<*>.boxedClass(): KClass<*> { - if (!this.isValueClass()) return this - - // get backing field - val backingField = this.valueField() - - // get boxed value - return backingField.returnType.classifier as KClass<*> -} - - -private fun KClass.valueField(): KProperty1 { - @Suppress("UNCHECKED_CAST") - return valueClassFieldCache.getOrPut(this) { - require(isValue) { "$this is not a value class" } - - // value classes always have a primary constructor... - val constructor = primaryConstructor!! - // ...and exactly one constructor parameter - val constructorParameter = constructor.parameters.first() - // ...with a backing field - val backingField = declaredMemberProperties - .first { it.name == constructorParameter.name } - .apply { isAccessible = true } - - backingField - } as KProperty1 -} - -private fun KClass.isValueClass() = try { - this.isValue -} catch (_: Throwable) { - false -} +private val KClass.boxedProperty: KProperty1 + get() = if (!this.isValue_safe) { + throw UnsupportedOperationException("$this is not a value class") + } else { + // value classes always have exactly one property + @Suppress("UNCHECKED_CAST") + valueClassFieldCache.getOrPut(this) { + this.declaredMemberProperties.first().apply { isAccessible = true } + } as KProperty1 + } /** - * POLYFILL for kotlin version < 1.5 - * will be shadowed by implementation in kotlin SDK 1.5+ + * Returns `true` if calling [KClass.isValue] is safe. * - * @return true if this is an inline class, else false + * (In some instances [KClass.isValue] can throw an exception.) */ -private val KClass.isValue: Boolean - get() = !isData && - primaryConstructor?.parameters?.size == 1 && - java.declaredMethods.any { it.name == "box-impl" } +private val KClass.isValue_safe: Boolean + get() = try { + this.isValue + } catch (_: UnsupportedOperationException) { + false + } diff --git a/agent/android/src/main/kotlin/io/mockk/proxy/android/advice/Advice.kt b/agent/android/src/main/kotlin/io/mockk/proxy/android/advice/Advice.kt index e1b9e18ab..4c4dd594f 100644 --- a/agent/android/src/main/kotlin/io/mockk/proxy/android/advice/Advice.kt +++ b/agent/android/src/main/kotlin/io/mockk/proxy/android/advice/Advice.kt @@ -81,7 +81,7 @@ internal class Advice( superMethodCall, arguments ) - ?.boxedValue() // unbox value class objects + ?.boxedValue // unbox value class objects } } diff --git a/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt b/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt index fb0982cef..961634ead 100644 --- a/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt +++ b/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt @@ -9,69 +9,57 @@ import kotlin.reflect.jvm.isAccessible // TODO this class is copy-pasted and should be de-duplicated // see https://github.com/mockk/mockk/issues/857 -private val valueClassFieldCache = mutableMapOf, KProperty1>() - /** - * Get boxed value of any value class + * Underlying property value of a **`value class`** or self. * - * @return boxed value of value class, if this is value class, else just itself + * The type of the return might also be a `value class`! */ -fun T.boxedValue(): Any? { - if (!this::class.isValueClass()) return this - - // get backing field - val backingField = this::class.valueField() - - // get boxed value +val T.boxedValue: Any? @Suppress("UNCHECKED_CAST") - return (backingField as KProperty1).get(this) -} + get() = if (!this::class.isValue_safe) { + this + } else { + (this::class as KClass).boxedProperty.get(this) + } /** - * Get class of boxed value of any value class + * Underlying property class of a **`value class`** or self. * - * @return class of boxed value, if this is value class, else just class of itself + * The returned class might also be a `value class`! */ -fun T.boxedClass(): KClass<*> { - return this::class.boxedClass() -} +val KClass<*>.boxedClass: KClass<*> + get() = if (!this.isValue_safe) { + this + } else { + this.boxedProperty.returnType.classifier as KClass<*> + } + +private val valueClassFieldCache = mutableMapOf, KProperty1>() /** - * Get the KClass of boxed value if this is a value class. + * Underlying property of a **`value class`**. * - * @return class of boxed value, if this is value class, else just class of itself + * The underlying property might also be a `value class`! */ -fun KClass<*>.boxedClass(): KClass<*> { - if (!this.isValueClass()) return this - - // get backing field - val backingField = this.valueField() - - // get boxed value - return backingField.returnType.classifier as KClass<*> -} +private val KClass.boxedProperty: KProperty1 + get() = if (!this.isValue_safe) { + throw UnsupportedOperationException("$this is not a value class") + } else { + // value classes always have exactly one property + @Suppress("UNCHECKED_CAST") + valueClassFieldCache.getOrPut(this) { + this.declaredMemberProperties.first().apply { isAccessible = true } + } as KProperty1 + } - -private fun KClass.valueField(): KProperty1 { - @Suppress("UNCHECKED_CAST") - return valueClassFieldCache.getOrPut(this) { - require(isValue) { "$this is not a value class" } - - // value classes always have a primary constructor... - val constructor = primaryConstructor!! - // ...and exactly one constructor parameter - val constructorParameter = constructor.parameters.first() - // ...with a backing field - val backingField = declaredMemberProperties - .first { it.name == constructorParameter.name } - .apply { isAccessible = true } - - backingField - } as KProperty1 -} - -private fun KClass.isValueClass() = try { - this.isValue -} catch (_: Throwable) { - false -} +/** + * Returns `true` if calling [KClass.isValue] is safe. + * + * (In some instances [KClass.isValue] can throw an exception.) + */ +private val KClass.isValue_safe: Boolean + get() = try { + this.isValue + } catch (_: UnsupportedOperationException) { + false + } diff --git a/agent/jvm/src/main/kotlin/io/mockk/proxy/jvm/advice/Interceptor.kt b/agent/jvm/src/main/kotlin/io/mockk/proxy/jvm/advice/Interceptor.kt index dcb8019c5..1ce1673cf 100644 --- a/agent/jvm/src/main/kotlin/io/mockk/proxy/jvm/advice/Interceptor.kt +++ b/agent/jvm/src/main/kotlin/io/mockk/proxy/jvm/advice/Interceptor.kt @@ -19,7 +19,7 @@ internal class Interceptor( method ) return handler.invocation(self, method, callOriginalMethod, arguments) - ?.boxedValue() // unbox value class objects + ?.boxedValue // unbox value class objects } } diff --git a/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt b/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt index 59a248c97..1419a1367 100644 --- a/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt +++ b/dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt @@ -218,7 +218,7 @@ actual object InternalPlatformDsl { actual fun coroutineCall(lambda: suspend () -> T): CoroutineCall = JvmCoroutineCall(lambda) - actual fun unboxClass(cls: KClass<*>): KClass<*> = cls.boxedClass() + actual fun unboxClass(cls: KClass<*>): KClass<*> = cls.boxedClass @Suppress("UNCHECKED_CAST") actual fun boxCast( diff --git a/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt b/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt index 7aa2b90b6..650c7aeca 100644 --- a/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt +++ b/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt @@ -3,7 +3,6 @@ package io.mockk import kotlin.reflect.KClass import kotlin.reflect.KProperty1 import kotlin.reflect.full.declaredMemberProperties -import kotlin.reflect.full.primaryConstructor import kotlin.reflect.jvm.isAccessible /** @@ -15,71 +14,58 @@ import kotlin.reflect.jvm.isAccessible */ internal object ValueClassSupportDsl { - private val valueClassFieldCache = mutableMapOf, KProperty1>() - /** - * Get boxed value of any value class + * Underlying property value of a **`value class`** or self. * - * @return boxed value of value class, if this is value class, else just itself + * The type of the return might also be a `value class`! */ - fun T.boxedValue(): Any? { - if (!this::class.isValueClass()) return this - - // get backing field - val backingField = this::class.valueField() - - // get boxed value + val T.boxedValue: Any? @Suppress("UNCHECKED_CAST") - return (backingField as KProperty1).get(this) - } + get() = if (!this::class.isValue_safe) { + this + } else { + (this::class as KClass).boxedProperty.get(this) + } /** - * Get class of boxed value of any value class + * Underlying property class of a **`value class`** or self. * - * @return class of boxed value, if this is value class, else just class of itself + * The returned class might also be a `value class`! */ - fun T.boxedClass(): KClass<*> { - return this::class.boxedClass() - } + val KClass<*>.boxedClass: KClass<*> + get() = if (!this.isValue_safe) { + this + } else { + this.boxedProperty.returnType.classifier as KClass<*> + } + + private val valueClassFieldCache = mutableMapOf, KProperty1>() /** - * Get the KClass of boxed value if this is a value class. + * Underlying property of a **`value class`**. * - * @return class of boxed value, if this is value class, else just class of itself + * The underlying property might also be a `value class`! */ - fun KClass<*>.boxedClass(): KClass<*> { - if (!this.isValueClass()) return this - - // get backing field - val backingField = this.valueField() - - // get boxed value - return backingField.returnType.classifier as KClass<*> - } - - - private fun KClass.valueField(): KProperty1 { - @Suppress("UNCHECKED_CAST") - return valueClassFieldCache.getOrPut(this) { - require(isValue) { "$this is not a value class" } - - // value classes always have a primary constructor... - val constructor = primaryConstructor!! - // ...and exactly one constructor parameter - val constructorParameter = constructor.parameters.first() - // ...with a backing field - val backingField = declaredMemberProperties - .first { it.name == constructorParameter.name } - .apply { isAccessible = true } - - backingField - } as KProperty1 - } - - private fun KClass.isValueClass() = try { - this.isValue - } catch (_: Throwable) { - false - } + private val KClass.boxedProperty: KProperty1 + get() = if (!this.isValue_safe) { + throw UnsupportedOperationException("$this is not a value class") + } else { + // value classes always have exactly one property + @Suppress("UNCHECKED_CAST") + valueClassFieldCache.getOrPut(this) { + this.declaredMemberProperties.first().apply { isAccessible = true } + } as KProperty1 + } + /** + * Returns `true` if calling [KClass.isValue] is safe. + * + * (In some instances [KClass.isValue] can throw an exception.) + */ + private val KClass.isValue_safe: Boolean + get() = try { + this.isValue + } catch (_: UnsupportedOperationException) { + false + } } diff --git a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt index a0681b77d..0816b89c6 100644 --- a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt +++ b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt @@ -5,128 +5,490 @@ import io.mockk.mockk import io.mockk.slot import io.mockk.verify import kotlin.jvm.JvmInline +import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertEquals class ValueClassTest { - private val mock = mockk() + private val dummyValueWrapperArg get() = DummyValueWrapper(DummyValue(42)) + private val dummyValueWrapperReturn get() = DummyValueWrapper(DummyValue(99)) + + private val dummyValueInnerArg get() = DummyValue(101) + private val dummyValueInnerReturn get() = DummyValue(202) + + // + @Test + fun `arg is ValueClass, returns Wrapper`() { + val mock = mockk { + every { argValueClassReturnWrapper(dummyValueInnerArg) } returns dummyValueWrapperReturn + } + + assertEquals(dummyValueWrapperReturn, mock.argValueClassReturnWrapper(dummyValueInnerArg)) + + verify { mock.argValueClassReturnWrapper(dummyValueInnerArg) } + } + + @Test + fun `arg is any(ValueClass), returns Wrapper`() { + val mock = mockk { + every { argValueClassReturnWrapper(any()) } returns dummyValueWrapperReturn + } + + assertEquals(dummyValueWrapperArg, mock.argValueClassReturnWrapper(dummyValueInnerArg)) + + verify { mock.argValueClassReturnWrapper(dummyValueInnerArg) } + } + + @Test + fun `arg is slot(ValueClass), returns Wrapper`() { + val slot = slot() + + val mock = mockk { + every { argValueClassReturnWrapper(capture(slot)) } returns dummyValueWrapperReturn + } + + val result = mock.argValueClassReturnWrapper(dummyValueInnerArg) + + assertEquals(dummyValueWrapperReturn, result) + + assertEquals(dummyValueInnerArg, slot.captured) + + verify { mock.argValueClassReturnWrapper(dummyValueInnerArg) } + } + + @Test + fun `arg is ValueClass, answers Wrapper`() { + val mock = mockk { + every { argValueClassReturnWrapper(dummyValueInnerArg) } answers { dummyValueWrapperReturn } + } + + assertEquals(dummyValueWrapperReturn, mock.argValueClassReturnWrapper(dummyValueInnerArg)) + + verify { mock.argValueClassReturnWrapper(dummyValueInnerArg) } + } + + @Test + fun `arg is any(ValueClass), answers Wrapper`() { + val mock = mockk { + every { argValueClassReturnWrapper(any()) } answers { dummyValueWrapperReturn } + } + + assertEquals(dummyValueWrapperReturn, mock.argValueClassReturnWrapper(dummyValueInnerArg)) + + verify { mock.argValueClassReturnWrapper(dummyValueInnerArg) } + } + + @Test + fun `arg is slot(ValueClass), answers Wrapper`() { + val slot = slot() + + val mock = mockk { + every { argValueClassReturnWrapper(capture(slot)) } answers { dummyValueWrapperReturn } + } + + val result = mock.argValueClassReturnWrapper(dummyValueInnerArg) + + assertEquals(dummyValueWrapperReturn, result) + + assertEquals(dummyValueInnerArg, slot.captured) + + verify { mock.argValueClassReturnWrapper(dummyValueInnerArg) } + } + // + + // + @Test + fun `arg is ValueClass, returns Inner`() { + val mock = mockk { + every { argValueClassReturnValueClass(dummyValueInnerArg) } returns dummyValueInnerReturn + } + + assertEquals(dummyValueInnerReturn, mock.argValueClassReturnValueClass(dummyValueInnerArg)) + + verify { mock.argValueClassReturnValueClass(dummyValueInnerArg) } + } + + @Test + fun `arg is any(ValueClass), returns Inner`() { + val mock = mockk { + every { argValueClassReturnValueClass(any()) } returns dummyValueInnerReturn + } + + assertEquals(dummyValueInnerReturn, mock.argValueClassReturnValueClass(dummyValueInnerArg)) + + verify { mock.argValueClassReturnValueClass(dummyValueInnerArg) } + } + + @Test + fun `arg is slot(ValueClass), returns Inner`() { + val slot = slot() + val mock = mockk { + every { argValueClassReturnValueClass(capture(slot)) } returns dummyValueInnerReturn + } + + val result = mock.argValueClassReturnValueClass(dummyValueInnerArg) + + assertEquals(dummyValueInnerReturn, result) + + assertEquals(dummyValueInnerArg, slot.captured) + + verify { mock.argValueClassReturnValueClass(dummyValueInnerArg) } + } + + @Test + fun `arg is ValueClass, answers Inner`() { + val mock = mockk { + every { argValueClassReturnValueClass(dummyValueInnerArg) } answers { dummyValueInnerReturn } + } + + assertEquals(dummyValueInnerReturn, mock.argValueClassReturnValueClass(dummyValueInnerArg)) + + verify { mock.argValueClassReturnValueClass(dummyValueInnerArg) } + } + + @Test + fun `arg is any(ValueClass), answers Inner`() { + val mock = mockk { + every { argValueClassReturnValueClass(any()) } answers { dummyValueInnerReturn } + } + + assertEquals(dummyValueInnerReturn, mock.argValueClassReturnValueClass(dummyValueInnerArg)) + + verify { mock.argValueClassReturnValueClass(dummyValueInnerArg) } + } + + @Test + fun `arg is slot(ValueClass), answers Inner`() { + val slot = slot() + + val mock = mockk { + every { argValueClassReturnValueClass(capture(slot)) } answers { dummyValueInnerReturn } + } + + val result = mock.argValueClassReturnValueClass(dummyValueInnerArg) + + assertEquals(dummyValueInnerReturn, result) + + assertEquals(dummyValueInnerArg, slot.captured) + + verify { mock.argValueClassReturnValueClass(dummyValueInnerArg) } + } + // + + // + @Test + fun `arg is Outer, returns Inner`() { + val mock = mockk { + every { argWrapperReturnValueClass(dummyValueWrapperArg) } returns dummyValueInnerReturn + } + + assertEquals(dummyValueInnerReturn, mock.argWrapperReturnValueClass(dummyValueWrapperArg)) + + verify { mock.argWrapperReturnValueClass(dummyValueWrapperArg) } + } + + @Test + fun `arg is any(Outer), returns Inner`() { + val mock = mockk { + every { argWrapperReturnValueClass(any()) } returns dummyValueInnerReturn + } + + assertEquals(dummyValueInnerReturn, mock.argWrapperReturnValueClass(dummyValueWrapperArg)) + + verify { mock.argWrapperReturnValueClass(dummyValueWrapperArg) } + } @Test - fun valueClassObjectAsReturnValue() { - every { mock.requestValue() } returns DummyValue(42) + fun `arg is slot(Outer), returns Inner`() { + val slot = slot() + val mock = mockk { + every { argWrapperReturnValueClass(capture(slot)) } returns dummyValueInnerReturn + } + + val result = mock.argWrapperReturnValueClass(dummyValueWrapperArg) - assertEquals(DummyValue(42), mock.requestValue()) + assertEquals(dummyValueInnerReturn, result) - verify { mock.requestValue() } + assertEquals(dummyValueWrapperArg, slot.captured) + + verify { mock.argWrapperReturnValueClass(dummyValueWrapperArg) } } @Test - fun valueClassObjectAsFunctionArgumentAndReturnValue() { - every { mock.processValue(DummyValue(1)) } returns DummyValue(42) + fun `arg is Outer, answers Inner`() { + val mock = mockk { + every { argWrapperReturnValueClass(dummyValueWrapperArg) } answers { dummyValueInnerReturn } + } - assertEquals(DummyValue(42), mock.processValue(DummyValue(1))) + assertEquals(dummyValueInnerReturn, mock.argWrapperReturnValueClass(dummyValueWrapperArg)) - verify { mock.processValue(DummyValue(1)) } + verify { mock.argWrapperReturnValueClass(dummyValueWrapperArg) } } @Test - fun valueClassObjectAsFunctionArgumentAndAnswerValue() { - every { mock.processValue(DummyValue(1)) } answers { DummyValue(42) } + fun `arg is any(Outer), answers Inner`() { + val mock = mockk { + every { argWrapperReturnValueClass(any()) } answers { dummyValueInnerReturn } + } - assertEquals(DummyValue(42), mock.processValue(DummyValue(1))) + assertEquals(dummyValueInnerReturn, mock.argWrapperReturnValueClass(dummyValueWrapperArg)) - verify { mock.processValue(DummyValue(1)) } + verify { mock.argWrapperReturnValueClass(dummyValueWrapperArg) } } @Test - fun anyValueClassMatcherAsFunctionArgumentAndValueClassObjectAsReturnValue() { - every { mock.processValue(any()) } returns DummyValue(42) + fun `arg is slot(Outer), answers Inner`() { + val slot = slot() - assertEquals(DummyValue(42), mock.processValue(DummyValue(1))) + val mock = mockk { + every { argWrapperReturnValueClass(capture(slot)) } answers { dummyValueInnerReturn } + } - verify { mock.processValue(DummyValue(1)) } + val result = mock.argWrapperReturnValueClass(dummyValueWrapperArg) + + assertEquals(dummyValueInnerReturn, result) + + assertEquals(dummyValueWrapperArg, slot.captured) + + verify { mock.argWrapperReturnValueClass(dummyValueWrapperArg) } } + // + // @Test - fun `any matcher for value class`() { - val mock = mockk(relaxed = true) - val givenResult = 1 - every { mock.doSomething(any()) } returns givenResult + fun `arg is Outer, returns Wrapper`() { + val mock = mockk { + every { argWrapperReturnWrapper(dummyValueWrapperArg) } returns dummyValueWrapperReturn + } - val result = mock.doSomething(ValueDummy("moin")) + assertEquals(dummyValueWrapperReturn, mock.argWrapperReturnWrapper(dummyValueWrapperArg)) - assertEquals(givenResult, result) + verify { mock.argWrapperReturnWrapper(dummyValueWrapperArg) } } @Test - fun `slot for value class`() { - val mock = mockk(relaxed = true) - val slot = slot() - val givenResult = 1 - every { mock.doSomething(capture(slot)) } returns givenResult + fun `arg is any(Outer), returns Wrapper`() { + val mock = mockk { + every { argWrapperReturnWrapper(any()) } returns dummyValueWrapperReturn + } - val givenParameter = ValueDummy("s") + assertEquals(dummyValueWrapperReturn, mock.argWrapperReturnWrapper(dummyValueWrapperArg)) + + verify { mock.argWrapperReturnWrapper(dummyValueWrapperArg) } + } + + @Test + fun `arg is slot(Outer), returns Wrapper`() { + val slot = slot() + val mock = mockk { + every { argWrapperReturnWrapper(capture(slot)) } returns dummyValueWrapperReturn + } - val result = mock.doSomething(givenParameter) + val result = mock.argWrapperReturnWrapper(dummyValueWrapperArg) - assertEquals(givenResult, result) - assertEquals(givenParameter, slot.captured) + assertEquals(dummyValueWrapperReturn, result) + + assertEquals(dummyValueWrapperArg, slot.captured) + + verify { mock.argWrapperReturnWrapper(dummyValueWrapperArg) } + } + + @Test + fun `arg is Outer, answers Wrapper`() { + val mock = mockk { + every { argWrapperReturnWrapper(dummyValueWrapperArg) } answers { dummyValueWrapperReturn } + } + + assertEquals(dummyValueWrapperReturn, mock.argWrapperReturnWrapper(dummyValueWrapperArg)) + + verify { mock.argWrapperReturnWrapper(dummyValueWrapperArg) } + } + + @Test + fun `arg is any(Outer), answers Wrapper`() { + val mock = mockk { + every { argWrapperReturnWrapper(any()) } answers { dummyValueWrapperReturn } + } + + assertEquals(dummyValueWrapperReturn, mock.argWrapperReturnWrapper(dummyValueWrapperArg)) + + verify { mock.argWrapperReturnWrapper(dummyValueWrapperArg) } } @Test - fun `value class as return value`() { - val mock = mockk(relaxed = true) - val givenResult = ValueDummy("moin") - every { mock.getSomething() } returns givenResult + fun `arg is slot(Outer), answers Wrapper`() { + val slot = slot() + + val mock = mockk { + every { argWrapperReturnWrapper(capture(slot)) } answers { dummyValueWrapperReturn } + } + + val result = mock.argWrapperReturnWrapper(dummyValueWrapperArg) - val result = mock.getSomething() + assertEquals(dummyValueWrapperReturn, result) - assertEquals(givenResult, result) + assertEquals(dummyValueWrapperArg, slot.captured) + + verify { mock.argWrapperReturnWrapper(dummyValueWrapperArg) } } + // + // /** https://github.com/mockk/mockk/issues/729 */ @Test - fun `verify function with UInt return can be stubbed`() { + fun `arg None, returns UInt`() { val mock = mockk { - every { getUInt() } returns 999u + every { argNoneReturnsUInt() } returns 999u } - val result = mock.getUInt() + val result = mock.argNoneReturnsUInt() assertEquals(999u, result) } /** https://github.com/mockk/mockk/issues/729 */ @Test - fun `verify extension function with UInt return can be stubbed`() { + fun `arg None, answers UInt`() { + val mock = mockk { + every { argNoneReturnsUInt() } answers { 999u } + } + + val result = mock.argNoneReturnsUInt() + + assertEquals(999u, result) + } + // + + // + // + @Test + @Ignore // TODO fix infinite loop + fun `receiver is String, return is Inner`() { + val fn = mockk DummyValue>() + + every { "string".fn() } returns dummyValueInnerReturn + + val result = "string".fn() + + assertEquals(dummyValueInnerReturn, result) + } - val fn = mockk UInt>() + @Test + @Ignore // TODO fix infinite loop + fun `receiver is String, return is Wrapper`() { + val fn = mockk DummyValueWrapper>() - every { "string".fn() } returns 777u + every { "string".fn() } returns dummyValueWrapperArg val result = "string".fn() - assertEquals(777u, result) + assertEquals(dummyValueWrapperArg, result) } -} + // -@JvmInline -value class DummyValue(val value: Int) + // + @Test + @Ignore // TODO fix infinite loop + fun `receiver is Outer, return is Wrapper`() { + val fn = mockk DummyValueWrapper>() -class DummyService { + every { dummyValueWrapperArg.fn() } returns dummyValueWrapperArg - fun requestValue() = DummyValue(0) + val result = dummyValueWrapperArg.fn() - fun processValue(value: DummyValue) = DummyValue(0) + assertEquals(dummyValueWrapperArg, result) + } - fun getUInt(): UInt = 123u -} + @Test + @Ignore // TODO fix infinite loop + fun `receiver is Outer, return is Inner`() { + val fn = mockk DummyValue>() + + every { dummyValueWrapperArg.fn() } returns dummyValueInnerReturn + + val result = dummyValueWrapperArg.fn() + + assertEquals(dummyValueInnerArg, result) + } + + @Test + @Ignore // TODO fix infinite loop + fun `receiver is Outer, return is String`() { + val fn = mockk String>() + + every { dummyValueWrapperArg.fn() } returns "example" + + val result = dummyValueWrapperArg.fn() + + assertEquals("example", result) + } + // + + // + @Test + @Ignore // TODO fix infinite loop + fun `receiver is Inner, return is Wrapper`() { + val fn = mockk DummyValueWrapper>() + + every { dummyValueInnerArg.fn() } returns dummyValueWrapperReturn -@JvmInline -value class ValueDummy(val value: String) + val result = dummyValueInnerArg.fn() -interface ValueServiceDummy { - fun doSomething(value: ValueDummy): Int - fun getSomething(): ValueDummy + assertEquals(dummyValueWrapperArg, result) + } + + @Test + @Ignore // TODO fix infinite loop + fun `receiver is Inner, return is Inner`() { + val fn = mockk DummyValue>() + + every { dummyValueInnerArg.fn() } returns dummyValueInnerReturn + + val result = dummyValueInnerArg.fn() + + assertEquals(dummyValueInnerReturn, result) + } + + @Test + @Ignore // TODO fix infinite loop + fun `receiver is Inner, return is String`() { + val fn = mockk String>() + + every { dummyValueInnerArg.fn() } returns "example" + + val result = dummyValueInnerArg.fn() + + assertEquals("example", result) + } + // + // + + + companion object { + + @JvmInline + value class DummyValue(val value: Int) + + @JvmInline + value class DummyValueWrapper(val value: DummyValue) + + class DummyService { + + fun argWrapperReturnWrapper(outer: DummyValueWrapper): DummyValueWrapper = + DummyValueWrapper(DummyValue(0)) + + fun argWrapperReturnValueClass(outer: DummyValueWrapper): DummyValue = + DummyValue(0) + + fun argValueClassReturnWrapper(inner: DummyValue): DummyValueWrapper = + DummyValueWrapper(inner) + + fun argValueClassReturnValueClass(inner: DummyValue): DummyValue = + DummyValue(0) + + + fun argNoneReturnsUInt(): UInt = 123u + } + } } diff --git a/mockk/jvm/src/main/kotlin/io/mockk/impl/InternalPlatform.kt b/mockk/jvm/src/main/kotlin/io/mockk/impl/InternalPlatform.kt index cb586d936..4b2dc598a 100644 --- a/mockk/jvm/src/main/kotlin/io/mockk/impl/InternalPlatform.kt +++ b/mockk/jvm/src/main/kotlin/io/mockk/impl/InternalPlatform.kt @@ -63,7 +63,7 @@ actual object InternalPlatform { actual fun packRef(arg: Any?): Any? { return when { arg == null -> null - isPassedByValue(arg.boxedClass()) -> arg.boxedValue() + isPassedByValue(arg::class.boxedClass) -> arg.boxedValue else -> ref(arg) } } diff --git a/mockk/jvm/src/main/kotlin/io/mockk/impl/instantiation/JvmMockFactoryHelper.kt b/mockk/jvm/src/main/kotlin/io/mockk/impl/instantiation/JvmMockFactoryHelper.kt index 71552c367..b08b9250d 100644 --- a/mockk/jvm/src/main/kotlin/io/mockk/impl/instantiation/JvmMockFactoryHelper.kt +++ b/mockk/jvm/src/main/kotlin/io/mockk/impl/instantiation/JvmMockFactoryHelper.kt @@ -151,7 +151,7 @@ object JvmMockFactoryHelper { is KType -> kotlinReturnType.classifier as? KClass<*> ?: returnType.kotlin is KClass<*> -> kotlinReturnType else -> returnType.kotlin - }.boxedClass() + }.boxedClass val androidCompatibleReturnType = if (returnType.qualifiedName in androidUnsupportedTypes) { this@toDescription.returnType.kotlin From af349e7c138dede61bcf6fe9901f69c1e9e5c28a Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 24 Jul 2022 10:57:59 +0200 Subject: [PATCH 14/17] disable wrapped value class tests, rename tests for consistency --- .../test/kotlin/io/mockk/it/ValueClassTest.kt | 239 ++++++++++-------- 1 file changed, 128 insertions(+), 111 deletions(-) diff --git a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt index 0816b89c6..826a0e7b1 100644 --- a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt +++ b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt @@ -14,201 +14,210 @@ class ValueClassTest { private val dummyValueWrapperArg get() = DummyValueWrapper(DummyValue(42)) private val dummyValueWrapperReturn get() = DummyValueWrapper(DummyValue(99)) - private val dummyValueInnerArg get() = DummyValue(101) - private val dummyValueInnerReturn get() = DummyValue(202) + private val dummyValueClassArg get() = DummyValue(101) + private val dummyValueClassReturn get() = DummyValue(202) - // + // @Test - fun `arg is ValueClass, returns Wrapper`() { + fun `arg is ValueClass, returns ValueClass`() { val mock = mockk { - every { argValueClassReturnWrapper(dummyValueInnerArg) } returns dummyValueWrapperReturn + every { argValueClassReturnValueClass(dummyValueClassArg) } returns dummyValueClassReturn } - assertEquals(dummyValueWrapperReturn, mock.argValueClassReturnWrapper(dummyValueInnerArg)) + assertEquals(dummyValueClassReturn, mock.argValueClassReturnValueClass(dummyValueClassArg)) - verify { mock.argValueClassReturnWrapper(dummyValueInnerArg) } + verify { mock.argValueClassReturnValueClass(dummyValueClassArg) } } @Test - fun `arg is any(ValueClass), returns Wrapper`() { + fun `arg is any(ValueClass), returns ValueClass`() { val mock = mockk { - every { argValueClassReturnWrapper(any()) } returns dummyValueWrapperReturn + every { argValueClassReturnValueClass(any()) } returns dummyValueClassReturn } - assertEquals(dummyValueWrapperArg, mock.argValueClassReturnWrapper(dummyValueInnerArg)) + assertEquals(dummyValueClassReturn, mock.argValueClassReturnValueClass(dummyValueClassArg)) - verify { mock.argValueClassReturnWrapper(dummyValueInnerArg) } + verify { mock.argValueClassReturnValueClass(dummyValueClassArg) } } @Test - fun `arg is slot(ValueClass), returns Wrapper`() { + fun `arg is slot(ValueClass), returns ValueClass`() { val slot = slot() - val mock = mockk { - every { argValueClassReturnWrapper(capture(slot)) } returns dummyValueWrapperReturn + every { argValueClassReturnValueClass(capture(slot)) } returns dummyValueClassReturn } - val result = mock.argValueClassReturnWrapper(dummyValueInnerArg) + val result = mock.argValueClassReturnValueClass(dummyValueClassArg) - assertEquals(dummyValueWrapperReturn, result) + assertEquals(dummyValueClassReturn, result) - assertEquals(dummyValueInnerArg, slot.captured) + assertEquals(dummyValueClassArg, slot.captured) - verify { mock.argValueClassReturnWrapper(dummyValueInnerArg) } + verify { mock.argValueClassReturnValueClass(dummyValueClassArg) } } @Test - fun `arg is ValueClass, answers Wrapper`() { + fun `arg is ValueClass, answers ValueClass`() { val mock = mockk { - every { argValueClassReturnWrapper(dummyValueInnerArg) } answers { dummyValueWrapperReturn } + every { argValueClassReturnValueClass(dummyValueClassArg) } answers { dummyValueClassReturn } } - assertEquals(dummyValueWrapperReturn, mock.argValueClassReturnWrapper(dummyValueInnerArg)) + assertEquals(dummyValueClassReturn, mock.argValueClassReturnValueClass(dummyValueClassArg)) - verify { mock.argValueClassReturnWrapper(dummyValueInnerArg) } + verify { mock.argValueClassReturnValueClass(dummyValueClassArg) } } @Test - fun `arg is any(ValueClass), answers Wrapper`() { + fun `arg is any(ValueClass), answers ValueClass`() { val mock = mockk { - every { argValueClassReturnWrapper(any()) } answers { dummyValueWrapperReturn } + every { argValueClassReturnValueClass(any()) } answers { dummyValueClassReturn } } - assertEquals(dummyValueWrapperReturn, mock.argValueClassReturnWrapper(dummyValueInnerArg)) + assertEquals(dummyValueClassReturn, mock.argValueClassReturnValueClass(dummyValueClassArg)) - verify { mock.argValueClassReturnWrapper(dummyValueInnerArg) } + verify { mock.argValueClassReturnValueClass(dummyValueClassArg) } } @Test - fun `arg is slot(ValueClass), answers Wrapper`() { + fun `arg is slot(ValueClass), answers ValueClass`() { val slot = slot() val mock = mockk { - every { argValueClassReturnWrapper(capture(slot)) } answers { dummyValueWrapperReturn } + every { argValueClassReturnValueClass(capture(slot)) } answers { dummyValueClassReturn } } - val result = mock.argValueClassReturnWrapper(dummyValueInnerArg) + val result = mock.argValueClassReturnValueClass(dummyValueClassArg) - assertEquals(dummyValueWrapperReturn, result) + assertEquals(dummyValueClassReturn, result) - assertEquals(dummyValueInnerArg, slot.captured) + assertEquals(dummyValueClassArg, slot.captured) - verify { mock.argValueClassReturnWrapper(dummyValueInnerArg) } + verify { mock.argValueClassReturnValueClass(dummyValueClassArg) } } // - // + // @Test - fun `arg is ValueClass, returns Inner`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is ValueClass, returns Wrapper`() { val mock = mockk { - every { argValueClassReturnValueClass(dummyValueInnerArg) } returns dummyValueInnerReturn + every { argValueClassReturnWrapper(dummyValueClassArg) } returns dummyValueWrapperReturn } - assertEquals(dummyValueInnerReturn, mock.argValueClassReturnValueClass(dummyValueInnerArg)) + assertEquals(dummyValueWrapperReturn, mock.argValueClassReturnWrapper(dummyValueClassArg)) - verify { mock.argValueClassReturnValueClass(dummyValueInnerArg) } + verify { mock.argValueClassReturnWrapper(dummyValueClassArg) } } @Test - fun `arg is any(ValueClass), returns Inner`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is any(ValueClass), returns Wrapper`() { val mock = mockk { - every { argValueClassReturnValueClass(any()) } returns dummyValueInnerReturn + every { argValueClassReturnWrapper(any()) } returns dummyValueWrapperReturn } - assertEquals(dummyValueInnerReturn, mock.argValueClassReturnValueClass(dummyValueInnerArg)) + assertEquals(dummyValueWrapperArg, mock.argValueClassReturnWrapper(dummyValueClassArg)) - verify { mock.argValueClassReturnValueClass(dummyValueInnerArg) } + verify { mock.argValueClassReturnWrapper(dummyValueClassArg) } } @Test - fun `arg is slot(ValueClass), returns Inner`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is slot(ValueClass), returns Wrapper`() { val slot = slot() + val mock = mockk { - every { argValueClassReturnValueClass(capture(slot)) } returns dummyValueInnerReturn + every { argValueClassReturnWrapper(capture(slot)) } returns dummyValueWrapperReturn } - val result = mock.argValueClassReturnValueClass(dummyValueInnerArg) + val result = mock.argValueClassReturnWrapper(dummyValueClassArg) - assertEquals(dummyValueInnerReturn, result) + assertEquals(dummyValueWrapperReturn, result) - assertEquals(dummyValueInnerArg, slot.captured) + assertEquals(dummyValueClassArg, slot.captured) - verify { mock.argValueClassReturnValueClass(dummyValueInnerArg) } + verify { mock.argValueClassReturnWrapper(dummyValueClassArg) } } @Test - fun `arg is ValueClass, answers Inner`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is ValueClass, answers Wrapper`() { val mock = mockk { - every { argValueClassReturnValueClass(dummyValueInnerArg) } answers { dummyValueInnerReturn } + every { argValueClassReturnWrapper(dummyValueClassArg) } answers { dummyValueWrapperReturn } } - assertEquals(dummyValueInnerReturn, mock.argValueClassReturnValueClass(dummyValueInnerArg)) + assertEquals(dummyValueWrapperReturn, mock.argValueClassReturnWrapper(dummyValueClassArg)) - verify { mock.argValueClassReturnValueClass(dummyValueInnerArg) } + verify { mock.argValueClassReturnWrapper(dummyValueClassArg) } } @Test - fun `arg is any(ValueClass), answers Inner`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is any(ValueClass), answers Wrapper`() { val mock = mockk { - every { argValueClassReturnValueClass(any()) } answers { dummyValueInnerReturn } + every { argValueClassReturnWrapper(any()) } answers { dummyValueWrapperReturn } } - assertEquals(dummyValueInnerReturn, mock.argValueClassReturnValueClass(dummyValueInnerArg)) + assertEquals(dummyValueWrapperReturn, mock.argValueClassReturnWrapper(dummyValueClassArg)) - verify { mock.argValueClassReturnValueClass(dummyValueInnerArg) } + verify { mock.argValueClassReturnWrapper(dummyValueClassArg) } } @Test - fun `arg is slot(ValueClass), answers Inner`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is slot(ValueClass), answers Wrapper`() { val slot = slot() val mock = mockk { - every { argValueClassReturnValueClass(capture(slot)) } answers { dummyValueInnerReturn } + every { argValueClassReturnWrapper(capture(slot)) } answers { dummyValueWrapperReturn } } - val result = mock.argValueClassReturnValueClass(dummyValueInnerArg) + val result = mock.argValueClassReturnWrapper(dummyValueClassArg) - assertEquals(dummyValueInnerReturn, result) + assertEquals(dummyValueWrapperReturn, result) - assertEquals(dummyValueInnerArg, slot.captured) + assertEquals(dummyValueClassArg, slot.captured) - verify { mock.argValueClassReturnValueClass(dummyValueInnerArg) } + verify { mock.argValueClassReturnWrapper(dummyValueClassArg) } } // // @Test - fun `arg is Outer, returns Inner`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is Wrapper, returns ValueClass`() { val mock = mockk { - every { argWrapperReturnValueClass(dummyValueWrapperArg) } returns dummyValueInnerReturn + every { argWrapperReturnValueClass(dummyValueWrapperArg) } returns dummyValueClassReturn } - assertEquals(dummyValueInnerReturn, mock.argWrapperReturnValueClass(dummyValueWrapperArg)) + assertEquals(dummyValueClassReturn, mock.argWrapperReturnValueClass(dummyValueWrapperArg)) verify { mock.argWrapperReturnValueClass(dummyValueWrapperArg) } } @Test - fun `arg is any(Outer), returns Inner`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is any(Wrapper), returns ValueClass`() { val mock = mockk { - every { argWrapperReturnValueClass(any()) } returns dummyValueInnerReturn + every { argWrapperReturnValueClass(any()) } returns dummyValueClassReturn } - assertEquals(dummyValueInnerReturn, mock.argWrapperReturnValueClass(dummyValueWrapperArg)) + assertEquals(dummyValueClassReturn, mock.argWrapperReturnValueClass(dummyValueWrapperArg)) verify { mock.argWrapperReturnValueClass(dummyValueWrapperArg) } } @Test - fun `arg is slot(Outer), returns Inner`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is slot(Wrapper), returns ValueClass`() { val slot = slot() val mock = mockk { - every { argWrapperReturnValueClass(capture(slot)) } returns dummyValueInnerReturn + every { argWrapperReturnValueClass(capture(slot)) } returns dummyValueClassReturn } val result = mock.argWrapperReturnValueClass(dummyValueWrapperArg) - assertEquals(dummyValueInnerReturn, result) + assertEquals(dummyValueClassReturn, result) assertEquals(dummyValueWrapperArg, slot.captured) @@ -216,38 +225,41 @@ class ValueClassTest { } @Test - fun `arg is Outer, answers Inner`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is Wrapper, answers ValueClass`() { val mock = mockk { - every { argWrapperReturnValueClass(dummyValueWrapperArg) } answers { dummyValueInnerReturn } + every { argWrapperReturnValueClass(dummyValueWrapperArg) } answers { dummyValueClassReturn } } - assertEquals(dummyValueInnerReturn, mock.argWrapperReturnValueClass(dummyValueWrapperArg)) + assertEquals(dummyValueClassReturn, mock.argWrapperReturnValueClass(dummyValueWrapperArg)) verify { mock.argWrapperReturnValueClass(dummyValueWrapperArg) } } @Test - fun `arg is any(Outer), answers Inner`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is any(Wrapper), answers ValueClass`() { val mock = mockk { - every { argWrapperReturnValueClass(any()) } answers { dummyValueInnerReturn } + every { argWrapperReturnValueClass(any()) } answers { dummyValueClassReturn } } - assertEquals(dummyValueInnerReturn, mock.argWrapperReturnValueClass(dummyValueWrapperArg)) + assertEquals(dummyValueClassReturn, mock.argWrapperReturnValueClass(dummyValueWrapperArg)) verify { mock.argWrapperReturnValueClass(dummyValueWrapperArg) } } @Test - fun `arg is slot(Outer), answers Inner`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is slot(Wrapper), answers ValueClass`() { val slot = slot() val mock = mockk { - every { argWrapperReturnValueClass(capture(slot)) } answers { dummyValueInnerReturn } + every { argWrapperReturnValueClass(capture(slot)) } answers { dummyValueClassReturn } } val result = mock.argWrapperReturnValueClass(dummyValueWrapperArg) - assertEquals(dummyValueInnerReturn, result) + assertEquals(dummyValueClassReturn, result) assertEquals(dummyValueWrapperArg, slot.captured) @@ -257,7 +269,8 @@ class ValueClassTest { // @Test - fun `arg is Outer, returns Wrapper`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is Wrapper, returns Wrapper`() { val mock = mockk { every { argWrapperReturnWrapper(dummyValueWrapperArg) } returns dummyValueWrapperReturn } @@ -268,7 +281,8 @@ class ValueClassTest { } @Test - fun `arg is any(Outer), returns Wrapper`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is any(Wrapper), returns Wrapper`() { val mock = mockk { every { argWrapperReturnWrapper(any()) } returns dummyValueWrapperReturn } @@ -279,7 +293,8 @@ class ValueClassTest { } @Test - fun `arg is slot(Outer), returns Wrapper`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is slot(Wrapper), returns Wrapper`() { val slot = slot() val mock = mockk { every { argWrapperReturnWrapper(capture(slot)) } returns dummyValueWrapperReturn @@ -295,7 +310,8 @@ class ValueClassTest { } @Test - fun `arg is Outer, answers Wrapper`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is Wrapper, answers Wrapper`() { val mock = mockk { every { argWrapperReturnWrapper(dummyValueWrapperArg) } answers { dummyValueWrapperReturn } } @@ -306,7 +322,8 @@ class ValueClassTest { } @Test - fun `arg is any(Outer), answers Wrapper`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is any(Wrapper), answers Wrapper`() { val mock = mockk { every { argWrapperReturnWrapper(any()) } answers { dummyValueWrapperReturn } } @@ -317,7 +334,8 @@ class ValueClassTest { } @Test - fun `arg is slot(Outer), answers Wrapper`() { + @Ignore // TODO support nested value classes https://github.com/mockk/mockk/issues/859 + fun `arg is slot(Wrapper), answers Wrapper`() { val slot = slot() val mock = mockk { @@ -364,14 +382,14 @@ class ValueClassTest { // @Test @Ignore // TODO fix infinite loop - fun `receiver is String, return is Inner`() { + fun `receiver is String, return is ValueClass`() { val fn = mockk DummyValue>() - every { "string".fn() } returns dummyValueInnerReturn + every { "string".fn() } returns dummyValueClassReturn val result = "string".fn() - assertEquals(dummyValueInnerReturn, result) + assertEquals(dummyValueClassReturn, result) } @Test @@ -387,10 +405,10 @@ class ValueClassTest { } // - // + // @Test @Ignore // TODO fix infinite loop - fun `receiver is Outer, return is Wrapper`() { + fun `receiver is Wrapper, return is Wrapper`() { val fn = mockk DummyValueWrapper>() every { dummyValueWrapperArg.fn() } returns dummyValueWrapperArg @@ -402,19 +420,19 @@ class ValueClassTest { @Test @Ignore // TODO fix infinite loop - fun `receiver is Outer, return is Inner`() { + fun `receiver is Wrapper, return is ValueClass`() { val fn = mockk DummyValue>() - every { dummyValueWrapperArg.fn() } returns dummyValueInnerReturn + every { dummyValueWrapperArg.fn() } returns dummyValueClassReturn val result = dummyValueWrapperArg.fn() - assertEquals(dummyValueInnerArg, result) + assertEquals(dummyValueClassArg, result) } @Test @Ignore // TODO fix infinite loop - fun `receiver is Outer, return is String`() { + fun `receiver is Wrapper, return is String`() { val fn = mockk String>() every { dummyValueWrapperArg.fn() } returns "example" @@ -425,46 +443,45 @@ class ValueClassTest { } // - // + // @Test @Ignore // TODO fix infinite loop - fun `receiver is Inner, return is Wrapper`() { + fun `receiver is ValueClass, return is Wrapper`() { val fn = mockk DummyValueWrapper>() - every { dummyValueInnerArg.fn() } returns dummyValueWrapperReturn + every { dummyValueClassArg.fn() } returns dummyValueWrapperReturn - val result = dummyValueInnerArg.fn() + val result = dummyValueClassArg.fn() assertEquals(dummyValueWrapperArg, result) } @Test @Ignore // TODO fix infinite loop - fun `receiver is Inner, return is Inner`() { + fun `receiver is ValueClass, return is ValueClass`() { val fn = mockk DummyValue>() - every { dummyValueInnerArg.fn() } returns dummyValueInnerReturn + every { dummyValueClassArg.fn() } returns dummyValueClassReturn - val result = dummyValueInnerArg.fn() + val result = dummyValueClassArg.fn() - assertEquals(dummyValueInnerReturn, result) + assertEquals(dummyValueClassReturn, result) } @Test @Ignore // TODO fix infinite loop - fun `receiver is Inner, return is String`() { + fun `receiver is ValueClass, return is String`() { val fn = mockk String>() - every { dummyValueInnerArg.fn() } returns "example" + every { dummyValueClassArg.fn() } returns "example" - val result = dummyValueInnerArg.fn() + val result = dummyValueClassArg.fn() assertEquals("example", result) } // // - companion object { @JvmInline @@ -475,16 +492,16 @@ class ValueClassTest { class DummyService { - fun argWrapperReturnWrapper(outer: DummyValueWrapper): DummyValueWrapper = + fun argWrapperReturnWrapper(wrapper: DummyValueWrapper): DummyValueWrapper = DummyValueWrapper(DummyValue(0)) - fun argWrapperReturnValueClass(outer: DummyValueWrapper): DummyValue = + fun argWrapperReturnValueClass(wrapper: DummyValueWrapper): DummyValue = DummyValue(0) - fun argValueClassReturnWrapper(inner: DummyValue): DummyValueWrapper = - DummyValueWrapper(inner) + fun argValueClassReturnWrapper(valueClass: DummyValue): DummyValueWrapper = + DummyValueWrapper(valueClass) - fun argValueClassReturnValueClass(inner: DummyValue): DummyValue = + fun argValueClassReturnValueClass(valueClass: DummyValue): DummyValue = DummyValue(0) From 592bdaf710e1be674742afa61147dfad584d6072 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 24 Jul 2022 10:58:53 +0200 Subject: [PATCH 15/17] rename file to match obj name --- .../io/mockk/{ValueClassSupport.kt => ValueClassSupportDsl.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dsl/jvm/src/main/kotlin/io/mockk/{ValueClassSupport.kt => ValueClassSupportDsl.kt} (100%) diff --git a/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt b/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupportDsl.kt similarity index 100% rename from dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt rename to dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupportDsl.kt From fcaef2ea109d65cf0593182430206bada6ef47f8 Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 24 Jul 2022 20:28:20 +0200 Subject: [PATCH 16/17] added timeout for Gradle test tasks --- build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.gradle b/build.gradle index b8998b085..9085fd6b1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,5 @@ +import java.time.Duration + buildscript { ext.kotlin_gradle_version = findProperty('kotlin.version')?.toString() ?: '1.6.0' ext.android_gradle_version = '7.0.0' @@ -44,4 +46,8 @@ subprojects { subProject -> languageVersion = "1.5" } } + + tasks.withType(Test).configureEach { + timeout.set(Duration.ofMinutes(2)) + } } From 8b8017cc8498ab8768cfccf05fc0d95efeaf1f9e Mon Sep 17 00:00:00 2001 From: Adam <897017+aSemy@users.noreply.github.com> Date: Sun, 24 Jul 2022 20:35:36 +0200 Subject: [PATCH 17/17] bump test timeout --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9085fd6b1..8c0d28a46 100644 --- a/build.gradle +++ b/build.gradle @@ -48,6 +48,6 @@ subprojects { subProject -> } tasks.withType(Test).configureEach { - timeout.set(Duration.ofMinutes(2)) + timeout.set(Duration.ofMinutes(5)) } }