| """Build step class for running Python tests.""" |
| import json |
| import logging |
| import os |
| |
| from helpers import py_env_utils |
| from slave import base_step |
| |
| _UNITTEST_RESULT_MARKER = '__CQ_UNITTEST_RESULTS_SUMMARY__:' |
| |
| |
| class PyTestStep(base_step.BaseStep): |
| """Step for running python tests.""" |
| |
| def __init__(self, |
| project, |
| test_file_filter_regex, |
| timeout, |
| module_path_regex='.', |
| **kwargs): |
| """Creates a PyTestStep instance. |
| |
| Args: |
| project: Manifest project to look for tests inside of it. |
| test_file_filter_regex: File pattern for tests to be run. |
| timeout: Timeout for test run (in seconds). |
| module_path_regex: Regex to determine which modules to include. |
| **kwargs: Any additional args to pass to BaseStep. |
| """ |
| base_step.BaseStep.__init__(self, name='py_test', **kwargs) |
| self._project = project |
| self.test_file_filter_regex = test_file_filter_regex |
| self.module_path_regex = module_path_regex |
| self.timeout = timeout |
| |
| def _execute_tests(self, venv_root): # pylint: disable=method-hidden |
| """Execute the tests. |
| |
| Args: |
| venv_root: Path to the python virtual env root to activate. |
| |
| Returns: |
| Tuple: (review, passing) Where passing is a boolean if the tests |
| passed and review is the generated review message. |
| """ |
| test_runner = os.path.abspath( |
| os.path.join(self.get_cq_root(), |
| 'scripts/helpers/py_test_runner.py')) |
| directory = os.path.join( |
| self._getcwd(), self.get_project_path(self._project)) |
| test_results = os.path.join(self.get_tmp_dir(), 'test_review.json') |
| cmd = [test_runner, |
| '--test_dir', directory, |
| '--output', test_results, |
| '--test_file_filter_regex', self.test_file_filter_regex, |
| '--module_path_regex', self.module_path_regex, |
| '--timeout', str(self.timeout)] |
| env = { |
| 'SHELL_EXE_BINARY_PATH': self.get_step_data('cast_shell_exe_path', ''), |
| } |
| returncode, stdout, stderr = py_env_utils.exec_within_virtual_env( |
| self, venv_root, cmd, env=env) |
| |
| if returncode != 0: |
| logging.error('Failed to run tests: %s\n%s', stdout, stderr) |
| return {'message': 'Tests did not run properly', 'passing': False} |
| |
| return self._parse_test_results(test_results) |
| |
| def _parse_test_results(self, test_results_file): |
| with open(test_results_file) as f: |
| return json.load(f) |
| |
| def run(self): |
| """Runs python tests on the repository. |
| |
| Sets a build property "review" with the output. |
| |
| Returns: |
| True iff there were no test errors. |
| """ |
| venv_root = self.get_step_data('virtual_env_root') |
| try: |
| review = self._execute_tests(venv_root) |
| passing = review['passing'] |
| if passing: |
| logging.info(review.get('message')) |
| else: |
| self.add_review(review) |
| except Exception as e: # pylint: disable=broad-except |
| logging.exception('Failure while executing tests.') |
| self.add_review({'message': 'Failed due to exception [{}].'.format(e)}) |
| return False |
| |
| return passing |