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

444 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2024-11-20 01:33 +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# pylint: disable=too-many-locals,too-many-statements,unnecessary-dunder-call 

27 

28 

29import pytest 

30 

31from itertools import product 

32from types import MappingProxyType as DictionaryProxy 

33 

34from . import ( 

35 MODULES_QNAMES, 

36 PACKAGE_NAME, 

37 cache_import_module, 

38) 

39 

40 

41THESE_MODULE_QNAMES = tuple( 

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

43INITARGS_NAMES = ( 'Dictionary', ) 

44VALIDATOR_NAMES = ( 'ValidatorDictionary', 'ProducerValidatorDictionary' ) 

45PRODUCER_NAMES = ( 'ProducerDictionary', 'ProducerValidatorDictionary' ) 

46THESE_CLASSES_NAMES = ( *INITARGS_NAMES, *PRODUCER_NAMES, *VALIDATOR_NAMES ) 

47PRODUCER_VALIDATOR_NAMES = tuple( 

48 name for name in THESE_CLASSES_NAMES 

49 if name in PRODUCER_NAMES and name in VALIDATOR_NAMES ) 

50 

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

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

53 

54 

55def select_arguments( class_name ): 

56 ''' Chooses initializer arguments depending on class. ''' 

57 if class_name in PRODUCER_NAMES: 

58 if class_name in VALIDATOR_NAMES: 

59 return ( list, lambda k, v: isinstance( v, list ), ), { } 

60 return ( list, ), { } 

61 if class_name in VALIDATOR_NAMES: 

62 return ( lambda k, v: isinstance( v, int ), ), { } 

63 return ( ), { } 

64 

65 

66def select_simple_arguments( class_name ): 

67 ''' Choose simple test arguments depending on class. ''' 

68 posargs = ( ( ( 'foo', 1 ), ( 'bar', 2 ) ), { 'unicorn': True } ) 

69 nomargs = DictionaryProxy( dict( orb = False ) ) 

70 if class_name in PRODUCER_NAMES: 

71 if class_name in VALIDATOR_NAMES: 

72 posargs = ( 

73 ( ( 'foo', [ 1 ] ), ( 'bar', [ 2 ] ) ), { 'unicorn': [ ] } ) 

74 nomargs = DictionaryProxy( dict( orb = [ ] ) ) 

75 return posargs, nomargs 

76 return posargs, nomargs 

77 if class_name in VALIDATOR_NAMES: 

78 posargs = ( ( ( 'foo', 1 ), ( 'bar', 2 ) ), { 'unicorn': 42 } ) 

79 nomargs = DictionaryProxy( dict( orb = 84 ) ) 

80 return posargs, nomargs 

81 return posargs, nomargs 

82 

83 

84@pytest.mark.parametrize( 

85 'module_qname, class_name', 

86 product( THESE_MODULE_QNAMES, INITARGS_NAMES ) 

87) 

88def test_100_instantiation( module_qname, class_name ): 

89 ''' Class instantiates. ''' 

90 module = cache_import_module( module_qname ) 

91 factory = getattr( module, class_name ) 

92 dct1 = factory( ) 

93 assert isinstance( dct1, factory ) 

94 dct2 = factory( 

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

96 assert isinstance( dct2, factory ) 

97 assert 1 == dct2[ 'foo' ] 

98 assert 2 == dct2[ 'bar' ] 

99 assert dct2[ 'unicorn' ] 

100 assert not dct2[ 'orb' ] 

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

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

103 

104 

105@pytest.mark.parametrize( 

106 'module_qname, class_name', 

107 product( THESE_MODULE_QNAMES, PRODUCER_NAMES ) 

108) 

109def test_101_instantiation( module_qname, class_name ): 

110 ''' Producer class instantiates. ''' 

111 module = cache_import_module( module_qname ) 

112 factory = getattr( module, class_name ) 

113 posargs, nomargs = select_arguments( class_name ) 

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

115 assert isinstance( dct, factory ) 

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

117 

118 

119@pytest.mark.parametrize( 

120 'module_qname, class_name', 

121 product( THESE_MODULE_QNAMES, VALIDATOR_NAMES ) 

122) 

123def test_102_instantiation( module_qname, class_name ): 

124 ''' Validator class instantiates. ''' 

125 module = cache_import_module( module_qname ) 

126 factory = getattr( module, class_name ) 

127 posargs, nomargs = select_arguments( class_name ) 

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

129 assert isinstance( dct, factory ) 

130 value = [ ] if class_name in PRODUCER_NAMES else 42 

131 dct[ 'a' ] = value 

132 with pytest.raises( exceptions.EntryValidityError ): 

133 dct[ 'b' ] = 'invalid value' 

134 

135 

136@pytest.mark.parametrize( 

137 'module_qname, class_name', 

138 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

139) 

140def test_110_attribute_accretion( module_qname, class_name ): 

141 ''' Dictionary accretes attributes. ''' 

142 module = cache_import_module( module_qname ) 

143 factory = getattr( module, class_name ) 

144 posargs, nomargs = select_arguments( class_name ) 

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

146 obj.attr = 42 

147 with pytest.raises( exceptions.AttributeImmutabilityError ): 

148 obj.attr = -1 

149 assert 42 == obj.attr 

150 with pytest.raises( exceptions.AttributeImmutabilityError ): 

151 del obj.attr 

152 assert 42 == obj.attr 

153 

154 

155@pytest.mark.parametrize( 

156 'module_qname, class_name', 

157 product( THESE_MODULE_QNAMES, INITARGS_NAMES ) 

158) 

159def test_200_dictionary_entry_accretion( module_qname, class_name ): 

160 ''' Dictionary accretes entries. ''' 

161 module = cache_import_module( module_qname ) 

162 factory = getattr( module, class_name ) 

163 simple_posargs, simple_nomargs = select_simple_arguments( class_name ) 

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

165 with pytest.raises( exceptions.EntryImmutabilityError ): 

166 del dct[ 'foo' ] 

167 with pytest.raises( exceptions.EntryImmutabilityError ): 

168 dct[ 'foo' ] = 666 

169 dct[ 'baz' ] = 43 

170 with pytest.raises( exceptions.EntryImmutabilityError ): 

171 del dct[ 'baz' ] 

172 with pytest.raises( exceptions.EntryImmutabilityError ): 

173 dct[ 'baz' ] = 42 

174 

175 

176@pytest.mark.parametrize( 

177 'module_qname, class_name', 

178 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

179) 

180def test_150_setdefault_preserves_existing_entry( module_qname, class_name ): 

181 ''' Setdefault returns existing value without modification. ''' 

182 module = cache_import_module( module_qname ) 

183 factory = getattr( module, class_name ) 

184 posargs, nomargs = select_arguments( class_name ) 

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

186 # Add initial value 

187 if class_name in PRODUCER_NAMES: 

188 dct[ 'a' ] = [ 1 ] 

189 assert [ 1 ] == dct.setdefault( 'a', [ 42 ] ) 

190 assert [ 1 ] == dct[ 'a' ] 

191 else: 

192 dct[ 'a' ] = 1 

193 assert 1 == dct.setdefault( 'a', 42 ) 

194 assert 1 == dct[ 'a' ] 

195 

196 

197@pytest.mark.parametrize( 

198 'module_qname, class_name', 

199 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

200) 

201def test_151_setdefault_adds_missing_entry( module_qname, class_name ): 

202 ''' Setdefault adds new entry for missing key. ''' 

203 module = cache_import_module( module_qname ) 

204 factory = getattr( module, class_name ) 

205 posargs, nomargs = select_arguments( class_name ) 

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

207 if class_name in PRODUCER_NAMES: 

208 assert [ 42 ] == dct.setdefault( 'b', [ 42 ] ) 

209 assert [ 42 ] == dct[ 'b' ] 

210 else: 

211 assert 42 == dct.setdefault( 'b', 42 ) 

212 assert 42 == dct[ 'b' ] 

213 

214 

215@pytest.mark.parametrize( 

216 'module_qname, class_name', 

217 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

218) 

219def test_160_or_combines_dictionaries( module_qname, class_name ): 

220 ''' Dictionary union produces new dictionary with combined entries. ''' 

221 module = cache_import_module( module_qname ) 

222 factory = getattr( module, class_name ) 

223 posargs, nomargs = select_arguments( class_name ) 

224 d1 = factory( *posargs, **nomargs ) 

225 d2 = factory( *posargs, **nomargs ) 

226 d3 = { } 

227 if class_name in PRODUCER_VALIDATOR_NAMES: 

228 d1[ 'a' ] = [ 1 ] 

229 d2[ 'c' ] = [ 4 ] 

230 d3.update( d = [ 5 ], e = [ 6 ] ) 

231 else: 

232 d1[ 'a' ] = 1 

233 d2[ 'c' ] = 4 

234 d3.update( d = 5, e = 6 ) 

235 d4 = d1 | d2 

236 assert isinstance( d4, factory ) 

237 if class_name in PRODUCER_VALIDATOR_NAMES: 

238 assert d4 == { 'a': [ 1 ], 'c': [ 4 ] } 

239 else: assert d4 == { 'a': 1, 'c': 4 } 

240 d5 = d1 | d3 

241 assert isinstance( d5, factory ) 

242 if class_name in PRODUCER_VALIDATOR_NAMES: 

243 assert d5 == { 'a': [ 1 ], 'd': [ 5 ], 'e': [ 6 ] } 

244 else: assert d5 == { 'a': 1, 'd': 5, 'e': 6 } 

245 d6 = d3 | d1 

246 assert isinstance( d6, factory ) 

247 if class_name in PRODUCER_VALIDATOR_NAMES: 

248 assert d6 == { 'a': [ 1 ], 'd': [ 5 ], 'e': [ 6 ] } 

249 else: assert d6 == { 'a': 1, 'd': 5, 'e': 6 } 

250 d7 = factory( *posargs, **nomargs ) 

251 if class_name in PRODUCER_VALIDATOR_NAMES: 

252 d7[ 'a' ] = [ 2 ] 

253 else: d7[ 'a' ] = 2 

254 with pytest.raises( exceptions.EntryImmutabilityError ): 

255 _ = d1 | d7 

256 

257 

258@pytest.mark.parametrize( 

259 'module_qname, class_name', 

260 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

261) 

262def test_161_or_rejects_invalid_operands( module_qname, class_name ): 

263 ''' Dictionary union rejects non-mapping operands. ''' 

264 module = cache_import_module( module_qname ) 

265 factory = getattr( module, class_name ) 

266 posargs, nomargs = select_arguments( class_name ) 

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

268 assert NotImplemented == dct.__or__( [ ] ) 

269 assert NotImplemented == dct.__ror__( [ ] ) 

270 

271 

272@pytest.mark.parametrize( 

273 'module_qname, class_name', 

274 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

275) 

276def test_170_and_intersects_mappings( module_qname, class_name ): 

277 ''' Dictionary intersection with mapping matches key-value pairs. ''' 

278 module = cache_import_module( module_qname ) 

279 factory = getattr( module, class_name ) 

280 posargs, nomargs = select_arguments( class_name ) 

281 d1 = factory( *posargs, **nomargs ) 

282 d2 = factory( *posargs, **nomargs ) 

283 d3 = { } 

284 if class_name in PRODUCER_VALIDATOR_NAMES: 

285 d1.update( a = [ 1 ], b = [ 2 ], c = [ 3 ] ) 

286 d2.update( a = [ 1 ], b = [ 3 ], d = [ 4 ] ) 

287 d3.update( a = [ 1 ], c = [ 3 ], e = [ 5 ] ) 

288 else: 

289 d1.update( a = 1, b = 2, c = 3 ) 

290 d2.update( a = 1, b = 3, d = 4 ) 

291 d3.update( a = 1, c = 3, e = 5 ) 

292 d4 = d1 & d2 

293 assert isinstance( d4, factory ) 

294 if class_name in PRODUCER_VALIDATOR_NAMES: 

295 assert d4 == { 'a': [ 1 ] } 

296 else: assert d4 == { 'a': 1 } 

297 d5 = d1 & d3 

298 assert isinstance( d5, factory ) 

299 if class_name in PRODUCER_VALIDATOR_NAMES: 

300 assert d5 == { 'a': [ 1 ], 'c': [ 3 ] } 

301 else: assert d5 == { 'a': 1, 'c': 3 } 

302 d6 = d3 & d1 

303 assert isinstance( d6, factory ) 

304 if class_name in PRODUCER_VALIDATOR_NAMES: 

305 assert d6 == { 'a': [ 1 ], 'c': [ 3 ] } 

306 else: assert d6 == { 'a': 1, 'c': 3 } 

307 

308 

309@pytest.mark.parametrize( 

310 'module_qname, class_name', 

311 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

312) 

313def test_171_and_filters_by_keys( module_qname, class_name ): 

314 ''' Dictionary intersection with set filters by keys. ''' 

315 module = cache_import_module( module_qname ) 

316 factory = getattr( module, class_name ) 

317 posargs, nomargs = select_arguments( class_name ) 

318 d1 = factory( *posargs, **nomargs ) 

319 if class_name in PRODUCER_VALIDATOR_NAMES: 

320 d1.update( a = [ 1 ], b = [ 2 ], c = [ 3 ] ) 

321 else: d1.update( a = 1, b = 2, c = 3 ) 

322 s1 = { 'a', 'b' } 

323 d2 = d1 & s1 

324 assert isinstance( d2, factory ) 

325 if class_name in PRODUCER_VALIDATOR_NAMES: 

326 assert d2 == { 'a': [ 1 ], 'b': [ 2 ] } 

327 else: assert d2 == { 'a': 1, 'b': 2 } 

328 d3 = d1 & factory( *posargs, x = [ 0 ], a = [ 9 ], b = [ 8 ] ).keys( ) 

329 assert isinstance( d3, factory ) 

330 if class_name in PRODUCER_VALIDATOR_NAMES: 

331 assert d3 == { 'a': [ 1 ], 'b': [ 2 ] } 

332 else: assert d3 == { 'a': 1, 'b': 2 } 

333 d4 = s1 & d1 

334 assert isinstance( d4, factory ) 

335 if class_name in PRODUCER_VALIDATOR_NAMES: 

336 assert d4 == { 'a': [ 1 ], 'b': [ 2 ] } 

337 else: assert d4 == { 'a': 1, 'b': 2 } 

338 

339 

340@pytest.mark.parametrize( 

341 'module_qname, class_name', 

342 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

343) 

344def test_172_and_rejects_invalid_operands( module_qname, class_name ): 

345 ''' Dictionary intersection rejects invalid operands. ''' 

346 module = cache_import_module( module_qname ) 

347 factory = getattr( module, class_name ) 

348 posargs, nomargs = select_arguments( class_name ) 

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

350 assert NotImplemented == dct.__and__( [ ] ) 

351 assert NotImplemented == dct.__rand__( [ ] ) 

352 

353 

354@pytest.mark.parametrize( 

355 'module_qname, class_name', 

356 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

357) 

358def test_180_operations_preserve_accretion( module_qname, class_name ): 

359 ''' Dictionary operations maintain accretive contract. ''' 

360 module = cache_import_module( module_qname ) 

361 factory = getattr( module, class_name ) 

362 posargs, nomargs = select_arguments( class_name ) 

363 d1 = factory( *posargs, **nomargs ) 

364 d2 = factory( *posargs, **nomargs ) 

365 if class_name in PRODUCER_VALIDATOR_NAMES: 

366 d1[ 'a' ] = [ 1 ] 

367 d2[ 'a' ] = [ 2 ] 

368 else: 

369 d1[ 'a' ] = 1 

370 d2[ 'a' ] = 2 

371 with pytest.raises( exceptions.EntryImmutabilityError ): 

372 d1 | d2 # pylint: disable=pointless-statement 

373 d4 = d1 & { 'a' } 

374 with pytest.raises( exceptions.EntryImmutabilityError ): 

375 if class_name in PRODUCER_VALIDATOR_NAMES: 

376 d4[ 'a' ] = [ 3 ] 

377 else: d4[ 'a' ] = 3 

378 

379 

380@pytest.mark.parametrize( 

381 'module_qname, class_name', 

382 product( THESE_MODULE_QNAMES, PRODUCER_NAMES ) 

383) 

384def test_201_producer_dictionary_entry_accretion( module_qname, class_name ): 

385 ''' Producer dictionary accretes entries. ''' 

386 module = cache_import_module( module_qname ) 

387 factory = getattr( module, class_name ) 

388 posargs, nomargs = select_arguments( class_name ) 

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

390 if class_name not in VALIDATOR_NAMES: 

391 dct[ 'baz' ] = 43 

392 else: dct[ 'baz' ] = [ 43 ] 

393 with pytest.raises( exceptions.EntryImmutabilityError ): 

394 del dct[ 'baz' ] 

395 with pytest.raises( exceptions.EntryImmutabilityError ): 

396 if class_name not in VALIDATOR_NAMES: 

397 dct[ 'baz' ] = 42 

398 else: dct[ 'baz' ] = [ 43 ] 

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

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

401 with pytest.raises( exceptions.EntryImmutabilityError ): 

402 del dct[ 'abc' ] 

403 with pytest.raises( exceptions.EntryImmutabilityError ): 

404 if class_name not in VALIDATOR_NAMES: 

405 dct[ 'abc' ] = 666 

406 else: dct[ 'abc' ] = [ 666 ] 

407 

408 

409@pytest.mark.parametrize( 

410 'module_qname, class_name', 

411 product( THESE_MODULE_QNAMES, VALIDATOR_NAMES ) 

412) 

413def test_202_validator_dictionary_entry_accretion( module_qname, class_name ): 

414 ''' Validator dictionary accretes valid entries only. ''' 

415 module = cache_import_module( module_qname ) 

416 factory = getattr( module, class_name ) 

417 posargs, nomargs = select_arguments( class_name ) 

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

419 value = [ ] if class_name in PRODUCER_NAMES else 42 

420 dct[ 'foo' ] = value 

421 with pytest.raises( exceptions.EntryValidityError ): 

422 dct[ 'bar' ] = 'invalid value' 

423 with pytest.raises( exceptions.EntryImmutabilityError ): 

424 dct[ 'foo' ] = value 

425 if class_name in PRODUCER_NAMES: 

426 lst = dct[ 'baz' ] 

427 assert isinstance( lst, list ) 

428 lst.append( 42 ) 

429 assert 42 == dct[ 'baz' ][ 0 ] 

430 

431 

432@pytest.mark.parametrize( 

433 'module_qname, class_name', 

434 product( THESE_MODULE_QNAMES, PRODUCER_VALIDATOR_NAMES ) 

435) 

436def test_205_producer_validator_invalid_production( module_qname, class_name ): 

437 ''' Producer-validator dictionary rejects invalid produced values. ''' 

438 module = cache_import_module( module_qname ) 

439 factory = getattr( module, class_name ) 

440 # Producer that returns invalid values (not a list) 

441 dct = factory( lambda: 42, lambda k, v: isinstance( v, list ) ) 

442 with pytest.raises( exceptions.EntryValidityError ): 

443 _ = dct[ 'foo' ] # Production should fail validation 

444 

445 

446@pytest.mark.parametrize( 

447 'module_qname, class_name', 

448 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

449) 

450def test_210_dictionary_entry_accretion_via_update( module_qname, class_name ): 

451 ''' Dictionary accretes entries via update. ''' 

452 module = cache_import_module( module_qname ) 

453 factory = getattr( module, class_name ) 

454 posargs, nomargs = select_arguments( class_name ) 

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

456 simple_posargs, simple_nomargs = select_simple_arguments( class_name ) 

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

458 with pytest.raises( exceptions.EntryImmutabilityError ): 

459 del dct[ 'foo' ] 

460 with pytest.raises( exceptions.EntryImmutabilityError ): 

461 if class_name not in PRODUCER_VALIDATOR_NAMES: dct[ 'foo' ] = 666 

462 else: dct[ 'foo' ] = [ 666 ] 

463 if class_name not in PRODUCER_VALIDATOR_NAMES: dct[ 'baz' ] = 43 

464 else: dct[ 'baz' ] = [ 43 ] 

465 with pytest.raises( exceptions.EntryImmutabilityError ): 

466 del dct[ 'baz' ] 

467 with pytest.raises( exceptions.EntryImmutabilityError ): 

468 if class_name not in PRODUCER_VALIDATOR_NAMES: dct[ 'baz' ] = 42 

469 else: dct[ 'baz' ] = [ 42 ] 

470 

471 

472@pytest.mark.parametrize( 

473 'module_qname, class_name', 

474 product( THESE_MODULE_QNAMES, VALIDATOR_NAMES ) 

475) 

476def test_211_validator_dictionary_entry_accretion_via_update( 

477 module_qname, class_name 

478): 

479 ''' Validator dictionary accretes valid entries via update. ''' 

480 module = cache_import_module( module_qname ) 

481 factory = getattr( module, class_name ) 

482 posargs, nomargs = select_arguments( class_name ) 

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

484 value = [ ] if class_name in PRODUCER_NAMES else 42 

485 dct.update( foo = value ) 

486 with pytest.raises( exceptions.EntryValidityError ): 

487 dct.update( bar = 'invalid value' ) 

488 with pytest.raises( exceptions.EntryImmutabilityError ): 

489 dct[ 'foo' ] = value 

490 

491 

492@pytest.mark.parametrize( 

493 'module_qname, class_name', 

494 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

495) 

496def test_220_duplication( module_qname, class_name ): 

497 ''' Dictionary is duplicable. ''' 

498 module = cache_import_module( module_qname ) 

499 factory = getattr( module, class_name ) 

500 posargs, nomargs = select_arguments( class_name ) 

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

502 ddct = odct.copy( ) 

503 assert odct == ddct 

504 if class_name in PRODUCER_NAMES: 

505 odct[ 'baz' ] = [ 42 ] 

506 else: odct[ 'baz' ] = 42 

507 assert odct != ddct 

508 

509 

510@pytest.mark.parametrize( 

511 'module_qname, class_name', 

512 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

513) 

514def test_221_dictionary_iterability( module_qname, class_name ): 

515 ''' Dictionary is iterable. ''' 

516 module = cache_import_module( module_qname ) 

517 factory = getattr( module, class_name ) 

518 posargs, nomargs = select_arguments( class_name ) 

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

520 simple_posargs, simple_nomargs = select_simple_arguments( class_name ) 

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

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

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

524 

525 

526@pytest.mark.parametrize( 

527 'module_qname, class_name', 

528 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

529) 

530def test_222_dictionary_measurability( module_qname, class_name ): 

531 ''' Dictionary is measurable. ''' 

532 module = cache_import_module( module_qname ) 

533 factory = getattr( module, class_name ) 

534 posargs, nomargs = select_arguments( class_name ) 

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

536 simple_posargs, simple_nomargs = select_simple_arguments( class_name ) 

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

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

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

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

541 

542 

543@pytest.mark.parametrize( 

544 'module_qname, class_name', 

545 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

546) 

547def test_225_dictionary_equality( 

548 module_qname, class_name 

549): 

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

551 module = cache_import_module( module_qname ) 

552 factory = getattr( module, class_name ) 

553 posargs, nomargs = select_arguments( class_name ) 

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

555 simple_posargs, simple_nomargs = select_simple_arguments( class_name ) 

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

557 dct2 = dct1.copy( ) 

558 dct3 = dict( dct1 ) 

559 assert dct1 == dct2 

560 assert dct2 == dct1 

561 assert dct1 == dct3 

562 assert dct3 == dct1 

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

564 assert dct1 != -1 

565 assert dct1 != ( ) 

566 if class_name not in PRODUCER_VALIDATOR_NAMES: dct2[ 'baz' ] = 43 

567 else: dct2[ 'baz' ] = [ 43 ] 

568 assert dct1 != dct2 

569 assert dct2 != dct1 

570 

571 

572@pytest.mark.parametrize( 

573 'module_qname, class_name', 

574 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

575) 

576def test_230_string_representation( module_qname, class_name ): 

577 ''' Dictionary has expected string representations. ''' 

578 module = cache_import_module( module_qname ) 

579 factory = getattr( module, class_name ) 

580 posargs, nomargs = select_arguments( class_name ) 

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

582 simple_posargs, simple_nomargs = select_simple_arguments( class_name ) 

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

584 cdct = dict( dct ) 

585 assert str( cdct ) == str( dct ) 

586 assert str( cdct ) in repr( dct ) 

587 assert base.calculate_fqname( dct ) in repr( dct ) 

588 

589 

590@pytest.mark.parametrize( 

591 'module_qname, class_name', 

592 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

593) 

594def test_240_dictionary_entry_optional_retrieval( module_qname, class_name ): 

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

596 module = cache_import_module( module_qname ) 

597 factory = getattr( module, class_name ) 

598 posargs, nomargs = select_arguments( class_name ) 

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

600 simple_posargs, simple_nomargs = select_simple_arguments( class_name ) 

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

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

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

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

605 if class_name not in PRODUCER_VALIDATOR_NAMES: 

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

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

608 else: 

609 assert [ 1 ] == dct.get( 'foo' ) 

610 assert [ 1 ] == dct.get( 'foo', -1 ) 

611 

612 

613@pytest.mark.parametrize( 

614 'module_qname, class_name', 

615 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

616) 

617def test_250_with_data( module_qname, class_name ): 

618 ''' Dictionary creates new instance with different data. ''' 

619 module = cache_import_module( module_qname ) 

620 factory = getattr( module, class_name ) 

621 posargs, nomargs = select_arguments( class_name ) 

622 d1 = factory( *posargs, **nomargs ) 

623 if class_name in PRODUCER_VALIDATOR_NAMES: 

624 d1[ 'a' ] = [ 1 ] 

625 d1[ 'b' ] = [ 2 ] 

626 new_data = { 'c': [ 3 ], 'd': [ 4 ] } 

627 else: 

628 d1[ 'a' ] = 1 

629 d1[ 'b' ] = 2 

630 new_data = { 'c': 3, 'd': 4 } 

631 d2 = d1.with_data( new_data ) 

632 assert isinstance( d2, factory ) 

633 assert type( d1 ) is type( d2 ) 

634 assert d1 != d2 

635 if class_name in PRODUCER_VALIDATOR_NAMES: 

636 assert d2 == { 'c': [ 3 ], 'd': [ 4 ] } 

637 else: assert d2 == { 'c': 3, 'd': 4 } 

638 if class_name in PRODUCER_NAMES: 

639 assert isinstance( d2[ 'x' ], list ) 

640 if class_name in VALIDATOR_NAMES: 

641 with pytest.raises( exceptions.EntryValidityError ): 

642 d2[ 'y' ] = 'invalid' 

643 

644 

645@pytest.mark.parametrize( 

646 'module_qname, class_name', 

647 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

648) 

649def test_260_subclasses_abc_dictionary( module_qname, class_name ): 

650 ''' Subclasses 'collections.abc.Mapping'. ''' 

651 from collections.abc import Mapping as AbstractDictionary 

652 module = cache_import_module( module_qname ) 

653 factory = getattr( module, class_name ) 

654 issubclass( factory, AbstractDictionary ) 

655 

656 

657@pytest.mark.parametrize( 

658 'module_qname, class_name', 

659 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES ) 

660) 

661def test_900_docstring_sanity( module_qname, class_name ): 

662 ''' Class has valid docstring. ''' 

663 module = cache_import_module( module_qname ) 

664 factory = getattr( module, class_name ) 

665 assert hasattr( factory, '__doc__' ) 

666 assert isinstance( factory.__doc__, str ) 

667 assert factory.__doc__ 

668 

669 

670# TODO: Dictionary description.