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

org.mockito.kotlin.ArgumentCaptor.kt Maven / Gradle / Ivy

The newest version!
/*
 * The MIT License
 *
 * Copyright (c) 2018 Niek Haarman
 * Copyright (c) 2007 Mockito contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.mockito.kotlin

import org.mockito.kotlin.internal.createInstance
import org.mockito.ArgumentCaptor
import java.lang.reflect.Array
import kotlin.reflect.KClass

/**
 * Creates a [KArgumentCaptor] for given type.
 */
inline fun  argumentCaptor(): KArgumentCaptor {
    return KArgumentCaptor(ArgumentCaptor.forClass(T::class.java), T::class)
}

/**
 * Creates 2 [KArgumentCaptor]s for given types.
 */
inline fun  argumentCaptor(
    a: KClass = A::class,
    b: KClass = B::class
): Pair, KArgumentCaptor> {
    return Pair(
          KArgumentCaptor(ArgumentCaptor.forClass(a.java), a),
          KArgumentCaptor(ArgumentCaptor.forClass(b.java), b)
    )
}

/**
 * Creates 3 [KArgumentCaptor]s for given types.
 */
inline fun  argumentCaptor(
    a: KClass = A::class,
    b: KClass = B::class,
    c: KClass = C::class
): Triple, KArgumentCaptor, KArgumentCaptor> {
    return Triple(
          KArgumentCaptor(ArgumentCaptor.forClass(a.java), a),
          KArgumentCaptor(ArgumentCaptor.forClass(b.java), b),
          KArgumentCaptor(ArgumentCaptor.forClass(c.java), c)
    )
}

class ArgumentCaptorHolder4(
    val first: A,
    val second: B,
    val third: C,
    val fourth: D
) {

    operator fun component1() = first
    operator fun component2() = second
    operator fun component3() = third
    operator fun component4() = fourth
}

class ArgumentCaptorHolder5(
    val first: A,
    val second: B,
    val third: C,
    val fourth: D,
    val fifth: E
) {

    operator fun component1() = first
    operator fun component2() = second
    operator fun component3() = third
    operator fun component4() = fourth
    operator fun component5() = fifth
}

/**
 * Creates 4 [KArgumentCaptor]s for given types.
 */
inline fun  argumentCaptor(
    a: KClass = A::class,
    b: KClass = B::class,
    c: KClass = C::class,
    d: KClass = D::class
): ArgumentCaptorHolder4, KArgumentCaptor, KArgumentCaptor, KArgumentCaptor> {
    return ArgumentCaptorHolder4(
          KArgumentCaptor(ArgumentCaptor.forClass(a.java), a),
          KArgumentCaptor(ArgumentCaptor.forClass(b.java), b),
          KArgumentCaptor(ArgumentCaptor.forClass(c.java), c),
          KArgumentCaptor(ArgumentCaptor.forClass(d.java), d)
    )
}

/**
 * Creates 4 [KArgumentCaptor]s for given types.
 */
inline fun  argumentCaptor(
    a: KClass = A::class,
    b: KClass = B::class,
    c: KClass = C::class,
    d: KClass = D::class,
    e: KClass = E::class
): ArgumentCaptorHolder5, KArgumentCaptor, KArgumentCaptor, KArgumentCaptor, KArgumentCaptor> {
    return ArgumentCaptorHolder5(
          KArgumentCaptor(ArgumentCaptor.forClass(a.java), a),
          KArgumentCaptor(ArgumentCaptor.forClass(b.java), b),
          KArgumentCaptor(ArgumentCaptor.forClass(c.java), c),
          KArgumentCaptor(ArgumentCaptor.forClass(d.java), d),
          KArgumentCaptor(ArgumentCaptor.forClass(e.java), e)
    )
}

/**
 * Creates a [KArgumentCaptor] for given type, taking in a lambda to allow fast verification.
 */
inline fun  argumentCaptor(f: KArgumentCaptor.() -> Unit): KArgumentCaptor {
    return argumentCaptor().apply(f)
}

/**
 * Creates a [KArgumentCaptor] for given nullable type.
 */
inline fun  nullableArgumentCaptor(): KArgumentCaptor {
    return KArgumentCaptor(ArgumentCaptor.forClass(T::class.java), T::class)
}

/**
 * Creates a [KArgumentCaptor] for given nullable type, taking in a lambda to allow fast verification.
 */
inline fun  nullableArgumentCaptor(f: KArgumentCaptor.() -> Unit): KArgumentCaptor {
    return nullableArgumentCaptor().apply(f)
}

/**
 * Alias for [ArgumentCaptor.capture].
 */
inline fun  capture(captor: ArgumentCaptor): T {
    return captor.capture() ?: createInstance()
}

class KArgumentCaptor(
    private val captor: ArgumentCaptor,
    private val tClass: KClass<*>
) {

    /**
     * The first captured value of the argument.
     * @throws IndexOutOfBoundsException if the value is not available.
     */
    val firstValue: T
        get() = captor.firstValue

    /**
     * The second captured value of the argument.
     * @throws IndexOutOfBoundsException if the value is not available.
     */
    val secondValue: T
        get() = captor.secondValue

    /**
     * The third captured value of the argument.
     * @throws IndexOutOfBoundsException if the value is not available.
     */
    val thirdValue: T
        get() = captor.thirdValue

    /**
     * The last captured value of the argument.
     * @throws IndexOutOfBoundsException if the value is not available.
     */
    val lastValue: T
        get() = captor.lastValue

    val allValues: List
        get() = captor.allValues

    @Suppress("UNCHECKED_CAST")
    fun capture(): T {
        // Special handling for arrays to make it work for varargs
        // In Kotlin we want have to capture vararg like this `verify(m).methodName(*captor.capture())` to make the types work
        // If we return null for array types, the spread `*` operator will fail with NPE
        // If we return empty array, it will fail in MatchersBinder.validateMatchers
        // In Java, `captor.capture` returns null and so the method is called with `[null]`
        // In Kotlin, we have to create `[null]` explicitly.
        // This code-path is applied for non-vararg array arguments as well, but it seems to work fine.
        return captor.capture() ?: if (tClass.java.isArray) {
            singleElementArray()
        } else {
            createInstance(tClass)
        } as T
    }

    private fun singleElementArray(): Any? = Array.newInstance(tClass.java.componentType, 1)
}

val  ArgumentCaptor.firstValue: T
    get() = allValues[0]

val  ArgumentCaptor.secondValue: T
    get() = allValues[1]

val  ArgumentCaptor.thirdValue: T
    get() = allValues[2]

val  ArgumentCaptor.lastValue: T
    get() = allValues.last()