[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.
This commit is contained in:
Christophe Monniez 2024-09-02 15:41:07 +02:00 committed by xdo
parent dff50b49c4
commit 33452d27e7
4 changed files with 91 additions and 20 deletions

View File

@ -455,7 +455,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

View File

@ -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 = []

View File

@ -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({

View File

@ -25,6 +25,8 @@
<field name="active"/>
<field name="test_tags" decoration-danger="True" readonly="1" groups="!runbot.group_runbot_admin"/>
<field name="test_tags" decoration-danger="True" groups="runbot.group_runbot_admin" readonly="parent_id and not test_tags"/>
<field name="tags_min_version_id" invisible="not test_tags"/>
<field name="tags_max_version_id" invisible="not test_tags"/>
</group>
<group>
<field name="version_ids" widget="many2many_tags"/>
@ -157,14 +159,17 @@
<header>
<button name="%(runbot.runbot_open_bulk_wizard)d" string="Bulk Update" type="action" groups="runbot.group_runbot_admin,runbot.group_runbot_error_manager"/>
</header>
<field name="module_name" readonly="1"/>
<field name="summary" readonly="1"/>
<field name="module_name" optional="show" readonly="1"/>
<field name="summary" optional="show" readonly="1"/>
<field name="random" string="Random"/>
<field name="first_seen_date" string="First Seen" optional="hide" readonly="1"/>
<field name="last_seen_date" string="Last Seen" readonly="1"/>
<field name="build_count" readonly="1"/>
<field name="responsible"/>
<field name="team_id"/>
<field name="test_tags"/>
<field name="tags_min_version_id" string="Tags Min" optional="show"/>
<field name="tags_max_version_id" string="Tags Max" optional="show"/>
<field name="fixing_pr_id"/>
<field name="fixing_pr_alive" invisible="1"/>
<field name="fixing_pr_url" widget="url" text="view PR" readonly="1" invisible="not fixing_pr_url"/>