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