Skip to content

Commit

Permalink
Runnable and Suite configuration support
Browse files Browse the repository at this point in the history
This change allows for the a runnable configuration and the suite and
default configuration to coexist with the correct behavior.

In short, if the suite has a configuration, it will become the new
default configuration for a runnable, while its own configuration will
not be touched.

Because the purpose of the default_config and config are different,
the checks for the values they may contain are different.  The
default_config should contain the exact keys as in the used config.
The config itself, on the other hand, may contain nothing, or some
values, but it should never be more than a subset of the config.

Note: there's an opportunity here for changing the data
structure used for the "config" attribute, one that looks
up the value in the "default_config" if it doesn't exist
in the actual "config".  But, given that the size of the
"default_config" will always be very small (given that
it's filtered to contain only the used configuration
by the runner) it seemed an unnecessary optimization
at this time.

Fixes: avocado-framework#5998
Signed-off-by: Cleber Rosa <[email protected]>
  • Loading branch information
clebergnu committed Aug 22, 2024
1 parent e7d6f33 commit 7608215
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 17 deletions.
35 changes: 21 additions & 14 deletions avocado/core/nrunner/runnable.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import base64
import collections
import copy
import json
import logging
import os
Expand Down Expand Up @@ -105,10 +106,7 @@ def __init__(self, kind, uri, *args, config=None, identifier=None, **kwargs):
#: that is passed to runners, as long as a runner declares
#: its interest in using them with
#: attr:`avocado.core.nrunner.runner.BaseRunner.CONFIGURATION_USED`
self._config = {}
if config is None:
config = {}
self.config = self.filter_runnable_config(kind, config)
self.config = config or {}
self.args = args
self.tags = kwargs.pop("tags", None)
self.dependencies = self.read_dependencies(kwargs.pop("dependencies", None))
Expand Down Expand Up @@ -188,21 +186,30 @@ def identifier(self):

@property
def config(self):
return self._config
if not self._config:
return self._default_config
config_with_defaults = copy.copy(self._default_config)
config_with_defaults.update(self._config)
return config_with_defaults

@property
def default_config(self):
return self._default_config

def _config_setter_warning(self, config):
def _config_setter_warning(self, config, default_config=False):
configuration_used = Runnable.get_configuration_used_by_kind(self.kind)
if not set(configuration_used).issubset(set(config.keys())):
LOG.warning(
"The runnable config should have only values "
"essential for its runner. In the next version of "
"avocado, this will raise a ValueError. Please "
"use avocado.core.nrunner.runnable.Runnable.filter_runnable_config"
)
if default_config:
if set(configuration_used) == (set(config.keys())):
return
else:
if set(config.keys()).issubset(set(configuration_used)):
return
LOG.warning(
"The runnable config should have only values "
"essential for its runner. In the next version of "
"avocado, this will raise a ValueError. Please "
"use avocado.core.nrunner.runnable.Runnable.filter_runnable_config"
)

@config.setter
def config(self, config):
Expand Down Expand Up @@ -232,7 +239,7 @@ def default_config(self, config):
:param config: A config dict with default values for this Runnable.
:type config: dict
"""
self._config_setter_warning(config)
self._config_setter_warning(config, True)
self._default_config = config

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion avocado/core/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ def resolutions_to_runnables(resolutions, config):
if resolution.result != ReferenceResolutionResult.SUCCESS:
continue
for runnable in resolution.resolutions:
runnable.config = runnable.filter_runnable_config(runnable.kind, config)
runnable.default_config = runnable.filter_runnable_config(
runnable.kind, config
)
result.append(runnable)
return result

Expand Down
1 change: 1 addition & 0 deletions examples/nrunner/recipes/runnable/noop_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"kind": "noop", "uri": "noop", "config": {"runner.identifier_format": "nothing-op"}}
2 changes: 1 addition & 1 deletion selftests/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"job-api-7": 1,
"nrunner-interface": 70,
"nrunner-requirement": 28,
"unit": 677,
"unit": 678,
"jobs": 11,
"functional-parallel": 307,
"functional-serial": 7,
Expand Down
2 changes: 1 addition & 1 deletion selftests/functional/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ def test_runnables_recipe(self):
==================
asset: 1
exec-test: 3
noop: 2
noop: 3
package: 1
python-unittest: 1
sysinfo: 1"""
Expand Down
11 changes: 11 additions & 0 deletions selftests/unit/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ def test_config_runnable(self):
runnable = suite.tests[0]
self.assertEqual(runnable.config.get("runner.identifier_format"), "NOT FOO")

def test_config_runnable_and_suite(self):
config = {
"resolver.references": [
"examples/nrunner/recipes/runnable/noop_config.json",
],
"runner.identifier_format": "NOT FOO",
}
suite = TestSuite.from_config(config)
runnable = suite.tests[0]
self.assertEqual(runnable.config.get("runner.identifier_format"), "nothing-op")

def tearDown(self):
self.tmpdir.cleanup()

Expand Down

0 comments on commit 7608215

Please sign in to comment.