Coverage for tests/test_000_accretive/test_100_classes.py: 100%
176 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 class factory classes. '''
23# mypy: ignore-errors
24# pylint: disable=magic-value-comparison,protected-access
27import pytest
29from itertools import product
31from . import (
32 CONCEALMENT_PACKAGES_NAMES,
33 MODULES_QNAMES,
34 PACKAGE_NAME,
35 PROTECTION_PACKAGES_NAMES,
36 cache_import_module,
37)
40THESE_MODULE_QNAMES = tuple(
41 name for name in MODULES_QNAMES if name.endswith( '.classes' ) )
42THESE_CONCEALMENT_MODULE_QNAMES = tuple(
43 name for name in THESE_MODULE_QNAMES
44 if name.startswith( CONCEALMENT_PACKAGES_NAMES ) )
45THESE_NONCONCEALMENT_MODULE_QNAMES = tuple(
46 name for name in THESE_MODULE_QNAMES
47 if not name.startswith( CONCEALMENT_PACKAGES_NAMES ) )
48THESE_PROTECTION_MODULE_QNAMES = tuple(
49 name for name in THESE_MODULE_QNAMES
50 if name.startswith( PROTECTION_PACKAGES_NAMES ) )
51THESE_NONPROTECTION_MODULE_QNAMES = tuple(
52 name for name in THESE_MODULE_QNAMES
53 if not name.startswith( PROTECTION_PACKAGES_NAMES ) )
54ABC_FACTORIES_NAMES = ( 'ABCFactory', )
55THESE_CLASSES_NAMES = ( 'Class', *ABC_FACTORIES_NAMES, )
56NONABC_FACTORIES_NAMES = tuple(
57 name for name in THESE_CLASSES_NAMES
58 if name not in ABC_FACTORIES_NAMES )
60base = cache_import_module( f"{PACKAGE_NAME}.__" )
61exceptions = cache_import_module( f"{PACKAGE_NAME}.exceptions" )
64@pytest.mark.parametrize(
65 'module_qname, class_name',
66 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
67)
68def test_100_instantiation( module_qname, class_name ):
69 ''' Class instantiates. '''
70 module = cache_import_module( module_qname )
71 class_factory_class = getattr( module, class_name )
73 class Object( metaclass = class_factory_class ):
74 ''' test '''
76 assert isinstance( Object, class_factory_class )
79@pytest.mark.parametrize(
80 'module_qname, class_name',
81 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
82)
83def test_101_accretion( module_qname, class_name ):
84 ''' Class accretes attributes. '''
85 module = cache_import_module( module_qname )
86 class_factory_class = getattr( module, class_name )
88 class Object( metaclass = class_factory_class ):
89 ''' test '''
90 attr = 42
92 with pytest.raises( exceptions.IndelibleAttributeError ):
93 Object.attr = -1
94 assert 42 == Object.attr
95 with pytest.raises( exceptions.IndelibleAttributeError ):
96 del Object.attr
97 assert 42 == Object.attr
98 Object.accreted_attr = 'foo'
99 assert 'foo' == Object.accreted_attr
100 with pytest.raises( exceptions.IndelibleAttributeError ):
101 Object.accreted_attr = 'bar'
102 assert 'foo' == Object.accreted_attr
103 with pytest.raises( exceptions.IndelibleAttributeError ):
104 del Object.accreted_attr
105 assert 'foo' == Object.accreted_attr
108@pytest.mark.parametrize(
109 'module_qname, class_name',
110 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
111)
112def test_102_docstring_assignment( module_qname, class_name ):
113 ''' Class has dynamically-assigned docstring. '''
114 module = cache_import_module( module_qname )
115 class_factory_class = getattr( module, class_name )
117 class Object( metaclass = class_factory_class, docstring = 'dynamic' ):
118 ''' test '''
119 attr = 42
121 assert 'test' != Object.__doc__
122 assert 'dynamic' == Object.__doc__
125@pytest.mark.parametrize(
126 'module_qname, class_name',
127 product( THESE_CONCEALMENT_MODULE_QNAMES, THESE_CLASSES_NAMES )
128)
129def test_110_attribute_concealment( module_qname, class_name ):
130 ''' Class conceals attributes. '''
131 module = cache_import_module( module_qname )
132 class_factory_class = getattr( module, class_name )
134 class Object( metaclass = class_factory_class ):
135 ''' test '''
136 _class_attribute_visibility_includes_ = frozenset( ( '_private', ) )
138 assert not dir( Object )
139 Object.public = 42
140 assert 'public' in dir( Object )
141 Object._nonpublic = 3.1415926535
142 assert '_nonpublic' not in dir( Object )
143 assert '_private' not in dir( Object )
144 Object._private = 'foo'
145 assert '_private' in dir( Object )
148@pytest.mark.parametrize(
149 'module_qname, class_name',
150 product( THESE_NONCONCEALMENT_MODULE_QNAMES, THESE_CLASSES_NAMES )
151)
152def test_111_attribute_nonconcealment( module_qname, class_name ):
153 ''' Class does not conceal attributes. '''
154 module = cache_import_module( module_qname )
155 class_factory_class = getattr( module, class_name )
157 class Object( metaclass = class_factory_class ):
158 ''' test '''
159 _class_attribute_visibility_includes_ = frozenset( ( '_private', ) )
161 assert '_class_attribute_visibility_includes_' in dir( Object )
162 Object.public = 42
163 assert 'public' in dir( Object )
164 Object._nonpublic = 3.1415926535
165 assert '_nonpublic' in dir( Object )
166 assert '_private' not in dir( Object )
167 Object._private = 'foo'
168 assert '_private' in dir( Object )
171@pytest.mark.parametrize(
172 'module_qname, class_name',
173 product( THESE_PROTECTION_MODULE_QNAMES, THESE_CLASSES_NAMES )
174)
175def test_150_class_attribute_protection( module_qname, class_name ):
176 ''' Class attributes are protected. '''
177 module = cache_import_module( module_qname )
178 class_factory_class = getattr( module, class_name )
179 with pytest.raises( exceptions.IndelibleAttributeError ):
180 class_factory_class.__setattr__ = None
181 with pytest.raises( exceptions.IndelibleAttributeError ):
182 del class_factory_class.__setattr__
183 class_factory_class.foo = 42
184 with pytest.raises( exceptions.IndelibleAttributeError ):
185 class_factory_class.foo = -1
186 with pytest.raises( exceptions.IndelibleAttributeError ):
187 del class_factory_class.foo
188 # Cleanup.
189 type.__delattr__( class_factory_class, 'foo' )
192@pytest.mark.parametrize(
193 'module_qname, class_name',
194 product( THESE_NONPROTECTION_MODULE_QNAMES, THESE_CLASSES_NAMES )
195)
196def test_151_class_attribute_nonprotection( module_qname, class_name ):
197 ''' Class attributes are not protected. '''
198 module = cache_import_module( module_qname )
199 class_factory_class = getattr( module, class_name )
200 # Note: Do not mess with '__delattr__' or '__setattr__' as part of testing.
201 # The functions on the class are restored as descriptor wrappers.
202 # This breaks resolution of these class methods.
203 class_factory_class.foo = 42
204 assert 42 == class_factory_class.foo
205 class_factory_class.foo = -1
206 assert -1 == class_factory_class.foo
207 del class_factory_class.foo
208 assert not hasattr( class_factory_class, 'foo' )
211@pytest.mark.parametrize(
212 'module_qname, class_name',
213 product( THESE_MODULE_QNAMES, ABC_FACTORIES_NAMES )
214)
215def test_200_abc_mutation_allowance( module_qname, class_name ):
216 ''' Class allows mutation of ABC machinery. '''
217 from collections.abc import Mapping
218 from platform import python_implementation
219 module = cache_import_module( module_qname )
220 class_factory_class = getattr( module, class_name )
222 class Object( metaclass = class_factory_class ):
223 ''' test '''
225 class Dict( # pylint: disable=abstract-method,unused-variable
226 Object, Mapping
227 ):
228 ''' test '''
230 python_kind = python_implementation( )
231 assert hasattr( Object, '__abstractmethods__' )
232 if python_kind in ( 'CPython', ):
233 assert hasattr( Object, '_abc_impl' )
236@pytest.mark.parametrize(
237 'module_qname, class_name',
238 product( THESE_MODULE_QNAMES, NONABC_FACTORIES_NAMES )
239)
240def test_201_abc_mutation_prevention( module_qname, class_name ):
241 ''' Class prevents mutation of ABC machinery. '''
242 from abc import ABCMeta
243 from collections.abc import Mapping
244 module = cache_import_module( module_qname )
245 class_factory_class = getattr( module, class_name )
247 class Class( class_factory_class, ABCMeta ):
248 ''' test '''
250 class Object( metaclass = Class ):
251 ''' test '''
253 with pytest.raises( exceptions.IndelibleAttributeError ):
255 class Dict( # pylint: disable=abstract-method,unused-variable
256 Object, Mapping
257 ):
258 ''' test '''
261# TODO? String representations.
264@pytest.mark.parametrize(
265 'module_qname, class_name',
266 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
267)
268def test_900_docstring_sanity( module_qname, class_name ):
269 ''' Class has valid docstring. '''
270 module = cache_import_module( module_qname )
271 class_factory_class = getattr( module, class_name )
272 assert hasattr( class_factory_class, '__doc__' )
273 assert isinstance( class_factory_class.__doc__, str )
274 assert class_factory_class.__doc__
277@pytest.mark.parametrize(
278 'module_qname, class_name',
279 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
280)
281def test_901_docstring_describes_cfc( module_qname, class_name ):
282 ''' Class docstring describes class factory class. '''
283 module = cache_import_module( module_qname )
284 class_factory_class = getattr( module, class_name )
285 fragment = base.generate_docstring( 'description of class factory class' )
286 assert fragment in class_factory_class.__doc__
289@pytest.mark.parametrize(
290 'module_qname, class_name',
291 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
292)
293def test_902_docstring_mentions_accretion( module_qname, class_name ):
294 ''' Class docstring mentions accretion. '''
295 module = cache_import_module( module_qname )
296 class_factory_class = getattr( module, class_name )
297 fragment = base.generate_docstring( 'class attributes accretion' )
298 assert fragment in class_factory_class.__doc__
301@pytest.mark.parametrize(
302 'module_qname, class_name',
303 product( THESE_CONCEALMENT_MODULE_QNAMES, THESE_CLASSES_NAMES )
304)
305def test_910_docstring_mentions_concealment( module_qname, class_name ):
306 ''' Class docstring mentions concealment. '''
307 module = cache_import_module( module_qname )
308 class_factory_class = getattr( module, class_name )
309 fragment = base.generate_docstring( 'class attributes concealment' )
310 assert fragment in class_factory_class.__doc__
313@pytest.mark.parametrize(
314 'module_qname, class_name',
315 product( THESE_NONCONCEALMENT_MODULE_QNAMES, THESE_CLASSES_NAMES )
316)
317def test_911_docstring_not_mentions_concealment( module_qname, class_name ):
318 ''' Class docstring does not mention concealment. '''
319 module = cache_import_module( module_qname )
320 class_factory_class = getattr( module, class_name )
321 fragment = base.generate_docstring( 'class attributes concealment' )
322 assert fragment not in class_factory_class.__doc__
325@pytest.mark.parametrize(
326 'module_qname, class_name',
327 product( THESE_PROTECTION_MODULE_QNAMES, THESE_CLASSES_NAMES )
328)
329def test_930_docstring_mentions_protection( module_qname, class_name ):
330 ''' Class docstring mentions protection. '''
331 module = cache_import_module( module_qname )
332 class_factory_class = getattr( module, class_name )
333 fragment = base.generate_docstring( 'protection of class factory class' )
334 assert fragment in class_factory_class.__doc__
337@pytest.mark.parametrize(
338 'module_qname, class_name',
339 product( THESE_NONPROTECTION_MODULE_QNAMES, THESE_CLASSES_NAMES )
340)
341def test_931_docstring_not_mentions_protection( module_qname, class_name ):
342 ''' Class docstring does not mention protection. '''
343 module = cache_import_module( module_qname )
344 class_factory_class = getattr( module, class_name )
345 fragment = base.generate_docstring( 'protection of class factory class' )
346 assert fragment not in class_factory_class.__doc__
349@pytest.mark.parametrize(
350 'module_qname, class_name',
351 product( THESE_MODULE_QNAMES, ABC_FACTORIES_NAMES )
352)
353def test_950_docstring_mentions_abc_exemption( module_qname, class_name ):
354 ''' Class docstring mentions ABC attributes exemption. '''
355 module = cache_import_module( module_qname )
356 class_factory_class = getattr( module, class_name )
357 fragment = base.generate_docstring( 'abc attributes exemption' )
358 assert fragment in class_factory_class.__doc__
361@pytest.mark.parametrize(
362 'module_qname, class_name',
363 product( THESE_MODULE_QNAMES, NONABC_FACTORIES_NAMES )
364)
365def test_951_docstring_not_mentions_abc_exemption( module_qname, class_name ):
366 ''' Class docstring does not mention ABC attributes exemption. '''
367 module = cache_import_module( module_qname )
368 class_factory_class = getattr( module, class_name )
369 fragment = base.generate_docstring( 'abc attributes exemption' )
370 assert fragment not in class_factory_class.__doc__