Coverage for tsfpga/test/unit/test_module.py: 99%

174 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-29 20:01 +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://gitlab.com/tsfpga/tsfpga 

7# -------------------------------------------------------------------------------------------------- 

8 

9# Standard libraries 

10from pathlib import Path 

11from unittest import TestCase 

12from unittest.mock import ANY, MagicMock, patch 

13 

14# Third party libraries 

15import pytest 

16 

17# First party libraries 

18from tsfpga.module import BaseModule, get_modules 

19from tsfpga.system_utils import create_directory, create_file 

20 

21 

22def test_add_vunit_config_name(): 

23 module = BaseModule(path=Path(), library_name="") 

24 

25 test = MagicMock() 

26 pre_config = MagicMock() 

27 post_check = MagicMock() 

28 

29 module.add_vunit_config(test=test, pre_config=pre_config, post_check=post_check) 

30 test.add_config.assert_called_once_with( 

31 name="", generics={}, pre_config=pre_config, post_check=post_check 

32 ) 

33 test.reset_mock() 

34 

35 module.add_vunit_config(test=test, name="apa") 

36 test.add_config.assert_called_once_with( 

37 name="apa", generics={}, pre_config=None, post_check=None 

38 ) 

39 test.reset_mock() 

40 

41 module.add_vunit_config(test=test, generics=dict(apa="hest", foo="bar")) 

42 test.add_config.assert_called_once_with( 

43 name="apa_hest.foo_bar", 

44 generics=dict(apa="hest", foo="bar"), 

45 pre_config=None, 

46 post_check=None, 

47 ) 

48 test.reset_mock() 

49 

50 module.add_vunit_config(test=test, name="zebra", generics=dict(apa="hest", foo="bar")) 

51 test.add_config.assert_called_once_with( 

52 name="zebra.apa_hest.foo_bar", 

53 generics=dict(apa="hest", foo="bar"), 

54 pre_config=None, 

55 post_check=None, 

56 ) 

57 

58 

59def test_add_vunit_config_random_seed(): 

60 module = BaseModule(path=Path(), library_name="") 

61 test = MagicMock() 

62 

63 # No seed at all 

64 module.add_vunit_config(test=test) 

65 assert "generics" not in test.add_config.call_args 

66 

67 module.add_vunit_config(test=test, set_random_seed=False) 

68 assert "generics" not in test.add_config.call_args 

69 

70 # No seed, with generics set 

71 module.add_vunit_config(test=test, generics={"apa": "whatever"}) 

72 assert "seed" not in test.add_config.call_args.kwargs["generics"] 

73 

74 # Static seed 

75 module.add_vunit_config(test=test, set_random_seed=0) 

76 assert isinstance(test.add_config.call_args.kwargs["generics"]["seed"], int) 

77 assert test.add_config.call_args.kwargs["generics"]["seed"] == 0 

78 

79 module.add_vunit_config(test=test, set_random_seed=123) 

80 assert isinstance(test.add_config.call_args.kwargs["generics"]["seed"], int) 

81 assert test.add_config.call_args.kwargs["generics"]["seed"] == 123 

82 

83 # Use random seed 

84 module.add_vunit_config(test=test, set_random_seed=True) 

85 assert isinstance(test.add_config.call_args.kwargs["generics"]["seed"], int) 

86 assert test.add_config.call_args.kwargs["generics"]["seed"] >= 0 

87 

88 # Setting explicit value should still work 

89 module.add_vunit_config(test=test, generics={"seed": 711}) 

90 assert test.add_config.call_args.kwargs["generics"]["seed"] == 711 

91 

92 # If a value is already set it will be overwritten 

93 module.add_vunit_config(test=test, generics={"seed": -5}, set_random_seed=True) 

94 assert test.add_config.call_args.kwargs["generics"]["seed"] != -5 

95 

96 

97def test_file_list_filtering(tmp_path): 

98 module_name = "zebra" 

99 path = tmp_path / module_name 

100 

101 create_directory(path / "folder_should_not_be_included") 

102 create_file(path / "should_not_be_included.apa") 

103 

104 synth_files = { 

105 create_file(path / "syn.v"), 

106 create_file(path / "rtl" / "syn.v"), 

107 create_file(path / "src" / "syn.vhd"), 

108 create_file(path / "hdl" / "rtl" / "syn.vhd"), 

109 create_file(path / "hdl" / "package" / "syn.vhd"), 

110 } 

111 

112 test_files = { 

113 create_file(path / "test" / "test.v"), 

114 create_file(path / "rtl" / "tb" / "test.vhd"), 

115 } 

116 

117 sim_files = {create_file(path / "sim" / "sim.vhd")} 

118 

119 my_module = BaseModule(path=path, library_name="zebra") 

120 

121 files = {file.path for file in my_module.get_synthesis_files()} 

122 assert files == synth_files 

123 

124 files = {file.path for file in my_module.get_simulation_files()} 

125 assert files == synth_files | test_files | sim_files 

126 

127 files = {file.path for file in my_module.get_simulation_files(include_tests=False)} 

128 assert files == synth_files | sim_files 

129 

130 files = {file.path for file in my_module.get_simulation_files(files_include=synth_files)} 

131 assert files == synth_files 

132 

133 files = {file.path for file in my_module.get_simulation_files(files_avoid=synth_files)} 

134 assert files == test_files | sim_files 

135 

136 

137def test_get_synthesis_files_calls_get_simulation_files_with_correct_arguments(): 

138 module = BaseModule(path=Path(), library_name="") 

139 with patch("tsfpga.module.BaseModule.get_synthesis_files") as get_synthesis_files: 

140 module.get_simulation_files(files_include=True, files_avoid=False, apa=123) 

141 get_synthesis_files.assert_called_once_with(files_include=True, files_avoid=False, apa=123) 

142 

143 

144def test_get_documentation_files(tmp_path): 

145 module_name = "zebra" 

146 path = tmp_path / module_name 

147 

148 synth_files = { 

149 create_file(path / "rtl" / "syn.v"), 

150 create_file(path / "src" / "syn.vhd"), 

151 } 

152 

153 # Test files 

154 create_file(path / "test" / "test.v") 

155 create_file(path / "rtl" / "tb" / "test.vhd") 

156 

157 sim_files = {create_file(path / "sim" / "sim.vhd")} 

158 

159 module = BaseModule(path=path, library_name="zebra") 

160 

161 # Should include everything except test files 

162 files = {file.path for file in module.get_documentation_files()} 

163 assert files == synth_files | sim_files 

164 

165 

166def test_scoped_constraints(tmp_path): 

167 module_path = tmp_path / "apa" 

168 create_file(module_path / "src" / "hest.vhd") 

169 create_file(module_path / "scoped_constraints" / "hest.tcl") 

170 

171 my_module = BaseModule(module_path, "apa") 

172 scoped_constraints = my_module.get_scoped_constraints() 

173 assert len(scoped_constraints) == 1 

174 assert scoped_constraints[0].ref == "hest" 

175 

176 

177def test_scoped_constraint_entity_not_existing_should_raise_error(tmp_path): 

178 module_path = tmp_path / "apa" 

179 create_file(module_path / "scoped_constraints" / "hest.tcl") 

180 

181 module = BaseModule(module_path, "apa") 

182 with pytest.raises(FileNotFoundError) as exception_info: 

183 module.get_scoped_constraints() 

184 assert str(exception_info.value).startswith("Could not find a matching entity file") 

185 

186 

187def test_can_cast_to_string_without_error(): 

188 str(BaseModule(Path("dummy"), "dummy")) 

189 

190 

191def test_test_case_name(): 

192 assert ( 

193 BaseModule.test_case_name(generics=dict(apa=3, hest_zebra="foo")) == "apa_3.hest_zebra_foo" 

194 ) 

195 assert ( 

196 BaseModule.test_case_name(name="foo", generics=dict(apa=3, hest_zebra="bar")) 

197 == "foo.apa_3.hest_zebra_bar" 

198 ) 

199 

200 

201def test_getting_registers_calls_registers_hook(tmp_path): 

202 with patch("tsfpga.module.from_toml", autospec=True) as from_toml, patch( 

203 "tsfpga.module.BaseModule.registers_hook", autospec=True 

204 ) as registers_hook: 

205 create_file(tmp_path / "a" / "regs_a.toml") 

206 module = BaseModule(path=tmp_path / "a", library_name="a") 

207 registers = module.registers 

208 

209 # TOML file exists so register creation from TOML should run 

210 from_toml.assert_called_once() 

211 registers_hook.assert_called_once() 

212 assert registers is not None 

213 

214 with patch("tsfpga.module.from_toml", autospec=True) as from_toml, patch( 

215 "tsfpga.module.BaseModule.registers_hook", autospec=True 

216 ) as registers_hook: 

217 module = BaseModule(path=tmp_path / "b", library_name="b") 

218 registers = module.registers 

219 

220 # TOML file does not exist, so register creation from TOML should not run 

221 from_toml.assert_not_called() 

222 # Register hook shall still run however 

223 registers_hook.assert_called_once() 

224 assert registers is None 

225 

226 

227@pytest.mark.usefixtures("fixture_tmp_path") 

228class TestGetModules(TestCase): 

229 

230 tmp_path = None 

231 

232 def setUp(self): 

233 create_directory(self.tmp_path / "a") 

234 create_directory(self.tmp_path / "b") 

235 create_directory(self.tmp_path / "c") 

236 

237 self.modules_folders = [self.tmp_path] 

238 

239 def test_name_filtering_include(self): 

240 modules = get_modules(self.modules_folders, names_include=["a", "b"]) 

241 assert set(module.name for module in modules) == set(["a", "b"]) 

242 

243 def test_name_filtering_avoid(self): 

244 modules = get_modules(self.modules_folders, names_avoid=["a", "b"]) 

245 assert set(module.name for module in modules) == set(["c"]) 

246 

247 def test_name_filtering_include_and_avoid(self): 

248 modules = get_modules( 

249 self.modules_folders, names_include=["a", "c"], names_avoid=["b", "c"] 

250 ) 

251 assert set(module.name for module in modules) == set(["a"]) 

252 

253 def test_library_name_does_not_have_lib_suffix(self): 

254 modules = get_modules(self.modules_folders) 

255 assert set(module.library_name for module in modules) == set(["a", "b", "c"]) 

256 

257 def test_library_name_has_lib_suffix(self): 

258 modules = get_modules(self.modules_folders, library_name_has_lib_suffix=True) 

259 assert set(module.library_name for module in modules) == set(["a_lib", "b_lib", "c_lib"]) 

260 

261 def test_stray_file_can_exist_in_modules_folder_without_error(self): 

262 create_file(self.tmp_path / "text_file.txt") 

263 modules = get_modules(self.modules_folders) 

264 assert len(modules) == 3 

265 

266 def test_local_override_of_module_type(self): 

267 module_file_content = """ 

268from tsfpga.module import BaseModule 

269 

270class Module(BaseModule): 

271 

272 def id(self): 

273 return """ 

274 

275 create_file(self.tmp_path / "a" / "module_a.py", module_file_content + '"a"') 

276 create_file(self.tmp_path / "b" / "module_b.py", module_file_content + '"b"') 

277 

278 modules = get_modules(self.modules_folders) 

279 

280 assert len(modules) == 3 

281 for module in modules: 

282 if module.name == "a": 

283 assert module.id() == "a" 

284 elif module.name == "b": 

285 assert module.id() == "b" 

286 elif module.name == "c": 

287 assert isinstance(module, BaseModule) 

288 else: 

289 assert False 

290 

291 @patch("tsfpga.module.from_toml", autospec=True) 

292 def test_register_object_creation_called_when_getting_synthesis_files(self, from_toml): 

293 toml_file = create_file(self.tmp_path / "a" / "regs_a.toml") 

294 

295 module = get_modules(self.modules_folders).get("a") 

296 module.get_synthesis_files() 

297 module.get_synthesis_files() 

298 

299 from_toml.assert_called_once_with("a", toml_file, ANY) 

300 

301 @patch("tsfpga.module.from_toml", autospec=True) 

302 def test_register_object_creation_called_when_getting_simulation_files(self, from_toml): 

303 toml_file = create_file(self.tmp_path / "a" / "regs_a.toml") 

304 

305 module = get_modules(self.modules_folders).get("a") 

306 module.get_simulation_files() 

307 module.get_simulation_files() 

308 

309 from_toml.assert_called_once_with("a", toml_file, ANY) 

310 

311 @patch("tsfpga.module.from_toml", autospec=True) 

312 def test_register_object_creation_called_once_when_getting_mixed_files(self, from_toml): 

313 toml_file = create_file(self.tmp_path / "a" / "regs_a.toml") 

314 

315 module = get_modules(self.modules_folders).get("a") 

316 module.get_synthesis_files() 

317 module.get_simulation_files() 

318 

319 from_toml.assert_called_once_with("a", toml_file, ANY)