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

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# -------------------------------------------------------------------------------------------------- 

8 

9from unittest.mock import MagicMock 

10 

11import pytest 

12 

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 

21 

22 

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 

32 

33 # Note that his has 'success' set to True by default. 

34 project.build.return_value = VivadoBuildResult(name=name, synthesis_run_name="") 

35 

36 module = MagicMock(spec=BaseModule) 

37 module.name = name 

38 module.get_build_projects.return_value = [project] 

39 

40 return module, project 

41 

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") 

45 

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 ) 

52 

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 ] 

60 

61 return TestBuildProjectList() 

62 

63 

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 ] 

81 

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 ] 

90 

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 ] 

100 

101 

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 

106 

107 

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 ) 

115 

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 ) 

121 

122 

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 ) 

130 

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 ) 

135 

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("") 

139 

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 ) 

145 

146 # Still only called once after second create_unless_exists() 

147 build_project_list_test.project_one.create.assert_called_once() 

148 

149 

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 ) 

158 

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 ) 

165 

166 

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 

171 

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 ) 

178 

179 

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 ) 

188 

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 ) 

194 

195 

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 ) 

205 

206 collect_artifacts.assert_called_once_with( 

207 project=build_project_list_test.project_one, 

208 output_path=tmp_path / "projects_path" / "one", 

209 ) 

210 

211 

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 ) 

222 

223 collect_artifacts.assert_called_once_with( 

224 project=build_project_list_test.project_one, 

225 output_path=tmp_path / "output_path" / "one", 

226 ) 

227 

228 

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 ) 

241 

242 

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") 

248 

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()