io.mockk.InternalPlatformDsl.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mockk-dsl-jvm Show documentation
Show all versions of mockk-dsl-jvm Show documentation
Java MockK DSL providing API for MockK implementation
package io.mockk
import io.mockk.ValueClassSupportDsl.boxedClass
import java.lang.reflect.AccessibleObject
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import java.util.concurrent.atomic.AtomicLong
import kotlin.coroutines.Continuation
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KMutableProperty1
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)
actual fun runCoroutine(block: suspend () -> T): T {
return runBlocking {
block()
}
}
actual fun Any?.toStr() =
try {
when (this) {
null -> "null"
is BooleanArray -> this.contentToString()
is ByteArray -> this.contentToString()
is CharArray -> this.contentToString()
is ShortArray -> this.contentToString()
is IntArray -> this.contentToString()
is LongArray -> this.contentToString()
is FloatArray -> this.contentToString()
is DoubleArray -> this.contentToString()
is Array<*> -> this.contentDeepToString()
Void.TYPE.kotlin -> "void"
kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED -> "SUSPEND_MARKER"
is Continuation<*> -> "continuation {}"
is KClass<*> -> this.simpleName ?: ""
is Method -> name + "(" + parameterTypes.joinToString { it.simpleName } + ")"
is Function<*> -> "lambda {}"
else -> toString()
}
} catch (thr: Throwable) {
""
}
actual fun deepEquals(obj1: Any?, obj2: Any?): Boolean {
return if (obj1 === obj2) {
true
} else if (obj1 == null || obj2 == null) {
obj1 === obj2
} else if (obj1.javaClass.isArray && obj2.javaClass.isArray) {
arrayDeepEquals(obj1, obj2)
} else {
obj1 == obj2
}
}
private fun arrayDeepEquals(obj1: Any, obj2: Any): Boolean {
return when (obj1) {
is BooleanArray -> obj1 contentEquals obj2 as BooleanArray
is ByteArray -> obj1 contentEquals obj2 as ByteArray
is CharArray -> obj1 contentEquals obj2 as CharArray
is ShortArray -> obj1 contentEquals obj2 as ShortArray
is IntArray -> obj1 contentEquals obj2 as IntArray
is LongArray -> obj1 contentEquals obj2 as LongArray
is FloatArray -> obj1 contentEquals obj2 as FloatArray
is DoubleArray -> obj1 contentEquals obj2 as DoubleArray
else -> return obj1 as Array<*> contentDeepEquals obj2 as Array<*>
}
}
actual fun unboxChar(value: Any): Any = value
actual fun Any.toArray(): Array<*> =
when (this) {
is BooleanArray -> this.toTypedArray()
is ByteArray -> this.toTypedArray()
is CharArray -> this.toTypedArray()
is ShortArray -> this.toTypedArray()
is IntArray -> this.toTypedArray()
is LongArray -> this.toTypedArray()
is FloatArray -> this.toTypedArray()
is DoubleArray -> this.toTypedArray()
else -> this as Array<*>
}
actual fun classForName(name: String): Any = Class.forName(name).kotlin
actual fun dynamicCall(
self: Any,
methodName: String,
args: Array,
anyContinuationGen: () -> Continuation<*>
): Any? {
val params = arrayOf(self, *args)
val func = self::class.allAncestorFunctions().firstOrNull {
if (it.name != methodName) {
return@firstOrNull false
}
if (it.parameters.size != params.size) {
return@firstOrNull false
}
for ((idx, param) in it.parameters.withIndex()) {
val matches = when (val classifier = param.type.classifier) {
is KClass<*> -> classifier.isInstance(params[idx])
is KTypeParameter -> classifier.upperBounds.anyIsInstance(params[idx])
else -> false
}
if (!matches) {
return@firstOrNull false
}
}
return@firstOrNull true
}
?: throw MockKException("can't find function $methodName(${args.joinToString(", ")}) of class ${self.javaClass.name} for dynamic call.\n" +
"If you were trying to verify a private function, make sure to provide type information to exactly match the functions signature.")
func.javaMethod?.let { makeAccessible(it) }
return if (func.isSuspend) {
func.call(*params, anyContinuationGen())
} else {
func.call(*params)
}
}
private fun KClass<*>.allAncestorFunctions(): Sequence> {
return (sequenceOf(this) + this.allSuperclasses.asSequence())
.flatMap { it.functions }
}
private fun List.anyIsInstance(value: Any?): Boolean {
return any { bound ->
val classifier = bound.classifier
if (classifier is KClass<*>) {
classifier.isInstance(value)
} else {
false
}
}
}
actual fun dynamicGet(self: Any, name: String): Any? {
val property = self::class.memberProperties
.filterIsInstance>()
.firstOrNull {
it.name == name
} ?: throw MockKException("can't find property $name for dynamic property get")
property.isAccessible = true
return property.get(self)
}
actual fun dynamicSet(self: Any, name: String, value: Any?) {
val property = self::class.memberProperties
.filterIsInstance>()
.firstOrNull {
it.name == name
} ?: throw MockKException("can't find property $name for dynamic property set")
property.isAccessible = true
return property.set(self, value)
}
actual fun dynamicSetField(self: Any, name: String, value: Any?) {
val field = self.javaClass
.declaredFields.firstOrNull { it.name == name }
?: return
makeAccessible(field)
field.set(self, value)
}
fun makeAccessible(obj: AccessibleObject) {
try {
obj.isAccessible = true
} catch (ex: Throwable) {
// skip
}
}
actual fun threadLocal(initializer: () -> T): InternalRef {
class TL : ThreadLocal(), InternalRef {
override fun initialValue(): T {
return initializer()
}
override val value: T
get() = get()
}
return TL()
}
actual fun counter() = object : InternalCounter {
val atomicValue = AtomicLong()
override val value: Long
get() = atomicValue.get()
override fun increment() = atomicValue.getAndIncrement()
}
actual fun coroutineCall(lambda: suspend () -> T): CoroutineCall = JvmCoroutineCall(lambda)
actual fun unboxClass(cls: KClass<*>): KClass<*> = cls.boxedClass
@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 {
companion object {
val callMethod: Method = JvmCoroutineCall::class.java.getMethod("callCoroutine", Continuation::class.java)
}
suspend fun callCoroutine() = lambda()
override fun callWithContinuation(continuation: Continuation<*>): T {
return try {
@Suppress("UNCHECKED_CAST")
callMethod.invoke(this, continuation) as T
} catch (ex: InvocationTargetException) {
throw ex.targetException
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy