Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 } | |
| OLD | NEW |