2525
2626package org.mockito.kotlin
2727
28- import org.mockito.kotlin.internal.createInstance
2928import org.mockito.ArgumentCaptor
29+ import org.mockito.kotlin.internal.toKotlinType
30+ import org.mockito.kotlin.internal.createInstance
31+ import org.mockito.kotlin.internal.valueClassInnerClass
3032import java.lang.reflect.Array
3133import kotlin.reflect.KClass
34+ import kotlin.reflect.KType
35+ import kotlin.reflect.typeOf
3236
3337/* *
3438 * Creates a [KArgumentCaptor] for given type.
39+ *
40+ * Caution: this factory method cannot be used to create a captor for a suspend
41+ * function, please refer to [suspendFunctionArgumentCaptor] for that.
42+ * This incompatibility is caused by the use of `typeOf<T>()` which is the way
43+ * to determine runtime nullability of T, but this function is yet incompatible with
44+ * suspend functions at compile time. That incompatibility has been declared since
45+ * Kotlin 1.6, and the promised proper support for suspend functions has not been
46+ * delivered ever since.
47+ * See [Kotlin issue KT-47562](https://youtrack.jetbrains.com/issue/KT-47562/Support-suspend-functional-types-in-typeOf)
48+ * for more details.
49+ */
50+ inline fun <reified T : Any ? > argumentCaptor (): KArgumentCaptor <T > {
51+ return KArgumentCaptor (typeOf<T >())
52+ }
53+
54+ /* *
55+ * Creates a [KArgumentCaptor] for given (suspend function).
3556 */
36- inline fun <reified T : Any > argumentCaptor (): KArgumentCaptor <T > {
57+ inline fun <reified T : Function < * >> suspendFunctionArgumentCaptor (): KArgumentCaptor <T > {
3758 return KArgumentCaptor (T ::class )
3859}
3960
4061/* *
4162 * Creates 2 [KArgumentCaptor]s for given types.
63+ *
64+ * Caution: this factory method cannot be used to create a captor for a suspend
65+ * function, please refer to [suspendFunctionArgumentCaptor] for that.
66+ * This incompatibility is caused by the use of `typeOf<T>()` which is the way
67+ * to determine runtime nullability of T, but this function is yet incompatible with
68+ * suspend functions at compile time. That incompatibility has been declared since
69+ * Kotlin 1.6, and the promised proper support for suspend functions has not been
70+ * delivered ever since.
71+ * See [Kotlin issue KT-47562](https://youtrack.jetbrains.com/issue/KT-47562/Support-suspend-functional-types-in-typeOf)
72+ * for more details.
4273 */
4374inline fun <reified A : Any , reified B : Any > argumentCaptor (
44- a : KClass <A > = A : :class,
45- b : KClass <B > = B : :class
75+ @Suppress( " unused " ) a : KClass <A > = A : :class,
76+ @Suppress( " unused " ) b : KClass <B > = B : :class
4677): Pair <KArgumentCaptor <A >, KArgumentCaptor<B>> {
4778 return Pair (
48- KArgumentCaptor (a ),
49- KArgumentCaptor (b )
79+ KArgumentCaptor (typeOf< A >() ),
80+ KArgumentCaptor (typeOf< B >() )
5081 )
5182}
5283
5384/* *
5485 * Creates 3 [KArgumentCaptor]s for given types.
86+ *
87+ * Caution: this factory method cannot be used to create a captor for a suspend
88+ * function, please refer to [suspendFunctionArgumentCaptor] for that.
89+ * This incompatibility is caused by the use of `typeOf<T>()` which is the way
90+ * to determine runtime nullability of T, but this function is yet incompatible with
91+ * suspend functions at compile time. That incompatibility has been declared since
92+ * Kotlin 1.6, and the promised proper support for suspend functions has not been
93+ * delivered ever since.
94+ * See [Kotlin issue KT-47562](https://youtrack.jetbrains.com/issue/KT-47562/Support-suspend-functional-types-in-typeOf)
95+ * for more details.
5596 */
5697inline fun <reified A : Any , reified B : Any , reified C : Any > argumentCaptor (
57- a : KClass <A > = A : :class,
58- b : KClass <B > = B : :class,
59- c : KClass <C > = C : :class
98+ @Suppress( " unused " ) a : KClass <A > = A : :class,
99+ @Suppress( " unused " ) b : KClass <B > = B : :class,
100+ @Suppress( " unused " ) c : KClass <C > = C : :class
60101): Triple <KArgumentCaptor <A >, KArgumentCaptor<B>, KArgumentCaptor<C>> {
61102 return Triple (
62- KArgumentCaptor (a ),
63- KArgumentCaptor (b ),
64- KArgumentCaptor (c )
103+ KArgumentCaptor (typeOf< A >() ),
104+ KArgumentCaptor (typeOf< B >() ),
105+ KArgumentCaptor (typeOf< C >() )
65106 )
66107}
67108
@@ -71,21 +112,46 @@ class ArgumentCaptorHolder4<out A, out B, out C, out D>(
71112 val third : C ,
72113 val fourth : D
73114) {
74-
75115 operator fun component1 () = first
76116 operator fun component2 () = second
77117 operator fun component3 () = third
78118 operator fun component4 () = fourth
79119}
80120
121+ /* *
122+ * Creates 4 [KArgumentCaptor]s for given types.
123+ *
124+ * Caution: this factory method cannot be used to create a captor for a suspend
125+ * function, please refer to [suspendFunctionArgumentCaptor] for that.
126+ * This incompatibility is caused by the use of `typeOf<T>()` which is the way
127+ * to determine runtime nullability of T, but this function is yet incompatible with
128+ * suspend functions at compile time. That incompatibility has been declared since
129+ * Kotlin 1.6, and the promised proper support for suspend functions has not been
130+ * delivered ever since.
131+ * See [Kotlin issue KT-47562](https://youtrack.jetbrains.com/issue/KT-47562/Support-suspend-functional-types-in-typeOf)
132+ * for more details.
133+ */
134+ inline fun <reified A : Any , reified B : Any , reified C : Any , reified D : Any > argumentCaptor (
135+ @Suppress(" unused" ) a : KClass <A > = A : :class,
136+ @Suppress(" unused" ) b : KClass <B > = B : :class,
137+ @Suppress(" unused" ) c : KClass <C > = C : :class,
138+ @Suppress(" unused" ) d : KClass <D > = D : :class
139+ ): ArgumentCaptorHolder4 <KArgumentCaptor <A >, KArgumentCaptor<B>, KArgumentCaptor<C>, KArgumentCaptor<D>> {
140+ return ArgumentCaptorHolder4 (
141+ KArgumentCaptor (typeOf<A >()),
142+ KArgumentCaptor (typeOf<B >()),
143+ KArgumentCaptor (typeOf<C >()),
144+ KArgumentCaptor (typeOf<D >())
145+ )
146+ }
147+
81148class ArgumentCaptorHolder5 <out A , out B , out C , out D , out E >(
82149 val first : A ,
83150 val second : B ,
84151 val third : C ,
85152 val fourth : D ,
86153 val fifth : E
87154) {
88-
89155 operator fun component1 () = first
90156 operator fun component2 () = second
91157 operator fun component3 () = third
@@ -94,38 +160,31 @@ class ArgumentCaptorHolder5<out A, out B, out C, out D, out E>(
94160}
95161
96162/* *
97- * Creates 4 [KArgumentCaptor]s for given types.
98- */
99- inline fun <reified A : Any , reified B : Any , reified C : Any , reified D : Any > argumentCaptor (
100- a : KClass <A > = A : :class,
101- b : KClass <B > = B : :class,
102- c : KClass <C > = C : :class,
103- d : KClass <D > = D : :class
104- ): ArgumentCaptorHolder4 <KArgumentCaptor <A >, KArgumentCaptor<B>, KArgumentCaptor<C>, KArgumentCaptor<D>> {
105- return ArgumentCaptorHolder4 (
106- KArgumentCaptor (a),
107- KArgumentCaptor (b),
108- KArgumentCaptor (c),
109- KArgumentCaptor (d)
110- )
111- }
112-
113- /* *
114- * Creates 4 [KArgumentCaptor]s for given types.
163+ * Creates 5 [KArgumentCaptor]s for given types.
164+ *
165+ * Caution: this factory method cannot be used to create a captor for a suspend
166+ * function, please refer to [suspendFunctionArgumentCaptor] for that.
167+ * This incompatibility is caused by the use of `typeOf<T>()` which is the way
168+ * to determine runtime nullability of T, but this function is yet incompatible with
169+ * suspend functions at compile time. That incompatibility has been declared since
170+ * Kotlin 1.6, and the promised proper support for suspend functions has not been
171+ * delivered ever since.
172+ * See [Kotlin issue KT-47562](https://youtrack.jetbrains.com/issue/KT-47562/Support-suspend-functional-types-in-typeOf)
173+ * for more details.
115174 */
116175inline fun <reified A : Any , reified B : Any , reified C : Any , reified D : Any , reified E : Any > argumentCaptor (
117- a : KClass <A > = A : :class,
118- b : KClass <B > = B : :class,
119- c : KClass <C > = C : :class,
120- d : KClass <D > = D : :class,
121- e : KClass <E > = E : :class
176+ @Suppress( " unused " ) a : KClass <A > = A : :class,
177+ @Suppress( " unused " ) b : KClass <B > = B : :class,
178+ @Suppress( " unused " ) c : KClass <C > = C : :class,
179+ @Suppress( " unused " ) d : KClass <D > = D : :class,
180+ @Suppress( " unused " ) e : KClass <E > = E : :class
122181): ArgumentCaptorHolder5 <KArgumentCaptor <A >, KArgumentCaptor<B>, KArgumentCaptor<C>, KArgumentCaptor<D>, KArgumentCaptor<E>> {
123182 return ArgumentCaptorHolder5 (
124- KArgumentCaptor (a ),
125- KArgumentCaptor (b ),
126- KArgumentCaptor (c ),
127- KArgumentCaptor (d ),
128- KArgumentCaptor (e )
183+ KArgumentCaptor (typeOf< A >() ),
184+ KArgumentCaptor (typeOf< B >() ),
185+ KArgumentCaptor (typeOf< C >() ),
186+ KArgumentCaptor (typeOf< D >() ),
187+ KArgumentCaptor (typeOf< E >() )
129188 )
130189}
131190
@@ -140,7 +199,7 @@ inline fun <reified T : Any> argumentCaptor(f: KArgumentCaptor<T>.() -> Unit): K
140199 * Creates a [KArgumentCaptor] for given nullable type.
141200 */
142201inline fun <reified T : Any > nullableArgumentCaptor (): KArgumentCaptor <T ?> {
143- return KArgumentCaptor (T :: class )
202+ return KArgumentCaptor (typeOf< T >() )
144203}
145204
146205/* *
@@ -157,58 +216,61 @@ inline fun <reified T : Any> capture(captor: ArgumentCaptor<T>): T {
157216 return captor.capture() ? : createInstance()
158217}
159218
160- class KArgumentCaptor <out T : Any ?> (
161- private val tClass : KClass <* >
219+ class KArgumentCaptor <out T : Any ?>(
220+ private val clazz : KClass <* >,
221+ private val isMarkedNullable : Boolean = false
162222) {
223+ constructor (kType: KType ): this (
224+ kType.classifier as KClass <* >,
225+ kType.isMarkedNullable
226+ )
227+
163228 private val captor: ArgumentCaptor <Any ?> =
164- if (tClass.isValue) {
165- val boxImpl =
166- tClass.java.declaredMethods
167- .single { it.name == " box-impl" && it.parameterCount == 1 }
168- boxImpl.parameters[0 ].type // is the boxed type of the value type
229+ if (clazz.isValue && ! isMarkedNullable) {
230+ clazz.valueClassInnerClass()
169231 } else {
170- tClass.java
232+ clazz
171233 }.let {
172- ArgumentCaptor .forClass(it)
234+ ArgumentCaptor .forClass(it.java )
173235 }
174236
175237 /* *
176238 * The first captured value of the argument.
177239 * @throws IndexOutOfBoundsException if the value is not available.
178240 */
179241 val firstValue: T
180- get() = toKotlinType( captor.firstValue)
242+ get() = captor.firstValue.toKotlinType(clazz )
181243
182244 /* *
183245 * The second captured value of the argument.
184246 * @throws IndexOutOfBoundsException if the value is not available.
185247 */
186248 val secondValue: T
187- get() = toKotlinType( captor.secondValue)
249+ get() = captor.secondValue.toKotlinType(clazz )
188250
189251 /* *
190252 * The third captured value of the argument.
191253 * @throws IndexOutOfBoundsException if the value is not available.
192254 */
193255 val thirdValue: T
194- get() = toKotlinType( captor.thirdValue)
256+ get() = captor.thirdValue.toKotlinType(clazz )
195257
196258 /* *
197259 * The last captured value of the argument.
198260 * @throws IndexOutOfBoundsException if the value is not available.
199261 */
200262 val lastValue: T
201- get() = toKotlinType( captor.lastValue)
263+ get() = captor.lastValue.toKotlinType(clazz )
202264
203265 /* *
204266 * The *only* captured value of the argument,
205267 * or throws an exception if no value or more than one value was captured.
206268 */
207269 val singleValue: T
208- get() = toKotlinType( captor.singleValue)
270+ get() = captor.singleValue.toKotlinType(clazz )
209271
210272 val allValues: List <T >
211- get() = captor.allValues.map(:: toKotlinType)
273+ get() = captor.allValues.map { it. toKotlinType(clazz) }
212274
213275 @Suppress(" UNCHECKED_CAST" )
214276 fun capture (): T {
@@ -219,28 +281,14 @@ class KArgumentCaptor<out T : Any?> (
219281 // In Java, `captor.capture` returns null and so the method is called with `[null]`
220282 // In Kotlin, we have to create `[null]` explicitly.
221283 // This code-path is applied for non-vararg array arguments as well, but it seems to work fine.
222- return captor.capture() as T ? : if (tClass .java.isArray) {
284+ return captor.capture().toKotlinType(clazz) ? : if (clazz .java.isArray) {
223285 singleElementArray()
224286 } else {
225- createInstance(tClass )
287+ createInstance(clazz )
226288 } as T
227289 }
228290
229- private fun singleElementArray (): Any? = Array .newInstance(tClass.java.componentType, 1 )
230-
231- @Suppress(" UNCHECKED_CAST" )
232- private fun toKotlinType (rawCapturedValue : Any? ) : T {
233- return if (tClass.isValue) {
234- rawCapturedValue
235- ?.let {
236- val boxImpl =
237- tClass.java.declaredMethods.single { it.name == " box-impl" && it.parameterCount == 1 }
238- boxImpl.invoke(null , it)
239- } as T
240- } else {
241- rawCapturedValue as T
242- }
243- }
291+ private fun singleElementArray (): Any? = Array .newInstance(clazz.java.componentType, 1 )
244292}
245293
246294val <T > ArgumentCaptor <T >.firstValue: T
0 commit comments