1 /*
<lambda>null2 * Copyright (C) 2020 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.testutils
18
19 import android.os.Build
20 import androidx.test.InstrumentationRegistry
21 import com.android.modules.utils.build.UnboundedSdkLevel
22 import java.util.regex.Pattern
23 import org.junit.Assume.assumeTrue
24 import org.junit.rules.TestRule
25 import org.junit.runner.Description
26 import org.junit.runners.model.Statement
27
28 @Deprecated("Use Build.VERSION_CODES", ReplaceWith("Build.VERSION_CODES.S_V2"))
29 const val SC_V2 = Build.VERSION_CODES.S_V2
30 // TODO: Remove this when Build.VERSION_CODES.VANILLA_ICE_CREAM is available in all branches
31 // where this code builds
32 const val VANILLA_ICE_CREAM = 35 // Bui1ld.VERSION_CODES.VANILLA_ICE_CREAM
33
34 private val MAX_TARGET_SDK_ANNOTATION_RE = Pattern.compile("MaxTargetSdk([0-9]+)$")
35 private val targetSdk = InstrumentationRegistry.getContext().applicationInfo.targetSdkVersion
36
37 private fun isDevSdkInRange(minExclusive: String?, maxInclusive: String?): Boolean {
38 return (minExclusive == null || !isAtMost(minExclusive)) &&
39 (maxInclusive == null || isAtMost(maxInclusive))
40 }
41
isAtMostnull42 private fun isAtMost(sdkVersionOrCodename: String): Boolean {
43 // UnboundedSdkLevel does not support builds < Q, and may stop supporting Q as well since it
44 // is intended for mainline modules that are now R+.
45 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
46 // Assume that any codename passed as argument from current code is a more recent build than
47 // Q: this util did not exist before Q, and codenames are only used before the corresponding
48 // build is finalized. This util could list 28 older codenames to check against (as per
49 // ro.build.version.known_codenames in more recent builds), but this does not seem valuable.
50 val intVersion = sdkVersionOrCodename.toIntOrNull() ?: return true
51 return Build.VERSION.SDK_INT <= intVersion
52 }
53 return UnboundedSdkLevel.isAtMost(sdkVersionOrCodename)
54 }
55
56 /**
57 * Returns true if the development SDK version of the device is in the provided annotation range.
58 *
59 * If the device is not using a release SDK, the development SDK differs from
60 * [Build.VERSION.SDK_INT], and is indicated by the device codenames; see [UnboundedSdkLevel].
61 */
isDevSdkInRangenull62 fun isDevSdkInRange(
63 ignoreUpTo: DevSdkIgnoreRule.IgnoreUpTo?,
64 ignoreAfter: DevSdkIgnoreRule.IgnoreAfter?
65 ): Boolean {
66 val minExclusive =
67 if (ignoreUpTo?.value == 0) ignoreUpTo.codename
68 else ignoreUpTo?.value?.toString()
69 val maxInclusive =
70 if (ignoreAfter?.value == 0) ignoreAfter.codename
71 else ignoreAfter?.value?.toString()
72 return isDevSdkInRange(minExclusive, maxInclusive)
73 }
74
getMaxTargetSdknull75 private fun getMaxTargetSdk(description: Description): Int? {
76 return description.annotations.firstNotNullOfOrNull {
77 MAX_TARGET_SDK_ANNOTATION_RE.matcher(it.annotationClass.simpleName).let { m ->
78 if (m.find()) m.group(1).toIntOrNull() else null
79 }
80 }
81 }
82
83 /**
84 * A test rule to ignore tests based on the development SDK level.
85 *
86 * If the device is not using a release SDK, the development SDK is considered to be higher than
87 * [Build.VERSION.SDK_INT].
88 *
89 * @param ignoreClassUpTo Skip all tests in the class if the device dev SDK is <= this codename or
90 * SDK level.
91 * @param ignoreClassAfter Skip all tests in the class if the device dev SDK is > this codename or
92 * SDK level.
93 */
94 class DevSdkIgnoreRule @JvmOverloads constructor(
95 private val ignoreClassUpTo: String? = null,
96 private val ignoreClassAfter: String? = null
97 ) : TestRule {
98 /**
99 * @param ignoreClassUpTo Skip all tests in the class if the device dev SDK is <= this value.
100 * @param ignoreClassAfter Skip all tests in the class if the device dev SDK is > this value.
101 */
102 @JvmOverloads
103 constructor(ignoreClassUpTo: Int?, ignoreClassAfter: Int? = null) : this(
104 ignoreClassUpTo?.toString(), ignoreClassAfter?.toString())
105
applynull106 override fun apply(base: Statement, description: Description): Statement {
107 return IgnoreBySdkStatement(base, description)
108 }
109
110 /**
111 * Ignore the test for any development SDK that is strictly after [value].
112 *
113 * If the device is not using a release SDK, the development SDK is considered to be higher
114 * than [Build.VERSION.SDK_INT].
115 */
116 annotation class IgnoreAfter(val value: Int = 0, val codename: String = "")
117
118 /**
119 * Ignore the test for any development SDK that lower than or equal to [value].
120 *
121 * If the device is not using a release SDK, the development SDK is considered to be higher
122 * than [Build.VERSION.SDK_INT].
123 */
124 annotation class IgnoreUpTo(val value: Int = 0, val codename: String = "")
125
126 private inner class IgnoreBySdkStatement(
127 private val base: Statement,
128 private val description: Description
129 ) : Statement() {
evaluatenull130 override fun evaluate() {
131 val ignoreAfter = description.getAnnotation(IgnoreAfter::class.java)
132 val ignoreUpTo = description.getAnnotation(IgnoreUpTo::class.java)
133
134 val devSdkMessage = "Skipping test for build ${Build.VERSION.CODENAME} " +
135 "with SDK ${Build.VERSION.SDK_INT}"
136 assumeTrue(devSdkMessage, isDevSdkInRange(ignoreClassUpTo, ignoreClassAfter))
137 assumeTrue(devSdkMessage, isDevSdkInRange(ignoreUpTo, ignoreAfter))
138
139 val maxTargetSdk = getMaxTargetSdk(description)
140 if (maxTargetSdk != null) {
141 assumeTrue("Skipping test, target SDK $targetSdk greater than $maxTargetSdk",
142 targetSdk <= maxTargetSdk)
143 }
144 base.evaluate()
145 }
146 }
147 }
148