Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(476)

Side by Side Diff: testing/android/java/src/org/chromium/test/driver/OnDeviceInstrumentationDriver.java

Issue 1034053002: [Android] Add an out-of-app instrumentation driver APK. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: the rename Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.test.driver;
6
7 import android.app.Activity;
8 import android.app.Instrumentation;
9 import android.content.ComponentName;
10 import android.content.Intent;
11 import android.content.ServiceConnection;
12 import android.os.Bundle;
13 import android.os.Environment;
14 import android.os.Handler;
15 import android.os.IBinder;
16 import android.os.Looper;
17 import android.os.Message;
18 import android.os.Messenger;
19 import android.os.RemoteException;
20 import android.test.InstrumentationTestRunner;
21 import android.util.Log;
22
23 import org.chromium.test.passenger.OnDeviceInstrumentationPassenger;
24 import org.chromium.test.reporter.ReportingService;
25 import org.chromium.test.support.ResultsBundleGenerator;
26 import org.chromium.test.support.RobotiumBundleGenerator;
27
28 import java.io.BufferedReader;
29 import java.io.File;
30 import java.io.FileReader;
31 import java.io.IOException;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.regex.Pattern;
38
39 /**
40 * An Instrumentation that drives instrumentation tests from outside the app. (H ence the name.)
Yaron 2015/04/07 15:39:04 I think you can now drop "(Hence the name.)"
jbudorick 2015/04/07 19:24:45 oops, done.
41 */
42 public class OnDeviceInstrumentationDriver extends Instrumentation {
jbudorick 2015/04/07 15:09:22 Renamed: Outstrumentation -> OnDeviceInstrumenta
Yaron 2015/04/07 15:39:04 I think it would be helpful to have a mini-dd or o
jbudorick 2015/04/07 19:24:45 Can do. I've got it on a whiteboard right now and
43
44 private static final String TAG = "OnDeviceInstrumentationDriver";
45
46 private static final String EXTRA_TEST_LIST =
47 "org.chromium.test.driver.OnDeviceInstrumentationDriver.TestList";
48 private static final String EXTRA_TEST_LIST_FILE =
49 "org.chromium.test.driver.OnDeviceInstrumentationDriver.TestListFile ";
50 private static final String EXTRA_TARGET_PACKAGE =
51 "org.chromium.test.driver.OnDeviceInstrumentationDriver.TargetPackag e";
52 private static final String EXTRA_TARGET_CLASS =
53 "org.chromium.test.driver.OnDeviceInstrumentationDriver.TargetClass" ;
54
55 private static final Pattern COMMA = Pattern.compile(",");
56 private static final int SERVICE_WAIT_TIMEOUT = 5000; // ms
57 private static final int TEST_WAIT_TIMEOUT = 10000; // ms
58
59 private boolean mDriverStarted;
60 private Thread mDriverThread;
61 private Bundle mTargetArgs;
62 private String mTargetClass;
63 private String mTargetPackage;
64 private List<String> mTestClasses;
65
66 /** Parse any arguments and prepare to run tests.
67
68 @param arguments The arguments to parse.
69 */
70 @Override
71 public void onCreate(Bundle arguments) {
72 mTargetArgs = new Bundle(arguments);
73 mTargetPackage = arguments.getString(EXTRA_TARGET_PACKAGE);
74 if (mTargetPackage == null) {
75 fail("No target package.");
76 return;
77 }
78 mTargetArgs.remove(EXTRA_TARGET_PACKAGE);
79
80 mTargetClass = arguments.getString(EXTRA_TARGET_CLASS);
81 if (mTargetClass == null) {
82 fail("No target class.");
83 return;
84 }
85 mTargetArgs.remove(EXTRA_TARGET_CLASS);
86
87 mTestClasses = new ArrayList<String>();
88 String testList = arguments.getString(EXTRA_TEST_LIST);
89 if (testList != null) {
90 mTestClasses.addAll(Arrays.asList(COMMA.split(testList)));
91 mTargetArgs.remove(EXTRA_TEST_LIST);
92 }
93
94 String testListFilePath = arguments.getString(EXTRA_TEST_LIST_FILE);
95 if (testListFilePath != null) {
96 File testListFile = new File(Environment.getExternalStorageDirectory (),
97 testListFilePath);
98 try {
99 BufferedReader testListFileReader =
100 new BufferedReader(new FileReader(testListFile));
101 String test;
102 while ((test = testListFileReader.readLine()) != null) {
103 mTestClasses.add(test);
104 }
105 testListFileReader.close();
106 } catch (IOException e) {
107 Log.e(TAG, "Error reading " + testListFile.getAbsolutePath(), e) ;
108 }
109 mTargetArgs.remove(EXTRA_TEST_LIST_FILE);
110 }
111
112 if (mTestClasses.isEmpty()) {
113 fail("No tests.");
114 return;
115 }
116
117 mDriverThread = new Thread(
118 new Driver(mTargetPackage, mTargetClass, mTargetArgs, mTestClass es));
119
120 start();
121 }
122
123 /** Start running tests. */
124 @Override
125 public void onStart() {
126 super.onStart();
127
128 getContext().startService(new Intent(getContext(), ReportingService.clas s));
129
130 // Start the driver on its own thread s.t. it can block while the main t hread's
131 // Looper receives and handles messages.
132 if (!mDriverStarted) {
133 mDriverThread.start();
134 mDriverStarted = true;
135 }
136 }
137
138 /** Clean up the reporting service. */
139 @Override
140 public void onDestroy() {
141 getContext().stopService(new Intent(getContext(), ReportingService.class ));
142 super.onDestroy();
143 }
144
145 private class Driver implements Runnable {
146
147 private static final String TAG = OnDeviceInstrumentationDriver.TAG + ". Driver";
148
149 private Bundle mTargetArgs;
150 private String mTargetClass;
151 private String mTargetPackage;
152 private List<String> mTestClasses;
153 private ReportHandler mHandler;
154
155 public Driver(String targetPackage, String targetClass, Bundle targetArg s,
156 List<String> testClasses) {
157 mTargetPackage = targetPackage;
158 mTargetClass = targetClass;
159 mTargetArgs = targetArgs;
160 mTestClasses = testClasses;
161 mHandler = new ReportHandler(Looper.getMainLooper());
162 }
163
164 private class ReportingServiceConnection implements ServiceConnection {
165
166 private static final String TAG = Driver.TAG + ".ReportingServiceCon nection";
167
168 private Messenger mReportingService;
169 private Object mServiceLock = new Object();
170
171 /** Called when a service has been connected.
172
173 @param name The name of the service that has been connected.
174 @param service The IBinder for the service.
175 */
176 @Override
177 public void onServiceConnected(ComponentName name, IBinder service) {
178 synchronized (mServiceLock) {
179 mReportingService = new Messenger(service);
180 mServiceLock.notify();
181 }
182 }
183
184 /** Registers a {@link android.os.Messenger} with the
185 {@link org.chromium.test.driver.reporter.ReportingService}.
186
187 This lets the reporting service know that it should relay messag es it receives
188 to the provided receiver.
189
190 @param receiver The object to register with the reporting servic e.
191 */
192 public void registerWithReportingService(Messenger receiver)
193 throws InterruptedException, RemoteException {
194 synchronized (mServiceLock) {
195 Log.i(TAG, "acquired service lock");
196 while (mReportingService == null) {
197 Log.i(TAG, "waiting for service connection.");
198 mServiceLock.wait(SERVICE_WAIT_TIMEOUT);
199 }
200
201 Log.i(TAG, "reporting service non-null");
202
203 Message registration = Message.obtain();
204 registration.what = ReportingService.MSG_REGISTER_RECEIVER;
205 registration.replyTo = receiver;
206 mReportingService.send(registration);
207
208 Log.i(TAG, "sent registration message");
209 }
210 }
211
212 /** Unregisters a {@link android.os.Messenger} with the
213 {@link org.chromium.test.driver.reporter.ReportingService}.
214
215 This lets the reporting service know that it should no longer re lay messages it
216 receives to this object.
217
218 @param receiver The object to unregister from the reporting serv ice.
219 */
220 public void unregisterWithReportingService(Messenger receiver)
221 throws InterruptedException, RemoteException {
222 synchronized (mServiceLock) {
223 if (mReportingService == null) return;
224
225 Message registration = Message.obtain();
226 registration.what = ReportingService.MSG_UNREGISTER_RECEIVER ;
227 registration.replyTo = receiver;
228 mReportingService.send(registration);
229 }
230 }
231
232 /** Called when a service has disconnected.
233
234 @param name The name of the service that has disconnected.
235 */
236 @Override
237 public void onServiceDisconnected(ComponentName name) {
238 synchronized (mServiceLock) {
239 mReportingService = null;
240 mServiceLock.notify();
241 }
242 }
243 }
244
245 private class ReportHandler extends Handler {
246 private static final String TAG = Driver.TAG + ".ReportHandler";
247
248 private final Object mLock = new Object();
249 private final Map<String, ResultsBundleGenerator.TestResult> mFinish ed =
250 new HashMap<String, ResultsBundleGenerator.TestResult>();
251
252 public ReportHandler(Looper looper) {
253 super(looper);
254 }
255
256 /** Receive a message.
257
258 This does nothing unless the message is a test report from the
259 {@link org.chromium.test.driver.reporter.ReportingService}.
260
261 @param msg The message to handle.
262 */
263 @Override
264 public void handleMessage(Message msg) {
265 switch (msg.what) {
266 case ReportingService.MSG_REPORT_TEST_STARTED:
267 case ReportingService.MSG_REPORT_TEST_PASSED:
268 case ReportingService.MSG_REPORT_TEST_FAILED:
269 handleTestStatusMessage(msg);
270 break;
271 default:
272 super.handleMessage(msg);
273 break;
274 }
275 }
276
277 private void handleTestStatusMessage(Message msg) {
278 String testClass = msg.getData().getString(ReportingService.MSG_ DATA_TEST_CLASS);
279 String testMethod = msg.getData().getString(ReportingService.MSG _DATA_TEST_METHOD);
280 String testName = testClass + "#" + testMethod;
281 synchronized (mLock) {
282 switch (msg.what) {
283 case ReportingService.MSG_REPORT_TEST_STARTED:
284 testStarted(testClass, testMethod);
285 break;
286 case ReportingService.MSG_REPORT_TEST_PASSED:
287 mFinished.put(testName, ResultsBundleGenerator.TestR esult.PASSED);
288 testPassed(testClass, testMethod);
289 mLock.notify();
290 break;
291 case ReportingService.MSG_REPORT_TEST_FAILED:
292 mFinished.put(testName, ResultsBundleGenerator.TestR esult.FAILED);
293 testFailed(testClass, testMethod);
294 mLock.notify();
295 break;
296 default:
297 throw new IllegalArgumentException(
298 "Received unexpected message from reporting service: "
299 + msg.what);
300 }
301 }
302 }
303
304 /** Wait until the test with the given name has been reported as fin ished.
305
306 @param testName The name of the test to wait for.
307 */
308 public void waitForTestFinished(String testName) throws InterruptedE xception {
309 synchronized (mLock) {
310 while (!mFinished.containsKey(testName)) {
311 Log.i(TAG, "Waiting for " + testName + " to finish.");
312 mLock.wait(TEST_WAIT_TIMEOUT);
313 }
314 }
315 }
316
317 /** Get the test results.
318
319 @return A {@link android.os.Bundle} containing the results of al l reported tests.
320 */
321 public Bundle getResults() {
322 return new RobotiumBundleGenerator().generate(mFinished);
323 }
324 }
325
326 /** Run the tests. */
327 @Override
328 public void run() {
329 try {
330 ReportingServiceConnection c = new ReportingServiceConnection();
331 if (!getContext().bindService(
332 new Intent(getContext(), ReportingService.class), c, 0)) {
333 fail("Reporting service not bound.");
334 return;
335 }
336
337 Messenger receiver = new Messenger(mHandler);
338 c.registerWithReportingService(receiver);
339
340 for (String t : mTestClasses) {
341 Intent slaveIntent = new Intent();
342 slaveIntent.setComponent(new ComponentName(
343 mTargetPackage, OnDeviceInstrumentationPassenger.cla ss.getName()));
344 slaveIntent.putExtra(
345 OnDeviceInstrumentationPassenger.EXTRA_INSTRUMENTATI ON_PACKAGE,
346 mTargetPackage);
347 slaveIntent.putExtra(
348 OnDeviceInstrumentationPassenger.EXTRA_INSTRUMENTATI ON_CLASS,
349 mTargetClass);
350 slaveIntent.putExtra(OnDeviceInstrumentationPassenger.EXTRA_ TEST, t);
351 slaveIntent.putExtra(OnDeviceInstrumentationPassenger.EXTRA_ TARGET_ARGS,
352 mTargetArgs);
353 slaveIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
354
355 getContext().startActivity(slaveIntent);
356
357 mHandler.waitForTestFinished(t);
358 }
359
360 c.unregisterWithReportingService(receiver);
361 getContext().unbindService(c);
362 } catch (RemoteException e) {
363 fail("Error connecting to reporting service.", e);
364 return;
365 } catch (InterruptedException e) {
366 fail("Interrupted while running tests.", e);
367 return;
368 }
369 pass(mHandler.getResults());
370 }
371
372 }
373
374 private void fail(String reason) {
375 Log.e(TAG, reason);
376 failImpl(reason);
377 }
378
379 private void fail(String reason, Exception e) {
380 Log.e(TAG, reason, e);
381 failImpl(reason);
382 }
383
384 private void failImpl(String reason) {
385 Bundle b = new Bundle();
386 b.putString("reason", reason);
387 finish(Activity.RESULT_CANCELED, b);
388 }
389
390 private void pass(Bundle results) {
391 finish(Activity.RESULT_OK, results);
392 }
393
394 private void testStarted(String testClass, String testMethod) {
395 sendTestStatus(InstrumentationTestRunner.REPORT_VALUE_RESULT_START, test Class, testMethod);
396 }
397
398 private void testPassed(String testClass, String testMethod) {
399 sendTestStatus(InstrumentationTestRunner.REPORT_VALUE_RESULT_OK, testCla ss, testMethod);
400 }
401
402 private void testFailed(String testClass, String testMethod) {
403 sendTestStatus(InstrumentationTestRunner.REPORT_VALUE_RESULT_ERROR, test Class, testMethod);
404 }
405
406 private void sendTestStatus(int status, String testClass, String testMethod) {
407 Bundle statusBundle = new Bundle();
408 statusBundle.putString(InstrumentationTestRunner.REPORT_KEY_NAME_CLASS, testClass);
409 statusBundle.putString(InstrumentationTestRunner.REPORT_KEY_NAME_TEST, t estMethod);
410 sendStatus(status, statusBundle);
411 }
412 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698