1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package android.testing; 16 17 import android.testing.TestableLooper.LooperFrameworkMethod; 18 import android.testing.TestableLooper.RunWithLooper; 19 20 import androidx.test.internal.runner.junit4.statement.RunAfters; 21 import androidx.test.internal.runner.junit4.statement.RunBefores; 22 import androidx.test.internal.runner.junit4.statement.UiThreadStatement; 23 24 import org.junit.After; 25 import org.junit.Before; 26 import org.junit.Test; 27 import org.junit.internal.runners.statements.FailOnTimeout; 28 import org.junit.runners.BlockJUnit4ClassRunner; 29 import org.junit.runners.model.FrameworkMethod; 30 import org.junit.runners.model.InitializationError; 31 import org.junit.runners.model.Statement; 32 33 import java.util.ArrayList; 34 import java.util.List; 35 36 /** 37 * A runner with support for extra annotations provided by the Testables library. 38 * @see UiThreadTest 39 * @see TestableLooper.RunWithLooper 40 */ 41 public class AndroidTestingRunner extends BlockJUnit4ClassRunner { 42 43 private final long mTimeout; 44 private final Class<?> mKlass; 45 AndroidTestingRunner(Class<?> klass)46 public AndroidTestingRunner(Class<?> klass) throws InitializationError { 47 super(klass); 48 mKlass = klass; 49 // Can't seem to get reference to timeout parameter from here, so set default to 10 mins. 50 mTimeout = 10 * 60 * 1000; 51 } 52 53 @Override methodInvoker(FrameworkMethod method, Object test)54 protected Statement methodInvoker(FrameworkMethod method, Object test) { 55 method = looperWrap(method, test, method); 56 final Statement statement = super.methodInvoker(method, test); 57 return shouldRunOnUiThread(method) ? new UiThreadStatement(statement, true) : statement; 58 } 59 withBefores(FrameworkMethod method, Object target, Statement statement)60 protected Statement withBefores(FrameworkMethod method, Object target, Statement statement) { 61 List befores = looperWrap(method, target, 62 this.getTestClass().getAnnotatedMethods(Before.class)); 63 return befores.isEmpty() ? statement : new RunBefores(method, statement, 64 befores, target); 65 } 66 withAfters(FrameworkMethod method, Object target, Statement statement)67 protected Statement withAfters(FrameworkMethod method, Object target, Statement statement) { 68 List afters = looperWrap(method, target, 69 this.getTestClass().getAnnotatedMethods(After.class)); 70 return afters.isEmpty() ? statement : new RunAfters(method, statement, afters, 71 target); 72 } 73 withPotentialTimeout(FrameworkMethod method, Object test, Statement next)74 protected Statement withPotentialTimeout(FrameworkMethod method, Object test, Statement next) { 75 long timeout = this.getTimeout(method.getAnnotation(Test.class)); 76 if (timeout <= 0L && mTimeout > 0L) { 77 timeout = mTimeout; 78 } 79 80 return timeout <= 0L ? next : new FailOnTimeout(next, timeout); 81 } 82 getTimeout(Test annotation)83 private long getTimeout(Test annotation) { 84 return annotation == null ? 0L : annotation.timeout(); 85 } 86 looperWrap(FrameworkMethod method, Object test, List<FrameworkMethod> methods)87 protected List<FrameworkMethod> looperWrap(FrameworkMethod method, Object test, 88 List<FrameworkMethod> methods) { 89 RunWithLooper annotation = method.getAnnotation(RunWithLooper.class); 90 if (annotation == null) annotation = mKlass.getAnnotation(RunWithLooper.class); 91 if (annotation != null) { 92 methods = new ArrayList<>(methods); 93 for (int i = 0; i < methods.size(); i++) { 94 methods.set(i, LooperFrameworkMethod.get(methods.get(i), 95 annotation.setAsMainLooper(), test)); 96 } 97 } 98 return methods; 99 } 100 looperWrap(FrameworkMethod method, Object test, FrameworkMethod base)101 protected FrameworkMethod looperWrap(FrameworkMethod method, Object test, 102 FrameworkMethod base) { 103 RunWithLooper annotation = method.getAnnotation(RunWithLooper.class); 104 if (annotation == null) annotation = mKlass.getAnnotation(RunWithLooper.class); 105 if (annotation != null) { 106 return LooperFrameworkMethod.get(base, annotation.setAsMainLooper(), test); 107 } 108 return base; 109 } 110 shouldRunOnUiThread(FrameworkMethod method)111 public boolean shouldRunOnUiThread(FrameworkMethod method) { 112 if (mKlass.getAnnotation(UiThreadTest.class) != null) { 113 return true; 114 } else { 115 return UiThreadStatement.shouldRunOnUiThread(method); 116 } 117 } 118 } 119