All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.mockk.impl.recording.PermanentMocker.kt Maven / Gradle / Ivy

There is a newer version: 1.13.12
Show newest version
package io.mockk.impl.recording

import io.mockk.EqMatcher
import io.mockk.EquivalentMatcher
import io.mockk.RecordedCall
import io.mockk.impl.InternalPlatform
import io.mockk.impl.log.Logger
import io.mockk.impl.log.SafeToString
import io.mockk.impl.stub.StubRepository

class PermanentMocker(
    val stubRepo: StubRepository,
    val safeToString: SafeToString
) {

    val log = safeToString(Logger())

    val permanentMocks = InternalPlatform.identityMap()
    val callRef = InternalPlatform.weakMap()

    fun mock(calls: List): List {
        val result = mutableListOf()
        for (call in calls) {
            val permanentCall = permamentize(call)
            result.add(permanentCall)
        }

        val callTree = safeToString.exec { describeCallTree(result) }
        if (callTree.size == 1) {
            log.trace { "Mocked permanently: " + callTree[0] }
        } else {
            log.trace { "Mocked permanently:\n" + callTree.joinToString(", ") }
        }

        return result
    }

    private fun permamentize(call: RecordedCall): RecordedCall {
        val newCall = makeCallPermanent(call)

        val retValue = call.retValue
        if (call.isRetValueMock && retValue != null) {
            val equivalentCall = makeEquivalent(newCall)

            log.trace { "Child search key: ${equivalentCall.matcher}" }

            val childMock = stubRepo.stubFor(newCall.matcher.self)
                .childMockK(equivalentCall.matcher, equivalentCall.retType)

            val newNewCall = newCall.copy(retValue = childMock)

            permanentMocks[retValue] = childMock
            callRef[retValue] = newNewCall

            return newNewCall
        }

        return newCall
    }

    private fun makeEquivalent(newCall: RecordedCall): RecordedCall {
        val equivalentArgs = newCall.matcher.args.map {
            when (it) {
                is EquivalentMatcher -> it.equivalent()
                else -> it
            }
        }

        val equivalentIM = newCall.matcher.copy(args = equivalentArgs)
        return newCall.copy(matcher = equivalentIM)
    }

    private fun makeCallPermanent(call: RecordedCall): RecordedCall {
        val selfChain = callRef[call.matcher.self]
        val argChains = call.matcher.args
            .map {
                when (it) {
                    is EqMatcher -> callRef[it.value] ?: it
                    else -> it
                }
            }

        val newSelf = permanentMocks[call.matcher.self] ?: call.matcher.self
        val newArgs = call.matcher.args.map { it.substitute(permanentMocks) }
        val newMatcher = call.matcher.copy(self = newSelf, args = newArgs)
        return call.copy(
            matcher = newMatcher,
            selfChain = selfChain,
            argChains = argChains
        )
    }

    private fun describeCallTree(calls: MutableList): List {
        val callTree = linkedMapOf()
        val usedCalls = hashSetOf()

        for (call in calls) {
            callTree[call] = formatCall(
                call,
                callTree,
                usedCalls
            )
        }

        return calls.filter {
            it !in usedCalls
        }.map {
                callTree[it] ?: ""
            }
    }

    private fun formatCall(
        call: RecordedCall,
        tree: Map,
        usedCalls: MutableSet
    ): String {
        val methodName = call.matcher.method.name
        val args = call.argChains!!.map {
            when (it) {
                is RecordedCall -> {
                    usedCalls.add(it)
                    tree[it] ?: ""
                }
                else -> it.toString()
            }
        }

        val selfChain = call.selfChain
        val prefix = if (selfChain != null) {
            usedCalls.add(selfChain)
            (tree[selfChain] ?: "") + "."
        } else {
            call.matcher.self.toString() + "."
        }

        if (methodName.startsWith("get") &&
            methodName.length > 3 &&
            args.isEmpty()
        ) {
            return prefix +
                    methodName[3].toLowerCase() +
                    methodName.substring(4)
        }

        return prefix + methodName + "(" + args.joinToString(", ") + ")"
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy