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
« 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 -*-
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#============================================================================#
21''' Assert correct function of dictionaries. '''
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
29import pytest
31from itertools import product
32from types import MappingProxyType as DictionaryProxy
34from . import (
35 MODULES_QNAMES,
36 PACKAGE_NAME,
37 cache_import_module,
38)
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 )
51base = cache_import_module( f"{PACKAGE_NAME}.__" )
52exceptions = cache_import_module( f"{PACKAGE_NAME}.exceptions" )
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 ( ), { }
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
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( ) )
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 )
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'
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
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
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' ]
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' ]
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
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__( [ ] )
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 }
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 }
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__( [ ] )
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
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 ]
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 ]
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
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 ]
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
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
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( ) ) )
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 )
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
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 )
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 )
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'
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 )
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__
670# TODO: Dictionary description.