Coverage for tests/test_000_frigid/test_200_objects.py: 100%
176 statements
« prev ^ index » next coverage.py v7.6.8, created at 2024-12-05 03:47 +0000
« prev ^ index » next coverage.py v7.6.8, created at 2024-12-05 03:47 +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 objects. '''
23# mypy: ignore-errors
24# pylint: disable=attribute-defined-outside-init
25# pylint: disable=invalid-name,magic-value-comparison
26# pylint: disable=missing-class-docstring,protected-access,unused-variable
29import pytest
31from dataclasses import dataclass
32from itertools import product
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( '.objects' ) )
43THESE_CLASSES_NAMES = ( 'Object', )
45base = cache_import_module( f"{PACKAGE_NAME}.__" )
46exceptions = cache_import_module( f"{PACKAGE_NAME}.exceptions" )
49@pytest.mark.parametrize(
50 'module_qname, class_name',
51 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
52)
53def test_100_instantiation( module_qname, class_name ):
54 ''' Class instantiates. '''
55 module = cache_import_module( module_qname )
56 Object = getattr( module, class_name )
57 obj = Object( )
58 assert isinstance( obj, Object )
61@pytest.mark.parametrize(
62 'module_qname, class_name',
63 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
64)
65def test_101_immutability( module_qname, class_name ):
66 ''' Object prevents attribute modification after initialization. '''
67 module = cache_import_module( module_qname )
68 Object = getattr( module, class_name )
70 class Example( Object ):
71 def __init__( self ):
72 self.value = 42
73 super( ).__init__( )
75 obj = Example( )
76 assert 42 == obj.value
77 with pytest.raises( exceptions.AttributeImmutabilityError ):
78 obj.value = -1
79 assert 42 == obj.value
80 with pytest.raises( exceptions.AttributeImmutabilityError ):
81 obj.new_attr = 'test'
82 with pytest.raises( exceptions.AttributeImmutabilityError ):
83 del obj.value
86@pytest.mark.parametrize(
87 'module_qname, class_name',
88 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
89)
90def test_102_string_representation( module_qname, class_name ):
91 ''' Object has expected string representations. '''
92 module = cache_import_module( module_qname )
93 factory = getattr( module, class_name )
94 obj = factory( )
95 assert base.calculate_fqname( obj ) in repr( obj )
98def test_200_immutable_decorator_basic( ):
99 ''' Decorator makes regular class immutable. '''
100 module = cache_import_module( f"{PACKAGE_NAME}.objects" )
102 @module.immutable
103 class Example:
104 def __init__( self ):
105 self.value = 42
107 obj = Example( )
108 assert 42 == obj.value
109 with pytest.raises( exceptions.AttributeImmutabilityError ):
110 obj.value = 24
111 with pytest.raises( exceptions.AttributeImmutabilityError ):
112 obj.new_attr = 'test'
113 with pytest.raises( exceptions.AttributeImmutabilityError ):
114 del obj.value
117def test_201_immutable_decorator_with_dataclass( ):
118 ''' Decorator works with dataclass. '''
119 module = cache_import_module( f"{PACKAGE_NAME}.objects" )
121 @module.immutable
122 @dataclass( kw_only = True )
123 class Example:
124 x: int
125 y: str = 'default'
127 obj = Example( x = 42 )
128 assert 42 == obj.x
129 assert 'default' == obj.y
130 with pytest.raises( exceptions.AttributeImmutabilityError ):
131 obj.x = 24
132 with pytest.raises( exceptions.AttributeImmutabilityError ):
133 obj.y = 'changed'
136def test_202_immutable_decorator_inheritance( ):
137 ''' Decorator properly handles inheritance. '''
138 module = cache_import_module( f"{PACKAGE_NAME}.objects" )
140 @module.immutable
141 class Base:
142 def __init__( self ):
143 self.base_attr = 'base'
144 super( ).__init__( )
146 class Derived( Base ):
147 def __init__( self ):
148 self.derived_attr = 'derived'
149 super( ).__init__( )
151 obj = Derived( )
152 assert 'base' == obj.base_attr
153 assert 'derived' == obj.derived_attr
154 with pytest.raises( exceptions.AttributeImmutabilityError ):
155 obj.base_attr = 'modified'
156 with pytest.raises( exceptions.AttributeImmutabilityError ):
157 obj.derived_attr = 'modified'
160def test_203_immutable_decorator_compatibility( ):
161 ''' Decorator raises error for incompatible classes. '''
162 module = cache_import_module( f"{PACKAGE_NAME}.objects" )
164 with pytest.raises( exceptions.DecoratorCompatibilityError ):
165 @module.immutable
166 class BadExample:
167 def __setattr__( self, name, value ):
168 pass # pragma: no coverage
170 with pytest.raises( exceptions.DecoratorCompatibilityError ):
171 @module.immutable
172 class AnotherBadExample:
173 def __delattr__( self, name ):
174 pass # pragma: no coverage
177def test_204_immutable_decorator_slots( ):
178 ''' Decorator handles classes with slots. '''
179 module = cache_import_module( f"{PACKAGE_NAME}.objects" )
181 @module.immutable
182 class Example:
183 __slots__ = ( 'x', 'y', '_behaviors_' )
185 def __init__( self ):
186 self.x = 1
187 self.y = 2
189 obj = Example( )
190 assert 1 == obj.x
191 assert 2 == obj.y
192 with pytest.raises( exceptions.AttributeImmutabilityError ):
193 obj.x = 3
194 with pytest.raises( exceptions.AttributeImmutabilityError ):
195 obj.y = 4
196 with pytest.raises( exceptions.AttributeImmutabilityError ):
197 del obj.x
200def test_205_immutable_decorator_existing_behaviors( ):
201 ''' Decorator handles classes with existing behaviors. '''
202 module = cache_import_module( f"{PACKAGE_NAME}.objects" )
204 @module.immutable
205 class DictExample:
206 def __init__( self ):
207 self._behaviors_ = { 'existing' }
208 self.value = 42
210 obj1 = DictExample( )
211 assert 'existing' in obj1._behaviors_
212 assert module.__.behavior_label in obj1._behaviors_
213 with pytest.raises( exceptions.AttributeImmutabilityError ):
214 obj1.value = 24
216 @module.immutable
217 class SlotsExample:
218 __slots__ = ( '_behaviors_', 'value' )
220 def __init__( self ):
221 self._behaviors_ = { 'existing' }
222 self.value = 42
224 obj2 = SlotsExample( )
225 assert 'existing' in obj2._behaviors_
226 assert module.__.behavior_label in obj2._behaviors_
227 with pytest.raises( exceptions.AttributeImmutabilityError ):
228 obj2.value = 24
231def test_206_immutable_decorator_mixed_slots_dict( ):
232 ''' Decorator handles classes with both slots and dict. '''
233 module = cache_import_module( f"{PACKAGE_NAME}.objects" )
235 @module.immutable
236 class Example:
237 __slots__ = ( 'x', 'y', '__dict__' )
239 def __init__( self ):
240 self.x = 1
241 self.y = 2
242 self.z = 3 # Goes to __dict__
244 obj = Example( )
245 assert 1 == obj.x
246 assert 2 == obj.y
247 assert 3 == obj.z # pylint: disable=no-member
248 with pytest.raises( exceptions.AttributeImmutabilityError ):
249 obj.x = 4
250 with pytest.raises( exceptions.AttributeImmutabilityError ):
251 obj.z = 4
254def test_207_immutable_decorator_initialization_deletion( ):
255 ''' Decorator allows deletion during initialization. '''
256 module = cache_import_module( f"{PACKAGE_NAME}.objects" )
258 @module.immutable
259 class Example:
260 __slots__ = ( 'x', '_behaviors_' )
262 def __init__( self ):
263 self.x = 1
264 del self.x
266 obj = Example( )
267 with pytest.raises( AttributeError ):
268 _ = obj.x
269 with pytest.raises( exceptions.AttributeImmutabilityError ):
270 del obj._behaviors_ # pylint: disable=no-member
273@pytest.mark.parametrize(
274 'module_qname, class_name',
275 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
276)
277def test_900_docstring_sanity( module_qname, class_name ):
278 ''' Class has valid docstring. '''
279 module = cache_import_module( module_qname )
280 Object = getattr( module, class_name )
281 assert hasattr( Object, '__doc__' )
282 assert isinstance( Object.__doc__, str )
283 assert Object.__doc__
286@pytest.mark.parametrize(
287 'module_qname, class_name',
288 product( THESE_MODULE_QNAMES, THESE_CLASSES_NAMES )
289)
290def test_902_docstring_mentions_immutability( module_qname, class_name ):
291 ''' Class docstring mentions immutability. '''
292 module = cache_import_module( module_qname )
293 Object = getattr( module, class_name )
294 fragment = base.generate_docstring( 'instance attributes immutability' )
295 assert fragment in Object.__doc__