• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tools.metalava.model.junit4
18 
19 import com.android.tools.metalava.model.junit4.ParameterizedRunner.TestArguments
20 import org.junit.runner.Runner
21 import org.junit.runners.Parameterized
22 import org.junit.runners.Parameterized.Parameters
23 import org.junit.runners.model.FrameworkMethod
24 import org.junit.runners.model.TestClass
25 import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParametersFactory
26 import org.junit.runners.parameterized.ParametersRunnerFactory
27 import org.junit.runners.parameterized.TestWithParameters
28 
29 /**
30  * Extends [ParameterizedRunner] to combine parameters from the test class (specified using
31  * [Parameters] and additional arguments from [argumentsProvider].
32  *
33  * @param clazz the test class to run.
34  * @param argumentsProvider provider of [TestArguments] used by this runner. Is also passed any
35  *   additional parameters (provided by the test class using the standard [Parameterized]
36  *   mechanism), if any. They can be filtered and/or combined in some way with parameters provides
37  *   by this.
38  * @param parametersRunnerFactory factory for creating a [Runner] from a [TestWithParameters].
39  */
40 abstract class CustomizableParameterizedRunner<A : Any>(
41     clazz: Class<*>,
42     private val argumentsProvider: (TestClass, List<Array<Any>>?) -> TestArguments<A>,
43     parametersRunnerFactory: ParametersRunnerFactory =
44         BlockJUnit4ClassRunnerWithParametersFactory(),
45 ) : ParameterizedRunner<A>(TestClass(clazz), parametersRunnerFactory) {
46 
computeTestArgumentsnull47     override fun computeTestArguments(testClass: TestClass): TestArguments<A> {
48         // Get additional arguments (if any) from the actual test class.
49         val additionalArguments = getAdditionalArguments(testClass)
50 
51         // Obtain [TestArguments] from the provider and store the list of argument sets in the
52         // [RunnersFactory.allParametersField].
53         val testArguments = argumentsProvider(testClass, additionalArguments)
54 
55         // Check to see if there is a parameters filter method provided.
56         val parametersFilterMethod =
57             testClass.getAnnotatedMethods(ParameterFilter::class.java).firstOrNull {
58                 it.isPublic && it.isStatic
59             }
60 
61         // If there is then apply it to the testArguments, otherwise return it unfiltered.
62         val filteredArguments =
63             if (parametersFilterMethod == null) {
64                 testArguments
65             } else {
66                 testArguments.copy(
67                     argumentSets =
68                         testArguments.argumentSets.filter {
69                             invokeFilterMethod(parametersFilterMethod, it)
70                         }
71                 )
72             }
73 
74         return filteredArguments
75     }
76 
77     /**
78      * Invoke the [parametersFilterMethod] on [argument].
79      *
80      * Subclasses that wish to use filters must override this.
81      */
invokeFilterMethodnull82     protected open fun invokeFilterMethod(
83         parametersFilterMethod: FrameworkMethod,
84         argument: A
85     ): Boolean {
86         error("Subclass does not implement invokeFilterMethod(...) method")
87     }
88 
89     companion object {
90         /**
91          * Get additional arguments, if any, provided by the [testClass] through use of a
92          * [Parameters] function.
93          *
94          * The returned values have been normalized so each entry is an `Array<Any>`.
95          */
getAdditionalArgumentsnull96         private fun getAdditionalArguments(testClass: TestClass): List<Array<Any>>? {
97             val parametersMethod =
98                 testClass.getAnnotatedMethods(Parameters::class.java).firstOrNull {
99                     it.isPublic && it.isStatic
100                 }
101                     ?: return null
102             return when (val parameters = parametersMethod.invokeExplosively(null)) {
103                     is List<*> -> parameters
104                     is Iterable<*> -> parameters.toList()
105                     is Array<*> -> parameters.toList()
106                     else ->
107                         error(
108                             "${testClass.name}.{${parametersMethod.name}() must return an Iterable of arrays."
109                         )
110                 }
111                 .filterNotNull()
112                 .map {
113                     if (
114                         it is Array<*> &&
115                             it.javaClass.isArray &&
116                             it.javaClass.componentType == Object::class.java
117                     ) {
118                         @Suppress("UNCHECKED_CAST")
119                         it as Array<Any>
120                     } else {
121                         arrayOf(it)
122                     }
123                 }
124         }
125     }
126 }
127