1 /* 2 * 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 androidx.test.ext.junit.runners.AndroidJUnit4 20 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter 21 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo 22 import org.junit.runner.Description 23 import org.junit.runner.Runner 24 import org.junit.runner.notification.RunNotifier 25 26 /** 27 * A runner that can skip tests based on the development SDK as defined in [DevSdkIgnoreRule]. 28 * 29 * Generally [DevSdkIgnoreRule] should be used for that purpose (using rules is preferable over 30 * replacing the test runner), however JUnit runners inspect all methods in the test class before 31 * processing test rules. This may cause issues if the test methods are referencing classes that do 32 * not exist on the SDK of the device the test is run on. 33 * 34 * This runner inspects [IgnoreAfter] and [IgnoreUpTo] annotations on the test class, and will skip 35 * the whole class if they do not match the development SDK as defined in [DevSdkIgnoreRule]. 36 * Otherwise, it will delegate to [AndroidJUnit4] to run the test as usual. 37 * 38 * Example usage: 39 * 40 * @RunWith(DevSdkIgnoreRunner::class) 41 * @IgnoreUpTo(Build.VERSION_CODES.Q) 42 * class MyTestClass { ... } 43 */ 44 class DevSdkIgnoreRunner(private val klass: Class<*>) : Runner() { 45 private val baseRunner = klass.let { 46 val ignoreAfter = it.getAnnotation(IgnoreAfter::class.java) 47 val ignoreUpTo = it.getAnnotation(IgnoreUpTo::class.java) 48 49 if (isDevSdkInRange(ignoreUpTo?.value, ignoreAfter?.value)) AndroidJUnit4(klass) else null 50 } 51 52 override fun run(notifier: RunNotifier) { 53 if (baseRunner != null) { 54 baseRunner.run(notifier) 55 return 56 } 57 58 // Report a single, skipped placeholder test for this class, so that the class is still 59 // visible as skipped in test results. 60 notifier.fireTestIgnored( 61 Description.createTestDescription(klass, "skippedClassForDevSdkMismatch")) 62 } 63 64 override fun getDescription(): Description { 65 return baseRunner?.description ?: Description.createSuiteDescription(klass) 66 } 67 68 override fun testCount(): Int { 69 // When ignoring the tests, a skipped placeholder test is reported, so test count is 1. 70 return baseRunner?.testCount() ?: 1 71 } 72 }