mach_test_package_initialize.py (7895B)
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 json 6 import os 7 import sys 8 import types 9 10 SEARCH_PATHS = [ 11 "gtest", 12 "marionette/client", 13 "marionette/harness", 14 "mochitest", 15 "mozbase/manifestparser", 16 "mozbase/mozcrash", 17 "mozbase/mozdebug", 18 "mozbase/mozdevice", 19 "mozbase/mozfile", 20 "mozbase/mozgeckoprofile", 21 "mozbase/mozhttpd", 22 "mozbase/mozinfo", 23 "mozbase/mozinstall", 24 "mozbase/mozleak", 25 "mozbase/mozlog", 26 "mozbase/moznetwork", 27 "mozbase/mozpower", 28 "mozbase/mozprocess", 29 "mozbase/mozprofile", 30 "mozbase/mozrunner", 31 "mozbase/mozscreenshot", 32 "mozbase/mozserve", 33 "mozbase/mozshellutil", 34 "mozbase/mozsystemmonitor", 35 "mozbase/moztest", 36 "mozbase/mozversion", 37 "reftest", 38 "tools/mach", 39 "tools/mozterm", 40 "tools/geckoprocesstypes_generator", 41 "tools/six", 42 "tools/wptserve", 43 "web-platform", 44 "web-platform/tests/tools/wptrunner", 45 "xpcshell", 46 ] 47 48 49 CATEGORIES = { 50 "testing": { 51 "short": "Testing", 52 "long": "Run tests.", 53 "priority": 30, 54 }, 55 "devenv": { 56 "short": "Development Environment", 57 "long": "Set up and configure your development environment.", 58 "priority": 20, 59 }, 60 "misc": { 61 "short": "Potpourri", 62 "long": "Potent potables and assorted snacks.", 63 "priority": 10, 64 }, 65 "disabled": { 66 "short": "Disabled", 67 "long": "The disabled commands are hidden by default. Use -v to display them. " 68 "These commands are unavailable for your current context, " 69 'run "mach <command>" to see why.', 70 "priority": 0, 71 }, 72 } 73 74 75 IS_WIN = sys.platform in ("win32", "cygwin") 76 77 78 def ancestors(path, depth=0): 79 """Emit the parent directories of a path.""" 80 count = 1 81 while path and count != depth: 82 yield path 83 newpath = os.path.dirname(path) 84 if newpath == path: 85 break 86 path = newpath 87 count += 1 88 89 90 def activate_mozharness_venv(context): 91 """Activate the mozharness virtualenv in-process.""" 92 venv = os.path.join( 93 context.mozharness_workdir, 94 context.mozharness_config.get("virtualenv_path", "venv"), 95 ) 96 97 if not os.path.isdir(venv): 98 print(f"No mozharness virtualenv detected at '{venv}'.") 99 return 1 100 101 venv_bin = os.path.join(venv, "Scripts" if IS_WIN else "bin") 102 activate_path = os.path.join(venv_bin, "activate_this.py") 103 104 exec(open(activate_path).read(), dict(__file__=activate_path)) 105 106 if isinstance(os.environ["PATH"], str): 107 os.environ["PATH"] = os.environ["PATH"].encode("utf-8") 108 109 # sys.executable is used by mochitest-media to start the websocketprocessbridge, 110 # for some reason it doesn't get set when calling `activate_this.py` so set it 111 # here instead. 112 binary = "python" 113 if IS_WIN: 114 binary += ".exe" 115 sys.executable = os.path.join(venv_bin, binary) 116 117 118 def find_firefox(context): 119 """Try to automagically find the firefox binary.""" 120 import mozinstall 121 122 search_paths = [] 123 124 # Check for a mozharness setup 125 config = context.mozharness_config 126 if config and "binary_path" in config: 127 return config["binary_path"] 128 elif config: 129 search_paths.append(os.path.join(context.mozharness_workdir, "application")) 130 131 # Check for test-stage setup 132 dist_bin = os.path.join(os.path.dirname(context.package_root), "bin") 133 if os.path.isdir(dist_bin): 134 search_paths.append(dist_bin) 135 136 for path in search_paths: 137 try: 138 return mozinstall.get_binary(path, "firefox") 139 except mozinstall.InvalidBinary: 140 continue 141 142 143 def find_hostutils(context): 144 workdir = context.mozharness_workdir 145 hostutils = os.path.join(workdir, "hostutils") 146 for fname in os.listdir(hostutils): 147 fpath = os.path.join(hostutils, fname) 148 if os.path.isdir(fpath) and fname.startswith("host-utils"): 149 return fpath 150 151 152 def normalize_test_path(test_root, path): 153 if os.path.isabs(path) or os.path.exists(path): 154 return os.path.normpath(os.path.abspath(path)) 155 156 for parent in ancestors(test_root): 157 test_path = os.path.join(parent, path) 158 if os.path.exists(test_path): 159 return os.path.normpath(os.path.abspath(test_path)) 160 # Not a valid path? Return as is and let test harness deal with it 161 return path 162 163 164 def bootstrap(test_package_root): 165 test_package_root = os.path.abspath(test_package_root) 166 167 sys.path[0:0] = [os.path.join(test_package_root, path) for path in SEARCH_PATHS] 168 import mach.main 169 from mach.command_util import MachCommandReference, load_commands_from_spec 170 171 # Centralized registry of available mach commands 172 MACH_COMMANDS = { 173 "gtest": MachCommandReference("gtest/mach_test_package_commands.py"), 174 "marionette-test": MachCommandReference( 175 "marionette/mach_test_package_commands.py" 176 ), 177 "mochitest": MachCommandReference("mochitest/mach_test_package_commands.py"), 178 "geckoview-junit": MachCommandReference( 179 "mochitest/mach_test_package_commands.py" 180 ), 181 "reftest": MachCommandReference("reftest/mach_test_package_commands.py"), 182 "mach-commands": MachCommandReference( 183 "python/mach/mach/commands/commandinfo.py" 184 ), 185 "mach-debug-commands": MachCommandReference( 186 "python/mach/mach/commands/commandinfo.py" 187 ), 188 "mach-completion": MachCommandReference( 189 "python/mach/mach/commands/commandinfo.py" 190 ), 191 "web-platform-tests": MachCommandReference( 192 "web-platform/mach_test_package_commands.py" 193 ), 194 "wpt": MachCommandReference("web-platform/mach_test_package_commands.py"), 195 "xpcshell-test": MachCommandReference("xpcshell/mach_test_package_commands.py"), 196 } 197 198 def populate_context(context, key=None): 199 # These values will be set lazily, and cached after first being invoked. 200 if key == "package_root": 201 return test_package_root 202 203 if key == "bin_dir": 204 return os.path.join(test_package_root, "bin") 205 206 if key == "certs_dir": 207 return os.path.join(test_package_root, "certs") 208 209 if key == "module_dir": 210 return os.path.join(test_package_root, "modules") 211 212 if key == "ancestors": 213 return ancestors 214 215 if key == "normalize_test_path": 216 return normalize_test_path 217 218 if key == "firefox_bin": 219 return find_firefox(context) 220 221 if key == "hostutils": 222 return find_hostutils(context) 223 224 if key == "mozharness_config": 225 for dir_path in ancestors(context.package_root): 226 mozharness_config = os.path.join(dir_path, "logs", "localconfig.json") 227 if os.path.isfile(mozharness_config): 228 with open(mozharness_config, "rb") as f: 229 return json.load(f) 230 return {} 231 232 if key == "mozharness_workdir": 233 config = context.mozharness_config 234 if config: 235 return os.path.join(config["base_work_dir"], config["work_dir"]) 236 237 if key == "activate_mozharness_venv": 238 return types.MethodType(activate_mozharness_venv, context) 239 240 mach = mach.main.Mach(os.getcwd()) 241 mach.populate_context_handler = populate_context 242 243 for category, meta in CATEGORIES.items(): 244 mach.define_category(category, meta["short"], meta["long"], meta["priority"]) 245 246 # Depending on which test zips were extracted, 247 # the command module might not exist 248 load_commands_from_spec(MACH_COMMANDS, test_package_root, missing_ok=True) 249 250 return mach