Bug 1509459 - Get the flexbox highlighter state if the highlighter is ready in the...
[gecko.git] / testing / remotecppunittests.py
blob2cec248aff859fc565b254c84ebfa7513044e2af
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, ADBTimeoutError
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 ADBTimeoutError:
143 raise
144 except ADBProcessError as e:
145 output = e.adb_process.stdout
146 returncode = e.adb_process.exitcode
148 self.log.process_output(basename, "\n%s" % output,
149 command=[remote_bin])
150 with mozfile.TemporaryDirectory() as tempdir:
151 self.device.pull(self.remote_home_dir, tempdir)
152 if mozcrash.check_for_crashes(tempdir, symbols_path,
153 test_name=basename):
154 self.log.test_end(basename, status='CRASH', expected='PASS')
155 return False
156 result = returncode == 0
157 if not result:
158 self.log.test_end(basename, status='FAIL', expected='PASS',
159 message=("test failed with return code %s" %
160 returncode))
161 else:
162 self.log.test_end(basename, status='PASS', expected='PASS')
163 return result
166 class RemoteCPPUnittestOptions(cppunittests.CPPUnittestOptions):
168 def __init__(self):
169 cppunittests.CPPUnittestOptions.__init__(self)
170 defaults = {}
172 self.add_option("--deviceSerial", action="store",
173 type="string", dest="device_serial",
174 help="serial ID of device")
175 defaults["device_serial"] = None
177 self.add_option("--adbPath", action="store",
178 type="string", dest="adb_path",
179 help="Path to adb")
180 defaults["adb_path"] = None
182 self.add_option("--noSetup", action="store_false",
183 dest="setup",
184 help="do not copy any files to device (to be used only if "
185 "device is already setup)")
186 defaults["setup"] = True
188 self.add_option("--localLib", action="store",
189 type="string", dest="local_lib",
190 help="location of libraries to push -- preferably stripped")
191 defaults["local_lib"] = None
193 self.add_option("--apk", action="store",
194 type="string", dest="local_apk",
195 help="local path to Fennec APK")
196 defaults["local_apk"] = None
198 self.add_option("--localBinDir", action="store",
199 type="string", dest="local_bin",
200 help="local path to bin directory")
201 defaults[
202 "local_bin"] = build_obj.bindir if build_obj is not None else None
204 self.add_option("--remoteTestRoot", action="store",
205 type="string", dest="remote_test_root",
206 help="remote directory to use as test root (eg. /data/local/tests)")
207 # /data/local/tests is used because it is usually not possible to set +x permissions
208 # on binaries on /mnt/sdcard
209 defaults["remote_test_root"] = "/data/local/tests"
211 self.add_option("--addEnv", action="append",
212 type="string", dest="add_env",
213 help="additional remote environment variable definitions "
214 "(eg. --addEnv \"somevar=something\")")
215 defaults["add_env"] = None
217 self.set_defaults(**defaults)
220 def run_test_harness(options, args):
221 options.xre_path = os.path.abspath(options.xre_path)
222 cppunittests.update_mozinfo()
223 progs = cppunittests.extract_unittests_from_args(args,
224 mozinfo.info,
225 options.manifest_path)
226 tester = RemoteCPPUnitTests(options, [item[0] for item in progs])
227 result = tester.run_tests(progs, options.xre_path, options.symbols_path)
228 return result
231 def main():
232 parser = RemoteCPPUnittestOptions()
233 mozlog.commandline.add_logging_group(parser)
234 options, args = parser.parse_args()
235 if not args:
236 print >>sys.stderr, """Usage: %s <test binary> [<test binary>...]""" % sys.argv[0]
237 sys.exit(1)
238 if options.local_lib is not None and not os.path.isdir(options.local_lib):
239 print >>sys.stderr, """Error: --localLib directory %s not found""" % options.local_lib
240 sys.exit(1)
241 if options.local_apk is not None and not os.path.isfile(options.local_apk):
242 print >>sys.stderr, """Error: --apk file %s not found""" % options.local_apk
243 sys.exit(1)
244 if not options.xre_path:
245 print >>sys.stderr, """Error: --xre-path is required"""
246 sys.exit(1)
248 log = mozlog.commandline.setup_logging("remotecppunittests", options,
249 {"tbpl": sys.stdout})
250 try:
251 result = run_test_harness(options, args)
252 except Exception as e:
253 log.error(str(e))
254 result = False
255 sys.exit(0 if result else 1)
258 if __name__ == '__main__':
259 main()