Skip to content

Commit

Permalink
Properly handle sealed classes with Kotlin 1.7 and JDK 17
Browse files Browse the repository at this point in the history
Attempt to fix mockk#832 by supporting sealed classes based on previous work done by @aSemy.
  • Loading branch information
stuebingerb committed Sep 6, 2022
1 parent 6c571a6 commit 80c7ea3
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 6 deletions.
Expand Up @@ -24,6 +24,10 @@ class ObjenesisInstantiator(
if (cls == Any::class.java) {
@Suppress("UNCHECKED_CAST")
return Any() as T
} else if (cls.isSealedSafe()) {
cls.getPermittedSubclassesSafe().firstNotNullOfOrNull { subCls ->
runCatching { instance(subCls) }.getOrNull()
} ?: error("could not find subclass for sealed class $cls")
} else if (!Modifier.isFinal(cls.modifiers)) {
try {
val instance = instantiateViaProxy(cls)
Expand All @@ -42,6 +46,27 @@ class ObjenesisInstantiator(
return instanceViaObjenesis(cls)
}

/**
* `boolean Class.isSealed()` is only available with JDK 17+. Used via reflection to support
* builds with previous Java versions as well. This should be refactored to use the actual
* method once the minimum Java version is 17.
*/
private fun Class<*>.isSealedSafe(): Boolean =
javaClass.methods.firstOrNull { it.name == "isSealed" }?.invoke(this) == true

/**
* `Class<?>[] Class.getPermittedSubclasses` is only available with JDK 17+. Used via reflection
* to support builds with previous Java versions as well. This should be refactored to use the
* actual method once the minimum Java version is 17.
*/
private fun Class<*>.getPermittedSubclassesSafe(): Array<Class<*>> =
javaClass.methods.firstOrNull { it.name == "getPermittedSubclasses" }
?.let {
@Suppress("UNCHECKED_CAST")
it.invoke(this) as Array<Class<*>>
}
?: emptyArray()

private fun <T> instantiateViaProxy(cls: Class<T>): T? {
val proxyCls = if (!Modifier.isAbstract(cls.modifiers)) {
log.trace("Skipping instantiation subsclassing $cls because class is not abstract.")
Expand Down
Expand Up @@ -2,15 +2,13 @@ package io.mockk.it

import io.mockk.every
import io.mockk.mockk
import kotlin.test.Ignore
import kotlin.test.Test
import kotlin.test.assertEquals


class SealedClassTest {

@Test
@Ignore("Fails on JDK17+ https://github.com/mockk/mockk/issues/832")
fun serviceReturnsSealedClassImpl() {
val factory = mockk<Factory> {
every { create() } returns Leaf(1)
Expand All @@ -22,7 +20,6 @@ class SealedClassTest {
}

@Test
@Ignore("Fails on JDK17+ https://github.com/mockk/mockk/issues/832")
fun serviceAnswersSealedClassImpl() {
val factory = mockk<Factory> {
every { create() } answers { Leaf(1) }
Expand Down
Expand Up @@ -3,14 +3,12 @@ package io.mockk.it
import io.mockk.every
import io.mockk.mockk
import kotlin.test.Test
import kotlin.test.Ignore
import kotlin.test.assertEquals


class SealedInterfaceTest {

@Test
@Ignore("Fails on JDK17+ https://github.com/mockk/mockk/issues/832")
fun serviceReturnsSealedClassImpl() {
val factory = mockk<Factory> {
every { create() } returns Leaf(1)
Expand All @@ -22,7 +20,6 @@ class SealedInterfaceTest {
}

@Test
@Ignore("Fails on JDK17+ https://github.com/mockk/mockk/issues/832")
fun serviceAnswersSealedClassImpl() {
val factory = mockk<Factory> {
every { create() } answers { Leaf(1) }
Expand Down

0 comments on commit 80c7ea3

Please sign in to comment.