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

130 statements  

« prev     ^ index     » next       coverage.py v7.5.4, created at 2024-07-04 22:31 +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 internals. ''' 

22 

23# mypy: ignore-errors 

24# pylint: disable=magic-value-comparison,protected-access 

25 

26 

27import pytest 

28 

29from types import MappingProxyType as DictionaryProxy 

30 

31from . import PACKAGE_NAME, cache_import_module 

32 

33 

34MODULE_QNAME = f"{PACKAGE_NAME}.__" 

35CONCEALER_EXTENSIONS_NAMES = ( 

36 'ClassConcealerExtension', 

37 'ConcealerExtension', 

38) 

39MODULE_ATTRIBUTE_NAMES = ( 

40 *CONCEALER_EXTENSIONS_NAMES, 

41 'CoreDictionary', 

42 'Docstring', 

43 'discover_fqname', 

44 'discover_public_attributes', 

45 'generate_docstring', 

46 'reclassify_modules', 

47) 

48 

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

50module = cache_import_module( MODULE_QNAME ) 

51 

52dictionary_posargs = ( ( ( 'foo', 1 ), ( 'bar', 2 ) ), { 'unicorn': True } ) 

53dictionary_nomargs = DictionaryProxy( dict( orb = False ) ) 

54 

55 

56@pytest.mark.parametrize( 'class_name', CONCEALER_EXTENSIONS_NAMES ) 

57def test_100_concealer_extension_instantiation( class_name ): 

58 ''' Class instantiantes. ''' 

59 factory = getattr( module, class_name ) 

60 posargs = ( 'Object', ( ), { } ) if issubclass( factory, type ) else ( ) 

61 obj = factory( *posargs ) 

62 assert isinstance( obj, factory ) 

63 

64 

65@pytest.mark.parametrize( 'class_name', CONCEALER_EXTENSIONS_NAMES ) 

66def test_102_concealer_extension_attribute_concealment( class_name ): 

67 ''' Class conceals attributes. ''' 

68 factory = getattr( module, class_name ) 

69 posargs = ( 'Object', ( ), { } ) if issubclass( factory, type ) else ( ) 

70 concealer_name = ( 

71 '_class_attribute_visibility_includes_' 

72 if issubclass( factory, type ) 

73 else '_attribute_visibility_includes_' ) 

74 obj = factory( *posargs ) 

75 assert not dir( obj ) 

76 obj.public = 42 

77 assert 'public' in dir( obj ) 

78 obj._nonpublic = 3.1415926535 

79 assert '_nonpublic' not in dir( obj ) 

80 setattr( obj, concealer_name, frozenset( ( '_nonpublic', ) ) ) 

81 assert '_nonpublic' in dir( obj ) 

82 assert concealer_name not in dir( obj ) 

83 

84 

85def test_103_class_concealer_extension_creates_classes( ): 

86 ''' Class factory class instances are classes. ''' 

87 from inspect import isclass 

88 factory = module.ClassConcealerExtension 

89 assert issubclass( factory, type ) 

90 obj = factory( 'Object', ( ), { } ) 

91 assert isclass( obj ) 

92 

93 

94def test_200_core_dictionary_instantiation( ): 

95 ''' Class instantiates. ''' 

96 factory = module.CoreDictionary 

97 dct1 = factory( ) 

98 assert isinstance( dct1, factory ) 

99 dct2 = factory( *dictionary_posargs, **dictionary_nomargs ) 

100 assert isinstance( dct2, factory ) 

101 assert 1 == dct2[ 'foo' ] 

102 assert 2 == dct2[ 'bar' ] 

103 assert dct2[ 'unicorn' ] 

104 assert not dct2[ 'orb' ] 

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

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

107 

108 

109def test_201_core_dictionary_duplication( ): 

110 ''' Dictionary is duplicable. ''' 

111 factory = module.CoreDictionary 

112 odct = factory( *dictionary_posargs, **dictionary_nomargs ) 

113 ddct = odct.copy( ) 

114 assert odct == ddct 

115 odct[ 'baz' ] = 42 

116 assert odct != ddct 

117 

118 

119def test_210_core_dictionary_entry_accretion( ): 

120 ''' Dictionary accretes entries. ''' 

121 factory = module.CoreDictionary 

122 dct = factory( *dictionary_posargs, **dictionary_nomargs ) 

123 with pytest.raises( exceptions.IndelibleEntryError ): 

124 dct[ 'foo' ] = 42 

125 with pytest.raises( exceptions.IndelibleEntryError ): 

126 del dct[ 'foo' ] 

127 dct[ 'baz' ] = 3.1415926535 

128 with pytest.raises( exceptions.IndelibleEntryError ): 

129 dct[ 'baz' ] = -1 

130 with pytest.raises( exceptions.IndelibleEntryError ): 

131 del dct[ 'baz' ] 

132 

133 

134def test_211_core_dictionary_entry_accretion_via_update( ): 

135 ''' Dictionary accretes entries via update. ''' 

136 factory = module.CoreDictionary 

137 dct = factory( ) 

138 dct.update( *dictionary_posargs, **dictionary_nomargs ) 

139 with pytest.raises( exceptions.IndelibleEntryError ): 

140 dct[ 'foo' ] = 42 

141 with pytest.raises( exceptions.IndelibleEntryError ): 

142 del dct[ 'foo' ] 

143 dct[ 'baz' ] = 3.1415926535 

144 with pytest.raises( exceptions.IndelibleEntryError ): 

145 dct[ 'baz' ] = -1 

146 with pytest.raises( exceptions.IndelibleEntryError ): 

147 del dct[ 'baz' ] 

148 

149 

150def test_220_core_dictionary_operation_prevention( ): 

151 ''' Dictionary cannot perform entry deletions and mutations. ''' 

152 factory = module.CoreDictionary 

153 dct = factory( *dictionary_posargs, **dictionary_nomargs ) 

154 with pytest.raises( exceptions.InvalidOperationError ): 

155 dct.clear( ) 

156 with pytest.raises( exceptions.InvalidOperationError ): 

157 dct.pop( 'foo' ) 

158 with pytest.raises( exceptions.InvalidOperationError ): 

159 dct.pop( 'foo', default = -1 ) 

160 with pytest.raises( exceptions.InvalidOperationError ): 

161 dct.pop( 'baz' ) 

162 with pytest.raises( exceptions.InvalidOperationError ): 

163 dct.popitem( ) 

164 

165 

166def test_300_fqname_discovery( ): 

167 ''' Fully-qualified name of object is discovered. ''' 

168 assert 'builtins.NoneType' == module.discover_fqname( None ) 

169 assert ( 

170 'builtins.type' 

171 == module.discover_fqname( module.ConcealerExtension ) ) 

172 obj = module.ConcealerExtension( ) 

173 assert ( 

174 f"{MODULE_QNAME}.ConcealerExtension" == module.discover_fqname( obj ) ) 

175 

176 

177@pytest.mark.parametrize( 

178 'provided, expected', 

179 ( 

180 ( { 'foo': 12 }, ( ) ), 

181 ( { '_foo': cache_import_module }, ( ) ), 

182 ( 

183 { name: getattr( module, name ) 

184 for name in MODULE_ATTRIBUTE_NAMES }, 

185 MODULE_ATTRIBUTE_NAMES 

186 ), 

187 ) 

188) 

189def test_400_public_attribute_discovery( provided, expected ): 

190 ''' Public attributes are discovered from dictionary. ''' 

191 assert expected == module.discover_public_attributes( provided ) 

192 

193 

194def test_500_docstring_generation_argument_acceptance( ): 

195 ''' Docstring generator errors on invalid arguments. ''' 

196 class Foo: pass # pylint: disable=missing-class-docstring 

197 with pytest.raises( KeyError ): 

198 module.generate_docstring( 1 ) 

199 with pytest.raises( KeyError ): 

200 module.generate_docstring( '8-bit theater' ) 

201 assert not module.generate_docstring( Foo ) 

202 assert module.generate_docstring( 'instance attributes accretion' ) 

203 assert module.generate_docstring( module.Docstring( 'foo bar' ) ) 

204 

205 

206def test_501_docstring_generation_validity( ): 

207 ''' Generated docstrings are correctly formatted. ''' 

208 from inspect import getdoc 

209 

210 class Foo: 

211 ''' headline 

212 

213 additional information 

214 ''' 

215 

216 docstring_generated = module.generate_docstring( 

217 Foo, 

218 module.Docstring( 'foo bar' ), 

219 'class attributes accretion' ) 

220 docstring_expected = '\n\n'.join( ( 

221 getdoc( Foo ), 

222 'foo bar', 

223 module.generate_docstring( 'class attributes accretion' ) ) ) 

224 assert docstring_expected == docstring_generated 

225 

226 

227def test_600_module_reclassification( ): 

228 ''' Modules are correctly reclassified. ''' 

229 from types import ModuleType as Module 

230 m1 = Module( 'm1' ) 

231 m2 = Module( 'm2' ) 

232 

233 class FooModule( Module ): 

234 ''' test ''' 

235 

236 m3 = FooModule( 'm3' ) 

237 attrs = { 'bar': 42, 'orb': True, 'm1': m1, 'm2': m2, 'm3': m3 } 

238 assert not isinstance( m1, FooModule ) 

239 assert not isinstance( m2, FooModule ) 

240 assert isinstance( m3, FooModule ) 

241 module.reclassify_modules( attrs, FooModule ) 

242 assert isinstance( m1, FooModule ) 

243 assert isinstance( m2, FooModule ) 

244 assert isinstance( m3, FooModule )