All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
jvmMain.io.mockk.impl.instantiation.JvmMockFactoryHelper.kt Maven / Gradle / Ivy
package io.mockk.impl.instantiation
import io.mockk.*
import io.mockk.impl.InternalPlatform
import io.mockk.impl.stub.Stub
import io.mockk.core.ValueClassSupport.boxedClass
import io.mockk.core.ValueClassSupport.maybeUnboxValueForMethodReturn
import io.mockk.proxy.MockKInvocationHandler
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.util.concurrent.Callable
import kotlin.coroutines.Continuation
import kotlin.reflect.*
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.javaField
import kotlin.reflect.jvm.javaMethod
import kotlin.reflect.jvm.kotlinFunction
object JvmMockFactoryHelper {
fun mockHandler(stub: Stub) = object : MockKInvocationHandler {
override fun invocation(self: Any, method: Method?, originalCall: Callable<*>?, args: Array) =
stdFunctions(self, method!!, args) {
stub.handleInvocation(
self,
method.toDescription(),
{
handleOriginalCall(originalCall, method)
},
args,
findBackingField(self, method)
)
}
}
private fun findBackingField(clazz: KClass<*>, method: Method): KProperty1<*, *>? = runCatching {
/**
* `runCatching()` is used to avoid crashes when analyzing unsupported types as
* top-file extension function resulting into
* `Packages and file facades are not yet supported in Kotlin reflection.`
*
* Also, the functional types are unsupported, we skip them early.
*/
if (Function::class.java.isAssignableFrom(clazz.java)) {
return null
}
return clazz.memberProperties.firstOrNull {
it.getter.javaMethod == method ||
(it is KMutableProperty<*> && it.setter.javaMethod == method)
}
}.getOrNull()
private fun findBackingField(self: Any, method: Method): BackingFieldValueProvider {
return {
val property = findBackingField(self::class, method)
property?.javaField?.let { field ->
BackingFieldValue(
property.name,
{
InternalPlatformDsl.makeAccessible(field)
field.get(self)
},
{
InternalPlatformDsl.makeAccessible(field)
field.set(self, it)
}
)
}
}
}
private inline fun stdFunctions(
self: Any,
method: Method,
args: Array,
otherwise: () -> Any?
): Any? {
if (self is Class<*>) {
if (method.isHashCode()) {
return System.identityHashCode(self)
} else if (method.isEquals()) {
return self === args[0]
}
}
return otherwise()
}
private fun handleOriginalCall(originalMethod: Callable<*>?, method: Method): Any? {
if (originalMethod == null) {
throw MockKException("No way to call original method ${method.toDescription()}")
}
return try {
originalMethod.call()
} catch (ex: InvocationTargetException) {
throw ex.cause ?: throw ex
}
}
internal fun Method.toDescription(): MethodDescription {
val cached = cache[this]
if (cached != null) return cached
val kotlinFunc =
try {
// workaround for
// https://github.com/mockk/mockk/issues/18
// https://github.com/mockk/mockk/issues/22
kotlinFunction
} catch (ex: Throwable) {
null
}
val vararg = when {
kotlinFunc != null ->
kotlinFunc.parameters
.filter { it.kind != KParameter.Kind.INSTANCE }
.indexOfFirst { it.isVararg }
isVarArgs ->
parameterTypes.size - 1
else -> -1
}
val returnTypeIsUnit = when {
kotlinFunc != null ->
kotlinFunc.returnType.toString() == "kotlin.Unit"
else ->
returnType == Void.TYPE
}
val returnTypeIsNothing =
kotlinFunc?.returnType?.toString() == "kotlin.Nothing"
val isSuspend = when {
kotlinFunc != null ->
kotlinFunc.isSuspend
else -> parameterTypes.lastOrNull()?.let {
Continuation::class.java.isAssignableFrom(it)
} ?: false
}
val isFnCall = Function::class.java.isAssignableFrom(declaringClass)
val kotlinReturnType = kotlinFunc?.returnType
?: findBackingField(declaringClass.kotlin, this)?.returnType
val returnTypeNullable = kotlinReturnType?.isMarkedNullable ?: false
val returnTypeClass: KClass<*> = when (kotlinReturnType) {
is KType -> kotlinReturnType.classifier as? KClass<*> ?: returnType.kotlin
is KClass<*> -> kotlinReturnType
else -> returnType.kotlin
}
val returnType = if (!returnTypeNullable) returnTypeClass.boxedClass else returnTypeClass
val androidCompatibleReturnType = if (returnType.qualifiedName in androidUnsupportedTypes) {
[email protected]
} else {
returnType
}
val result = MethodDescription(
name,
androidCompatibleReturnType,
returnTypeNullable,
returnTypeIsUnit,
returnTypeIsNothing,
isSuspend,
isFnCall,
declaringClass.kotlin,
parameterTypes.map { it.kotlin },
vararg,
Modifier.isPrivate(modifiers) ||
Modifier.isProtected(modifiers)
)
cache[this] = result
return result
}
/**
* These types have to be resolved to kotlin.Array on Android to work properly.
*/
private val androidUnsupportedTypes = setOf(
"kotlin.BooleanArray",
"kotlin.ByteArray",
"kotlin.ShortArray",
"kotlin.CharArray",
"kotlin.IntArray",
"kotlin.LongArray",
"kotlin.FloatArray",
"kotlin.DoubleArray"
)
fun Method.isHashCode() = name == "hashCode" && parameterTypes.isEmpty()
fun Method.isEquals() = name == "equals" && parameterTypes.size == 1 && parameterTypes[0] === Object::class.java
val cache = InternalPlatform.weakMap()
}