1 // Copyright 2015 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base.test; 6 7 import android.app.Instrumentation; 8 import android.content.Context; 9 import android.os.Bundle; 10 import android.os.SystemClock; 11 12 import junit.framework.TestCase; 13 import junit.framework.TestResult; 14 15 import org.chromium.base.Log; 16 import org.chromium.base.test.util.SkipCheck; 17 18 import java.lang.reflect.Method; 19 import java.util.ArrayList; 20 import java.util.List; 21 22 /** 23 * A test result that can skip tests. 24 */ 25 public class BaseTestResult extends TestResult { 26 private static final String TAG = "base_test"; 27 28 private static final int SLEEP_INTERVAL_MS = 50; 29 private static final int WAIT_DURATION_MS = 5000; 30 31 private final Instrumentation mInstrumentation; 32 private final List<SkipCheck> mSkipChecks; 33 private final List<PreTestHook> mPreTestHooks; 34 35 /** 36 * Creates an instance of BaseTestResult. 37 */ BaseTestResult(Instrumentation instrumentation)38 public BaseTestResult(Instrumentation instrumentation) { 39 mSkipChecks = new ArrayList<>(); 40 mPreTestHooks = new ArrayList<>(); 41 mInstrumentation = instrumentation; 42 } 43 44 /** 45 * An interface for classes that have some code to run before a test. They run after 46 * {@link SkipCheck}s. Provides access to the test method (and the annotations defined for it) 47 * and the instrumentation context. 48 */ 49 public interface PreTestHook { 50 /** 51 * @param targetContext the instrumentation context that will be used during the test. 52 * @param testMethod the test method to be run. 53 */ run(Context targetContext, Method testMethod)54 public void run(Context targetContext, Method testMethod); 55 } 56 57 /** 58 * Adds a check for whether a test should run. 59 * 60 * @param skipCheck The check to add. 61 */ addSkipCheck(SkipCheck skipCheck)62 public void addSkipCheck(SkipCheck skipCheck) { 63 mSkipChecks.add(skipCheck); 64 } 65 66 /** 67 * Adds hooks that will be executed before each test that runs. 68 * 69 * @param preTestHook The hook to add. 70 */ addPreTestHook(PreTestHook preTestHook)71 public void addPreTestHook(PreTestHook preTestHook) { 72 mPreTestHooks.add(preTestHook); 73 } 74 shouldSkip(TestCase test)75 protected boolean shouldSkip(TestCase test) { 76 for (SkipCheck s : mSkipChecks) { 77 if (s.shouldSkip(test)) return true; 78 } 79 return false; 80 } 81 runPreTestHooks(TestCase test)82 private void runPreTestHooks(TestCase test) { 83 try { 84 Method testMethod = test.getClass().getMethod(test.getName()); 85 Context targetContext = getTargetContext(); 86 87 for (PreTestHook hook : mPreTestHooks) { 88 hook.run(targetContext, testMethod); 89 } 90 } catch (NoSuchMethodException e) { 91 Log.e(TAG, "Unable to run pre test hooks.", e); 92 } 93 } 94 95 @Override run(TestCase test)96 protected void run(TestCase test) { 97 runPreTestHooks(test); 98 99 if (shouldSkip(test)) { 100 startTest(test); 101 102 Bundle skipResult = new Bundle(); 103 skipResult.putString("class", test.getClass().getName()); 104 skipResult.putString("test", test.getName()); 105 skipResult.putBoolean("test_skipped", true); 106 mInstrumentation.sendStatus(0, skipResult); 107 108 endTest(test); 109 } else { 110 super.run(test); 111 } 112 } 113 114 /** 115 * Gets the target context. 116 * 117 * On older versions of Android, getTargetContext() may initially return null, so we have to 118 * wait for it to become available. 119 * 120 * @return The target {@link Context} if available; null otherwise. 121 */ getTargetContext()122 public Context getTargetContext() { 123 Context targetContext = mInstrumentation.getTargetContext(); 124 try { 125 long startTime = SystemClock.uptimeMillis(); 126 // TODO(jbudorick): Convert this to CriteriaHelper once that moves to base/. 127 while (targetContext == null 128 && SystemClock.uptimeMillis() - startTime < WAIT_DURATION_MS) { 129 Thread.sleep(SLEEP_INTERVAL_MS); 130 targetContext = mInstrumentation.getTargetContext(); 131 } 132 } catch (InterruptedException e) { 133 Log.e(TAG, "Interrupted while attempting to initialize the command line."); 134 } 135 return targetContext; 136 } 137 } 138