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

Side by Side Diff: testing/android/java/src/org/chromium/test/outstrumentation/master/Outstrumentation.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: Created 5 years, 9 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.outstrumentation.master;
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.util.Log;
21
22 import org.chromium.test.outstrumentation.reporter.ReportingService;
23 import org.chromium.test.outstrumentation.slave.OutstrumentationSlaveActivity;
24 import org.chromium.test.support.ResultsBundleGenerator;
25 import org.chromium.test.support.RobotiumBundleGenerator;
26
27 import java.io.BufferedReader;
28 import java.io.File;
29 import java.io.FileReader;
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.regex.Pattern;
37
38 /**
39 * An Instrumentation that drives instrumentation tests from outside the app. (H ence the name.)
40 */
41 public class Outstrumentation extends Instrumentation {
42
43 private static final String TAG = "Outstrumentation";
44
45 private static final String EXTRA_TEST_LIST =
46 "org.chromium.test.outstrumentation.master.Outstrumentation.TestList ";
47 private static final String EXTRA_TEST_LIST_FILE =
48 "org.chromium.test.outstrumentation.master.Outstrumentation.TestList File";
49 private static final String EXTRA_TARGET_PACKAGE =
50 "org.chromium.test.outstrumentation.master.Outstrumentation.TargetPa ckage";
51 private static final String EXTRA_TARGET_CLASS =
52 "org.chromium.test.outstrumentation.master.Outstrumentation.TargetCl ass";
53
54 private static final Pattern COMMA = Pattern.compile(",");
55
56 private Bundle mTargetArgs;
57 private String mTargetClass;
58 private String mTargetPackage;
59 private List<String> mTestClasses;
60
61 @Override
62 public void onCreate(Bundle arguments) {
63 mTargetArgs = new Bundle(arguments);
64 mTargetPackage = arguments.getString(EXTRA_TARGET_PACKAGE);
65 if (mTargetPackage == null) {
66 fail("No target package.");
67 return;
68 }
69 mTargetArgs.remove(EXTRA_TARGET_PACKAGE);
Ted C 2015/04/06 17:43:54 why remove things?
jbudorick 2015/04/07 00:54:38 I want Outstrumentation (% name change) to forward
70
71 mTargetClass = arguments.getString(EXTRA_TARGET_CLASS);
72 if (mTargetClass == null) {
73 fail("No target class.");
74 return;
75 }
76 mTargetArgs.remove(EXTRA_TARGET_CLASS);
77
78 mTestClasses = new ArrayList<String>();
79 String testList = arguments.getString(EXTRA_TEST_LIST);
80 if (testList != null) {
81 mTestClasses.addAll(Arrays.asList(COMMA.split(testList)));
82 mTargetArgs.remove(EXTRA_TEST_LIST);
83 }
84
85 String testListFilePath = arguments.getString(EXTRA_TEST_LIST_FILE);
86 if (testListFilePath != null) {
87 File testListFile = new File(Environment.getExternalStorageDirectory (),
88 testListFilePath);
Ted C 2015/04/06 17:43:54 java indenting is 8 from the previous line
jbudorick 2015/04/07 00:54:39 Done.
89 try {
90 BufferedReader testListFileReader =
91 new BufferedReader(new FileReader(testListFile));
92 while (true) {
Ted C 2015/04/06 17:43:54 nit: I normally see this as: String test; while (
jbudorick 2015/04/07 00:54:39 Done.
93 String test = testListFileReader.readLine();
94 if (test == null) break;
95 mTestClasses.add(test);
96 }
97 testListFileReader.close();
98 } catch (IOException e) {
99 Log.e(TAG, "Error reading " + testListFile.getAbsolutePath(), e) ;
100 }
101 mTargetArgs.remove(EXTRA_TEST_LIST_FILE);
102 }
103
104 if (mTestClasses.isEmpty()) {
105 fail("No tests.");
106 return;
107 }
108
109 start();
110 }
111
112 @Override
113 public void onStart() {
114 super.onStart();
115
116 getContext().startService(new Intent(getContext(), ReportingService.clas s));
117
118 // Start the driver on its own thread s.t. it can block while the main t hread's
119 // Looper receives and handles messages.
120 Thread driverThread = new Thread(
121 new Driver(mTargetPackage, mTargetClass, mTargetArgs, mTestClass es));
122 driverThread.start();
123 }
124
125 @Override
126 public void onDestroy() {
Ted C 2015/04/06 17:43:54 onStop is the corollary to onStart. So the thread
jbudorick 2015/04/07 00:54:38 Moved thread creation to onCreate and added a bool
127 getContext().stopService(new Intent(getContext(), ReportingService.class ));
128 super.onDestroy();
129 }
130
131 private class Driver implements Runnable {
132
133 private static final String TAG = Outstrumentation.TAG + ".Driver";
134
135 private Messenger mReportingService;
Ted C 2015/04/06 17:43:54 why not declared inside of ReportingServiceConnect
jbudorick 2015/04/07 00:54:39 I'm not actually sure why I did that; I imagine th
136 private Object mServiceLock = new Object();
137 private Bundle mTargetArgs;
138 private String mTargetClass;
139 private String mTargetPackage;
140 private List<String> mTestClasses;
141 private ReportHandler mHandler;
142
143 public Driver(String targetPackage, String targetClass, Bundle targetArg s,
144 List<String> testClasses) {
145 mTargetPackage = targetPackage;
146 mTargetClass = targetClass;
147 mTargetArgs = targetArgs;
148 mTestClasses = testClasses;
149 mHandler = new ReportHandler(Looper.getMainLooper());
150 }
151
152 private class ReportingServiceConnection implements ServiceConnection {
153
154 private static final String TAG = Driver.TAG + ".ReportingServiceCon nection";
155
156 @Override
157 public void onServiceConnected(ComponentName name, IBinder service) {
158 // Register receiver.
159 Log.i(TAG, "service connected");
160 synchronized (mServiceLock) {
161 mReportingService = new Messenger(service);
162 mServiceLock.notify();
163 }
164 }
165
166 public void registerWithReportingService(Messenger receiver)
Ted C 2015/04/06 17:43:54 javadoc all the public methods (and in ReportHandl
jbudorick 2015/04/07 00:54:39 Done.
167 throws InterruptedException, RemoteException {
168 synchronized (mServiceLock) {
169 Log.i(TAG, "acquired service lock");
170 while (mReportingService == null) {
171 Log.i(TAG, "waiting for service connection.");
172 mServiceLock.wait();
173 }
174
175 Log.i(TAG, "reporting service non-null");
176
177 Message registration = Message.obtain();
178 registration.what = ReportingService.MSG_REGISTER_RECEIVER;
179 registration.replyTo = receiver;
180 mReportingService.send(registration);
181
182 Log.i(TAG, "sent registration message");
183 }
184 }
185
186 public void unregisterWithReportingService()
187 throws InterruptedException, RemoteException {
188 synchronized (mServiceLock) {
189 if (mReportingService == null) return;
190
191 Message registration = Message.obtain();
192 registration.what = ReportingService.MSG_UNREGISTER_RECEIVER ;
193 mReportingService.send(registration);
194 }
195 }
196
197 @Override
198 public void onServiceDisconnected(ComponentName name) {
199 synchronized (mServiceLock) {
200 mReportingService = null;
201 mServiceLock.notify();
202 }
203 }
204 }
205
206 private class ReportHandler extends Handler {
207 private static final String TAG = Driver.TAG + ".ReportHandler";
208
209 private final Object mLock = new Object();
210 private final Map<String, ResultsBundleGenerator.TestResult> mFinish ed =
211 new HashMap<String, ResultsBundleGenerator.TestResult>();
212
213 public ReportHandler(Looper looper) {
214 super(looper);
215 }
216
217 @Override
218 public void handleMessage(Message msg) {
219 switch (msg.what) {
220 case ReportingService.MSG_REPORT_TEST_STARTED:
221 case ReportingService.MSG_REPORT_TEST_PASSED:
222 case ReportingService.MSG_REPORT_TEST_FAILED:
223 handleTestStatusMessage(msg);
224 break;
225 default:
226 super.handleMessage(msg);
227 break;
228 }
229 }
230
231 private void handleTestStatusMessage(Message msg) {
232 String testClass = msg.getData().getString(ReportingService.MSG_ DATA_TEST_CLASS);
233 String testMethod = msg.getData().getString(ReportingService.MSG _DATA_TEST_METHOD);
234 String testName = testClass + "#" + testMethod;
235 synchronized (mLock) {
236 switch (msg.what) {
237 case ReportingService.MSG_REPORT_TEST_STARTED:
238 testStarted(testClass, testMethod);
239 break;
240 case ReportingService.MSG_REPORT_TEST_PASSED:
241 mFinished.put(testName, ResultsBundleGenerator.TestR esult.PASSED);
242 testPassed(testClass, testMethod);
243 mLock.notify();
244 break;
245 case ReportingService.MSG_REPORT_TEST_FAILED:
246 mFinished.put(testName, ResultsBundleGenerator.TestR esult.FAILED);
247 testFailed(testClass, testMethod);
248 mLock.notify();
249 break;
250 default:
Ted C 2015/04/06 17:43:54 probably should fail in this case as it would indi
jbudorick 2015/04/07 00:54:39 Done.
251 break;
252 }
253 }
254 }
255
256 public void waitForTestFinished(String testName) throws InterruptedE xception {
257 synchronized (mLock) {
258 while (!mFinished.containsKey(testName)) {
259 mLock.wait();
260 }
261 }
262 }
263
264 public Bundle getResults() {
265 return new RobotiumBundleGenerator().generate(mFinished);
266 }
267 }
268
269 @Override
270 public void run() {
271 try {
272 ReportingServiceConnection c = new ReportingServiceConnection();
273 if (!getContext().bindService(
274 new Intent(getContext(), ReportingService.class), c, 0)) {
275 fail("Reporting service not bound.");
276 return;
277 }
278
279 c.registerWithReportingService(new Messenger(mHandler));
280
281 for (String t : mTestClasses) {
282 Intent slaveIntent = new Intent();
283 slaveIntent.setComponent(
284 new ComponentName(mTargetPackage,
285 OutstrumentationSlaveActivity.clas s.getName()));
Ted C 2015/04/06 17:43:54 same indent comment...8 from the previous line
jbudorick 2015/04/07 00:54:39 the troubles with jumping between languages done.
286 slaveIntent.putExtra(
287 OutstrumentationSlaveActivity.EXTRA_INSTRUMENTATION_ PACKAGE,
288 mTargetPackage);
289 slaveIntent.putExtra(
290 OutstrumentationSlaveActivity.EXTRA_INSTRUMENTATION_ CLASS,
291 mTargetClass);
292 slaveIntent.putExtra(OutstrumentationSlaveActivity.EXTRA_TES T, t);
293 slaveIntent.putExtra(OutstrumentationSlaveActivity.EXTRA_TAR GET_ARGS,
294 mTargetArgs);
295 slaveIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
296
297 getContext().startActivity(slaveIntent);
298
299 mHandler.waitForTestFinished(t);
300 }
301
302 c.unregisterWithReportingService();
303 getContext().unbindService(c);
304 } catch (RemoteException e) {
305 fail("Error connecting to reporting service.", e);
306 return;
307 } catch (InterruptedException e) {
308 fail("Interrupted while running tests.", e);
309 return;
310 }
311 pass(mHandler.getResults());
312 }
313
314 }
315
316 private void fail(String reason) {
317 Log.e(TAG, reason);
318 failImpl(reason);
319 }
320
321 private void fail(String reason, Exception e) {
322 Log.e(TAG, reason, e);
323 failImpl(reason);
324 }
325
326 private void failImpl(String reason) {
327 Bundle b = new Bundle();
328 b.putString("reason", reason);
329 finish(Activity.RESULT_CANCELED, b);
330 }
331
332 private void pass(Bundle results) {
333 finish(Activity.RESULT_OK, results);
334 }
335
336 private void testStarted(String testClass, String testMethod) {
337 Bundle status = new Bundle();
338 status.putString("class", testClass);
339 status.putString("test", testMethod);
340 sendStatus(1, status);
Ted C 2015/04/06 17:43:54 where are these status values defined (1, 0, -1)?
jbudorick 2015/04/07 00:54:39 I had thought they were private constants in Instr
341 }
342
343 private void testPassed(String testClass, String testMethod) {
344 Bundle status = new Bundle();
345 status.putString("class", testClass);
346 status.putString("test", testMethod);
347 sendStatus(0, status);
348 }
349
350 private void testFailed(String testClass, String testMethod) {
351 Bundle status = new Bundle();
352 status.putString("class", testClass);
353 status.putString("test", testMethod);
354 sendStatus(-1, status);
355 }
356 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698