mach_commands.py (10121B)
1 # This Source Code Form is subject to the terms of the Mozilla Public 2 # License, v. 2.0. If a copy of the MPL was not distributed with this 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5 import os 6 import re 7 import sys 8 from argparse import Namespace 9 10 from mach.decorators import Command 11 from mozbuild.base import MachCommandConditions as conditions 12 from mozbuild.base import MozbuildObject 13 14 parser = None 15 16 17 class ReftestRunner(MozbuildObject): 18 """Easily run reftests. 19 20 This currently contains just the basics for running reftests. We may want 21 to hook up result parsing, etc. 22 """ 23 24 def __init__(self, *args, **kwargs): 25 MozbuildObject.__init__(self, *args, **kwargs) 26 27 # TODO Bug 794506 remove once mach integrates with virtualenv. 28 build_path = os.path.join(self.topobjdir, "build") 29 if build_path not in sys.path: 30 sys.path.append(build_path) 31 32 self.tests_dir = os.path.join(self.topobjdir, "_tests") 33 self.reftest_dir = os.path.join(self.tests_dir, "reftest") 34 35 def _make_shell_string(self, s): 36 return "'%s'" % re.sub("'", r"'\''", s) 37 38 def _setup_objdir(self, args): 39 # reftest imports will happen from the objdir 40 sys.path.insert(0, self.reftest_dir) 41 42 tests = os.path.join(self.reftest_dir, "tests") 43 if not os.path.isdir(tests) and not os.path.islink(tests): 44 # This symbolic link is used by the desktop tests to 45 # locate the actual test files when running using file:. 46 os.symlink(self.topsrcdir, tests) 47 48 def run_desktop_test(self, **kwargs): 49 """Runs a reftest, in desktop Firefox.""" 50 import runreftest 51 52 args = Namespace(**kwargs) 53 if args.suite not in ("reftest", "crashtest", "jstestbrowser"): 54 raise Exception("None or unrecognized reftest suite type.") 55 56 default_manifest = { 57 "reftest": (self.topsrcdir, "layout", "reftests", "reftest.list"), 58 "crashtest": (self.topsrcdir, "testing", "crashtest", "crashtests.list"), 59 "jstestbrowser": ( 60 self.topobjdir, 61 "dist", 62 "test-stage", 63 "jsreftest", 64 "tests", 65 "js", 66 "src", 67 "tests", 68 "jstests.list", 69 ), 70 } 71 72 args.extraProfileFiles.append(os.path.join(self.topobjdir, "dist", "plugins")) 73 args.symbolsPath = os.path.join(self.topobjdir, "dist", "crashreporter-symbols") 74 args.sandboxReadWhitelist.extend([self.topsrcdir, self.topobjdir]) 75 76 if not args.tests: 77 args.tests = [os.path.join(*default_manifest[args.suite])] 78 79 if args.suite == "jstestbrowser": 80 args.extraProfileFiles.append( 81 os.path.join( 82 self.topobjdir, 83 "dist", 84 "test-stage", 85 "jsreftest", 86 "tests", 87 "js", 88 "src", 89 "tests", 90 "user.js", 91 ) 92 ) 93 94 self.log_manager.enable_unstructured() 95 try: 96 rv = runreftest.run_test_harness(parser, args) 97 finally: 98 self.log_manager.disable_unstructured() 99 100 return rv 101 102 def run_android_test(self, **kwargs): 103 """Runs a reftest, in an Android application.""" 104 105 args = Namespace(**kwargs) 106 if args.suite not in ("reftest", "crashtest", "jstestbrowser"): 107 raise Exception("None or unrecognized reftest suite type.") 108 109 self._setup_objdir(args) 110 import remotereftest 111 112 default_manifest = { 113 "reftest": (self.topsrcdir, "layout", "reftests", "reftest.list"), 114 "crashtest": (self.topsrcdir, "testing", "crashtest", "crashtests.list"), 115 "jstestbrowser": ( 116 self.topobjdir, 117 "dist", 118 "test-stage", 119 "jsreftest", 120 "tests", 121 "js", 122 "src", 123 "tests", 124 "jstests.list", 125 ), 126 } 127 128 if not args.tests: 129 args.tests = [os.path.join(*default_manifest[args.suite])] 130 131 args.extraProfileFiles.append( 132 os.path.join(self.topsrcdir, "mobile", "android", "fonts") 133 ) 134 135 hyphenation_path = os.path.join(self.topsrcdir, "intl", "locales") 136 137 for dirpath, dirnames, filenames in os.walk(hyphenation_path): 138 for filename in filenames: 139 if filename.endswith(".dic"): 140 args.extraProfileFiles.append(os.path.join(dirpath, filename)) 141 142 if not args.httpdPath: 143 args.httpdPath = os.path.join(self.tests_dir, "modules") 144 if not args.symbolsPath: 145 args.symbolsPath = os.path.join(self.topobjdir, "crashreporter-symbols") 146 if not args.xrePath: 147 args.xrePath = os.environ.get("MOZ_HOST_BIN") 148 if not args.app: 149 args.app = "org.mozilla.geckoview.test_runner" 150 if not args.utilityPath: 151 args.utilityPath = args.xrePath 152 args.ignoreWindowSize = True 153 154 from mozrunner.devices.android_device import get_adb_path 155 156 if not args.adb_path: 157 args.adb_path = get_adb_path(self) 158 159 if "geckoview" not in args.app: 160 args.e10s = False 161 print("using e10s=False for non-geckoview app") 162 163 # A symlink and some path manipulations are required so that test 164 # manifests can be found both locally and remotely (via a url) 165 # using the same relative path. 166 if args.suite == "jstestbrowser": 167 staged_js_dir = os.path.join( 168 self.topobjdir, "dist", "test-stage", "jsreftest" 169 ) 170 tests = os.path.join(self.reftest_dir, "jsreftest") 171 if not os.path.isdir(tests) and not os.path.islink(tests): 172 os.symlink(staged_js_dir, tests) 173 args.extraProfileFiles.append( 174 os.path.join(staged_js_dir, "tests", "js", "src", "tests", "user.js") 175 ) 176 else: 177 tests = os.path.join(self.reftest_dir, "tests") 178 if not os.path.isdir(tests) and not os.path.islink(tests): 179 os.symlink(self.topsrcdir, tests) 180 for i, path in enumerate(args.tests): 181 # Non-absolute paths are relative to the packaged directory, which 182 # has an extra tests/ at the start 183 if os.path.exists(os.path.abspath(path)): 184 path = os.path.relpath(path, os.path.join(self.topsrcdir)) 185 args.tests[i] = os.path.join("tests", path) 186 187 self.log_manager.enable_unstructured() 188 try: 189 rv = remotereftest.run_test_harness(parser, args) 190 finally: 191 self.log_manager.disable_unstructured() 192 193 return rv 194 195 196 def process_test_objects(kwargs): 197 """|mach test| works by providing a test_objects argument, from 198 which the test path must be extracted and converted into a normal 199 reftest tests argument.""" 200 201 if "test_objects" in kwargs: 202 if kwargs["tests"] is None: 203 kwargs["tests"] = [] 204 kwargs["tests"].extend(item["path"] for item in kwargs["test_objects"]) 205 del kwargs["test_objects"] 206 207 208 def get_parser(): 209 import reftestcommandline 210 211 global parser 212 here = os.path.abspath(os.path.dirname(__file__)) 213 build_obj = MozbuildObject.from_environment(cwd=here) 214 if conditions.is_android(build_obj): 215 parser = reftestcommandline.RemoteArgumentsParser() 216 else: 217 parser = reftestcommandline.DesktopArgumentsParser() 218 return parser 219 220 221 @Command( 222 "reftest", 223 category="testing", 224 description="Run reftests (layout and graphics correctness).", 225 parser=get_parser, 226 ) 227 def run_reftest(command_context, **kwargs): 228 kwargs["suite"] = "reftest" 229 return _run_reftest(command_context, **kwargs) 230 231 232 @Command( 233 "jstestbrowser", 234 category="testing", 235 description="Run js/src/tests in the browser.", 236 parser=get_parser, 237 ) 238 def run_jstestbrowser(command_context, **kwargs): 239 if command_context.substs.get("JS_DISABLE_SHELL"): 240 raise Exception( 241 "jstestbrowser requires --enable-js-shell be specified in mozconfig." 242 ) 243 command_context._mach_context.commands.dispatch( 244 "build", command_context._mach_context, what=["stage-jstests"] 245 ) 246 kwargs["suite"] = "jstestbrowser" 247 return _run_reftest(command_context, **kwargs) 248 249 250 @Command( 251 "crashtest", 252 category="testing", 253 description="Run crashtests (Check if crashes on a page).", 254 parser=get_parser, 255 ) 256 def run_crashtest(command_context, **kwargs): 257 kwargs["suite"] = "crashtest" 258 return _run_reftest(command_context, **kwargs) 259 260 261 def _run_reftest(command_context, **kwargs): 262 kwargs["topsrcdir"] = command_context.topsrcdir 263 process_test_objects(kwargs) 264 reftest = command_context._spawn(ReftestRunner) 265 # Unstructured logging must be enabled prior to calling 266 # adb which uses an unstructured logger in its constructor. 267 reftest.log_manager.enable_unstructured() 268 if conditions.is_android(command_context): 269 from mozrunner.devices.android_device import ( 270 InstallIntent, 271 verify_android_device, 272 ) 273 274 install = InstallIntent.NO if kwargs.get("no_install") else InstallIntent.YES 275 verbose = False 276 if ( 277 kwargs.get("log_mach_verbose") 278 or kwargs.get("log_tbpl_level") == "debug" 279 or kwargs.get("log_mach_level") == "debug" 280 or kwargs.get("log_raw_level") == "debug" 281 ): 282 verbose = True 283 verify_android_device( 284 command_context, 285 install=install, 286 xre=True, 287 network=True, 288 app=kwargs["app"], 289 device_serial=kwargs["deviceSerial"], 290 verbose=verbose, 291 ) 292 return reftest.run_android_test(**kwargs) 293 return reftest.run_desktop_test(**kwargs)