diff options
Diffstat (limited to 'testing/tools/mach_test_package_initialize.py')
-rw-r--r-- | testing/tools/mach_test_package_initialize.py | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/testing/tools/mach_test_package_initialize.py b/testing/tools/mach_test_package_initialize.py new file mode 100644 index 0000000000..62af41a08c --- /dev/null +++ b/testing/tools/mach_test_package_initialize.py @@ -0,0 +1,248 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import json +import os +import sys +import types + +SEARCH_PATHS = [ + "gtest", + "marionette/client", + "marionette/harness", + "mochitest", + "mozbase/manifestparser", + "mozbase/mozcrash", + "mozbase/mozdebug", + "mozbase/mozdevice", + "mozbase/mozfile", + "mozbase/mozgeckoprofile", + "mozbase/mozhttpd", + "mozbase/mozinfo", + "mozbase/mozinstall", + "mozbase/mozleak", + "mozbase/mozlog", + "mozbase/moznetwork", + "mozbase/mozpower", + "mozbase/mozprocess", + "mozbase/mozprofile", + "mozbase/mozrunner", + "mozbase/mozscreenshot", + "mozbase/mozsystemmonitor", + "mozbase/moztest", + "mozbase/mozversion", + "reftest", + "tools/mach", + "tools/mozterm", + "tools/geckoprocesstypes_generator", + "tools/six", + "tools/wptserve", + "web-platform", + "web-platform/tests/tools/wptrunner", + "xpcshell", +] + + +CATEGORIES = { + "testing": { + "short": "Testing", + "long": "Run tests.", + "priority": 30, + }, + "devenv": { + "short": "Development Environment", + "long": "Set up and configure your development environment.", + "priority": 20, + }, + "misc": { + "short": "Potpourri", + "long": "Potent potables and assorted snacks.", + "priority": 10, + }, + "disabled": { + "short": "Disabled", + "long": "The disabled commands are hidden by default. Use -v to display them. " + "These commands are unavailable for your current context, " + 'run "mach <command>" to see why.', + "priority": 0, + }, +} + + +IS_WIN = sys.platform in ("win32", "cygwin") + + +def ancestors(path, depth=0): + """Emit the parent directories of a path.""" + count = 1 + while path and count != depth: + yield path + newpath = os.path.dirname(path) + if newpath == path: + break + path = newpath + count += 1 + + +def activate_mozharness_venv(context): + """Activate the mozharness virtualenv in-process.""" + venv = os.path.join( + context.mozharness_workdir, + context.mozharness_config.get("virtualenv_path", "venv"), + ) + + if not os.path.isdir(venv): + print("No mozharness virtualenv detected at '{}'.".format(venv)) + return 1 + + venv_bin = os.path.join(venv, "Scripts" if IS_WIN else "bin") + activate_path = os.path.join(venv_bin, "activate_this.py") + + exec(open(activate_path).read(), dict(__file__=activate_path)) + + if isinstance(os.environ["PATH"], str): + os.environ["PATH"] = os.environ["PATH"].encode("utf-8") + + # sys.executable is used by mochitest-media to start the websocketprocessbridge, + # for some reason it doesn't get set when calling `activate_this.py` so set it + # here instead. + binary = "python" + if IS_WIN: + binary += ".exe" + sys.executable = os.path.join(venv_bin, binary) + + +def find_firefox(context): + """Try to automagically find the firefox binary.""" + import mozinstall + + search_paths = [] + + # Check for a mozharness setup + config = context.mozharness_config + if config and "binary_path" in config: + return config["binary_path"] + elif config: + search_paths.append(os.path.join(context.mozharness_workdir, "application")) + + # Check for test-stage setup + dist_bin = os.path.join(os.path.dirname(context.package_root), "bin") + if os.path.isdir(dist_bin): + search_paths.append(dist_bin) + + for path in search_paths: + try: + return mozinstall.get_binary(path, "firefox") + except mozinstall.InvalidBinary: + continue + + +def find_hostutils(context): + workdir = context.mozharness_workdir + hostutils = os.path.join(workdir, "hostutils") + for fname in os.listdir(hostutils): + fpath = os.path.join(hostutils, fname) + if os.path.isdir(fpath) and fname.startswith("host-utils"): + return fpath + + +def normalize_test_path(test_root, path): + if os.path.isabs(path) or os.path.exists(path): + return os.path.normpath(os.path.abspath(path)) + + for parent in ancestors(test_root): + test_path = os.path.join(parent, path) + if os.path.exists(test_path): + return os.path.normpath(os.path.abspath(test_path)) + # Not a valid path? Return as is and let test harness deal with it + return path + + +def bootstrap(test_package_root): + test_package_root = os.path.abspath(test_package_root) + + sys.path[0:0] = [os.path.join(test_package_root, path) for path in SEARCH_PATHS] + import mach.main + from mach.main import MachCommandReference + + # Centralized registry of available mach commands + MACH_COMMANDS = { + "gtest": MachCommandReference("gtest/mach_test_package_commands.py"), + "marionette-test": MachCommandReference( + "marionette/mach_test_package_commands.py" + ), + "mochitest": MachCommandReference("mochitest/mach_test_package_commands.py"), + "geckoview-junit": MachCommandReference( + "mochitest/mach_test_package_commands.py" + ), + "reftest": MachCommandReference("reftest/mach_test_package_commands.py"), + "mach-commands": MachCommandReference( + "python/mach/mach/commands/commandinfo.py" + ), + "mach-debug-commands": MachCommandReference( + "python/mach/mach/commands/commandinfo.py" + ), + "mach-completion": MachCommandReference( + "python/mach/mach/commands/commandinfo.py" + ), + "web-platform-tests": MachCommandReference( + "web-platform/mach_test_package_commands.py" + ), + "wpt": MachCommandReference("web-platform/mach_test_package_commands.py"), + "xpcshell-test": MachCommandReference("xpcshell/mach_test_package_commands.py"), + } + + def populate_context(context, key=None): + # These values will be set lazily, and cached after first being invoked. + if key == "package_root": + return test_package_root + + if key == "bin_dir": + return os.path.join(test_package_root, "bin") + + if key == "certs_dir": + return os.path.join(test_package_root, "certs") + + if key == "module_dir": + return os.path.join(test_package_root, "modules") + + if key == "ancestors": + return ancestors + + if key == "normalize_test_path": + return normalize_test_path + + if key == "firefox_bin": + return find_firefox(context) + + if key == "hostutils": + return find_hostutils(context) + + if key == "mozharness_config": + for dir_path in ancestors(context.package_root): + mozharness_config = os.path.join(dir_path, "logs", "localconfig.json") + if os.path.isfile(mozharness_config): + with open(mozharness_config, "rb") as f: + return json.load(f) + return {} + + if key == "mozharness_workdir": + config = context.mozharness_config + if config: + return os.path.join(config["base_work_dir"], config["work_dir"]) + + if key == "activate_mozharness_venv": + return types.MethodType(activate_mozharness_venv, context) + + mach = mach.main.Mach(os.getcwd()) + mach.populate_context_handler = populate_context + + for category, meta in CATEGORIES.items(): + mach.define_category(category, meta["short"], meta["long"], meta["priority"]) + + # Depending on which test zips were extracted, + # the command module might not exist + mach.load_commands_from_spec(MACH_COMMANDS, test_package_root, missing_ok=True) + + return mach |