TestSize.java
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.test.internal.runner;
import android.support.annotation.VisibleForTesting;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.junit.runner.Description;
/**
* This class represents a test size qualifier which can be used to filter out tests from a test
* suite.
*
* <p>It quietly handles runner size filter annotations and old platform size annotations.
*/
public final class TestSize {
/** @see androidx.test.filters.SmallTest */
public static final TestSize SMALL =
new TestSize(
"small",
androidx.test.filters.SmallTest.class,
android.test.suitebuilder.annotation.SmallTest.class,
200 /* in ms */);
/** @see androidx.test.filters.MediumTest */
public static final TestSize MEDIUM =
new TestSize(
"medium",
androidx.test.filters.MediumTest.class,
android.test.suitebuilder.annotation.MediumTest.class,
1000 /* in ms */);
/** @see androidx.test.filters.LargeTest */
public static final TestSize LARGE =
new TestSize(
"large",
androidx.test.filters.LargeTest.class,
android.test.suitebuilder.annotation.LargeTest.class,
Float.MAX_VALUE /* no threshold */);
/**
* A "null object" that is returned in case no test size matches, to avoid a null return value.
*/
public static final TestSize NONE = new TestSize("", null, null, 0);
private static final Set<TestSize> ALL_SIZES =
Collections.unmodifiableSet(new HashSet<>(Arrays.asList(SMALL, MEDIUM, LARGE)));
private final String mSizeQualifierName;
private final Class<? extends Annotation> mPlatformAnnotationClass;
private final Class<? extends Annotation> mRunnerFilterAnnotationClass;
/**
* This value the maximum allowed runtime (in ms) for a test included in the test size suite. It
* is used to make an educated guess at which size bucket a test belongs to.
*/
private final float mTestSizeRunTimeThreshold;
@VisibleForTesting
public TestSize(
String sizeQualifierName,
Class<? extends Annotation> platformAnnotationClass,
Class<? extends Annotation> runnerFilterAnnotationClass,
float testSizeRuntimeThreshold) {
mSizeQualifierName = sizeQualifierName;
mPlatformAnnotationClass = platformAnnotationClass;
mRunnerFilterAnnotationClass = runnerFilterAnnotationClass;
mTestSizeRunTimeThreshold = testSizeRuntimeThreshold;
}
/** @return the test size name */
public String getSizeQualifierName() {
return mSizeQualifierName;
}
/**
* @return true if the test method in the {@link Description} is annotated with the test size
* annotation class.
*/
public boolean testMethodIsAnnotatedWithTestSize(Description description) {
if (description.getAnnotation(mRunnerFilterAnnotationClass) != null
|| description.getAnnotation(mPlatformAnnotationClass) != null) {
// If the test method is annotated with a test size annotation include it
return true;
}
// Otherwise exclude it
return false;
}
/**
* @return true if the test class in the {@link Description} is annotated with the test size
* annotation.
*/
public boolean testClassIsAnnotatedWithTestSize(Description description) {
final Class<?> testClass = description.getTestClass();
if (null == testClass) {
return false;
}
if (testClass.isAnnotationPresent(mRunnerFilterAnnotationClass)
|| testClass.isAnnotationPresent(mPlatformAnnotationClass)) {
// If the test class is annotated with a test size annotation include it.
return true;
}
return false;
}
/** @return the suite run time threshold for a given test size. */
public float getRunTimeThreshold() {
return mTestSizeRunTimeThreshold;
}
/**
* Maps a runtime to a test size.
*
* @param testRuntime the runtime of the test
* @return the test size which was mapped to the runtime
*/
public static TestSize getTestSizeForRunTime(float testRuntime) {
if (runTimeSmallerThanThreshold(testRuntime, SMALL.getRunTimeThreshold())) {
return SMALL;
} else if (runTimeSmallerThanThreshold(testRuntime, MEDIUM.getRunTimeThreshold())) {
return MEDIUM;
}
return LARGE;
}
/**
* @param annotationClass the test size annotation class
* @return true if the the test size annotation is valid
*/
public static boolean isAnyTestSize(Class<? extends Annotation> annotationClass) {
for (TestSize testSize : ALL_SIZES) {
if (testSize.getRunnerAnnotation() == annotationClass
|| testSize.getFrameworkAnnotation() == annotationClass) {
return true;
}
}
return false;
}
/**
* Creates a test size instance from a test size string. This method will return {@link
* TestSize#NONE} if the test size is unknown.
*/
public static TestSize fromString(final String testSize) {
TestSize testSizeFromString = NONE;
for (TestSize testSizeValue : ALL_SIZES) {
if (testSizeValue.getSizeQualifierName().equals(testSize)) {
testSizeFromString = testSizeValue;
}
}
return testSizeFromString;
}
/**
* Creates a test size instance from a {@link Description}. This method will return {@link
* TestSize#NONE} if the description does not contain any test size information.
*/
public static TestSize fromDescription(Description description) {
TestSize testSize = NONE;
// Match on method level first
for (TestSize testMethodSizeValue : ALL_SIZES) {
if (testMethodSizeValue.testMethodIsAnnotatedWithTestSize(description)) {
testSize = testMethodSizeValue;
break;
}
}
// If size annotation not matched on method level look at class level
if (NONE.equals(testSize)) {
for (TestSize testClassSizeValue : ALL_SIZES) {
if (testClassSizeValue.testClassIsAnnotatedWithTestSize(description)) {
testSize = testClassSizeValue;
break;
}
}
}
return testSize;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TestSize testSize = (TestSize) o;
return mSizeQualifierName.equals(testSize.mSizeQualifierName);
}
@Override
public int hashCode() {
return mSizeQualifierName.hashCode();
}
private static boolean runTimeSmallerThanThreshold(float testRuntime, float runtimeThreshold) {
return Float.compare(testRuntime, runtimeThreshold) < 0;
}
private Class<? extends Annotation> getFrameworkAnnotation() {
return mPlatformAnnotationClass;
}
private Class<? extends Annotation> getRunnerAnnotation() {
return mRunnerFilterAnnotationClass;
}
}