Coverage for tsfpga/test/test_build_project_list.py: 100%
86 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-21 23:39 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-21 23:39 +0000
1# --------------------------------------------------------------------------------------------------
2# Copyright (c) Lukas Vik. All rights reserved.
3#
4# This file is part of the tsfpga project, a project platform for modern FPGA development.
5# https://tsfpga.com
6# https://github.com/tsfpga/tsfpga
7# --------------------------------------------------------------------------------------------------
9from unittest.mock import MagicMock
11import pytest
13from tsfpga.build_project_list import (
14 BuildProjectList,
15 get_build_projects,
16)
17from tsfpga.module import BaseModule
18from tsfpga.system_utils import create_directory
19from tsfpga.vivado.build_result import VivadoBuildResult
20from tsfpga.vivado.project import VivadoProject
23@pytest.fixture
24def build_project_list_test():
25 class TestBuildProjectList:
26 @staticmethod
27 def _get_mocks(name, is_netlist_build=False):
28 project = MagicMock(spec=VivadoProject)
29 project.name = name
30 project.__str__.return_value = f"MockProject {name}"
31 project.is_netlist_build = is_netlist_build
33 # Note that his has 'success' set to True by default.
34 project.build.return_value = VivadoBuildResult(name=name, synthesis_run_name="")
36 module = MagicMock(spec=BaseModule)
37 module.name = name
38 module.get_build_projects.return_value = [project]
40 return module, project
42 def __init__(self):
43 self.module_one, self.project_one = self._get_mocks(name="one")
44 self.module_two, self.project_two = self._get_mocks(name="two")
46 self.module_three, self.project_three = self._get_mocks(
47 name="three", is_netlist_build=True
48 )
49 self.module_four, self.project_four = self._get_mocks(
50 name="four", is_netlist_build=True
51 )
53 self.modules = [self.module_one, self.module_two, self.module_three, self.module_four]
54 self.projects = [
55 self.project_one,
56 self.project_two,
57 self.project_three,
58 self.project_four,
59 ]
61 return TestBuildProjectList()
64def test_get_build_projects(build_project_list_test):
65 assert get_build_projects(
66 modules=build_project_list_test.modules,
67 project_filters=[],
68 include_netlist_not_full_builds=True,
69 ) == [
70 build_project_list_test.project_three,
71 build_project_list_test.project_four,
72 ]
73 assert get_build_projects(
74 modules=build_project_list_test.modules,
75 project_filters=[],
76 include_netlist_not_full_builds=False,
77 ) == [
78 build_project_list_test.project_one,
79 build_project_list_test.project_two,
80 ]
82 assert get_build_projects(
83 modules=build_project_list_test.modules,
84 project_filters=["thr*", "fou*"],
85 include_netlist_not_full_builds=True,
86 ) == [
87 build_project_list_test.project_three,
88 build_project_list_test.project_four,
89 ]
91 # Filter matches 'three' twice, but should only return once.
92 assert get_build_projects(
93 modules=build_project_list_test.modules,
94 project_filters=["three", "*r*"],
95 include_netlist_not_full_builds=True,
96 ) == [
97 build_project_list_test.project_three,
98 build_project_list_test.project_four,
99 ]
102def test_can_list_without_error(build_project_list_test):
103 list_str = str(BuildProjectList(build_project_list_test.projects))
104 assert "one" in list_str
105 assert "two" in list_str
108def test_create(build_project_list_test, tmp_path):
109 project_list = BuildProjectList(build_project_list_test.projects)
110 assert project_list.create(
111 projects_path=tmp_path / "projects_path",
112 num_parallel_builds=2,
113 ip_cache_path=tmp_path / "ip_cache_path",
114 )
116 for project in build_project_list_test.projects:
117 project.create.assert_called_once_with(
118 project_path=tmp_path / "projects_path" / project.name / "project",
119 ip_cache_path=tmp_path / "ip_cache_path",
120 )
123def test_create_unless_exists(build_project_list_test, tmp_path):
124 project_list = BuildProjectList([build_project_list_test.project_one])
125 assert project_list.create_unless_exists(
126 projects_path=tmp_path / "projects_path",
127 num_parallel_builds=2,
128 ip_cache_path=tmp_path / "ip_cache_path",
129 )
131 build_project_list_test.project_one.create.assert_called_once_with(
132 project_path=tmp_path / "projects_path" / "one" / "project",
133 ip_cache_path=tmp_path / "ip_cache_path",
134 )
136 # Create project file manually
137 create_directory(tmp_path / "projects_path" / "one" / "project")
138 (tmp_path / "projects_path" / "one" / "project" / "one.xpr").write_text("")
140 assert project_list.create_unless_exists(
141 projects_path=tmp_path / "projects_path",
142 num_parallel_builds=2,
143 ip_cache_path=tmp_path / "ip_cache_path",
144 )
146 # Still only called once after second create_unless_exists()
147 build_project_list_test.project_one.create.assert_called_once()
150def test_build(build_project_list_test, tmp_path):
151 project_list = BuildProjectList([build_project_list_test.project_one])
152 assert project_list.build(
153 projects_path=tmp_path / "projects_path",
154 num_parallel_builds=2,
155 num_threads_per_build=4,
156 other_build_argument=True,
157 )
159 build_project_list_test.project_one.build.assert_called_once_with(
160 project_path=tmp_path / "projects_path" / "one" / "project",
161 output_path=tmp_path / "projects_path" / "one",
162 num_threads=4,
163 other_build_argument=True,
164 )
167def test_build_fail_should_return_false(build_project_list_test, tmp_path):
168 project_list = BuildProjectList([build_project_list_test.project_one])
169 build_project_list_test.project_one.build.return_value = MagicMock(spec=VivadoBuildResult)
170 build_project_list_test.project_one.build.return_value.success = False
172 assert not project_list.build(
173 projects_path=tmp_path / "projects_path",
174 num_parallel_builds=2,
175 num_threads_per_build=4,
176 other_build_argument=True,
177 )
180def test_build_with_output_path(build_project_list_test, tmp_path):
181 project_list = BuildProjectList([build_project_list_test.project_one])
182 assert project_list.build(
183 projects_path=tmp_path / "projects_path",
184 num_parallel_builds=2,
185 num_threads_per_build=4,
186 output_path=tmp_path / "output_path",
187 )
189 build_project_list_test.project_one.build.assert_called_once_with(
190 project_path=tmp_path / "projects_path" / "one" / "project",
191 output_path=tmp_path / "output_path" / "one",
192 num_threads=4,
193 )
196def test_build_with_collect_artifacts(build_project_list_test, tmp_path):
197 project_list = BuildProjectList([build_project_list_test.project_one])
198 collect_artifacts = MagicMock()
199 assert project_list.build(
200 projects_path=tmp_path / "projects_path",
201 num_parallel_builds=2,
202 num_threads_per_build=4,
203 collect_artifacts=collect_artifacts,
204 )
206 collect_artifacts.assert_called_once_with(
207 project=build_project_list_test.project_one,
208 output_path=tmp_path / "projects_path" / "one",
209 )
212def test_build_with_collect_artifacts_and_output_path(build_project_list_test, tmp_path):
213 project_list = BuildProjectList([build_project_list_test.project_one])
214 collect_artifacts = MagicMock()
215 assert project_list.build(
216 projects_path=tmp_path / "projects_path",
217 num_parallel_builds=2,
218 num_threads_per_build=4,
219 output_path=tmp_path / "output_path",
220 collect_artifacts=collect_artifacts,
221 )
223 collect_artifacts.assert_called_once_with(
224 project=build_project_list_test.project_one,
225 output_path=tmp_path / "output_path" / "one",
226 )
229def test_build_with_collect_artifacts_return_false_should_fail_build(
230 build_project_list_test, tmp_path
231):
232 project_list = BuildProjectList([build_project_list_test.project_one])
233 collect_artifacts = MagicMock()
234 collect_artifacts.return_value = False
235 assert not project_list.build(
236 projects_path=tmp_path / "projects_path",
237 num_parallel_builds=2,
238 num_threads_per_build=4,
239 collect_artifacts=collect_artifacts,
240 )
243def test_open(build_project_list_test, tmp_path):
244 project_list = BuildProjectList(
245 [build_project_list_test.project_three, build_project_list_test.project_four]
246 )
247 assert project_list.open(projects_path=tmp_path / "projects_path")
249 build_project_list_test.project_three.open.assert_called_once_with(
250 project_path=tmp_path / "projects_path" / "three" / "project"
251 )
252 build_project_list_test.project_four.open.assert_called_once_with(
253 project_path=tmp_path / "projects_path" / "four" / "project"
254 )
255 build_project_list_test.project_one.open.assert_not_called()
256 build_project_list_test.project_two.open.assert_not_called()