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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 #!/usr/bin/python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 # On Android we build unit test bundles as shared libraries. To run
7 # tests, we launch a special "test runner" apk which loads the library
8 # then jumps into it. Since java is required for many tests
9 # (e.g. PathUtils.java), a "pure native" test bundle is inadequate.
10 #
11 # This script, generate_native_test.py, is used to generate the source
12 # for an apk that wraps a unit test shared library bundle. That
13 # allows us to have a single boiler-plate application be used across
14 # all unit test bundles.
15
16 import logging
17 import optparse
18 import os
19 import re
20 import shutil
21 import subprocess
22 import sys
23
24
25 class NativeTestApkGenerator(object):
26 """Generate a native test apk source tree.
27
28 TODO(jrg): develop this more so the activity name is replaced as
29 well. That will allow multiple test runners to be installed at the
30 same time. (The complication is that it involves renaming a java
31 class, which implies regeneration of a jni header, and on and on...)
32 """
33
34 # Files or directories we need to copy to create a complete apk test shell.
35 _SOURCE_FILES = ['AndroidManifest.xml',
36 'native_test_apk.xml',
37 'res', # res/values/strings.xml
38 'java', # .../ChromeNativeTestActivity.java
39 ]
40
41 # Files in the destion directory that have a "replaceme" string
42 # which should be replaced by the basename of the shared library.
43 # Note we also update the filename if 'replaceme' is itself found in
44 # the filename.
45 _REPLACEME_FILES = ['AndroidManifest.xml',
46 'native_test_apk.xml',
47 'res/values/strings.xml']
48
49 def __init__(self, native_library, jars, output_directory):
50 self._native_library = native_library
51 self._jars = jars
52 self._output_directory = output_directory
53 self._root_name = None
54 if self._native_library:
55 self._root_name = self._LibraryRoot()
56 logging.warn('root name: %s' % self._root_name)
57
58
59 def _LibraryRoot(self):
60 """Return a root name for a shared library.
61
62 The root name should be suitable for substitution in apk files
63 like the manifest. For example, blah/foo/libbase_unittests.so
64 becomes base_unittests.
65 """
66 rootfinder = re.match('.?lib(.+).so',
67 os.path.basename(self._native_library))
68 if rootfinder:
69 return rootfinder.group(1)
70 else:
71 return None
72
73 def _CopyTemplateFiles(self):
74 """Copy files needed to build a new apk.
75
76 TODO(jrg): add more smarts so we don't change file timestamps if
77 the files don't change?
78 """
79 srcdir = os.path.dirname(os.path.realpath( __file__))
80 if not os.path.exists(self._output_directory):
81 os.makedirs(self._output_directory)
82 for f in self._SOURCE_FILES:
83 src = os.path.join(srcdir, f)
84 dest = os.path.join(self._output_directory, f)
85 if os.path.isfile(src):
86 if os.path.exists(dest):
87 os.remove(dest)
88 logging.warn('%s --> %s' % (src, dest))
89 shutil.copyfile(src, dest)
90 else: # directory
91 if os.path.exists(dest):
92 # One more sanity check since we're deleting a directory...
93 if not '/out/' in dest:
94 raise Exception('Unbelievable output directory; bailing for safety')
95 shutil.rmtree(dest)
96 logging.warn('%s --> %s' % (src, dest))
97 shutil.copytree(src, dest)
98
99 def _ReplaceStrings(self):
100 """Replace 'replaceme' strings in generated files with a root libname.
101
102 If we have no root libname (e.g. no shlib was specified), do nothing.
103 """
104 if not self._root_name:
105 return
106 logging.warn('Replacing "replaceme" with ' + self._root_name)
107 for f in self._REPLACEME_FILES:
108 dest = os.path.join(self._output_directory, f)
109 contents = open(dest).read()
110 contents = contents.replace('replaceme', self._root_name)
111 dest = dest.replace('replaceme', self._root_name) # update the filename!
112 open(dest, "w").write(contents)
113
114 def _CopyLibraryAndJars(self):
115 """Copy the shlib and jars into the apk source tree (if relevant)"""
116 if self._native_library:
117 destdir = os.path.join(self._output_directory, 'libs/armeabi')
118 if not os.path.exists(destdir):
119 os.makedirs(destdir)
120 dest = os.path.join(destdir, os.path.basename(self._native_library))
121 logging.warn('%s --> %s' % (self._native_library, dest))
122 shutil.copyfile(self._native_library, dest)
123 if self._jars:
124 destdir = os.path.join(self._output_directory, 'libs')
125 if not os.path.exists(destdir):
126 os.makedirs(destdir)
127 for jar in self._jars:
128 dest = os.path.join(destdir, os.path.basename(jar))
129 logging.warn('%s --> %s' % (jar, dest))
130 shutil.copyfile(jar, dest)
131
132 def CreateBundle(self):
133 """Create the apk bundle source and assemble components."""
134 self._CopyTemplateFiles()
135 self._ReplaceStrings()
136 self._CopyLibraryAndJars()
137
138 def Compile(self, ant_args):
139 """Build the generated apk with ant.
140
141 Args:
142 ant_args: extra args to pass to ant
143 """
144 cmd = ['ant']
145 if ant_args:
146 cmd.append(ant_args)
147 cmd.extend(['-buildfile',
148 os.path.join(self._output_directory, 'native_test_apk.xml')])
149 logging.warn(cmd)
150 p = subprocess.Popen(cmd, stderr=subprocess.STDOUT)
151 (stdout, _) = p.communicate()
152 logging.warn(stdout)
153 if p.returncode != 0:
154 logging.error('Ant return code %d' % p.returncode)
155 sys.exit(p.returncode)
156
157
158 def main(argv):
159 parser = optparse.OptionParser()
160 parser.add_option('--verbose',
161 help='Be verbose')
162 parser.add_option('--native_library',
163 help='Full name of native shared library test bundle')
164 parser.add_option('--jar', action='append',
165 help='Include this jar; can be specified multiple times')
166 parser.add_option('--output',
167 help='Output directory for generated files.')
168 parser.add_option('--ant-compile', action='store_true',
169 help='If specified, build the generated apk with ant')
170 parser.add_option('--ant-args',
171 help='extra args for ant')
172
173 options, _ = parser.parse_args(argv)
174
175 # It is not an error to specify no native library; the apk should
176 # still be generated and build. It will, however, print
177 # NATIVE_LOADER_FAILED when run.
178 if not options.output:
179 raise Exception('No output directory specified for generated files')
180
181 if options.verbose:
182 logging.basicConfig(level=logging.DEBUG, format=' %(message)s')
183
184 ntag = NativeTestApkGenerator(native_library=options.native_library,
185 jars=options.jar,
186 output_directory=options.output)
187 ntag.CreateBundle()
188 if options.ant_compile:
189 ntag.Compile(options.ant_args)
190
191 logging.warn('COMPLETE.')
192
193 if __name__ == '__main__':
194 sys.exit(main(sys.argv))
OLDNEW
« 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