Skip to content

Commit

Permalink
mockk#510 | Fix problem with 2 matchers with the same type.
Browse files Browse the repository at this point in the history
  • Loading branch information
kukulam-allegro committed Oct 15, 2020
1 parent 54e29c1 commit d4f75a7
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 13 deletions.
Expand Up @@ -19,7 +19,7 @@ class ChainedCallDetector(safeToString: SafeToString) {
fun detect(
callRounds: List<CallRound>,
callN: Int,
matcherMap: HashMap<List<Any>, Matcher<*>>
matcherList: SignatureMatchersList
) {
val callInAllRounds = callRounds.map { it.calls[callN] }
val zeroCall = callInAllRounds[0]
Expand Down Expand Up @@ -51,7 +51,7 @@ class ChainedCallDetector(safeToString: SafeToString) {

log.trace { "Signature for $nArgument argument of ${zeroCall.method.toStr()}: $signature" }

val matcherBySignature = matcherMap.remove(signature)
val matcherBySignature = matcherList.remove(signature)

return buildMatcher(
nArgument == 0,
Expand Down Expand Up @@ -84,7 +84,7 @@ class ChainedCallDetector(safeToString: SafeToString) {

log.trace { "Signature for $nArgument/$nVarArg argument of ${zeroCall.method.toStr()}: $signature" }

val matcherBySignature = matcherMap.remove(signature)
val matcherBySignature = matcherList.remove(signature)
varArgMatchers.add(
buildMatcher(
nArgument == 0 && nVarArg == 0,
Expand Down
Expand Up @@ -45,7 +45,7 @@ class SignatureMatcherDetector(
}
}

val matcherMap = hashMapOf<List<Any>, Matcher<*>>()
val matcherList = SignatureMatchersList()
val allCompositeMatchers = mutableListOf<List<CompositeMatcher<*>>>()

fun gatherMatchers() {
Expand All @@ -59,10 +59,10 @@ class SignatureMatcherDetector(
})
}

matcherMap[signature] = matcher
matcherList.add(signature, matcher)
}

log.trace { "Matcher map: $matcherMap" }
log.trace { "Matcher list: $matcherList" }
}

@Suppress("UNCHECKED_CAST")
Expand All @@ -77,7 +77,7 @@ class SignatureMatcherDetector(

log.trace { "Signature for $nOp operand of $matcher composite matcher: $signature" }

matcherMap.remove(signature)
matcherList.remove(signature)
?: ChainedCallDetector.eqOrNullMatcher(matcher.operandValues[nOp])
} as List<Matcher<Any?>>?
}
Expand All @@ -90,13 +90,13 @@ class SignatureMatcherDetector(

repeat(nCalls) { callN ->
val detector = chainedCallDetectorFactory()
detector.detect(callRounds, callN, matcherMap)
detector.detect(callRounds, callN, matcherList)
calls.add(detector.call)
}

processCompositeMatchers()
if (matcherMap.isNotEmpty()) {
throw MockKException("Failed matching mocking signature for\n${callRounds[0].calls.joinToString("\n")}\nleft matchers: ${matcherMap.values}")
if (matcherList.isNotEmpty()) {
throw MockKException("Failed matching mocking signature for\n${callRounds[0].calls.joinToString("\n")}\nleft matchers: $matcherList")
}
}
}
Expand Down
@@ -0,0 +1,24 @@
package io.mockk.impl.recording

import io.mockk.Matcher

class SignatureMatchersList {
private var matchers = mutableListOf<SignatureWithMatcher>()

fun add(signature: List<Any>, matcher: Matcher<*>) {
matchers.add(SignatureWithMatcher(signature, matcher))
}

fun remove(signature: List<Any?>): Matcher<*>? {
val index = matchers.indexOfFirst { it.signature == signature }
return if (index > -1) matchers.removeAt(index).matcher else null
}

fun isNotEmpty() = matchers.isNotEmpty()

override fun toString(): String {
return matchers.map { it.matcher }.toString()
}

private data class SignatureWithMatcher(val signature: List<Any>, val matcher: Matcher<*>)
}
41 changes: 41 additions & 0 deletions mockk/common/src/test/kotlin/io/mockk/gh/Issue510Test.kt
@@ -0,0 +1,41 @@
package io.mockk.gh

import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlin.test.Test

class Issue510Test {

class ShopService {

fun buyProducts(products: List<Product>) {
println("You bought...")
}

fun addProductAndOrders(products: List<Product>, orders: List<Order>) {
println("Add and orders...")
}
}

data class Product(val name: String, val price: Int)
data class Order(val name: String)

@Test
fun `should match with two arguments of type list`() {
// given
val shopService = mockk<ShopService>()
val products = listOf(Product("raspberry", 2), Product("banana", 1))
val orders = listOf(Order("raspberry"), Order("banana"))

every {
shopService.addProductAndOrders(products = any(), orders = any())
} returns Unit

// when
shopService.addProductAndOrders(products, orders)

// then
verify { shopService.addProductAndOrders(products, orders) }
}
}
Expand Up @@ -30,16 +30,16 @@ class ChainedCallDetectorTest {
every { call1.method.varArgsArg } returns -1
every { call2.method.varArgsArg } returns -1

detector.detect(listOf(callRound1, callRound2), 0, hashMapOf())
detector.detect(listOf(callRound1, callRound2), 0, SignatureMatchersList())

assertEquals("abc", detector.call.matcher.method.name)
}

@Test
fun givenTwoCallsRoundsWithOneCallOneArgWhenDetectCallsHappenThenOneCallWithArgIsReturned() {
val matcherMap = hashMapOf<List<Any>, Matcher<*>>()
val matcherMap = SignatureMatchersList()

matcherMap[listOf(signedMatcher1.signature, signedMatcher2.signature)] = signedMatcher1.matcher
matcherMap.add(listOf(signedMatcher1.signature, signedMatcher2.signature), signedMatcher1.matcher)

every { callRound1.calls } returns listOf(call1)
every { callRound2.calls } returns listOf(call2)
Expand Down
@@ -0,0 +1,94 @@
package io.mockk.impl.recording

import io.mockk.AllAnyMatcher
import io.mockk.mockk
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue

class SignatureMatchersListTest {

@Test
fun shouldAddMatcherForSignature() {
// given
val map = SignatureMatchersList()
val signedMatcher = mockk<SignedMatcher>(relaxed = true)
val signature = listOf(signedMatcher.signature)
val matcher = signedMatcher.matcher

// when
map.add(signature, matcher)
map.add(signature, matcher)

// then
assertTrue { map.isNotEmpty() }
}

@Test
fun shouldNotContainAnyMatchers() {
// given
val map = SignatureMatchersList()

// when && then
assertFalse { map.isNotEmpty() }
}

@Test
fun shouldNotRemoveFromEmptyList() {
// given
val map = SignatureMatchersList()
val signedMatcher = mockk<SignedMatcher>(relaxed = true)
val signature = listOf(signedMatcher.signature)

// when
val matcher = map.remove(signature)

// then
assertFalse { map.isNotEmpty() }
assertNull(matcher)
}

@Test
fun shouldRemoveFromList() {
// given
val map = SignatureMatchersList()
val signedMatcher1 = mockk<SignedMatcher>(relaxed = true)
val signedMatcher2 = mockk<SignedMatcher>(relaxed = true)
val signature1 = listOf(signedMatcher1.signature)
val signature2 = listOf(signedMatcher2.signature)
val matcher1 = signedMatcher1.matcher
val matcher2 = signedMatcher2.matcher

// when
map.add(signature1, matcher1)
map.add(signature2, matcher2)

// and
val matcher = map.remove(signature1)

// then
assertTrue { map.isNotEmpty() }
assertEquals(matcher, matcher1)
}

@Test
fun shouldPrintListCorrectly() {
// given
val list = SignatureMatchersList()
val signedMatcher = mockk<SignedMatcher>(relaxed = true)
val signature = listOf(signedMatcher.signature)
val matcher = AllAnyMatcher<Any>()
val expectedMatchersListAsString = "[$matcher]"

// when
list.add(signature, matcher)

// and
val matchersAsString = list.toString()

// then
assertEquals(matchersAsString, expectedMatchersListAsString)
}
}

0 comments on commit d4f75a7

Please sign in to comment.