Skip to content

Commit

Permalink
Fix #123
Browse files Browse the repository at this point in the history
  • Loading branch information
oleksiyp committed Aug 29, 2018
1 parent 00bc796 commit 6aa4827
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 22 deletions.
6 changes: 6 additions & 0 deletions common/src/main/kotlin/io/mockk/impl/stub/ConstructorStub.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ class ConstructorStub(
it.substitute(revertRepresentation)
}

override fun allRecordedCalls(method: MethodDescription) =
stub.allRecordedCalls(method)
.map {
it.substitute(revertRepresentation)
}

override fun clear(answers: Boolean, calls: Boolean, childMocks: Boolean) =
stub.clear(answers, calls, childMocks)

Expand Down
15 changes: 15 additions & 0 deletions common/src/main/kotlin/io/mockk/impl/stub/MockKStub.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ open class MockKStub(
private val answers = InternalPlatform.synchronizedMutableList<InvocationAnswer>()
private val childs = InternalPlatform.synchronizedMutableMap<InvocationMatcher, Any>()
private val recordedCalls = InternalPlatform.synchronizedMutableList<Invocation>()
private val recordedCallsByMethod =
InternalPlatform.synchronizedMutableMap<MethodDescription, MutableList<Invocation>>()

lateinit var hashCodeStr: String

Expand Down Expand Up @@ -109,6 +111,12 @@ open class MockKStub(

if (record) {
recordedCalls.add(invocation)

synchronized(recordedCallsByMethod) {
recordedCallsByMethod.getOrPut(invocation.method, { mutableListOf() })
.add(invocation)
}

gatewayAccess.stubRepository.notifyCallRecorded(this)
}
}
Expand All @@ -119,6 +127,12 @@ open class MockKStub(
}
}

override fun allRecordedCalls(method: MethodDescription): List<Invocation> {
synchronized(recordedCallsByMethod) {
return recordedCallsByMethod[method]?.toList() ?: listOf()
}
}

override fun toStr() = "${type.simpleName}($name)"

override fun childMockK(matcher: InvocationMatcher, childType: KClass<*>): Any {
Expand Down Expand Up @@ -215,6 +229,7 @@ open class MockKStub(
}
if (calls) {
this.recordedCalls.clear()
this.recordedCallsByMethod.clear()
}
if (childMocks) {
this.childs.clear()
Expand Down
2 changes: 2 additions & 0 deletions common/src/main/kotlin/io/mockk/impl/stub/Stub.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ interface Stub : Disposable {

fun allRecordedCalls(): List<Invocation>

fun allRecordedCalls(method: MethodDescription): List<Invocation>

fun clear(answers: Boolean, calls: Boolean, childMocks: Boolean)

fun handleInvocation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,13 @@ open class UnorderedCallVerifier(
}

private fun matchCall(recordedCall: RecordedCall, min: Int, max: Int, callIdxMsg: String): VerificationResult {
val stub = stubRepo.stubFor(recordedCall.matcher.self)
val matcher = recordedCall.matcher
val stub = stubRepo.stubFor(matcher.self)
val allCallsForMock = stub.allRecordedCalls()
val allCallsForMockMethod = allCallsForMock.filter {
recordedCall.matcher.method == it.method
}

val allCallsForMockMethod = stub.allRecordedCalls(matcher.method)

val result = if (min == 0 && max == 0) {
val n = allCallsForMockMethod.filter { recordedCall.matcher.match(it) }.count()
if (n == 0) {
if (!allCallsForMockMethod.any(matcher::match)) {
VerificationResult(true)
} else {
VerificationResult(
Expand Down Expand Up @@ -74,8 +71,8 @@ open class UnorderedCallVerifier(
}
}
1 -> {
val onlyCall = allCallsForMockMethod.get(0)
if (recordedCall.matcher.match(onlyCall)) {
val onlyCall = allCallsForMockMethod[0]
if (matcher.match(onlyCall)) {
if (1 in min..max) {
VerificationResult(true)
} else {
Expand All @@ -90,15 +87,15 @@ open class UnorderedCallVerifier(
}
} else {
VerificationResult(false, safeToString.exec {
"$callIdxMsg. Only one matching call to ${stub.toStr()}/${recordedCall.matcher.method.toStr()} happened, but arguments are not matching:\n" +
describeArgumentDifference(recordedCall.matcher, onlyCall) +
"$callIdxMsg. Only one matching call to ${stub.toStr()}/${matcher.method.toStr()} happened, but arguments are not matching:\n" +
describeArgumentDifference(matcher, onlyCall) +
"\nStack trace:\n" +
stackTrace(0, allCallsForMock.first().callStack())
})
}
}
else -> {
val n = allCallsForMockMethod.filter { recordedCall.matcher.match(it) }.count()
val n = allCallsForMockMethod.filter(matcher::match).count()
if (n in min..max) {
VerificationResult(true)
} else {
Expand Down Expand Up @@ -128,7 +125,7 @@ open class UnorderedCallVerifier(

captureBlocks.add({
for (call in allCallsForMockMethod) {
recordedCall.matcher.captureAnswer(call)
matcher.captureAnswer(call)
}
})

Expand Down
13 changes: 4 additions & 9 deletions mockk/src/main/kotlin/io/mockk/impl/JvmMockKGateway.kt
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,14 @@ class JvmMockKGateway : MockKGateway {
)


val unorderedVerifier = UnorderedCallVerifier(stubRepo, safeToString)
val allVerifier = AllCallsCallVerifier(stubRepo, safeToString)
val orderedVerifier = OrderedCallVerifier(stubRepo, safeToString)
val sequenceVerifier = SequenceCallVerifier(stubRepo, safeToString)

override fun verifier(params: VerificationParameters): CallVerifier {
val ordering = params.ordering

val verifier = when (ordering) {
Ordering.UNORDERED -> unorderedVerifier
Ordering.ALL -> allVerifier
Ordering.ORDERED -> orderedVerifier
Ordering.SEQUENCE -> sequenceVerifier
Ordering.UNORDERED -> UnorderedCallVerifier(stubRepo, safeToString)
Ordering.ALL -> AllCallsCallVerifier(stubRepo, safeToString)
Ordering.ORDERED -> OrderedCallVerifier(stubRepo, safeToString)
Ordering.SEQUENCE -> SequenceCallVerifier(stubRepo, safeToString)
}

return if (params.timeout > 0) {
Expand Down
88 changes: 88 additions & 0 deletions mockk/src/test/kotlin/io/mockk/gh/Issue123Test.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package io.mockk.gh

import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.junit5.MockKExtension
import io.mockk.verify
import org.junit.jupiter.api.extension.ExtendWith
import org.slf4j.LoggerFactory
import java.util.*
import java.util.Collections.synchronizedList
import kotlin.test.Test
import kotlin.test.assertTrue

class Issue123Test {
class MockCls {
fun op(a: Int, b: Int) = a + b
}

@MockK
lateinit var mock1: MockCls
@MockK
lateinit var mock2: MockCls
@MockK
lateinit var mock3: MockCls
@MockK
lateinit var mock4: MockCls
@MockK
lateinit var mock5: MockCls
@MockK
lateinit var mock6: MockCls

init {
MockKAnnotations.init(this)
}

@Test
fun test() {
every { mock1.op(any(), any()) } answers { firstArg<Int>() - secondArg<Int>() }
every { mock2.op(any(), any()) } answers { firstArg<Int>() * secondArg<Int>() }
every { mock3.op(any(), any()) } answers { firstArg<Int>() / secondArg<Int>() }
every { mock4.op(any(), any()) } answers { firstArg<Int>() xor secondArg() }
every { mock5.op(any(), any()) } answers { firstArg<Int>() and secondArg() }
every { mock6.op(any(), any()) } answers { firstArg<Int>() or secondArg() }

val rnd = Random()
val threads = mutableListOf<Thread>()
val exceptions = synchronizedList(mutableListOf<Exception>())

repeat(30) { n ->
Thread {
val mocks = listOf(mock1, mock2, mock3, mock4, mock5, mock6)
try {
data class Op(val mock: MockCls, val a: Int, val b: Int)

val ops = mutableListOf<Op>()
repeat(500) {
val mock = mocks[rnd.nextInt(mocks.size)]
val a = rnd.nextInt(100) + 5
val b = rnd.nextInt(100) + 5
mock.op(a, b)
ops.add(Op(mock, a, b))
}
log.info("Done operations for thread {}", n)

repeat(500) {
val op = ops[rnd.nextInt(ops.size)]
verify { op.mock.op(op.a, op.b) }
}
log.info("Done verification block for thread {}", n)
} catch (ex: Exception) {
log.warn("Exception for thread {}", n, ex)
exceptions.add(ex)
}
}
.apply { start() }
.apply { threads.add(this) }
}

threads.forEach { it.join() }

assertTrue(exceptions.isEmpty())
}

companion object {
val log = LoggerFactory.getLogger(Issue123Test::class.java)
}
}

0 comments on commit 6aa4827

Please sign in to comment.