Coverage for hdl_registers/test/unit/test_register_list.py: 100%

216 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-01-29 22:03 +0000

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

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

3# 

4# This file is part of the hdl_registers project, a HDL register generator fast enough to be run 

5# in real time. 

6# https://hdl-registers.com 

7# https://gitlab.com/hdl_registers/hdl_registers 

8# -------------------------------------------------------------------------------------------------- 

9 

10# Standard libraries 

11import copy 

12import unittest 

13from pathlib import Path 

14from unittest.mock import patch 

15 

16# Third party libraries 

17import pytest 

18from tsfpga.system_utils import create_file 

19 

20# First party libraries 

21from hdl_registers.parser import from_toml 

22from hdl_registers.register import Register 

23from hdl_registers.register_list import RegisterList 

24 

25 

26def test_from_default_registers(): 

27 register_a = Register(name="a", index=0, mode="r", description="AA") 

28 register_b = Register(name="b", index=1, mode="w", description="BB") 

29 default_registers = [register_a, register_b] 

30 

31 register_list = RegisterList.from_default_registers( 

32 name="apa", source_definition_file=None, default_registers=default_registers 

33 ) 

34 

35 # Change some things in the register objects to show that they are copied 

36 default_registers.append(Register(name="c", index=2, mode="r_w", description="CC")) 

37 register_a.mode = "w" 

38 register_b.name = "x" 

39 

40 assert len(register_list.register_objects) == 2 

41 assert register_list.get_register("a").mode == "r" 

42 assert register_list.get_register("b").name == "b" 

43 

44 

45@patch("hdl_registers.register_list.git_commands_are_available", autospec=True) 

46@patch("hdl_registers.register_list.get_git_commit", autospec=True) 

47@patch("hdl_registers.register_list.svn_commands_are_available", autospec=True) 

48@patch("hdl_registers.register_list.get_svn_revision_information", autospec=True) 

49def test_generated_source_info( 

50 get_svn_revision_information, 

51 svn_commands_are_available, 

52 get_git_commit, 

53 git_commands_are_available, 

54): 

55 source_definition_file = Path("/apa/whatever/regs.toml") 

56 register_list = RegisterList(name="a", source_definition_file=source_definition_file) 

57 expected_first_line = "This file is automatically generated by hdl_registers." 

58 

59 # Test with git information 

60 git_commands_are_available.return_value = True 

61 get_git_commit.return_value = "HASH" 

62 

63 got = register_list.generated_source_info() 

64 assert got[0] == expected_first_line 

65 assert " from file regs.toml at commit HASH." in got[1] 

66 

67 # Test with SVN information 

68 git_commands_are_available.return_value = False 

69 svn_commands_are_available.return_value = True 

70 get_svn_revision_information.return_value = "REVISION" 

71 

72 got = register_list.generated_source_info() 

73 assert got[0] == expected_first_line 

74 assert " from file regs.toml at revision REVISION." in got[1] 

75 

76 # Test with no source definition file 

77 register_list = RegisterList(name="a", source_definition_file=None) 

78 

79 got = register_list.generated_source_info() 

80 assert got[0] == expected_first_line 

81 assert "from file" not in got[1] 

82 assert " at revision REVISION." in got[1] 

83 

84 

85def test_header_constants(): 

86 registers = RegisterList(name="apa", source_definition_file=None) 

87 hest = registers.add_constant("hest", 123) 

88 zebra = registers.add_constant("zebra", 456, "description") 

89 

90 assert len(registers.constants) == 2 

91 

92 assert registers.get_constant("hest") == hest 

93 assert registers.get_constant("zebra") == zebra 

94 

95 with pytest.raises(ValueError) as exception_info: 

96 assert registers.get_constant("non existing") is None 

97 assert ( 

98 str(exception_info.value) 

99 == 'Could not find constant "non existing" within register list "apa"' 

100 ) 

101 

102 zebra.value = -5 

103 assert registers.get_constant("zebra").value == -5 

104 

105 

106def test_invalid_register_mode_should_raise_exception(): 

107 registers = RegisterList(None, None) 

108 registers.append_register(name="test", mode="r_w", description="") 

109 

110 with pytest.raises(ValueError) as exception_info: 

111 registers.append_register(name="hest", mode="x", description="") 

112 assert str(exception_info.value) == 'Invalid mode "x" for register "hest"' 

113 

114 register_array = registers.append_register_array("array", 2, "") 

115 register_array.append_register(name="apa", mode="r", description="") 

116 with pytest.raises(ValueError) as exception_info: 

117 register_array.append_register(name="zebra", mode="y", description="") 

118 assert str(exception_info.value) == 'Invalid mode "y" for register "zebra"' 

119 

120 

121def test_registers_are_appended_properly_and_can_be_edited_in_place(): 

122 register_array = RegisterList(name="apa", source_definition_file=Path(".")) 

123 

124 register_hest = register_array.append_register(name="hest", mode="r", description="") 

125 assert register_hest.index == 0 

126 

127 register_zebra = register_array.append_register(name="zebra", mode="r", description="") 

128 assert register_zebra.index == 1 

129 

130 register_hest.description = "new desc" 

131 assert register_array.register_objects[0].description == "new desc" 

132 

133 

134def test_register_arrays_are_appended_properly_and_can_be_edited_in_place(): 

135 register_array = RegisterList(name="apa", source_definition_file=Path(".")) 

136 

137 register_array_hest = register_array.append_register_array( 

138 name="hest", length=4, description="" 

139 ) 

140 assert register_array_hest.base_index == 0 

141 register_array_hest.append_register(name="foo", mode="r", description="") 

142 register_array_hest.append_register(name="bar", mode="w", description="") 

143 

144 register_array_zebra = register_array.append_register_array( 

145 name="zebra", length=2, description="" 

146 ) 

147 assert register_array_zebra.base_index == 8 

148 

149 

150def test_get_register(): 

151 register_list = RegisterList(name="apa", source_definition_file=None) 

152 hest = register_list.append_register(name="hest", mode="r", description="") 

153 zebra = register_list.append_register(name="zebra", mode="r", description="") 

154 register_list.append_register_array(name="register_array", length=3, description="") 

155 

156 assert register_list.get_register("hest") is hest 

157 assert register_list.get_register("zebra") is zebra 

158 

159 with pytest.raises(ValueError) as exception_info: 

160 assert register_list.get_register("non existing") is None 

161 assert ( 

162 str(exception_info.value) 

163 == 'Could not find register "non existing" within register list "apa"' 

164 ) 

165 

166 with pytest.raises(ValueError) as exception_info: 

167 assert register_list.get_register("register_array") is None 

168 assert ( 

169 str(exception_info.value) 

170 == 'Could not find register "register_array" within register list "apa"' 

171 ) 

172 register_list.get_register_array("register_array") 

173 

174 

175def test_get_register_array(): 

176 register_list = RegisterList(name="apa", source_definition_file=None) 

177 hest = register_list.append_register_array(name="hest", length=3, description="") 

178 zebra = register_list.append_register_array(name="zebra", length=2, description="") 

179 register_list.append_register(name="register", mode="r", description="") 

180 

181 assert register_list.get_register_array("hest") is hest 

182 assert register_list.get_register_array("zebra") is zebra 

183 

184 with pytest.raises(ValueError) as exception_info: 

185 assert register_list.get_register_array("non existing") is None 

186 assert ( 

187 str(exception_info.value) 

188 == 'Could not find register array "non existing" within register list "apa"' 

189 ) 

190 

191 with pytest.raises(ValueError) as exception_info: 

192 assert register_list.get_register_array("register") is None 

193 assert ( 

194 str(exception_info.value) 

195 == 'Could not find register array "register" within register list "apa"' 

196 ) 

197 register_list.get_register("register") 

198 

199 

200def test_get_register_index(): 

201 register_list = RegisterList(name=None, source_definition_file=None) 

202 

203 register_list.append_register(name="apa", mode="r", description="") 

204 register_list.append_register(name="hest", mode="r", description="") 

205 

206 zebra = register_list.append_register_array(name="zebra", length=2, description="") 

207 zebra.append_register(name="bar", mode="r", description="") 

208 zebra.append_register(name="baz", mode="r", description="") 

209 

210 assert register_list.get_register_index(register_name="apa") == 0 

211 assert register_list.get_register_index(register_name="hest") == 1 

212 assert ( 

213 register_list.get_register_index( 

214 register_name="bar", register_array_name="zebra", register_array_index=0 

215 ) 

216 == 2 

217 ) 

218 assert ( 

219 register_list.get_register_index( 

220 register_name="baz", register_array_name="zebra", register_array_index=1 

221 ) 

222 == 5 

223 ) 

224 

225 

226def test_repr_basic(): 

227 # Check that repr is an actual representation, not just "X object at 0xABCDEF" 

228 assert "apa" in repr(RegisterList(name="apa", source_definition_file=Path("."))) 

229 

230 # Different name 

231 assert repr(RegisterList(name="apa", source_definition_file=Path("."))) != repr( 

232 RegisterList(name="hest", source_definition_file=Path(".")) 

233 ) 

234 

235 # Different source_definition_file 

236 assert repr(RegisterList(name="apa", source_definition_file=Path("."))) != repr( 

237 RegisterList(name="apa", source_definition_file=Path("./zebra")) 

238 ) 

239 

240 

241def test_repr_with_constant_added(): 

242 register_list_a = RegisterList(name="apa", source_definition_file=Path(".")) 

243 register_list_b = RegisterList(name="apa", source_definition_file=Path(".")) 

244 assert repr(register_list_a) == repr(register_list_b) 

245 

246 register_list_a.add_constant(name="zebra", value=3) 

247 

248 assert repr(register_list_a) != repr(register_list_b) 

249 

250 

251def test_repr_with_register_appended(): 

252 register_list_a = RegisterList(name="apa", source_definition_file=Path(".")) 

253 register_list_b = RegisterList(name="apa", source_definition_file=Path(".")) 

254 assert repr(register_list_a) == repr(register_list_b) 

255 

256 register_list_a.append_register(name="zebra", mode="w", description="") 

257 

258 assert repr(register_list_a) != repr(register_list_b) 

259 

260 

261def test_repr_with_register_array_appended(): 

262 register_list_a = RegisterList(name="apa", source_definition_file=Path(".")) 

263 register_list_b = RegisterList(name="apa", source_definition_file=Path(".")) 

264 assert repr(register_list_a) == repr(register_list_b) 

265 

266 register_list_a.append_register_array(name="zebra", length=4, description="") 

267 

268 assert repr(register_list_a) != repr(register_list_b) 

269 

270 

271def test_deep_copy_of_register_list_actually_copies_everything(): 

272 original_list = RegisterList("original", Path("/original_file.txt")) 

273 original_list.add_constant("original_constant", value=2, description="original constant") 

274 original_list.append_register("original_register", "w", description="original register") 

275 original_array = original_list.append_register_array("original_array", length=4, description="") 

276 original_array.append_register(name="original_register_in_array", mode="r", description="") 

277 

278 copied_list = copy.deepcopy(original_list) 

279 

280 assert copied_list.constants is not original_list.constants 

281 assert copied_list.constants[0] is not original_list.constants[0] 

282 

283 copied_list.add_constant(name="new_constant", value=5) 

284 assert len(copied_list.constants) == 2 and len(original_list.constants) == 1 

285 

286 assert copied_list.register_objects is not original_list.register_objects 

287 assert copied_list.register_objects[0] is not original_list.register_objects[0] 

288 

289 # Original register in position 0, original register array in position 1, new register in 2 

290 copied_list.append_register(name="new_register", mode="r", description="") 

291 assert len(copied_list.register_objects) == 3 and len(original_list.register_objects) == 2 

292 

293 assert copied_list.register_objects[1] is not original_list.register_objects[1] 

294 assert ( 

295 copied_list.register_objects[1].registers is not original_list.register_objects[1].registers 

296 ) 

297 assert ( 

298 copied_list.register_objects[1].registers[0] 

299 is not original_list.register_objects[1].registers[0] 

300 ) 

301 copied_list.register_objects[1].append_register( 

302 name="new_register_in_array", mode="r_w", description="" 

303 ) 

304 assert len(copied_list.register_objects[1].registers) == 2 

305 assert len(original_list.register_objects[1].registers) == 1 

306 

307 

308# pylint: disable=too-many-public-methods 

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

310class TestRegisterList(unittest.TestCase): 

311 

312 tmp_path = None 

313 

314 module_name = "sensor" 

315 toml_data = """\ 

316################################################################################ 

317[register.data] 

318 

319mode = "w" 

320description = "My register" 

321 

322""" 

323 

324 def setUp(self): 

325 self.toml_file = self.create_toml_file_with_extras() 

326 

327 def create_toml_file_with_extras(self, toml_extras=""): 

328 data = self.toml_data + toml_extras 

329 return create_file(self.tmp_path / "sensor_regs.toml", data) 

330 

331 def test_create_vhdl_package_should_not_run_if_nothing_has_changed(self): 

332 register_list = from_toml(self.module_name, self.toml_file) 

333 register_list.add_constant(name="apa", value=3) 

334 register_list.create_vhdl_package(self.tmp_path) 

335 

336 register_list = from_toml(self.module_name, self.toml_file) 

337 register_list.add_constant(name="apa", value=3) 

338 with patch( 

339 "hdl_registers.register_list.RegisterList._create_vhdl_package", autospec=True 

340 ) as mocked_create_vhdl_package: 

341 register_list.create_vhdl_package(self.tmp_path) 

342 mocked_create_vhdl_package.assert_not_called() 

343 

344 def test_create_vhdl_package_should_run_if_hash_or_version_can_not_be_read(self): 

345 register_list = from_toml(self.module_name, self.toml_file) 

346 register_list.create_vhdl_package(self.tmp_path) 

347 

348 # Overwrite the generated file, without a valid header 

349 vhd_file = self.tmp_path / "sensor_regs_pkg.vhd" 

350 assert vhd_file.exists() 

351 create_file(vhd_file, contents="-- Mumbo jumbo\n") 

352 

353 register_list = from_toml(self.module_name, self.toml_file) 

354 with patch( 

355 "hdl_registers.register_list.RegisterList._create_vhdl_package", autospec=True 

356 ) as mocked_create_vhdl_package: 

357 register_list.create_vhdl_package(self.tmp_path) 

358 mocked_create_vhdl_package.assert_called_once() 

359 

360 def test_create_vhdl_package_should_run_again_if_toml_file_has_changed(self): 

361 register_list = from_toml(self.module_name, self.toml_file) 

362 register_list.create_vhdl_package(self.tmp_path) 

363 

364 self.create_toml_file_with_extras( 

365 """ 

366[constant.apa] 

367 

368value = 3 

369""" 

370 ) 

371 register_list = from_toml(self.module_name, self.toml_file) 

372 with patch( 

373 "hdl_registers.register_list.RegisterList._create_vhdl_package", autospec=True 

374 ) as mocked_create_vhdl_package: 

375 register_list.create_vhdl_package(self.tmp_path) 

376 mocked_create_vhdl_package.assert_called_once() 

377 

378 def test_create_vhdl_package_should_run_again_if_list_is_modified(self): 

379 register_list = from_toml(self.module_name, self.toml_file) 

380 register_list.create_vhdl_package(self.tmp_path) 

381 

382 register_list = from_toml(self.module_name, self.toml_file) 

383 register_list.add_constant(name="apa", value=3) 

384 with patch( 

385 "hdl_registers.register_list.RegisterList._create_vhdl_package", autospec=True 

386 ) as mocked_create_vhdl_package: 

387 register_list.create_vhdl_package(self.tmp_path) 

388 mocked_create_vhdl_package.assert_called_once() 

389 

390 def test_create_vhdl_package_should_run_again_if_version_is_changed(self): 

391 register_list = from_toml(self.module_name, self.toml_file) 

392 register_list.create_vhdl_package(self.tmp_path) 

393 

394 with patch( 

395 "hdl_registers.register_list.RegisterList._create_vhdl_package", autospec=True 

396 ) as mocked_create_vhdl_package, patch( 

397 "hdl_registers.register_list.__version__", autospec=True 

398 ) as _: 

399 register_list.create_vhdl_package(self.tmp_path) 

400 mocked_create_vhdl_package.assert_called_once()