Skip to content

Commit

Permalink
Merge remote-tracking branch 'richtja/runnable_identifier'
Browse files Browse the repository at this point in the history
Signed-off-by: Cleber Rosa <[email protected]>
  • Loading branch information
clebergnu committed Aug 12, 2024
2 parents d223935 + 331cd09 commit 4c590e2
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 31 deletions.
47 changes: 27 additions & 20 deletions avocado/core/nrunner/runnable.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class Runnable:
execute a runnable.
"""

def __init__(self, kind, uri, *args, config=None, **kwargs):
def __init__(self, kind, uri, *args, config=None, identifier=None, **kwargs):
self.kind = kind
#: The main reference to what needs to be run. This is free
#: form, but commonly set to the path to a file containing the
Expand All @@ -113,6 +113,7 @@ def __init__(self, kind, uri, *args, config=None, **kwargs):
#: expressing assets that the test will require in order to run.
self.assets = kwargs.pop("assets", None)
self.kwargs = kwargs
self._identifier = identifier

def __repr__(self):
fmt = (
Expand Down Expand Up @@ -156,27 +157,29 @@ def identifier(self):
Since this is formatter, combined values can be used. Example:
"{uri}-{args}".
"""
fmt = self.config.get("runner.identifier_format", "{uri}")
if not self._identifier:
fmt = self.config.get("runner.identifier_format", "{uri}")

# Optimize for the most common scenario
if fmt == "{uri}":
return self.uri
# Optimize for the most common scenario
if fmt == "{uri}":
return self.uri

# For args we can use the entire list of arguments or with a specific
# index.
args = "-".join(self.args)
if "args" in fmt and "[" in fmt:
args = self.args
# For args we can use the entire list of arguments or with a specific
# index.
args = "-".join(self.args)
if "args" in fmt and "[" in fmt:
args = self.args

# For kwargs we can use the entire list of values or with a specific
# index.
kwargs = "-".join(str(self.kwargs.values()))
if "kwargs" in fmt and "[" in fmt:
kwargs = self.kwargs
# For kwargs we can use the entire list of values or with a specific
# index.
kwargs = "-".join(str(self.kwargs.values()))
if "kwargs" in fmt and "[" in fmt:
kwargs = self.kwargs

options = {"uri": self.uri, "args": args, "kwargs": kwargs}
options = {"uri": self.uri, "args": args, "kwargs": kwargs}
self._identifier = fmt.format(**options)

return fmt.format(**options)
return self._identifier

@property
def config(self):
Expand Down Expand Up @@ -255,7 +258,7 @@ def _validate_recipe(cls, recipe):
"""
if not cls._validate_recipe_json_schema(recipe):
# This is a simplified validation of the recipe
allowed = set(["kind", "uri", "args", "kwargs", "config"])
allowed = set(["kind", "uri", "args", "kwargs", "config", "identifier"])
if not "kind" in recipe:
raise RunnableRecipeInvalidError('Missing required property "kind"')
if not set(recipe.keys()).issubset(allowed):
Expand All @@ -279,6 +282,7 @@ def from_dict(cls, recipe_dict):
recipe_dict.get("uri"),
*recipe_dict.get("args", ()),
config=config,
identifier=recipe_dict.get("identifier"),
**recipe_dict.get("kwargs", {}),
)

Expand All @@ -296,12 +300,14 @@ def from_recipe(cls, recipe_path):
return cls.from_dict(recipe_dict)

@classmethod
def from_avocado_config(cls, kind, uri, *args, config=None, **kwargs):
def from_avocado_config(
cls, kind, uri, *args, config=None, identifier=None, **kwargs
):
"""Creates runnable with only essential config for runner of specific kind."""
if not config:
config = {}
config = cls.filter_runnable_config(kind, config)
return cls(kind, uri, *args, config=config, **kwargs)
return cls(kind, uri, *args, config=config, identifier=identifier, **kwargs)

@classmethod
def get_configuration_used_by_kind(cls, kind):
Expand Down Expand Up @@ -423,6 +429,7 @@ def get_dict(self):
if self.uri is not None:
recipe["uri"] = self.uri
recipe["config"] = self.config
recipe["identifier"] = self.identifier
if self.args is not None:
recipe["args"] = self.args
kwargs = self.kwargs.copy()
Expand Down
19 changes: 16 additions & 3 deletions avocado/plugins/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,13 @@ def _prepare_matrix_for_display(matrix, verbose=False):
type_label = TERM_SUPPORT.healthy_str(kind)
if verbose:
colored_matrix.append(
(type_label, item[1], item[2], _get_tags_as_string(item[3] or {}))
(
type_label,
item[1],
item[2],
item[3],
_get_tags_as_string(item[4] or {}),
)
)
else:
colored_matrix.append((type_label, item[1]))
Expand All @@ -65,6 +71,7 @@ def _display(self, suite, matrix):
header = (
TERM_SUPPORT.header_str("Type"),
TERM_SUPPORT.header_str("Test"),
TERM_SUPPORT.header_str("Uri"),
TERM_SUPPORT.header_str("Resolver"),
TERM_SUPPORT.header_str("Tag(s)"),
)
Expand Down Expand Up @@ -140,10 +147,16 @@ def _get_resolution_matrix(suite):
if verbose:
tags = runnable.tags or {}
test_matrix.append(
(runnable.kind, runnable.uri, resolution.origin, tags)
(
runnable.kind,
runnable.identifier,
runnable.uri,
resolution.origin,
tags,
)
)
else:
test_matrix.append((runnable.kind, runnable.uri))
test_matrix.append((runnable.kind, runnable.identifier))
return test_matrix

@staticmethod
Expand Down
4 changes: 4 additions & 0 deletions avocado/schemas/runnable-recipe.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
"config": {
"description": "Avocado settings that should be applied to this runnable. At least the ones declared as CONFIGURATION_USED in the runner specific for this kind should be present",
"type": "object"
},
"identifier": {
"description": "ID of runnable which will be used for identification during the runtime and in logs. If this is not specified it will be autogenerated based on `runner.identifier_format` config value",
"type": "string"
}
},
"additionalProperties": false,
Expand Down
9 changes: 8 additions & 1 deletion examples/jobs/custom_exec_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,17 @@
# here, 'Hello World!' is appended to the uri (/usr/bin/echo)
echo = Runnable("exec-test", "/usr/bin/echo", "Hello World!")

# here, echo-hello-world is used as id of this runnable
id_test = Runnable(
"exec-test", "/usr/bin/echo", "Hello World!", identifier="echo-hello-world"
)

# the execution of examples/tests/sleeptest.sh takes around 2 seconds
# and the output of the /usr/bin/echo test is available at the
# job-results/latest/test-results/exec-test-2-_usr_bin_echo/stdout file.
suite = TestSuite(name="exec-test", tests=[sleeptest, echo])
# and the last test is the same as the echo, but you can notice that
# the identifier in avocado output is more specific to what this test is doing.
suite = TestSuite(name="exec-test", tests=[sleeptest, echo, id_test])

with Job(test_suites=[suite]) as j:
sys.exit(j.run())
2 changes: 1 addition & 1 deletion examples/nrunner/recipes/runnable/exec_test_sleep_3.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"kind": "exec-test", "uri": "/bin/sleep", "args": ["3"]}
{"kind": "exec-test", "uri": "/bin/sleep", "identifier": "sleep-test", "args": ["3"]}
1 change: 1 addition & 0 deletions examples/nrunner/recipes/runnable/identifier.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"kind": "noop", "identifier": "test-noop"}
6 changes: 4 additions & 2 deletions examples/nrunner/recipes/runnables/true_false.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
[
{"kind": "exec-test",
"uri": "/bin/true"},
"uri": "/bin/true",
"identifier": "true-test"},

{"kind": "exec-test",
"uri": "/bin/false"}
"uri": "/bin/false",
"identifier": "false-test"}
]
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": 670,
"unit": 671,
"jobs": 11,
"functional-parallel": 307,
"functional-serial": 7,
Expand Down
4 changes: 2 additions & 2 deletions selftests/functional/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def test_runnable_recipe_origin(self):
cmd_line = f"{AVOCADO} -V list {test_path}"
result = process.run(cmd_line)
self.assertIn(
b"python-unittest selftests/unit/test.py:TestClassTestUnit.test_long_name runnable-recipe\n",
b"python-unittest selftests/unit/test.py:TestClassTestUnit.test_long_name selftests/unit/test.py:TestClassTestUnit.test_long_name runnable-recipe\n",
result.stdout,
)

Expand Down Expand Up @@ -183,7 +183,7 @@ def test_runnables_recipe(self):
==================
asset: 1
exec-test: 3
noop: 1
noop: 2
package: 1
python-unittest: 1
sysinfo: 1"""
Expand Down
18 changes: 17 additions & 1 deletion selftests/unit/runnable.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ def test_get_dict(self):
"uri": "_uri_",
"args": ("arg1", "arg2"),
"config": {"runner.identifier_format": "{uri}"},
"identifier": "_uri_",
},
)

Expand All @@ -92,6 +93,7 @@ def test_get_json(self):
'{"kind": "noop", '
'"uri": "_uri_", '
'"config": {"runner.identifier_format": "{uri}"}, '
'"identifier": "_uri_", '
'"args": ["arg1", "arg2"]}'
)
self.assertEqual(runnable.get_json(), expected)
Expand Down Expand Up @@ -144,6 +146,19 @@ def test_config(self):
runnable.config.get("runner.identifier_format"), "{uri}-{args[0]}"
)

def test_identifier(self):
open_mocked = unittest.mock.mock_open(
read_data=(
'{"kind": "exec-test", "uri": "/bin/sh", '
'"args": ["/etc/profile"], '
'"config": {"runner.identifier_format": "{uri}-{args[0]}"}, '
'"identifier": "exec-test-1"}'
)
)
with unittest.mock.patch("avocado.core.nrunner.runnable.open", open_mocked):
runnable = Runnable.from_recipe("fake_path")
self.assertEqual(runnable.identifier, "exec-test-1")


class RunnableFromCommandLineArgs(unittest.TestCase):
def test_noop(self):
Expand Down Expand Up @@ -219,10 +234,11 @@ def test_runnable_to_recipe_uri(self):
self.assertEqual(loaded_runnable.uri, "/bin/true")

def test_runnable_to_recipe_args(self):
runnable = Runnable("exec-test", "/bin/sleep", "0.01")
runnable = Runnable("exec-test", "/bin/sleep", "0.01", identifier="exec-test-1")
open_mocked = unittest.mock.mock_open(read_data=runnable.get_json())
with unittest.mock.patch("avocado.core.nrunner.runnable.open", open_mocked):
loaded_runnable = Runnable.from_recipe("fake_path")
self.assertEqual(loaded_runnable.kind, "exec-test")
self.assertEqual(loaded_runnable.uri, "/bin/sleep")
self.assertEqual(loaded_runnable.args, ("0.01",))
self.assertEqual(loaded_runnable.identifier, "exec-test-1")

0 comments on commit 4c590e2

Please sign in to comment.