Coverage for tests/integration_tests/test_projects.py: 100%
152 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-06 10:20 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-06 10:20 +0000
1"""
2Test various "apio" commands.
3"""
5import os
6from os.path import getsize
7from pathlib import Path
8from tests.conftest import ApioRunner
9from apio.commands.apio import apio_top_cli as apio
12def test_project_with_legacy_board_id(apio_runner: ApioRunner):
13 """Test a project that uses a legacy board id."""
15 # -- This is a slow test. Skip it if running with --fast-only flag.
16 apio_runner.skip_test_if_fast_only()
18 # -- We shared the apio home with the other tests in this file to speed
19 # -- up apio package installation. Tests should not mutate the shared home
20 # -- to avoid cross-interference between tests in this file.
21 with apio_runner.in_sandbox() as sb:
23 # -- Fetch an example of a board that has a legacy name.
24 result = sb.invoke_apio_cmd(
25 apio, ["examples", "fetch", "ice40-hx8k/leds"]
26 )
27 sb.assert_result_ok(result)
29 # -- Run 'apio build'
30 result = sb.invoke_apio_cmd(apio, ["build"])
31 sb.assert_result_ok(result)
33 # -- Modify the apio.ini to have the legacy board id
34 sb.write_apio_ini(
35 {
36 "[env:default]": {
37 "board": "iCE40-HX8K",
38 "top-module": "leds",
39 }
40 }
41 )
43 # -- Run 'apio clean'
44 result = sb.invoke_apio_cmd(apio, ["clean"])
45 sb.assert_result_ok(result)
47 # -- Run 'apio build' again. It should also succeed.
48 result = sb.invoke_apio_cmd(apio, ["build"])
49 sb.assert_result_ok(result)
52def _test_project(
53 apio_runner: ApioRunner,
54 *,
55 remote_proj_dir: bool,
56 example: str,
57 testbench_file: str,
58 bitstream: str,
59 report_item: str,
60):
61 """A common project integration test. Invoked per each tested
62 architecture.
63 """
65 # pylint: disable=too-many-arguments
66 # pylint: disable=too-many-statements
68 # -- This is a slow test. Skip it if running with --fast-only flag.
69 apio_runner.skip_test_if_fast_only()
71 # -- Extract the base name of the testbench file
72 testbench, _ = os.path.splitext(testbench_file)
74 # -- We shared the apio home with the other tests in this file to speed
75 # -- up apio package installation. Tests should not mutate the shared home
76 # -- to avoid cross-interference between tests in this file.
77 with apio_runner.in_sandbox() as sb:
79 # -- If testing from a remote dir, step out of the proj dir, and
80 # -- use -p proj_dir arg.
81 if remote_proj_dir:
82 os.chdir(sb.sandbox_dir)
83 sb.proj_dir.rmdir()
84 proj_arg = ["-p", str(sb.proj_dir)]
85 dst_arg = ["-d", str(sb.proj_dir)]
86 else:
87 proj_arg = []
88 dst_arg = []
90 # -- If testing from a remote dir, the proj dir should not exist yet,
91 # -- else, the project dir should be empty.
92 if remote_proj_dir:
93 assert not sb.proj_dir.exists()
94 else:
95 assert not os.listdir(sb.proj_dir)
97 # -- 'apio examples fetch <example> -d <proj_dir>'
98 args = ["examples", "fetch", example] + dst_arg
99 result = sb.invoke_apio_cmd(apio, args)
100 sb.assert_result_ok(result)
101 assert f"Copying {example} example files" in result.output
102 assert "fetched successfully" in result.output
103 assert getsize(sb.proj_dir / "apio.ini")
105 # -- Remember the original list of project files.
106 project_files = os.listdir(sb.proj_dir)
108 # -- 'apio build'
109 args = ["build"] + proj_arg
110 result = sb.invoke_apio_cmd(apio, args)
111 sb.assert_result_ok(result)
112 assert "SUCCESS" in result.output
113 assert "yosys -p" in result.output
115 assert getsize(sb.proj_dir / "_build/default" / bitstream)
117 # -- 'apio build' (no change)
118 args = ["build"] + proj_arg
119 result = sb.invoke_apio_cmd(apio, args)
120 sb.assert_result_ok(result)
121 assert "SUCCESS" in result.output
122 assert "yosys -p" not in result.output
124 # -- Modify apio.ini
125 apio_ini_lines = sb.read_file(
126 sb.proj_dir / "apio.ini", lines_mode=True
127 )
128 apio_ini_lines.append(" ")
129 sb.write_file(sb.proj_dir / "apio.ini", apio_ini_lines, exists_ok=True)
131 # -- 'apio build'
132 # -- Apio.ini modification should triggers a new build.
133 args = ["build"] + proj_arg
134 result = sb.invoke_apio_cmd(apio, args)
135 sb.assert_result_ok(result)
136 assert "SUCCESS" in result.output
137 assert "yosys -p" in result.output
139 # -- 'apio lint'
140 args = ["lint"] + proj_arg
141 result = sb.invoke_apio_cmd(apio, args)
142 sb.assert_result_ok(result)
143 assert "SUCCESS" in result.output
144 assert getsize(sb.proj_dir / "_build/default/hardware.vlt")
146 # -- 'apio format'
147 args = ["format"] + proj_arg
148 result = sb.invoke_apio_cmd(apio, args)
149 sb.assert_result_ok(result)
151 # -- 'apio format <testbench-file>'
152 # -- This tests the project relative specification even when
153 # -- the option --project-dir is used.
154 args = ["format", testbench_file] + proj_arg
155 result = sb.invoke_apio_cmd(apio, args)
156 sb.assert_result_ok(result)
158 # -- 'apio test'
159 args = ["test"] + proj_arg
160 result = sb.invoke_apio_cmd(apio, args)
161 sb.assert_result_ok(result)
162 assert "SUCCESS" in result.output
163 assert getsize(sb.proj_dir / f"_build/default/{testbench}.out")
164 assert getsize(sb.proj_dir / f"_build/default/{testbench}.vcd")
165 # -- For issue https://github.com/FPGAwars/apio/issues/557
166 assert "warning: Timing checks are not supported" not in result.output
168 # -- 'apio clean'
169 args = ["clean"] + proj_arg
170 result = sb.invoke_apio_cmd(apio, args)
171 sb.assert_result_ok(result)
172 assert "Cleanup completed" in result.output
173 assert not (sb.proj_dir / f"_build/default/{testbench}.out").exists()
174 assert not (sb.proj_dir / f"_build/default/{testbench}.vcd").exists()
176 # -- 'apio sim --no-gtkwave'
177 args = ["sim", "--no-gtkwave"] + proj_arg
178 result = sb.invoke_apio_cmd(apio, args)
179 sb.assert_result_ok(result)
180 assert "SUCCESS" in result.output
181 assert getsize(sb.proj_dir / f"_build/default/{testbench}.out")
182 assert getsize(sb.proj_dir / f"_build/default/{testbench}.vcd")
183 # -- For issue https://github.com/FPGAwars/apio/issues/557
184 assert "warning: Timing checks are not supported" not in result.output
186 # -- 'apio clean'
187 args = ["clean"] + proj_arg
188 result = sb.invoke_apio_cmd(apio, args)
189 sb.assert_result_ok(result)
190 assert "Cleanup completed" in result.output
191 assert not (sb.proj_dir / f"_build/default/{testbench}.out").exists()
192 assert not (sb.proj_dir / f"_build/default/{testbench}.vcd").exists()
194 # -- 'apio test <testbench-file>'
195 args = ["test", testbench_file] + proj_arg
196 result = sb.invoke_apio_cmd(apio, args)
197 sb.assert_result_ok(result)
198 assert "SUCCESS" in result.output
199 assert getsize(sb.proj_dir / f"_build/default/{testbench}.out")
200 assert getsize(sb.proj_dir / f"_build/default/{testbench}.vcd")
201 # -- For issue https://github.com/FPGAwars/apio/issues/557
202 assert "warning: Timing checks are not supported" not in result.output
204 # -- 'apio sim --no-gtkw <testbench-file>'
205 args = ["sim", "--no-gtkwave", testbench_file] + proj_arg
206 result = sb.invoke_apio_cmd(apio, args)
207 sb.assert_result_ok(result)
208 assert "SUCCESS" in result.output
209 assert getsize(sb.proj_dir / f"_build/default/{testbench}.out")
210 assert getsize(sb.proj_dir / f"_build/default/{testbench}.vcd")
211 # -- For issue https://github.com/FPGAwars/apio/issues/557
212 assert "warning: Timing checks are not supported" not in result.output
214 # -- 'apio clean'
215 args = ["clean"] + proj_arg
216 result = sb.invoke_apio_cmd(apio, args)
217 sb.assert_result_ok(result)
218 assert "Cleanup completed" in result.output
219 assert not (sb.proj_dir / f"_build/default/{testbench}.out").exists()
220 assert not (sb.proj_dir / f"_build/default/{testbench}.vcd").exists()
222 # -- 'apio report'
223 args = ["report"] + proj_arg
224 result = sb.invoke_apio_cmd(apio, args)
225 sb.assert_result_ok(result)
226 assert "SUCCESS" in result.output
227 assert report_item in result.output
228 assert "─────┐" in result.output # Graphical table border
229 assert getsize(sb.proj_dir / "_build/default/hardware.pnr")
231 # -- 'apio graph -n'
232 args = ["graph", "-n"] + proj_arg
233 result = sb.invoke_apio_cmd(apio, args)
234 sb.assert_result_ok(result)
235 assert "SUCCESS" in result.output
236 assert getsize(sb.proj_dir / "_build/default/graph.dot")
237 assert getsize(sb.proj_dir / "_build/default/graph.svg")
239 # -- 'apio clean'
240 assert Path(sb.proj_dir / "_build/default").exists()
241 args = ["clean"] + proj_arg
242 result = sb.invoke_apio_cmd(apio, args)
243 sb.assert_result_ok(result)
244 assert "Cleanup completed" in result.output
245 assert not Path(sb.proj_dir / "_build/default").exists()
247 # -- Here we should have only the original project files.
248 assert set(os.listdir(sb.proj_dir)) == set(project_files)
251# NOTE: We use the alhambra-ii/bcd-counter example because it uses
252# subdirectories. This increases the test coverage.
253def test_project_ice40_local_dir(apio_runner: ApioRunner):
254 """Tests building and testing an ice40 project as the current working
255 dir."""
256 _test_project(
257 apio_runner,
258 remote_proj_dir=False,
259 example="alhambra-ii/bcd-counter",
260 testbench_file="main_tb.v",
261 bitstream="hardware.bin",
262 report_item="ICESTORM_LC",
263 )
266def test_project_ice40_remote_dir(apio_runner: ApioRunner):
267 """Tests building and testing an ice40 project from a remote dir, using
268 the -p option."""
269 _test_project(
270 apio_runner,
271 remote_proj_dir=True,
272 example="alhambra-ii/bcd-counter",
273 testbench_file="main_tb.v",
274 bitstream="hardware.bin",
275 report_item="ICESTORM_LC",
276 )
279def test_project_ice40_system_verilog(apio_runner: ApioRunner):
280 """Tests building and testing an ice40 project that contains system
281 verilog files."""
282 _test_project(
283 apio_runner,
284 remote_proj_dir=False,
285 example="alhambra-ii/bcd-counter-sv",
286 testbench_file="main_tb.sv",
287 bitstream="hardware.bin",
288 report_item="ICESTORM_LC",
289 )
292def test_project_ecp5_local_dir(apio_runner: ApioRunner):
293 """Tests building and testing an ecp5 project as the current working dir"""
294 _test_project(
295 apio_runner,
296 remote_proj_dir=False,
297 example="colorlight-5a-75b-v8/ledon",
298 testbench_file="ledon_tb.v",
299 bitstream="hardware.bit",
300 report_item="ALU54B",
301 )
304def test_project_ecp5_remote_dir(apio_runner: ApioRunner):
305 """Tests building and testing an ecp5 project from a remote directory."""
306 _test_project(
307 apio_runner,
308 remote_proj_dir=True,
309 example="colorlight-5a-75b-v8/ledon",
310 testbench_file="ledon_tb.v",
311 bitstream="hardware.bit",
312 report_item="ALU54B",
313 )
316def test_project_ecp5_system_verilog(apio_runner: ApioRunner):
317 """Tests building and testing an ecp5 project that contains system
318 verilog files."""
319 _test_project(
320 apio_runner,
321 remote_proj_dir=False,
322 example="colorlight-5a-75b-v8/ledon-sv",
323 testbench_file="ledon_tb.sv",
324 bitstream="hardware.bit",
325 report_item="ALU54B",
326 )
329def test_project_gowin_local_dir(apio_runner: ApioRunner):
330 """Tests building and testing a gowin project as the current working dir"""
331 _test_project(
332 apio_runner,
333 remote_proj_dir=False,
334 example="sipeed-tang-nano-9k/blinky",
335 testbench_file="blinky_tb.v",
336 bitstream="hardware.fs",
337 report_item="LUT4",
338 )
341def test_project_gowin_remote_dir(apio_runner: ApioRunner):
342 """Tests building and testing a gowin project from a remote directory."""
343 _test_project(
344 apio_runner,
345 remote_proj_dir=True,
346 example="sipeed-tang-nano-9k/blinky",
347 testbench_file="blinky_tb.v",
348 bitstream="hardware.fs",
349 report_item="LUT4",
350 )
353def test_project_gowin_system_verilog(apio_runner: ApioRunner):
354 """Tests building and testing an gowin project that contains system
355 verilog files."""
356 _test_project(
357 apio_runner,
358 remote_proj_dir=False,
359 example="sipeed-tang-nano-9k/blinky-sv",
360 testbench_file="blinky_tb.sv",
361 bitstream="hardware.fs",
362 report_item="LUT4",
363 )