Coverage for tests/unit_tests/test_resources.py: 98%
82 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"""
2Tests of apio_context.py
3"""
5import re
6from tests.conftest import ApioRunner
7from apio.apio_context import (
8 ApioContext,
9 PackagesPolicy,
10 ProjectPolicy,
11 RemoteConfigPolicy,
12)
13from apio.utils.resource_util import (
14 validate_config,
15 validate_packages,
16 validate_platforms,
17 _validate_board_info,
18 _validate_fpga_info,
19 _validate_programmer_info,
20 collect_project_resources,
21 validate_project_resources,
22)
25def lc_part_num(part_num: str) -> str:
26 """Convert an fpga part number to a lower-case id."""
27 return part_num.lower().replace("/", "-")
30def test_resources_references(apio_runner: ApioRunner):
31 """Tests the consistency of the board references to fpgas and
32 programmers."""
34 with apio_runner.in_sandbox():
36 # -- Create an apio context so we can access the resources.
37 apio_ctx = ApioContext(
38 project_policy=ProjectPolicy.NO_PROJECT,
39 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
40 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
41 )
43 unused_programmers = set(apio_ctx.programmers.keys())
45 for board_id, board_info in apio_ctx.boards.items():
46 # -- Prepare a context message for failing assertions.
47 board_msg = f"While testing board {board_id}"
49 # -- Assert that required fields exist.
50 assert "fpga-id" in board_info, board_msg
51 assert "programmer" in board_info, board_msg
52 assert "id" in board_info["programmer"], board_msg
54 # -- Check that the fpga exists.
55 board_fpga_id = board_info["fpga-id"]
56 assert apio_ctx.fpgas[board_fpga_id], board_msg
58 # -- Check that the programmer exists.
59 board_programmer_id = board_info["programmer"]["id"]
60 assert apio_ctx.programmers[board_programmer_id], board_msg
62 # -- Track unused programmers. Since a programmer may be used
63 # -- by more than one board, it may already be removed.
64 if board_programmer_id in unused_programmers:
65 unused_programmers.remove(board_programmer_id)
67 # -- We should end up with an empty set of unused programmers.
68 assert not unused_programmers, unused_programmers
71def test_resources_ids_and_order(apio_runner: ApioRunner):
72 """Tests the formats of boards, fpgas, and programmers names."""
74 # -- For boards we allow lower-case-0-9.
75 board_id_regex = re.compile(r"^[a-z][a-z0-9-]*$")
77 # -- For fpga ids we allow lower-case-0-9.
78 fpga_id_regex = re.compile(r"^[a-z][a-z0-9-/]*$")
80 # -- For programmer ids we allow lower-case-0-9.
81 programmer_id_regex = re.compile(r"^[a-z][a-z0-9-]*$")
83 with apio_runner.in_sandbox():
85 # -- Create an apio context so we can access the resources.
86 apio_ctx = ApioContext(
87 project_policy=ProjectPolicy.NO_PROJECT,
88 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
89 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
90 )
92 # -- Test the format of the board ids.
93 for board_id in apio_ctx.boards.keys():
94 assert board_id_regex.match(board_id), f"{board_id=}"
96 # -- Test the format of the fpgas ids and part numbers.
97 for fpga_id, fgpa_info in apio_ctx.fpgas.items():
98 assert fpga_id_regex.match(fpga_id), f"{fpga_id=}"
99 # Fpga id is either the fpga part num converted to lower-case
100 # or its the lower-case part num with a suffix that starts with
101 # '-'. E.g, for part num 'PART-NUM', the fpga id can be 'part-num'
102 # or 'part-num-somethings'
103 lc_part = lc_part_num(fgpa_info["part-num"])
104 assert fpga_id == lc_part or fpga_id.startswith(
105 lc_part + "-"
106 ), f"{fpga_id=}"
108 # -- Test the format of the programmers ids.
109 for programmer_id in apio_ctx.programmers.keys():
110 assert programmer_id_regex.match(
111 programmer_id
112 ), f"{programmer_id=}"
115def test_resources_are_valid(apio_runner: ApioRunner):
116 """Validate resources against a schema."""
117 with apio_runner.in_sandbox():
119 apio_ctx = ApioContext(
120 project_policy=ProjectPolicy.NO_PROJECT,
121 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
122 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
123 )
125 validate_config(apio_ctx.config)
126 validate_packages(apio_ctx.all_packages)
127 validate_platforms(apio_ctx.platforms)
129 for fpga_id, fpga_info in apio_ctx.fpgas.items():
130 _validate_fpga_info(fpga_id, fpga_info)
132 for programmer_id, programmer_info in apio_ctx.programmers.items():
133 _validate_programmer_info(programmer_id, programmer_info)
135 for board_id, board_info in apio_ctx.boards.items():
136 _validate_board_info(board_id, board_info)
138 # -- Collect project resources for this board. This tests that
139 # -- the references are ok.
140 project_resources = collect_project_resources(
141 board_id, apio_ctx.boards, apio_ctx.fpgas, apio_ctx.programmers
142 )
144 # -- Validate the project resources.
145 validate_project_resources(project_resources)
148def test_fpga_definitions(apio_runner: ApioRunner):
149 """Tests the fields of the fpga definitions."""
151 with apio_runner.in_sandbox():
153 # -- Create an apio context so we can access the resources.
154 apio_ctx = ApioContext(
155 project_policy=ProjectPolicy.NO_PROJECT,
156 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
157 packages_policy=PackagesPolicy.ENSURE_PACKAGES,
158 )
160 for fpga_id, fpga_info in apio_ctx.fpgas.items():
162 context = f"In fpga definition {fpga_id}"
164 # -- Verify the "arch" field.
165 assert "arch" in fpga_info, context
166 arch = fpga_info["arch"]
168 # -- Ice40
169 if arch == "ice40":
170 assert fpga_info.keys() == {
171 "part-num",
172 "arch",
173 "size",
174 "type",
175 "pack",
176 }, context
177 assert fpga_info["part-num"], context
178 assert fpga_info["arch"], context
179 assert fpga_info["size"], context
180 assert fpga_info["type"], context
181 assert fpga_info["pack"], context
182 continue
184 # -- Ecp5
185 if arch == "ecp5":
186 assert set(fpga_info.keys()) == {
187 "part-num",
188 "arch",
189 "size",
190 "type",
191 "pack",
192 "speed",
193 }, context
194 assert fpga_info["part-num"], context
195 assert fpga_info["arch"], context
196 assert fpga_info["size"], context
197 assert fpga_info["type"], context
198 assert fpga_info["pack"], context
199 assert fpga_info["speed"], context
200 continue
202 # -- Gowin
203 if arch == "gowin": 203 ↛ 217line 203 didn't jump to line 217 because the condition on line 203 was always true
204 assert fpga_info.keys() == {
205 "part-num",
206 "arch",
207 "size",
208 "type",
209 }, context
210 assert fpga_info["part-num"], context
211 assert fpga_info["arch"], context
212 assert fpga_info["size"], context
213 assert fpga_info["type"], context
214 continue
216 # -- Unknown arch
217 raise ValueError(f"Unknown arch value: {arch}")