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
14 from mozbuild
.base
import (
18 from mach
.decorators
import (
26 class MachCommands(MachCommandBase
):
27 @Command('python', category
='devenv',
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',
48 help='Verbose output.')
49 @CommandArgument('--stop',
52 help='Stop running tests after the first error or failure.')
53 @CommandArgument('tests', nargs
='+',
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.
67 if test
.endswith('.py') and os
.path
.isfile(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'))
75 self
.log(logging
.WARN
, 'python-test', {'test': test
},
76 'TEST-UNEXPECTED-FAIL | Invalid test: {test}')
81 file_displayed_test
= [] # Used as a boolean.
82 def _line_handler(line
):
83 if not file_displayed_test
and line
.startswith('TEST-'):
84 file_displayed_test
.append(True)
86 inner_return_code
= self
.run_process(
87 [self
.virtualenv_manager
.python_path
, f
],
88 ensure_exit_code
=False, # Don't throw on non-zero exit code.
89 log_name
='python-test',
90 # subprocess requires native strings in os.environ on Windows
91 append_env
={b
'PYTHONDONTWRITEBYTECODE': str('1')},
92 line_handler
=_line_handler
)
93 return_code
+= inner_return_code
95 if not file_displayed_test
:
96 self
.log(logging
.WARN
, 'python-test', {'file': f
},
97 'TEST-UNEXPECTED-FAIL | No test output (missing mozunit.main() call?): {file}')
100 if inner_return_code
!= 0:
101 self
.log(logging
.INFO
, 'python-test', {'file': f
},
102 'Test failed: {file}')
104 self
.log(logging
.INFO
, 'python-test', {'file': f
},
105 'Test passed: {file}')
106 if stop
and return_code
> 0:
109 return 0 if return_code
== 0 else 1