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

85 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-15 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, get_build_projects 

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, synthesis_run_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 self.projects = [ 

51 self.project_one, 

52 self.project_two, 

53 self.project_three, 

54 self.project_four, 

55 ] 

56 

57 return TestBuildProjectList() 

58 

59 

60def test_get_build_projects(build_project_list_test): 

61 assert get_build_projects( 

62 modules=build_project_list_test.modules, 

63 project_filters=[], 

64 include_netlist_not_full_builds=True, 

65 ) == [ 

66 build_project_list_test.project_three, 

67 build_project_list_test.project_four, 

68 ] 

69 assert get_build_projects( 

70 modules=build_project_list_test.modules, 

71 project_filters=[], 

72 include_netlist_not_full_builds=False, 

73 ) == [ 

74 build_project_list_test.project_one, 

75 build_project_list_test.project_two, 

76 ] 

77 

78 assert get_build_projects( 

79 modules=build_project_list_test.modules, 

80 project_filters=["thr*", "fou*"], 

81 include_netlist_not_full_builds=True, 

82 ) == [ 

83 build_project_list_test.project_three, 

84 build_project_list_test.project_four, 

85 ] 

86 

87 # Filter matches 'three' twice, but should only return once. 

88 assert get_build_projects( 

89 modules=build_project_list_test.modules, 

90 project_filters=["three", "*r*"], 

91 include_netlist_not_full_builds=True, 

92 ) == [ 

93 build_project_list_test.project_three, 

94 build_project_list_test.project_four, 

95 ] 

96 

97 

98def test_can_list_without_error(build_project_list_test): 

99 list_str = str(BuildProjectList(build_project_list_test.projects)) 

100 assert "one" in list_str 

101 assert "two" in list_str 

102 

103 

104def test_create(build_project_list_test, tmp_path): 

105 project_list = BuildProjectList(build_project_list_test.projects) 

106 assert project_list.create( 

107 projects_path=tmp_path / "projects_path", 

108 num_parallel_builds=2, 

109 ip_cache_path=tmp_path / "ip_cache_path", 

110 ) 

111 

112 for project in build_project_list_test.projects: 

113 project.create.assert_called_once_with( 

114 project_path=tmp_path / "projects_path" / project.name / "project", 

115 ip_cache_path=tmp_path / "ip_cache_path", 

116 ) 

117 

118 

119def test_create_unless_exists(build_project_list_test, tmp_path): 

120 project_list = BuildProjectList([build_project_list_test.project_one]) 

121 assert project_list.create_unless_exists( 

122 projects_path=tmp_path / "projects_path", 

123 num_parallel_builds=2, 

124 ip_cache_path=tmp_path / "ip_cache_path", 

125 ) 

126 

127 build_project_list_test.project_one.create.assert_called_once_with( 

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

129 ip_cache_path=tmp_path / "ip_cache_path", 

130 ) 

131 

132 # Create project file manually 

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

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

135 

136 assert project_list.create_unless_exists( 

137 projects_path=tmp_path / "projects_path", 

138 num_parallel_builds=2, 

139 ip_cache_path=tmp_path / "ip_cache_path", 

140 ) 

141 

142 # Still only called once after second create_unless_exists() 

143 build_project_list_test.project_one.create.assert_called_once() 

144 

145 

146def test_build(build_project_list_test, tmp_path): 

147 project_list = BuildProjectList([build_project_list_test.project_one]) 

148 assert project_list.build( 

149 projects_path=tmp_path / "projects_path", 

150 num_parallel_builds=2, 

151 num_threads_per_build=4, 

152 other_build_argument=True, 

153 ) 

154 

155 build_project_list_test.project_one.build.assert_called_once_with( 

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

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

158 num_threads=4, 

159 other_build_argument=True, 

160 ) 

161 

162 

163def test_build_fail_should_return_false(build_project_list_test, tmp_path): 

164 project_list = BuildProjectList([build_project_list_test.project_one]) 

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

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

167 

168 assert not project_list.build( 

169 projects_path=tmp_path / "projects_path", 

170 num_parallel_builds=2, 

171 num_threads_per_build=4, 

172 other_build_argument=True, 

173 ) 

174 

175 

176def test_build_with_output_path(build_project_list_test, tmp_path): 

177 project_list = BuildProjectList([build_project_list_test.project_one]) 

178 assert project_list.build( 

179 projects_path=tmp_path / "projects_path", 

180 num_parallel_builds=2, 

181 num_threads_per_build=4, 

182 output_path=tmp_path / "output_path", 

183 ) 

184 

185 build_project_list_test.project_one.build.assert_called_once_with( 

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

187 output_path=tmp_path / "output_path" / "one", 

188 num_threads=4, 

189 ) 

190 

191 

192def test_build_with_collect_artifacts(build_project_list_test, tmp_path): 

193 project_list = BuildProjectList([build_project_list_test.project_one]) 

194 collect_artifacts = MagicMock() 

195 assert project_list.build( 

196 projects_path=tmp_path / "projects_path", 

197 num_parallel_builds=2, 

198 num_threads_per_build=4, 

199 collect_artifacts=collect_artifacts, 

200 ) 

201 

202 collect_artifacts.assert_called_once_with( 

203 project=build_project_list_test.project_one, 

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

205 ) 

206 

207 

208def test_build_with_collect_artifacts_and_output_path(build_project_list_test, tmp_path): 

209 project_list = BuildProjectList([build_project_list_test.project_one]) 

210 collect_artifacts = MagicMock() 

211 assert project_list.build( 

212 projects_path=tmp_path / "projects_path", 

213 num_parallel_builds=2, 

214 num_threads_per_build=4, 

215 output_path=tmp_path / "output_path", 

216 collect_artifacts=collect_artifacts, 

217 ) 

218 

219 collect_artifacts.assert_called_once_with( 

220 project=build_project_list_test.project_one, 

221 output_path=tmp_path / "output_path" / "one", 

222 ) 

223 

224 

225def test_build_with_collect_artifacts_return_false_should_fail_build( 

226 build_project_list_test, tmp_path 

227): 

228 project_list = BuildProjectList([build_project_list_test.project_one]) 

229 collect_artifacts = MagicMock() 

230 collect_artifacts.return_value = False 

231 assert not project_list.build( 

232 projects_path=tmp_path / "projects_path", 

233 num_parallel_builds=2, 

234 num_threads_per_build=4, 

235 collect_artifacts=collect_artifacts, 

236 ) 

237 

238 

239def test_open(build_project_list_test, tmp_path): 

240 project_list = BuildProjectList( 

241 [build_project_list_test.project_three, build_project_list_test.project_four] 

242 ) 

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

244 

245 build_project_list_test.project_three.open.assert_called_once_with( 

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

247 ) 

248 build_project_list_test.project_four.open.assert_called_once_with( 

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

250 ) 

251 build_project_list_test.project_one.open.assert_not_called() 

252 build_project_list_test.project_two.open.assert_not_called()