mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 22:25:30 +00:00
417a2977b3
The reason to use '+' prefixing was to distinguish between options to the mach command itself, and options that are passed down to whatever the command does (like mach run passing down args to the built application). That makes things unnecessarily awkward, and quite non-standard. Instead, use standard '-' prefixing, and pass all the unknown arguments down. If there is overlap between the known arguments and arguments supported by the underlying tool (like -remote when using mach run), it is possible to use '--' to mark all following arguments as being targetted at the underlying tool. For instance: mach run -- -remote something would run firefox -remote something while mach run -remote something would run firefox something As allow_all_arguments is redundant with the presence of a argparse.REMAINDER CommandArgument, allow_all_arguments is removed. The only mach command with a argparse.REMAINDER CommandArgument without allow_all_arguments was "mach dmd", and it did so because it didn't want to use '+' prefixes.
122 lines
4.8 KiB
Python
122 lines
4.8 KiB
Python
# 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 print_function, unicode_literals
|
|
|
|
import argparse
|
|
import glob
|
|
import logging
|
|
import mozpack.path
|
|
import os
|
|
import sys
|
|
|
|
from mozbuild.base import (
|
|
MachCommandBase,
|
|
)
|
|
|
|
from mach.decorators import (
|
|
CommandArgument,
|
|
CommandProvider,
|
|
Command,
|
|
)
|
|
|
|
|
|
@CommandProvider
|
|
class MachCommands(MachCommandBase):
|
|
@Command('python', category='devenv',
|
|
description='Run Python.')
|
|
@CommandArgument('args', nargs=argparse.REMAINDER)
|
|
def python(self, args):
|
|
# Avoid logging the command
|
|
self.log_manager.terminal_handler.setLevel(logging.CRITICAL)
|
|
|
|
self._activate_virtualenv()
|
|
|
|
return self.run_process([self.virtualenv_manager.python_path] + args,
|
|
pass_thru=True, # Allow user to run Python interactively.
|
|
ensure_exit_code=False, # Don't throw on non-zero exit code.
|
|
# Note: subprocess requires native strings in os.environ on Windows
|
|
append_env={b'PYTHONDONTWRITEBYTECODE': str('1')})
|
|
|
|
@Command('python-test', category='testing',
|
|
description='Run Python unit tests.')
|
|
@CommandArgument('--verbose',
|
|
default=False,
|
|
action='store_true',
|
|
help='Verbose output.')
|
|
@CommandArgument('--stop',
|
|
default=False,
|
|
action='store_true',
|
|
help='Stop running tests after the first error or failure.')
|
|
@CommandArgument('tests', nargs='+',
|
|
metavar='TEST',
|
|
help='Tests to run. Each test can be a single file or a directory.')
|
|
def python_test(self, tests, verbose=False, stop=False):
|
|
self._activate_virtualenv()
|
|
|
|
# Python's unittest, and in particular discover, has problems with
|
|
# clashing namespaces when importing multiple test modules. What follows
|
|
# is a simple way to keep environments separate, at the price of
|
|
# launching Python multiple times. This also runs tests via mozunit,
|
|
# which produces output in the format Mozilla infrastructure expects.
|
|
return_code = 0
|
|
files = []
|
|
# We search for files in both the current directory (for people running
|
|
# from topsrcdir or cd'd into their test directory) and topsrcdir (to
|
|
# support people running mach from the objdir). The |break|s in the
|
|
# loop below ensure that we don't run tests twice if we're running mach
|
|
# from topsrcdir
|
|
search_dirs = ['.', self.topsrcdir]
|
|
last_search_dir = search_dirs[-1]
|
|
for t in tests:
|
|
for d in search_dirs:
|
|
test = mozpack.path.join(d, t)
|
|
if test.endswith('.py') and os.path.isfile(test):
|
|
files.append(test)
|
|
break
|
|
elif os.path.isfile(test + '.py'):
|
|
files.append(test + '.py')
|
|
break
|
|
elif os.path.isdir(test):
|
|
files += glob.glob(mozpack.path.join(test, 'test*.py'))
|
|
files += glob.glob(mozpack.path.join(test, 'unit*.py'))
|
|
break
|
|
elif d == last_search_dir:
|
|
self.log(logging.WARN, 'python-test',
|
|
{'test': t},
|
|
'TEST-UNEXPECTED-FAIL | Invalid test: {test}')
|
|
if stop:
|
|
return 1
|
|
|
|
for f in files:
|
|
file_displayed_test = [] # Used as a boolean.
|
|
def _line_handler(line):
|
|
if not file_displayed_test and line.startswith('TEST-'):
|
|
file_displayed_test.append(True)
|
|
|
|
inner_return_code = self.run_process(
|
|
[self.virtualenv_manager.python_path, f],
|
|
ensure_exit_code=False, # Don't throw on non-zero exit code.
|
|
log_name='python-test',
|
|
# subprocess requires native strings in os.environ on Windows
|
|
append_env={b'PYTHONDONTWRITEBYTECODE': str('1')},
|
|
line_handler=_line_handler)
|
|
return_code += inner_return_code
|
|
|
|
if not file_displayed_test:
|
|
self.log(logging.WARN, 'python-test', {'file': f},
|
|
'TEST-UNEXPECTED-FAIL | No test output (missing mozunit.main() call?): {file}')
|
|
|
|
if verbose:
|
|
if inner_return_code != 0:
|
|
self.log(logging.INFO, 'python-test', {'file': f},
|
|
'Test failed: {file}')
|
|
else:
|
|
self.log(logging.INFO, 'python-test', {'file': f},
|
|
'Test passed: {file}')
|
|
if stop and return_code > 0:
|
|
return 1
|
|
|
|
return 0 if return_code == 0 else 1
|