From 6287bc543008215c4db82d31ff66145299126b10 Mon Sep 17 00:00:00 2001 From: Christophe Monniez Date: Mon, 2 Sep 2024 15:41:07 +0200 Subject: [PATCH] [IMP] runbot: add the possibility to disable a test per version When disabling tests on runbot by using the test_tags on a build error, the tests are disabled on every tested version. This can be annoying when a test only fails from one version up to another. With this commit, a min and max version can be specified on a build_error when the test_tags field is used. If the min and max are not used, the test will be disabled cross versions. --- runbot/models/build_config.py | 2 +- runbot/models/build_error.py | 26 ++++++++--- runbot/tests/test_build_error.py | 70 +++++++++++++++++++++++++----- runbot/views/build_error_views.xml | 13 ++++-- 4 files changed, 91 insertions(+), 20 deletions(-) diff --git a/runbot/models/build_config.py b/runbot/models/build_config.py index 4beffd03..0faaa636 100644 --- a/runbot/models/build_config.py +++ b/runbot/models/build_config.py @@ -454,7 +454,7 @@ class ConfigStep(models.Model): test_tags += self.test_tags.replace(' ', '').split(',') if self.enable_auto_tags and not build.params_id.config_data.get('disable_auto_tags', False): if grep(config_path, "[/module][:class]"): - auto_tags = self.env['runbot.build.error']._disabling_tags() + auto_tags = self.env['runbot.build.error']._disabling_tags(build) if auto_tags: test_tags += auto_tags diff --git a/runbot/models/build_error.py b/runbot/models/build_error.py index 24301ee8..b25240e1 100644 --- a/runbot/models/build_error.py +++ b/runbot/models/build_error.py @@ -73,6 +73,8 @@ class BuildError(models.Model): last_seen_build_id = fields.Many2one('runbot.build', compute='_compute_last_seen_build_id', string='Last Seen build', store=True) last_seen_date = fields.Datetime(string='Last Seen Date', compute='_compute_seen_date', store=True) test_tags = fields.Char(string='Test tags', help="Comma separated list of test_tags to use to reproduce/remove this error", tracking=True) + tags_min_version_id = fields.Many2one('runbot.version', 'Tags Min version', help="Minimal version where the test tags will be applied.") + tags_max_version_id = fields.Many2one('runbot.version', 'Tags Max version', help="Maximal version where the test tags will be applied.") @api.constrains('test_tags') def _check_test_tags(self): @@ -80,6 +82,12 @@ class BuildError(models.Model): if build_error.test_tags and '-' in build_error.test_tags: raise ValidationError('Build error test_tags should not be negated') + @api.onchange('test_tags') + def _onchange_test_tags(self): + for record in self: + record.tags_min_version_id = min(record.version_ids, key=lambda rec: rec.number) + record.tags_max_version_id = max(record.version_ids, key=lambda rec: rec.number) + @api.model_create_multi def create(self, vals_list): cleaners = self.env['runbot.error.regex'].search([('re_type', '=', 'cleaning')]) @@ -263,14 +271,22 @@ class BuildError(models.Model): return window_action @api.model - def _test_tags_list(self): - active_errors = self.search([('test_tags', '!=', False)]) - test_tag_list = active_errors.mapped('test_tags') + def _test_tags_list(self, build_id=False): + version = build_id.params_id.version_id.number if build_id else False + + def filter_tags(e): + if version: + min_v = e.tags_min_version_id.number or '' + max_v = e.tags_max_version_id.number or '~' + return min_v <= version and max_v >= version + return True + + test_tag_list = self.search([('test_tags', '!=', False)]).filtered(filter_tags).mapped('test_tags') return [test_tag for error_tags in test_tag_list for test_tag in (error_tags).split(',')] @api.model - def _disabling_tags(self): - return ['-%s' % tag for tag in self._test_tags_list()] + def _disabling_tags(self, build_id=False): + return ['-%s' % tag for tag in self._test_tags_list(build_id)] def _search_version(self, operator, value): exclude_domain = [] diff --git a/runbot/tests/test_build_error.py b/runbot/tests/test_build_error.py index 3729778d..d199e041 100644 --- a/runbot/tests/test_build_error.py +++ b/runbot/tests/test_build_error.py @@ -29,6 +29,16 @@ class TestBuildError(RunbotCase): create_vals.update(vals) return self.Build.create(create_vals) + def create_params(self, vals): + create_vals = { + 'version_id': self.version_13.id, + 'project_id': self.project.id, + 'config_id': self.default_config.id, + 'create_batch_id': self.dev_batch.id, + } + create_vals.update(vals) + return self.BuildParameters.create(create_vals) + def create_log(self, vals): log_vals = { 'level': 'ERROR', @@ -301,7 +311,7 @@ class TestBuildError(RunbotCase): self.assertEqual(error_a.build_count, 1) self.assertEqual(error_b.build_count, 2) - def test_build_error_test_tags(self): + def test_build_error_test_tags_no_version(self): build_a = self.create_test_build({'local_result': 'ko'}) build_b = self.create_test_build({'local_result': 'ko'}) @@ -309,34 +319,74 @@ class TestBuildError(RunbotCase): 'content': 'foo', 'build_ids': [(6, 0, [build_a.id])], 'random': True, - 'active': True + 'active': True, + 'test_tags': 'foo,bar', }) error_b = self.BuildError.create({ 'content': 'bar', 'build_ids': [(6, 0, [build_b.id])], 'random': True, - 'active': False + 'active': False, + 'test_tags': 'blah', }) - - error_a.test_tags = 'foo,bar' - error_b.test_tags = 'blah' - self.assertIn('foo', self.BuildError._test_tags_list()) - self.assertIn('bar', self.BuildError._test_tags_list()) self.assertIn('-foo', self.BuildError._disabling_tags()) self.assertIn('-bar', self.BuildError._disabling_tags()) # test that test tags on fixed errors are not taken into account - self.assertNotIn('blah', self.BuildError._test_tags_list()) self.assertNotIn('-blah', self.BuildError._disabling_tags()) error_a.test_tags = False error_b.active = True error_b.parent_id = error_a.id self.assertEqual(error_b.test_tags, False) - self.assertEqual(self.BuildError._disabling_tags(), ['-blah',]) + self.assertEqual(self.BuildError._disabling_tags(), ['-blah']) + def test_build_error_test_tags_min_max_version(self): + version_17 = self.Version.create({'name': '17.0'}) + version_saas_171 = self.Version.create({'name': 'saas-17.1'}) + version_master = self.Version.create({'name': 'master'}) + + build_v13 = self.create_test_build({'local_result': 'ko'}) + build_v17 = self.create_test_build({'local_result': 'ko', 'params_id': self.create_params({'version_id': version_17.id}).id}) + build_saas_171 = self.create_test_build({'local_result': 'ko', 'params_id': self.create_params({'version_id': version_saas_171.id}).id}) + build_master = self.create_test_build({'local_result': 'ko', 'params_id': self.create_params({'version_id': version_master.id}).id}) + + self.BuildError.create( + [ + { + "content": "foobar", + "build_ids": [(6, 0, [build_v13.id])], + "test_tags": "every,where", + }, + { + "content": "blah", + "build_ids": [(6, 0, [build_v17.id])], + "test_tags": "tag_17_up_to_master", + "tags_min_version_id": version_17.id, + }, + { + "content": "spam", + "build_ids": [(6, 0, [build_v17.id])], + "test_tags": "tag_up_to_17", + "tags_max_version_id": version_17.id, + }, + { + "content": "eggs", + "build_ids": [(6, 0, [build_saas_171.id])], + "test_tags": "tag_only_17.1", + "tags_min_version_id": version_saas_171.id, + "tags_max_version_id": version_saas_171.id, + }, + ] + ) + + self.assertEqual(sorted(['-every', '-where', '-tag_17_up_to_master', '-tag_up_to_17', '-tag_only_17.1']), sorted(self.BuildError._disabling_tags()), "Should return the whole list without parameters") + self.assertEqual(sorted(['-every', '-where', '-tag_up_to_17']), sorted(self.BuildError._disabling_tags(build_v13))) + self.assertEqual(sorted(['-every', '-where', '-tag_up_to_17', '-tag_17_up_to_master']), sorted(self.BuildError._disabling_tags(build_v17))) + self.assertEqual(sorted(['-every', '-where', '-tag_17_up_to_master', '-tag_only_17.1']), sorted(self.BuildError._disabling_tags(build_saas_171))) + self.assertEqual(sorted(['-every', '-where', '-tag_17_up_to_master']), sorted(self.BuildError._disabling_tags(build_master))) def test_build_error_team_wildcards(self): website_team = self.BuildErrorTeam.create({ diff --git a/runbot/views/build_error_views.xml b/runbot/views/build_error_views.xml index 570c5818..89ab1a9d 100644 --- a/runbot/views/build_error_views.xml +++ b/runbot/views/build_error_views.xml @@ -25,6 +25,8 @@ + + @@ -147,8 +149,8 @@ runbot.build.error.tree runbot.build.error -