commonMain.io.mockk.Matchers.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.InternalPlatformDsl.toArray
import io.mockk.InternalPlatformDsl.toStr
import kotlin.math.min
import kotlin.reflect.KClass
/**
* Matcher that checks equality. By reference and by value (equals method)
*/
data class EqMatcher(private val valueArg: T, val ref: Boolean = false, val inverse: Boolean = false) :
Matcher {
val value = InternalPlatformDsl.unboxChar(valueArg)
override fun match(arg: T?): Boolean {
val result = if (ref) {
arg === value
} else {
if (arg == null) {
false
} else {
val unboxedArg = InternalPlatformDsl.unboxChar(arg)
InternalPlatformDsl.deepEquals(unboxedArg, value)
}
}
return if (inverse) !result else result
}
override fun substitute(map: Map) =
copy(valueArg = valueArg.internalSubstitute(map))
override fun toString(): String {
return if (ref)
"${if (inverse) "refNonEq" else "refEq"}(${value.toStr()})"
else
"${if (inverse) "nonEq" else "eq"}(${value.toStr()})"
}
}
/**
* Matcher that always returns one same value.
*/
data class ConstantMatcher(val constValue: Boolean) : Matcher {
override fun match(arg: T?): Boolean = constValue
override fun toString(): String = if (constValue) "any()" else "none()"
}
/**
* Delegates matching to lambda function
*/
data class FunctionMatcher(
val matchingFunc: (T) -> Boolean,
override val argumentType: KClass<*>
) : Matcher, TypedMatcher, EquivalentMatcher {
override fun equivalent(): Matcher = ConstantMatcher(true)
override fun match(arg: T?): Boolean {
return if (arg == null) {
false
} else {
try {
matchingFunc(arg)
} catch (a: AssertionError) {
false
}
}
}
override fun toString(): String = "matcher<${argumentType.simpleName}>()"
}
data class FunctionWithNullableArgMatcher(
val matchingFunc: (T?) -> Boolean,
override val argumentType: KClass<*>
) : Matcher, TypedMatcher, EquivalentMatcher {
override fun equivalent(): Matcher = ConstantMatcher(true)
override fun match(arg: T?): Boolean = matchingFunc(arg)
override fun checkType(arg: Any?): Boolean {
if (arg == null) {
return true
}
return super.checkType(arg)
}
override fun toString(): String = "matcher<${argumentType.simpleName}>()"
}
/**
* Matcher capturing all results to the list.
*/
data class CaptureMatcher(
val captureList: MutableList,
override val argumentType: KClass<*>
) : Matcher, CapturingMatcher, TypedMatcher, EquivalentMatcher {
override fun equivalent(): Matcher = ConstantMatcher(true)
@Suppress("UNCHECKED_CAST")
override fun capture(arg: Any?) {
captureList.add(arg as T)
}
override fun match(arg: T?): Boolean = true
override fun toString(): String = "capture<${argumentType.simpleName}>()"
}
/**
* Matcher capturing all results to the list. Allows nulls
*/
data class CaptureNullableMatcher(
val captureList: MutableList,
override val argumentType: KClass<*>
) : Matcher, CapturingMatcher, TypedMatcher, EquivalentMatcher {
override fun equivalent(): Matcher = ConstantMatcher(true)
@Suppress("UNCHECKED_CAST")
override fun capture(arg: Any?) {
captureList.add(arg as T?)
}
override fun match(arg: T?): Boolean = true
override fun checkType(arg: Any?): Boolean {
if (arg == null) {
return true
}
return super.checkType(arg)
}
override fun toString(): String = "capture<${argumentType.simpleName}?>()"
}
/**
* Matcher capturing one last NON nullable value to the [CapturingSlot]
*/
data class CapturingSlotMatcher(
val captureSlot: CapturingSlot,
override val argumentType: KClass<*>,
) : Matcher, CapturingMatcher, TypedMatcher, EquivalentMatcher {
override fun equivalent(): Matcher = ConstantMatcher(true)
override fun capture(arg: Any?) {
// does not capture null values
if (arg != null) {
captureSlot.captured = InternalPlatformDsl.boxCast(argumentType, arg)
}
}
override fun match(arg: T?): Boolean = true
override fun toString(): String = "slotCapture<${argumentType.simpleName}>()"
}
/**
* Matcher capturing one last nullable value to the [CapturingSlot]
*/
data class CapturingNullableSlotMatcher(
val captureSlot: CapturingSlot,
override val argumentType: KClass<*>,
) : Matcher, CapturingMatcher, TypedMatcher, EquivalentMatcher {
override fun equivalent(): Matcher = ConstantMatcher(true)
override fun capture(arg: Any?) {
if (arg == null) {
captureSlot.captured = null
} else {
captureSlot.captured = InternalPlatformDsl.boxCast(argumentType, arg)
}
}
override fun match(arg: T?): Boolean = true
override fun checkType(arg: Any?): Boolean {
if (arg == null) {
return true
}
return super.checkType(arg)
}
override fun toString(): String = "slotCapture<${argumentType.simpleName}>()"
}
/**
* Matcher comparing values
*/
data class ComparingMatcher>(
val value: T,
val cmpFunc: Int,
override val argumentType: KClass
) : Matcher, TypedMatcher {
override fun match(arg: T?): Boolean {
if (arg == null) return false
val n = arg.compareTo(value)
return when (cmpFunc) {
2 -> n >= 0
1 -> n > 0
0 -> n == 0
-1 -> n < 0
-2 -> n <= 0
else -> throw MockKException("Bad comparison function")
}
}
override fun substitute(map: Map) =
copy(value = value.internalSubstitute(map))
override fun toString(): String =
when (cmpFunc) {
-2 -> "lessAndEquals($value)"
-1 -> "less($value)"
0 -> "cmpEq($value)"
1 -> "more($value)"
2 -> "moreAndEquals($value)"
else -> throw MockKException("Bad comparison function")
}
}
/**
* Boolean logic "AND" and "OR" matcher composed of two other matchers
*/
data class AndOrMatcher(
val and: Boolean,
val first: T,
val second: T
) : Matcher, CompositeMatcher, CapturingMatcher {
override val operandValues: List
get() = listOf(first, second)
override var subMatchers: List>? = null
override fun match(arg: T?): Boolean =
if (and)
subMatchers!![0].match(arg) && subMatchers!![1].match(arg)
else
subMatchers!![0].match(arg) || subMatchers!![1].match(arg)
override fun substitute(map: Map): Matcher {
val matcher = copy(
first = first.internalSubstitute(map),
second = second.internalSubstitute(map)
)
val sm = subMatchers
if (sm != null) {
matcher.subMatchers = sm.map { it.substitute(map) }
}
return matcher
}
override fun capture(arg: Any?) {
captureSubMatchers(arg)
}
override fun toString(): String {
val sm = subMatchers
val op = if (and) "and" else "or"
return if (sm != null)
"$op(${sm[0]}, ${sm[1]})"
else
"$op()"
}
}
/**
* Boolean logic "NOT" matcher composed of one matcher
*/
data class NotMatcher(val value: T) : Matcher, CompositeMatcher, CapturingMatcher {
override val operandValues: List
get() = listOf(value)
override var subMatchers: List>? = null
override fun match(arg: T?): Boolean =
!subMatchers!![0].match(arg)
override fun substitute(map: Map): Matcher {
val matcher = copy(value = value.internalSubstitute(map))
val sm = subMatchers
if (sm != null) {
matcher.subMatchers = sm.map { it.substitute(map) }
}
return matcher
}
override fun capture(arg: Any?) {
captureSubMatchers(arg)
}
override fun toString(): String {
val sm = subMatchers
return if (sm != null)
"not(${sm[0]})"
else
"not()"
}
}
/**
* Checks if argument is null or non-null
*/
data class NullCheckMatcher(val inverse: Boolean = false) : Matcher {
override fun match(arg: T?): Boolean = if (inverse) arg != null else arg == null
override fun toString(): String {
return if (inverse)
"notNull()"
else
"null()"
}
}
/**
* Checks matcher data type
*/
data class OfTypeMatcher(val cls: KClass<*>) : Matcher {
override fun match(arg: T?): Boolean = cls.isInstance(arg)
override fun toString() = "ofType(${cls.simpleName})"
}
/**
* Matcher to replace all unspecified argument matchers to any()
* Handled by logic in a special way
*/
class AllAnyMatcher : Matcher {
override fun match(arg: T?): Boolean = true
override fun toString() = "allAny()"
}
/**
* Invokes lambda
*/
class InvokeMatcher(val block: (T) -> Unit) : Matcher, EquivalentMatcher {
override fun equivalent(): Matcher = ConstantMatcher(true)
override fun match(arg: T?): Boolean {
if (arg == null) {
return true
}
block(arg)
return true
}
override fun toString(): String = "coInvoke()"
}
/**
* Matcher that can match arrays via provided matchers for each element.
*/
data class ArrayMatcher(private val matchers: List>) : Matcher, CapturingMatcher {
override fun capture(arg: Any?) {
if (arg == null) {
return
}
val arr = arg.toArray()
repeat(min(arr.size, matchers.size)) { i ->
val matcher = matchers[i]
if (matcher is CapturingMatcher) {
matcher.capture(arr[i])
}
}
}
override fun match(arg: T?): Boolean {
if (arg == null) {
return false
}
val arr = arg.toArray()
if (arr.size != matchers.size) {
return false
}
repeat(arr.size) { i ->
if (!matchers[i].match(arr[i])) {
return false
}
}
return true
}
override fun substitute(map: Map) =
copy(matchers = matchers.map { it.substitute(map) })
override fun toString(): String {
return matchers.joinToString(prefix = "[", postfix = "]")
}
}
data class VarargMatcher(
private val all: Boolean,
private val matcher: MockKMatcherScope.MockKVarargScope.(T?) -> Boolean,
private val prefix: List> = listOf(),
private val postfix: List> = listOf()
) : Matcher, CapturingMatcher {
@Suppress("UNCHECKED_CAST")
override fun match(arg: Any?): Boolean {
if (arg == null) {
return false
}
val arr = arg.toArray()
if (arr.size < prefix.size + postfix.size) {
return false
}
repeat(prefix.size) {
val el = arr[it] as T?
if (!prefix[it].match(el)) {
return false
}
}
repeat(postfix.size) {
val el = arr[arr.size - postfix.size + it] as T?
if (!postfix[it].match(el)) {
return false
}
}
val centralPartSize = arr.size - postfix.size - prefix.size
if (all) {
repeat(centralPartSize) {
val position = it + prefix.size
val el = arr[position] as T?
val scope = MockKMatcherScope.MockKVarargScope(position, arr.size)
if (!scope.matcher(el)) {
return false
}
}
return true
} else {
repeat(centralPartSize) {
val position = it + prefix.size
val el = arr[position] as T?
val scope = MockKMatcherScope.MockKVarargScope(position, arr.size)
if (scope.matcher(el)) {
return true
}
}
return false
}
}
@Suppress("UNCHECKED_CAST")
override fun capture(arg: Any?) {
if (arg == null) {
return
}
val arr = arg.toArray()
if (arr.size < prefix.size + postfix.size) {
return
}
repeat(prefix.size) {
val el = arr[it] as T?
val elMatcher = prefix[it]
if (elMatcher is CapturingMatcher) {
elMatcher.capture(el)
}
}
repeat(postfix.size) {
val el = arr[arr.size - postfix.size + it] as T?
val elMatcher = postfix[it]
if (elMatcher is CapturingMatcher) {
elMatcher.capture(el)
}
}
}
override fun toString() = "VarargMatcher(all=$all, prefix=$prefix, postfix=$postfix, centralPart=lambda)"
}
fun CompositeMatcher<*>.captureSubMatchers(arg: Any?) {
subMatchers?.let {
it.filterIsInstance()
.forEach { matcher -> matcher.capture(arg) }
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy