1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php 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 17 package com.android.ide.eclipse.adt.internal.launch.junit.runtime; 18 19 import com.android.ddmlib.testrunner.ITestRunListener; 20 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; 21 import com.android.ddmlib.testrunner.TestIdentifier; 22 import com.android.ide.eclipse.adt.AdtPlugin; 23 24 import org.eclipse.jdt.internal.junit.runner.MessageIds; 25 import org.eclipse.jdt.internal.junit.runner.RemoteTestRunner; 26 import org.eclipse.jdt.internal.junit.runner.TestExecution; 27 import org.eclipse.jdt.internal.junit.runner.TestReferenceFailure; 28 29 /** 30 * Supports Eclipse JUnit execution of Android tests. 31 * <p/> 32 * Communicates back to a Eclipse JDT JUnit client via a socket connection. 33 * 34 * @see org.eclipse.jdt.internal.junit.runner.RemoteTestRunner for more details on the protocol 35 */ 36 @SuppressWarnings("restriction") 37 public class RemoteAdtTestRunner extends RemoteTestRunner { 38 39 private AndroidJUnitLaunchInfo mLaunchInfo; 40 private TestExecution mExecution; 41 42 /** 43 * Initialize the JDT JUnit test runner parameters from the {@code args}. 44 * 45 * @param args name-value pair of arguments to pass to parent JUnit runner. 46 * @param launchInfo the Android specific test launch info 47 */ init(String[] args, AndroidJUnitLaunchInfo launchInfo)48 protected void init(String[] args, AndroidJUnitLaunchInfo launchInfo) { 49 defaultInit(args); 50 mLaunchInfo = launchInfo; 51 } 52 53 /** 54 * Runs a set of tests, and reports back results using parent class. 55 * <p/> 56 * JDT Unit expects to be sent data in the following sequence: 57 * <ol> 58 * <li>The total number of tests to be executed.</li> 59 * <li>The test 'tree' data about the tests to be executed, which is composed of the set of 60 * test class names, the number of tests in each class, and the names of each test in the 61 * class.</li> 62 * <li>The test execution result for each test method. Expects individual notifications of 63 * the test execution start, any failures, and the end of the test execution.</li> 64 * <li>The end of the test run, with its elapsed time.</li> 65 * </ol> 66 * <p/> 67 * In order to satisfy this, this method performs two actual Android instrumentation runs. 68 * The first is a 'log only' run that will collect the test tree data, without actually 69 * executing the tests, and send it back to JDT JUnit. The second is the actual test execution, 70 * whose results will be communicated back in real-time to JDT JUnit. 71 * 72 * @param testClassNames ignored - the AndroidJUnitLaunchInfo will be used to determine which 73 * tests to run. 74 * @param testName ignored 75 * @param execution used to report test progress 76 */ 77 @Override runTests(String[] testClassNames, String testName, TestExecution execution)78 public void runTests(String[] testClassNames, String testName, TestExecution execution) { 79 // hold onto this execution reference so it can be used to report test progress 80 mExecution = execution; 81 82 RemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(mLaunchInfo.getAppPackage(), 83 mLaunchInfo.getRunner(), mLaunchInfo.getDevice()); 84 85 if (mLaunchInfo.getTestClass() != null) { 86 if (mLaunchInfo.getTestMethod() != null) { 87 runner.setMethodName(mLaunchInfo.getTestClass(), mLaunchInfo.getTestMethod()); 88 } else { 89 runner.setClassName(mLaunchInfo.getTestClass()); 90 } 91 } 92 93 if (mLaunchInfo.getTestPackage() != null) { 94 runner.setTestPackageName(mLaunchInfo.getTestPackage()); 95 } 96 97 // set log only to first collect test case info, so Eclipse has correct test case count/ 98 // tree info 99 runner.setLogOnly(true); 100 TestCollector collector = new TestCollector(); 101 runner.run(collector); 102 if (collector.getErrorMessage() != null) { 103 // error occurred during test collection. 104 reportError(collector.getErrorMessage()); 105 // abort here 106 notifyTestRunEnded(0); 107 return; 108 } 109 notifyTestRunStarted(collector.getTestCaseCount()); 110 collector.sendTrees(this); 111 112 // now do real execution 113 runner.setLogOnly(false); 114 if (mLaunchInfo.isDebugMode()) { 115 runner.setDebug(true); 116 } 117 runner.run(new TestRunListener()); 118 } 119 120 /** 121 * Main entry method to run tests 122 * 123 * @param programArgs JDT JUnit program arguments to be processed by parent 124 * @param junitInfo the {@link AndroidJUnitLaunchInfo} containing info about this test ru 125 */ runTests(String[] programArgs, AndroidJUnitLaunchInfo junitInfo)126 public void runTests(String[] programArgs, AndroidJUnitLaunchInfo junitInfo) { 127 init(programArgs, junitInfo); 128 run(); 129 } 130 131 /** 132 * Stop the current test run. 133 */ terminate()134 public void terminate() { 135 stop(); 136 } 137 138 @Override stop()139 protected void stop() { 140 if (mExecution != null) { 141 mExecution.stop(); 142 } 143 } 144 notifyTestRunEnded(long elapsedTime)145 private void notifyTestRunEnded(long elapsedTime) { 146 // copy from parent - not ideal, but method is private 147 sendMessage(MessageIds.TEST_RUN_END + elapsedTime); 148 flush(); 149 //shutDown(); 150 } 151 152 /** 153 * @param errorMessage 154 */ reportError(String errorMessage)155 private void reportError(String errorMessage) { 156 AdtPlugin.printErrorToConsole(mLaunchInfo.getProject(), 157 String.format("Test run failed: %s", errorMessage)); 158 // is this needed? 159 //notifyTestRunStopped(-1); 160 } 161 162 /** 163 * TestRunListener that communicates results in real-time back to JDT JUnit 164 */ 165 private class TestRunListener implements ITestRunListener { 166 167 /* (non-Javadoc) 168 * @see com.android.ddmlib.testrunner.ITestRunListener#testEnded(com.android.ddmlib.testrunner.TestIdentifier) 169 */ testEnded(TestIdentifier test)170 public void testEnded(TestIdentifier test) { 171 mExecution.getListener().notifyTestEnded(new TestCaseReference(test)); 172 } 173 174 /* (non-Javadoc) 175 * @see com.android.ddmlib.testrunner.ITestRunListener#testFailed(com.android.ddmlib.testrunner.ITestRunListener.TestFailure, com.android.ddmlib.testrunner.TestIdentifier, java.lang.String) 176 */ testFailed(TestFailure status, TestIdentifier test, String trace)177 public void testFailed(TestFailure status, TestIdentifier test, String trace) { 178 String statusString; 179 if (status == TestFailure.ERROR) { 180 statusString = MessageIds.TEST_ERROR; 181 } else { 182 statusString = MessageIds.TEST_FAILED; 183 } 184 TestReferenceFailure failure = 185 new TestReferenceFailure(new TestCaseReference(test), 186 statusString, trace, null); 187 mExecution.getListener().notifyTestFailed(failure); 188 } 189 190 /* (non-Javadoc) 191 * @see com.android.ddmlib.testrunner.ITestRunListener#testRunEnded(long) 192 */ testRunEnded(long elapsedTime)193 public void testRunEnded(long elapsedTime) { 194 notifyTestRunEnded(elapsedTime); 195 AdtPlugin.printToConsole(mLaunchInfo.getProject(), "Test run complete"); 196 } 197 198 /* (non-Javadoc) 199 * @see com.android.ddmlib.testrunner.ITestRunListener#testRunFailed(java.lang.String) 200 */ testRunFailed(String errorMessage)201 public void testRunFailed(String errorMessage) { 202 reportError(errorMessage); 203 } 204 205 /* (non-Javadoc) 206 * @see com.android.ddmlib.testrunner.ITestRunListener#testRunStarted(int) 207 */ testRunStarted(int testCount)208 public void testRunStarted(int testCount) { 209 // ignore 210 } 211 212 /* (non-Javadoc) 213 * @see com.android.ddmlib.testrunner.ITestRunListener#testRunStopped(long) 214 */ testRunStopped(long elapsedTime)215 public void testRunStopped(long elapsedTime) { 216 notifyTestRunStopped(elapsedTime); 217 AdtPlugin.printToConsole(mLaunchInfo.getProject(), "Test run stopped"); 218 } 219 220 /* (non-Javadoc) 221 * @see com.android.ddmlib.testrunner.ITestRunListener#testStarted(com.android.ddmlib.testrunner.TestIdentifier) 222 */ testStarted(TestIdentifier test)223 public void testStarted(TestIdentifier test) { 224 TestCaseReference testId = new TestCaseReference(test); 225 mExecution.getListener().notifyTestStarted(testId); 226 } 227 } 228 }