Bug 1454184 [wpt PR 10474] - [Resource Timing] Align TAO parsing to spec, a=testonly
[gecko.git] / testing / remotecppunittests.py
blob322a9914fe2488e334d7543bf334556170d1845c
1 #!/usr/bin/env python
3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 import os
8 import sys
9 import subprocess
10 from zipfile import ZipFile
11 import runcppunittests as cppunittests
12 import mozcrash
13 import mozfile
14 import mozinfo
15 import mozlog
16 import posixpath
17 from mozdevice import ADBAndroid, ADBProcessError
19 try:
20 from mozbuild.base import MozbuildObject
21 build_obj = MozbuildObject.from_environment()
22 except ImportError:
23 build_obj = None
26 class RemoteCPPUnitTests(cppunittests.CPPUnitTests):
28 def __init__(self, options, progs):
29 cppunittests.CPPUnitTests.__init__(self)
30 self.options = options
31 self.device = ADBAndroid(adb=options.adb_path or 'adb',
32 device=options.device_serial,
33 test_root=options.remote_test_root)
34 self.remote_test_root = posixpath.join(self.device.test_root, "cppunittests")
35 self.remote_bin_dir = posixpath.join(self.remote_test_root, "b")
36 self.remote_tmp_dir = posixpath.join(self.remote_test_root, "tmp")
37 self.remote_home_dir = posixpath.join(self.remote_test_root, "h")
38 if options.setup:
39 self.setup_bin(progs)
41 def setup_bin(self, progs):
42 self.device.rm(self.remote_test_root, force=True, recursive=True)
43 self.device.mkdir(self.remote_home_dir, parents=True)
44 self.device.mkdir(self.remote_tmp_dir)
45 self.push_libs()
46 self.push_progs(progs)
47 self.device.chmod(self.remote_bin_dir, recursive=True, root=True)
49 def push_libs(self):
50 if self.options.local_apk:
51 with mozfile.TemporaryDirectory() as tmpdir:
52 apk_contents = ZipFile(self.options.local_apk)
54 for info in apk_contents.infolist():
55 if info.filename.endswith(".so"):
56 print >> sys.stderr, "Pushing %s.." % info.filename
57 remote_file = posixpath.join(
58 self.remote_bin_dir, os.path.basename(info.filename))
59 apk_contents.extract(info, tmpdir)
60 local_file = os.path.join(tmpdir, info.filename)
61 with open(local_file) as f:
62 # Decompress xz-compressed file.
63 if f.read(5)[1:] == '7zXZ':
64 cmd = [
65 'xz', '-df', '--suffix', '.so', local_file]
66 subprocess.check_output(cmd)
67 # xz strips the ".so" file suffix.
68 os.rename(local_file[:-3], local_file)
69 self.device.push(local_file, remote_file)
71 elif self.options.local_lib:
72 for file in os.listdir(self.options.local_lib):
73 if file.endswith(".so"):
74 print >> sys.stderr, "Pushing %s.." % file
75 remote_file = posixpath.join(self.remote_bin_dir, file)
76 local_file = os.path.join(self.options.local_lib, file)
77 self.device.push(local_file, remote_file)
78 # Additional libraries may be found in a sub-directory such as
79 # "lib/armeabi-v7a"
80 for subdir in ["assets", "lib"]:
81 local_arm_lib = os.path.join(self.options.local_lib, subdir)
82 if os.path.isdir(local_arm_lib):
83 for root, dirs, files in os.walk(local_arm_lib):
84 for file in files:
85 if (file.endswith(".so")):
86 print >> sys.stderr, "Pushing %s.." % file
87 remote_file = posixpath.join(
88 self.remote_bin_dir, file)
89 local_file = os.path.join(root, file)
90 self.device.push(local_file, remote_file)
92 def push_progs(self, progs):
93 for local_file in progs:
94 remote_file = posixpath.join(
95 self.remote_bin_dir, os.path.basename(local_file))
96 self.device.push(local_file, remote_file)
98 def build_environment(self):
99 env = self.build_core_environment()
100 env['LD_LIBRARY_PATH'] = self.remote_bin_dir
101 env["TMPDIR"] = self.remote_tmp_dir
102 env["HOME"] = self.remote_home_dir
103 env["MOZ_XRE_DIR"] = self.remote_bin_dir
104 if self.options.add_env:
105 for envdef in self.options.add_env:
106 envdef_parts = envdef.split("=", 1)
107 if len(envdef_parts) == 2:
108 env[envdef_parts[0]] = envdef_parts[1]
109 elif len(envdef_parts) == 1:
110 env[envdef_parts[0]] = ""
111 else:
112 self.log.warning(
113 "invalid --addEnv option skipped: %s" % envdef)
115 return env
117 def run_one_test(self, prog, env, symbols_path=None, interactive=False,
118 timeout_factor=1):
120 Run a single C++ unit test program remotely.
122 Arguments:
123 * prog: The path to the test program to run.
124 * env: The environment to use for running the program.
125 * symbols_path: A path to a directory containing Breakpad-formatted
126 symbol files for producing stack traces on crash.
127 * timeout_factor: An optional test-specific timeout multiplier.
129 Return True if the program exits with a zero status, False otherwise.
131 basename = os.path.basename(prog)
132 remote_bin = posixpath.join(self.remote_bin_dir, basename)
133 self.log.test_start(basename)
134 test_timeout = cppunittests.CPPUnitTests.TEST_PROC_TIMEOUT * \
135 timeout_factor
137 try:
138 output = self.device.shell_output(remote_bin, env=env,
139 cwd=self.remote_home_dir,
140 timeout=test_timeout)
141 returncode = 0
142 except ADBProcessError as e:
143 output = e.adb_process.stdout
144 returncode = e.adb_process.exitcode
146 self.log.process_output(basename, "\n%s" % output,
147 command=[remote_bin])
148 with mozfile.TemporaryDirectory() as tempdir:
149 self.device.pull(self.remote_home_dir, tempdir)
150 if mozcrash.check_for_crashes(tempdir, symbols_path,
151 test_name=basename):
152 self.log.test_end(basename, status='CRASH', expected='PASS')
153 return False
154 result = returncode == 0
155 if not result:
156 self.log.test_end(basename, status='FAIL', expected='PASS',
157 message=("test failed with return code %s" %
158 returncode))
159 else:
160 self.log.test_end(basename, status='PASS', expected='PASS')
161 return result
164 class RemoteCPPUnittestOptions(cppunittests.CPPUnittestOptions):
166 def __init__(self):
167 cppunittests.CPPUnittestOptions.__init__(self)
168 defaults = {}
170 self.add_option("--deviceSerial", action="store",
171 type="string", dest="device_serial",
172 help="serial ID of device")
173 defaults["device_serial"] = None
175 self.add_option("--adbPath", action="store",
176 type="string", dest="adb_path",
177 help="Path to adb")
178 defaults["adb_path"] = None
180 self.add_option("--noSetup", action="store_false",
181 dest="setup",
182 help="do not copy any files to device (to be used only if "
183 "device is already setup)")
184 defaults["setup"] = True
186 self.add_option("--localLib", action="store",
187 type="string", dest="local_lib",
188 help="location of libraries to push -- preferably stripped")
189 defaults["local_lib"] = None
191 self.add_option("--apk", action="store",
192 type="string", dest="local_apk",
193 help="local path to Fennec APK")
194 defaults["local_apk"] = None
196 self.add_option("--localBinDir", action="store",
197 type="string", dest="local_bin",
198 help="local path to bin directory")
199 defaults[
200 "local_bin"] = build_obj.bindir if build_obj is not None else None
202 self.add_option("--remoteTestRoot", action="store",
203 type="string", dest="remote_test_root",
204 help="remote directory to use as test root (eg. /data/local/tests)")
205 # /data/local/tests is used because it is usually not possible to set +x permissions
206 # on binaries on /mnt/sdcard
207 defaults["remote_test_root"] = "/data/local/tests"
209 self.add_option("--addEnv", action="append",
210 type="string", dest="add_env",
211 help="additional remote environment variable definitions "
212 "(eg. --addEnv \"somevar=something\")")
213 defaults["add_env"] = None
215 self.set_defaults(**defaults)
218 def run_test_harness(options, args):
219 options.xre_path = os.path.abspath(options.xre_path)
220 cppunittests.update_mozinfo()
221 progs = cppunittests.extract_unittests_from_args(args,
222 mozinfo.info,
223 options.manifest_path)
224 tester = RemoteCPPUnitTests(options, [item[0] for item in progs])
225 result = tester.run_tests(progs, options.xre_path, options.symbols_path)
226 return result
229 def main():
230 parser = RemoteCPPUnittestOptions()
231 mozlog.commandline.add_logging_group(parser)
232 options, args = parser.parse_args()
233 if not args:
234 print >>sys.stderr, """Usage: %s <test binary> [<test binary>...]""" % sys.argv[0]
235 sys.exit(1)
236 if options.local_lib is not None and not os.path.isdir(options.local_lib):
237 print >>sys.stderr, """Error: --localLib directory %s not found""" % options.local_lib
238 sys.exit(1)
239 if options.local_apk is not None and not os.path.isfile(options.local_apk):
240 print >>sys.stderr, """Error: --apk file %s not found""" % options.local_apk
241 sys.exit(1)
242 if not options.xre_path:
243 print >>sys.stderr, """Error: --xre-path is required"""
244 sys.exit(1)
246 log = mozlog.commandline.setup_logging("remotecppunittests", options,
247 {"tbpl": sys.stdout})
248 try:
249 result = run_test_harness(options, args)
250 except Exception as e:
251 log.error(str(e))
252 result = False
253 sys.exit(0 if result else 1)
256 if __name__ == '__main__':
257 main()