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

216 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-09-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/tsfpga/hdl_registers 

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

9 

10import copy 

11from pathlib import Path 

12import unittest 

13from unittest.mock import patch 

14 

15import pytest 

16 

17from tsfpga.system_utils import create_file 

18 

19from hdl_registers.parser import from_toml 

20from hdl_registers.register import Register 

21from hdl_registers.register_list import RegisterList 

22 

23 

24def test_from_default_registers(): 

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

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

27 default_registers = [register_a, register_b] 

28 

29 register_list = RegisterList.from_default_registers( 

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

31 ) 

32 

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

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

35 register_a.mode = "w" 

36 register_b.name = "x" 

37 

38 assert len(register_list.register_objects) == 2 

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

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

41 

42 

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

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

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

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

47def test_generated_source_info( 

48 get_svn_revision_information, 

49 svn_commands_are_available, 

50 get_git_commit, 

51 git_commands_are_available, 

52): 

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

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

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

56 

57 # Test with git information 

58 git_commands_are_available.return_value = True 

59 get_git_commit.return_value = "HASH" 

60 

61 got = register_list.generated_source_info() 

62 assert got[0] == expected_first_line 

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

64 

65 # Test with SVN information 

66 git_commands_are_available.return_value = False 

67 svn_commands_are_available.return_value = True 

68 get_svn_revision_information.return_value = "REVISION" 

69 

70 got = register_list.generated_source_info() 

71 assert got[0] == expected_first_line 

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

73 

74 # Test with no source definition file 

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

76 

77 got = register_list.generated_source_info() 

78 assert got[0] == expected_first_line 

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

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

81 

82 

83def test_header_constants(): 

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

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

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

87 

88 assert len(registers.constants) == 2 

89 

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

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

92 

93 with pytest.raises(ValueError) as exception_info: 

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

95 assert ( 

96 str(exception_info.value) 

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

98 ) 

99 

100 zebra.value = -5 

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

102 

103 

104def test_invalid_register_mode_should_raise_exception(): 

105 registers = RegisterList(None, None) 

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

107 

108 with pytest.raises(ValueError) as exception_info: 

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

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

111 

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

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

114 with pytest.raises(ValueError) as exception_info: 

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

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

117 

118 

119def test_registers_are_appended_properly_and_can_be_edited_in_place(): 

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

121 

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

123 assert register_hest.index == 0 

124 

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

126 assert register_zebra.index == 1 

127 

128 register_hest.description = "new desc" 

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

130 

131 

132def test_register_arrays_are_appended_properly_and_can_be_edited_in_place(): 

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

134 

135 register_array_hest = register_array.append_register_array( 

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

137 ) 

138 assert register_array_hest.base_index == 0 

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

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

141 

142 register_array_zebra = register_array.append_register_array( 

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

144 ) 

145 assert register_array_zebra.base_index == 8 

146 

147 

148def test_get_register(): 

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

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

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

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

153 

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

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

156 

157 with pytest.raises(ValueError) as exception_info: 

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

159 assert ( 

160 str(exception_info.value) 

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

162 ) 

163 

164 with pytest.raises(ValueError) as exception_info: 

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

166 assert ( 

167 str(exception_info.value) 

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

169 ) 

170 register_list.get_register_array("register_array") 

171 

172 

173def test_get_register_array(): 

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

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

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

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

178 

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

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

181 

182 with pytest.raises(ValueError) as exception_info: 

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

184 assert ( 

185 str(exception_info.value) 

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

187 ) 

188 

189 with pytest.raises(ValueError) as exception_info: 

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

191 assert ( 

192 str(exception_info.value) 

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

194 ) 

195 register_list.get_register("register") 

196 

197 

198def test_get_register_index(): 

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

200 

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

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

203 

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

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

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

207 

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

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

210 assert ( 

211 register_list.get_register_index( 

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

213 ) 

214 == 2 

215 ) 

216 assert ( 

217 register_list.get_register_index( 

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

219 ) 

220 == 5 

221 ) 

222 

223 

224def test_repr_basic(): 

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

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

227 

228 # Different name 

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

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

231 ) 

232 

233 # Different source_definition_file 

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

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

236 ) 

237 

238 

239def test_repr_with_constant_added(): 

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

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

242 assert repr(register_list_a) == repr(register_list_b) 

243 

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

245 

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

247 

248 

249def test_repr_with_register_appended(): 

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

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

252 assert repr(register_list_a) == repr(register_list_b) 

253 

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

255 

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

257 

258 

259def test_repr_with_register_array_appended(): 

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

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

262 assert repr(register_list_a) == repr(register_list_b) 

263 

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

265 

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

267 

268 

269def test_deep_copy_of_register_list_actually_copies_everything(): 

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

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

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

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

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

275 

276 copied_list = copy.deepcopy(original_list) 

277 

278 assert copied_list.constants is not original_list.constants 

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

280 

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

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

283 

284 assert copied_list.register_objects is not original_list.register_objects 

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

286 

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

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

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

290 

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

292 assert ( 

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

294 ) 

295 assert ( 

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

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

298 ) 

299 copied_list.register_objects[1].append_register( 

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

301 ) 

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

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

304 

305 

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

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

308class TestRegisterList(unittest.TestCase): 

309 

310 tmp_path = None 

311 

312 module_name = "sensor" 

313 toml_data = """\ 

314################################################################################ 

315[register.data] 

316 

317mode = "w" 

318description = "My register" 

319 

320""" 

321 

322 def setUp(self): 

323 self.toml_file = self.create_toml_file_with_extras() 

324 

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

326 data = self.toml_data + toml_extras 

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

328 

329 def test_create_vhdl_package_should_not_run_if_nothing_has_changed(self): 

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

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

332 register_list.create_vhdl_package(self.tmp_path) 

333 

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

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

336 with patch( 

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

338 ) as mocked_create_vhdl_package: 

339 register_list.create_vhdl_package(self.tmp_path) 

340 mocked_create_vhdl_package.assert_not_called() 

341 

342 def test_create_vhdl_package_should_run_if_hash_or_version_can_not_be_read(self): 

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

344 register_list.create_vhdl_package(self.tmp_path) 

345 

346 # Overwrite the generated file, without a valid header 

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

348 assert vhd_file.exists() 

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

350 

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

352 with patch( 

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

354 ) as mocked_create_vhdl_package: 

355 register_list.create_vhdl_package(self.tmp_path) 

356 mocked_create_vhdl_package.assert_called_once() 

357 

358 def test_create_vhdl_package_should_run_again_if_toml_file_has_changed(self): 

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

360 register_list.create_vhdl_package(self.tmp_path) 

361 

362 self.create_toml_file_with_extras( 

363 """ 

364[constant.apa] 

365 

366value = 3 

367""" 

368 ) 

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

370 with patch( 

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

372 ) as mocked_create_vhdl_package: 

373 register_list.create_vhdl_package(self.tmp_path) 

374 mocked_create_vhdl_package.assert_called_once() 

375 

376 def test_create_vhdl_package_should_run_again_if_list_is_modified(self): 

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

378 register_list.create_vhdl_package(self.tmp_path) 

379 

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

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

382 with patch( 

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

384 ) as mocked_create_vhdl_package: 

385 register_list.create_vhdl_package(self.tmp_path) 

386 mocked_create_vhdl_package.assert_called_once() 

387 

388 def test_create_vhdl_package_should_run_again_if_version_is_changed(self): 

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

390 register_list.create_vhdl_package(self.tmp_path) 

391 

392 with patch( 

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

394 ) as mocked_create_vhdl_package, patch( 

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

396 ) as _: 

397 register_list.create_vhdl_package(self.tmp_path) 

398 mocked_create_vhdl_package.assert_called_once()