python/mach_commands.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/python/mach_commands.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,109 @@
     1.4 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.7 +
     1.8 +from __future__ import print_function, unicode_literals
     1.9 +
    1.10 +import argparse
    1.11 +import glob
    1.12 +import logging
    1.13 +import mozpack.path
    1.14 +import os
    1.15 +import sys
    1.16 +
    1.17 +from mozbuild.base import (
    1.18 +    MachCommandBase,
    1.19 +)
    1.20 +
    1.21 +from mach.decorators import (
    1.22 +    CommandArgument,
    1.23 +    CommandProvider,
    1.24 +    Command,
    1.25 +)
    1.26 +
    1.27 +
    1.28 +@CommandProvider
    1.29 +class MachCommands(MachCommandBase):
    1.30 +    @Command('python', category='devenv',
    1.31 +        allow_all_args=True,
    1.32 +        description='Run Python.')
    1.33 +    @CommandArgument('args', nargs=argparse.REMAINDER)
    1.34 +    def python(self, args):
    1.35 +        # Avoid logging the command
    1.36 +        self.log_manager.terminal_handler.setLevel(logging.CRITICAL)
    1.37 +
    1.38 +        self._activate_virtualenv()
    1.39 +
    1.40 +        return self.run_process([self.virtualenv_manager.python_path] + args,
    1.41 +            pass_thru=True, # Allow user to run Python interactively.
    1.42 +            ensure_exit_code=False, # Don't throw on non-zero exit code.
    1.43 +            # Note: subprocess requires native strings in os.environ on Windows
    1.44 +            append_env={b'PYTHONDONTWRITEBYTECODE': str('1')})
    1.45 +
    1.46 +    @Command('python-test', category='testing',
    1.47 +        description='Run Python unit tests.')
    1.48 +    @CommandArgument('--verbose',
    1.49 +        default=False,
    1.50 +        action='store_true',
    1.51 +        help='Verbose output.')
    1.52 +    @CommandArgument('--stop',
    1.53 +        default=False,
    1.54 +        action='store_true',
    1.55 +        help='Stop running tests after the first error or failure.')
    1.56 +    @CommandArgument('tests', nargs='+',
    1.57 +        metavar='TEST',
    1.58 +        help='Tests to run. Each test can be a single file or a directory.')
    1.59 +    def python_test(self, tests, verbose=False, stop=False):
    1.60 +        self._activate_virtualenv()
    1.61 +
    1.62 +        # Python's unittest, and in particular discover, has problems with
    1.63 +        # clashing namespaces when importing multiple test modules. What follows
    1.64 +        # is a simple way to keep environments separate, at the price of
    1.65 +        # launching Python multiple times. This also runs tests via mozunit,
    1.66 +        # which produces output in the format Mozilla infrastructure expects.
    1.67 +        return_code = 0
    1.68 +        files = []
    1.69 +        for test in tests:
    1.70 +            if test.endswith('.py') and os.path.isfile(test):
    1.71 +                files.append(test)
    1.72 +            elif os.path.isfile(test + '.py'):
    1.73 +                files.append(test + '.py')
    1.74 +            elif os.path.isdir(test):
    1.75 +                files += glob.glob(mozpack.path.join(test, 'test*.py'))
    1.76 +                files += glob.glob(mozpack.path.join(test, 'unit*.py'))
    1.77 +            else:
    1.78 +                self.log(logging.WARN, 'python-test', {'test': test},
    1.79 +                         'TEST-UNEXPECTED-FAIL | Invalid test: {test}')
    1.80 +                if stop:
    1.81 +                    return 1
    1.82 +
    1.83 +        for f in files:
    1.84 +            file_displayed_test = [] # Used as a boolean.
    1.85 +            def _line_handler(line):
    1.86 +                if not file_displayed_test and line.startswith('TEST-'):
    1.87 +                    file_displayed_test.append(True)
    1.88 +
    1.89 +            inner_return_code = self.run_process(
    1.90 +                [self.virtualenv_manager.python_path, f],
    1.91 +                ensure_exit_code=False, # Don't throw on non-zero exit code.
    1.92 +                log_name='python-test',
    1.93 +                # subprocess requires native strings in os.environ on Windows
    1.94 +                append_env={b'PYTHONDONTWRITEBYTECODE': str('1')},
    1.95 +                line_handler=_line_handler)
    1.96 +            return_code += inner_return_code
    1.97 +
    1.98 +            if not file_displayed_test:
    1.99 +                self.log(logging.WARN, 'python-test', {'file': f},
   1.100 +                         'TEST-UNEXPECTED-FAIL | No test output (missing mozunit.main() call?): {file}')
   1.101 +
   1.102 +            if verbose:
   1.103 +                if inner_return_code != 0:
   1.104 +                    self.log(logging.INFO, 'python-test', {'file': f},
   1.105 +                             'Test failed: {file}')
   1.106 +                else:
   1.107 +                    self.log(logging.INFO, 'python-test', {'file': f},
   1.108 +                             'Test passed: {file}')
   1.109 +            if stop and return_code > 0:
   1.110 +                return 1
   1.111 +
   1.112 +        return 0 if return_code == 0 else 1

mercurial