Thanks to visit codestin.com
Credit goes to tsfpga.com

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

286 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-09 08:30 +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 pathlib import Path 

10from unittest.mock import ANY, MagicMock, patch 

11 

12import pytest 

13 

14from tsfpga.module import BaseModule, get_module, get_modules 

15from tsfpga.system_utils import create_directory, create_file 

16 

17 

18def test_add_vunit_config_name(): 

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

20 

21 test = MagicMock() 

22 pre_config = MagicMock() 

23 post_check = MagicMock() 

24 

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

26 test.add_config.assert_called_once_with( 

27 name="0", generics={}, pre_config=pre_config, post_check=post_check 

28 ) 

29 test.reset_mock() 

30 

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

32 test.add_config.assert_called_once_with( 

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

34 ) 

35 test.reset_mock() 

36 

37 module.add_vunit_config(test=test, generics={"apa": "hest", "foo": "bar"}) 

38 test.add_config.assert_called_once_with( 

39 name="apa_hest.foo_bar", 

40 generics={"apa": "hest", "foo": "bar"}, 

41 pre_config=None, 

42 post_check=None, 

43 ) 

44 test.reset_mock() 

45 

46 module.add_vunit_config(test=test, name="zebra", generics={"apa": "hest", "foo": "bar"}) 

47 test.add_config.assert_called_once_with( 

48 name="zebra.apa_hest.foo_bar", 

49 generics={"apa": "hest", "foo": "bar"}, 

50 pre_config=None, 

51 post_check=None, 

52 ) 

53 

54 

55def test_add_vunit_config_random_seed(): 

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

57 test = MagicMock() 

58 

59 # No seed at all 

60 module.add_vunit_config(test=test) 

61 assert not test.add_config.call_args.kwargs["generics"] 

62 

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

64 assert not test.add_config.call_args.kwargs["generics"] 

65 

66 # No seed, with generics set 

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

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

69 

70 # Static seed 

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

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

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

74 

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

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

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

78 

79 # Use random seed 

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

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

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

83 

84 # Setting explicit value should still work 

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

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

87 

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

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

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

91 

92 

93def test_add_vunit_config_count_1(): 

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

95 test = MagicMock() 

96 

97 module.add_vunit_config(test=test, count=1) 

98 assert test.add_config.call_count == 1 

99 assert test.add_config.call_args.kwargs["name"] == "0" 

100 

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

102 assert test.add_config.call_args.kwargs["name"] == "apa" 

103 

104 

105def test_add_vunit_config_count_2(): 

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

107 test = MagicMock() 

108 

109 module.add_vunit_config(test=test, count=2) 

110 assert test.add_config.call_count == 2 

111 assert test.add_config.call_args_list[0].kwargs["name"] == "0" 

112 assert test.add_config.call_args_list[1].kwargs["name"] == "1" 

113 

114 module.add_vunit_config(test=test, name="apa", count=2) 

115 assert test.add_config.call_args_list[2].kwargs["name"] == "apa.0" 

116 assert test.add_config.call_args_list[3].kwargs["name"] == "apa.1" 

117 

118 module.add_vunit_config(test=test, generics={"apa": True}, count=2) 

119 assert test.add_config.call_args_list[4].kwargs["name"] == "apa_True.0" 

120 assert test.add_config.call_args_list[5].kwargs["name"] == "apa_True.1" 

121 

122 

123def test_file_list_filtering(tmp_path): 

124 module_name = "zebra" 

125 path = tmp_path / module_name 

126 

127 create_directory(path / "folder_should_not_be_included") 

128 create_file(path / "should_not_be_included.apa") 

129 

130 synth_files = { 

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

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

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

134 create_file(path / "hdl" / "rtl" / "syn.vhdl"), 

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

136 } 

137 

138 test_files = { 

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

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

141 } 

142 

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

144 

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

146 

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

148 assert files == synth_files 

149 

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

151 assert files == synth_files | test_files | sim_files 

152 

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

154 assert files == synth_files | sim_files 

155 

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

157 assert files == synth_files 

158 

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

160 assert files == test_files | sim_files 

161 

162 

163def test_get_synthesis_files_calls_get_simulation_files_with_correct_arguments(): 

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

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

166 module.get_simulation_files( 

167 files_include=True, 

168 files_avoid=False, 

169 apa=123, 

170 include_vhdl_files=1, 

171 include_verilog_files=2, 

172 include_systemverilog_files=3, 

173 ) 

174 get_synthesis_files.assert_called_once_with( 

175 files_include=True, 

176 files_avoid=False, 

177 apa=123, 

178 include_vhdl_files=1, 

179 include_verilog_files=2, 

180 include_systemverilog_files=3, 

181 ) 

182 

183 

184def test_get_vhdl_files(tmp_path): 

185 paths = { 

186 create_file(tmp_path / "apa.vhdl"), 

187 create_file(tmp_path / "apa.vhd"), 

188 } 

189 create_file(tmp_path / "apa.v") 

190 create_file(tmp_path / "apa.vh") 

191 create_file(tmp_path / "apa.sv") 

192 create_file(tmp_path / "apa.svh") 

193 

194 got_hdl_files = BaseModule(path=tmp_path, library_name="").get_synthesis_files( 

195 include_verilog_files=False, include_systemverilog_files=False 

196 ) 

197 assert {hdl_file.path for hdl_file in got_hdl_files} == paths 

198 

199 

200def test_get_verilog_files(tmp_path): 

201 paths = {create_file(tmp_path / "apa.v"), create_file(tmp_path / "apa.vh")} 

202 create_file(tmp_path / "apa.vhdl") 

203 create_file(tmp_path / "apa.vhd") 

204 create_file(tmp_path / "apa.sv") 

205 create_file(tmp_path / "apa.svh") 

206 

207 got_hdl_files = BaseModule(path=tmp_path, library_name="").get_simulation_files( 

208 include_vhdl_files=False, include_systemverilog_files=False 

209 ) 

210 assert {hdl_file.path for hdl_file in got_hdl_files} == paths 

211 

212 

213def test_get_systemverilog_files(tmp_path): 

214 paths = {create_file(tmp_path / "apa.sv"), create_file(tmp_path / "apa.svh")} 

215 create_file(tmp_path / "apa.vhdl") 

216 create_file(tmp_path / "apa.vhd") 

217 create_file(tmp_path / "apa.v") 

218 create_file(tmp_path / "apa.vh") 

219 

220 got_hdl_files = BaseModule(path=tmp_path, library_name="").get_documentation_files( 

221 include_vhdl_files=False, include_verilog_files=False 

222 ) 

223 assert {hdl_file.path for hdl_file in got_hdl_files} == paths 

224 

225 

226def test_get_documentation_files(tmp_path): 

227 module_name = "zebra" 

228 path = tmp_path / module_name 

229 

230 synth_files = { 

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

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

233 } 

234 

235 # Test files 

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

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

238 

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

240 

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

242 

243 # Should include everything except test files 

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

245 assert files == synth_files | sim_files 

246 

247 

248def test_scoped_constraints(tmp_path): 

249 module_path = tmp_path / "apa" 

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

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

252 

253 my_module = BaseModule(module_path, "apa") 

254 scoped_constraints = my_module.get_scoped_constraints() 

255 assert len(scoped_constraints) == 1 

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

257 

258 

259def test_scoped_constraint_entity_not_existing_should_raise_error(tmp_path): 

260 module_path = tmp_path / "apa" 

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

262 

263 module = BaseModule(module_path, "apa") 

264 with pytest.raises(FileNotFoundError) as exception_info: 

265 module.get_scoped_constraints() 

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

267 

268 

269def test_can_cast_to_string_without_error(): 

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

271 

272 

273def test_test_case_name(): 

274 assert ( 

275 BaseModule.test_case_name(generics={"apa": 3, "hest_zebra": "foo"}) 

276 == "apa_3.hest_zebra_foo" 

277 ) 

278 assert ( 

279 BaseModule.test_case_name(name="foo", generics={"apa": 3, "hest_zebra": "bar"}) 

280 == "foo.apa_3.hest_zebra_bar" 

281 ) 

282 

283 

284def test_getting_registers_calls_registers_hook(tmp_path): 

285 with ( 

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

287 patch("tsfpga.module.BaseModule.registers_hook", autospec=True) as registers_hook, 

288 ): 

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

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

291 registers = module.registers 

292 

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

294 from_toml.assert_called_once() 

295 registers_hook.assert_called_once() 

296 assert registers is not None 

297 

298 with ( 

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

300 patch("tsfpga.module.BaseModule.registers_hook", autospec=True) as registers_hook, 

301 ): 

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

303 registers = module.registers 

304 

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

306 from_toml.assert_not_called() 

307 # Register hook shall still run however 

308 registers_hook.assert_called_once() 

309 assert registers is None 

310 

311 

312def test_creating_synthesis_files_does_not_create_simulation_files(tmp_path): 

313 create_file(tmp_path / "a" / "regs_a.toml", "apa.mode = 'r_w'") 

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

315 

316 synthesis_file = module.register_synthesis_folder / "a_regs_pkg.vhd" 

317 simulation_file = module.register_simulation_folder / "a_register_read_write_pkg.vhd" 

318 

319 module.get_synthesis_files() 

320 assert synthesis_file.exists() 

321 assert not simulation_file.exists() 

322 assert not module.register_simulation_folder.exists() 

323 

324 module.get_simulation_files() 

325 assert simulation_file.exists() 

326 

327 

328def test_old_register_package_should_be_deleted(tmp_path): 

329 create_file(tmp_path / "a" / "regs_a.toml", "apa.mode = 'r_w'") 

330 regs_pkg = create_file(tmp_path / "a" / "a_regs_pkg.vhd") 

331 

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

333 module.get_synthesis_files() 

334 

335 assert not regs_pkg.exists() 

336 

337 

338@pytest.fixture 

339def get_modules_test(tmp_path): 

340 class GetModulesTest: 

341 def __init__(self): 

342 create_directory(tmp_path / "a") 

343 create_directory(tmp_path / "b") 

344 create_directory(tmp_path / "c") 

345 

346 self.modules_folder = tmp_path 

347 self.modules_folders = [self.modules_folder] 

348 

349 return GetModulesTest() 

350 

351 

352def test_get_module(get_modules_test): 

353 module = get_module(name="a", modules_folder=get_modules_test.modules_folder) 

354 assert module.name == "a" 

355 assert module.library_name == "a" 

356 assert module.path == get_modules_test.modules_folder / "a" 

357 

358 module = get_module( 

359 name="b", 

360 modules_folders=[get_modules_test.modules_folder], 

361 library_name_has_lib_suffix=True, 

362 ) 

363 assert module.name == "b" 

364 assert module.library_name == "b_lib" 

365 assert module.path == get_modules_test.modules_folder / "b" 

366 

367 

368def test_get_module_not_found_should_raise_exception(get_modules_test): 

369 with pytest.raises(RuntimeError) as exception_info: 

370 get_module(name="d", modules_folder=get_modules_test.modules_folder) 

371 assert str(exception_info.value) == 'Could not find module "d".' 

372 

373 

374def test_get_module_found_multiple_should_raise_exception(get_modules_test): 

375 create_directory(get_modules_test.modules_folder / "a" / "x") 

376 create_directory(get_modules_test.modules_folder / "b" / "x") 

377 

378 with pytest.raises(RuntimeError) as exception_info: 

379 get_module( 

380 name="x", 

381 modules_folders=[ 

382 get_modules_test.modules_folder / "a", 

383 get_modules_test.modules_folder / "b", 

384 ], 

385 ) 

386 assert str(exception_info.value) == 'Found multiple modules named "x".' 

387 

388 

389def test_name_filtering_include(get_modules_test): 

390 modules = get_modules( 

391 modules_folders=get_modules_test.modules_folders, names_include=["a", "b"] 

392 ) 

393 assert {module.name for module in modules} == {"a", "b"} 

394 

395 

396def test_name_filtering_avoid(get_modules_test): 

397 modules = get_modules(get_modules_test.modules_folder, names_avoid=["a", "b"]) 

398 assert {module.name for module in modules} == {"c"} 

399 

400 

401def test_name_filtering_include_and_avoid(get_modules_test): 

402 modules = get_modules( 

403 get_modules_test.modules_folder, names_include=["a", "c"], names_avoid=["b", "c"] 

404 ) 

405 assert {module.name for module in modules} == {"a"} 

406 

407 

408def test_library_name_does_not_have_lib_suffix(get_modules_test): 

409 modules = get_modules(get_modules_test.modules_folder) 

410 assert {module.library_name for module in modules} == {"a", "b", "c"} 

411 

412 

413def test_library_name_has_lib_suffix(get_modules_test): 

414 modules = get_modules(get_modules_test.modules_folder, library_name_has_lib_suffix=True) 

415 assert {module.library_name for module in modules} == {"a_lib", "b_lib", "c_lib"} 

416 

417 

418def test_stray_file_can_exist_in_modules_folder_without_error(get_modules_test): 

419 create_file(get_modules_test.modules_folder / "text_file.txt") 

420 modules = get_modules(get_modules_test.modules_folder) 

421 assert len(modules) == 3 

422 

423 

424def test_local_override_of_module_type(get_modules_test): 

425 module_file_content = """ 

426from tsfpga.module import BaseModule 

427 

428class Module(BaseModule): 

429 def id(self): 

430 return """ 

431 

432 create_file(get_modules_test.modules_folder / "a" / "module_a.py", module_file_content + '"a"') 

433 create_file(get_modules_test.modules_folder / "b" / "module_b.py", module_file_content + '"b"') 

434 

435 modules = get_modules(get_modules_test.modules_folder) 

436 

437 assert len(modules) == 3 

438 for module in modules: 

439 if module.name == "a": 

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

441 elif module.name == "b": 

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

443 elif module.name == "c": 

444 assert isinstance(module, BaseModule) 

445 else: 

446 raise AssertionError 

447 

448 

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

450@patch("tsfpga.module.VhdlRegisterPackageGenerator.create_if_needed", autospec=True) 

451@patch("tsfpga.module.VhdlRecordPackageGenerator.create_if_needed", autospec=True) 

452@patch("tsfpga.module.VhdlAxiLiteWrapperGenerator.create_if_needed", autospec=True) 

453def test_register_toml_file_parsed_only_once_when_getting_synthesis_files( 

454 create3, create2, create1, from_toml, tmp_path 

455): 

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

457 

458 module = get_modules(tmp_path).get("a") 

459 module.get_synthesis_files() 

460 module.get_synthesis_files() 

461 

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

463 assert create3.call_count == 2 

464 assert create2.call_count == 2 

465 assert create1.call_count == 2 

466 

467 

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

469@patch("tsfpga.module.VhdlRegisterPackageGenerator.create_if_needed", autospec=True) 

470@patch("tsfpga.module.VhdlRecordPackageGenerator.create_if_needed", autospec=True) 

471@patch("tsfpga.module.VhdlAxiLiteWrapperGenerator.create_if_needed", autospec=True) 

472@patch("tsfpga.module.VhdlSimulationReadWritePackageGenerator.create_if_needed", autospec=True) 

473@patch("tsfpga.module.VhdlSimulationCheckPackageGenerator.create_if_needed", autospec=True) 

474@patch("tsfpga.module.VhdlSimulationWaitUntilPackageGenerator.create_if_needed", autospec=True) 

475def test_register_toml_file_parsed_only_once_when_getting_simulation_files( 

476 create6, create5, create4, create3, create2, create1, from_toml, tmp_path 

477): 

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

479 

480 module = get_modules(tmp_path).get("a") 

481 module.get_simulation_files() 

482 module.get_simulation_files() 

483 

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

485 assert create6.call_count == 2 

486 assert create5.call_count == 2 

487 assert create4.call_count == 2 

488 assert create3.call_count == 2 

489 assert create2.call_count == 2 

490 assert create1.call_count == 2 

491 

492 

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

494@patch("tsfpga.module.VhdlRegisterPackageGenerator.create_if_needed", autospec=True) 

495@patch("tsfpga.module.VhdlRecordPackageGenerator.create_if_needed", autospec=True) 

496@patch("tsfpga.module.VhdlAxiLiteWrapperGenerator.create_if_needed", autospec=True) 

497@patch("tsfpga.module.VhdlSimulationReadWritePackageGenerator.create_if_needed", autospec=True) 

498@patch("tsfpga.module.VhdlSimulationCheckPackageGenerator.create_if_needed", autospec=True) 

499@patch("tsfpga.module.VhdlSimulationWaitUntilPackageGenerator.create_if_needed", autospec=True) 

500def test_register_toml_file_parsed_only_once_when_getting_mixed_files( 

501 create6, create5, create4, create3, create2, create1, from_toml, tmp_path 

502): 

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

504 

505 module = get_modules(tmp_path).get("a") 

506 module.get_synthesis_files() 

507 module.get_simulation_files() 

508 

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

510 assert create6.call_count == 1 

511 assert create5.call_count == 1 

512 assert create4.call_count == 1 

513 assert create3.call_count == 2 

514 assert create2.call_count == 2 

515 assert create1.call_count == 2