diff --git a/buildSrc/src/main/kotlin/buildsrc/convention/mockk-publishing.gradle.kts b/buildSrc/src/main/kotlin/buildsrc/convention/mockk-publishing.gradle.kts index 2dc5aa3d1..65fce2547 100644 --- a/buildSrc/src/main/kotlin/buildsrc/convention/mockk-publishing.gradle.kts +++ b/buildSrc/src/main/kotlin/buildsrc/convention/mockk-publishing.gradle.kts @@ -50,9 +50,9 @@ val localrepo: String by project publishing { repositories { // publish to local dir, for testing - maven(rootProject.layout.projectDirectory.dir(localrepo)) { + /*maven(rootProject.layout.projectDirectory.dir(localrepo)) { name = "LocalRepo" - } + }*/ /*maven { url = uri(sonatypeRepositoryReleaseUrl) diff --git a/gradle.properties b/gradle.properties index 3c484eaae..dd3721bf5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=1.13.18-SNAPSHOT +version=1.14.1-SNAPSHOT # Enable Gradle build cache https://docs.gradle.org/current/userguide/build_cache.html org.gradle.caching=true org.gradle.configureondemand=false diff --git a/modules/mockk-agent/src/jvmMain/kotlin/io/mockk/proxy/jvm/transformation/InliningClassTransformer.kt b/modules/mockk-agent/src/jvmMain/kotlin/io/mockk/proxy/jvm/transformation/InliningClassTransformer.kt index f672aa493..9573e3c2e 100644 --- a/modules/mockk-agent/src/jvmMain/kotlin/io/mockk/proxy/jvm/transformation/InliningClassTransformer.kt +++ b/modules/mockk-agent/src/jvmMain/kotlin/io/mockk/proxy/jvm/transformation/InliningClassTransformer.kt @@ -15,6 +15,7 @@ import net.bytebuddy.asm.AsmVisitorWrapper import net.bytebuddy.description.ModifierReviewable.OfByteCodeElement import net.bytebuddy.description.method.MethodDescription import net.bytebuddy.dynamic.ClassFileLocator.Simple.of +import net.bytebuddy.dynamic.VisibilityBridgeStrategy import net.bytebuddy.matcher.ElementMatchers.* import java.io.File import java.lang.instrument.ClassFileTransformer @@ -71,7 +72,8 @@ internal class InliningClassTransformer( ?: return classfileBuffer try { - val builder = byteBuddy.redefine(classBeingRedefined, of(classBeingRedefined.name, classfileBuffer)) + val builder = byteBuddy + .decorate(classBeingRedefined, of(classBeingRedefined.name, classfileBuffer)) .visit(FixParameterNamesVisitor(classBeingRedefined)) val type = builder diff --git a/modules/mockk-agent/src/jvmTest/java/io/mockk/proxy/JvmMockKProxyMakerTest.java b/modules/mockk-agent/src/jvmTest/java/io/mockk/proxy/JvmMockKProxyMakerTest.java index e196a1af4..3ad4b5b78 100644 --- a/modules/mockk-agent/src/jvmTest/java/io/mockk/proxy/JvmMockKProxyMakerTest.java +++ b/modules/mockk-agent/src/jvmTest/java/io/mockk/proxy/JvmMockKProxyMakerTest.java @@ -411,6 +411,25 @@ public void openClassAnnotationProxy() throws Exception { checkProxyHandlerCalled(1, proxy, "i", 1, 1); } + interface InterfaceWithDefault { + default void foo(Runnable d) { + d.run(); + } + } + + static public class ClassImplementingPackagePrivateInterfaceWithDefaultMethod implements InterfaceWithDefault { + } + + @Test + public void classImplementingPackagePrivateInterfaceWithDefaultMethodProxy() { + ClassImplementingPackagePrivateInterfaceWithDefaultMethod proxy = makeProxy(ClassImplementingPackagePrivateInterfaceWithDefaultMethod.class); + + proxy.foo(() -> { executed[0] = true; }); + + assertFalse(executed[0]); + checkProxyHandlerCalled(1, proxy, "foo", 1, 1); + } + private void checkProxyHandlerCalled(int nTimes, Object proxy, String methodName) { checkProxyHandlerCalled(nTimes, proxy, methodName, 0, 0); } diff --git a/modules/mockk-core/src/jvmMain/kotlin/io/mockk/core/ValueClassSupport.kt b/modules/mockk-core/src/jvmMain/kotlin/io/mockk/core/ValueClassSupport.kt index 906a73426..c387d03f8 100644 --- a/modules/mockk-core/src/jvmMain/kotlin/io/mockk/core/ValueClassSupport.kt +++ b/modules/mockk-core/src/jvmMain/kotlin/io/mockk/core/ValueClassSupport.kt @@ -70,6 +70,7 @@ actual object ValueClassSupport { /** * Underlying property value of a **`value class`** or self. + * Includes workaround for [Result] class * * The type of the return might also be a `value class`! */ @@ -78,7 +79,13 @@ actual object ValueClassSupport { get() = if (!this::class.isValue_safe) { this } else { - (this::class as KClass).boxedProperty.get(this)?.boxedValue + val klass = (this::class as KClass) + val boxedProperty = klass.boxedProperty.get(this) + if (klass == Result::class) { + boxedProperty + } else { + boxedProperty?.boxedValue + } } /** diff --git a/modules/mockk-dsl/src/commonMain/kotlin/io/mockk/API.kt b/modules/mockk-dsl/src/commonMain/kotlin/io/mockk/API.kt index 1557ce58d..9ca840df9 100644 --- a/modules/mockk-dsl/src/commonMain/kotlin/io/mockk/API.kt +++ b/modules/mockk-dsl/src/commonMain/kotlin/io/mockk/API.kt @@ -270,13 +270,27 @@ object MockKDsl { * Checks if all recorded calls were verified. */ fun internalConfirmVerified(mocks: Array) { + val verifier = MockKGateway.implementation().verificationAcknowledger + if (mocks.isEmpty()) { - MockKGateway.implementation().verificationAcknowledger.acknowledgeVerified() + verifier.acknowledgeVerified() + } else { + mocks.forEach { verifier.acknowledgeVerified(it) } } - for (mock in mocks) { - MockKGateway.implementation().verificationAcknowledger.acknowledgeVerified(mock) - } + resetVerificationState() + } + + private fun resetVerificationState() { + val options = ClearOptions( + answers = false, + recordedCalls = true, + childMocks = false, + verificationMarks = true, + exclusionRules = false + ) + + MockKGateway.implementation().clearer.clearAll(options, currentThreadOnly = true) } /** diff --git a/modules/mockk/src/commonMain/kotlin/io/mockk/MockK.kt b/modules/mockk/src/commonMain/kotlin/io/mockk/MockK.kt index 8baeda0c8..e9d7f90cf 100644 --- a/modules/mockk/src/commonMain/kotlin/io/mockk/MockK.kt +++ b/modules/mockk/src/commonMain/kotlin/io/mockk/MockK.kt @@ -258,7 +258,9 @@ fun coVerify( } /** - * Verifies that all calls inside [verifyBlock] happened. **Does not** verify any order. + * Verifies all the calls of the mocks specified inside [verifyBlock]. + * Once a mock is specified inside [verifyBlock], all its recorded calls are expected to be verified within [verifyBlock]. Otherwise, the verification fails. + * **Does not** verify any order. * * If ordering is important, use [verifyOrder]. * diff --git a/modules/mockk/src/commonTest/kotlin/io/mockk/it/ConstructorMockTest.kt b/modules/mockk/src/commonTest/kotlin/io/mockk/it/ConstructorMockTest.kt index 6e8c7f526..c1361dcc7 100644 --- a/modules/mockk/src/commonTest/kotlin/io/mockk/it/ConstructorMockTest.kt +++ b/modules/mockk/src/commonTest/kotlin/io/mockk/it/ConstructorMockTest.kt @@ -152,6 +152,19 @@ class ConstructorMockTest { } } + @Test + fun `constructedWith() using default constructor params`() { + mockkConstructor(MockCls::class) + + every { constructedWith().op(1, 3) } returns 33 + + assertEquals(33, MockCls().op(1, 3)) + + verify { + constructedWith().op(1, 3) + } + } + @Test fun unmockkAllconstructedWith() { mockkConstructor(MockCls::class) diff --git a/modules/mockk/src/commonTest/kotlin/io/mockk/it/ValueClassTest.kt b/modules/mockk/src/commonTest/kotlin/io/mockk/it/ValueClassTest.kt index 626643800..6cac07cf7 100644 --- a/modules/mockk/src/commonTest/kotlin/io/mockk/it/ValueClassTest.kt +++ b/modules/mockk/src/commonTest/kotlin/io/mockk/it/ValueClassTest.kt @@ -7,6 +7,7 @@ import io.mockk.slot import io.mockk.spyk import io.mockk.verify import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.assertTimeoutPreemptively import java.time.Duration import java.util.UUID @@ -18,6 +19,9 @@ class ValueClassTest { private val dummyValueWrapperArg get() = DummyValueWrapper(DummyValue(42)) private val dummyValueWrapperReturn get() = DummyValueWrapper(DummyValue(99)) + private val complexValueParam = UUID.fromString("4d19b22c-7754-4c55-ba4d-f80109708a1f") + private val complexValueResultArg get() = ComplexValue(complexValueParam) + private val complexValueResultReturn get() = Result.success(complexValueResultArg) private val dummyValueClassArg get() = DummyValue(101) private val dummyComplexValueClassArg get() = ComplexValue(UUID.fromString("4d19b22c-7754-4c55-ba4d-f80109708a1f")) @@ -542,6 +546,18 @@ class ValueClassTest { } // + @Test + fun `receiver is ComplexValue, return is Result with ComplexValue`() { + val service = mockk() + + every { service.argGenericValueClassReturnResultWrapped(complexValueResultArg) } returns complexValueResultReturn + + val result = service.argGenericValueClassReturnResultWrapped(complexValueResultArg) + + assertEquals(complexValueResultReturn.getOrNull()?.value, result.getOrNull()?.value) + assertEquals(complexValueResultReturn.getOrNull(), result.getOrNull()) + } + // @Test @Ignore // TODO fix infinite loop @@ -787,6 +803,9 @@ class ValueClassTest { fun argValueClassReturnWrapper(valueClass: DummyValue): DummyValueWrapper = DummyValueWrapper(valueClass) + fun argGenericValueClassReturnResultWrapped(valueClass: ComplexValue): Result = + Result.success(valueClass) + fun argValueClassReturnValueClass(valueClass: DummyValue): DummyValue = DummyValue(0) diff --git a/modules/mockk/src/jvmMain/kotlin/io/mockk/impl/instantiation/JvmConstructorMockFactory.kt b/modules/mockk/src/jvmMain/kotlin/io/mockk/impl/instantiation/JvmConstructorMockFactory.kt index 6440e412a..a16105cad 100644 --- a/modules/mockk/src/jvmMain/kotlin/io/mockk/impl/instantiation/JvmConstructorMockFactory.kt +++ b/modules/mockk/src/jvmMain/kotlin/io/mockk/impl/instantiation/JvmConstructorMockFactory.kt @@ -116,7 +116,7 @@ class JvmConstructorMockFactory( private fun getConstructorMock(args: Array>?): ConstructorMock? { return synchronized(handlers) { - if (args == null) { + if (args.isNullOrEmpty()) { if (allHandler == null) { allHandler = ConstructorMock(cls, recordPrivateCalls) } diff --git a/modules/mockk/src/jvmTest/kotlin/io/mockk/it/VerificationAcknowledgeTest.kt b/modules/mockk/src/jvmTest/kotlin/io/mockk/it/VerificationAcknowledgeTest.kt index 1e0886b73..5a7150439 100644 --- a/modules/mockk/src/jvmTest/kotlin/io/mockk/it/VerificationAcknowledgeTest.kt +++ b/modules/mockk/src/jvmTest/kotlin/io/mockk/it/VerificationAcknowledgeTest.kt @@ -587,52 +587,8 @@ class VerificationAcknowledgeTest { confirmVerified(mock) } - @Test - fun clearMarks() { - - every { mock.op(1) } returns 1 - every { mock.op(2) } returns 2 - every { mock.op(3) } returns 3 - every { mock.op(4) } returns 4 - - assertEquals(1, mock.op(1)) - assertEquals(2, mock.op(2)) - assertEquals(3, mock.op(3)) - assertEquals(4, mock.op(4)) - - verify { - mock.op(1) - mock.op(2) - mock.op(3) - } - - assertFails { - confirmVerified(mock) - } - - verify { - mock.op(4) - } - - confirmVerified(mock) - - clearMocks( - mock, - answers = false, - recordedCalls = false, - childMocks = false, - verificationMarks = true, - exclusionRules = false - ) - - assertFails { - confirmVerified(mock) - } - } - @Test fun confirmVerifiedAll() { - clearAllMocks() doCalls1() verify { @@ -646,7 +602,6 @@ class VerificationAcknowledgeTest { @Test fun confirmVerifiedAllInverse() { - clearAllMocks() doCalls1() verify { @@ -661,7 +616,6 @@ class VerificationAcknowledgeTest { @Test fun confirmVerifiedAllExclude() { - clearAllMocks() excludeRecords(current = false) { mock.op(7) } diff --git a/test-modules/client-tests/src/jvmTest/java/io/mockk/core/ClassImplementingPackagePrivateInterfaceWithDefaultMethod.java b/test-modules/client-tests/src/jvmTest/java/io/mockk/core/ClassImplementingPackagePrivateInterfaceWithDefaultMethod.java new file mode 100644 index 000000000..1e0df39bf --- /dev/null +++ b/test-modules/client-tests/src/jvmTest/java/io/mockk/core/ClassImplementingPackagePrivateInterfaceWithDefaultMethod.java @@ -0,0 +1,11 @@ +package io.mockk.core; + + +interface InterfaceWithDefault { + default int foo() { + return 10; + } +} + +public class ClassImplementingPackagePrivateInterfaceWithDefaultMethod implements InterfaceWithDefault { +} diff --git a/test-modules/client-tests/src/jvmTest/kotlin/io/mockk/impl/DefaultMethodTest.kt b/test-modules/client-tests/src/jvmTest/kotlin/io/mockk/impl/DefaultMethodTest.kt new file mode 100644 index 000000000..00a6bdf41 --- /dev/null +++ b/test-modules/client-tests/src/jvmTest/kotlin/io/mockk/impl/DefaultMethodTest.kt @@ -0,0 +1,17 @@ +package io.mockk.impl + +import io.mockk.core.ClassImplementingPackagePrivateInterfaceWithDefaultMethod +import io.mockk.every +import io.mockk.mockk +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + + +class DefaultMethodTest { + @Test + fun `should mock ClassImplementingPackagePrivateInterfaceWithDefaultMethod class`() { + val obj = mockk() + every { obj.foo() } returns 12 + assertEquals(12, obj.foo()) + } +}