Coverage for tests/test_000_accretive/test_500_dictionaries.py: 100%

270 statements  

« prev     ^ index     » next       coverage.py v7.5.4, created at 2024-07-06 17:17 +0000

1# vim: set filetype=python fileencoding=utf-8: 

2# -*- coding: utf-8 -*- 

3 

4#============================================================================# 

5# # 

6# Licensed under the Apache License, Version 2.0 (the "License"); # 

7# you may not use this file except in compliance with the License. # 

8# You may obtain a copy of the License at # 

9# # 

10# http://www.apache.org/licenses/LICENSE-2.0 # 

11# # 

12# Unless required by applicable law or agreed to in writing, software # 

13# distributed under the License is distributed on an "AS IS" BASIS, # 

14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 

15# See the License for the specific language governing permissions and # 

16# limitations under the License. # 

17# # 

18#============================================================================# 

19 

20 

21''' Assert correct function of dictionaries. ''' 

22 

23# mypy: ignore-errors 

24# pylint: disable=attribute-defined-outside-init 

25# pylint: disable=invalid-name,magic-value-comparison,protected-access 

26 

27 

28import pytest 

29 

30from itertools import product 

31from types import MappingProxyType as DictionaryProxy 

32 

33from . import ( 

34 CONCEALMENT_PACKAGES_NAMES, 

35 MODULES_QNAMES, 

36 PACKAGE_NAME, 

37 PROTECTION_PACKAGES_NAMES, 

38 cache_import_module, 

39) 

40 

41 

42THESE_MODULE_QNAMES = tuple( 

43 name for name in MODULES_QNAMES if name.endswith( '.dictionaries' ) ) 

44THESE_CONCEALMENT_MODULE_QNAMES = tuple( 

45 name for name in THESE_MODULE_QNAMES 

46 if name.startswith( CONCEALMENT_PACKAGES_NAMES ) ) 

47THESE_NONCONCEALMENT_MODULE_QNAMES = tuple( 

48 name for name in THESE_MODULE_QNAMES 

49 if not name.startswith( CONCEALMENT_PACKAGES_NAMES ) ) 

50THESE_PROTECTION_MODULE_QNAMES = tuple( 

51 name for name in THESE_MODULE_QNAMES 

52 if name.startswith( PROTECTION_PACKAGES_NAMES ) ) 

53THESE_NONPROTECTION_MODULE_QNAMES = tuple( 

54 name for name in THESE_MODULE_QNAMES 

55 if not name.startswith( PROTECTION_PACKAGES_NAMES ) ) 

56INITARGS_NAMES = ( 'Dictionary', ) 

57PRODUCER_NAMES = ( 'ProducerDictionary', ) 

58THESE_CLASSES_NAMES = ( *INITARGS_NAMES, *PRODUCER_NAMES, ) 

59NONPRODUCER_NAMES = tuple( 

60 name for name in THESE_CLASSES_NAMES 

61 if name not in PRODUCER_NAMES ) 

62 

63base = cache_import_module( f"{PACKAGE_NAME}.__" ) 

64exceptions = cache_import_module( f"{PACKAGE_NAME}.exceptions" ) 

65 

66simple_posargs = ( ( ( 'foo', 1 ), ( 'bar', 2 ) ), { 'unicorn': True } ) 

67simple_nomargs = DictionaryProxy( dict( orb = False ) ) 

68 

69 

70def select_arguments( class_name ): 

71 ''' Choose initializer arguments depending on class. ''' 

72 if class_name in PRODUCER_NAMES: return ( list, ), { } 

73 return ( ), { } 

74 

75 

76@pytest.mark.parametrize( 

77 'module_qname, class_name', 

78 product( THESE_MODULE_QNAMES, INITARGS_NAMES ) 

79) 

80def test_100_instantiation( module_qname, class_name ): 

81 ''' Class instantiates. ''' 

82 module = cache_import_module( module_qname ) 

83 factory = getattr( module, class_name ) 

84 dct1 = factory( ) 

85 assert isinstance( dct1, factory ) 

86 dct2 = factory( 

87 ( ( 'foo', 1 ), ( 'bar', 2 ) ), { 'unicorn': True }, orb = False ) 

88 assert isinstance( dct2, factory ) 

89 assert 1 == dct2[ 'foo' ] 

90 assert 2 == dct2[ 'bar' ] 

91 assert dct2[ 'unicorn' ] 

92 assert not dct2[ 'orb' ] 

93 assert ( 'foo', 'bar', 'unicorn', 'orb' ) == tuple( dct2.keys( ) ) 

94 assert ( 1, 2, True, False ) == tuple( dct2.values( ) ) 

95 

96 

97@pytest.mark.parametrize( 

98 'module_qname, class_name', 

99 product( THESE_MODULE_QNAMES, PRODUCER_NAMES ) 

100) 

101def test_101_instantiation( module_qname, class_name ): 

102 ''' Producer class instantiates. ''' 

103 module = cache_import_module( module_qname ) 

104 factory = getattr( module, class_name ) 

105 dct = factory( list ) 

106 assert isinstance( dct, factory ) 

107 assert isinstance( dct[ 'a' ], list ) 

108 

109 

110@pytest.mark.parametrize( 

111 'module_qname, class_name', 

112 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

113) 

114def test_201_duplication( module_qname, class_name ): 

115 ''' Dictionary is duplicable. ''' 

116 module = cache_import_module( module_qname ) 

117 factory = getattr( module, class_name ) 

118 posargs, nomargs = select_arguments( class_name ) 

119 odct = factory( *posargs, **nomargs ) 

120 ddct = odct.copy( ) 

121 assert odct == ddct 

122 odct[ 'baz' ] = 42 

123 assert odct != ddct 

124 

125 

126@pytest.mark.parametrize( 

127 'module_qname, class_name', 

128 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

129) 

130def test_110_attribute_accretion( module_qname, class_name ): 

131 ''' Dictionary accretes attributes. ''' 

132 module = cache_import_module( module_qname ) 

133 factory = getattr( module, class_name ) 

134 posargs, nomargs = select_arguments( class_name ) 

135 obj = factory( *posargs, **nomargs ) 

136 obj.attr = 42 

137 with pytest.raises( exceptions.IndelibleAttributeError ): 

138 obj.attr = -1 

139 assert 42 == obj.attr 

140 with pytest.raises( exceptions.IndelibleAttributeError ): 

141 del obj.attr 

142 assert 42 == obj.attr 

143 

144 

145@pytest.mark.parametrize( 

146 'module_qname, class_name', 

147 product( THESE_CONCEALMENT_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

148) 

149def test_120_attribute_concealment( module_qname, class_name ): 

150 ''' Object conceals attributes. ''' 

151 module = cache_import_module( module_qname ) 

152 factory = getattr( module, class_name ) 

153 posargs, nomargs = select_arguments( class_name ) 

154 

155 class Concealer( factory ): 

156 ''' test ''' 

157 _attribute_visibility_includes_ = frozenset( ( '_private', ) ) 

158 

159 obj = Concealer( *posargs, **nomargs ) 

160 assert dir( obj ) 

161 obj.public = 42 

162 assert 'public' in dir( obj ) 

163 obj._nonpublic = 3.1415926535 

164 assert '_nonpublic' not in dir( obj ) 

165 assert '_private' not in dir( obj ) 

166 obj._private = 'foo' 

167 assert '_private' in dir( obj ) 

168 

169 

170@pytest.mark.parametrize( 

171 'module_qname, class_name', 

172 product( THESE_NONCONCEALMENT_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

173) 

174def test_121_attribute_nonconcealment( module_qname, class_name ): 

175 ''' Object does not conceal attributes. ''' 

176 module = cache_import_module( module_qname ) 

177 factory = getattr( module, class_name ) 

178 posargs, nomargs = select_arguments( class_name ) 

179 

180 class Concealer( factory ): 

181 ''' test ''' 

182 _attribute_visibility_includes_ = frozenset( ( '_private', ) ) 

183 

184 obj = Concealer( *posargs, **nomargs ) 

185 assert '_attribute_visibility_includes_' in dir( obj ) 

186 obj.public = 42 

187 assert 'public' in dir( obj ) 

188 obj._nonpublic = 3.1415926535 

189 assert '_nonpublic' in dir( obj ) 

190 assert '_private' not in dir( obj ) 

191 obj._private = 'foo' 

192 assert '_private' in dir( obj ) 

193 

194 

195@pytest.mark.parametrize( 

196 'module_qname, class_name', 

197 product( THESE_PROTECTION_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

198) 

199def test_150_class_attribute_protection( module_qname, class_name ): 

200 ''' Class attributes are protected. ''' 

201 module = cache_import_module( module_qname ) 

202 factory = getattr( module, class_name ) 

203 with pytest.raises( exceptions.IndelibleAttributeError ): 

204 factory.__setattr__ = None 

205 with pytest.raises( exceptions.IndelibleAttributeError ): 

206 del factory.__setattr__ 

207 factory.foo = 42 

208 with pytest.raises( exceptions.IndelibleAttributeError ): 

209 factory.foo = -1 

210 with pytest.raises( exceptions.IndelibleAttributeError ): 

211 del factory.foo 

212 # Cleanup. 

213 type.__delattr__( factory, 'foo' ) 

214 

215 

216@pytest.mark.parametrize( 

217 'module_qname, class_name', 

218 product( THESE_NONPROTECTION_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

219) 

220def test_151_class_attribute_nonprotection( module_qname, class_name ): 

221 ''' Class attributes are not protected. ''' 

222 module = cache_import_module( module_qname ) 

223 factory = getattr( module, class_name ) 

224 factory.foo = 42 

225 assert 42 == factory.foo 

226 factory.foo = -1 

227 assert -1 == factory.foo 

228 del factory.foo 

229 assert not hasattr( factory, 'foo' ) 

230 

231 

232@pytest.mark.parametrize( 

233 'module_qname, class_name', 

234 product( THESE_MODULE_QNAMES, INITARGS_NAMES ) 

235) 

236def test_200_dictionary_entry_accretion( module_qname, class_name ): 

237 ''' Dictionary accretes entries. ''' 

238 module = cache_import_module( module_qname ) 

239 factory = getattr( module, class_name ) 

240 dct = factory( *simple_posargs, **simple_nomargs ) 

241 with pytest.raises( exceptions.IndelibleEntryError ): 

242 del dct[ 'foo' ] 

243 with pytest.raises( exceptions.IndelibleEntryError ): 

244 dct[ 'foo' ] = 666 

245 dct[ 'baz' ] = 43 

246 with pytest.raises( exceptions.IndelibleEntryError ): 

247 del dct[ 'baz' ] 

248 with pytest.raises( exceptions.IndelibleEntryError ): 

249 dct[ 'baz' ] = 42 

250 

251 

252@pytest.mark.parametrize( 

253 'module_qname, class_name', 

254 product( THESE_MODULE_QNAMES, PRODUCER_NAMES ) 

255) 

256def test_201_producer_dictionary_entry_accretion( module_qname, class_name ): 

257 ''' Producer dictionary accretes entries. ''' 

258 module = cache_import_module( module_qname ) 

259 factory = getattr( module, class_name ) 

260 dct = factory( list ) 

261 dct[ 'baz' ] = 43 

262 with pytest.raises( exceptions.IndelibleEntryError ): 

263 del dct[ 'baz' ] 

264 with pytest.raises( exceptions.IndelibleEntryError ): 

265 dct[ 'baz' ] = 42 

266 dct[ 'abc' ].append( 12 ) 

267 assert 12 == dct[ 'abc' ][ 0 ] 

268 with pytest.raises( exceptions.IndelibleEntryError ): 

269 del dct[ 'abc' ] 

270 with pytest.raises( exceptions.IndelibleEntryError ): 

271 dct[ 'abc' ] = 666 

272 

273 

274@pytest.mark.parametrize( 

275 'module_qname, class_name', 

276 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

277) 

278def test_210_dictionary_entry_accretion_via_update( module_qname, class_name ): 

279 ''' Dictionary accretes entries via update. ''' 

280 module = cache_import_module( module_qname ) 

281 factory = getattr( module, class_name ) 

282 posargs, nomargs = select_arguments( class_name ) 

283 dct = factory( *posargs, **nomargs ) 

284 dct.update( *simple_posargs, **simple_nomargs ) 

285 with pytest.raises( exceptions.IndelibleEntryError ): 

286 del dct[ 'foo' ] 

287 with pytest.raises( exceptions.IndelibleEntryError ): 

288 dct[ 'foo' ] = 666 

289 dct[ 'baz' ] = 43 

290 with pytest.raises( exceptions.IndelibleEntryError ): 

291 del dct[ 'baz' ] 

292 with pytest.raises( exceptions.IndelibleEntryError ): 

293 dct[ 'baz' ] = 42 

294 

295 

296@pytest.mark.parametrize( 

297 'module_qname, class_name', 

298 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

299) 

300def test_220_dictionary_iterability( module_qname, class_name ): 

301 ''' Dictionary is iterable. ''' 

302 module = cache_import_module( module_qname ) 

303 factory = getattr( module, class_name ) 

304 posargs, nomargs = select_arguments( class_name ) 

305 dct = factory( *posargs, **nomargs ) 

306 dct.update( *simple_posargs, **simple_nomargs ) 

307 assert frozenset( dct.keys( ) ) == frozenset( iter( dct ) ) 

308 assert tuple( dct.items( ) ) == tuple( zip( dct.keys( ), dct.values( ) ) ) 

309 

310 

311@pytest.mark.parametrize( 

312 'module_qname, class_name', 

313 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

314) 

315def test_221_dictionary_measurability( module_qname, class_name ): 

316 ''' Dictionary is measurable. ''' 

317 module = cache_import_module( module_qname ) 

318 factory = getattr( module, class_name ) 

319 posargs, nomargs = select_arguments( class_name ) 

320 dct = factory( *posargs, **nomargs ) 

321 dct.update( *simple_posargs, **simple_nomargs ) 

322 assert len( dct.keys( ) ) == len( dct ) 

323 assert len( dct.items( ) ) == len( dct ) 

324 assert len( dct.values( ) ) == len( dct ) 

325 

326 

327@pytest.mark.parametrize( 

328 'module_qname, class_name', 

329 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

330) 

331def test_225_dictionary_equality( module_qname, class_name ): 

332 ''' Dictionary is equivalent to another dictionary with same values. ''' 

333 module = cache_import_module( module_qname ) 

334 factory = getattr( module, class_name ) 

335 posargs, nomargs = select_arguments( class_name ) 

336 dct1 = factory( *posargs, **nomargs ) 

337 dct1.update( *simple_posargs, **simple_nomargs ) 

338 dct2 = dct1.copy( ) 

339 dct3 = dict( dct1 ) 

340 assert dct1 == dct2 

341 assert dct2 == dct1 

342 assert dct1 == dct3 

343 assert dct3 == dct1 

344 assert not ( dct1 == -1 ) # pylint: disable=superfluous-parens 

345 assert dct1 != -1 

346 assert dct1 != ( ) 

347 dct2[ 'baz' ] = 43 

348 assert dct1 != dct2 

349 assert dct2 != dct1 

350 

351 

352@pytest.mark.parametrize( 

353 'module_qname, class_name', 

354 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

355) 

356def test_230_string_representation( module_qname, class_name ): 

357 ''' Dictionary has expected string representations. ''' 

358 module = cache_import_module( module_qname ) 

359 factory = getattr( module, class_name ) 

360 posargs, nomargs = select_arguments( class_name ) 

361 dct = factory( *posargs, **nomargs ) 

362 dct.update( *simple_posargs, **simple_nomargs ) 

363 cdct = dict( dct ) 

364 assert str( cdct ) == str( dct ) 

365 assert str( cdct ) in repr( dct ) 

366 assert base.discover_fqname( dct ) in repr( dct ) 

367 

368 

369@pytest.mark.parametrize( 

370 'module_qname, class_name', 

371 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

372) 

373def test_230_dictionary_entry_optional_retrieval( module_qname, class_name ): 

374 ''' Default value on optional access of dictionary entry. ''' 

375 module = cache_import_module( module_qname ) 

376 factory = getattr( module, class_name ) 

377 posargs, nomargs = select_arguments( class_name ) 

378 dct = factory( *posargs, **nomargs ) 

379 dct.update( *simple_posargs, **simple_nomargs ) 

380 assert None is dct.get( 'baz' ) 

381 assert -1 == dct.get( 'baz', -1 ) 

382 assert -1 == dct.get( 'baz', default = -1 ) 

383 assert 1 == dct.get( 'foo' ) 

384 assert 1 == dct.get( 'foo', -1 ) 

385 

386 

387@pytest.mark.parametrize( 

388 'module_qname, class_name', 

389 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

390) 

391def test_230_subclasses_abc_dictionary( module_qname, class_name ): 

392 ''' Subclasses 'collections.abc.Mapping'. ''' 

393 from collections.abc import Mapping as AbstractDictionary 

394 module = cache_import_module( module_qname ) 

395 factory = getattr( module, class_name ) 

396 issubclass( factory, AbstractDictionary ) 

397 

398 

399@pytest.mark.parametrize( 

400 'module_qname, class_name', 

401 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

402) 

403def test_900_docstring_sanity( module_qname, class_name ): 

404 ''' Class has valid docstring. ''' 

405 module = cache_import_module( module_qname ) 

406 factory = getattr( module, class_name ) 

407 assert hasattr( factory, '__doc__' ) 

408 assert isinstance( factory.__doc__, str ) 

409 assert factory.__doc__ 

410 

411 

412# TODO: Dictionary description. 

413 

414 

415@pytest.mark.parametrize( 

416 'module_qname, class_name', 

417 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

418) 

419def test_902_docstring_mentions_accretion( module_qname, class_name ): 

420 ''' Class docstring mentions accretion. ''' 

421 module = cache_import_module( module_qname ) 

422 factory = getattr( module, class_name ) 

423 fragment = base.generate_docstring( 'instance attributes accretion' ) 

424 assert fragment in factory.__doc__ 

425 

426 

427@pytest.mark.parametrize( 

428 'module_qname, class_name', 

429 product( THESE_CONCEALMENT_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

430) 

431def test_910_docstring_mentions_concealment( module_qname, class_name ): 

432 ''' Class docstring mentions concealment. ''' 

433 module = cache_import_module( module_qname ) 

434 factory = getattr( module, class_name ) 

435 fragment = base.generate_docstring( 'instance attributes concealment' ) 

436 assert fragment in factory.__doc__ 

437 

438 

439@pytest.mark.parametrize( 

440 'module_qname, class_name', 

441 product( THESE_NONCONCEALMENT_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

442) 

443def test_911_docstring_not_mentions_concealment( module_qname, class_name ): 

444 ''' Class docstring does not mention concealment. ''' 

445 module = cache_import_module( module_qname ) 

446 factory = getattr( module, class_name ) 

447 fragment = base.generate_docstring( 'instance attributes concealment' ) 

448 assert fragment not in factory.__doc__ 

449 

450 

451@pytest.mark.parametrize( 

452 'module_qname, class_name', 

453 product( THESE_PROTECTION_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

454) 

455def test_930_docstring_mentions_protection( module_qname, class_name ): 

456 ''' Class docstring mentions protection. ''' 

457 module = cache_import_module( module_qname ) 

458 factory = getattr( module, class_name ) 

459 fragment = base.generate_docstring( 'protection of class' ) 

460 assert fragment in factory.__doc__ 

461 

462 

463@pytest.mark.parametrize( 

464 'module_qname, class_name', 

465 product( THESE_NONPROTECTION_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

466) 

467def test_931_docstring_not_mentions_protection( module_qname, class_name ): 

468 ''' Class docstring does not mention protection. ''' 

469 module = cache_import_module( module_qname ) 

470 factory = getattr( module, class_name ) 

471 fragment = base.generate_docstring( 'protection of class' ) 

472 assert fragment not in factory.__doc__