1 /* 2 * Copyright (C) 2019 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 package android.platform.test.util; 17 18 import android.os.Bundle; 19 import androidx.annotation.VisibleForTesting; 20 import androidx.test.InstrumentationRegistry; 21 22 import java.lang.reflect.Constructor; 23 24 import org.junit.runner.Runner; 25 import org.junit.runners.JUnit4; 26 import org.junit.runners.model.RunnerBuilder; 27 28 /** 29 * A {@link RunnerBuilder} that enables using a specified runner to run tests in a suite. 30 * 31 * <p>Usage: When instrumenting with AndroidJUnitRunner, use 32 * 33 * <pre>-e runnerBuilder android.platform.test.longevity.HealthRunnerBuilder -e use-runner 34 * (name_of_runner) 35 * 36 * <p>The runner name can be supplied with the fully qualified class name of the runner. It also has 37 * to be a {@link Runner} class and statically included in the APK. 38 */ 39 public class HealthRunnerBuilder extends RunnerBuilder { 40 @VisibleForTesting static final String RUNNER_OPTION = "use-runner"; 41 // The runner class to use as specified by the option. Defaults to the JUnit4 runner. 42 private Class<?> mRunnerClass = JUnit4.class; 43 HealthRunnerBuilder()44 public HealthRunnerBuilder() { 45 this(InstrumentationRegistry.getArguments()); 46 } 47 48 @VisibleForTesting HealthRunnerBuilder(Bundle args)49 HealthRunnerBuilder(Bundle args) { 50 String runnerName = args.getString(RUNNER_OPTION); 51 if (runnerName != null) { 52 Class<?> loadedClass = null; 53 try { 54 loadedClass = HealthRunnerBuilder.class.getClassLoader().loadClass(runnerName); 55 } catch (ClassNotFoundException e) { 56 throw new RuntimeException( 57 String.format( 58 "Could not find class with fully qualified name %s.", runnerName)); 59 } 60 // Ensure that the class found is a Runner. 61 if (loadedClass != null && Runner.class.isAssignableFrom(loadedClass)) { 62 mRunnerClass = loadedClass; 63 } else { 64 throw new RuntimeException(String.format("Class %s is not a runner.", loadedClass)); 65 } 66 } 67 } 68 69 @Override runnerForClass(Class<?> testClass)70 public Runner runnerForClass(Class<?> testClass) throws Throwable { 71 try { 72 Constructor<?> runnerConstructor = mRunnerClass.getConstructor(Class.class); 73 // Cast is safe as getRunnerClass() ensures that instantiations of its return type are 74 // subclasses of Runner. 75 return (Runner) runnerConstructor.newInstance(testClass); 76 } catch (NoSuchMethodException e) { 77 throw new RuntimeException( 78 String.format( 79 "Runner %s cannot be instantiated with the test class alone.", 80 mRunnerClass), 81 e); 82 } catch (SecurityException e) { 83 throw new RuntimeException(e); 84 } 85 } 86 } 87