Coverage for tsfpga/test/test_build_project_list.py: 100%

102 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-08-29 20:51 +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 BuildProjectList 

14from tsfpga.module import BaseModule 

15from tsfpga.system_utils import create_directory 

16from tsfpga.vivado.project import BuildResult, VivadoProject 

17 

18 

19@pytest.fixture 

20def build_project_list_test(): 

21 class TestBuildProjectList: 

22 @staticmethod 

23 def _get_mocks(name, is_netlist_build=False): 

24 project = MagicMock(spec=VivadoProject) 

25 project.name = name 

26 project.__str__.return_value = f"MockProject {name}" 

27 project.is_netlist_build = is_netlist_build 

28 

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

30 project.build.return_value = BuildResult(name=name) 

31 

32 module = MagicMock(spec=BaseModule) 

33 module.name = name 

34 module.get_build_projects.return_value = [project] 

35 

36 return module, project 

37 

38 def __init__(self): 

39 self.module_one, self.project_one = self._get_mocks(name="one") 

40 self.module_two, self.project_two = self._get_mocks(name="two") 

41 

42 self.module_three, self.project_three = self._get_mocks( 

43 name="three", is_netlist_build=True 

44 ) 

45 self.module_four, self.project_four = self._get_mocks( 

46 name="four", is_netlist_build=True 

47 ) 

48 

49 self.modules = [self.module_one, self.module_two, self.module_three, self.module_four] 

50 

51 return TestBuildProjectList() 

52 

53 

54def test_can_list_without_error(build_project_list_test): 

55 list_str = str(BuildProjectList(build_project_list_test.modules, project_filters=[])) 

56 assert "one" in list_str 

57 assert "two" in list_str 

58 

59 

60def test_project_filtering(build_project_list_test): 

61 project_list = BuildProjectList(build_project_list_test.modules, project_filters=[]) 

62 assert len(project_list.projects) == 2 

63 assert build_project_list_test.project_one in project_list.projects 

64 assert build_project_list_test.project_two in project_list.projects 

65 

66 project_list = BuildProjectList( 

67 build_project_list_test.modules, project_filters=["apa", "*ne", "three", "four"] 

68 ) 

69 assert len(project_list.projects) == 1 

70 assert build_project_list_test.project_one in project_list.projects 

71 

72 project_list = BuildProjectList( 

73 build_project_list_test.modules, project_filters=["one", "one", "on*"] 

74 ) 

75 assert len(project_list.projects) == 1 

76 assert build_project_list_test.project_one in project_list.projects 

77 

78 project_list = BuildProjectList( 

79 build_project_list_test.modules, project_filters=[], include_netlist_not_top_builds=True 

80 ) 

81 assert len(project_list.projects) == 2 

82 assert build_project_list_test.project_three in project_list.projects 

83 assert build_project_list_test.project_four in project_list.projects 

84 

85 project_list = BuildProjectList( 

86 build_project_list_test.modules, 

87 include_netlist_not_top_builds=True, 

88 project_filters=["apa", "one", "two", "thr*"], 

89 ) 

90 assert len(project_list.projects) == 1 

91 assert build_project_list_test.project_three in project_list.projects 

92 

93 

94def test_create(build_project_list_test, tmp_path): 

95 project_list = BuildProjectList(build_project_list_test.modules, project_filters=["one", "two"]) 

96 assert project_list.create( 

97 projects_path=tmp_path / "projects_path", 

98 num_parallel_builds=2, 

99 ip_cache_path=tmp_path / "ip_cache_path", 

100 ) 

101 

102 build_project_list_test.project_one.create.assert_called_once_with( 

103 project_path=tmp_path / "projects_path" / "one" / "project", 

104 ip_cache_path=tmp_path / "ip_cache_path", 

105 ) 

106 

107 build_project_list_test.project_two.create.assert_called_once_with( 

108 project_path=tmp_path / "projects_path" / "two" / "project", 

109 ip_cache_path=tmp_path / "ip_cache_path", 

110 ) 

111 

112 build_project_list_test.project_three.create.assert_not_called() 

113 build_project_list_test.project_four.create.assert_not_called() 

114 

115 

116def test_create_unless_exists(build_project_list_test, tmp_path): 

117 project_list = BuildProjectList(build_project_list_test.modules, project_filters=["one"]) 

118 assert project_list.create_unless_exists( 

119 projects_path=tmp_path / "projects_path", 

120 num_parallel_builds=2, 

121 ip_cache_path=tmp_path / "ip_cache_path", 

122 ) 

123 

124 build_project_list_test.project_one.create.assert_called_once_with( 

125 project_path=tmp_path / "projects_path" / "one" / "project", 

126 ip_cache_path=tmp_path / "ip_cache_path", 

127 ) 

128 

129 # Create project file manually 

130 create_directory(tmp_path / "projects_path" / "one" / "project") 

131 (tmp_path / "projects_path" / "one" / "project" / "one.xpr").write_text("") 

132 

133 assert project_list.create_unless_exists( 

134 projects_path=tmp_path / "projects_path", 

135 num_parallel_builds=2, 

136 ip_cache_path=tmp_path / "ip_cache_path", 

137 ) 

138 

139 # Still only called once after second create_unless_exists() 

140 build_project_list_test.project_one.create.assert_called_once() 

141 

142 build_project_list_test.project_two.create.assert_not_called() 

143 build_project_list_test.project_three.create.assert_not_called() 

144 build_project_list_test.project_four.create.assert_not_called() 

145 

146 

147def test_build(build_project_list_test, tmp_path): 

148 project_list = BuildProjectList(build_project_list_test.modules, project_filters=["one"]) 

149 assert project_list.build( 

150 projects_path=tmp_path / "projects_path", 

151 num_parallel_builds=2, 

152 num_threads_per_build=4, 

153 other_build_argument=True, 

154 ) 

155 

156 build_project_list_test.project_one.build.assert_called_once_with( 

157 project_path=tmp_path / "projects_path" / "one" / "project", 

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

159 num_threads=4, 

160 other_build_argument=True, 

161 ) 

162 

163 

164def test_build_fail_should_return_false(build_project_list_test, tmp_path): 

165 project_list = BuildProjectList(build_project_list_test.modules, project_filters=["one"]) 

166 build_project_list_test.project_one.build.return_value = MagicMock(spec=BuildResult) 

167 build_project_list_test.project_one.build.return_value.success = False 

168 

169 assert not project_list.build( 

170 projects_path=tmp_path / "projects_path", 

171 num_parallel_builds=2, 

172 num_threads_per_build=4, 

173 other_build_argument=True, 

174 ) 

175 

176 

177def test_build_with_output_path(build_project_list_test, tmp_path): 

178 project_list = BuildProjectList(build_project_list_test.modules, project_filters=["one"]) 

179 assert project_list.build( 

180 projects_path=tmp_path / "projects_path", 

181 num_parallel_builds=2, 

182 num_threads_per_build=4, 

183 output_path=tmp_path / "output_path", 

184 ) 

185 

186 build_project_list_test.project_one.build.assert_called_once_with( 

187 project_path=tmp_path / "projects_path" / "one" / "project", 

188 output_path=tmp_path / "output_path" / "one", 

189 num_threads=4, 

190 ) 

191 

192 

193def test_build_with_collect_artifacts(build_project_list_test, tmp_path): 

194 project_list = BuildProjectList(build_project_list_test.modules, project_filters=["one"]) 

195 collect_artifacts = MagicMock() 

196 assert project_list.build( 

197 projects_path=tmp_path / "projects_path", 

198 num_parallel_builds=2, 

199 num_threads_per_build=4, 

200 collect_artifacts=collect_artifacts, 

201 ) 

202 

203 collect_artifacts.assert_called_once_with( 

204 project=build_project_list_test.project_one, 

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

206 ) 

207 

208 

209def test_build_with_collect_artifacts_and_output_path(build_project_list_test, tmp_path): 

210 project_list = BuildProjectList(build_project_list_test.modules, project_filters=["one"]) 

211 collect_artifacts = MagicMock() 

212 assert project_list.build( 

213 projects_path=tmp_path / "projects_path", 

214 num_parallel_builds=2, 

215 num_threads_per_build=4, 

216 output_path=tmp_path / "output_path", 

217 collect_artifacts=collect_artifacts, 

218 ) 

219 

220 collect_artifacts.assert_called_once_with( 

221 project=build_project_list_test.project_one, 

222 output_path=tmp_path / "output_path" / "one", 

223 ) 

224 

225 

226def test_build_with_collect_artifacts_return_false_should_fail_build( 

227 build_project_list_test, tmp_path 

228): 

229 project_list = BuildProjectList(build_project_list_test.modules, project_filters=["one"]) 

230 collect_artifacts = MagicMock() 

231 collect_artifacts.return_value = False 

232 assert not project_list.build( 

233 projects_path=tmp_path / "projects_path", 

234 num_parallel_builds=2, 

235 num_threads_per_build=4, 

236 collect_artifacts=collect_artifacts, 

237 ) 

238 

239 

240def test_open(build_project_list_test, tmp_path): 

241 project_list = BuildProjectList( 

242 build_project_list_test.modules, project_filters=[], include_netlist_not_top_builds=True 

243 ) 

244 assert project_list.open(projects_path=tmp_path / "projects_path") 

245 

246 build_project_list_test.project_three.open.assert_called_once_with( 

247 project_path=tmp_path / "projects_path" / "three" / "project" 

248 ) 

249 build_project_list_test.project_four.open.assert_called_once_with( 

250 project_path=tmp_path / "projects_path" / "four" / "project" 

251 ) 

252 build_project_list_test.project_one.open.assert_not_called() 

253 build_project_list_test.project_two.open.assert_not_called()