jpayne@68
|
1 """Test case implementation"""
|
jpayne@68
|
2
|
jpayne@68
|
3 import sys
|
jpayne@68
|
4 import functools
|
jpayne@68
|
5 import difflib
|
jpayne@68
|
6 import logging
|
jpayne@68
|
7 import pprint
|
jpayne@68
|
8 import re
|
jpayne@68
|
9 import warnings
|
jpayne@68
|
10 import collections
|
jpayne@68
|
11 import contextlib
|
jpayne@68
|
12 import traceback
|
jpayne@68
|
13 import types
|
jpayne@68
|
14
|
jpayne@68
|
15 from . import result
|
jpayne@68
|
16 from .util import (strclass, safe_repr, _count_diff_all_purpose,
|
jpayne@68
|
17 _count_diff_hashable, _common_shorten_repr)
|
jpayne@68
|
18
|
jpayne@68
|
19 __unittest = True
|
jpayne@68
|
20
|
jpayne@68
|
21 _subtest_msg_sentinel = object()
|
jpayne@68
|
22
|
jpayne@68
|
23 DIFF_OMITTED = ('\nDiff is %s characters long. '
|
jpayne@68
|
24 'Set self.maxDiff to None to see it.')
|
jpayne@68
|
25
|
jpayne@68
|
26 class SkipTest(Exception):
|
jpayne@68
|
27 """
|
jpayne@68
|
28 Raise this exception in a test to skip it.
|
jpayne@68
|
29
|
jpayne@68
|
30 Usually you can use TestCase.skipTest() or one of the skipping decorators
|
jpayne@68
|
31 instead of raising this directly.
|
jpayne@68
|
32 """
|
jpayne@68
|
33
|
jpayne@68
|
34 class _ShouldStop(Exception):
|
jpayne@68
|
35 """
|
jpayne@68
|
36 The test should stop.
|
jpayne@68
|
37 """
|
jpayne@68
|
38
|
jpayne@68
|
39 class _UnexpectedSuccess(Exception):
|
jpayne@68
|
40 """
|
jpayne@68
|
41 The test was supposed to fail, but it didn't!
|
jpayne@68
|
42 """
|
jpayne@68
|
43
|
jpayne@68
|
44
|
jpayne@68
|
45 class _Outcome(object):
|
jpayne@68
|
46 def __init__(self, result=None):
|
jpayne@68
|
47 self.expecting_failure = False
|
jpayne@68
|
48 self.result = result
|
jpayne@68
|
49 self.result_supports_subtests = hasattr(result, "addSubTest")
|
jpayne@68
|
50 self.success = True
|
jpayne@68
|
51 self.skipped = []
|
jpayne@68
|
52 self.expectedFailure = None
|
jpayne@68
|
53 self.errors = []
|
jpayne@68
|
54
|
jpayne@68
|
55 @contextlib.contextmanager
|
jpayne@68
|
56 def testPartExecutor(self, test_case, isTest=False):
|
jpayne@68
|
57 old_success = self.success
|
jpayne@68
|
58 self.success = True
|
jpayne@68
|
59 try:
|
jpayne@68
|
60 yield
|
jpayne@68
|
61 except KeyboardInterrupt:
|
jpayne@68
|
62 raise
|
jpayne@68
|
63 except SkipTest as e:
|
jpayne@68
|
64 self.success = False
|
jpayne@68
|
65 self.skipped.append((test_case, str(e)))
|
jpayne@68
|
66 except _ShouldStop:
|
jpayne@68
|
67 pass
|
jpayne@68
|
68 except:
|
jpayne@68
|
69 exc_info = sys.exc_info()
|
jpayne@68
|
70 if self.expecting_failure:
|
jpayne@68
|
71 self.expectedFailure = exc_info
|
jpayne@68
|
72 else:
|
jpayne@68
|
73 self.success = False
|
jpayne@68
|
74 self.errors.append((test_case, exc_info))
|
jpayne@68
|
75 # explicitly break a reference cycle:
|
jpayne@68
|
76 # exc_info -> frame -> exc_info
|
jpayne@68
|
77 exc_info = None
|
jpayne@68
|
78 else:
|
jpayne@68
|
79 if self.result_supports_subtests and self.success:
|
jpayne@68
|
80 self.errors.append((test_case, None))
|
jpayne@68
|
81 finally:
|
jpayne@68
|
82 self.success = self.success and old_success
|
jpayne@68
|
83
|
jpayne@68
|
84
|
jpayne@68
|
85 def _id(obj):
|
jpayne@68
|
86 return obj
|
jpayne@68
|
87
|
jpayne@68
|
88
|
jpayne@68
|
89 _module_cleanups = []
|
jpayne@68
|
90 def addModuleCleanup(function, /, *args, **kwargs):
|
jpayne@68
|
91 """Same as addCleanup, except the cleanup items are called even if
|
jpayne@68
|
92 setUpModule fails (unlike tearDownModule)."""
|
jpayne@68
|
93 _module_cleanups.append((function, args, kwargs))
|
jpayne@68
|
94
|
jpayne@68
|
95
|
jpayne@68
|
96 def doModuleCleanups():
|
jpayne@68
|
97 """Execute all module cleanup functions. Normally called for you after
|
jpayne@68
|
98 tearDownModule."""
|
jpayne@68
|
99 exceptions = []
|
jpayne@68
|
100 while _module_cleanups:
|
jpayne@68
|
101 function, args, kwargs = _module_cleanups.pop()
|
jpayne@68
|
102 try:
|
jpayne@68
|
103 function(*args, **kwargs)
|
jpayne@68
|
104 except Exception as exc:
|
jpayne@68
|
105 exceptions.append(exc)
|
jpayne@68
|
106 if exceptions:
|
jpayne@68
|
107 # Swallows all but first exception. If a multi-exception handler
|
jpayne@68
|
108 # gets written we should use that here instead.
|
jpayne@68
|
109 raise exceptions[0]
|
jpayne@68
|
110
|
jpayne@68
|
111
|
jpayne@68
|
112 def skip(reason):
|
jpayne@68
|
113 """
|
jpayne@68
|
114 Unconditionally skip a test.
|
jpayne@68
|
115 """
|
jpayne@68
|
116 def decorator(test_item):
|
jpayne@68
|
117 if not isinstance(test_item, type):
|
jpayne@68
|
118 @functools.wraps(test_item)
|
jpayne@68
|
119 def skip_wrapper(*args, **kwargs):
|
jpayne@68
|
120 raise SkipTest(reason)
|
jpayne@68
|
121 test_item = skip_wrapper
|
jpayne@68
|
122
|
jpayne@68
|
123 test_item.__unittest_skip__ = True
|
jpayne@68
|
124 test_item.__unittest_skip_why__ = reason
|
jpayne@68
|
125 return test_item
|
jpayne@68
|
126 if isinstance(reason, types.FunctionType):
|
jpayne@68
|
127 test_item = reason
|
jpayne@68
|
128 reason = ''
|
jpayne@68
|
129 return decorator(test_item)
|
jpayne@68
|
130 return decorator
|
jpayne@68
|
131
|
jpayne@68
|
132 def skipIf(condition, reason):
|
jpayne@68
|
133 """
|
jpayne@68
|
134 Skip a test if the condition is true.
|
jpayne@68
|
135 """
|
jpayne@68
|
136 if condition:
|
jpayne@68
|
137 return skip(reason)
|
jpayne@68
|
138 return _id
|
jpayne@68
|
139
|
jpayne@68
|
140 def skipUnless(condition, reason):
|
jpayne@68
|
141 """
|
jpayne@68
|
142 Skip a test unless the condition is true.
|
jpayne@68
|
143 """
|
jpayne@68
|
144 if not condition:
|
jpayne@68
|
145 return skip(reason)
|
jpayne@68
|
146 return _id
|
jpayne@68
|
147
|
jpayne@68
|
148 def expectedFailure(test_item):
|
jpayne@68
|
149 test_item.__unittest_expecting_failure__ = True
|
jpayne@68
|
150 return test_item
|
jpayne@68
|
151
|
jpayne@68
|
152 def _is_subtype(expected, basetype):
|
jpayne@68
|
153 if isinstance(expected, tuple):
|
jpayne@68
|
154 return all(_is_subtype(e, basetype) for e in expected)
|
jpayne@68
|
155 return isinstance(expected, type) and issubclass(expected, basetype)
|
jpayne@68
|
156
|
jpayne@68
|
157 class _BaseTestCaseContext:
|
jpayne@68
|
158
|
jpayne@68
|
159 def __init__(self, test_case):
|
jpayne@68
|
160 self.test_case = test_case
|
jpayne@68
|
161
|
jpayne@68
|
162 def _raiseFailure(self, standardMsg):
|
jpayne@68
|
163 msg = self.test_case._formatMessage(self.msg, standardMsg)
|
jpayne@68
|
164 raise self.test_case.failureException(msg)
|
jpayne@68
|
165
|
jpayne@68
|
166 class _AssertRaisesBaseContext(_BaseTestCaseContext):
|
jpayne@68
|
167
|
jpayne@68
|
168 def __init__(self, expected, test_case, expected_regex=None):
|
jpayne@68
|
169 _BaseTestCaseContext.__init__(self, test_case)
|
jpayne@68
|
170 self.expected = expected
|
jpayne@68
|
171 self.test_case = test_case
|
jpayne@68
|
172 if expected_regex is not None:
|
jpayne@68
|
173 expected_regex = re.compile(expected_regex)
|
jpayne@68
|
174 self.expected_regex = expected_regex
|
jpayne@68
|
175 self.obj_name = None
|
jpayne@68
|
176 self.msg = None
|
jpayne@68
|
177
|
jpayne@68
|
178 def handle(self, name, args, kwargs):
|
jpayne@68
|
179 """
|
jpayne@68
|
180 If args is empty, assertRaises/Warns is being used as a
|
jpayne@68
|
181 context manager, so check for a 'msg' kwarg and return self.
|
jpayne@68
|
182 If args is not empty, call a callable passing positional and keyword
|
jpayne@68
|
183 arguments.
|
jpayne@68
|
184 """
|
jpayne@68
|
185 try:
|
jpayne@68
|
186 if not _is_subtype(self.expected, self._base_type):
|
jpayne@68
|
187 raise TypeError('%s() arg 1 must be %s' %
|
jpayne@68
|
188 (name, self._base_type_str))
|
jpayne@68
|
189 if not args:
|
jpayne@68
|
190 self.msg = kwargs.pop('msg', None)
|
jpayne@68
|
191 if kwargs:
|
jpayne@68
|
192 raise TypeError('%r is an invalid keyword argument for '
|
jpayne@68
|
193 'this function' % (next(iter(kwargs)),))
|
jpayne@68
|
194 return self
|
jpayne@68
|
195
|
jpayne@68
|
196 callable_obj, *args = args
|
jpayne@68
|
197 try:
|
jpayne@68
|
198 self.obj_name = callable_obj.__name__
|
jpayne@68
|
199 except AttributeError:
|
jpayne@68
|
200 self.obj_name = str(callable_obj)
|
jpayne@68
|
201 with self:
|
jpayne@68
|
202 callable_obj(*args, **kwargs)
|
jpayne@68
|
203 finally:
|
jpayne@68
|
204 # bpo-23890: manually break a reference cycle
|
jpayne@68
|
205 self = None
|
jpayne@68
|
206
|
jpayne@68
|
207
|
jpayne@68
|
208 class _AssertRaisesContext(_AssertRaisesBaseContext):
|
jpayne@68
|
209 """A context manager used to implement TestCase.assertRaises* methods."""
|
jpayne@68
|
210
|
jpayne@68
|
211 _base_type = BaseException
|
jpayne@68
|
212 _base_type_str = 'an exception type or tuple of exception types'
|
jpayne@68
|
213
|
jpayne@68
|
214 def __enter__(self):
|
jpayne@68
|
215 return self
|
jpayne@68
|
216
|
jpayne@68
|
217 def __exit__(self, exc_type, exc_value, tb):
|
jpayne@68
|
218 if exc_type is None:
|
jpayne@68
|
219 try:
|
jpayne@68
|
220 exc_name = self.expected.__name__
|
jpayne@68
|
221 except AttributeError:
|
jpayne@68
|
222 exc_name = str(self.expected)
|
jpayne@68
|
223 if self.obj_name:
|
jpayne@68
|
224 self._raiseFailure("{} not raised by {}".format(exc_name,
|
jpayne@68
|
225 self.obj_name))
|
jpayne@68
|
226 else:
|
jpayne@68
|
227 self._raiseFailure("{} not raised".format(exc_name))
|
jpayne@68
|
228 else:
|
jpayne@68
|
229 traceback.clear_frames(tb)
|
jpayne@68
|
230 if not issubclass(exc_type, self.expected):
|
jpayne@68
|
231 # let unexpected exceptions pass through
|
jpayne@68
|
232 return False
|
jpayne@68
|
233 # store exception, without traceback, for later retrieval
|
jpayne@68
|
234 self.exception = exc_value.with_traceback(None)
|
jpayne@68
|
235 if self.expected_regex is None:
|
jpayne@68
|
236 return True
|
jpayne@68
|
237
|
jpayne@68
|
238 expected_regex = self.expected_regex
|
jpayne@68
|
239 if not expected_regex.search(str(exc_value)):
|
jpayne@68
|
240 self._raiseFailure('"{}" does not match "{}"'.format(
|
jpayne@68
|
241 expected_regex.pattern, str(exc_value)))
|
jpayne@68
|
242 return True
|
jpayne@68
|
243
|
jpayne@68
|
244
|
jpayne@68
|
245 class _AssertWarnsContext(_AssertRaisesBaseContext):
|
jpayne@68
|
246 """A context manager used to implement TestCase.assertWarns* methods."""
|
jpayne@68
|
247
|
jpayne@68
|
248 _base_type = Warning
|
jpayne@68
|
249 _base_type_str = 'a warning type or tuple of warning types'
|
jpayne@68
|
250
|
jpayne@68
|
251 def __enter__(self):
|
jpayne@68
|
252 # The __warningregistry__'s need to be in a pristine state for tests
|
jpayne@68
|
253 # to work properly.
|
jpayne@68
|
254 for v in sys.modules.values():
|
jpayne@68
|
255 if getattr(v, '__warningregistry__', None):
|
jpayne@68
|
256 v.__warningregistry__ = {}
|
jpayne@68
|
257 self.warnings_manager = warnings.catch_warnings(record=True)
|
jpayne@68
|
258 self.warnings = self.warnings_manager.__enter__()
|
jpayne@68
|
259 warnings.simplefilter("always", self.expected)
|
jpayne@68
|
260 return self
|
jpayne@68
|
261
|
jpayne@68
|
262 def __exit__(self, exc_type, exc_value, tb):
|
jpayne@68
|
263 self.warnings_manager.__exit__(exc_type, exc_value, tb)
|
jpayne@68
|
264 if exc_type is not None:
|
jpayne@68
|
265 # let unexpected exceptions pass through
|
jpayne@68
|
266 return
|
jpayne@68
|
267 try:
|
jpayne@68
|
268 exc_name = self.expected.__name__
|
jpayne@68
|
269 except AttributeError:
|
jpayne@68
|
270 exc_name = str(self.expected)
|
jpayne@68
|
271 first_matching = None
|
jpayne@68
|
272 for m in self.warnings:
|
jpayne@68
|
273 w = m.message
|
jpayne@68
|
274 if not isinstance(w, self.expected):
|
jpayne@68
|
275 continue
|
jpayne@68
|
276 if first_matching is None:
|
jpayne@68
|
277 first_matching = w
|
jpayne@68
|
278 if (self.expected_regex is not None and
|
jpayne@68
|
279 not self.expected_regex.search(str(w))):
|
jpayne@68
|
280 continue
|
jpayne@68
|
281 # store warning for later retrieval
|
jpayne@68
|
282 self.warning = w
|
jpayne@68
|
283 self.filename = m.filename
|
jpayne@68
|
284 self.lineno = m.lineno
|
jpayne@68
|
285 return
|
jpayne@68
|
286 # Now we simply try to choose a helpful failure message
|
jpayne@68
|
287 if first_matching is not None:
|
jpayne@68
|
288 self._raiseFailure('"{}" does not match "{}"'.format(
|
jpayne@68
|
289 self.expected_regex.pattern, str(first_matching)))
|
jpayne@68
|
290 if self.obj_name:
|
jpayne@68
|
291 self._raiseFailure("{} not triggered by {}".format(exc_name,
|
jpayne@68
|
292 self.obj_name))
|
jpayne@68
|
293 else:
|
jpayne@68
|
294 self._raiseFailure("{} not triggered".format(exc_name))
|
jpayne@68
|
295
|
jpayne@68
|
296
|
jpayne@68
|
297
|
jpayne@68
|
298 _LoggingWatcher = collections.namedtuple("_LoggingWatcher",
|
jpayne@68
|
299 ["records", "output"])
|
jpayne@68
|
300
|
jpayne@68
|
301
|
jpayne@68
|
302 class _CapturingHandler(logging.Handler):
|
jpayne@68
|
303 """
|
jpayne@68
|
304 A logging handler capturing all (raw and formatted) logging output.
|
jpayne@68
|
305 """
|
jpayne@68
|
306
|
jpayne@68
|
307 def __init__(self):
|
jpayne@68
|
308 logging.Handler.__init__(self)
|
jpayne@68
|
309 self.watcher = _LoggingWatcher([], [])
|
jpayne@68
|
310
|
jpayne@68
|
311 def flush(self):
|
jpayne@68
|
312 pass
|
jpayne@68
|
313
|
jpayne@68
|
314 def emit(self, record):
|
jpayne@68
|
315 self.watcher.records.append(record)
|
jpayne@68
|
316 msg = self.format(record)
|
jpayne@68
|
317 self.watcher.output.append(msg)
|
jpayne@68
|
318
|
jpayne@68
|
319
|
jpayne@68
|
320
|
jpayne@68
|
321 class _AssertLogsContext(_BaseTestCaseContext):
|
jpayne@68
|
322 """A context manager used to implement TestCase.assertLogs()."""
|
jpayne@68
|
323
|
jpayne@68
|
324 LOGGING_FORMAT = "%(levelname)s:%(name)s:%(message)s"
|
jpayne@68
|
325
|
jpayne@68
|
326 def __init__(self, test_case, logger_name, level):
|
jpayne@68
|
327 _BaseTestCaseContext.__init__(self, test_case)
|
jpayne@68
|
328 self.logger_name = logger_name
|
jpayne@68
|
329 if level:
|
jpayne@68
|
330 self.level = logging._nameToLevel.get(level, level)
|
jpayne@68
|
331 else:
|
jpayne@68
|
332 self.level = logging.INFO
|
jpayne@68
|
333 self.msg = None
|
jpayne@68
|
334
|
jpayne@68
|
335 def __enter__(self):
|
jpayne@68
|
336 if isinstance(self.logger_name, logging.Logger):
|
jpayne@68
|
337 logger = self.logger = self.logger_name
|
jpayne@68
|
338 else:
|
jpayne@68
|
339 logger = self.logger = logging.getLogger(self.logger_name)
|
jpayne@68
|
340 formatter = logging.Formatter(self.LOGGING_FORMAT)
|
jpayne@68
|
341 handler = _CapturingHandler()
|
jpayne@68
|
342 handler.setFormatter(formatter)
|
jpayne@68
|
343 self.watcher = handler.watcher
|
jpayne@68
|
344 self.old_handlers = logger.handlers[:]
|
jpayne@68
|
345 self.old_level = logger.level
|
jpayne@68
|
346 self.old_propagate = logger.propagate
|
jpayne@68
|
347 logger.handlers = [handler]
|
jpayne@68
|
348 logger.setLevel(self.level)
|
jpayne@68
|
349 logger.propagate = False
|
jpayne@68
|
350 return handler.watcher
|
jpayne@68
|
351
|
jpayne@68
|
352 def __exit__(self, exc_type, exc_value, tb):
|
jpayne@68
|
353 self.logger.handlers = self.old_handlers
|
jpayne@68
|
354 self.logger.propagate = self.old_propagate
|
jpayne@68
|
355 self.logger.setLevel(self.old_level)
|
jpayne@68
|
356 if exc_type is not None:
|
jpayne@68
|
357 # let unexpected exceptions pass through
|
jpayne@68
|
358 return False
|
jpayne@68
|
359 if len(self.watcher.records) == 0:
|
jpayne@68
|
360 self._raiseFailure(
|
jpayne@68
|
361 "no logs of level {} or higher triggered on {}"
|
jpayne@68
|
362 .format(logging.getLevelName(self.level), self.logger.name))
|
jpayne@68
|
363
|
jpayne@68
|
364
|
jpayne@68
|
365 class _OrderedChainMap(collections.ChainMap):
|
jpayne@68
|
366 def __iter__(self):
|
jpayne@68
|
367 seen = set()
|
jpayne@68
|
368 for mapping in self.maps:
|
jpayne@68
|
369 for k in mapping:
|
jpayne@68
|
370 if k not in seen:
|
jpayne@68
|
371 seen.add(k)
|
jpayne@68
|
372 yield k
|
jpayne@68
|
373
|
jpayne@68
|
374
|
jpayne@68
|
375 class TestCase(object):
|
jpayne@68
|
376 """A class whose instances are single test cases.
|
jpayne@68
|
377
|
jpayne@68
|
378 By default, the test code itself should be placed in a method named
|
jpayne@68
|
379 'runTest'.
|
jpayne@68
|
380
|
jpayne@68
|
381 If the fixture may be used for many test cases, create as
|
jpayne@68
|
382 many test methods as are needed. When instantiating such a TestCase
|
jpayne@68
|
383 subclass, specify in the constructor arguments the name of the test method
|
jpayne@68
|
384 that the instance is to execute.
|
jpayne@68
|
385
|
jpayne@68
|
386 Test authors should subclass TestCase for their own tests. Construction
|
jpayne@68
|
387 and deconstruction of the test's environment ('fixture') can be
|
jpayne@68
|
388 implemented by overriding the 'setUp' and 'tearDown' methods respectively.
|
jpayne@68
|
389
|
jpayne@68
|
390 If it is necessary to override the __init__ method, the base class
|
jpayne@68
|
391 __init__ method must always be called. It is important that subclasses
|
jpayne@68
|
392 should not change the signature of their __init__ method, since instances
|
jpayne@68
|
393 of the classes are instantiated automatically by parts of the framework
|
jpayne@68
|
394 in order to be run.
|
jpayne@68
|
395
|
jpayne@68
|
396 When subclassing TestCase, you can set these attributes:
|
jpayne@68
|
397 * failureException: determines which exception will be raised when
|
jpayne@68
|
398 the instance's assertion methods fail; test methods raising this
|
jpayne@68
|
399 exception will be deemed to have 'failed' rather than 'errored'.
|
jpayne@68
|
400 * longMessage: determines whether long messages (including repr of
|
jpayne@68
|
401 objects used in assert methods) will be printed on failure in *addition*
|
jpayne@68
|
402 to any explicit message passed.
|
jpayne@68
|
403 * maxDiff: sets the maximum length of a diff in failure messages
|
jpayne@68
|
404 by assert methods using difflib. It is looked up as an instance
|
jpayne@68
|
405 attribute so can be configured by individual tests if required.
|
jpayne@68
|
406 """
|
jpayne@68
|
407
|
jpayne@68
|
408 failureException = AssertionError
|
jpayne@68
|
409
|
jpayne@68
|
410 longMessage = True
|
jpayne@68
|
411
|
jpayne@68
|
412 maxDiff = 80*8
|
jpayne@68
|
413
|
jpayne@68
|
414 # If a string is longer than _diffThreshold, use normal comparison instead
|
jpayne@68
|
415 # of difflib. See #11763.
|
jpayne@68
|
416 _diffThreshold = 2**16
|
jpayne@68
|
417
|
jpayne@68
|
418 # Attribute used by TestSuite for classSetUp
|
jpayne@68
|
419
|
jpayne@68
|
420 _classSetupFailed = False
|
jpayne@68
|
421
|
jpayne@68
|
422 _class_cleanups = []
|
jpayne@68
|
423
|
jpayne@68
|
424 def __init__(self, methodName='runTest'):
|
jpayne@68
|
425 """Create an instance of the class that will use the named test
|
jpayne@68
|
426 method when executed. Raises a ValueError if the instance does
|
jpayne@68
|
427 not have a method with the specified name.
|
jpayne@68
|
428 """
|
jpayne@68
|
429 self._testMethodName = methodName
|
jpayne@68
|
430 self._outcome = None
|
jpayne@68
|
431 self._testMethodDoc = 'No test'
|
jpayne@68
|
432 try:
|
jpayne@68
|
433 testMethod = getattr(self, methodName)
|
jpayne@68
|
434 except AttributeError:
|
jpayne@68
|
435 if methodName != 'runTest':
|
jpayne@68
|
436 # we allow instantiation with no explicit method name
|
jpayne@68
|
437 # but not an *incorrect* or missing method name
|
jpayne@68
|
438 raise ValueError("no such test method in %s: %s" %
|
jpayne@68
|
439 (self.__class__, methodName))
|
jpayne@68
|
440 else:
|
jpayne@68
|
441 self._testMethodDoc = testMethod.__doc__
|
jpayne@68
|
442 self._cleanups = []
|
jpayne@68
|
443 self._subtest = None
|
jpayne@68
|
444
|
jpayne@68
|
445 # Map types to custom assertEqual functions that will compare
|
jpayne@68
|
446 # instances of said type in more detail to generate a more useful
|
jpayne@68
|
447 # error message.
|
jpayne@68
|
448 self._type_equality_funcs = {}
|
jpayne@68
|
449 self.addTypeEqualityFunc(dict, 'assertDictEqual')
|
jpayne@68
|
450 self.addTypeEqualityFunc(list, 'assertListEqual')
|
jpayne@68
|
451 self.addTypeEqualityFunc(tuple, 'assertTupleEqual')
|
jpayne@68
|
452 self.addTypeEqualityFunc(set, 'assertSetEqual')
|
jpayne@68
|
453 self.addTypeEqualityFunc(frozenset, 'assertSetEqual')
|
jpayne@68
|
454 self.addTypeEqualityFunc(str, 'assertMultiLineEqual')
|
jpayne@68
|
455
|
jpayne@68
|
456 def addTypeEqualityFunc(self, typeobj, function):
|
jpayne@68
|
457 """Add a type specific assertEqual style function to compare a type.
|
jpayne@68
|
458
|
jpayne@68
|
459 This method is for use by TestCase subclasses that need to register
|
jpayne@68
|
460 their own type equality functions to provide nicer error messages.
|
jpayne@68
|
461
|
jpayne@68
|
462 Args:
|
jpayne@68
|
463 typeobj: The data type to call this function on when both values
|
jpayne@68
|
464 are of the same type in assertEqual().
|
jpayne@68
|
465 function: The callable taking two arguments and an optional
|
jpayne@68
|
466 msg= argument that raises self.failureException with a
|
jpayne@68
|
467 useful error message when the two arguments are not equal.
|
jpayne@68
|
468 """
|
jpayne@68
|
469 self._type_equality_funcs[typeobj] = function
|
jpayne@68
|
470
|
jpayne@68
|
471 def addCleanup(*args, **kwargs):
|
jpayne@68
|
472 """Add a function, with arguments, to be called when the test is
|
jpayne@68
|
473 completed. Functions added are called on a LIFO basis and are
|
jpayne@68
|
474 called after tearDown on test failure or success.
|
jpayne@68
|
475
|
jpayne@68
|
476 Cleanup items are called even if setUp fails (unlike tearDown)."""
|
jpayne@68
|
477 if len(args) >= 2:
|
jpayne@68
|
478 self, function, *args = args
|
jpayne@68
|
479 elif not args:
|
jpayne@68
|
480 raise TypeError("descriptor 'addCleanup' of 'TestCase' object "
|
jpayne@68
|
481 "needs an argument")
|
jpayne@68
|
482 elif 'function' in kwargs:
|
jpayne@68
|
483 function = kwargs.pop('function')
|
jpayne@68
|
484 self, *args = args
|
jpayne@68
|
485 import warnings
|
jpayne@68
|
486 warnings.warn("Passing 'function' as keyword argument is deprecated",
|
jpayne@68
|
487 DeprecationWarning, stacklevel=2)
|
jpayne@68
|
488 else:
|
jpayne@68
|
489 raise TypeError('addCleanup expected at least 1 positional '
|
jpayne@68
|
490 'argument, got %d' % (len(args)-1))
|
jpayne@68
|
491 args = tuple(args)
|
jpayne@68
|
492
|
jpayne@68
|
493 self._cleanups.append((function, args, kwargs))
|
jpayne@68
|
494 addCleanup.__text_signature__ = '($self, function, /, *args, **kwargs)'
|
jpayne@68
|
495
|
jpayne@68
|
496 @classmethod
|
jpayne@68
|
497 def addClassCleanup(cls, function, /, *args, **kwargs):
|
jpayne@68
|
498 """Same as addCleanup, except the cleanup items are called even if
|
jpayne@68
|
499 setUpClass fails (unlike tearDownClass)."""
|
jpayne@68
|
500 cls._class_cleanups.append((function, args, kwargs))
|
jpayne@68
|
501
|
jpayne@68
|
502 def setUp(self):
|
jpayne@68
|
503 "Hook method for setting up the test fixture before exercising it."
|
jpayne@68
|
504 pass
|
jpayne@68
|
505
|
jpayne@68
|
506 def tearDown(self):
|
jpayne@68
|
507 "Hook method for deconstructing the test fixture after testing it."
|
jpayne@68
|
508 pass
|
jpayne@68
|
509
|
jpayne@68
|
510 @classmethod
|
jpayne@68
|
511 def setUpClass(cls):
|
jpayne@68
|
512 "Hook method for setting up class fixture before running tests in the class."
|
jpayne@68
|
513
|
jpayne@68
|
514 @classmethod
|
jpayne@68
|
515 def tearDownClass(cls):
|
jpayne@68
|
516 "Hook method for deconstructing the class fixture after running all tests in the class."
|
jpayne@68
|
517
|
jpayne@68
|
518 def countTestCases(self):
|
jpayne@68
|
519 return 1
|
jpayne@68
|
520
|
jpayne@68
|
521 def defaultTestResult(self):
|
jpayne@68
|
522 return result.TestResult()
|
jpayne@68
|
523
|
jpayne@68
|
524 def shortDescription(self):
|
jpayne@68
|
525 """Returns a one-line description of the test, or None if no
|
jpayne@68
|
526 description has been provided.
|
jpayne@68
|
527
|
jpayne@68
|
528 The default implementation of this method returns the first line of
|
jpayne@68
|
529 the specified test method's docstring.
|
jpayne@68
|
530 """
|
jpayne@68
|
531 doc = self._testMethodDoc
|
jpayne@68
|
532 return doc and doc.split("\n")[0].strip() or None
|
jpayne@68
|
533
|
jpayne@68
|
534
|
jpayne@68
|
535 def id(self):
|
jpayne@68
|
536 return "%s.%s" % (strclass(self.__class__), self._testMethodName)
|
jpayne@68
|
537
|
jpayne@68
|
538 def __eq__(self, other):
|
jpayne@68
|
539 if type(self) is not type(other):
|
jpayne@68
|
540 return NotImplemented
|
jpayne@68
|
541
|
jpayne@68
|
542 return self._testMethodName == other._testMethodName
|
jpayne@68
|
543
|
jpayne@68
|
544 def __hash__(self):
|
jpayne@68
|
545 return hash((type(self), self._testMethodName))
|
jpayne@68
|
546
|
jpayne@68
|
547 def __str__(self):
|
jpayne@68
|
548 return "%s (%s)" % (self._testMethodName, strclass(self.__class__))
|
jpayne@68
|
549
|
jpayne@68
|
550 def __repr__(self):
|
jpayne@68
|
551 return "<%s testMethod=%s>" % \
|
jpayne@68
|
552 (strclass(self.__class__), self._testMethodName)
|
jpayne@68
|
553
|
jpayne@68
|
554 def _addSkip(self, result, test_case, reason):
|
jpayne@68
|
555 addSkip = getattr(result, 'addSkip', None)
|
jpayne@68
|
556 if addSkip is not None:
|
jpayne@68
|
557 addSkip(test_case, reason)
|
jpayne@68
|
558 else:
|
jpayne@68
|
559 warnings.warn("TestResult has no addSkip method, skips not reported",
|
jpayne@68
|
560 RuntimeWarning, 2)
|
jpayne@68
|
561 result.addSuccess(test_case)
|
jpayne@68
|
562
|
jpayne@68
|
563 @contextlib.contextmanager
|
jpayne@68
|
564 def subTest(self, msg=_subtest_msg_sentinel, **params):
|
jpayne@68
|
565 """Return a context manager that will return the enclosed block
|
jpayne@68
|
566 of code in a subtest identified by the optional message and
|
jpayne@68
|
567 keyword parameters. A failure in the subtest marks the test
|
jpayne@68
|
568 case as failed but resumes execution at the end of the enclosed
|
jpayne@68
|
569 block, allowing further test code to be executed.
|
jpayne@68
|
570 """
|
jpayne@68
|
571 if self._outcome is None or not self._outcome.result_supports_subtests:
|
jpayne@68
|
572 yield
|
jpayne@68
|
573 return
|
jpayne@68
|
574 parent = self._subtest
|
jpayne@68
|
575 if parent is None:
|
jpayne@68
|
576 params_map = _OrderedChainMap(params)
|
jpayne@68
|
577 else:
|
jpayne@68
|
578 params_map = parent.params.new_child(params)
|
jpayne@68
|
579 self._subtest = _SubTest(self, msg, params_map)
|
jpayne@68
|
580 try:
|
jpayne@68
|
581 with self._outcome.testPartExecutor(self._subtest, isTest=True):
|
jpayne@68
|
582 yield
|
jpayne@68
|
583 if not self._outcome.success:
|
jpayne@68
|
584 result = self._outcome.result
|
jpayne@68
|
585 if result is not None and result.failfast:
|
jpayne@68
|
586 raise _ShouldStop
|
jpayne@68
|
587 elif self._outcome.expectedFailure:
|
jpayne@68
|
588 # If the test is expecting a failure, we really want to
|
jpayne@68
|
589 # stop now and register the expected failure.
|
jpayne@68
|
590 raise _ShouldStop
|
jpayne@68
|
591 finally:
|
jpayne@68
|
592 self._subtest = parent
|
jpayne@68
|
593
|
jpayne@68
|
594 def _feedErrorsToResult(self, result, errors):
|
jpayne@68
|
595 for test, exc_info in errors:
|
jpayne@68
|
596 if isinstance(test, _SubTest):
|
jpayne@68
|
597 result.addSubTest(test.test_case, test, exc_info)
|
jpayne@68
|
598 elif exc_info is not None:
|
jpayne@68
|
599 if issubclass(exc_info[0], self.failureException):
|
jpayne@68
|
600 result.addFailure(test, exc_info)
|
jpayne@68
|
601 else:
|
jpayne@68
|
602 result.addError(test, exc_info)
|
jpayne@68
|
603
|
jpayne@68
|
604 def _addExpectedFailure(self, result, exc_info):
|
jpayne@68
|
605 try:
|
jpayne@68
|
606 addExpectedFailure = result.addExpectedFailure
|
jpayne@68
|
607 except AttributeError:
|
jpayne@68
|
608 warnings.warn("TestResult has no addExpectedFailure method, reporting as passes",
|
jpayne@68
|
609 RuntimeWarning)
|
jpayne@68
|
610 result.addSuccess(self)
|
jpayne@68
|
611 else:
|
jpayne@68
|
612 addExpectedFailure(self, exc_info)
|
jpayne@68
|
613
|
jpayne@68
|
614 def _addUnexpectedSuccess(self, result):
|
jpayne@68
|
615 try:
|
jpayne@68
|
616 addUnexpectedSuccess = result.addUnexpectedSuccess
|
jpayne@68
|
617 except AttributeError:
|
jpayne@68
|
618 warnings.warn("TestResult has no addUnexpectedSuccess method, reporting as failure",
|
jpayne@68
|
619 RuntimeWarning)
|
jpayne@68
|
620 # We need to pass an actual exception and traceback to addFailure,
|
jpayne@68
|
621 # otherwise the legacy result can choke.
|
jpayne@68
|
622 try:
|
jpayne@68
|
623 raise _UnexpectedSuccess from None
|
jpayne@68
|
624 except _UnexpectedSuccess:
|
jpayne@68
|
625 result.addFailure(self, sys.exc_info())
|
jpayne@68
|
626 else:
|
jpayne@68
|
627 addUnexpectedSuccess(self)
|
jpayne@68
|
628
|
jpayne@68
|
629 def _callSetUp(self):
|
jpayne@68
|
630 self.setUp()
|
jpayne@68
|
631
|
jpayne@68
|
632 def _callTestMethod(self, method):
|
jpayne@68
|
633 method()
|
jpayne@68
|
634
|
jpayne@68
|
635 def _callTearDown(self):
|
jpayne@68
|
636 self.tearDown()
|
jpayne@68
|
637
|
jpayne@68
|
638 def _callCleanup(self, function, /, *args, **kwargs):
|
jpayne@68
|
639 function(*args, **kwargs)
|
jpayne@68
|
640
|
jpayne@68
|
641 def run(self, result=None):
|
jpayne@68
|
642 orig_result = result
|
jpayne@68
|
643 if result is None:
|
jpayne@68
|
644 result = self.defaultTestResult()
|
jpayne@68
|
645 startTestRun = getattr(result, 'startTestRun', None)
|
jpayne@68
|
646 if startTestRun is not None:
|
jpayne@68
|
647 startTestRun()
|
jpayne@68
|
648
|
jpayne@68
|
649 result.startTest(self)
|
jpayne@68
|
650
|
jpayne@68
|
651 testMethod = getattr(self, self._testMethodName)
|
jpayne@68
|
652 if (getattr(self.__class__, "__unittest_skip__", False) or
|
jpayne@68
|
653 getattr(testMethod, "__unittest_skip__", False)):
|
jpayne@68
|
654 # If the class or method was skipped.
|
jpayne@68
|
655 try:
|
jpayne@68
|
656 skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
|
jpayne@68
|
657 or getattr(testMethod, '__unittest_skip_why__', ''))
|
jpayne@68
|
658 self._addSkip(result, self, skip_why)
|
jpayne@68
|
659 finally:
|
jpayne@68
|
660 result.stopTest(self)
|
jpayne@68
|
661 return
|
jpayne@68
|
662 expecting_failure_method = getattr(testMethod,
|
jpayne@68
|
663 "__unittest_expecting_failure__", False)
|
jpayne@68
|
664 expecting_failure_class = getattr(self,
|
jpayne@68
|
665 "__unittest_expecting_failure__", False)
|
jpayne@68
|
666 expecting_failure = expecting_failure_class or expecting_failure_method
|
jpayne@68
|
667 outcome = _Outcome(result)
|
jpayne@68
|
668 try:
|
jpayne@68
|
669 self._outcome = outcome
|
jpayne@68
|
670
|
jpayne@68
|
671 with outcome.testPartExecutor(self):
|
jpayne@68
|
672 self._callSetUp()
|
jpayne@68
|
673 if outcome.success:
|
jpayne@68
|
674 outcome.expecting_failure = expecting_failure
|
jpayne@68
|
675 with outcome.testPartExecutor(self, isTest=True):
|
jpayne@68
|
676 self._callTestMethod(testMethod)
|
jpayne@68
|
677 outcome.expecting_failure = False
|
jpayne@68
|
678 with outcome.testPartExecutor(self):
|
jpayne@68
|
679 self._callTearDown()
|
jpayne@68
|
680
|
jpayne@68
|
681 self.doCleanups()
|
jpayne@68
|
682 for test, reason in outcome.skipped:
|
jpayne@68
|
683 self._addSkip(result, test, reason)
|
jpayne@68
|
684 self._feedErrorsToResult(result, outcome.errors)
|
jpayne@68
|
685 if outcome.success:
|
jpayne@68
|
686 if expecting_failure:
|
jpayne@68
|
687 if outcome.expectedFailure:
|
jpayne@68
|
688 self._addExpectedFailure(result, outcome.expectedFailure)
|
jpayne@68
|
689 else:
|
jpayne@68
|
690 self._addUnexpectedSuccess(result)
|
jpayne@68
|
691 else:
|
jpayne@68
|
692 result.addSuccess(self)
|
jpayne@68
|
693 return result
|
jpayne@68
|
694 finally:
|
jpayne@68
|
695 result.stopTest(self)
|
jpayne@68
|
696 if orig_result is None:
|
jpayne@68
|
697 stopTestRun = getattr(result, 'stopTestRun', None)
|
jpayne@68
|
698 if stopTestRun is not None:
|
jpayne@68
|
699 stopTestRun()
|
jpayne@68
|
700
|
jpayne@68
|
701 # explicitly break reference cycles:
|
jpayne@68
|
702 # outcome.errors -> frame -> outcome -> outcome.errors
|
jpayne@68
|
703 # outcome.expectedFailure -> frame -> outcome -> outcome.expectedFailure
|
jpayne@68
|
704 outcome.errors.clear()
|
jpayne@68
|
705 outcome.expectedFailure = None
|
jpayne@68
|
706
|
jpayne@68
|
707 # clear the outcome, no more needed
|
jpayne@68
|
708 self._outcome = None
|
jpayne@68
|
709
|
jpayne@68
|
710 def doCleanups(self):
|
jpayne@68
|
711 """Execute all cleanup functions. Normally called for you after
|
jpayne@68
|
712 tearDown."""
|
jpayne@68
|
713 outcome = self._outcome or _Outcome()
|
jpayne@68
|
714 while self._cleanups:
|
jpayne@68
|
715 function, args, kwargs = self._cleanups.pop()
|
jpayne@68
|
716 with outcome.testPartExecutor(self):
|
jpayne@68
|
717 self._callCleanup(function, *args, **kwargs)
|
jpayne@68
|
718
|
jpayne@68
|
719 # return this for backwards compatibility
|
jpayne@68
|
720 # even though we no longer use it internally
|
jpayne@68
|
721 return outcome.success
|
jpayne@68
|
722
|
jpayne@68
|
723 @classmethod
|
jpayne@68
|
724 def doClassCleanups(cls):
|
jpayne@68
|
725 """Execute all class cleanup functions. Normally called for you after
|
jpayne@68
|
726 tearDownClass."""
|
jpayne@68
|
727 cls.tearDown_exceptions = []
|
jpayne@68
|
728 while cls._class_cleanups:
|
jpayne@68
|
729 function, args, kwargs = cls._class_cleanups.pop()
|
jpayne@68
|
730 try:
|
jpayne@68
|
731 function(*args, **kwargs)
|
jpayne@68
|
732 except Exception as exc:
|
jpayne@68
|
733 cls.tearDown_exceptions.append(sys.exc_info())
|
jpayne@68
|
734
|
jpayne@68
|
735 def __call__(self, *args, **kwds):
|
jpayne@68
|
736 return self.run(*args, **kwds)
|
jpayne@68
|
737
|
jpayne@68
|
738 def debug(self):
|
jpayne@68
|
739 """Run the test without collecting errors in a TestResult"""
|
jpayne@68
|
740 self.setUp()
|
jpayne@68
|
741 getattr(self, self._testMethodName)()
|
jpayne@68
|
742 self.tearDown()
|
jpayne@68
|
743 while self._cleanups:
|
jpayne@68
|
744 function, args, kwargs = self._cleanups.pop(-1)
|
jpayne@68
|
745 function(*args, **kwargs)
|
jpayne@68
|
746
|
jpayne@68
|
747 def skipTest(self, reason):
|
jpayne@68
|
748 """Skip this test."""
|
jpayne@68
|
749 raise SkipTest(reason)
|
jpayne@68
|
750
|
jpayne@68
|
751 def fail(self, msg=None):
|
jpayne@68
|
752 """Fail immediately, with the given message."""
|
jpayne@68
|
753 raise self.failureException(msg)
|
jpayne@68
|
754
|
jpayne@68
|
755 def assertFalse(self, expr, msg=None):
|
jpayne@68
|
756 """Check that the expression is false."""
|
jpayne@68
|
757 if expr:
|
jpayne@68
|
758 msg = self._formatMessage(msg, "%s is not false" % safe_repr(expr))
|
jpayne@68
|
759 raise self.failureException(msg)
|
jpayne@68
|
760
|
jpayne@68
|
761 def assertTrue(self, expr, msg=None):
|
jpayne@68
|
762 """Check that the expression is true."""
|
jpayne@68
|
763 if not expr:
|
jpayne@68
|
764 msg = self._formatMessage(msg, "%s is not true" % safe_repr(expr))
|
jpayne@68
|
765 raise self.failureException(msg)
|
jpayne@68
|
766
|
jpayne@68
|
767 def _formatMessage(self, msg, standardMsg):
|
jpayne@68
|
768 """Honour the longMessage attribute when generating failure messages.
|
jpayne@68
|
769 If longMessage is False this means:
|
jpayne@68
|
770 * Use only an explicit message if it is provided
|
jpayne@68
|
771 * Otherwise use the standard message for the assert
|
jpayne@68
|
772
|
jpayne@68
|
773 If longMessage is True:
|
jpayne@68
|
774 * Use the standard message
|
jpayne@68
|
775 * If an explicit message is provided, plus ' : ' and the explicit message
|
jpayne@68
|
776 """
|
jpayne@68
|
777 if not self.longMessage:
|
jpayne@68
|
778 return msg or standardMsg
|
jpayne@68
|
779 if msg is None:
|
jpayne@68
|
780 return standardMsg
|
jpayne@68
|
781 try:
|
jpayne@68
|
782 # don't switch to '{}' formatting in Python 2.X
|
jpayne@68
|
783 # it changes the way unicode input is handled
|
jpayne@68
|
784 return '%s : %s' % (standardMsg, msg)
|
jpayne@68
|
785 except UnicodeDecodeError:
|
jpayne@68
|
786 return '%s : %s' % (safe_repr(standardMsg), safe_repr(msg))
|
jpayne@68
|
787
|
jpayne@68
|
788 def assertRaises(self, expected_exception, *args, **kwargs):
|
jpayne@68
|
789 """Fail unless an exception of class expected_exception is raised
|
jpayne@68
|
790 by the callable when invoked with specified positional and
|
jpayne@68
|
791 keyword arguments. If a different type of exception is
|
jpayne@68
|
792 raised, it will not be caught, and the test case will be
|
jpayne@68
|
793 deemed to have suffered an error, exactly as for an
|
jpayne@68
|
794 unexpected exception.
|
jpayne@68
|
795
|
jpayne@68
|
796 If called with the callable and arguments omitted, will return a
|
jpayne@68
|
797 context object used like this::
|
jpayne@68
|
798
|
jpayne@68
|
799 with self.assertRaises(SomeException):
|
jpayne@68
|
800 do_something()
|
jpayne@68
|
801
|
jpayne@68
|
802 An optional keyword argument 'msg' can be provided when assertRaises
|
jpayne@68
|
803 is used as a context object.
|
jpayne@68
|
804
|
jpayne@68
|
805 The context manager keeps a reference to the exception as
|
jpayne@68
|
806 the 'exception' attribute. This allows you to inspect the
|
jpayne@68
|
807 exception after the assertion::
|
jpayne@68
|
808
|
jpayne@68
|
809 with self.assertRaises(SomeException) as cm:
|
jpayne@68
|
810 do_something()
|
jpayne@68
|
811 the_exception = cm.exception
|
jpayne@68
|
812 self.assertEqual(the_exception.error_code, 3)
|
jpayne@68
|
813 """
|
jpayne@68
|
814 context = _AssertRaisesContext(expected_exception, self)
|
jpayne@68
|
815 try:
|
jpayne@68
|
816 return context.handle('assertRaises', args, kwargs)
|
jpayne@68
|
817 finally:
|
jpayne@68
|
818 # bpo-23890: manually break a reference cycle
|
jpayne@68
|
819 context = None
|
jpayne@68
|
820
|
jpayne@68
|
821 def assertWarns(self, expected_warning, *args, **kwargs):
|
jpayne@68
|
822 """Fail unless a warning of class warnClass is triggered
|
jpayne@68
|
823 by the callable when invoked with specified positional and
|
jpayne@68
|
824 keyword arguments. If a different type of warning is
|
jpayne@68
|
825 triggered, it will not be handled: depending on the other
|
jpayne@68
|
826 warning filtering rules in effect, it might be silenced, printed
|
jpayne@68
|
827 out, or raised as an exception.
|
jpayne@68
|
828
|
jpayne@68
|
829 If called with the callable and arguments omitted, will return a
|
jpayne@68
|
830 context object used like this::
|
jpayne@68
|
831
|
jpayne@68
|
832 with self.assertWarns(SomeWarning):
|
jpayne@68
|
833 do_something()
|
jpayne@68
|
834
|
jpayne@68
|
835 An optional keyword argument 'msg' can be provided when assertWarns
|
jpayne@68
|
836 is used as a context object.
|
jpayne@68
|
837
|
jpayne@68
|
838 The context manager keeps a reference to the first matching
|
jpayne@68
|
839 warning as the 'warning' attribute; similarly, the 'filename'
|
jpayne@68
|
840 and 'lineno' attributes give you information about the line
|
jpayne@68
|
841 of Python code from which the warning was triggered.
|
jpayne@68
|
842 This allows you to inspect the warning after the assertion::
|
jpayne@68
|
843
|
jpayne@68
|
844 with self.assertWarns(SomeWarning) as cm:
|
jpayne@68
|
845 do_something()
|
jpayne@68
|
846 the_warning = cm.warning
|
jpayne@68
|
847 self.assertEqual(the_warning.some_attribute, 147)
|
jpayne@68
|
848 """
|
jpayne@68
|
849 context = _AssertWarnsContext(expected_warning, self)
|
jpayne@68
|
850 return context.handle('assertWarns', args, kwargs)
|
jpayne@68
|
851
|
jpayne@68
|
852 def assertLogs(self, logger=None, level=None):
|
jpayne@68
|
853 """Fail unless a log message of level *level* or higher is emitted
|
jpayne@68
|
854 on *logger_name* or its children. If omitted, *level* defaults to
|
jpayne@68
|
855 INFO and *logger* defaults to the root logger.
|
jpayne@68
|
856
|
jpayne@68
|
857 This method must be used as a context manager, and will yield
|
jpayne@68
|
858 a recording object with two attributes: `output` and `records`.
|
jpayne@68
|
859 At the end of the context manager, the `output` attribute will
|
jpayne@68
|
860 be a list of the matching formatted log messages and the
|
jpayne@68
|
861 `records` attribute will be a list of the corresponding LogRecord
|
jpayne@68
|
862 objects.
|
jpayne@68
|
863
|
jpayne@68
|
864 Example::
|
jpayne@68
|
865
|
jpayne@68
|
866 with self.assertLogs('foo', level='INFO') as cm:
|
jpayne@68
|
867 logging.getLogger('foo').info('first message')
|
jpayne@68
|
868 logging.getLogger('foo.bar').error('second message')
|
jpayne@68
|
869 self.assertEqual(cm.output, ['INFO:foo:first message',
|
jpayne@68
|
870 'ERROR:foo.bar:second message'])
|
jpayne@68
|
871 """
|
jpayne@68
|
872 return _AssertLogsContext(self, logger, level)
|
jpayne@68
|
873
|
jpayne@68
|
874 def _getAssertEqualityFunc(self, first, second):
|
jpayne@68
|
875 """Get a detailed comparison function for the types of the two args.
|
jpayne@68
|
876
|
jpayne@68
|
877 Returns: A callable accepting (first, second, msg=None) that will
|
jpayne@68
|
878 raise a failure exception if first != second with a useful human
|
jpayne@68
|
879 readable error message for those types.
|
jpayne@68
|
880 """
|
jpayne@68
|
881 #
|
jpayne@68
|
882 # NOTE(gregory.p.smith): I considered isinstance(first, type(second))
|
jpayne@68
|
883 # and vice versa. I opted for the conservative approach in case
|
jpayne@68
|
884 # subclasses are not intended to be compared in detail to their super
|
jpayne@68
|
885 # class instances using a type equality func. This means testing
|
jpayne@68
|
886 # subtypes won't automagically use the detailed comparison. Callers
|
jpayne@68
|
887 # should use their type specific assertSpamEqual method to compare
|
jpayne@68
|
888 # subclasses if the detailed comparison is desired and appropriate.
|
jpayne@68
|
889 # See the discussion in http://bugs.python.org/issue2578.
|
jpayne@68
|
890 #
|
jpayne@68
|
891 if type(first) is type(second):
|
jpayne@68
|
892 asserter = self._type_equality_funcs.get(type(first))
|
jpayne@68
|
893 if asserter is not None:
|
jpayne@68
|
894 if isinstance(asserter, str):
|
jpayne@68
|
895 asserter = getattr(self, asserter)
|
jpayne@68
|
896 return asserter
|
jpayne@68
|
897
|
jpayne@68
|
898 return self._baseAssertEqual
|
jpayne@68
|
899
|
jpayne@68
|
900 def _baseAssertEqual(self, first, second, msg=None):
|
jpayne@68
|
901 """The default assertEqual implementation, not type specific."""
|
jpayne@68
|
902 if not first == second:
|
jpayne@68
|
903 standardMsg = '%s != %s' % _common_shorten_repr(first, second)
|
jpayne@68
|
904 msg = self._formatMessage(msg, standardMsg)
|
jpayne@68
|
905 raise self.failureException(msg)
|
jpayne@68
|
906
|
jpayne@68
|
907 def assertEqual(self, first, second, msg=None):
|
jpayne@68
|
908 """Fail if the two objects are unequal as determined by the '=='
|
jpayne@68
|
909 operator.
|
jpayne@68
|
910 """
|
jpayne@68
|
911 assertion_func = self._getAssertEqualityFunc(first, second)
|
jpayne@68
|
912 assertion_func(first, second, msg=msg)
|
jpayne@68
|
913
|
jpayne@68
|
914 def assertNotEqual(self, first, second, msg=None):
|
jpayne@68
|
915 """Fail if the two objects are equal as determined by the '!='
|
jpayne@68
|
916 operator.
|
jpayne@68
|
917 """
|
jpayne@68
|
918 if not first != second:
|
jpayne@68
|
919 msg = self._formatMessage(msg, '%s == %s' % (safe_repr(first),
|
jpayne@68
|
920 safe_repr(second)))
|
jpayne@68
|
921 raise self.failureException(msg)
|
jpayne@68
|
922
|
jpayne@68
|
923 def assertAlmostEqual(self, first, second, places=None, msg=None,
|
jpayne@68
|
924 delta=None):
|
jpayne@68
|
925 """Fail if the two objects are unequal as determined by their
|
jpayne@68
|
926 difference rounded to the given number of decimal places
|
jpayne@68
|
927 (default 7) and comparing to zero, or by comparing that the
|
jpayne@68
|
928 difference between the two objects is more than the given
|
jpayne@68
|
929 delta.
|
jpayne@68
|
930
|
jpayne@68
|
931 Note that decimal places (from zero) are usually not the same
|
jpayne@68
|
932 as significant digits (measured from the most significant digit).
|
jpayne@68
|
933
|
jpayne@68
|
934 If the two objects compare equal then they will automatically
|
jpayne@68
|
935 compare almost equal.
|
jpayne@68
|
936 """
|
jpayne@68
|
937 if first == second:
|
jpayne@68
|
938 # shortcut
|
jpayne@68
|
939 return
|
jpayne@68
|
940 if delta is not None and places is not None:
|
jpayne@68
|
941 raise TypeError("specify delta or places not both")
|
jpayne@68
|
942
|
jpayne@68
|
943 diff = abs(first - second)
|
jpayne@68
|
944 if delta is not None:
|
jpayne@68
|
945 if diff <= delta:
|
jpayne@68
|
946 return
|
jpayne@68
|
947
|
jpayne@68
|
948 standardMsg = '%s != %s within %s delta (%s difference)' % (
|
jpayne@68
|
949 safe_repr(first),
|
jpayne@68
|
950 safe_repr(second),
|
jpayne@68
|
951 safe_repr(delta),
|
jpayne@68
|
952 safe_repr(diff))
|
jpayne@68
|
953 else:
|
jpayne@68
|
954 if places is None:
|
jpayne@68
|
955 places = 7
|
jpayne@68
|
956
|
jpayne@68
|
957 if round(diff, places) == 0:
|
jpayne@68
|
958 return
|
jpayne@68
|
959
|
jpayne@68
|
960 standardMsg = '%s != %s within %r places (%s difference)' % (
|
jpayne@68
|
961 safe_repr(first),
|
jpayne@68
|
962 safe_repr(second),
|
jpayne@68
|
963 places,
|
jpayne@68
|
964 safe_repr(diff))
|
jpayne@68
|
965 msg = self._formatMessage(msg, standardMsg)
|
jpayne@68
|
966 raise self.failureException(msg)
|
jpayne@68
|
967
|
jpayne@68
|
968 def assertNotAlmostEqual(self, first, second, places=None, msg=None,
|
jpayne@68
|
969 delta=None):
|
jpayne@68
|
970 """Fail if the two objects are equal as determined by their
|
jpayne@68
|
971 difference rounded to the given number of decimal places
|
jpayne@68
|
972 (default 7) and comparing to zero, or by comparing that the
|
jpayne@68
|
973 difference between the two objects is less than the given delta.
|
jpayne@68
|
974
|
jpayne@68
|
975 Note that decimal places (from zero) are usually not the same
|
jpayne@68
|
976 as significant digits (measured from the most significant digit).
|
jpayne@68
|
977
|
jpayne@68
|
978 Objects that are equal automatically fail.
|
jpayne@68
|
979 """
|
jpayne@68
|
980 if delta is not None and places is not None:
|
jpayne@68
|
981 raise TypeError("specify delta or places not both")
|
jpayne@68
|
982 diff = abs(first - second)
|
jpayne@68
|
983 if delta is not None:
|
jpayne@68
|
984 if not (first == second) and diff > delta:
|
jpayne@68
|
985 return
|
jpayne@68
|
986 standardMsg = '%s == %s within %s delta (%s difference)' % (
|
jpayne@68
|
987 safe_repr(first),
|
jpayne@68
|
988 safe_repr(second),
|
jpayne@68
|
989 safe_repr(delta),
|
jpayne@68
|
990 safe_repr(diff))
|
jpayne@68
|
991 else:
|
jpayne@68
|
992 if places is None:
|
jpayne@68
|
993 places = 7
|
jpayne@68
|
994 if not (first == second) and round(diff, places) != 0:
|
jpayne@68
|
995 return
|
jpayne@68
|
996 standardMsg = '%s == %s within %r places' % (safe_repr(first),
|
jpayne@68
|
997 safe_repr(second),
|
jpayne@68
|
998 places)
|
jpayne@68
|
999
|
jpayne@68
|
1000 msg = self._formatMessage(msg, standardMsg)
|
jpayne@68
|
1001 raise self.failureException(msg)
|
jpayne@68
|
1002
|
jpayne@68
|
1003 def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None):
|
jpayne@68
|
1004 """An equality assertion for ordered sequences (like lists and tuples).
|
jpayne@68
|
1005
|
jpayne@68
|
1006 For the purposes of this function, a valid ordered sequence type is one
|
jpayne@68
|
1007 which can be indexed, has a length, and has an equality operator.
|
jpayne@68
|
1008
|
jpayne@68
|
1009 Args:
|
jpayne@68
|
1010 seq1: The first sequence to compare.
|
jpayne@68
|
1011 seq2: The second sequence to compare.
|
jpayne@68
|
1012 seq_type: The expected datatype of the sequences, or None if no
|
jpayne@68
|
1013 datatype should be enforced.
|
jpayne@68
|
1014 msg: Optional message to use on failure instead of a list of
|
jpayne@68
|
1015 differences.
|
jpayne@68
|
1016 """
|
jpayne@68
|
1017 if seq_type is not None:
|
jpayne@68
|
1018 seq_type_name = seq_type.__name__
|
jpayne@68
|
1019 if not isinstance(seq1, seq_type):
|
jpayne@68
|
1020 raise self.failureException('First sequence is not a %s: %s'
|
jpayne@68
|
1021 % (seq_type_name, safe_repr(seq1)))
|
jpayne@68
|
1022 if not isinstance(seq2, seq_type):
|
jpayne@68
|
1023 raise self.failureException('Second sequence is not a %s: %s'
|
jpayne@68
|
1024 % (seq_type_name, safe_repr(seq2)))
|
jpayne@68
|
1025 else:
|
jpayne@68
|
1026 seq_type_name = "sequence"
|
jpayne@68
|
1027
|
jpayne@68
|
1028 differing = None
|
jpayne@68
|
1029 try:
|
jpayne@68
|
1030 len1 = len(seq1)
|
jpayne@68
|
1031 except (TypeError, NotImplementedError):
|
jpayne@68
|
1032 differing = 'First %s has no length. Non-sequence?' % (
|
jpayne@68
|
1033 seq_type_name)
|
jpayne@68
|
1034
|
jpayne@68
|
1035 if differing is None:
|
jpayne@68
|
1036 try:
|
jpayne@68
|
1037 len2 = len(seq2)
|
jpayne@68
|
1038 except (TypeError, NotImplementedError):
|
jpayne@68
|
1039 differing = 'Second %s has no length. Non-sequence?' % (
|
jpayne@68
|
1040 seq_type_name)
|
jpayne@68
|
1041
|
jpayne@68
|
1042 if differing is None:
|
jpayne@68
|
1043 if seq1 == seq2:
|
jpayne@68
|
1044 return
|
jpayne@68
|
1045
|
jpayne@68
|
1046 differing = '%ss differ: %s != %s\n' % (
|
jpayne@68
|
1047 (seq_type_name.capitalize(),) +
|
jpayne@68
|
1048 _common_shorten_repr(seq1, seq2))
|
jpayne@68
|
1049
|
jpayne@68
|
1050 for i in range(min(len1, len2)):
|
jpayne@68
|
1051 try:
|
jpayne@68
|
1052 item1 = seq1[i]
|
jpayne@68
|
1053 except (TypeError, IndexError, NotImplementedError):
|
jpayne@68
|
1054 differing += ('\nUnable to index element %d of first %s\n' %
|
jpayne@68
|
1055 (i, seq_type_name))
|
jpayne@68
|
1056 break
|
jpayne@68
|
1057
|
jpayne@68
|
1058 try:
|
jpayne@68
|
1059 item2 = seq2[i]
|
jpayne@68
|
1060 except (TypeError, IndexError, NotImplementedError):
|
jpayne@68
|
1061 differing += ('\nUnable to index element %d of second %s\n' %
|
jpayne@68
|
1062 (i, seq_type_name))
|
jpayne@68
|
1063 break
|
jpayne@68
|
1064
|
jpayne@68
|
1065 if item1 != item2:
|
jpayne@68
|
1066 differing += ('\nFirst differing element %d:\n%s\n%s\n' %
|
jpayne@68
|
1067 ((i,) + _common_shorten_repr(item1, item2)))
|
jpayne@68
|
1068 break
|
jpayne@68
|
1069 else:
|
jpayne@68
|
1070 if (len1 == len2 and seq_type is None and
|
jpayne@68
|
1071 type(seq1) != type(seq2)):
|
jpayne@68
|
1072 # The sequences are the same, but have differing types.
|
jpayne@68
|
1073 return
|
jpayne@68
|
1074
|
jpayne@68
|
1075 if len1 > len2:
|
jpayne@68
|
1076 differing += ('\nFirst %s contains %d additional '
|
jpayne@68
|
1077 'elements.\n' % (seq_type_name, len1 - len2))
|
jpayne@68
|
1078 try:
|
jpayne@68
|
1079 differing += ('First extra element %d:\n%s\n' %
|
jpayne@68
|
1080 (len2, safe_repr(seq1[len2])))
|
jpayne@68
|
1081 except (TypeError, IndexError, NotImplementedError):
|
jpayne@68
|
1082 differing += ('Unable to index element %d '
|
jpayne@68
|
1083 'of first %s\n' % (len2, seq_type_name))
|
jpayne@68
|
1084 elif len1 < len2:
|
jpayne@68
|
1085 differing += ('\nSecond %s contains %d additional '
|
jpayne@68
|
1086 'elements.\n' % (seq_type_name, len2 - len1))
|
jpayne@68
|
1087 try:
|
jpayne@68
|
1088 differing += ('First extra element %d:\n%s\n' %
|
jpayne@68
|
1089 (len1, safe_repr(seq2[len1])))
|
jpayne@68
|
1090 except (TypeError, IndexError, NotImplementedError):
|
jpayne@68
|
1091 differing += ('Unable to index element %d '
|
jpayne@68
|
1092 'of second %s\n' % (len1, seq_type_name))
|
jpayne@68
|
1093 standardMsg = differing
|
jpayne@68
|
1094 diffMsg = '\n' + '\n'.join(
|
jpayne@68
|
1095 difflib.ndiff(pprint.pformat(seq1).splitlines(),
|
jpayne@68
|
1096 pprint.pformat(seq2).splitlines()))
|
jpayne@68
|
1097
|
jpayne@68
|
1098 standardMsg = self._truncateMessage(standardMsg, diffMsg)
|
jpayne@68
|
1099 msg = self._formatMessage(msg, standardMsg)
|
jpayne@68
|
1100 self.fail(msg)
|
jpayne@68
|
1101
|
jpayne@68
|
1102 def _truncateMessage(self, message, diff):
|
jpayne@68
|
1103 max_diff = self.maxDiff
|
jpayne@68
|
1104 if max_diff is None or len(diff) <= max_diff:
|
jpayne@68
|
1105 return message + diff
|
jpayne@68
|
1106 return message + (DIFF_OMITTED % len(diff))
|
jpayne@68
|
1107
|
jpayne@68
|
1108 def assertListEqual(self, list1, list2, msg=None):
|
jpayne@68
|
1109 """A list-specific equality assertion.
|
jpayne@68
|
1110
|
jpayne@68
|
1111 Args:
|
jpayne@68
|
1112 list1: The first list to compare.
|
jpayne@68
|
1113 list2: The second list to compare.
|
jpayne@68
|
1114 msg: Optional message to use on failure instead of a list of
|
jpayne@68
|
1115 differences.
|
jpayne@68
|
1116
|
jpayne@68
|
1117 """
|
jpayne@68
|
1118 self.assertSequenceEqual(list1, list2, msg, seq_type=list)
|
jpayne@68
|
1119
|
jpayne@68
|
1120 def assertTupleEqual(self, tuple1, tuple2, msg=None):
|
jpayne@68
|
1121 """A tuple-specific equality assertion.
|
jpayne@68
|
1122
|
jpayne@68
|
1123 Args:
|
jpayne@68
|
1124 tuple1: The first tuple to compare.
|
jpayne@68
|
1125 tuple2: The second tuple to compare.
|
jpayne@68
|
1126 msg: Optional message to use on failure instead of a list of
|
jpayne@68
|
1127 differences.
|
jpayne@68
|
1128 """
|
jpayne@68
|
1129 self.assertSequenceEqual(tuple1, tuple2, msg, seq_type=tuple)
|
jpayne@68
|
1130
|
jpayne@68
|
1131 def assertSetEqual(self, set1, set2, msg=None):
|
jpayne@68
|
1132 """A set-specific equality assertion.
|
jpayne@68
|
1133
|
jpayne@68
|
1134 Args:
|
jpayne@68
|
1135 set1: The first set to compare.
|
jpayne@68
|
1136 set2: The second set to compare.
|
jpayne@68
|
1137 msg: Optional message to use on failure instead of a list of
|
jpayne@68
|
1138 differences.
|
jpayne@68
|
1139
|
jpayne@68
|
1140 assertSetEqual uses ducktyping to support different types of sets, and
|
jpayne@68
|
1141 is optimized for sets specifically (parameters must support a
|
jpayne@68
|
1142 difference method).
|
jpayne@68
|
1143 """
|
jpayne@68
|
1144 try:
|
jpayne@68
|
1145 difference1 = set1.difference(set2)
|
jpayne@68
|
1146 except TypeError as e:
|
jpayne@68
|
1147 self.fail('invalid type when attempting set difference: %s' % e)
|
jpayne@68
|
1148 except AttributeError as e:
|
jpayne@68
|
1149 self.fail('first argument does not support set difference: %s' % e)
|
jpayne@68
|
1150
|
jpayne@68
|
1151 try:
|
jpayne@68
|
1152 difference2 = set2.difference(set1)
|
jpayne@68
|
1153 except TypeError as e:
|
jpayne@68
|
1154 self.fail('invalid type when attempting set difference: %s' % e)
|
jpayne@68
|
1155 except AttributeError as e:
|
jpayne@68
|
1156 self.fail('second argument does not support set difference: %s' % e)
|
jpayne@68
|
1157
|
jpayne@68
|
1158 if not (difference1 or difference2):
|
jpayne@68
|
1159 return
|
jpayne@68
|
1160
|
jpayne@68
|
1161 lines = []
|
jpayne@68
|
1162 if difference1:
|
jpayne@68
|
1163 lines.append('Items in the first set but not the second:')
|
jpayne@68
|
1164 for item in difference1:
|
jpayne@68
|
1165 lines.append(repr(item))
|
jpayne@68
|
1166 if difference2:
|
jpayne@68
|
1167 lines.append('Items in the second set but not the first:')
|
jpayne@68
|
1168 for item in difference2:
|
jpayne@68
|
1169 lines.append(repr(item))
|
jpayne@68
|
1170
|
jpayne@68
|
1171 standardMsg = '\n'.join(lines)
|
jpayne@68
|
1172 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1173
|
jpayne@68
|
1174 def assertIn(self, member, container, msg=None):
|
jpayne@68
|
1175 """Just like self.assertTrue(a in b), but with a nicer default message."""
|
jpayne@68
|
1176 if member not in container:
|
jpayne@68
|
1177 standardMsg = '%s not found in %s' % (safe_repr(member),
|
jpayne@68
|
1178 safe_repr(container))
|
jpayne@68
|
1179 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1180
|
jpayne@68
|
1181 def assertNotIn(self, member, container, msg=None):
|
jpayne@68
|
1182 """Just like self.assertTrue(a not in b), but with a nicer default message."""
|
jpayne@68
|
1183 if member in container:
|
jpayne@68
|
1184 standardMsg = '%s unexpectedly found in %s' % (safe_repr(member),
|
jpayne@68
|
1185 safe_repr(container))
|
jpayne@68
|
1186 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1187
|
jpayne@68
|
1188 def assertIs(self, expr1, expr2, msg=None):
|
jpayne@68
|
1189 """Just like self.assertTrue(a is b), but with a nicer default message."""
|
jpayne@68
|
1190 if expr1 is not expr2:
|
jpayne@68
|
1191 standardMsg = '%s is not %s' % (safe_repr(expr1),
|
jpayne@68
|
1192 safe_repr(expr2))
|
jpayne@68
|
1193 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1194
|
jpayne@68
|
1195 def assertIsNot(self, expr1, expr2, msg=None):
|
jpayne@68
|
1196 """Just like self.assertTrue(a is not b), but with a nicer default message."""
|
jpayne@68
|
1197 if expr1 is expr2:
|
jpayne@68
|
1198 standardMsg = 'unexpectedly identical: %s' % (safe_repr(expr1),)
|
jpayne@68
|
1199 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1200
|
jpayne@68
|
1201 def assertDictEqual(self, d1, d2, msg=None):
|
jpayne@68
|
1202 self.assertIsInstance(d1, dict, 'First argument is not a dictionary')
|
jpayne@68
|
1203 self.assertIsInstance(d2, dict, 'Second argument is not a dictionary')
|
jpayne@68
|
1204
|
jpayne@68
|
1205 if d1 != d2:
|
jpayne@68
|
1206 standardMsg = '%s != %s' % _common_shorten_repr(d1, d2)
|
jpayne@68
|
1207 diff = ('\n' + '\n'.join(difflib.ndiff(
|
jpayne@68
|
1208 pprint.pformat(d1).splitlines(),
|
jpayne@68
|
1209 pprint.pformat(d2).splitlines())))
|
jpayne@68
|
1210 standardMsg = self._truncateMessage(standardMsg, diff)
|
jpayne@68
|
1211 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1212
|
jpayne@68
|
1213 def assertDictContainsSubset(self, subset, dictionary, msg=None):
|
jpayne@68
|
1214 """Checks whether dictionary is a superset of subset."""
|
jpayne@68
|
1215 warnings.warn('assertDictContainsSubset is deprecated',
|
jpayne@68
|
1216 DeprecationWarning)
|
jpayne@68
|
1217 missing = []
|
jpayne@68
|
1218 mismatched = []
|
jpayne@68
|
1219 for key, value in subset.items():
|
jpayne@68
|
1220 if key not in dictionary:
|
jpayne@68
|
1221 missing.append(key)
|
jpayne@68
|
1222 elif value != dictionary[key]:
|
jpayne@68
|
1223 mismatched.append('%s, expected: %s, actual: %s' %
|
jpayne@68
|
1224 (safe_repr(key), safe_repr(value),
|
jpayne@68
|
1225 safe_repr(dictionary[key])))
|
jpayne@68
|
1226
|
jpayne@68
|
1227 if not (missing or mismatched):
|
jpayne@68
|
1228 return
|
jpayne@68
|
1229
|
jpayne@68
|
1230 standardMsg = ''
|
jpayne@68
|
1231 if missing:
|
jpayne@68
|
1232 standardMsg = 'Missing: %s' % ','.join(safe_repr(m) for m in
|
jpayne@68
|
1233 missing)
|
jpayne@68
|
1234 if mismatched:
|
jpayne@68
|
1235 if standardMsg:
|
jpayne@68
|
1236 standardMsg += '; '
|
jpayne@68
|
1237 standardMsg += 'Mismatched values: %s' % ','.join(mismatched)
|
jpayne@68
|
1238
|
jpayne@68
|
1239 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1240
|
jpayne@68
|
1241
|
jpayne@68
|
1242 def assertCountEqual(self, first, second, msg=None):
|
jpayne@68
|
1243 """Asserts that two iterables have the same elements, the same number of
|
jpayne@68
|
1244 times, without regard to order.
|
jpayne@68
|
1245
|
jpayne@68
|
1246 self.assertEqual(Counter(list(first)),
|
jpayne@68
|
1247 Counter(list(second)))
|
jpayne@68
|
1248
|
jpayne@68
|
1249 Example:
|
jpayne@68
|
1250 - [0, 1, 1] and [1, 0, 1] compare equal.
|
jpayne@68
|
1251 - [0, 0, 1] and [0, 1] compare unequal.
|
jpayne@68
|
1252
|
jpayne@68
|
1253 """
|
jpayne@68
|
1254 first_seq, second_seq = list(first), list(second)
|
jpayne@68
|
1255 try:
|
jpayne@68
|
1256 first = collections.Counter(first_seq)
|
jpayne@68
|
1257 second = collections.Counter(second_seq)
|
jpayne@68
|
1258 except TypeError:
|
jpayne@68
|
1259 # Handle case with unhashable elements
|
jpayne@68
|
1260 differences = _count_diff_all_purpose(first_seq, second_seq)
|
jpayne@68
|
1261 else:
|
jpayne@68
|
1262 if first == second:
|
jpayne@68
|
1263 return
|
jpayne@68
|
1264 differences = _count_diff_hashable(first_seq, second_seq)
|
jpayne@68
|
1265
|
jpayne@68
|
1266 if differences:
|
jpayne@68
|
1267 standardMsg = 'Element counts were not equal:\n'
|
jpayne@68
|
1268 lines = ['First has %d, Second has %d: %r' % diff for diff in differences]
|
jpayne@68
|
1269 diffMsg = '\n'.join(lines)
|
jpayne@68
|
1270 standardMsg = self._truncateMessage(standardMsg, diffMsg)
|
jpayne@68
|
1271 msg = self._formatMessage(msg, standardMsg)
|
jpayne@68
|
1272 self.fail(msg)
|
jpayne@68
|
1273
|
jpayne@68
|
1274 def assertMultiLineEqual(self, first, second, msg=None):
|
jpayne@68
|
1275 """Assert that two multi-line strings are equal."""
|
jpayne@68
|
1276 self.assertIsInstance(first, str, 'First argument is not a string')
|
jpayne@68
|
1277 self.assertIsInstance(second, str, 'Second argument is not a string')
|
jpayne@68
|
1278
|
jpayne@68
|
1279 if first != second:
|
jpayne@68
|
1280 # don't use difflib if the strings are too long
|
jpayne@68
|
1281 if (len(first) > self._diffThreshold or
|
jpayne@68
|
1282 len(second) > self._diffThreshold):
|
jpayne@68
|
1283 self._baseAssertEqual(first, second, msg)
|
jpayne@68
|
1284 firstlines = first.splitlines(keepends=True)
|
jpayne@68
|
1285 secondlines = second.splitlines(keepends=True)
|
jpayne@68
|
1286 if len(firstlines) == 1 and first.strip('\r\n') == first:
|
jpayne@68
|
1287 firstlines = [first + '\n']
|
jpayne@68
|
1288 secondlines = [second + '\n']
|
jpayne@68
|
1289 standardMsg = '%s != %s' % _common_shorten_repr(first, second)
|
jpayne@68
|
1290 diff = '\n' + ''.join(difflib.ndiff(firstlines, secondlines))
|
jpayne@68
|
1291 standardMsg = self._truncateMessage(standardMsg, diff)
|
jpayne@68
|
1292 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1293
|
jpayne@68
|
1294 def assertLess(self, a, b, msg=None):
|
jpayne@68
|
1295 """Just like self.assertTrue(a < b), but with a nicer default message."""
|
jpayne@68
|
1296 if not a < b:
|
jpayne@68
|
1297 standardMsg = '%s not less than %s' % (safe_repr(a), safe_repr(b))
|
jpayne@68
|
1298 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1299
|
jpayne@68
|
1300 def assertLessEqual(self, a, b, msg=None):
|
jpayne@68
|
1301 """Just like self.assertTrue(a <= b), but with a nicer default message."""
|
jpayne@68
|
1302 if not a <= b:
|
jpayne@68
|
1303 standardMsg = '%s not less than or equal to %s' % (safe_repr(a), safe_repr(b))
|
jpayne@68
|
1304 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1305
|
jpayne@68
|
1306 def assertGreater(self, a, b, msg=None):
|
jpayne@68
|
1307 """Just like self.assertTrue(a > b), but with a nicer default message."""
|
jpayne@68
|
1308 if not a > b:
|
jpayne@68
|
1309 standardMsg = '%s not greater than %s' % (safe_repr(a), safe_repr(b))
|
jpayne@68
|
1310 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1311
|
jpayne@68
|
1312 def assertGreaterEqual(self, a, b, msg=None):
|
jpayne@68
|
1313 """Just like self.assertTrue(a >= b), but with a nicer default message."""
|
jpayne@68
|
1314 if not a >= b:
|
jpayne@68
|
1315 standardMsg = '%s not greater than or equal to %s' % (safe_repr(a), safe_repr(b))
|
jpayne@68
|
1316 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1317
|
jpayne@68
|
1318 def assertIsNone(self, obj, msg=None):
|
jpayne@68
|
1319 """Same as self.assertTrue(obj is None), with a nicer default message."""
|
jpayne@68
|
1320 if obj is not None:
|
jpayne@68
|
1321 standardMsg = '%s is not None' % (safe_repr(obj),)
|
jpayne@68
|
1322 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1323
|
jpayne@68
|
1324 def assertIsNotNone(self, obj, msg=None):
|
jpayne@68
|
1325 """Included for symmetry with assertIsNone."""
|
jpayne@68
|
1326 if obj is None:
|
jpayne@68
|
1327 standardMsg = 'unexpectedly None'
|
jpayne@68
|
1328 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1329
|
jpayne@68
|
1330 def assertIsInstance(self, obj, cls, msg=None):
|
jpayne@68
|
1331 """Same as self.assertTrue(isinstance(obj, cls)), with a nicer
|
jpayne@68
|
1332 default message."""
|
jpayne@68
|
1333 if not isinstance(obj, cls):
|
jpayne@68
|
1334 standardMsg = '%s is not an instance of %r' % (safe_repr(obj), cls)
|
jpayne@68
|
1335 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1336
|
jpayne@68
|
1337 def assertNotIsInstance(self, obj, cls, msg=None):
|
jpayne@68
|
1338 """Included for symmetry with assertIsInstance."""
|
jpayne@68
|
1339 if isinstance(obj, cls):
|
jpayne@68
|
1340 standardMsg = '%s is an instance of %r' % (safe_repr(obj), cls)
|
jpayne@68
|
1341 self.fail(self._formatMessage(msg, standardMsg))
|
jpayne@68
|
1342
|
jpayne@68
|
1343 def assertRaisesRegex(self, expected_exception, expected_regex,
|
jpayne@68
|
1344 *args, **kwargs):
|
jpayne@68
|
1345 """Asserts that the message in a raised exception matches a regex.
|
jpayne@68
|
1346
|
jpayne@68
|
1347 Args:
|
jpayne@68
|
1348 expected_exception: Exception class expected to be raised.
|
jpayne@68
|
1349 expected_regex: Regex (re.Pattern object or string) expected
|
jpayne@68
|
1350 to be found in error message.
|
jpayne@68
|
1351 args: Function to be called and extra positional args.
|
jpayne@68
|
1352 kwargs: Extra kwargs.
|
jpayne@68
|
1353 msg: Optional message used in case of failure. Can only be used
|
jpayne@68
|
1354 when assertRaisesRegex is used as a context manager.
|
jpayne@68
|
1355 """
|
jpayne@68
|
1356 context = _AssertRaisesContext(expected_exception, self, expected_regex)
|
jpayne@68
|
1357 return context.handle('assertRaisesRegex', args, kwargs)
|
jpayne@68
|
1358
|
jpayne@68
|
1359 def assertWarnsRegex(self, expected_warning, expected_regex,
|
jpayne@68
|
1360 *args, **kwargs):
|
jpayne@68
|
1361 """Asserts that the message in a triggered warning matches a regexp.
|
jpayne@68
|
1362 Basic functioning is similar to assertWarns() with the addition
|
jpayne@68
|
1363 that only warnings whose messages also match the regular expression
|
jpayne@68
|
1364 are considered successful matches.
|
jpayne@68
|
1365
|
jpayne@68
|
1366 Args:
|
jpayne@68
|
1367 expected_warning: Warning class expected to be triggered.
|
jpayne@68
|
1368 expected_regex: Regex (re.Pattern object or string) expected
|
jpayne@68
|
1369 to be found in error message.
|
jpayne@68
|
1370 args: Function to be called and extra positional args.
|
jpayne@68
|
1371 kwargs: Extra kwargs.
|
jpayne@68
|
1372 msg: Optional message used in case of failure. Can only be used
|
jpayne@68
|
1373 when assertWarnsRegex is used as a context manager.
|
jpayne@68
|
1374 """
|
jpayne@68
|
1375 context = _AssertWarnsContext(expected_warning, self, expected_regex)
|
jpayne@68
|
1376 return context.handle('assertWarnsRegex', args, kwargs)
|
jpayne@68
|
1377
|
jpayne@68
|
1378 def assertRegex(self, text, expected_regex, msg=None):
|
jpayne@68
|
1379 """Fail the test unless the text matches the regular expression."""
|
jpayne@68
|
1380 if isinstance(expected_regex, (str, bytes)):
|
jpayne@68
|
1381 assert expected_regex, "expected_regex must not be empty."
|
jpayne@68
|
1382 expected_regex = re.compile(expected_regex)
|
jpayne@68
|
1383 if not expected_regex.search(text):
|
jpayne@68
|
1384 standardMsg = "Regex didn't match: %r not found in %r" % (
|
jpayne@68
|
1385 expected_regex.pattern, text)
|
jpayne@68
|
1386 # _formatMessage ensures the longMessage option is respected
|
jpayne@68
|
1387 msg = self._formatMessage(msg, standardMsg)
|
jpayne@68
|
1388 raise self.failureException(msg)
|
jpayne@68
|
1389
|
jpayne@68
|
1390 def assertNotRegex(self, text, unexpected_regex, msg=None):
|
jpayne@68
|
1391 """Fail the test if the text matches the regular expression."""
|
jpayne@68
|
1392 if isinstance(unexpected_regex, (str, bytes)):
|
jpayne@68
|
1393 unexpected_regex = re.compile(unexpected_regex)
|
jpayne@68
|
1394 match = unexpected_regex.search(text)
|
jpayne@68
|
1395 if match:
|
jpayne@68
|
1396 standardMsg = 'Regex matched: %r matches %r in %r' % (
|
jpayne@68
|
1397 text[match.start() : match.end()],
|
jpayne@68
|
1398 unexpected_regex.pattern,
|
jpayne@68
|
1399 text)
|
jpayne@68
|
1400 # _formatMessage ensures the longMessage option is respected
|
jpayne@68
|
1401 msg = self._formatMessage(msg, standardMsg)
|
jpayne@68
|
1402 raise self.failureException(msg)
|
jpayne@68
|
1403
|
jpayne@68
|
1404
|
jpayne@68
|
1405 def _deprecate(original_func):
|
jpayne@68
|
1406 def deprecated_func(*args, **kwargs):
|
jpayne@68
|
1407 warnings.warn(
|
jpayne@68
|
1408 'Please use {0} instead.'.format(original_func.__name__),
|
jpayne@68
|
1409 DeprecationWarning, 2)
|
jpayne@68
|
1410 return original_func(*args, **kwargs)
|
jpayne@68
|
1411 return deprecated_func
|
jpayne@68
|
1412
|
jpayne@68
|
1413 # see #9424
|
jpayne@68
|
1414 failUnlessEqual = assertEquals = _deprecate(assertEqual)
|
jpayne@68
|
1415 failIfEqual = assertNotEquals = _deprecate(assertNotEqual)
|
jpayne@68
|
1416 failUnlessAlmostEqual = assertAlmostEquals = _deprecate(assertAlmostEqual)
|
jpayne@68
|
1417 failIfAlmostEqual = assertNotAlmostEquals = _deprecate(assertNotAlmostEqual)
|
jpayne@68
|
1418 failUnless = assert_ = _deprecate(assertTrue)
|
jpayne@68
|
1419 failUnlessRaises = _deprecate(assertRaises)
|
jpayne@68
|
1420 failIf = _deprecate(assertFalse)
|
jpayne@68
|
1421 assertRaisesRegexp = _deprecate(assertRaisesRegex)
|
jpayne@68
|
1422 assertRegexpMatches = _deprecate(assertRegex)
|
jpayne@68
|
1423 assertNotRegexpMatches = _deprecate(assertNotRegex)
|
jpayne@68
|
1424
|
jpayne@68
|
1425
|
jpayne@68
|
1426
|
jpayne@68
|
1427 class FunctionTestCase(TestCase):
|
jpayne@68
|
1428 """A test case that wraps a test function.
|
jpayne@68
|
1429
|
jpayne@68
|
1430 This is useful for slipping pre-existing test functions into the
|
jpayne@68
|
1431 unittest framework. Optionally, set-up and tidy-up functions can be
|
jpayne@68
|
1432 supplied. As with TestCase, the tidy-up ('tearDown') function will
|
jpayne@68
|
1433 always be called if the set-up ('setUp') function ran successfully.
|
jpayne@68
|
1434 """
|
jpayne@68
|
1435
|
jpayne@68
|
1436 def __init__(self, testFunc, setUp=None, tearDown=None, description=None):
|
jpayne@68
|
1437 super(FunctionTestCase, self).__init__()
|
jpayne@68
|
1438 self._setUpFunc = setUp
|
jpayne@68
|
1439 self._tearDownFunc = tearDown
|
jpayne@68
|
1440 self._testFunc = testFunc
|
jpayne@68
|
1441 self._description = description
|
jpayne@68
|
1442
|
jpayne@68
|
1443 def setUp(self):
|
jpayne@68
|
1444 if self._setUpFunc is not None:
|
jpayne@68
|
1445 self._setUpFunc()
|
jpayne@68
|
1446
|
jpayne@68
|
1447 def tearDown(self):
|
jpayne@68
|
1448 if self._tearDownFunc is not None:
|
jpayne@68
|
1449 self._tearDownFunc()
|
jpayne@68
|
1450
|
jpayne@68
|
1451 def runTest(self):
|
jpayne@68
|
1452 self._testFunc()
|
jpayne@68
|
1453
|
jpayne@68
|
1454 def id(self):
|
jpayne@68
|
1455 return self._testFunc.__name__
|
jpayne@68
|
1456
|
jpayne@68
|
1457 def __eq__(self, other):
|
jpayne@68
|
1458 if not isinstance(other, self.__class__):
|
jpayne@68
|
1459 return NotImplemented
|
jpayne@68
|
1460
|
jpayne@68
|
1461 return self._setUpFunc == other._setUpFunc and \
|
jpayne@68
|
1462 self._tearDownFunc == other._tearDownFunc and \
|
jpayne@68
|
1463 self._testFunc == other._testFunc and \
|
jpayne@68
|
1464 self._description == other._description
|
jpayne@68
|
1465
|
jpayne@68
|
1466 def __hash__(self):
|
jpayne@68
|
1467 return hash((type(self), self._setUpFunc, self._tearDownFunc,
|
jpayne@68
|
1468 self._testFunc, self._description))
|
jpayne@68
|
1469
|
jpayne@68
|
1470 def __str__(self):
|
jpayne@68
|
1471 return "%s (%s)" % (strclass(self.__class__),
|
jpayne@68
|
1472 self._testFunc.__name__)
|
jpayne@68
|
1473
|
jpayne@68
|
1474 def __repr__(self):
|
jpayne@68
|
1475 return "<%s tec=%s>" % (strclass(self.__class__),
|
jpayne@68
|
1476 self._testFunc)
|
jpayne@68
|
1477
|
jpayne@68
|
1478 def shortDescription(self):
|
jpayne@68
|
1479 if self._description is not None:
|
jpayne@68
|
1480 return self._description
|
jpayne@68
|
1481 doc = self._testFunc.__doc__
|
jpayne@68
|
1482 return doc and doc.split("\n")[0].strip() or None
|
jpayne@68
|
1483
|
jpayne@68
|
1484
|
jpayne@68
|
1485 class _SubTest(TestCase):
|
jpayne@68
|
1486
|
jpayne@68
|
1487 def __init__(self, test_case, message, params):
|
jpayne@68
|
1488 super().__init__()
|
jpayne@68
|
1489 self._message = message
|
jpayne@68
|
1490 self.test_case = test_case
|
jpayne@68
|
1491 self.params = params
|
jpayne@68
|
1492 self.failureException = test_case.failureException
|
jpayne@68
|
1493
|
jpayne@68
|
1494 def runTest(self):
|
jpayne@68
|
1495 raise NotImplementedError("subtests cannot be run directly")
|
jpayne@68
|
1496
|
jpayne@68
|
1497 def _subDescription(self):
|
jpayne@68
|
1498 parts = []
|
jpayne@68
|
1499 if self._message is not _subtest_msg_sentinel:
|
jpayne@68
|
1500 parts.append("[{}]".format(self._message))
|
jpayne@68
|
1501 if self.params:
|
jpayne@68
|
1502 params_desc = ', '.join(
|
jpayne@68
|
1503 "{}={!r}".format(k, v)
|
jpayne@68
|
1504 for (k, v) in self.params.items())
|
jpayne@68
|
1505 parts.append("({})".format(params_desc))
|
jpayne@68
|
1506 return " ".join(parts) or '(<subtest>)'
|
jpayne@68
|
1507
|
jpayne@68
|
1508 def id(self):
|
jpayne@68
|
1509 return "{} {}".format(self.test_case.id(), self._subDescription())
|
jpayne@68
|
1510
|
jpayne@68
|
1511 def shortDescription(self):
|
jpayne@68
|
1512 """Returns a one-line description of the subtest, or None if no
|
jpayne@68
|
1513 description has been provided.
|
jpayne@68
|
1514 """
|
jpayne@68
|
1515 return self.test_case.shortDescription()
|
jpayne@68
|
1516
|
jpayne@68
|
1517 def __str__(self):
|
jpayne@68
|
1518 return "{} {}".format(self.test_case, self._subDescription())
|