blob: 3e27274c40e610295fd5891df874efc58395f5c8 [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.native_test;
import android.app.Activity;
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 org.chromium.base.CommandLine;
import org.chromium.base.Log;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.multidex.ChromiumMultiDexInstaller;
import org.chromium.base.test.util.UrlUtils;
import org.chromium.test.reporter.TestStatusReporter;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
/**
* Helper to run tests inside Activity or NativeActivity.
*/
@JNINamespace("testing::android")
public class NativeTest {
public static final String EXTRA_COMMAND_LINE_FILE =
"org.chromium.native_test.NativeTest.CommandLineFile";
public static final String EXTRA_COMMAND_LINE_FLAGS =
"org.chromium.native_test.NativeTest.CommandLineFlags";
public static final String EXTRA_RUN_IN_SUB_THREAD =
"org.chromium.native_test.NativeTest.RunInSubThread";
public static final String EXTRA_SHARD =
"org.chromium.native_test.NativeTest.Shard";
public static final String EXTRA_STDOUT_FILE =
"org.chromium.native_test.NativeTest.StdoutFile";
private static final String TAG = "cr_NativeTest";
private String mCommandLineFilePath;
private StringBuilder mCommandLineFlags = new StringBuilder();
private TestStatusReporter mReporter;
private boolean mRunInSubThread = false;
private String mStdoutFilePath;
private static class ReportingUncaughtExceptionHandler
implements Thread.UncaughtExceptionHandler {
private TestStatusReporter mReporter;
private Thread.UncaughtExceptionHandler mWrappedHandler;
public ReportingUncaughtExceptionHandler(TestStatusReporter reporter,
Thread.UncaughtExceptionHandler wrappedHandler) {
mReporter = reporter;
mWrappedHandler = wrappedHandler;
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
mReporter.uncaughtException(Process.myPid(), ex);
if (mWrappedHandler != null) mWrappedHandler.uncaughtException(thread, ex);
}
}
public void preCreate(Activity activity) {
ChromiumMultiDexInstaller.install(activity);
}
public void postCreate(Activity activity) {
CommandLine.init(new String[]{});
parseArgumentsFromIntent(activity, activity.getIntent());
mReporter = new TestStatusReporter(activity);
mReporter.testRunStarted(Process.myPid());
Thread.setDefaultUncaughtExceptionHandler(
new ReportingUncaughtExceptionHandler(mReporter,
Thread.getDefaultUncaughtExceptionHandler()));
}
private void parseArgumentsFromIntent(Activity activity, Intent intent) {
Log.i(TAG, "Extras:");
Bundle extras = intent.getExtras();
if (extras != null) {
for (String s : extras.keySet()) {
Log.i(TAG, " %s", s);
}
}
mCommandLineFilePath = intent.getStringExtra(EXTRA_COMMAND_LINE_FILE);
if (mCommandLineFilePath == null) {
mCommandLineFilePath = "";
} else {
File commandLineFile = new File(mCommandLineFilePath);
if (!commandLineFile.isAbsolute()) {
mCommandLineFilePath = Environment.getExternalStorageDirectory() + "/"
+ mCommandLineFilePath;
}
Log.i(TAG, "command line file path: %s", mCommandLineFilePath);
}
String commandLineFlags = intent.getStringExtra(EXTRA_COMMAND_LINE_FLAGS);
if (commandLineFlags != null) mCommandLineFlags.append(commandLineFlags);
mRunInSubThread = intent.hasExtra(EXTRA_RUN_IN_SUB_THREAD);
ArrayList<String> shard = intent.getStringArrayListExtra(EXTRA_SHARD);
if (shard != null) {
StringBuilder filterFlag = new StringBuilder();
filterFlag.append("--gtest_filter=");
for (Iterator<String> test_iter = shard.iterator(); test_iter.hasNext();) {
filterFlag.append(test_iter.next());
if (test_iter.hasNext()) {
filterFlag.append(":");
}
}
appendCommandLineFlags(filterFlag.toString());
}
mStdoutFilePath = intent.getStringExtra(EXTRA_STDOUT_FILE);
}
public void appendCommandLineFlags(String flags) {
mCommandLineFlags.append(" ").append(flags);
}
public void postStart(final Activity activity, boolean forceRunInSubThread) {
final Runnable runTestsTask = new Runnable() {
@Override
public void run() {
runTests(activity);
}
};
if (mRunInSubThread || forceRunInSubThread) {
// Post a task that posts a task that creates a new thread and runs tests on it.
// On L and M, the system posts a task to the main thread that prints to stdout
// from android::Layout (https://goo.gl/vZA38p). Chaining the subthread creation
// through multiple tasks executed on the main thread ensures that this task
// runs before we start running tests s.t. its output doesn't interfere with
// the test output. See crbug.com/678146 for additional context.
final Handler handler = new Handler();
final Runnable startTestThreadTask = new Runnable() {
@Override
public void run() {
new Thread(runTestsTask).start();
}
};
final Runnable postTestStarterTask = new Runnable() {
@Override
public void run() {
handler.post(startTestThreadTask);
}
};
handler.post(postTestStarterTask);
} else {
// Post a task to run the tests. This allows us to not block
// onCreate and still run tests on the main thread.
new Handler().post(runTestsTask);
}
}
private void runTests(Activity activity) {
nativeRunTests(mCommandLineFlags.toString(), mCommandLineFilePath, mStdoutFilePath,
activity.getApplicationContext(), UrlUtils.getIsolatedTestRoot());
activity.finish();
mReporter.testRunFinished(Process.myPid());
}
// Signal a failure of the native test loader to python scripts
// which run tests. For example, we look for
// RUNNER_FAILED build/android/test_package.py.
private void nativeTestFailed() {
Log.e(TAG, "[ RUNNER_FAILED ] could not load native library");
}
private native void nativeRunTests(String commandLineFlags, String commandLineFilePath,
String stdoutFilePath, Context appContext, String testDataDir);
}