• 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 android.platform.test.rule
18 
19 import com.google.common.truth.Truth.assertThat
20 import com.google.common.truth.Truth.assertWithMessage
21 import java.lang.annotation.Inherited
22 import kotlin.reflect.full.memberFunctions
23 import org.junit.Test
24 import org.junit.runner.Description
25 import org.junit.runner.JUnitCore
26 import org.junit.runner.Request
27 import org.junit.runner.RunWith
28 import org.junit.runners.Parameterized
29 
30 @Inherited @Retention(AnnotationRetention.RUNTIME) annotation class Flavor(val value: String = "")
31 
32 @Retention(AnnotationRetention.RUNTIME) annotation class NotInherited(val value: String = "")
33 
34 @Inherited
35 @Retention(AnnotationRetention.RUNTIME)
36 @MetaAnnotation
37 @Flavor("umami")
38 annotation class Umami()
39 
descriptionnull40 inline fun <reified T> String.description() =
41     Description.createTestDescription(
42         T::class.java,
43         this,
44         *(T::class.memberFunctions.single { it.name == this }.annotations.toTypedArray()),
45     )
46 
47 class TestAnnotationScannerTest {
48     class NoAnnotation {
aTestnull49         fun aTest() {}
50     }
51 
52     @Test
noAnnotationnull53     fun noAnnotation() {
54         val scanner = TestAnnotationScanner()
55         val method = "aTest".description<NoAnnotation>()
56         assertThat(scanner.find<Flavor>(method)).isNull()
57     }
58 
59     class AnnotationOnMethod {
aTestnull60         @Flavor("spicy") fun aTest() {}
61     }
62 
63     @Test
annotationOnMethodnull64     fun annotationOnMethod() {
65         val scanner = TestAnnotationScanner()
66         val method = "aTest".description<AnnotationOnMethod>()
67         assertThat(scanner.find<Flavor>(method)?.value).isEqualTo("spicy")
68     }
69 
70     @Flavor("sour")
71     class AnnotationOnClass {
aTestnull72         fun aTest() {}
73     }
74 
75     @Test
annotationOnClassnull76     fun annotationOnClass() {
77         val scanner = TestAnnotationScanner()
78         val method = "aTest".description<AnnotationOnClass>()
79         assertThat(scanner.find<Flavor>(method)?.value).isEqualTo("sour")
80     }
81 
82     @Flavor("bitter")
83     class AnnotationOnBoth {
aTestnull84         @Flavor("sweet") fun aTest() {}
85     }
86 
87     @Test
methodWinsOnBothnull88     fun methodWinsOnBoth() {
89         val scanner = TestAnnotationScanner()
90         val method = "aTest".description<AnnotationOnBoth>()
91         assertThat(scanner.find<Flavor>(method)?.value).isEqualTo("sweet")
92     }
93 
94     @NotInherited("only for this class")
95     @Flavor("tangy")
96     @AllowedDevices(DeviceProduct.CF_PHONE)
97     open class Superclass
98 
99     class Subclass : Superclass() {
aTestnull100         fun aTest() {}
101     }
102 
103     @Test
findSuperclassIfInheritednull104     fun findSuperclassIfInherited() {
105         val scanner = TestAnnotationScanner()
106         val method = "aTest".description<Subclass>()
107         assertThat(scanner.find<Flavor>(method)?.value).isEqualTo("tangy")
108     }
109 
110     @Test
findAllowedDevicesFromSuperclassnull111     fun findAllowedDevicesFromSuperclass() {
112         val scanner = TestAnnotationScanner()
113         val method = "aTest".description<Subclass>()
114         assertThat(scanner.find<AllowedDevices>(method)?.allowed)
115             .isEqualTo(arrayOf(DeviceProduct.CF_PHONE))
116     }
117 
118     @Test
dontFindSuperclassIfNotInheritednull119     fun dontFindSuperclassIfNotInherited() {
120         val scanner = TestAnnotationScanner()
121         val method = "aTest".description<Subclass>()
122         assertThat(scanner.find<NotInherited>(method)).isNull()
123     }
124 
125     @Umami
126     open class UmamiTest {
aTestnull127         fun aTest() {}
128     }
129 
130     @Test
metaAnnotationnull131     fun metaAnnotation() {
132         val scanner = TestAnnotationScanner()
133         val method = "aTest".description<UmamiTest>()
134         assertThat(scanner.find<Flavor>(method)!!.value).isEqualTo("umami")
135     }
136 
137     @RunWith(Parameterized::class)
138     class ParameterizedUmamiSubclassTest : UmamiTest() {
139         companion object {
140             @Parameterized.Parameters(name = "{0}")
141             @JvmStatic
getParamsnull142             fun getParams() = listOf("1", "2", "3")
143         }
144 
145         @Parameterized.Parameter lateinit var param: String
146 
147         @Test fun bTest() {}
148     }
149 
150     @Test
parameterizedSubclassnull151     fun parameterizedSubclass() {
152         val scanner = TestAnnotationScanner()
153         val request = Request.classes(ParameterizedUmamiSubclassTest::class.java)
154         assertThat(JUnitCore().run(request).failures).isEmpty()
155         val rootDescription = request.runner.description
156         val leaves = rootDescription.leafDescriptions()
157         assertThat(leaves.map { it.methodName }).contains("bTest[1]")
158         val bTestDescription = leaves.first { it.methodName == "bTest[1]" }
159         assertWithMessage(rootDescription.familyTree())
160             .that(scanner.find<Flavor>(bTestDescription)?.value)
161             .isEqualTo("umami")
162     }
163 }
164 
Descriptionnull165 private fun Description.familyTree(): String {
166     return toString() +
167         if (isTest) {
168             ""
169         } else {
170             children.map { it.familyTree() }.toString()
171         }
172 }
173 
Descriptionnull174 private fun Description.leafDescriptions(): List<Description> {
175     return if (isTest) {
176         listOf(this)
177     } else {
178         children.flatMap { it.leafDescriptions() }
179     }
180 }
181