Skip to content

Commit 6566101

Browse files
tests: Add test for BuildPipeline
Signed-off-by: Kacper Słomiński <kslominski@antmicro.com>
1 parent bc80d65 commit 6566101

1 file changed

Lines changed: 202 additions & 0 deletions

File tree

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# Copyright (c) 2026 Antmicro <www.antmicro.com>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from pathlib import Path
5+
from typing import Optional, cast
6+
7+
import pytest
8+
from pyfakefs.fake_filesystem import FakeFilesystem
9+
10+
from topwrap.model.design import Design
11+
from topwrap.model.misc import Identifier
12+
from topwrap.model.module import Module
13+
from topwrap.plugin.base import (
14+
BasePlugin,
15+
BuildContext,
16+
Source,
17+
StringSource,
18+
)
19+
from topwrap.plugin.manager import PluginManager
20+
from topwrap.plugin.pipeline import BuildPipeline
21+
from topwrap.plugin.steps import InputStage, OutputStage, Transformation, Validation
22+
23+
24+
class MockPluginManager(PluginManager):
25+
def __init__(self, plugins: list[BasePlugin]):
26+
super().__init__()
27+
self.plugins = [(p.priority, type(p).__name__, p) for p in plugins]
28+
self.plugins = sorted(self.plugins, key=lambda x: x[0], reverse=True)
29+
self.loaded = True
30+
31+
def load_plugins(self):
32+
pass
33+
34+
35+
class MockInputStage(InputStage):
36+
name: str = "Mock input"
37+
38+
def process_input(
39+
self, design_source: Optional[Source], sources: list[Source], ctx: BuildContext
40+
):
41+
for s in sources:
42+
assert isinstance(s, StringSource)
43+
mod = Module(id=Identifier(s.content))
44+
ctx.loaded_modules += [Module(id=Identifier(s.content))]
45+
if design_source:
46+
assert isinstance(design_source, StringSource)
47+
mod = Module(id=Identifier(design_source.content))
48+
mod.design = Design()
49+
ctx.loaded_modules += [mod]
50+
ctx.top_module = mod
51+
52+
53+
class MockTransformation(Transformation):
54+
name: str = "Mock transformation"
55+
56+
def __init__(self, priority: int, design_only: bool, steps: list[str]):
57+
self.priority = priority
58+
self.design_only = design_only
59+
self.steps = steps
60+
61+
def apply_transformation(self, mod: Module, ctx: BuildContext):
62+
self.steps += [f"transform {mod.id.name} {self.priority} {self.design_only}"]
63+
64+
65+
class MockValidation(Validation):
66+
name: str = "Mock validation"
67+
68+
def __init__(self, priority: int, design_only: bool, steps: list[str]):
69+
self.priority = priority
70+
self.design_only = design_only
71+
self.steps = steps
72+
73+
def validate(self, mod: Module, ctx: BuildContext):
74+
self.steps += [f"validate {mod.id.name} {self.priority} {self.design_only}"]
75+
76+
77+
class MockOutputStage(OutputStage):
78+
name: str = "Mock output stage"
79+
80+
def __init__(self, parent_name: str, steps: list[str]):
81+
self.parent_name = parent_name
82+
self.steps = steps
83+
84+
def generate_output(self, ctx: BuildContext):
85+
ctx.outputs[self.name + self.parent_name] = f"mock.{self.parent_name}.output"
86+
self.steps += [f"generate_output {self.parent_name}"]
87+
88+
def write_output_to(self, target_dir: Path, ctx: BuildContext):
89+
assert (self.name + self.parent_name) in ctx.outputs
90+
out = cast(str, ctx.outputs[self.name + self.parent_name])
91+
92+
with open(target_dir / out, "w") as f:
93+
f.write(out)
94+
self.steps += [f"write_output_to {target_dir / out} {self.parent_name}"]
95+
96+
97+
class MockPlugin(BasePlugin):
98+
priority: int = 1
99+
100+
def __init__(self, steps: list[str]):
101+
self.steps = steps
102+
103+
def pre_ir_generation(self, ctx: BuildContext):
104+
self.steps += [f"pre_ir_generation {self.__class__.__name__}"]
105+
106+
def post_ir_generation(self, ctx: BuildContext):
107+
self.steps += [f"post_ir_generation {self.__class__.__name__}"]
108+
109+
def pre_transform(self, ctx: BuildContext):
110+
self.steps += [f"pre_transform {self.__class__.__name__}"]
111+
112+
def post_transform(self, ctx: BuildContext):
113+
self.steps += [f"post_transform {self.__class__.__name__}"]
114+
115+
def pre_validate(self, ctx: BuildContext):
116+
self.steps += [f"pre_validate {self.__class__.__name__}"]
117+
118+
def post_validate(self, ctx: BuildContext):
119+
self.steps += [f"post_validate {self.__class__.__name__}"]
120+
121+
def pre_output_generation(self, ctx: BuildContext):
122+
self.steps += [f"pre_output_generation {self.__class__.__name__}"]
123+
124+
def post_output_generation(self, ctx: BuildContext):
125+
self.steps += [f"post_output_generation {self.__class__.__name__}"]
126+
127+
def pre_output_writing(self, ctx: BuildContext, target_dir: Path):
128+
self.steps += [f"post_output_writing {self.__class__.__name__} {target_dir}"]
129+
130+
def post_output_writing(self, ctx: BuildContext, target_dir: Path):
131+
self.steps += [f"post_output_writing {self.__class__.__name__} {target_dir}"]
132+
133+
134+
class MockPluginHiPrio(MockPlugin):
135+
priority: int = 10
136+
137+
138+
class TestBuildPipeline:
139+
@pytest.mark.usefixtures("fs")
140+
def test_blank_pipeline(self, fs: FakeFilesystem):
141+
steps: list[str] = []
142+
143+
pipeline = BuildPipeline(
144+
inputs=[MockInputStage()],
145+
transformations=[
146+
MockTransformation(1, False, steps),
147+
MockTransformation(2, True, steps),
148+
],
149+
validations=[MockValidation(1, False, steps), MockValidation(2, True, steps)],
150+
outputs=[MockOutputStage("A", steps), MockOutputStage("B", steps)],
151+
plugin_manager=MockPluginManager(
152+
[
153+
MockPlugin(steps),
154+
MockPluginHiPrio(steps),
155+
]
156+
),
157+
)
158+
159+
outdir = fs.create_dir("output")
160+
pipeline.run_str(["a", "b", "c"], "d", Path(outdir.path))
161+
162+
assert steps == [
163+
"pre_ir_generation MockPluginHiPrio",
164+
"pre_ir_generation MockPlugin",
165+
"post_ir_generation MockPluginHiPrio",
166+
"post_ir_generation MockPlugin",
167+
"pre_transform MockPluginHiPrio",
168+
"pre_transform MockPlugin",
169+
"transform d 2 True",
170+
"transform a 1 False",
171+
"transform b 1 False",
172+
"transform c 1 False",
173+
"transform d 1 False",
174+
"post_transform MockPluginHiPrio",
175+
"post_transform MockPlugin",
176+
"pre_validate MockPluginHiPrio",
177+
"pre_validate MockPlugin",
178+
"validate d 2 True",
179+
"validate a 1 False",
180+
"validate b 1 False",
181+
"validate c 1 False",
182+
"validate d 1 False",
183+
"post_validate MockPluginHiPrio",
184+
"post_validate MockPlugin",
185+
"pre_output_generation MockPluginHiPrio",
186+
"pre_output_generation MockPlugin",
187+
"generate_output A",
188+
"generate_output B",
189+
"post_output_generation MockPluginHiPrio",
190+
"post_output_generation MockPlugin",
191+
"post_output_writing MockPluginHiPrio /output",
192+
"post_output_writing MockPlugin /output",
193+
"write_output_to /output/mock.A.output A",
194+
"write_output_to /output/mock.B.output B",
195+
"post_output_writing MockPluginHiPrio /output",
196+
"post_output_writing MockPlugin /output",
197+
]
198+
199+
assert set(fs.listdir(outdir.path)) == {
200+
"mock.A.output",
201+
"mock.B.output",
202+
}

0 commit comments

Comments
 (0)