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

Unified Diff: testing/android/generate_native_test.py

Issue 10051021: apk-based test runner work for android (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 8 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/generate_native_test.py
diff --git a/testing/android/generate_native_test.py b/testing/android/generate_native_test.py
new file mode 100755
index 0000000000000000000000000000000000000000..7b7853d2894e2f55f43421ee829813cbc271d520
--- /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))
« no previous file with comments | « testing/android/OWNERS ('k') | testing/android/java/src/org/chromium/native_test/ChromeNativeTestActivity.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698