Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New match feature (new) #1422

Merged
merged 10 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions checkbox-ng/plainbox/impl/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,9 @@ class DynamicSection(dict):
"exclude": VarSpec(
list, [], "Exclude test matching patterns from running."
),
"match": VarSpec(
list, [], "Only run job that match or their dependencies."
),
},
),
(
Expand Down
14 changes: 8 additions & 6 deletions checkbox-ng/plainbox/impl/secure/qualifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,13 @@
import itertools
import logging
import operator
import os
import re
import sre_constants

from plainbox.abc import IUnitQualifier
from plainbox.i18n import gettext as _
from plainbox.impl import pod
from plainbox.impl.secure.origin import FileTextSource
from plainbox.impl.secure.origin import Origin
from plainbox.impl.secure.origin import UnknownTextSource


_logger = logging.getLogger("plainbox.secure.qualifiers")
Expand Down Expand Up @@ -165,14 +162,19 @@ def __init__(self, pattern, origin, inclusive=True):
raise exc
self._pattern_text = pattern

def get_simple_match(self, job):
def get_simple_match(self, unit):
"""
Check if the given job matches this qualifier.
Check if the given unit matches this qualifier.

This method should not be called directly, it is an implementation
detail of SimpleQualifier class.
"""
return self._pattern.match(job.id) is not None
pattern = self._pattern
if unit.template_id:
return bool(
pattern.match(unit.template_id) or pattern.match(unit.id)
)
return pattern.match(unit.id) is not None

@property
def pattern_text(self):
Expand Down
14 changes: 14 additions & 0 deletions checkbox-ng/plainbox/impl/secure/test_qualifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,20 @@ def test_get_vote(self):
IUnitQualifier.VOTE_IGNORE,
)

def test_matches_any_id(self):
template_job_mock = mock.Mock(template_id="some", id="other")

reg_match_template_id = RegExpJobQualifier("some", self.origin)
self.assertTrue(
reg_match_template_id.get_simple_match(template_job_mock)
)

reg_match_id = RegExpJobQualifier("other", self.origin)
self.assertTrue(reg_match_id.get_simple_match(template_job_mock))

reg_match_nothing = RegExpJobQualifier("nothing", self.origin)
self.assertFalse(reg_match_nothing.get_simple_match(template_job_mock))


class JobIdQualifierTests(TestCase):
"""
Expand Down
24 changes: 23 additions & 1 deletion checkbox-ng/plainbox/impl/session/assistant.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# This file is part of Checkbox.
#
# Copyright 2012-2023 Canonical Ltd.
# Copyright 2012-2024 Canonical Ltd.
# Written by:
# Zygmunt Krynicki <[email protected]>
# Maciej Kisielewski <[email protected]>
# Massimiliano Girardi <[email protected]>
#
# Checkbox is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3,
Expand Down Expand Up @@ -181,6 +182,7 @@ def __init__(
# manager matters, the context and metadata are just shortcuts to stuff
# available on the manager.
self._exclude_qualifiers = []
self._match_qualifiers = []
self._manager = None
self._context = None
self._metadata = None
Expand Down Expand Up @@ -335,6 +337,12 @@ def use_alternate_configuration(self, config):
self._exclude_qualifiers.append(
RegExpJobQualifier(pattern, None, False)
)

self._match_qualifiers = []
for pattern in self._config.get_value("test selection", "match"):
self._match_qualifiers.append(
RegExpJobQualifier(pattern, None, True)
)
Unit.config = config
# NOTE: We expect applications to call this at most once.
del UsageExpectation.of(self).allowed_calls[
Expand Down Expand Up @@ -935,6 +943,20 @@ def finish_bootstrap(self):
)
],
)
if self._match_qualifiers:
# when `match` is provided, use the test plan but prune it to
# only pull the jobs asked in the launcher or their dependencies
desired_job_list = select_units(
desired_job_list,
self._match_qualifiers
+ self._exclude_qualifiers
+ [
JobIdQualifier(
"com.canonical.plainbox::collect-manifest", None, False
)
],
)

self._context.state.update_desired_job_list(desired_job_list)
# Set subsequent usage expectations i.e. all of the runtime parts are
# available now.
Expand Down
55 changes: 54 additions & 1 deletion checkbox-ng/plainbox/impl/session/test_assistant.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# This file is part of Checkbox.
#
# Copyright 2015 Canonical Ltd.
# Copyright 2015-2024 Canonical Ltd.
# Written by:
# Zygmunt Krynicki <[email protected]>
# Massimiliano Girardi <[email protected]>
#
# Checkbox is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3,
Expand Down Expand Up @@ -223,3 +224,55 @@ def test_get_bootstrap_todo_list(
self.assertEqual(
self_mock._context.state.update_desired_job_list.call_count, 1
)

@mock.patch("plainbox.impl.session.assistant.UsageExpectation")
def test_use_alternate_configuration(self, ue_mock, mock_get_providers):
self_mock = mock.MagicMock()

def get_value(section, value):
if section == "test selection" and value == "exclude":
return [r".*some.*", r".*other.*"]
elif section == "test selection" and value == "match":
return [r".*target", r".*another_target"]
raise AssertionError(
"Need more configuration sections/config to mock,"
" test asked for [{}][{}]".format(section, value)
)

config_mock = mock.MagicMock()
config_mock.get_value.side_effect = get_value

SessionAssistant.use_alternate_configuration(self_mock, config_mock)

self.assertEqual(len(self_mock._exclude_qualifiers), 2)
self.assertEqual(len(self_mock._match_qualifiers), 2)

@mock.patch("plainbox.impl.session.assistant.UsageExpectation")
@mock.patch("plainbox.impl.session.assistant.select_units")
def test_finish_bootstrap_match_nominal(
self, select_units_mock, ue_mock, get_providers_mock
):
self_mock = mock.MagicMock()
# this is just to test that the subfunction is called if this arr is
# defined, assumes the select_units function is mocked
self_mock._match_qualifiers = [1, 2, 3]

SessionAssistant.finish_bootstrap(self_mock)

# called once to get all the jobs for the selected testplan
# and another time to prune it for match`
self.assertEqual(select_units_mock.call_count, 2)

@mock.patch("plainbox.impl.session.assistant.UsageExpectation")
@mock.patch("plainbox.impl.session.assistant.select_units")
def test_finish_bootstrap_match_no_match(
self, select_units_mock, ue_mock, get_providers_mock
):
self_mock = mock.MagicMock()
self_mock._match_qualifiers = []

SessionAssistant.finish_bootstrap(self_mock)

# called once to get all the jobs for the selected testplan
# and another time to prune it for match
self.assertEqual(select_units_mock.call_count, 1)
2 changes: 1 addition & 1 deletion checkbox-support/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
'requests_unixsocket2; python_version>="3.12"',
'importlib_metadata; python_version<"3.8"',
'systemd-python==233; python_version=="3.5"',
'systemd-python>=235; python_version>="3.6"',
'systemd-python>=234; python_version>="3.6"',
'pyyaml',
]
[metadata]
Expand Down
2 changes: 1 addition & 1 deletion checkbox-support/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ install_requires=
requests_unixsocket2; python_version>="3.12"
importlib_metadata; python_version<"3.8"
systemd-python == 233; python_version=="3.5"
systemd-python == 235; python_version>="3.6"
systemd-python >= 234; python_version>="3.6"
[metadata]
name=checkbox-support
[options.entry_points]
Expand Down
25 changes: 22 additions & 3 deletions docs/reference/launcher.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,25 @@ Note: To clear the exclude list use...

...in your 'last' config.

``match``
List of regex patterns that job ids and template ids will be matched against.
Checkbox will only run the matching jobs, their dependencies and any job
included in the testplan bootstrap section. This is useful to re-run the
failing subset of jobs included in a test plan.

Only run ``bluetooth`` jobs and their dependencies:

.. code-block:: ini

[test selection]
match = .*bluetooth.*

.. note::
``exclude`` takes precedence over ``match``.

.. note::
You can use ``match`` only to select jobs already included in a test
plan. You can not use it to include additional tests in a test plan.

.. _launcher_ui:

Expand All @@ -227,7 +246,7 @@ This section controls which type of UI to use.
* ``silent`` skips the tests that would require human interaction. This UI
type requires forcing test selection and test plan selection. It's not
'silent' in the traditional command-line tool sense.
* ``converged`` launches the QML interface. It requires ``checkbox-converged``
* ``converged`` launches the QML interface. It requires ``checkbox-converged``
to be installed on your system.
* ``converged-silent`` launches the QML interface and skips the tests that
would require human interaction. It requires ``checkbox-converged`` to be
Expand Down Expand Up @@ -287,7 +306,7 @@ This section controls which type of UI to use.

.. note::

You can use ``auto-retry=no`` inline in the test plan to exclude a job
You can use ``auto-retry=no`` inline in the test plan to exclude a job
from auto-retrying. For more details, see :doc:`../how-to/launcher/auto-retry`.

``max_attempts``
Expand All @@ -300,7 +319,7 @@ This section controls which type of UI to use.
the testing session. This can be useful when the jobs rely on external
factors (e.g. a WiFi access point) and you want to wait before retrying the
same job. Default value: ``1``.

Restart section
===============

Expand Down
1 change: 1 addition & 0 deletions metabox/metabox/core/machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ def _get_install_dependencies_cmds(self):
else '"pip>20"'
)
return [
"bash -c 'sudo apt-get install -qq -y pkg-config libsystemd-dev'",
"bash -c 'sudo python3 -m pip install -U {}'".format(pip_version),
]

Expand Down
Loading
Loading