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
« 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 -*-
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
28import pytest
30from itertools import product
31from types import MappingProxyType as DictionaryProxy
33from . import (
34 CONCEALMENT_PACKAGES_NAMES,
35 MODULES_QNAMES,
36 PACKAGE_NAME,
37 PROTECTION_PACKAGES_NAMES,
38 cache_import_module,
39)
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 )
63base = cache_import_module( f"{PACKAGE_NAME}.__" )
64exceptions = cache_import_module( f"{PACKAGE_NAME}.exceptions" )
66simple_posargs = ( ( ( 'foo', 1 ), ( 'bar', 2 ) ), { 'unicorn': True } )
67simple_nomargs = DictionaryProxy( dict( orb = False ) )
70def select_arguments( class_name ):
71 ''' Choose initializer arguments depending on class. '''
72 if class_name in PRODUCER_NAMES: return ( list, ), { }
73 return ( ), { }
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( ) )
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 )
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
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
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 )
155 class Concealer( factory ):
156 ''' test '''
157 _attribute_visibility_includes_ = frozenset( ( '_private', ) )
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 )
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 )
180 class Concealer( factory ):
181 ''' test '''
182 _attribute_visibility_includes_ = frozenset( ( '_private', ) )
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 )
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' )
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' )
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
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
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
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( ) ) )
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 )
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
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 )
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 )
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 )
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__
412# TODO: Dictionary description.
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__
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__
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__
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__
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__