Bumping manifests a=b2g-bump
[gecko.git] / python / mach_commands.py
blobe30753924475ee0d10ad908a3fa30ed7b7d952c5
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 from __future__ import print_function, unicode_literals
7 import argparse
8 import glob
9 import logging
10 import mozpack.path
11 import os
12 import sys
14 from mozbuild.base import (
15 MachCommandBase,
18 from mach.decorators import (
19 CommandArgument,
20 CommandProvider,
21 Command,
25 @CommandProvider
26 class MachCommands(MachCommandBase):
27 @Command('python', category='devenv',
28 allow_all_args=True,
29 description='Run Python.')
30 @CommandArgument('args', nargs=argparse.REMAINDER)
31 def python(self, args):
32 # Avoid logging the command
33 self.log_manager.terminal_handler.setLevel(logging.CRITICAL)
35 self._activate_virtualenv()
37 return self.run_process([self.virtualenv_manager.python_path] + args,
38 pass_thru=True, # Allow user to run Python interactively.
39 ensure_exit_code=False, # Don't throw on non-zero exit code.
40 # Note: subprocess requires native strings in os.environ on Windows
41 append_env={b'PYTHONDONTWRITEBYTECODE': str('1')})
43 @Command('python-test', category='testing',
44 description='Run Python unit tests.')
45 @CommandArgument('--verbose',
46 default=False,
47 action='store_true',
48 help='Verbose output.')
49 @CommandArgument('--stop',
50 default=False,
51 action='store_true',
52 help='Stop running tests after the first error or failure.')
53 @CommandArgument('tests', nargs='+',
54 metavar='TEST',
55 help='Tests to run. Each test can be a single file or a directory.')
56 def python_test(self, tests, verbose=False, stop=False):
57 self._activate_virtualenv()
59 # Python's unittest, and in particular discover, has problems with
60 # clashing namespaces when importing multiple test modules. What follows
61 # is a simple way to keep environments separate, at the price of
62 # launching Python multiple times. This also runs tests via mozunit,
63 # which produces output in the format Mozilla infrastructure expects.
64 return_code = 0
65 files = []
66 for test in [mozpack.path.join(self.topsrcdir, t) for t in tests]:
67 if test.endswith('.py') and os.path.isfile(test):
68 files.append(test)
69 elif os.path.isfile(test + '.py'):
70 files.append(test + '.py')
71 elif os.path.isdir(test):
72 files += glob.glob(mozpack.path.join(test, 'test*.py'))
73 files += glob.glob(mozpack.path.join(test, 'unit*.py'))
74 else:
75 self.log(logging.WARN, 'python-test',
76 {'test': mozpack.path.relpath(test, self.topsrcdir)},
77 'TEST-UNEXPECTED-FAIL | Invalid test: {test}')
78 if stop:
79 return 1
81 for f in files:
82 file_displayed_test = [] # Used as a boolean.
83 def _line_handler(line):
84 if not file_displayed_test and line.startswith('TEST-'):
85 file_displayed_test.append(True)
87 inner_return_code = self.run_process(
88 [self.virtualenv_manager.python_path, f],
89 ensure_exit_code=False, # Don't throw on non-zero exit code.
90 log_name='python-test',
91 # subprocess requires native strings in os.environ on Windows
92 append_env={b'PYTHONDONTWRITEBYTECODE': str('1')},
93 line_handler=_line_handler)
94 return_code += inner_return_code
96 if not file_displayed_test:
97 self.log(logging.WARN, 'python-test', {'file': f},
98 'TEST-UNEXPECTED-FAIL | No test output (missing mozunit.main() call?): {file}')
100 if verbose:
101 if inner_return_code != 0:
102 self.log(logging.INFO, 'python-test', {'file': f},
103 'Test failed: {file}')
104 else:
105 self.log(logging.INFO, 'python-test', {'file': f},
106 'Test passed: {file}')
107 if stop and return_code > 0:
108 return 1
110 return 0 if return_code == 0 else 1