summaryrefslogtreecommitdiffstats
path: root/testing
diff options
context:
space:
mode:
authorjrg@chromium.org <jrg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-19 21:02:09 +0000
committerjrg@chromium.org <jrg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-19 21:02:09 +0000
commitd9f9695633f8ea3dd1d4d02136c12cf2ffe429b8 (patch)
tree18b8981c285c249d714b079cafce988ad66d5f8d /testing
parentd9426956e7653f57361544bfa9ff7cfd84590a5a (diff)
downloadchromium_src-d9f9695633f8ea3dd1d4d02136c12cf2ffe429b8.zip
chromium_src-d9f9695633f8ea3dd1d4d02136c12cf2ffe429b8.tar.gz
chromium_src-d9f9695633f8ea3dd1d4d02136c12cf2ffe429b8.tar.bz2
apk-based test runner work for android. 2 unit test bundles converted over (ipc, base).
OFF by default; enable with a gyp var. E.g. GYP_DEFINES="$GYP_DEFINES gtest_target_type=shared_library" android_gyp Some useful commands: adb uninstall org.chromium.native_test adb install -r out/Release/base_unittests_apk/ChromeNativeTests-debug.apk adb shell am start -n org.chromium.native_test/org.chromium.native_test.ChromeNativeTestActivity For the moment, all apks can be built simultaneously but use the same activity name. Thus you cannot have more than one installed at the same time. BUG=None TEST= Review URL: http://codereview.chromium.org/10051021 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@133053 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'testing')
-rw-r--r--testing/android/AndroidManifest.xml30
-rw-r--r--testing/android/OWNERS3
-rwxr-xr-xtesting/android/generate_native_test.py194
-rw-r--r--testing/android/java/src/org/chromium/native_test/ChromeNativeTestActivity.java66
-rw-r--r--testing/android/native_test.gyp96
-rw-r--r--testing/android/native_test_apk.xml55
-rw-r--r--testing/android/native_test_launcher.cc190
-rw-r--r--testing/android/res/values/strings.xml11
8 files changed, 645 insertions, 0 deletions
diff --git a/testing/android/AndroidManifest.xml b/testing/android/AndroidManifest.xml
new file mode 100644
index 0000000..de98aec
--- /dev/null
+++ b/testing/android/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.chromium.native_test"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk android:minSdkVersion="15" />
+
+ <application android:label="ChromeNativeTests">
+ <activity android:name=".ChromeNativeTestActivity"
+ android:label="ChromeNativeTest"
+ android:configChanges="orientation|keyboardHidden">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+ <!-- TODO(jrg): add more permissions as needed by unit tests. -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+
+</manifest>
diff --git a/testing/android/OWNERS b/testing/android/OWNERS
new file mode 100644
index 0000000..da07f99
--- /dev/null
+++ b/testing/android/OWNERS
@@ -0,0 +1,3 @@
+bulach@chromium.org
+jrg@chromium.org
+
diff --git a/testing/android/generate_native_test.py b/testing/android/generate_native_test.py
new file mode 100755
index 0000000..7b7853d
--- /dev/null
+++ b/testing/android/generate_native_test.py
@@ -0,0 +1,194 @@
+#!/usr/bin/python
+# Copyright (c) 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.
+
+# On Android we build unit test bundles as shared libraries. To run
+# tests, we launch a special "test runner" apk which loads the library
+# then jumps into it. Since java is required for many tests
+# (e.g. PathUtils.java), a "pure native" test bundle is inadequate.
+#
+# This script, generate_native_test.py, is used to generate the source
+# for an apk that wraps a unit test shared library bundle. That
+# allows us to have a single boiler-plate application be used across
+# all unit test bundles.
+
+import logging
+import optparse
+import os
+import re
+import shutil
+import subprocess
+import sys
+
+
+class NativeTestApkGenerator(object):
+ """Generate a native test apk source tree.
+
+ TODO(jrg): develop this more so the activity name is replaced as
+ well. That will allow multiple test runners to be installed at the
+ same time. (The complication is that it involves renaming a java
+ class, which implies regeneration of a jni header, and on and on...)
+ """
+
+ # Files or directories we need to copy to create a complete apk test shell.
+ _SOURCE_FILES = ['AndroidManifest.xml',
+ 'native_test_apk.xml',
+ 'res', # res/values/strings.xml
+ 'java', # .../ChromeNativeTestActivity.java
+ ]
+
+ # Files in the destion directory that have a "replaceme" string
+ # which should be replaced by the basename of the shared library.
+ # Note we also update the filename if 'replaceme' is itself found in
+ # the filename.
+ _REPLACEME_FILES = ['AndroidManifest.xml',
+ 'native_test_apk.xml',
+ 'res/values/strings.xml']
+
+ def __init__(self, native_library, jars, output_directory):
+ self._native_library = native_library
+ self._jars = jars
+ self._output_directory = output_directory
+ self._root_name = None
+ if self._native_library:
+ self._root_name = self._LibraryRoot()
+ logging.warn('root name: %s' % self._root_name)
+
+
+ def _LibraryRoot(self):
+ """Return a root name for a shared library.
+
+ The root name should be suitable for substitution in apk files
+ like the manifest. For example, blah/foo/libbase_unittests.so
+ becomes base_unittests.
+ """
+ rootfinder = re.match('.?lib(.+).so',
+ os.path.basename(self._native_library))
+ if rootfinder:
+ return rootfinder.group(1)
+ else:
+ return None
+
+ def _CopyTemplateFiles(self):
+ """Copy files needed to build a new apk.
+
+ TODO(jrg): add more smarts so we don't change file timestamps if
+ the files don't change?
+ """
+ srcdir = os.path.dirname(os.path.realpath( __file__))
+ if not os.path.exists(self._output_directory):
+ os.makedirs(self._output_directory)
+ for f in self._SOURCE_FILES:
+ src = os.path.join(srcdir, f)
+ dest = os.path.join(self._output_directory, f)
+ if os.path.isfile(src):
+ if os.path.exists(dest):
+ os.remove(dest)
+ logging.warn('%s --> %s' % (src, dest))
+ shutil.copyfile(src, dest)
+ else: # directory
+ if os.path.exists(dest):
+ # One more sanity check since we're deleting a directory...
+ if not '/out/' in dest:
+ raise Exception('Unbelievable output directory; bailing for safety')
+ shutil.rmtree(dest)
+ logging.warn('%s --> %s' % (src, dest))
+ shutil.copytree(src, dest)
+
+ def _ReplaceStrings(self):
+ """Replace 'replaceme' strings in generated files with a root libname.
+
+ If we have no root libname (e.g. no shlib was specified), do nothing.
+ """
+ if not self._root_name:
+ return
+ logging.warn('Replacing "replaceme" with ' + self._root_name)
+ for f in self._REPLACEME_FILES:
+ dest = os.path.join(self._output_directory, f)
+ contents = open(dest).read()
+ contents = contents.replace('replaceme', self._root_name)
+ dest = dest.replace('replaceme', self._root_name) # update the filename!
+ open(dest, "w").write(contents)
+
+ def _CopyLibraryAndJars(self):
+ """Copy the shlib and jars into the apk source tree (if relevant)"""
+ if self._native_library:
+ destdir = os.path.join(self._output_directory, 'libs/armeabi')
+ if not os.path.exists(destdir):
+ os.makedirs(destdir)
+ dest = os.path.join(destdir, os.path.basename(self._native_library))
+ logging.warn('%s --> %s' % (self._native_library, dest))
+ shutil.copyfile(self._native_library, dest)
+ if self._jars:
+ destdir = os.path.join(self._output_directory, 'libs')
+ if not os.path.exists(destdir):
+ os.makedirs(destdir)
+ for jar in self._jars:
+ dest = os.path.join(destdir, os.path.basename(jar))
+ logging.warn('%s --> %s' % (jar, dest))
+ shutil.copyfile(jar, dest)
+
+ def CreateBundle(self):
+ """Create the apk bundle source and assemble components."""
+ self._CopyTemplateFiles()
+ self._ReplaceStrings()
+ self._CopyLibraryAndJars()
+
+ def Compile(self, ant_args):
+ """Build the generated apk with ant.
+
+ Args:
+ ant_args: extra args to pass to ant
+ """
+ cmd = ['ant']
+ if ant_args:
+ cmd.append(ant_args)
+ cmd.extend(['-buildfile',
+ os.path.join(self._output_directory, 'native_test_apk.xml')])
+ logging.warn(cmd)
+ p = subprocess.Popen(cmd, stderr=subprocess.STDOUT)
+ (stdout, _) = p.communicate()
+ logging.warn(stdout)
+ if p.returncode != 0:
+ logging.error('Ant return code %d' % p.returncode)
+ sys.exit(p.returncode)
+
+
+def main(argv):
+ parser = optparse.OptionParser()
+ parser.add_option('--verbose',
+ help='Be verbose')
+ parser.add_option('--native_library',
+ help='Full name of native shared library test bundle')
+ parser.add_option('--jar', action='append',
+ help='Include this jar; can be specified multiple times')
+ parser.add_option('--output',
+ help='Output directory for generated files.')
+ parser.add_option('--ant-compile', action='store_true',
+ help='If specified, build the generated apk with ant')
+ parser.add_option('--ant-args',
+ help='extra args for ant')
+
+ options, _ = parser.parse_args(argv)
+
+ # It is not an error to specify no native library; the apk should
+ # still be generated and build. It will, however, print
+ # NATIVE_LOADER_FAILED when run.
+ if not options.output:
+ raise Exception('No output directory specified for generated files')
+
+ if options.verbose:
+ logging.basicConfig(level=logging.DEBUG, format=' %(message)s')
+
+ ntag = NativeTestApkGenerator(native_library=options.native_library,
+ jars=options.jar,
+ output_directory=options.output)
+ ntag.CreateBundle()
+ if options.ant_compile:
+ ntag.Compile(options.ant_args)
+
+ logging.warn('COMPLETE.')
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/testing/android/java/src/org/chromium/native_test/ChromeNativeTestActivity.java b/testing/android/java/src/org/chromium/native_test/ChromeNativeTestActivity.java
new file mode 100644
index 0000000..b4bb6ad
--- /dev/null
+++ b/testing/android/java/src/org/chromium/native_test/ChromeNativeTestActivity.java
@@ -0,0 +1,66 @@
+// Copyright (c) 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.os.Bundle;
+import android.util.Log;
+
+// Android's NativeActivity is mostly useful for pure-native code.
+// Our tests need to go up to our own java classes, which is not possible using
+// the native activity class loader.
+// We start a background thread in here to run the tests and avoid an ANR.
+// TODO(bulach): watch out for tests that implicitly assume they run on the main
+// thread.
+public class ChromeNativeTestActivity extends Activity {
+ private final String TAG = "ChromeNativeTestActivity";
+
+ // Name of our shlib as obtained from a string resource.
+ private String mLibrary;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mLibrary = getResources().getString(R.string.native_library);
+ if ((mLibrary == null) || mLibrary.startsWith("replace")) {
+ nativeTestFailed();
+ return;
+ }
+
+ try {
+ loadLibrary();
+ new Thread() {
+ @Override
+ public void run() {
+ Log.d(TAG, ">>nativeRunTests");
+ nativeRunTests(getFilesDir().getAbsolutePath());
+ // TODO(jrg): make sure a crash in native code
+ // triggers nativeTestFailed().
+ Log.d(TAG, "<<nativeRunTests");
+ }
+ }.start();
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "Unable to load lib" + mLibrary + ".so: " + e);
+ nativeTestFailed();
+ throw e;
+ }
+ }
+
+ // 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 void loadLibrary() throws UnsatisfiedLinkError {
+ Log.i(TAG, "loading: " + mLibrary);
+ System.loadLibrary(mLibrary);
+ Log.i(TAG, "loaded: " + mLibrary);
+ }
+
+ private native void nativeRunTests(String filesDir);
+}
diff --git a/testing/android/native_test.gyp b/testing/android/native_test.gyp
new file mode 100644
index 0000000..1b60cbf
--- /dev/null
+++ b/testing/android/native_test.gyp
@@ -0,0 +1,96 @@
+# Copyright (c) 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.
+
+{
+ 'conditions': [
+ ['OS=="android"', {
+ 'targets': [
+ {
+ 'target_name': 'native_test_apk',
+ 'message': 'building native test apk',
+ 'type': 'none',
+ 'dependencies': [
+ 'native_test_native_code',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'native_test_apk',
+ 'inputs': [
+ '<(DEPTH)/testing/android/native_test_apk.xml',
+ '<!@(find <(DEPTH)/testing/android -name "*.java")',
+ 'native_test_launcher.cc'
+ ],
+ 'outputs': [
+ # Awkwardly, we build a Debug APK even when gyp is in
+ # Release mode. I don't think it matters (e.g. we're
+ # probably happy to not codesign) but naming should be
+ # fixed. The -debug name is an aspect of the android
+ # SDK antfiles (e.g. ${sdk.dir}/tools/ant/build.xml)
+ '<(PRODUCT_DIR)/ChromeNativeTests-debug.apk',
+ ],
+ 'action': [
+ 'ant',
+ '-DPRODUCT_DIR=<(PRODUCT_DIR)',
+ '-buildfile',
+ '<(DEPTH)/testing/android/native_test_apk.xml',
+ ]
+ }
+ ],
+ },
+ {
+ 'target_name': 'native_test_native_code',
+ 'message': 'building native pieces of native test package',
+ 'type': 'static_library',
+ 'sources': [
+ 'native_test_launcher.cc',
+ ],
+ 'direct_dependent_settings': {
+ 'ldflags!': [
+ # JNI_OnLoad is implemented in a .a and we need to
+ # re-export in the .so.
+ '-Wl,--exclude-libs=ALL',
+ ],
+ },
+ 'dependencies': [
+ 'jni_headers',
+ '../../base/base.gyp:base',
+ '../../base/base.gyp:test_support_base',
+ '../gtest.gyp:gtest',
+ ],
+ },
+ {
+ 'target_name': 'jni_headers',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'generate_jni_headers',
+ 'inputs': [
+ '../../base/android/jni_generator/jni_generator.py',
+ 'java/src/org/chromium/native_test/ChromeNativeTestActivity.java'
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/testing/android/jni/'
+ 'chrome_native_test_activity_jni.h',
+ ],
+ 'action': [
+ 'python',
+ '../../base/android/jni_generator/jni_generator.py',
+ '-o',
+ '<@(_inputs)',
+ '<@(_outputs)',
+ ],
+ }
+ ],
+ # So generated jni headers can be found by targets that
+ # depend on this.
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ },
+ },
+ ],
+ }]
+ ],
+}
diff --git a/testing/android/native_test_apk.xml b/testing/android/native_test_apk.xml
new file mode 100644
index 0000000..bae45ce
--- /dev/null
+++ b/testing/android/native_test_apk.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (c) 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.
+-->
+
+<project name="ChromeNativeTests" default="debug" basedir=".">
+
+ <description>
+ Building native test runner ChromeNativeTests_replaceme.apk
+ </description>
+
+ <property environment="env"/>
+ <property name="sdk.dir" location="${env.ANDROID_SDK_ROOT}"/>
+ <property name="sdk.version" value="${env.ANDROID_SDK_VERSION}"/>
+ <property name="src" location="."/>
+
+ <!-- TODO(jrg): although the source.dir setting points the android
+ antfiles to the correct location for finding our java, and our jars
+ are rebuilt when the java changes, a re-javac does not trigger a
+ re-package of our apk. Fix. -->
+ <property name="source.dir" location="java"/>
+
+ <property name="target" value="android-${env.ANDROID_SDK_VERSION}"/>
+
+ <condition property="location.base"
+ value="${sdk.dir}"
+ else="${sdk.dir}/platforms/android-${sdk.version}">
+ <isset property="env.ANDROID_BUILD_TOP"/>
+ </condition>
+
+ <!-- We expect PRODUCT_DIR to be set like the gyp var
+ (e.g. $ROOT/out/Debug) -->
+ <!-- TODO(jrg): ideally we need this to run before -build-setup, where
+ directories are made based on this variable. -->
+ <target name="-pre-build">
+ <if>
+ <condition>
+ <isset property="PRODUCT_DIR" />
+ </condition>
+ <else>
+ <fail message="PRODUCT_DIR env var not set?" />
+ </else>
+ </if>
+ </target>
+
+ <property name="out.dir" location="${PRODUCT_DIR}/replaceme_apk"/>
+
+ <!-- TODO(jrg): should I make this directory specific to the apk? -->
+ <property name="gen.absolute.dir" value="${out.dir}/gen"/>
+
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>
diff --git a/testing/android/native_test_launcher.cc b/testing/android/native_test_launcher.cc
new file mode 100644
index 0000000..bc5d47f
--- /dev/null
+++ b/testing/android/native_test_launcher.cc
@@ -0,0 +1,190 @@
+// Copyright (c) 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.
+
+#include <stdio.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/android/path_utils.h"
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "base/string_tokenizer.h"
+#include "base/string_util.h"
+#include "base/test/test_suite.h"
+#include "testing/android/jni/chrome_native_test_activity_jni.h"
+#include "gtest/gtest.h"
+
+// GTest's main function.
+extern int main(int argc, char** argv);
+
+namespace {
+
+void ParseArgsFromString(const std::string& command_line,
+ std::vector<std::string>* args) {
+ StringTokenizer tokenizer(command_line, kWhitespaceASCII);
+ tokenizer.set_quote_chars("\"");
+ while (tokenizer.GetNext()) {
+ std::string token;
+ RemoveChars(tokenizer.token(), "\"", &token);
+ args->push_back(token);
+ }
+}
+
+void ParseArgsFromCommandLineFile(const FilePath& internal_data_path,
+ std::vector<std::string>* args) {
+ static const char kCommandLineFile[] = "chrome-native-tests-command-line";
+ FilePath command_line(internal_data_path.Append(FilePath(kCommandLineFile)));
+ std::string command_line_string;
+ if (file_util::ReadFileToString(command_line, &command_line_string)) {
+ ParseArgsFromString(command_line_string, args);
+ }
+}
+
+void ArgsToArgv(const std::vector<std::string>& args,
+ std::vector<char*>* argv) {
+ // We need to pass in a non-const char**.
+ int argc = args.size();
+ argv->resize(argc);
+ for (int i = 0; i < argc; ++i)
+ (*argv)[i] = const_cast<char*>(args[i].c_str());
+}
+
+// As we are the native side of an Android app, we don't have any 'console', so
+// gtest's standard output goes nowhere.
+// Instead, we inject an "EventListener" in gtest and then we print the results
+// using LOG, which goes to adb logcat.
+class AndroidLogPrinter : public ::testing::EmptyTestEventListener {
+ public:
+ void Init(int* argc, char** argv);
+
+ // EmptyTestEventListener
+ virtual void OnTestProgramStart(
+ const ::testing::UnitTest& unit_test) OVERRIDE;
+ virtual void OnTestStart(const ::testing::TestInfo& test_info) OVERRIDE;
+ virtual void OnTestPartResult(
+ const ::testing::TestPartResult& test_part_result) OVERRIDE;
+ virtual void OnTestEnd(const ::testing::TestInfo& test_info) OVERRIDE;
+ virtual void OnTestProgramEnd(const ::testing::UnitTest& unit_test) OVERRIDE;
+};
+
+void AndroidLogPrinter::Init(int* argc, char** argv) {
+ // InitGoogleTest must be called befure we add ourselves as a listener.
+ ::testing::InitGoogleTest(argc, argv);
+ ::testing::TestEventListeners& listeners =
+ ::testing::UnitTest::GetInstance()->listeners();
+ // Adds a listener to the end. Google Test takes the ownership.
+ listeners.Append(this);
+}
+
+void AndroidLogPrinter::OnTestProgramStart(
+ const ::testing::UnitTest& unit_test) {
+ std::string msg = StringPrintf("[ START ] %d",
+ unit_test.test_to_run_count());
+ LOG(ERROR) << msg;
+}
+
+void AndroidLogPrinter::OnTestStart(const ::testing::TestInfo& test_info) {
+ std::string msg = StringPrintf("[ RUN ] %s.%s",
+ test_info.test_case_name(), test_info.name());
+ LOG(ERROR) << msg;
+}
+
+void AndroidLogPrinter::OnTestPartResult(
+ const ::testing::TestPartResult& test_part_result) {
+ std::string msg = StringPrintf(
+ "%s in %s:%d\n%s\n",
+ test_part_result.failed() ? "*** Failure" : "Success",
+ test_part_result.file_name(),
+ test_part_result.line_number(),
+ test_part_result.summary());
+ LOG(ERROR) << msg;
+}
+
+void AndroidLogPrinter::OnTestEnd(const ::testing::TestInfo& test_info) {
+ std::string msg = StringPrintf("%s %s.%s",
+ test_info.result()->Failed() ? "[ FAILED ]" : "[ OK ]",
+ test_info.test_case_name(), test_info.name());
+ LOG(ERROR) << msg;
+}
+
+void AndroidLogPrinter::OnTestProgramEnd(
+ const ::testing::UnitTest& unit_test) {
+ std::string msg = StringPrintf("[ END ] %d",
+ unit_test.successful_test_count());
+ LOG(ERROR) << msg;
+}
+
+void LibraryLoadedOnMainThread(JNIEnv* env) {
+ static const char* const kInitialArgv[] = { "ChromeTestActivity" };
+
+ {
+ // We need a test suite to be created before we do any tracing or
+ // logging: it creates a global at_exit_manager and initializes
+ // internal gtest data structures based on the command line.
+ // It needs to be scoped as it also resets the CommandLine.
+ std::vector<std::string> args;
+ FilePath path("/data/user/0/org.chromium.native_tests/files/");
+ ParseArgsFromCommandLineFile(path, &args);
+ std::vector<char*> argv;
+ ArgsToArgv(args, &argv);
+ base::TestSuite test_suite(argv.size(), &argv[0]);
+ }
+
+ CommandLine::Init(arraysize(kInitialArgv), kInitialArgv);
+
+ logging::InitLogging(NULL,
+ logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
+ logging::DONT_LOCK_LOG_FILE,
+ logging::DELETE_OLD_LOG_FILE,
+ logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
+ // To view log output with IDs and timestamps use "adb logcat -v threadtime".
+ logging::SetLogItems(false, // Process ID
+ false, // Thread ID
+ false, // Timestamp
+ false); // Tick count
+ VLOG(0) << "Chromium logging enabled: level = " << logging::GetMinLogLevel()
+ << ", default verbosity = " << logging::GetVlogVerbosity();
+ base::android::RegisterPathUtils(env);
+}
+
+} // namespace
+
+// This method is called on a separate java thread so that we won't trigger
+// an ANR.
+static void RunTests(JNIEnv* env, jobject obj, jstring jfiles_dir) {
+ FilePath files_dir(base::android::ConvertJavaStringToUTF8(env, jfiles_dir));
+ // A few options, such "--gtest_list_tests", will just use printf directly
+ // and won't use the "AndroidLogPrinter". Redirect stdout to a known file.
+ FilePath stdout_path(files_dir.Append(FilePath("stdout.txt")));
+ freopen(stdout_path.value().c_str(), "w", stdout);
+
+ std::vector<std::string> args;
+ ParseArgsFromCommandLineFile(files_dir, &args);
+
+ // We need to pass in a non-const char**.
+ std::vector<char*> argv;
+ ArgsToArgv(args, &argv);
+
+ int argc = argv.size();
+ // This object is owned by gtest.
+ AndroidLogPrinter* log = new AndroidLogPrinter();
+ log->Init(&argc, &argv[0]);
+
+ main(argc, &argv[0]);
+}
+
+// This is called by the VM when the shared library is first loaded.
+JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+ base::android::InitVM(vm);
+ JNIEnv* env = base::android::AttachCurrentThread();
+ if (!RegisterNativesImpl(env)) {
+ return -1;
+ }
+ LibraryLoadedOnMainThread(env);
+ return JNI_VERSION_1_4;
+}
diff --git a/testing/android/res/values/strings.xml b/testing/android/res/values/strings.xml
new file mode 100644
index 0000000..1597eb69
--- /dev/null
+++ b/testing/android/res/values/strings.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 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.
+-->
+<resources>
+ <!-- argument to pass to System.loadLibrary(). E.g. base_unittests
+ for loading libbase_unittests.so. -->
+ <string name="native_library">replaceme</string>
+</resources>