• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }