Merge "Use UnboundedSdkLevel for DevSdkIgnoreRule"
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/CompatUtil.kt b/staticlibs/testutils/devicetests/com/android/testutils/CompatUtil.kt
index ff8c668..82f1d9b 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/CompatUtil.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/CompatUtil.kt
@@ -17,7 +17,7 @@
 package com.android.testutils
 
 import android.net.NetworkSpecifier
-import android.os.Build
+import com.android.modules.utils.build.SdkLevel.isAtLeastS
 
 /**
  * Test utility to create [NetworkSpecifier]s on different SDK versions.
@@ -26,7 +26,7 @@
     @JvmStatic
     fun makeTestNetworkSpecifier(ifName: String): NetworkSpecifier {
         // Until R, there was no TestNetworkSpecifier, StringNetworkSpecifier was used instead
-        if (isDevSdkInRange(minExclusive = null, maxInclusive = Build.VERSION_CODES.R)) {
+        if (!isAtLeastS()) {
             return makeNetworkSpecifierInternal("android.net.StringNetworkSpecifier", ifName)
         }
         // TestNetworkSpecifier is not part of the SDK in some branches using this utility
@@ -37,7 +37,7 @@
     @JvmStatic
     fun makeEthernetNetworkSpecifier(ifName: String): NetworkSpecifier {
         // Until R, there was no EthernetNetworkSpecifier, StringNetworkSpecifier was used instead
-        if (isDevSdkInRange(minExclusive = null, maxInclusive = Build.VERSION_CODES.R)) {
+        if (!isAtLeastS()) {
             return makeNetworkSpecifierInternal("android.net.StringNetworkSpecifier", ifName)
         }
         // EthernetNetworkSpecifier is not part of the SDK in some branches using this utility
@@ -50,4 +50,4 @@
         return Class.forName(clazz)
                 .getConstructor(String::class.java).newInstance(specifier) as NetworkSpecifier
     }
-}
\ No newline at end of file
+}
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt b/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt
index 3fcf801..21e84da 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt
@@ -18,58 +18,41 @@
 
 import android.os.Build
 import androidx.test.InstrumentationRegistry
-import com.android.modules.utils.build.SdkLevel
-import kotlin.test.fail
+import com.android.modules.utils.build.UnboundedSdkLevel
 import org.junit.Assume.assumeTrue
 import org.junit.rules.TestRule
 import org.junit.runner.Description
 import org.junit.runners.model.Statement
 import java.util.regex.Pattern
 
-// TODO: Remove it when Build.VERSION_CODES.SC_V2 is available
-const val SC_V2 = 32
+@Deprecated("Use Build.VERSION_CODES", ReplaceWith("Build.VERSION_CODES.S_V2"))
+const val SC_V2 = Build.VERSION_CODES.S_V2
 
 private val MAX_TARGET_SDK_ANNOTATION_RE = Pattern.compile("MaxTargetSdk([0-9]+)$")
 private val targetSdk = InstrumentationRegistry.getContext().applicationInfo.targetSdkVersion
 
+private fun isDevSdkInRange(minExclusive: String?, maxInclusive: String?): Boolean {
+    return (minExclusive == null || !UnboundedSdkLevel.isAtMost(minExclusive)) &&
+            (maxInclusive == null || UnboundedSdkLevel.isAtMost(maxInclusive))
+}
+
 /**
- * Returns true if the development SDK version of the device is in the provided range.
+ * Returns true if the development SDK version of the device is in the provided annotation range.
  *
- * If the device is not using a release SDK, the development SDK is considered to be higher than
- * [Build.VERSION.SDK_INT].
+ * If the device is not using a release SDK, the development SDK differs from
+ * [Build.VERSION.SDK_INT], and is indicated by the device codenames; see [UnboundedSdkLevel].
  */
-fun isDevSdkInRange(minExclusive: Int?, maxInclusive: Int?): Boolean {
-    return (minExclusive == null || isDevSdkAfter(minExclusive)) &&
-            (maxInclusive == null || isDevSdkUpTo(maxInclusive))
-}
-
-private fun isDevSdkAfter(minExclusive: Int): Boolean {
-    // A development build for T typically has SDK_INT = 30 (R) or SDK_INT = 31 (S), so SDK_INT
-    // alone cannot be used to check the SDK version.
-    // For recent SDKs that still have development builds used for testing, use SdkLevel utilities
-    // instead of SDK_INT.
-    return when (minExclusive) {
-        // TODO: Use Build.VERSION_CODES.SC_V2 when it is available
-        SC_V2 -> SdkLevel.isAtLeastT()
-        // TODO: To use SdkLevel.isAtLeastSv2 when available
-        Build.VERSION_CODES.S -> fail("Do you expect to ignore the test until T? Use SC_V2 instead")
-        Build.VERSION_CODES.R -> SdkLevel.isAtLeastS()
-        // Development builds of SDK versions <= R are not used anymore
-        else -> Build.VERSION.SDK_INT > minExclusive
-    }
-}
-
-private fun isDevSdkUpTo(maxInclusive: Int): Boolean {
-    return when (maxInclusive) {
-        // TODO: Use Build.VERSION_CODES.SC_V2 when it is available
-        SC_V2 -> !SdkLevel.isAtLeastT()
-        // TODO: To use SdkLevel.isAtLeastSv2 when available
-        Build.VERSION_CODES.S ->
-                fail("Do you expect to ignore the test before T? Use SC_V2 instead")
-        Build.VERSION_CODES.R -> !SdkLevel.isAtLeastS()
-        // Development builds of SDK versions <= R are not used anymore
-        else -> Build.VERSION.SDK_INT <= maxInclusive
-    }
+fun isDevSdkInRange(
+    ignoreUpTo: DevSdkIgnoreRule.IgnoreUpTo?,
+    ignoreAfter: DevSdkIgnoreRule.IgnoreAfter?
+): Boolean {
+    val minExclusive =
+            if (ignoreUpTo?.value == 0) ignoreUpTo.codename
+            else ignoreUpTo?.value?.toString()
+    val maxInclusive =
+            if (ignoreAfter?.value == 0) ignoreAfter.codename
+            else ignoreAfter?.value?.toString()
+    return isDevSdkInRange(minExclusive, maxInclusive)
 }
 
 private fun getMaxTargetSdk(description: Description): Int? {
@@ -86,13 +69,23 @@
  * If the device is not using a release SDK, the development SDK is considered to be higher than
  * [Build.VERSION.SDK_INT].
  *
- * @param ignoreClassUpTo Skip all tests in the class if the device dev SDK is <= this value.
- * @param ignoreClassAfter Skip all tests in the class if the device dev SDK is > this value.
+ * @param ignoreClassUpTo Skip all tests in the class if the device dev SDK is <= this codename or
+ *                        SDK level.
+ * @param ignoreClassAfter Skip all tests in the class if the device dev SDK is > this codename or
+ *                         SDK level.
  */
 class DevSdkIgnoreRule @JvmOverloads constructor(
-    private val ignoreClassUpTo: Int? = null,
-    private val ignoreClassAfter: Int? = null
+    private val ignoreClassUpTo: String? = null,
+    private val ignoreClassAfter: String? = null
 ) : TestRule {
+    /**
+     * @param ignoreClassUpTo Skip all tests in the class if the device dev SDK is <= this value.
+     * @param ignoreClassAfter Skip all tests in the class if the device dev SDK is > this value.
+     */
+    @JvmOverloads
+    constructor(ignoreClassUpTo: Int?, ignoreClassAfter: Int? = null) : this(
+            ignoreClassUpTo?.toString(), ignoreClassAfter?.toString())
+
     override fun apply(base: Statement, description: Description): Statement {
         return IgnoreBySdkStatement(base, description)
     }
@@ -103,7 +96,7 @@
      * If the device is not using a release SDK, the development SDK is considered to be higher
      * than [Build.VERSION.SDK_INT].
      */
-    annotation class IgnoreAfter(val value: Int)
+    annotation class IgnoreAfter(val value: Int = 0, val codename: String = "")
 
     /**
      * Ignore the test for any development SDK that lower than or equal to [value].
@@ -111,7 +104,7 @@
      * If the device is not using a release SDK, the development SDK is considered to be higher
      * than [Build.VERSION.SDK_INT].
      */
-    annotation class IgnoreUpTo(val value: Int)
+    annotation class IgnoreUpTo(val value: Int = 0, val codename: String = "")
 
     private inner class IgnoreBySdkStatement(
         private val base: Statement,
@@ -124,7 +117,7 @@
             val devSdkMessage = "Skipping test for build ${Build.VERSION.CODENAME} " +
                     "with SDK ${Build.VERSION.SDK_INT}"
             assumeTrue(devSdkMessage, isDevSdkInRange(ignoreClassUpTo, ignoreClassAfter))
-            assumeTrue(devSdkMessage, isDevSdkInRange(ignoreUpTo?.value, ignoreAfter?.value))
+            assumeTrue(devSdkMessage, isDevSdkInRange(ignoreUpTo, ignoreAfter))
 
             val maxTargetSdk = getMaxTargetSdk(description)
             if (maxTargetSdk != null) {
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt b/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt
index da7bf97..2e73666 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt
@@ -52,7 +52,7 @@
         val ignoreAfter = it.getAnnotation(IgnoreAfter::class.java)
         val ignoreUpTo = it.getAnnotation(IgnoreUpTo::class.java)
 
-        if (isDevSdkInRange(ignoreUpTo?.value, ignoreAfter?.value)) AndroidJUnit4(klass) else null
+        if (isDevSdkInRange(ignoreUpTo, ignoreAfter)) AndroidJUnit4(klass) else null
     }
 
     override fun run(notifier: RunNotifier) {