1
|
|
|
# -*- coding: utf-8 |
2
|
|
|
"""Unit tests for code in didyoumean_internal.py.""" |
3
|
|
|
from didyoumean_internal import get_suggestion_string,\ |
4
|
|
|
add_string_to_exception,\ |
5
|
|
|
get_objects_in_frame, get_subclasses, get_types_for_str,\ |
6
|
|
|
get_types_for_str_using_inheritance,\ |
7
|
|
|
get_types_for_str_using_names |
8
|
|
|
import didyoumean_common_tests as common |
9
|
|
|
from didyoumean_common_tests import CommonTestOldStyleClass2,\ |
10
|
|
|
CommonTestNewStyleClass2 # to have these 2 in defined names |
11
|
|
|
import unittest2 |
12
|
|
|
import itertools |
13
|
|
|
import sys |
14
|
|
|
|
15
|
|
|
|
16
|
|
|
OLD_CLASS_SUPPORT = sys.version_info >= (3, 0) |
17
|
|
|
IS_PYPY = hasattr(sys, "pypy_translation_info") |
18
|
|
|
U_PREFIX_SUPPORT = not ((3, 0) <= sys.version_info < (3, 3)) |
19
|
|
|
U_PREFIX = "u" if U_PREFIX_SUPPORT else "" |
20
|
|
|
global_var = 42 # Please don't change the value |
21
|
|
|
|
22
|
|
|
|
23
|
|
|
class GetObjectInFrameTests(unittest2.TestCase): |
24
|
|
|
"""Class for tests related to frame/backtrace/etc inspection. |
25
|
|
|
|
26
|
|
|
Tested functions are : get_objects_in_frame. |
27
|
|
|
No tests about 'nonlocal' is written because it is only supported |
28
|
|
|
from Python 3. |
29
|
|
|
""" |
30
|
|
|
|
31
|
|
|
def name_corresponds_to(self, name, expected): |
32
|
|
|
"""Helper functions to test get_objects_in_frame. |
33
|
|
|
|
34
|
|
|
Check that the name corresponds to the expected objects (and their |
35
|
|
|
scope) in the frame of calling function. |
36
|
|
|
None can be used to match any object as it can be hard to describe |
37
|
|
|
an object when it is hidden by something in a closer scope. |
38
|
|
|
Also, extra care is to be taken when calling the function because |
39
|
|
|
giving value by names might affect the result (adding in local |
40
|
|
|
scope). |
41
|
|
|
""" |
42
|
|
|
frame = sys._getframe(1) # frame of calling function |
43
|
|
|
lst = get_objects_in_frame(frame).get(name, []) |
44
|
|
|
self.assertEqual(len(lst), len(expected)) |
45
|
|
|
for scopedobj, exp in zip(lst, expected): |
46
|
|
|
obj, scope = scopedobj |
47
|
|
|
expobj, expscope = exp |
48
|
|
|
self.assertEqual(scope, expscope, name) |
49
|
|
|
if expobj is not None: |
50
|
|
|
self.assertEqual(obj, expobj, name) |
51
|
|
|
|
52
|
|
|
def test_builtin(self): |
53
|
|
|
"""Test with builtin.""" |
54
|
|
|
builtin = len |
55
|
|
|
name = builtin.__name__ |
56
|
|
|
self.name_corresponds_to(name, [(builtin, 'builtin')]) |
57
|
|
|
|
58
|
|
|
def test_builtin2(self): |
59
|
|
|
"""Test with builtin.""" |
60
|
|
|
name = 'True' |
61
|
|
|
self.name_corresponds_to(name, [(bool(1), 'builtin')]) |
62
|
|
|
|
63
|
|
|
def test_global(self): |
64
|
|
|
"""Test with global.""" |
65
|
|
|
name = 'global_var' |
66
|
|
|
self.name_corresponds_to(name, [(42, 'global')]) |
67
|
|
|
|
68
|
|
|
def test_local(self): |
69
|
|
|
"""Test with local.""" |
70
|
|
|
name = 'toto' |
71
|
|
|
self.name_corresponds_to(name, []) |
72
|
|
|
toto = 0 |
73
|
|
|
self.name_corresponds_to(name, [(0, 'local')]) |
74
|
|
|
|
75
|
|
|
def test_local_and_global(self): |
76
|
|
|
"""Test with local hiding a global.""" |
77
|
|
|
name = 'global_var' |
78
|
|
|
self.name_corresponds_to(name, [(42, 'global')]) |
79
|
|
|
global_var = 1 |
80
|
|
|
self.name_corresponds_to(name, [(1, 'local'), (42, 'global')]) |
81
|
|
|
|
82
|
|
|
def test_global_keword(self): |
83
|
|
|
"""Test with global keyword.""" |
84
|
|
|
# Funny detail : the global keyword works even if at the end of |
85
|
|
|
# the function (after the code it affects) but this raises a |
86
|
|
|
# SyntaxWarning. |
87
|
|
|
global global_var |
88
|
|
|
name = 'global_var' |
89
|
|
|
global_var = 42 # value is unchanged |
90
|
|
|
self.name_corresponds_to(name, [(42, 'global')]) |
91
|
|
|
|
92
|
|
|
def test_del_local(self): |
93
|
|
|
"""Test with deleted local.""" |
94
|
|
|
name = 'toto' |
95
|
|
|
self.name_corresponds_to(name, []) |
96
|
|
|
toto = 0 |
97
|
|
|
self.name_corresponds_to(name, [(0, 'local')]) |
98
|
|
|
del toto |
99
|
|
|
self.name_corresponds_to(name, []) |
100
|
|
|
|
101
|
|
|
def test_del_local_hiding_global(self): |
102
|
|
|
"""Test with deleted local hiding a global.""" |
103
|
|
|
name = 'global_var' |
104
|
|
|
glob_desc = [(42, 'global')] |
105
|
|
|
local_desc = [(1, 'local')] |
106
|
|
|
self.name_corresponds_to(name, glob_desc) |
107
|
|
|
global_var = 1 |
108
|
|
|
self.name_corresponds_to(name, local_desc + glob_desc) |
109
|
|
|
del global_var |
110
|
|
|
self.name_corresponds_to(name, glob_desc) |
111
|
|
|
|
112
|
|
|
def test_enclosing(self): |
113
|
|
|
"""Test with nested functions.""" |
114
|
|
|
foo = 1 |
115
|
|
|
bar = 2 |
116
|
|
|
|
117
|
|
|
def nested_func(foo, baz): |
118
|
|
|
qux = 5 |
119
|
|
|
self.name_corresponds_to('qux', [(5, 'local')]) |
120
|
|
|
self.name_corresponds_to('baz', [(4, 'local')]) |
121
|
|
|
self.name_corresponds_to('foo', [(3, 'local')]) |
122
|
|
|
self.name_corresponds_to('bar', []) |
123
|
|
|
self.name_corresponds_to( |
124
|
|
|
'global_var', [(42, 'global')]) |
125
|
|
|
nested_func(3, 4) |
126
|
|
|
self.name_corresponds_to('nested_func', [(nested_func, 'local')]) |
127
|
|
|
self.name_corresponds_to('foo', [(1, 'local')]) |
128
|
|
|
self.name_corresponds_to('baz', []) |
129
|
|
|
|
130
|
|
|
def test_enclosing2(self): |
131
|
|
|
"""Test with nested functions.""" |
132
|
|
|
bar = 2 |
133
|
|
|
|
134
|
|
|
def nested_func(): |
135
|
|
|
self.name_corresponds_to('bar', []) |
136
|
|
|
bar = 3 |
137
|
|
|
self.name_corresponds_to('bar', [(3, 'local')]) |
138
|
|
|
|
139
|
|
|
nested_func() |
140
|
|
|
self.name_corresponds_to('nested_func', [(nested_func, 'local')]) |
141
|
|
|
|
142
|
|
|
def test_enclosing3(self): |
143
|
|
|
"""Test with nested functions.""" |
144
|
|
|
bar = 2 |
145
|
|
|
|
146
|
|
|
def nested_func(): |
147
|
|
|
self.name_corresponds_to('bar', [(2, 'local')]) |
148
|
|
|
tmp = bar |
149
|
|
|
self.name_corresponds_to('bar', [(2, 'local')]) |
150
|
|
|
|
151
|
|
|
nested_func() |
152
|
|
|
self.name_corresponds_to('nested_func', [(nested_func, 'local')]) |
153
|
|
|
|
154
|
|
|
def test_enclosing4(self): |
155
|
|
|
"""Test with nested functions.""" |
156
|
|
|
global_var = 1 |
157
|
|
|
|
158
|
|
|
def nested_func(): |
159
|
|
|
self.name_corresponds_to('global_var', [(42, 'global')]) |
160
|
|
|
|
161
|
|
|
nested_func() |
162
|
|
|
self.name_corresponds_to('global_var', [(1, 'local'), (42, 'global')]) |
163
|
|
|
|
164
|
|
|
def test_enclosing5(self): |
165
|
|
|
"""Test with nested functions.""" |
166
|
|
|
bar = 2 |
167
|
|
|
foo = 3 |
168
|
|
|
|
169
|
|
|
def nested_func(): |
170
|
|
|
bar = 4 |
171
|
|
|
baz = 5 |
172
|
|
|
self.name_corresponds_to('foo', []) |
173
|
|
|
self.name_corresponds_to('bar', [(4, 'local')]) |
174
|
|
|
|
175
|
|
|
def nested_func2(): |
176
|
|
|
self.name_corresponds_to('foo', []) |
177
|
|
|
self.name_corresponds_to('bar', []) |
178
|
|
|
|
179
|
|
|
nested_func2() |
180
|
|
|
|
181
|
|
|
nested_func() |
182
|
|
|
self.name_corresponds_to('nested_func', [(nested_func, 'local')]) |
183
|
|
|
|
184
|
|
|
|
185
|
|
|
class OldStyleBaseClass: |
186
|
|
|
"""Dummy class for testing purposes.""" |
187
|
|
|
|
188
|
|
|
pass |
189
|
|
|
|
190
|
|
|
|
191
|
|
|
class OldStyleDerivedClass(OldStyleBaseClass): |
192
|
|
|
"""Dummy class for testing purposes.""" |
193
|
|
|
|
194
|
|
|
pass |
195
|
|
|
|
196
|
|
|
|
197
|
|
|
class NewStyleBaseClass(object): |
198
|
|
|
"""Dummy class for testing purposes.""" |
199
|
|
|
|
200
|
|
|
pass |
201
|
|
|
|
202
|
|
|
|
203
|
|
|
class NewStyleDerivedClass(NewStyleBaseClass): |
204
|
|
|
"""Dummy class for testing purposes.""" |
205
|
|
|
|
206
|
|
|
pass |
207
|
|
|
|
208
|
|
|
|
209
|
|
|
def a_function(): |
210
|
|
|
"""Dummy function for testing purposes.""" |
211
|
|
|
pass |
212
|
|
|
|
213
|
|
|
|
214
|
|
|
def a_generator(): |
215
|
|
|
"""Dummy generator for testing purposes.""" |
216
|
|
|
yield 1 |
217
|
|
|
|
218
|
|
|
|
219
|
|
|
NEW_STYLE_CLASSES = [bool, int, float, str, tuple, list, set, dict, object, |
220
|
|
|
NewStyleBaseClass, NewStyleDerivedClass, |
221
|
|
|
common.CommonTestNewStyleClass, |
222
|
|
|
common.CommonTestNewStyleClass2, |
223
|
|
|
type(a_function), type(a_generator), |
224
|
|
|
type(len), type(None), type(type(None)), |
225
|
|
|
type(object), type(sys), type(range), |
226
|
|
|
type(NewStyleBaseClass), type(NewStyleDerivedClass), |
227
|
|
|
type(OldStyleBaseClass), type(OldStyleDerivedClass)] |
228
|
|
|
OLD_STYLE_CLASSES = [OldStyleBaseClass, OldStyleDerivedClass, |
229
|
|
|
CommonTestOldStyleClass2] |
230
|
|
|
CLASSES = [(c, True) for c in NEW_STYLE_CLASSES] + \ |
231
|
|
|
[(c, False) for c in OLD_STYLE_CLASSES] |
232
|
|
|
|
233
|
|
|
|
234
|
|
|
class GetTypesForStrTests(unittest2.TestCase): |
235
|
|
|
"""Test get_types_for_str.""" |
236
|
|
|
|
237
|
|
|
def test_get_subclasses(self): |
238
|
|
|
"""Test the get_subclasses function. |
239
|
|
|
|
240
|
|
|
All types are found when looking for subclasses of object, except |
241
|
|
|
for the old style classes on Python 2.x. |
242
|
|
|
""" |
243
|
|
|
all_classes = get_subclasses(object) |
244
|
|
|
for typ, new in CLASSES: |
245
|
|
|
self.assertTrue(typ in get_subclasses(typ)) |
246
|
|
|
if new or OLD_CLASS_SUPPORT: |
247
|
|
|
self.assertTrue(typ in all_classes) |
248
|
|
|
else: |
249
|
|
|
self.assertFalse(typ in all_classes) |
250
|
|
|
self.assertFalse(0 in all_classes) |
251
|
|
|
|
252
|
|
|
def test_get_types_for_str_using_inheritance(self): |
253
|
|
|
"""Test the get_types_for_str_using_inheritance function. |
254
|
|
|
|
255
|
|
|
All types are found when looking for subclasses of object, except |
256
|
|
|
for the old style classes on Python 2.x. |
257
|
|
|
|
258
|
|
|
Also, it seems like the returns is (almost) always precise as the |
259
|
|
|
returned set contains only the expected type and nothing else. |
260
|
|
|
""" |
261
|
|
|
for typ, new in CLASSES: |
262
|
|
|
types = get_types_for_str_using_inheritance(typ.__name__) |
263
|
|
|
if new or OLD_CLASS_SUPPORT: |
264
|
|
|
self.assertEqual(types, set([typ]), typ) |
265
|
|
|
else: |
266
|
|
|
self.assertEqual(types, set(), typ) |
267
|
|
|
|
268
|
|
|
self.assertFalse(get_types_for_str_using_inheritance('faketype')) |
269
|
|
|
|
270
|
|
|
def get_types_using_names(self, type_str): |
271
|
|
|
"""Wrapper around the get_types_using_names function.""" |
272
|
|
|
return get_types_for_str_using_names(type_str, sys._getframe(1)) |
273
|
|
|
|
274
|
|
|
def test_get_types_for_str_using_names(self): |
275
|
|
|
"""Test the get_types_using_names function. |
276
|
|
|
|
277
|
|
|
Old style classes are retrieved even on Python 2.x. |
278
|
|
|
However, a few builtin types are not in the names so can't be found. |
279
|
|
|
""" |
280
|
|
|
for typ in OLD_STYLE_CLASSES: |
281
|
|
|
types = self.get_types_using_names(typ.__name__) |
282
|
|
|
self.assertEqual(types, set([typ]), typ) |
283
|
|
|
for n in ['generator', 'module', 'function', 'faketype']: |
284
|
|
|
self.assertEqual(self.get_types_using_names(n), set(), n) |
285
|
|
|
n = 'NoneType' |
286
|
|
|
if IS_PYPY: |
287
|
|
|
self.assertEqual(len(self.get_types_using_names(n)), 1, n) |
288
|
|
|
else: |
289
|
|
|
self.assertEqual(self.get_types_using_names(n), set(), n) |
290
|
|
|
|
291
|
|
|
def get_types_for_str(self, type_str): |
292
|
|
|
"""Wrapper around the get_types_for_str function.""" |
293
|
|
|
return get_types_for_str(type_str, sys._getframe(1)) |
294
|
|
|
|
295
|
|
|
def test_get_types_for_str(self): |
296
|
|
|
"""Test the get_types_for_str function. |
297
|
|
|
|
298
|
|
|
Check that for all tested types, the proper type is retrieved. |
299
|
|
|
""" |
300
|
|
|
for typ, _ in CLASSES: |
301
|
|
|
types = self.get_types_for_str(typ.__name__) |
302
|
|
|
self.assertEqual(types, set([typ]), typ) |
303
|
|
|
|
304
|
|
|
self.assertEqual(self.get_types_for_str('faketype'), set()) |
305
|
|
|
|
306
|
|
|
def test_get_types_for_str2(self): |
307
|
|
|
"""Test the get_types_for_str function. |
308
|
|
|
|
309
|
|
|
Check that for all tested strings, a single type is retrived. |
310
|
|
|
This is useful to ensure that we are using the right names. |
311
|
|
|
""" |
312
|
|
|
for n in ['module', 'NoneType', 'function', |
313
|
|
|
'NewStyleBaseClass', 'NewStyleDerivedClass', |
314
|
|
|
'OldStyleBaseClass', 'OldStyleDerivedClass']: |
315
|
|
|
types = self.get_types_for_str(n) |
316
|
|
|
self.assertEqual(len(types), 1, n) |
317
|
|
|
for n in ['generator']: # FIXME: with pypy, we find an additional type |
318
|
|
|
types = self.get_types_for_str(n) |
319
|
|
|
self.assertEqual(len(types), 2 if IS_PYPY else 1, n) |
320
|
|
|
|
321
|
|
|
def test_old_class_not_in_namespace(self): |
322
|
|
|
"""Test the get_types_for_str function. |
323
|
|
|
|
324
|
|
|
Check that at the moment, CommonTestOldStyleClass is not found |
325
|
|
|
because it is not in the namespace. This behavior is to be improved. |
326
|
|
|
""" |
327
|
|
|
typ = common.CommonTestOldStyleClass |
328
|
|
|
expect_with_inherit = set([typ]) if OLD_CLASS_SUPPORT else set() |
329
|
|
|
name = typ.__name__ |
330
|
|
|
types1 = get_types_for_str_using_inheritance(name) |
331
|
|
|
types2 = self.get_types_using_names(name) |
332
|
|
|
types3 = self.get_types_for_str(name) |
333
|
|
|
self.assertEqual(types1, expect_with_inherit) |
334
|
|
|
self.assertEqual(types2, set()) |
335
|
|
|
self.assertEqual(types3, expect_with_inherit) |
336
|
|
|
|
337
|
|
|
|
338
|
|
|
class GetSuggStringTests(unittest2.TestCase): |
339
|
|
|
"""Tests about get_suggestion_string.""" |
340
|
|
|
|
341
|
|
|
def test_no_sugg(self): |
342
|
|
|
"""Empty list of suggestions.""" |
343
|
|
|
self.assertEqual(get_suggestion_string(()), "") |
344
|
|
|
|
345
|
|
|
def test_one_sugg(self): |
346
|
|
|
"""Single suggestion.""" |
347
|
|
|
self.assertEqual(get_suggestion_string(('0',)), ". Did you mean 0?") |
348
|
|
|
|
349
|
|
|
def test_same_sugg(self): |
350
|
|
|
"""Identical suggestion.""" |
351
|
|
|
self.assertEqual( |
352
|
|
|
get_suggestion_string(('0', '0')), ". Did you mean 0, 0?") |
353
|
|
|
|
354
|
|
|
def test_multiple_suggs(self): |
355
|
|
|
"""Multiple suggestions.""" |
356
|
|
|
self.assertEqual( |
357
|
|
|
get_suggestion_string(('0', '1')), ". Did you mean 0, 1?") |
358
|
|
|
|
359
|
|
|
|
360
|
|
|
class AddStringToExcTest(common.TestWithStringFunction): |
361
|
|
|
"""Generic class for tests about add_string_to_exception.""" |
362
|
|
|
|
363
|
|
|
prefix_repr = "" |
364
|
|
|
suffix_repr = "" |
365
|
|
|
check_str_sum = True |
366
|
|
|
|
367
|
|
|
def get_exception(self): |
368
|
|
|
"""Abstract method to get an instance of exception.""" |
369
|
|
|
raise NotImplementedError("'get_exception' needs to be implemented") |
370
|
|
|
|
371
|
|
|
def get_exc_before_and_after(self, string, func): |
372
|
|
|
"""Retrieve string representations of exceptions. |
373
|
|
|
|
374
|
|
|
Retrieve string representations of exceptions raised by code |
375
|
|
|
before and after calling add_string_to_exception. |
376
|
|
|
""" |
377
|
|
|
value = self.get_exception() |
378
|
|
|
before = func(value) |
379
|
|
|
add_string_to_exception(value, string) |
380
|
|
|
after = func(value) |
381
|
|
|
return (before, after) |
382
|
|
|
|
383
|
|
|
def check_string_added(self, func, string, prefix="", suffix=""): |
384
|
|
|
"""Check that add_string_to_exception adds the strings.""" |
385
|
|
|
s1, s2 = self.get_exc_before_and_after(string, func) |
386
|
|
|
self.assertStringAdded( |
387
|
|
|
prefix + string + suffix, s1, s2, self.check_str_sum) |
388
|
|
|
|
389
|
|
|
def test_add_empty_string_to_str(self): |
390
|
|
|
"""Empty string added to error's str value.""" |
391
|
|
|
self.check_string_added(str, "") |
392
|
|
|
|
393
|
|
|
def test_add_empty_string_to_repr(self): |
394
|
|
|
"""Empty string added to error's repr value.""" |
395
|
|
|
self.check_string_added(repr, "") |
396
|
|
|
|
397
|
|
|
def test_add_string_to_str(self): |
398
|
|
|
"""Non-empty string added to error's str value.""" |
399
|
|
|
self.check_string_added(str, "ABCDEstr") |
400
|
|
|
|
401
|
|
|
def test_add_string_to_repr(self): |
402
|
|
|
"""Non-empty string added to error's repr value.""" |
403
|
|
|
self.check_string_added( |
404
|
|
|
repr, "ABCDErepr", self.prefix_repr, self.suffix_repr) |
405
|
|
|
|
406
|
|
|
|
407
|
|
|
class AddStringToExcFromCodeTest(AddStringToExcTest): |
408
|
|
|
"""Generic class for tests about add_string_to_exception. |
409
|
|
|
|
410
|
|
|
The tested function is called on an exception created by running |
411
|
|
|
some failing code (`self.code`) and catching what it throws. |
412
|
|
|
""" |
413
|
|
|
|
414
|
|
|
code = NotImplemented |
415
|
|
|
|
416
|
|
|
def get_exception(self): |
417
|
|
|
"""Get the exception by running the code and catching errors.""" |
418
|
|
|
type_, value, _ = common.get_exception(self.code) |
419
|
|
|
self.assertEqual(self.error_type, type_) |
420
|
|
|
return value |
421
|
|
|
|
422
|
|
|
|
423
|
|
|
class AddStringToNameErrorTest(unittest2.TestCase, AddStringToExcFromCodeTest): |
424
|
|
|
"""Class for tests of add_string_to_exception on NameError.""" |
425
|
|
|
|
426
|
|
|
code = 'babar = 0\nbaba' |
427
|
|
|
error_type = NameError |
428
|
|
|
|
429
|
|
|
|
430
|
|
|
class AddStringToTypeErrorTest(unittest2.TestCase, AddStringToExcFromCodeTest): |
431
|
|
|
"""Class for tests of add_string_to_exception on TypeError.""" |
432
|
|
|
|
433
|
|
|
code = '[0](0)' |
434
|
|
|
error_type = TypeError |
435
|
|
|
|
436
|
|
|
|
437
|
|
|
class AddStringToImportErrorTest( |
438
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
439
|
|
|
"""Class for tests of add_string_to_exception on ImportError.""" |
440
|
|
|
|
441
|
|
|
code = 'import maths' |
442
|
|
|
error_type = ImportError |
443
|
|
|
|
444
|
|
|
|
445
|
|
|
class AddStringToKeyErrorTest( |
446
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
447
|
|
|
"""Class for tests of add_string_to_exception on KeyError.""" |
448
|
|
|
|
449
|
|
|
code = 'dict()["ffdsqmjklfqsd"]' |
450
|
|
|
error_type = KeyError |
451
|
|
|
|
452
|
|
|
|
453
|
|
|
class AddStringToAttributeErrorTest( |
454
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
455
|
|
|
"""Class for tests of add_string_to_exception on AttributeError.""" |
456
|
|
|
|
457
|
|
|
code = '[].does_not_exist' |
458
|
|
|
error_type = AttributeError |
459
|
|
|
|
460
|
|
|
|
461
|
|
|
class AddStringToSyntaxErrorTest( |
462
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
463
|
|
|
"""Class for tests of add_string_to_exception on SyntaxError.""" |
464
|
|
|
|
465
|
|
|
code = 'return' |
466
|
|
|
error_type = SyntaxError |
467
|
|
|
|
468
|
|
|
|
469
|
|
|
class AddStringToMemoryErrorTest( |
470
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
471
|
|
|
"""Class for tests of add_string_to_exception on MemoryError.""" |
472
|
|
|
|
473
|
|
|
code = '[0] * 999999999999999' |
474
|
|
|
error_type = MemoryError |
475
|
|
|
prefix_repr = "'" |
476
|
|
|
suffix_repr = "'," |
477
|
|
|
|
478
|
|
|
|
479
|
|
|
class AddStringToIOErrorTest( |
480
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
481
|
|
|
"""Class for tests of add_string_to_exception on NoFileIoError.""" |
482
|
|
|
|
483
|
|
|
code = 'with open("/does_not_exist") as f:\n\tpass' |
484
|
|
|
error_type = common.NoFileIoError |
485
|
|
|
|
486
|
|
|
|
487
|
|
|
class AddStringToUnicodeDecodeTest( |
488
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
489
|
|
|
"""Class for tests of add_string_to_exception on UnicodeDecodeError.""" |
490
|
|
|
|
491
|
|
|
code = "'foo'.encode('utf-16').decode('utf-8')" |
492
|
|
|
error_type = UnicodeDecodeError |
493
|
|
|
|
494
|
|
|
|
495
|
|
|
class AddStringToUnicodeEncodeTest( |
496
|
|
|
unittest2.TestCase, AddStringToExcFromCodeTest): |
497
|
|
|
"""Class for tests of add_string_to_exception on UnicodeEncodeError.""" |
498
|
|
|
|
499
|
|
|
code = U_PREFIX + '"\u0411".encode("iso-8859-15")' |
500
|
|
|
error_type = UnicodeEncodeError |
501
|
|
|
|
502
|
|
|
|
503
|
|
|
class AddStringToExcFromInstanceTest(AddStringToExcTest): |
504
|
|
|
"""Generic class for tests about add_string_to_exception. |
505
|
|
|
|
506
|
|
|
The tested function is called on an exception created by calling the |
507
|
|
|
constructor (`self.exc_type`) with the right arguments (`self.args`). |
508
|
|
|
Because of the way it creates exception, the tests are somewhat artificial |
509
|
|
|
(compared to AddStringToExcFromCodeTest for instance). However, the major |
510
|
|
|
advantage is that they can be easily generated (to have all subclasses of |
511
|
|
|
Exception tested). |
512
|
|
|
""" |
513
|
|
|
|
514
|
|
|
check_str_sum = False |
515
|
|
|
exc_type = NotImplemented |
516
|
|
|
args = NotImplemented |
517
|
|
|
|
518
|
|
|
def get_exception(self): |
519
|
|
|
"""Get the exception by calling the constructor with correct args.""" |
520
|
|
|
return self.exc_type(*self.args) |
521
|
|
|
|
522
|
|
|
|
523
|
|
|
class AddStringToZeroDivisionError( |
524
|
|
|
unittest2.TestCase, AddStringToExcFromInstanceTest): |
525
|
|
|
"""Class for tests of add_string_to_exception on ZeroDivisionError.""" |
526
|
|
|
|
527
|
|
|
exc_type = ZeroDivisionError |
528
|
|
|
args = ('', '', '', '', '') |
529
|
|
|
|
530
|
|
|
|
531
|
|
|
def get_instance(klass): |
532
|
|
|
"""Get instance for class by bruteforcing the parameters. |
533
|
|
|
|
534
|
|
|
Construction is attempted with a decreasing number of arguments so that |
535
|
|
|
the instanciated object has as many non-null attributes set as possible. |
536
|
|
|
This is important not for the creation but when the object gets used |
537
|
|
|
later on. Also, the order of the values has its importance for similar |
538
|
|
|
reasons. |
539
|
|
|
""" |
540
|
|
|
my_unicode = str if sys.version_info >= (3, 0) else unicode |
541
|
|
|
values_tried = [my_unicode(), bytes(), 0] |
542
|
|
|
for nb_arg in reversed(range(6)): |
543
|
|
|
for p in itertools.product(values_tried, repeat=nb_arg): |
544
|
|
|
try: |
545
|
|
|
return klass(*p), p |
546
|
|
|
except (TypeError, AttributeError) as e: |
547
|
|
|
pass |
548
|
|
|
except Exception as e: |
549
|
|
|
print(type(e), e) |
550
|
|
|
return None |
551
|
|
|
|
552
|
|
|
|
553
|
|
|
def generate_add_string_to_exc_tests(): |
554
|
|
|
"""Generate tests for add_string_to_exception. |
555
|
|
|
|
556
|
|
|
This function dynamically creates tests cases for the function |
557
|
|
|
add_string_to_exception for as many Exception subclasses as possible. |
558
|
|
|
This is not used at the moment because the generated classes need to |
559
|
|
|
be added in the global namespace and there is no good way to do this. |
560
|
|
|
However, it may be a good idea to call this when new versions of |
561
|
|
|
Python are released to ensure we handle all exceptions properly (and |
562
|
|
|
find the tests to be added manually if need be). |
563
|
|
|
""" |
564
|
|
|
for klass in get_subclasses(Exception): |
565
|
|
|
r = get_instance(klass) |
566
|
|
|
if r is not None: |
567
|
|
|
_, p = r |
568
|
|
|
class_name = ("NameForAddStringToExcFromInstanceTest" + |
569
|
|
|
klass.__name__ + str(id(klass))) |
570
|
|
|
assert class_name not in globals(), class_name |
571
|
|
|
globals()[class_name] = type( |
572
|
|
|
class_name, |
573
|
|
|
(AddStringToExcFromInstanceTest, unittest2.TestCase), |
574
|
|
|
{'exc_type': klass, 'args': p}) |
575
|
|
|
|
576
|
|
|
|
577
|
|
|
if __name__ == '__main__': |
578
|
|
|
print(sys.version_info) |
579
|
|
|
unittest2.main() |
580
|
|
|
|