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

164 statements  

« prev     ^ index     » next       coverage.py v6.4, created at 2022-05-28 04:01 +0000

1# -------------------------------------------------------------------------------------------------- 

2# Copyright (c) Lukas Vik. All rights reserved. 

3# 

4# This file is part of the tsfpga project. 

5# https://tsfpga.com 

6# https://gitlab.com/tsfpga/tsfpga 

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

8 

9from pathlib import Path 

10from unittest import TestCase 

11from unittest.mock import ANY, MagicMock, patch 

12 

13import pytest 

14 

15from tsfpga.module import BaseModule, get_modules 

16from tsfpga.system_utils import create_file, create_directory 

17 

18 

19def test_add_vunit_config_name(): 

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

21 

22 test = MagicMock() 

23 pre_config = MagicMock() 

24 post_check = MagicMock() 

25 

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

27 test.add_config.assert_called_once_with( 

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

29 ) 

30 test.reset_mock() 

31 

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

33 test.add_config.assert_called_once_with( 

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

35 ) 

36 test.reset_mock() 

37 

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

39 test.add_config.assert_called_once_with( 

40 name="apa_hest.foo_bar", 

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

42 pre_config=None, 

43 post_check=None, 

44 ) 

45 test.reset_mock() 

46 

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

48 test.add_config.assert_called_once_with( 

49 name="zebra.apa_hest.foo_bar", 

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

51 pre_config=None, 

52 post_check=None, 

53 ) 

54 

55 

56def test_add_vunit_config_random_seed(): 

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

58 test = MagicMock() 

59 

60 # No seed at all 

61 module.add_vunit_config(test=test) 

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

63 

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

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

66 

67 # No seed, with generics set 

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

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

70 

71 # Static seed 

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

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

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

75 

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

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

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

79 

80 # Use random seed 

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

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

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

84 

85 # Setting explicit value should still work 

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

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

88 

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

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

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

92 

93 

94def test_file_list_filtering(tmp_path): 

95 module_name = "zebra" 

96 path = tmp_path / module_name 

97 

98 create_directory(path / "folder_should_not_be_included") 

99 create_file(path / "should_not_be_included.apa") 

100 

101 synth_files = { 

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

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

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

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

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

107 } 

108 

109 test_files = { 

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

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

112 } 

113 

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

115 

116 my_module = BaseModule(path, "zebra") 

117 

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

119 assert files == synth_files 

120 

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

122 assert files == synth_files | test_files | sim_files 

123 

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

125 assert files == synth_files | sim_files 

126 

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

128 assert files == synth_files 

129 

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

131 assert files == test_files | sim_files 

132 

133 

134def test_get_synthesis_files_calls_get_simulation_files_with_correct_arguments(): 

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

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

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

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

139 

140 

141def test_scoped_constraints(tmp_path): 

142 module_path = tmp_path / "apa" 

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

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

145 

146 my_module = BaseModule(module_path, "apa") 

147 scoped_constraints = my_module.get_scoped_constraints() 

148 assert len(scoped_constraints) == 1 

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

150 

151 

152def test_scoped_constraint_entity_not_existing_should_raise_error(tmp_path): 

153 module_path = tmp_path / "apa" 

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

155 

156 module = BaseModule(module_path, "apa") 

157 with pytest.raises(FileNotFoundError) as exception_info: 

158 module.get_scoped_constraints() 

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

160 

161 

162def test_can_cast_to_string_without_error(): 

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

164 

165 

166def test_test_case_name(): 

167 assert ( 

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

169 ) 

170 assert ( 

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

172 == "foo.apa_3.hest_zebra_bar" 

173 ) 

174 

175 

176def test_getting_registers_calls_registers_hook(tmp_path): 

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

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

179 ) as registers_hook: 

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

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

182 registers = module.registers 

183 

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

185 from_toml.assert_called_once() 

186 registers_hook.assert_called_once() 

187 assert registers is not None 

188 

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

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

191 ) as registers_hook: 

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

193 registers = module.registers 

194 

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

196 from_toml.assert_not_called() 

197 # Register hook shall still run however 

198 registers_hook.assert_called_once() 

199 assert registers is None 

200 

201 

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

203class TestGetModules(TestCase): 

204 

205 tmp_path = None 

206 

207 def setUp(self): 

208 create_directory(self.tmp_path / "a") 

209 create_directory(self.tmp_path / "b") 

210 create_directory(self.tmp_path / "c") 

211 

212 self.modules_folders = [self.tmp_path] 

213 

214 def test_name_filtering_include(self): 

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

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

217 

218 def test_name_filtering_avoid(self): 

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

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

221 

222 def test_name_filtering_include_and_avoid(self): 

223 modules = get_modules( 

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

225 ) 

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

227 

228 def test_library_name_does_not_have_lib_suffix(self): 

229 modules = get_modules(self.modules_folders) 

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

231 

232 def test_library_name_has_lib_suffix(self): 

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

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

235 

236 def test_stray_file_can_exist_in_modules_folder_without_error(self): 

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

238 modules = get_modules(self.modules_folders) 

239 assert len(modules) == 3 

240 

241 def test_local_override_of_module_type(self): 

242 module_file_content = """ 

243from tsfpga.module import BaseModule 

244 

245class Module(BaseModule): 

246 

247 def id(self): 

248 return """ 

249 

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

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

252 

253 modules = get_modules(self.modules_folders) 

254 

255 assert len(modules) == 3 

256 for module in modules: 

257 if module.name == "a": 

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

259 elif module.name == "b": 

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

261 elif module.name == "c": 

262 assert isinstance(module, BaseModule) 

263 else: 

264 assert False 

265 

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

267 def test_register_object_creation_called_when_getting_synthesis_files(self, from_toml): 

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

269 

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

271 module.get_synthesis_files() 

272 module.get_synthesis_files() 

273 

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

275 

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

277 def test_register_object_creation_called_when_getting_simulation_files(self, from_toml): 

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

279 

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

281 module.get_simulation_files() 

282 module.get_simulation_files() 

283 

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

285 

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

287 def test_register_object_creation_called_once_when_getting_mixed_files(self, from_toml): 

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

289 

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

291 module.get_synthesis_files() 

292 module.get_simulation_files() 

293 

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