Skip to content

Commit

Permalink
Add annotation for methods not compatible with <API 26
Browse files Browse the repository at this point in the history
There is no good replacement method for them in lower API level
  • Loading branch information
XichengSpencer committed Feb 23, 2024
1 parent a489412 commit 0c1b62a
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.oppia.android.testing.espresso

import android.graphics.drawable.GradientDrawable
import android.os.Build
import android.view.View
import androidx.annotation.RequiresApi
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.TypeSafeMatcher
Expand All @@ -19,6 +21,7 @@ class GenericViewMatchers {
* Returns a [Matcher] that verifies a view has a fully opaque background. The view is expected
* to have a [GradientDrawable] background.
*/
@RequiresApi(Build.VERSION_CODES.N)
fun withOpaqueBackground(): Matcher<View> = withColorBackgroundMatching(
descriptionSuffix = "an opaque background"
) { color -> color?.extractAlpha() == 0xff }
Expand All @@ -27,6 +30,7 @@ class GenericViewMatchers {
* Returns a [Matcher] with the specified description suffix and color matcher, matching against
* filled background colors of views.
*/
@RequiresApi(Build.VERSION_CODES.N)
private fun withColorBackgroundMatching(
@Suppress("SameParameterValue") descriptionSuffix: String,
colorMatcher: (Long?) -> Boolean
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.oppia.android.testing.junit

import android.os.Build
import androidx.annotation.RequiresApi
import org.junit.runner.Description
import org.junit.runner.Runner
import org.junit.runner.manipulation.Filter
Expand Down Expand Up @@ -76,6 +78,7 @@ import kotlin.reflect.KClass
* contain (thus they should be treated as undefined outside of tests that specific define their
* value via [Iteration]).
*/
@RequiresApi(Build.VERSION_CODES.N)
class OppiaParameterizedTestRunner(private val testClass: Class<*>) : Suite(testClass, listOf()) {
private val parameterizedMethods = computeParameterizedMethods()
private val selectedRunnerClass by lazy { fetchSelectedRunnerPlatformClass() }
Expand All @@ -85,16 +88,24 @@ class OppiaParameterizedTestRunner(private val testClass: Class<*>) : Suite(test
parameterizedMethods.flatMap { (methodName, method) ->
method.iterationNames.map { iterationName ->
ProxyParameterizedTestRunner(
selectedRunnerClass, testClass, parameterizedMethods, methodName, iterationName
selectedRunnerClass,
testClass,
parameterizedMethods,
methodName,
iterationName
)
}
} + ProxyParameterizedTestRunner(
selectedRunnerClass, testClass, parameterizedMethods, methodName = null
selectedRunnerClass,
testClass,
parameterizedMethods,
methodName = null
)
}

override fun getChildren(): MutableList<Runner> = childrenRunners.toMutableList()

@RequiresApi(Build.VERSION_CODES.N)
private fun computeParameterizedMethods(): Map<String, ParameterizedMethod> {
val fieldsAndParsers = fetchParameterizedFields().map { field ->
val valueParser = ParameterValue.createParserForField(field)
Expand Down Expand Up @@ -184,12 +195,14 @@ class OppiaParameterizedTestRunner(private val testClass: Class<*>) : Suite(test
}.associateBy { it.methodName }
}

@RequiresApi(Build.VERSION_CODES.N)
private fun fetchParameterizedFields(): List<Field> {
return testClass.declaredFields.mapNotNull { field ->
field.getDeclaredAnnotation(Parameter::class.java)?.let { field }
}
}

@RequiresApi(Build.VERSION_CODES.N)
private fun fetchParameterizedMethodDeclarations(): List<ParameterizedMethodDeclaration> {
return testClass.declaredMethods.mapNotNull { method ->
method.getDeclaredAnnotationsByType(Iteration::class.java).map { parameters ->
Expand All @@ -208,6 +221,7 @@ class OppiaParameterizedTestRunner(private val testClass: Class<*>) : Suite(test
}
}

@RequiresApi(Build.VERSION_CODES.N)
private fun fetchSelectedRunnerPlatformClass(): Class<*> {
return checkNotNull(testClass.getDeclaredAnnotation(SelectRunnerPlatform::class.java)) {
"All suites using OppiaParameterizedTestRunner must declare their base platform runner" +
Expand Down Expand Up @@ -239,15 +253,17 @@ class OppiaParameterizedTestRunner(private val testClass: Class<*>) : Suite(test
* - [Float]s
* - [Double]s
*/
@Target(AnnotationTarget.FIELD) annotation class Parameter
@Target(AnnotationTarget.FIELD)
annotation class Parameter

/**
* Specifies that a method in a test that uses a [OppiaParameterizedTestRunner] runner should be
* run multiple times for each [Iteration] specified in the [value] iterations list.
*
* See the KDoc for the runner for example code.
*/
@Target(AnnotationTarget.FUNCTION) annotation class RunParameterized(vararg val value: Iteration)
@Target(AnnotationTarget.FUNCTION)
annotation class RunParameterized(vararg val value: Iteration)

// TODO(#4120): Migrate to Kotlin @Repeatable once Kotlin 1.6 is used (see:
// https://youtrack.jetbrains.com/issue/KT-12794).
Expand Down Expand Up @@ -300,7 +316,10 @@ class OppiaParameterizedTestRunner(private val testClass: Class<*>) : Suite(test
private fun constructDelegate(): Any {
val constructor =
runnerClass.getConstructor(
Class::class.java, Map::class.java, String::class.java, String::class.java
Class::class.java,
Map::class.java,
String::class.java,
String::class.java
)
return constructor.newInstance(testClass, parameterizedMethods, methodName, iterationName)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.oppia.android.testing.threading

import android.os.Build
import androidx.annotation.RequiresApi
import org.oppia.android.testing.time.FakeSystemClock
import java.lang.reflect.Method
import java.time.Duration
Expand Down Expand Up @@ -34,6 +36,7 @@ class TestCoroutineDispatchersRobolectricImpl @Inject constructor(
} while (hasPendingCompletableTasks())
}

@RequiresApi(Build.VERSION_CODES.O)
override fun advanceTimeBy(delayTimeMillis: Long) {
var remainingDelayMillis = delayTimeMillis
while (remainingDelayMillis > 0) {
Expand All @@ -49,6 +52,7 @@ class TestCoroutineDispatchersRobolectricImpl @Inject constructor(
}
}

@RequiresApi(Build.VERSION_CODES.O)
override fun advanceUntilIdle() {
// First, run through all tasks that are currently pending and can be run immediately.
runCurrent()
Expand All @@ -67,6 +71,7 @@ class TestCoroutineDispatchersRobolectricImpl @Inject constructor(
}
}

@RequiresApi(Build.VERSION_CODES.O)
private fun advanceToNextFutureTask(
currentTimeMillis: Long,
maxDelayMs: Long = Long.MAX_VALUE
Expand Down Expand Up @@ -94,6 +99,7 @@ class TestCoroutineDispatchersRobolectricImpl @Inject constructor(
}

/** Returns whether any of the dispatchers have any tasks to run, including in the future. */
@RequiresApi(Build.VERSION_CODES.O)
private fun hasPendingTasks(): Boolean {
return backgroundTestDispatcher.hasPendingTasks() ||
blockingTestDispatcher.hasPendingTasks() ||
Expand All @@ -107,6 +113,7 @@ class TestCoroutineDispatchersRobolectricImpl @Inject constructor(
!uiTaskCoordinator.isIdle()
}

@RequiresApi(Build.VERSION_CODES.O)
private fun getNextFutureTaskTimeMillis(timeMillis: Long): Long? {
val nextBackgroundFutureTaskTimeMills =
backgroundTestDispatcher.getNextFutureTaskCompletionTimeMillis(timeMillis)
Expand All @@ -120,6 +127,7 @@ class TestCoroutineDispatchersRobolectricImpl @Inject constructor(
return futureTimes.firstOrNull()
}

@RequiresApi(Build.VERSION_CODES.O)
private fun getNextUiThreadFutureTaskTimeMillis(timeMillis: Long): Long? {
return uiTaskCoordinator.getNextUiThreadFutureTaskTimeMillis(timeMillis)
}
Expand All @@ -139,6 +147,7 @@ class TestCoroutineDispatchersRobolectricImpl @Inject constructor(
idleMethod.invoke(shadowUiLooper)
}

@RequiresApi(Build.VERSION_CODES.O)
fun getNextUiThreadFutureTaskTimeMillis(timeMillis: Long): Long? {
val nextScheduledTime = nextScheduledTimeMethod.invoke(shadowUiLooper) as Duration
val delayMs = nextScheduledTime.toMillis()
Expand Down

0 comments on commit 0c1b62a

Please sign in to comment.