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

Unified Diff: testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java

Issue 1173363008: [Android] Refactor browser test execution. (RELAND) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixed. Created 5 years, 6 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 side-by-side diff with in-line comments
Download patch
Index: testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java
diff --git a/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java b/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java
index 4db6286258a5b13d85d70d386d9233e6f7cf6cee..6e39401882a20d741df6361ac6951cfb31643c51 100644
--- a/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java
+++ b/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java
@@ -5,13 +5,19 @@
package org.chromium.native_test;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.Instrumentation;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
+import android.os.Handler;
+import android.os.Process;
+import android.util.SparseArray;
import org.chromium.base.Log;
+import org.chromium.test.reporter.TestStatusReceiver;
import org.chromium.test.support.ResultsBundleGenerator;
import org.chromium.test.support.RobotiumBundleGenerator;
@@ -20,10 +26,15 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -33,22 +44,38 @@ import java.util.regex.Pattern;
public class NativeTestInstrumentationTestRunner extends Instrumentation {
public static final String EXTRA_NATIVE_TEST_ACTIVITY =
- "org.chromium.native_test.NativeTestInstrumentationTestRunner."
- + "NativeTestActivity";
+ "org.chromium.native_test.NativeTestInstrumentationTestRunner.NativeTestActivity";
+ public static final String EXTRA_SHARD_NANO_TIMEOUT =
+ "org.chromium.native_test.NativeTestInstrumentationTestRunner.ShardNanoTimeout";
+ public static final String EXTRA_SHARD_SIZE_LIMIT =
+ "org.chromium.native_test.NativeTestInstrumentationTestRunner.ShardSizeLimit";
+ public static final String EXTRA_TEST_LIST_FILE =
+ "org.chromium.native_test.NativeTestInstrumentationTestRunner.TestList";
- private static final String TAG = Log.makeTag("native_test");
+ private static final String TAG = "cr.native_test";
- private static final int ACCEPT_TIMEOUT_MS = 5000;
+ private static final long DEFAULT_SHARD_NANO_TIMEOUT = 60 * 1000000000L;
+ // Default to no size limit.
+ private static final int DEFAULT_SHARD_SIZE_LIMIT = 0;
private static final String DEFAULT_NATIVE_TEST_ACTIVITY =
"org.chromium.native_test.NativeUnitTestActivity";
- private static final Pattern RE_TEST_OUTPUT = Pattern.compile("\\[ *([^ ]*) *\\] ?([^ ]+) .*");
+ private static final Pattern RE_TEST_OUTPUT =
+ Pattern.compile("\\[ *([^ ]*) *\\] ?([^ ]+)( .*)?$");
private ResultsBundleGenerator mBundleGenerator = new RobotiumBundleGenerator();
private String mCommandLineFile;
private String mCommandLineFlags;
+ private Handler mHandler = new Handler();
private String mNativeTestActivity;
private Bundle mLogBundle = new Bundle();
+ private TestStatusReceiver mReceiver;
+ private Map<String, ResultsBundleGenerator.TestResult> mResults =
+ new HashMap<String, ResultsBundleGenerator.TestResult>();
+ private Queue<ArrayList<String>> mShards = new ArrayDeque<ArrayList<String>>();
+ private long mShardNanoTimeout = DEFAULT_SHARD_NANO_TIMEOUT;
+ private int mShardSizeLimit = DEFAULT_SHARD_SIZE_LIMIT;
private File mStdoutFile;
+ private SparseArray<ShardMonitor> mMonitors = new SparseArray<ShardMonitor>();
@Override
public void onCreate(Bundle arguments) {
@@ -57,6 +84,39 @@ public class NativeTestInstrumentationTestRunner extends Instrumentation {
mNativeTestActivity = arguments.getString(EXTRA_NATIVE_TEST_ACTIVITY);
if (mNativeTestActivity == null) mNativeTestActivity = DEFAULT_NATIVE_TEST_ACTIVITY;
+ String shardNanoTimeout = arguments.getString(EXTRA_SHARD_NANO_TIMEOUT);
+ if (shardNanoTimeout != null) mShardNanoTimeout = Long.parseLong(shardNanoTimeout);
+
+ String shardSizeLimit = arguments.getString(EXTRA_SHARD_SIZE_LIMIT);
+ if (shardSizeLimit != null) mShardSizeLimit = Integer.parseInt(shardSizeLimit);
+
+ String testListFilePath = arguments.getString(EXTRA_TEST_LIST_FILE);
+ if (testListFilePath != null) {
+ File testListFile = new File(testListFilePath);
+ try {
+ BufferedReader testListFileReader =
+ new BufferedReader(new FileReader(testListFile));
+
+ String test;
+ ArrayList<String> workingShard = new ArrayList<String>();
+ while ((test = testListFileReader.readLine()) != null) {
+ workingShard.add(test);
+ if (workingShard.size() == mShardSizeLimit) {
+ mShards.add(workingShard);
+ workingShard = new ArrayList<String>();
+ }
+ }
+
+ if (!workingShard.isEmpty()) {
+ mShards.add(workingShard);
+ }
+
+ testListFileReader.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Error reading %s", testListFile.getAbsolutePath(), e);
+ }
+ }
+
try {
mStdoutFile = File.createTempFile(
".temp_stdout_", ".txt", Environment.getExternalStorageDirectory());
@@ -66,62 +126,153 @@ public class NativeTestInstrumentationTestRunner extends Instrumentation {
finish(Activity.RESULT_CANCELED, new Bundle());
return;
}
+
start();
}
@Override
public void onStart() {
super.onStart();
- Bundle results = runTests();
- finish(Activity.RESULT_OK, results);
+
+ mReceiver = new TestStatusReceiver();
+ mReceiver.register(getContext());
+ mReceiver.registerCallback(new TestStatusReceiver.TestRunCallback() {
+ @Override
+ public void testRunStarted(int pid) {
+ if (pid != Process.myPid()) {
+ ShardMonitor m = new ShardMonitor(
+ pid, System.nanoTime() + mShardNanoTimeout);
+ mMonitors.put(pid, m);
+ mHandler.post(m);
+ }
+ }
+
+ @Override
+ public void testRunFinished(int pid) {
+ ShardMonitor m = mMonitors.get(pid);
+ if (m != null) {
+ m.stopped();
+ mMonitors.remove(pid);
+ }
+ mHandler.post(new ShardEnder(pid));
+ }
+ });
+
+ mHandler.post(new ShardStarter());
}
- /** Runs the tests in the NativeTestActivity and returns a Bundle containing the results.
- */
- private Bundle runTests() {
- Log.i(TAG, "Creating activity.");
- Activity activityUnderTest = startNativeTestActivity();
+ /** Monitors a test shard's execution. */
+ private class ShardMonitor implements Runnable {
+ private static final int MONITOR_FREQUENCY_MS = 1000;
- Log.i(TAG, "Waiting for tests to finish.");
- try {
- while (!activityUnderTest.isFinishing()) {
- Thread.sleep(100);
+ private long mExpirationNanoTime;
+ private int mPid;
+ private AtomicBoolean mStopped;
+
+ public ShardMonitor(int pid, long expirationNanoTime) {
+ mPid = pid;
+ mExpirationNanoTime = expirationNanoTime;
+ mStopped = new AtomicBoolean(false);
+ }
+
+ public void stopped() {
+ mStopped.set(true);
+ }
+
+ @Override
+ public void run() {
+ if (mStopped.get()) {
+ return;
+ }
+
+ if (isAppProcessAlive(getContext(), mPid)) {
+ if (System.nanoTime() > mExpirationNanoTime) {
+ Log.e(TAG, "Test process %d timed out.", mPid);
+ mHandler.post(new ShardEnder(mPid));
+ return;
+ } else {
+ mHandler.postDelayed(this, MONITOR_FREQUENCY_MS);
+ return;
+ }
}
- } catch (InterruptedException e) {
- Log.e(TAG, "Interrupted while waiting for activity to be destroyed: ", e);
+
+ Log.e(TAG, "Test process %d died unexpectedly.", mPid);
+ mHandler.post(new ShardEnder(mPid));
}
- Log.i(TAG, "Getting results.");
- Map<String, ResultsBundleGenerator.TestResult> results = parseResults(activityUnderTest);
+ }
- Log.i(TAG, "Parsing results and generating output.");
- return mBundleGenerator.generate(results);
+ private static boolean isAppProcessAlive(Context context, int pid) {
+ ActivityManager activityManager =
+ (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ for (ActivityManager.RunningAppProcessInfo processInfo :
+ activityManager.getRunningAppProcesses()) {
+ if (processInfo.pid == pid) return true;
+ }
+ return false;
}
/** Starts the NativeTestActivty.
*/
- private Activity startNativeTestActivity() {
- Intent i = new Intent(Intent.ACTION_MAIN);
- i.setComponent(new ComponentName(getContext().getPackageName(), mNativeTestActivity));
- i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- if (mCommandLineFile != null) {
- Log.i(TAG, "Passing command line file extra: %s", mCommandLineFile);
- i.putExtra(NativeTestActivity.EXTRA_COMMAND_LINE_FILE, mCommandLineFile);
+ private class ShardStarter implements Runnable {
+ @Override
+ public void run() {
+ Intent i = new Intent(Intent.ACTION_MAIN);
+ i.setComponent(new ComponentName(getContext().getPackageName(), mNativeTestActivity));
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (mCommandLineFile != null) {
+ Log.i(TAG, "Passing command line file extra: %s", mCommandLineFile);
+ i.putExtra(NativeTestActivity.EXTRA_COMMAND_LINE_FILE, mCommandLineFile);
+ }
+ if (mCommandLineFlags != null) {
+ Log.i(TAG, "Passing command line flag extra: %s", mCommandLineFlags);
+ i.putExtra(NativeTestActivity.EXTRA_COMMAND_LINE_FLAGS, mCommandLineFlags);
+ }
+ if (mShards != null && !mShards.isEmpty()) {
+ ArrayList<String> shard = mShards.remove();
+ i.putStringArrayListExtra(NativeTestActivity.EXTRA_SHARD, shard);
+ }
+ i.putExtra(NativeTestActivity.EXTRA_STDOUT_FILE, mStdoutFile.getAbsolutePath());
+ getContext().startActivity(i);
}
- if (mCommandLineFlags != null) {
- Log.i(TAG, "Passing command line flag extra: %s", mCommandLineFlags);
- i.putExtra(NativeTestActivity.EXTRA_COMMAND_LINE_FLAGS, mCommandLineFlags);
+ }
+
+ private class ShardEnder implements Runnable {
+ private static final int WAIT_FOR_DEATH_MILLIS = 10;
+
+ private int mPid;
+
+ public ShardEnder(int pid) {
+ mPid = pid;
+ }
+
+ @Override
+ public void run() {
+ if (mPid != Process.myPid()) {
+ Process.killProcess(mPid);
+ try {
+ while (isAppProcessAlive(getContext(), mPid)) {
+ Thread.sleep(WAIT_FOR_DEATH_MILLIS);
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "%d may still be alive.", mPid, e);
+ }
+ }
+ mResults.putAll(parseResults());
+
+ if (mShards != null && !mShards.isEmpty()) {
+ mHandler.post(new ShardStarter());
+ } else {
+ finish(Activity.RESULT_OK, mBundleGenerator.generate(mResults));
+ }
}
- i.putExtra(NativeTestActivity.EXTRA_STDOUT_FILE, mStdoutFile.getAbsolutePath());
- return startActivitySync(i);
}
/**
* Generates a map between test names and test results from the instrumented Activity's
* output.
*/
- private Map<String, ResultsBundleGenerator.TestResult> parseResults(
- Activity activityUnderTest) {
+ private Map<String, ResultsBundleGenerator.TestResult> parseResults() {
Map<String, ResultsBundleGenerator.TestResult> results =
new HashMap<String, ResultsBundleGenerator.TestResult>();
@@ -153,7 +304,7 @@ public class NativeTestInstrumentationTestRunner extends Instrumentation {
Log.i(TAG, l);
}
} catch (FileNotFoundException e) {
- Log.e(TAG, "Couldn't find stdout file file: ", e);
+ Log.e(TAG, "Couldn't find stdout file: ", e);
} catch (IOException e) {
Log.e(TAG, "Error handling stdout file: ", e);
} finally {

Powered by Google App Engine
This is Rietveld 408576698