summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornileshagrawal@chromium.org <nileshagrawal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-09 16:59:12 +0000
committernileshagrawal@chromium.org <nileshagrawal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-09 16:59:12 +0000
commit2037ef804dac0e366e751d30aecfe1f868fb4f2b (patch)
tree01093f4b02edb7b2d979f58f1a7f0dc241ab70f1
parentcc3a5c8ca42579c0f9f710c18f51c566b4c75e86 (diff)
downloadchromium_src-2037ef804dac0e366e751d30aecfe1f868fb4f2b.zip
chromium_src-2037ef804dac0e366e751d30aecfe1f868fb4f2b.tar.gz
chromium_src-2037ef804dac0e366e751d30aecfe1f868fb4f2b.tar.bz2
Detect crashes while running native tests in APK.
Adding signal handlers (for fatal signals) to output a marker indicating that the test crashed. BUG=125059 TEST= Review URL: https://chromiumcodereview.appspot.com/10310046 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@136050 0039d316-1c4b-4281-b951-d872f2087c98
-rwxr-xr-xbuild/android/run_tests.py3
-rw-r--r--build/android/single_test_runner.py7
-rw-r--r--build/android/test_package.py22
-rw-r--r--build/android/test_result.py8
-rw-r--r--testing/android/native_test_launcher.cc41
5 files changed, 67 insertions, 14 deletions
diff --git a/build/android/run_tests.py b/build/android/run_tests.py
index 1015d26..ca1f877 100755
--- a/build/android/run_tests.py
+++ b/build/android/run_tests.py
@@ -193,8 +193,7 @@ def RunTests(device, test_suite, gtest_filter, test_arguments, rebaseline,
if test_suite in _TEST_SUITES:
logging.critical('(Remember to include the path: out/Release/%s)',
test_suite)
- return TestResults.FromOkAndFailed([], [BaseTestResult(test_suite, '')],
- False, False)
+ return TestResults.FromRun(failed=[BaseTestResult(test_suite, '')])
fully_qualified_test_suites = [test_suite]
else:
fully_qualified_test_suites = FullyQualifiedTestSuites(apk)
diff --git a/build/android/single_test_runner.py b/build/android/single_test_runner.py
index fe59cb6..503088c 100644
--- a/build/android/single_test_runner.py
+++ b/build/android/single_test_runner.py
@@ -271,10 +271,9 @@ class SingleTestRunner(BaseTestRunner):
logging.info('*' * 80)
if executed_names == all_tests:
break
- self.test_results = TestResults.FromOkAndFailed(list(executed_results -
- failed_results),
- list(failed_results),
- False, False)
+ self.test_results = TestResults.FromRun(
+ ok=list(executed_results - failed_results),
+ failed=list(failed_results))
def RunTests(self):
"""Runs all tests (in rebaseline mode, runs each test in isolation).
diff --git a/build/android/test_package.py b/build/android/test_package.py
index e4b10e1..8364c94 100644
--- a/build/android/test_package.py
+++ b/build/android/test_package.py
@@ -129,11 +129,15 @@ class TestPackage(object):
"""
ok_tests = []
failed_tests = []
+ crashed_tests = []
timed_out = False
overall_fail = False
re_run = re.compile('\[ RUN \] ?(.*)\r\n')
# APK tests rely on the END tag.
re_end = re.compile('\[ END \] ?(.*)\r\n')
+ # Signal handlers are installed before starting tests
+ # to output the CRASHED marker when a crash happens.
+ re_crash = re.compile('\[ CRASHED \](.*)\r\n')
re_fail = re.compile('\[ FAILED \] ?(.*)\r\n')
re_runner_fail = re.compile('\[ RUNNER_FAILED \] ?(.*)\r\n')
re_ok = re.compile('\[ OK \] ?(.*)\r\n')
@@ -152,16 +156,22 @@ class TestPackage(object):
if self.dump_debug_info:
self.dump_debug_info.TakeScreenshot('_Test_Start_Run_')
full_test_name = p.match.group(1)
- found = p.expect([re_ok, re_fail, pexpect.EOF, pexpect.TIMEOUT],
+ found = p.expect([re_ok, re_fail, re_crash, pexpect.EOF, pexpect.TIMEOUT],
timeout=self.timeout)
if found == 0: # re_ok
ok_tests += [BaseTestResult(full_test_name.replace('\r', ''),
p.before)]
continue
+ if found == 2: # re_crash
+ crashed_tests += [BaseTestResult(full_test_name.replace('\r', ''),
+ p.before)]
+ overall_fail = True
+ break
+ # The test failed.
failed_tests += [BaseTestResult(full_test_name.replace('\r', ''),
p.before)]
- if found >= 2:
- # The test crashed / bailed out (i.e., didn't print OK or FAIL).
+ if found >= 3:
+ # The test bailed out (i.e., didn't print OK or FAIL).
if found == 3: # pexpect.TIMEOUT
logging.error('Test terminated after %d second timeout.',
self.timeout)
@@ -177,5 +187,7 @@ class TestPackage(object):
'\npexpect.after: %s'
% (p.before,
p.after))]
- return TestResults.FromOkAndFailed(ok_tests, failed_tests,
- timed_out, overall_fail)
+ # Create TestResults and return
+ return TestResults.FromRun(ok=ok_tests, failed=failed_tests,
+ crashed=crashed_tests, timed_out=timed_out,
+ overall_fail=overall_fail)
diff --git a/build/android/test_result.py b/build/android/test_result.py
index dfafca7..7d9b216 100644
--- a/build/android/test_result.py
+++ b/build/android/test_result.py
@@ -61,10 +61,12 @@ class TestResults(object):
self.overall_fail = False
@staticmethod
- def FromOkAndFailed(ok, failed, timed_out, overall_fail):
+ def FromRun(ok=None, failed=None, crashed=None, timed_out=False,
+ overall_fail=False):
ret = TestResults()
- ret.ok = ok
- ret.failed = failed
+ ret.ok = ok or []
+ ret.failed = failed or []
+ ret.crashed = crashed or []
ret.timed_out = timed_out
ret.overall_fail = overall_fail
return ret
diff --git a/testing/android/native_test_launcher.cc b/testing/android/native_test_launcher.cc
index 5e600b8..ad1db27 100644
--- a/testing/android/native_test_launcher.cc
+++ b/testing/android/native_test_launcher.cc
@@ -2,6 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// This class sets up the environment for running the native tests inside an
+// android application. It outputs (to logcat) markers identifying the
+// START/END/CRASH of the test suite, FAILURE/SUCCESS of individual tests etc.
+// These markers are read by the test runner script to generate test results.
+// It injects an event listener in gtest to detect various test stages and
+// installs signal handlers to detect crashes.
+
+#include <android/log.h>
+#include <signal.h>
#include <stdio.h>
#include "base/android/jni_android.h"
@@ -26,6 +35,33 @@ extern int main(int argc, char** argv);
namespace {
+// The list of signals which are considered to be crashes.
+const int kExceptionSignals[] = {
+ SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1
+};
+
+struct sigaction g_old_sa[NSIG];
+
+// This function runs in a compromised context. It should not allocate memory.
+void SignalHandler(int sig, siginfo_t *info, void *reserved)
+{
+ // Output the crash marker.
+ __android_log_write(ANDROID_LOG_ERROR, "chromium", "[ CRASHED ]");
+ g_old_sa[sig].sa_sigaction(sig, info, reserved);
+}
+
+void InstallHandlers() {
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+
+ sa.sa_sigaction = SignalHandler;
+ sa.sa_flags = SA_SIGINFO;
+
+ for (unsigned int i = 0; kExceptionSignals[i] != -1; ++i) {
+ sigaction(kExceptionSignals[i], &sa, &g_old_sa[kExceptionSignals[i]]);
+ }
+}
+
void ParseArgsFromString(const std::string& command_line,
std::vector<std::string>* args) {
StringTokenizer tokenizer(command_line, kWhitespaceASCII);
@@ -191,11 +227,16 @@ static void RunTests(JNIEnv* env,
// This is called by the VM when the shared library is first loaded.
JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+
+ // Install signal handlers to detect crashes.
+ InstallHandlers();
+
base::android::InitVM(vm);
JNIEnv* env = base::android::AttachCurrentThread();
if (!RegisterNativesImpl(env)) {
return -1;
}
LibraryLoadedOnMainThread(env);
+
return JNI_VERSION_1_4;
}