Coverage for tests/unit_tests/common/test_build_report.py: 100%
43 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 01:55 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 01:55 +0000
1"""Test for build_report.py."""
3from pathlib import Path
4import pytest
5from pytest import LogCaptureFixture
6from tests.conftest import ApioRunner
7from apio.common.apio_console import cunstyle
8from apio.common.build_report import (
9 ResourceReport,
10 ClockReport,
11 BuildReport,
12 read_build_report,
13)
15# -- Test json for ECP5. Having 'TRELLIS' in a utilization key indicates
16# -- that's it's ECP5.
17ECP5_TEST_SUMMARY = """
18{
19 "other-stuff": {
20 "bla-bla": "bla-bla"
21 },
22 "utilization": {
23 "TRELLIS_ECLKBUF": {
24 "available": 8,
25 "used": 0
26 },
27 "SIOLOGIC": {
28 "available": 69,
29 "used": 0
30 },
31 "TRELLIS_COMB": {
32 "available": 24288,
33 "used": 12144
34 }
35 },
36 "fmax": {
37 "$glbnet$MY_CLK$TRELLIS_IO_IN": {
38 "achieved": 295.420,
39 "constraint": 12
40 }
41 }
42}
43"""
46# -- Test json for non ECP5. The utilization keys do not contain "TRELLIS".
47# -- Note that the user clock name "CLK" is at different part of the fmax keys
48# -- compared to ECP5 above.
49NON_ECP5_TEST_SUMMARY = """
50{
51 "other-stuff": {
52 "bla-bla": "bla-bla"
53 },
54 "utilization": {
55 "ICESTORM_PLL": {
56 "available": 2,
57 "used": 0
58 },
59 "ICESTORM_LC": {
60 "available": 7680,
61 "used": 27
62 }
63 },
64 "fmax": {
65 "MY_CLK$SB_IO_IN_$glb_clk": {
66 "achieved": 194.363,
67 "constraint": 12
68 }
69 }
70}
71"""
74def test_ecp5_read_build_report(apio_runner):
75 """Tests the read_build_report() function for ECP 5 hardware.pnr."""
77 with apio_runner.in_sandbox() as sb:
79 file_path = Path("_build/default/hardware.pnr")
81 sb.write_file(file_path, ECP5_TEST_SUMMARY)
83 build_report = read_build_report(file_path)
84 assert isinstance(build_report, BuildReport)
86 print(build_report)
88 assert build_report == BuildReport(
89 resources=[
90 ResourceReport(
91 name="SIOLOGIC", available=69, used=0, percentage=0.0
92 ),
93 ResourceReport(
94 name="TRELLIS_COMB",
95 available=24288,
96 used=12144,
97 percentage=50.0,
98 ),
99 ResourceReport(
100 name="TRELLIS_ECLKBUF", available=8, used=0, percentage=0.0
101 ),
102 ],
103 clocks=[ClockReport(name="MY_CLK", fmax_mhz=295.420)],
104 )
107def test_non_ecp5_read_build_report(
108 apio_runner: ApioRunner, capsys: LogCaptureFixture
109):
110 """Tests the read_build_report() function for non ECP 5 hardware.pnr."""
112 with apio_runner.in_sandbox() as sb:
114 file_path = Path("_build/default/hardware.pnr")
116 sb.write_file(file_path, NON_ECP5_TEST_SUMMARY)
118 build_report = read_build_report(file_path)
119 assert isinstance(build_report, BuildReport)
121 print(build_report)
123 assert build_report == BuildReport(
124 resources=[
125 ResourceReport(
126 name="ICESTORM_LC",
127 available=7680,
128 used=27,
129 percentage=0.3515625,
130 ),
131 ResourceReport(
132 name="ICESTORM_PLL", available=2, used=0, percentage=0.0
133 ),
134 ],
135 clocks=[ClockReport(name="MY_CLK", fmax_mhz=194.363)],
136 )
139def test_hardware_pnr_reading_failure(
140 apio_runner: ApioRunner, capsys: LogCaptureFixture
141):
142 """Tests the case where reading hardware.pne fails."""
143 with apio_runner.in_sandbox():
144 file_path = Path("_build/default/hardware.pnr")
145 capsys.readouterr() # Reset capture
146 with pytest.raises(SystemExit) as e:
147 # -- Since we didn't create hardware.pnr, reading should fail.
148 read_build_report(file_path)
149 captured = capsys.readouterr()
150 assert e.value.code == 1
151 assert "Error: Failed to read" in cunstyle(captured.out)
154def test_hardware_pnr_parsing_failure(
155 apio_runner: ApioRunner, capsys: LogCaptureFixture
156):
157 """Tests the case where reading hardware.pne fails."""
158 with apio_runner.in_sandbox() as sb:
159 file_path = Path("_build/default/hardware.pnr")
160 sb.write_file(file_path, "Broken JSON file")
161 capsys.readouterr() # Reset capture
162 with pytest.raises(SystemExit) as e:
163 # -- Since we didn't create hardware.pnr, reading should fail.
164 read_build_report(file_path)
165 captured = capsys.readouterr()
166 assert e.value.code == 1
167 assert "Error: Failed parsing json file" in cunstyle(captured.out)