diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /testing/mach_commands.py | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | uxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz |
Add m-esr52 at 52.6.0
Diffstat (limited to 'testing/mach_commands.py')
-rw-r--r-- | testing/mach_commands.py | 831 |
1 files changed, 831 insertions, 0 deletions
diff --git a/testing/mach_commands.py b/testing/mach_commands.py new file mode 100644 index 0000000000..866bc530ca --- /dev/null +++ b/testing/mach_commands.py @@ -0,0 +1,831 @@ +# 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/. + +from __future__ import absolute_import, print_function, unicode_literals + +import json +import os +import sys +import tempfile +import subprocess +import shutil +from collections import defaultdict + +from mach.decorators import ( + CommandArgument, + CommandProvider, + Command, +) + +from mozbuild.base import MachCommandBase +from mozbuild.base import MachCommandConditions as conditions +import mozpack.path as mozpath +from argparse import ArgumentParser + +UNKNOWN_TEST = ''' +I was unable to find tests from the given argument(s). + +You should specify a test directory, filename, test suite name, or +abbreviation. If no arguments are given, there must be local file +changes and corresponding IMPACTED_TESTS annotations in moz.build +files relevant to those files. + +It's possible my little brain doesn't know about the type of test you are +trying to execute. If you suspect this, please request support by filing +a bug at +https://bugzilla.mozilla.org/enter_bug.cgi?product=Testing&component=General. +'''.strip() + +UNKNOWN_FLAVOR = ''' +I know you are trying to run a %s test. Unfortunately, I can't run those +tests yet. Sorry! +'''.strip() + +MOCHITEST_CHUNK_BY_DIR = 4 +MOCHITEST_TOTAL_CHUNKS = 5 + +TEST_SUITES = { + 'cppunittest': { + 'aliases': ('Cpp', 'cpp'), + 'mach_command': 'cppunittest', + 'kwargs': {'test_file': None}, + }, + 'crashtest': { + 'aliases': ('C', 'Rc', 'RC', 'rc'), + 'mach_command': 'crashtest', + 'kwargs': {'test_file': None}, + }, + 'firefox-ui-functional': { + 'aliases': ('Fxfn',), + 'mach_command': 'firefox-ui-functional', + 'kwargs': {}, + }, + 'firefox-ui-update': { + 'aliases': ('Fxup',), + 'mach_command': 'firefox-ui-update', + 'kwargs': {}, + }, + 'jetpack': { + 'aliases': ('J',), + 'mach_command': 'jetpack-test', + 'kwargs': {}, + }, + 'check-spidermonkey': { + 'aliases': ('Sm', 'sm'), + 'mach_command': 'check-spidermonkey', + 'kwargs': {'valgrind': False}, + }, + 'mochitest-a11y': { + 'mach_command': 'mochitest', + 'kwargs': {'flavor': 'a11y', 'test_paths': None}, + }, + 'mochitest-browser': { + 'aliases': ('bc', 'BC', 'Bc'), + 'mach_command': 'mochitest', + 'kwargs': {'flavor': 'browser-chrome', 'test_paths': None}, + }, + 'mochitest-chrome': { + 'mach_command': 'mochitest', + 'kwargs': {'flavor': 'chrome', 'test_paths': None}, + }, + 'mochitest-devtools': { + 'aliases': ('dt', 'DT', 'Dt'), + 'mach_command': 'mochitest', + 'kwargs': {'subsuite': 'devtools', 'test_paths': None}, + }, + 'mochitest-plain': { + 'mach_command': 'mochitest', + 'kwargs': {'flavor': 'plain', 'test_paths': None}, + }, + 'python': { + 'mach_command': 'python-test', + 'kwargs': {'tests': None}, + }, + 'reftest': { + 'aliases': ('RR', 'rr', 'Rr'), + 'mach_command': 'reftest', + 'kwargs': {'tests': None}, + }, + 'web-platform-tests': { + 'aliases': ('wpt',), + 'mach_command': 'web-platform-tests', + 'kwargs': {} + }, + 'valgrind': { + 'aliases': ('V', 'v'), + 'mach_command': 'valgrind-test', + 'kwargs': {}, + }, + 'xpcshell': { + 'aliases': ('X', 'x'), + 'mach_command': 'xpcshell-test', + 'kwargs': {'test_file': 'all'}, + }, +} + +# Maps test flavors to metadata on how to run that test. +TEST_FLAVORS = { + 'a11y': { + 'mach_command': 'mochitest', + 'kwargs': {'flavor': 'a11y', 'test_paths': []}, + }, + 'browser-chrome': { + 'mach_command': 'mochitest', + 'kwargs': {'flavor': 'browser-chrome', 'test_paths': []}, + }, + 'crashtest': {}, + 'chrome': { + 'mach_command': 'mochitest', + 'kwargs': {'flavor': 'chrome', 'test_paths': []}, + }, + 'firefox-ui-functional': { + 'mach_command': 'firefox-ui-functional', + 'kwargs': {'tests': []}, + }, + 'firefox-ui-update': { + 'mach_command': 'firefox-ui-update', + 'kwargs': {'tests': []}, + }, + 'marionette': { + 'mach_command': 'marionette-test', + 'kwargs': {'tests': []}, + }, + 'mochitest': { + 'mach_command': 'mochitest', + 'kwargs': {'flavor': 'mochitest', 'test_paths': []}, + }, + 'python': { + 'mach_command': 'python-test', + 'kwargs': {}, + }, + 'reftest': { + 'mach_command': 'reftest', + 'kwargs': {'tests': []} + }, + 'steeplechase': {}, + 'web-platform-tests': { + 'mach_command': 'web-platform-tests', + 'kwargs': {'include': []} + }, + 'xpcshell': { + 'mach_command': 'xpcshell-test', + 'kwargs': {'test_paths': []}, + }, +} + +for i in range(1, MOCHITEST_TOTAL_CHUNKS + 1): + TEST_SUITES['mochitest-%d' %i] = { + 'aliases': ('M%d' % i, 'm%d' % i), + 'mach_command': 'mochitest', + 'kwargs': { + 'flavor': 'mochitest', + 'subsuite': 'default', + 'chunk_by_dir': MOCHITEST_CHUNK_BY_DIR, + 'total_chunks': MOCHITEST_TOTAL_CHUNKS, + 'this_chunk': i, + 'test_paths': None, + }, + } + +TEST_HELP = ''' +Test or tests to run. Tests can be specified by filename, directory, suite +name or suite alias. + +The following test suites and aliases are supported: %s +''' % ', '.join(sorted(TEST_SUITES)) +TEST_HELP = TEST_HELP.strip() + + +@CommandProvider +class Test(MachCommandBase): + @Command('test', category='testing', description='Run tests (detects the kind of test and runs it).') + @CommandArgument('what', default=None, nargs='*', help=TEST_HELP) + def test(self, what): + """Run tests from names or paths. + + mach test accepts arguments specifying which tests to run. Each argument + can be: + + * The path to a test file + * A directory containing tests + * A test suite name + * An alias to a test suite name (codes used on TreeHerder) + + If no input is provided, tests will be run based on files changed in + the local tree. Relevant tests, tags, or flavors are determined by + IMPACTED_TESTS annotations in moz.build files relevant to the + changed files. + + When paths or directories are given, they are first resolved to test + files known to the build system. + + If resolved tests belong to more than one test type/flavor/harness, + the harness for each relevant type/flavor will be invoked. e.g. if + you specify a directory with xpcshell and browser chrome mochitests, + both harnesses will be invoked. + """ + from mozbuild.testing import TestResolver + + # Parse arguments and assemble a test "plan." + run_suites = set() + run_tests = [] + resolver = self._spawn(TestResolver) + + for entry in what: + # If the path matches the name or alias of an entire suite, run + # the entire suite. + if entry in TEST_SUITES: + run_suites.add(entry) + continue + suitefound = False + for suite, v in TEST_SUITES.items(): + if entry in v.get('aliases', []): + run_suites.add(suite) + suitefound = True + if suitefound: + continue + + # Now look for file/directory matches in the TestResolver. + relpath = self._wrap_path_argument(entry).relpath() + tests = list(resolver.resolve_tests(paths=[relpath])) + run_tests.extend(tests) + + if not tests: + print('UNKNOWN TEST: %s' % entry, file=sys.stderr) + + if not what: + # TODO: This isn't really related to try, and should be + # extracted to a common library for vcs interactions when it is + # introduced in bug 1185599. + from autotry import AutoTry + at = AutoTry(self.topsrcdir, resolver, self._mach_context) + changed_files = at.find_changed_files() + if changed_files: + print("Tests will be run based on modifications to the " + "following files:\n\t%s" % "\n\t".join(changed_files)) + + from mozbuild.frontend.reader import ( + BuildReader, + EmptyConfig, + ) + config = EmptyConfig(self.topsrcdir) + reader = BuildReader(config) + files_info = reader.files_info(changed_files) + + paths, tags, flavors = set(), set(), set() + for info in files_info.values(): + paths |= info.test_files + tags |= info.test_tags + flavors |= info.test_flavors + + # This requires multiple calls to resolve_tests, because the test + # resolver returns tests that match every condition, while we want + # tests that match any condition. Bug 1210213 tracks implementing + # more flexible querying. + if tags: + run_tests = list(resolver.resolve_tests(tags=tags)) + if paths: + run_tests += [t for t in resolver.resolve_tests(paths=paths) + if not (tags & set(t.get('tags', '').split()))] + if flavors: + run_tests = [t for t in run_tests if t['flavor'] not in flavors] + for flavor in flavors: + run_tests += list(resolver.resolve_tests(flavor=flavor)) + + if not run_suites and not run_tests: + print(UNKNOWN_TEST) + return 1 + + status = None + for suite_name in run_suites: + suite = TEST_SUITES[suite_name] + + if 'mach_command' in suite: + res = self._mach_context.commands.dispatch( + suite['mach_command'], self._mach_context, + **suite['kwargs']) + if res: + status = res + + buckets = {} + for test in run_tests: + key = (test['flavor'], test.get('subsuite', '')) + buckets.setdefault(key, []).append(test) + + for (flavor, subsuite), tests in sorted(buckets.items()): + if flavor not in TEST_FLAVORS: + print(UNKNOWN_FLAVOR % flavor) + status = 1 + continue + + m = TEST_FLAVORS[flavor] + if 'mach_command' not in m: + print(UNKNOWN_FLAVOR % flavor) + status = 1 + continue + + kwargs = dict(m['kwargs']) + kwargs['subsuite'] = subsuite + + res = self._mach_context.commands.dispatch( + m['mach_command'], self._mach_context, + test_objects=tests, **kwargs) + if res: + status = res + + return status + + +@CommandProvider +class MachCommands(MachCommandBase): + @Command('cppunittest', category='testing', + description='Run cpp unit tests (C++ tests).') + @CommandArgument('test_files', nargs='*', metavar='N', + help='Test to run. Can be specified as one or more files or ' \ + 'directories, or omitted. If omitted, the entire test suite is ' \ + 'executed.') + + def run_cppunit_test(self, **params): + import mozinfo + from mozlog import commandline + log = commandline.setup_logging("cppunittest", + {}, + {"tbpl": sys.stdout}) + + # See if we have crash symbols + symbols_path = os.path.join(self.distdir, 'crashreporter-symbols') + if not os.path.isdir(symbols_path): + symbols_path = None + + # If no tests specified, run all tests in main manifest + tests = params['test_files'] + if len(tests) == 0: + tests = [os.path.join(self.distdir, 'cppunittests')] + manifest_path = os.path.join(self.topsrcdir, 'testing', 'cppunittest.ini') + else: + manifest_path = None + + if conditions.is_android(self): + from mozrunner.devices.android_device import verify_android_device + verify_android_device(self, install=False) + return self.run_android_test(tests, symbols_path, manifest_path, log) + + return self.run_desktop_test(tests, symbols_path, manifest_path, log) + + def run_desktop_test(self, tests, symbols_path, manifest_path, log): + import runcppunittests as cppunittests + from mozlog import commandline + + parser = cppunittests.CPPUnittestOptions() + commandline.add_logging_group(parser) + options, args = parser.parse_args() + + options.symbols_path = symbols_path + options.manifest_path = manifest_path + options.xre_path = self.bindir + + try: + result = cppunittests.run_test_harness(options, tests) + except Exception as e: + log.error("Caught exception running cpp unit tests: %s" % str(e)) + result = False + raise + + return 0 if result else 1 + + def run_android_test(self, tests, symbols_path, manifest_path, log): + import remotecppunittests as remotecppunittests + from mozlog import commandline + + parser = remotecppunittests.RemoteCPPUnittestOptions() + commandline.add_logging_group(parser) + options, args = parser.parse_args() + + options.symbols_path = symbols_path + options.manifest_path = manifest_path + options.xre_path = self.bindir + options.dm_trans = "adb" + options.local_lib = self.bindir.replace('bin', 'fennec') + for file in os.listdir(os.path.join(self.topobjdir, "dist")): + if file.endswith(".apk") and file.startswith("fennec"): + options.local_apk = os.path.join(self.topobjdir, "dist", file) + log.info("using APK: " + options.local_apk) + break + + try: + result = remotecppunittests.run_test_harness(options, tests) + except Exception as e: + log.error("Caught exception running cpp unit tests: %s" % str(e)) + result = False + raise + + return 0 if result else 1 + +def executable_name(name): + return name + '.exe' if sys.platform.startswith('win') else name + +@CommandProvider +class CheckSpiderMonkeyCommand(MachCommandBase): + @Command('check-spidermonkey', category='testing', description='Run SpiderMonkey tests (JavaScript engine).') + @CommandArgument('--valgrind', action='store_true', help='Run jit-test suite with valgrind flag') + + def run_checkspidermonkey(self, **params): + import subprocess + import sys + + js = os.path.join(self.bindir, executable_name('js')) + + print('Running jit-tests') + jittest_cmd = [os.path.join(self.topsrcdir, 'js', 'src', 'jit-test', 'jit_test.py'), + js, '--no-slow', '--jitflags=all'] + if params['valgrind']: + jittest_cmd.append('--valgrind') + + jittest_result = subprocess.call(jittest_cmd) + + print('running jstests') + jstest_cmd = [os.path.join(self.topsrcdir, 'js', 'src', 'tests', 'jstests.py'), + js, '--jitflags=all'] + jstest_result = subprocess.call(jstest_cmd) + + print('running jsapi-tests') + jsapi_tests_cmd = [os.path.join(self.bindir, executable_name('jsapi-tests'))] + jsapi_tests_result = subprocess.call(jsapi_tests_cmd) + + print('running check-style') + check_style_cmd = [sys.executable, os.path.join(self.topsrcdir, 'config', 'check_spidermonkey_style.py')] + check_style_result = subprocess.call(check_style_cmd, cwd=os.path.join(self.topsrcdir, 'js', 'src')) + + print('running check-masm') + check_masm_cmd = [sys.executable, os.path.join(self.topsrcdir, 'config', 'check_macroassembler_style.py')] + check_masm_result = subprocess.call(check_masm_cmd, cwd=os.path.join(self.topsrcdir, 'js', 'src')) + + print('running check-js-msg-encoding') + check_js_msg_cmd = [sys.executable, os.path.join(self.topsrcdir, 'config', 'check_js_msg_encoding.py')] + check_js_msg_result = subprocess.call(check_js_msg_cmd, cwd=self.topsrcdir) + + all_passed = jittest_result and jstest_result and jsapi_tests_result and check_style_result and check_masm_result and check_js_msg_result + + return all_passed + +@CommandProvider +class JsapiTestsCommand(MachCommandBase): + @Command('jsapi-tests', category='testing', description='Run jsapi tests (JavaScript engine).') + @CommandArgument('test_name', nargs='?', metavar='N', + help='Test to run. Can be a prefix or omitted. If omitted, the entire ' \ + 'test suite is executed.') + + def run_jsapitests(self, **params): + import subprocess + + bin_suffix = '' + if sys.platform.startswith('win'): + bin_suffix = '.exe' + + print('running jsapi-tests') + jsapi_tests_cmd = [os.path.join(self.bindir, executable_name('jsapi-tests'))] + if params['test_name']: + jsapi_tests_cmd.append(params['test_name']) + + jsapi_tests_result = subprocess.call(jsapi_tests_cmd) + + return jsapi_tests_result + +def autotry_parser(): + from autotry import arg_parser + return arg_parser() + +@CommandProvider +class PushToTry(MachCommandBase): + def normalise_list(self, items, allow_subitems=False): + from autotry import parse_arg + + rv = defaultdict(list) + for item in items: + parsed = parse_arg(item) + for key, values in parsed.iteritems(): + rv[key].extend(values) + + if not allow_subitems: + if not all(item == [] for item in rv.itervalues()): + raise ValueError("Unexpected subitems in argument") + return rv.keys() + else: + return rv + + def validate_args(self, **kwargs): + from autotry import AutoTry + if not kwargs["paths"] and not kwargs["tests"] and not kwargs["tags"]: + print("Paths, tags, or tests must be specified as an argument to autotry.") + sys.exit(1) + + if kwargs["platforms"] is None: + if 'AUTOTRY_PLATFORM_HINT' in os.environ: + kwargs["platforms"] = [os.environ['AUTOTRY_PLATFORM_HINT']] + else: + print("Platforms must be specified as an argument to autotry.") + sys.exit(1) + + try: + platforms = self.normalise_list(kwargs["platforms"]) + except ValueError as e: + print("Error parsing -p argument:\n%s" % e.message) + sys.exit(1) + + try: + tests = (self.normalise_list(kwargs["tests"], allow_subitems=True) + if kwargs["tests"] else {}) + except ValueError as e: + print("Error parsing -u argument (%s):\n%s" % (kwargs["tests"], e.message)) + sys.exit(1) + + try: + talos = (self.normalise_list(kwargs["talos"], allow_subitems=True) + if kwargs["talos"] else []) + except ValueError as e: + print("Error parsing -t argument:\n%s" % e.message) + sys.exit(1) + + paths = [] + for p in kwargs["paths"]: + p = mozpath.normpath(os.path.abspath(p)) + if not (os.path.isdir(p) and p.startswith(self.topsrcdir)): + print('Specified path "%s" is not a directory under the srcdir,' + ' unable to specify tests outside of the srcdir' % p) + sys.exit(1) + if len(p) <= len(self.topsrcdir): + print('Specified path "%s" is at the top of the srcdir and would' + ' select all tests.' % p) + sys.exit(1) + paths.append(os.path.relpath(p, self.topsrcdir)) + + try: + tags = self.normalise_list(kwargs["tags"]) if kwargs["tags"] else [] + except ValueError as e: + print("Error parsing --tags argument:\n%s" % e.message) + sys.exit(1) + + extra_values = {k['dest'] for k in AutoTry.pass_through_arguments.values()} + extra_args = {k: v for k, v in kwargs.items() + if k in extra_values and v} + + return kwargs["builds"], platforms, tests, talos, paths, tags, extra_args + + + @Command('try', + category='testing', + description='Push selected tests to the try server', + parser=autotry_parser) + + def autotry(self, **kwargs): + """Autotry is in beta, please file bugs blocking 1149670. + + Push the current tree to try, with the specified syntax. + + Build options, platforms and regression tests may be selected + using the usual try options (-b, -p and -u respectively). In + addition, tests in a given directory may be automatically + selected by passing that directory as a positional argument to the + command. For example: + + mach try -b d -p linux64 dom testing/web-platform/tests/dom + + would schedule a try run for linux64 debug consisting of all + tests under dom/ and testing/web-platform/tests/dom. + + Test selection using positional arguments is available for + mochitests, reftests, xpcshell tests and web-platform-tests. + + Tests may be also filtered by passing --tag to the command, + which will run only tests marked as having the specified + tags e.g. + + mach try -b d -p win64 --tag media + + would run all tests tagged 'media' on Windows 64. + + If both positional arguments or tags and -u are supplied, the + suites in -u will be run in full. Where tests are selected by + positional argument they will be run in a single chunk. + + If no build option is selected, both debug and opt will be + scheduled. If no platform is selected a default is taken from + the AUTOTRY_PLATFORM_HINT environment variable, if set. + + The command requires either its own mercurial extension ("push-to-try", + installable from mach mercurial-setup) or a git repo using git-cinnabar + (available at https://github.com/glandium/git-cinnabar). + + """ + + from mozbuild.testing import TestResolver + from autotry import AutoTry + + print("mach try is under development, please file bugs blocking 1149670.") + + resolver_func = lambda: self._spawn(TestResolver) + at = AutoTry(self.topsrcdir, resolver_func, self._mach_context) + + if kwargs["list"]: + at.list_presets() + sys.exit() + + if kwargs["load"] is not None: + defaults = at.load_config(kwargs["load"]) + + if defaults is None: + print("No saved configuration called %s found in autotry.ini" % kwargs["load"], + file=sys.stderr) + + for key, value in kwargs.iteritems(): + if value in (None, []) and key in defaults: + kwargs[key] = defaults[key] + + if kwargs["push"] and at.find_uncommited_changes(): + print('ERROR please commit changes before continuing') + sys.exit(1) + + if not any(kwargs[item] for item in ("paths", "tests", "tags")): + kwargs["paths"], kwargs["tags"] = at.find_paths_and_tags(kwargs["verbose"]) + + builds, platforms, tests, talos, paths, tags, extra = self.validate_args(**kwargs) + + if paths or tags: + paths = [os.path.relpath(os.path.normpath(os.path.abspath(item)), self.topsrcdir) + for item in paths] + paths_by_flavor = at.paths_by_flavor(paths=paths, tags=tags) + + if not paths_by_flavor and not tests: + print("No tests were found when attempting to resolve paths:\n\n\t%s" % + paths) + sys.exit(1) + + if not kwargs["intersection"]: + paths_by_flavor = at.remove_duplicates(paths_by_flavor, tests) + else: + paths_by_flavor = {} + + try: + msg = at.calc_try_syntax(platforms, tests, talos, builds, paths_by_flavor, tags, + extra, kwargs["intersection"]) + except ValueError as e: + print(e.message) + sys.exit(1) + + if kwargs["verbose"] and paths_by_flavor: + print('The following tests will be selected: ') + for flavor, paths in paths_by_flavor.iteritems(): + print("%s: %s" % (flavor, ",".join(paths))) + + if kwargs["verbose"] or not kwargs["push"]: + print('The following try syntax was calculated:\n%s' % msg) + + if kwargs["push"]: + at.push_to_try(msg, kwargs["verbose"]) + + if kwargs["save"] is not None: + at.save_config(kwargs["save"], msg) + + +def get_parser(argv=None): + parser = ArgumentParser() + parser.add_argument(dest="suite_name", + nargs=1, + choices=['mochitest'], + type=str, + help="The test for which chunk should be found. It corresponds " + "to the mach test invoked (only 'mochitest' currently).") + + parser.add_argument(dest="test_path", + nargs=1, + type=str, + help="The test (any mochitest) for which chunk should be found.") + + parser.add_argument('--total-chunks', + type=int, + dest='total_chunks', + required=True, + help='Total number of chunks to split tests into.', + default=None) + + parser.add_argument('--chunk-by-runtime', + action='store_true', + dest='chunk_by_runtime', + help='Group tests such that each chunk has roughly the same runtime.', + default=False) + + parser.add_argument('--chunk-by-dir', + type=int, + dest='chunk_by_dir', + help='Group tests together in the same chunk that are in the same top ' + 'chunkByDir directories.', + default=None) + + parser.add_argument('--disable-e10s', + action='store_false', + dest='e10s', + help='Find test on chunk with electrolysis preferences disabled.', + default=True) + + parser.add_argument('-p', '--platform', + choices=['linux', 'linux64', 'mac', 'macosx64', 'win32', 'win64'], + dest='platform', + help="Platform for the chunk to find the test.", + default=None) + + parser.add_argument('--debug', + action='store_true', + dest='debug', + help="Find the test on chunk in a debug build.", + default=False) + + return parser + + +def download_mozinfo(platform=None, debug_build=False): + temp_dir = tempfile.mkdtemp() + temp_path = os.path.join(temp_dir, "mozinfo.json") + args = [ + 'mozdownload', + '-t', 'tinderbox', + '--ext', 'mozinfo.json', + '-d', temp_path, + ] + if platform: + if platform == 'macosx64': + platform = 'mac64' + args.extend(['-p', platform]) + if debug_build: + args.extend(['--debug-build']) + + subprocess.call(args) + return temp_dir, temp_path + +@CommandProvider +class ChunkFinder(MachCommandBase): + @Command('find-test-chunk', category='testing', + description='Find which chunk a test belongs to (works for mochitest).', + parser=get_parser) + def chunk_finder(self, **kwargs): + total_chunks = kwargs['total_chunks'] + test_path = kwargs['test_path'][0] + suite_name = kwargs['suite_name'][0] + _, dump_tests = tempfile.mkstemp() + + from mozbuild.testing import TestResolver + resolver = self._spawn(TestResolver) + relpath = self._wrap_path_argument(test_path).relpath() + tests = list(resolver.resolve_tests(paths=[relpath])) + if len(tests) != 1: + print('No test found for test_path: %s' % test_path) + sys.exit(1) + + flavor = tests[0]['flavor'] + subsuite = tests[0]['subsuite'] + args = { + 'totalChunks': total_chunks, + 'dump_tests': dump_tests, + 'chunkByDir': kwargs['chunk_by_dir'], + 'chunkByRuntime': kwargs['chunk_by_runtime'], + 'e10s': kwargs['e10s'], + 'subsuite': subsuite, + } + + temp_dir = None + if kwargs['platform'] or kwargs['debug']: + self._activate_virtualenv() + self.virtualenv_manager.install_pip_package('mozdownload==1.17') + temp_dir, temp_path = download_mozinfo(kwargs['platform'], kwargs['debug']) + args['extra_mozinfo_json'] = temp_path + + found = False + for this_chunk in range(1, total_chunks+1): + args['thisChunk'] = this_chunk + try: + self._mach_context.commands.dispatch(suite_name, self._mach_context, flavor=flavor, resolve_tests=False, **args) + except SystemExit: + pass + except KeyboardInterrupt: + break + + fp = open(os.path.expanduser(args['dump_tests']), 'r') + tests = json.loads(fp.read())['active_tests'] + for test in tests: + if test_path == test['path']: + if 'disabled' in test: + print('The test %s for flavor %s is disabled on the given platform' % (test_path, flavor)) + else: + print('The test %s for flavor %s is present in chunk number: %d' % (test_path, flavor, this_chunk)) + found = True + break + + if found: + break + + if not found: + raise Exception("Test %s not found." % test_path) + # Clean up the file + os.remove(dump_tests) + if temp_dir: + shutil.rmtree(temp_dir) |