123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832 |
- # 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)
|