From 3918d266fa1e7f09a0ca6d8c6f245a42efd30f38 Mon Sep 17 00:00:00 2001 From: Christophe Monniez Date: Wed, 26 Feb 2020 10:28:13 +0100 Subject: [PATCH] [IMP] runbot: add coverage xml export When coverage is computed, a post command is used to generate the HTML report. In order to use the coverage result locally the HTML report is not enough. With this commit, an XML report is also generated. It's a single xml file, downloadable from the build result web page. The _post_install_command method is renamed into its plural form because it was useless to return only one command. --- runbot/container.py | 2 +- runbot/models/build_config.py | 14 ++++++++++---- runbot/tests/test_build_config_step.py | 6 ++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/runbot/container.py b/runbot/container.py index 1a38358a..3de78ca9 100644 --- a/runbot/container.py +++ b/runbot/container.py @@ -270,7 +270,7 @@ def tests(args): if args.coverage: omit = ['--omit', '*__manifest__.py'] python_params = [ '-m', 'coverage', 'run', '--branch', '--source', '/data/build'] + omit - posts = [['python%s' % py_version, "-m", "coverage", "html", "-d", "/data/build/coverage", "--ignore-errors"]] + posts = [['python%s' % py_version, "-m", "coverage", "html", "-d", "/data/build/coverage", "--ignore-errors"], ['python%s' % py_version, "-m", "coverage", "xml", "--ignore-errors"]] os.makedirs(os.path.join(args.build_dir, 'coverage'), exist_ok=True) elif args.flamegraph: flame_log = '/data/build/logs/flame.log' diff --git a/runbot/models/build_config.py b/runbot/models/build_config.py index 5e6aed36..02f84005 100644 --- a/runbot/models/build_config.py +++ b/runbot/models/build_config.py @@ -388,7 +388,7 @@ class ConfigStep(models.Model): if extra_params: cmd.extend(shlex.split(extra_params)) - cmd.posts.append(self._post_install_command(build, modules_to_install, py_version)) # coverage post, extra-checks, ... + cmd.posts.extend(self._post_install_commands(build, modules_to_install, py_version)) # coverage post, extra-checks, ... dump_dir = '/data/build/logs/%s/' % db_name sql_dest = '%s/dump.sql' % dump_dir filestore_path = '/data/build/datadir/filestore/%s' % db_name @@ -421,6 +421,10 @@ class ConfigStep(models.Model): kwargs['log_type'] = 'link' build._log('', **kwargs) + if self.coverage: + message = 'Coverage xml: $$fa-download' + build._log('end_job', message, log_type='link', path='/data/build/logs/coverage.xml') + if self.flamegraph: link = self._perf_data_url(build, 'log.gz') message = 'Flamegraph data: $$fa-download$$' @@ -433,14 +437,16 @@ class ConfigStep(models.Model): def _modules_to_install(self, build): return set(build._get_modules_to_test(modules_patterns=self.install_modules)) - def _post_install_command(self, build, modules_to_install, py_version=None): + def _post_install_commands(self, build, modules_to_install, py_version=None): + cmds = [] if self.coverage: py_version = py_version if py_version is not None else build._get_py_version() # prepare coverage result cov_path = build._path('coverage') os.makedirs(cov_path, exist_ok=True) - return ['python%s' % py_version, "-m", "coverage", "html", "-d", "/data/build/coverage", "--ignore-errors"] - return [] + cmds.append(['python%s' % py_version, "-m", "coverage", "html", "-d", "/data/build/coverage", "--ignore-errors"]) + cmds.append(['python%s' % py_version, "-m", "coverage", "xml", "-o", "/data/build/logs/coverage.xml", "--ignore-errors"]) + return cmds def _perfs_data_path(self, ext='log'): return '/data/build/logs/flame_%s.%s' % (self.name, ext) diff --git a/runbot/tests/test_build_config_step.py b/runbot/tests/test_build_config_step.py index 29dba737..0e8c1a42 100644 --- a/runbot/tests/test_build_config_step.py +++ b/runbot/tests/test_build_config_step.py @@ -153,12 +153,10 @@ class TestBuildConfigStep(RunbotCase): }) def docker_run(cmd, log_path, *args, **kwargs): - cmds = cmd.build().split(' && ') - dest = self.parent_build.dest self.assertEqual(cmd.pres, [['sudo', 'pip3', 'install', '-r', 'bar/requirements.txt']]) self.assertEqual(cmd.cmd[:10], ['python3', '-m', 'coverage', 'run', '--branch', '--source', '/data/build', '--omit', '*__manifest__.py', 'bar/server.py']) - #['bar/server.py', '--addons-path', 'bar', '--no-xmlrpcs', '--no-netrpc', '-d', '08732-master-d0d0ca-coverage', '--test-enable', '--stop-after-init', '--log-level=test', '--max-cron-threads=0'] - self.assertEqual(cmd.posts, [['python3', '-m', 'coverage', 'html', '-d', '/data/build/coverage', '--ignore-errors']]) + self.assertIn(['python3', '-m', 'coverage', 'html', '-d', '/data/build/coverage', '--ignore-errors'], cmd.posts) + self.assertIn(['python3', '-m', 'coverage', 'xml', '-o', '/data/build/logs/coverage.xml', '--ignore-errors'], cmd.posts) self.assertEqual(log_path, 'dev/null/logpath') self.patchers['docker_run'].side_effect = docker_run