Skip to content

Commit 3c63f94

Browse files
committed
Add support for full recipe loading
1 parent 860c9e6 commit 3c63f94

File tree

8 files changed

+55
-41
lines changed

8 files changed

+55
-41
lines changed

tests/recipe/data/import.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ plans:
100100
restart-max-count: 1
101101
restart-with-reboot: false
102102
serial-number: 1
103-
discover-phase: default-0
104103
link: []
105104
test: echo one
106105
path: /default-0/tests/tests
@@ -134,7 +133,6 @@ plans:
134133
restart-max-count: 1
135134
restart-with-reboot: false
136135
serial-number: 2
137-
discover-phase: default-0
138136
link: []
139137
test: echo two
140138
path: /default-0/tests/tests

tests/recipe/data/insert.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ plans:
141141
restart-max-count: 1
142142
restart-with-reboot: false
143143
serial-number: 1
144-
discover-phase: discover-shell
145144
link: []
146145
test: /bin/true
147146
path: /discover-shell/tests
@@ -174,7 +173,6 @@ plans:
174173
restart-max-count: 1
175174
restart-with-reboot: false
176175
serial-number: 2
177-
discover-phase: default-0
178176
link: []
179177
test: ./test.sh
180178
path: /default-0/tests

tests/recipe/data/remote.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ plans:
9696
restart-max-count: 1
9797
restart-with-reboot: false
9898
serial-number: 1
99-
discover-phase: default-0
10099
link: []
101100
test: echo one
102101
path: /default-0/tests/tests

tests/recipe/data/simple.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ plans:
141141
restart-max-count: 1
142142
restart-with-reboot: false
143143
serial-number: 1
144-
discover-phase: discover-fmf
145144
link: []
146145
test: ./test.sh
147146
path: /discover-fmf/tests
@@ -178,7 +177,6 @@ plans:
178177
restart-max-count: 1
179178
restart-with-reboot: false
180179
serial-number: 2
181-
discover-phase: discover-shell
182180
link: []
183181
test: /bin/true
184182
path: /discover-shell/tests

tests/recipe/load.sh

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ rlJournalStart
88
rlPhaseEnd
99

1010
rlPhaseStartTest "Test recipe loading with library dependencies"
11-
rlRun -s "tmt run -vvv --scratch --id $run --recipe simple.yaml plan -n /plans/simple"
11+
rlRun -s "tmt run -vvv --scratch --id $run --recipe simple.yaml"
1212
rlAssertGrep "name: discover-fmf" $rlRun_LOG
1313
rlAssertGrep "name: discover-shell" $rlRun_LOG
1414
rlAssertGrep "summary: 2 tests selected" $rlRun_LOG
@@ -18,25 +18,17 @@ rlJournalStart
1818
rlPhaseEnd
1919

2020
rlPhaseStartTest "Test recipe loading with remote repository"
21-
rlRun -s "tmt run -vvv --scratch --id $run --recipe remote.yaml plan -n /plans/remote"
21+
rlRun -s "tmt run -vvv --scratch --id $run --recipe remote.yaml"
2222
rlAssertGrep "summary: 1 test selected" $rlRun_LOG
2323
rlAssertGrep "pass /tests/one" $rlRun_LOG
2424
rlAssertGrep "summary: 1 test executed" $rlRun_LOG
2525
rlPhaseEnd
2626

2727
rlPhaseStartTest "Test recipe loading of imported plan"
28-
rlRun -s "tmt run -vvv --scratch --id $run --recipe import.yaml discover plan -n /plans/import"
28+
rlRun -s "tmt run -vvv --scratch --id $run --recipe import.yaml discover"
2929
rlAssertGrep "summary: 2 tests selected" $rlRun_LOG
3030
rlPhaseEnd
3131

32-
rlPhaseStartTest "Test recipe loading with non-existent plan"
33-
rlRun "temp=\$(mktemp -d)" 0 "Create temporary directory"
34-
rlRun "cp -r .fmf simple.yaml $temp"
35-
rlRun "pushd $temp"
36-
rlRun -s "tmt run -vvv --scratch --id $run --recipe simple.yaml" 2 "Remote plan is not in the recipe"
37-
rlAssertGrep "Plan '/default/plan' not found in the recipe" $rlRun_LOG
38-
rlPhaseEnd
39-
4032
rlPhaseStartCleanup
4133
rlRun "popd"
4234
rlRun "rm -r $run" 0 "Remove run directory"

tmt/base/core.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3033,6 +3033,9 @@ def environment(self) -> Environment:
30333033
options.
30343034
"""
30353035

3036+
if self.recipe is not None:
3037+
return self.recipe.run.environment.copy()
3038+
30363039
return Environment(
30373040
{
30383041
**self._environment_from_workdir,

tmt/recipe.py

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
if TYPE_CHECKING:
1515
import tmt.base.core
16-
from tmt.base.core import Dependency, Run, _RawAdjustRule, _RawLinks
16+
from tmt.base.core import Dependency, Run, _RawAdjustRule
1717
from tmt.base.plan import Plan
1818

1919

@@ -27,14 +27,6 @@ def _unserialize_dependency(
2727
return dependency_factory(serialized)
2828

2929

30-
# This needs to be a stand-alone function because of the import of `tmt.base`.
31-
# It cannot be imported on module level because of circular dependency.
32-
def _unserialize_links(serialized: Optional['_RawLinks']) -> Optional['tmt.base.core.Links']:
33-
from tmt.base.core import Links
34-
35-
return Links(data=serialized)
36-
37-
3830
@container
3931
class _RecipeTest(SerializableContainer):
4032
name: str
@@ -58,18 +50,16 @@ class _RecipeTest(SerializableContainer):
5850
restart_max_count: int
5951
restart_with_reboot: bool
6052
serial_number: int
61-
discover_phase: str
6253
link: Optional['tmt.base.core.Links'] = field(
6354
serialize=lambda value: value.to_spec() if value else [],
64-
unserialize=lambda value: _unserialize_links(value),
6555
)
6656
test: ShellScript = field(
6757
serialize=lambda value: str(value),
6858
unserialize=lambda value: ShellScript(value),
6959
)
70-
path: Optional[Path] = field(
71-
serialize=lambda value: str(value) if value else None,
72-
unserialize=lambda value: Path(value) if value else None,
60+
path: Path = field(
61+
serialize=lambda value: str(value),
62+
unserialize=lambda value: Path(value),
7363
)
7464
require: list['Dependency'] = field(
7565
serialize=lambda value: [dependency.to_minimal_spec() for dependency in value],
@@ -89,9 +79,17 @@ class _RecipeTest(SerializableContainer):
8979
)
9080
check: list[Check] = field(
9181
serialize=lambda checks: [check.to_spec() for check in checks],
92-
unserialize=lambda serialized: [Check.from_spec(**check) for check in serialized],
9382
)
9483

84+
# ignore[override]: does not match the signature on purpose, we need to pass logger
85+
@classmethod
86+
def from_serialized(cls, serialized: dict[str, Any], logger: Logger) -> '_RecipeTest': # type: ignore[override]
87+
raw_checks = serialized.pop('check', [])
88+
serialized['check'] = []
89+
recipe_test = super().from_serialized(serialized)
90+
recipe_test.check = [Check.from_spec(check, logger) for check in raw_checks]
91+
return recipe_test
92+
9593
@classmethod
9694
def from_test_origin(cls, test_origin: 'TestOrigin') -> '_RecipeTest':
9795
return _RecipeTest(
@@ -109,7 +107,7 @@ def from_test_origin(cls, test_origin: 'TestOrigin') -> '_RecipeTest':
109107
link=test_origin.test.link,
110108
component=test_origin.test.component,
111109
test=test_origin.test.test or ShellScript(''),
112-
path=test_origin.test.path,
110+
path=test_origin.test.path or Path('/'),
113111
framework=test_origin.test.framework,
114112
manual=test_origin.test.manual,
115113
tty=test_origin.test.tty,
@@ -124,7 +122,6 @@ def from_test_origin(cls, test_origin: 'TestOrigin') -> '_RecipeTest':
124122
restart_max_count=test_origin.test.restart_max_count,
125123
restart_with_reboot=test_origin.test.restart_with_reboot,
126124
serial_number=test_origin.test.serial_number,
127-
discover_phase=test_origin.phase,
128125
)
129126

130127
def to_minimal_spec(self) -> dict[str, Any]:
@@ -134,7 +131,6 @@ def to_minimal_spec(self) -> dict[str, Any]:
134131
if value not in (None, [], {})
135132
}
136133
data.pop('__class__')
137-
data.pop('discover-phase')
138134
return data
139135

140136
def to_test(self, logger: Logger) -> 'tmt.base.core.Test':
@@ -202,7 +198,9 @@ def from_serialized(cls, serialized: dict[str, Any], logger: Logger) -> '_Recipe
202198
phases=[StepData.unserialize(phase, logger) for phase in serialized.get('phases', [])]
203199
if enabled
204200
else [],
205-
tests=[_RecipeTest.from_serialized(test) for test in serialized.get('tests', [])],
201+
tests=[
202+
_RecipeTest.from_serialized(test, logger) for test in serialized.get('tests', [])
203+
],
206204
)
207205

208206
@classmethod
@@ -305,7 +303,7 @@ def from_serialized(cls, serialized: dict[str, Any], logger: Logger) -> '_Recipe
305303
tag=serialized.get('tag', []),
306304
tier=serialized.get('tier'),
307305
adjust=serialized.get('adjust'),
308-
link=_unserialize_links(serialized.get('link')),
306+
link=serialized.get('link'),
309307
environment_from_fmf=Environment.from_fmf_spec(
310308
serialized.get('environment-from-fmf', {})
311309
),
@@ -358,8 +356,27 @@ def from_plan(cls, plan: 'Plan') -> '_RecipePlan':
358356
)
359357

360358
def to_spec(self) -> dict[str, Any]:
361-
# TODO: For now, only return the discover step.
362-
return {'discover': self.discover.to_spec()}
359+
spec = self.to_dict()
360+
361+
for e in [
362+
'environment_from_fmf',
363+
'environment_from_importing',
364+
'environment_from_cli',
365+
'environment_from_intrinsics',
366+
]:
367+
spec.pop(e)
368+
369+
spec['environment'] = self.environment_from_fmf.to_fmf_spec()
370+
spec['context'] = self.context.to_spec()
371+
372+
spec['discover'] = self.discover.to_spec()
373+
spec['provision'] = self.provision.to_spec()
374+
spec['prepare'] = self.prepare.to_spec()
375+
spec['execute'] = self.execute.to_spec()
376+
spec['report'] = self.report.to_spec()
377+
spec['finish'] = self.finish.to_spec()
378+
spec['cleanup'] = self.cleanup.to_spec()
379+
return spec
363380

364381

365382
@container
@@ -425,7 +442,7 @@ def tests(self, recipe: Recipe, plan_name: str) -> list[TestOrigin]:
425442
if plan.name == plan_name:
426443
return [
427444
TestOrigin(
428-
phase=test.discover_phase,
445+
phase=test.path.unrooted().parts[0],
429446
test=test.to_test(self._logger),
430447
)
431448
for test in plan.discover.tests
@@ -438,4 +455,5 @@ def update_tree(recipe: Recipe, tree: fmf.Tree) -> None:
438455
"""
439456
Load the plans from the recipe and update the given fmf tree with their specifications.
440457
"""
458+
tree.children.clear()
441459
tree.update({plan.name: plan.to_spec() for plan in recipe.plans})

tmt/steps/discover/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,14 @@ def discover_from_recipe(self, logger: Optional[tmt.log.Logger] = None) -> None:
588588
if test_origin.phase == self.name
589589
]
590590

591+
# Recreate test paths if they don't exist
592+
for test in self._tests:
593+
if test.path is None:
594+
continue
595+
path = self.test_dir / test.path.unrooted().relative_to(Path(self.safe_name) / 'tests')
596+
if not path.exists():
597+
path.mkdir(parents=True, exist_ok=True)
598+
591599

592600
class Discover(tmt.steps.Step):
593601
"""

0 commit comments

Comments
 (0)