testtools: Merge in new upstream.
[nivanova/samba-autobuild/.git] / lib / testtools / testtools / testcase.py
1 # Copyright (c) 2008-2010 Jonathan M. Lange. See LICENSE for details.
2
3 """Test case related stuff."""
4
5 __metaclass__ = type
6 __all__ = [
7     'clone_test_with_new_id',
8     'run_test_with',
9     'skip',
10     'skipIf',
11     'skipUnless',
12     'TestCase',
13     ]
14
15 import copy
16 import itertools
17 import sys
18 import types
19 import unittest
20
21 from testtools import (
22     content,
23     try_import,
24     )
25 from testtools.compat import advance_iterator
26 from testtools.matchers import (
27     Annotate,
28     Equals,
29     )
30 from testtools.monkey import patch
31 from testtools.runtest import RunTest
32 from testtools.testresult import TestResult
33
34 wraps = try_import('functools.wraps')
35
36 class TestSkipped(Exception):
37     """Raised within TestCase.run() when a test is skipped."""
38 TestSkipped = try_import('unittest.case.SkipTest', TestSkipped)
39
40
41 class _UnexpectedSuccess(Exception):
42     """An unexpected success was raised.
43
44     Note that this exception is private plumbing in testtools' testcase
45     module.
46     """
47 _UnexpectedSuccess = try_import(
48     'unittest.case._UnexpectedSuccess', _UnexpectedSuccess)
49
50 class _ExpectedFailure(Exception):
51     """An expected failure occured.
52
53     Note that this exception is private plumbing in testtools' testcase
54     module.
55     """
56 _ExpectedFailure = try_import(
57     'unittest.case._ExpectedFailure', _ExpectedFailure)
58
59
60 def run_test_with(test_runner, **kwargs):
61     """Decorate a test as using a specific `RunTest`.
62
63     e.g.
64       @run_test_with(CustomRunner, timeout=42)
65       def test_foo(self):
66           self.assertTrue(True)
67
68     The returned decorator works by setting an attribute on the decorated
69     function.  `TestCase.__init__` looks for this attribute when deciding
70     on a `RunTest` factory.  If you wish to use multiple decorators on a test
71     method, then you must either make this one the top-most decorator, or
72     you must write your decorators so that they update the wrapping function
73     with the attributes of the wrapped function.  The latter is recommended
74     style anyway.  `functools.wraps`, `functools.wrapper` and
75     `twisted.python.util.mergeFunctionMetadata` can help you do this.
76
77     :param test_runner: A `RunTest` factory that takes a test case and an
78         optional list of exception handlers.  See `RunTest`.
79     :param **kwargs: Keyword arguments to pass on as extra arguments to
80         `test_runner`.
81     :return: A decorator to be used for marking a test as needing a special
82         runner.
83     """
84     def decorator(function):
85         # Set an attribute on 'function' which will inform TestCase how to
86         # make the runner.
87         function._run_test_with = (
88             lambda case, handlers=None:
89                 test_runner(case, handlers=handlers, **kwargs))
90         return function
91     return decorator
92
93
94 class TestCase(unittest.TestCase):
95     """Extensions to the basic TestCase.
96
97     :ivar exception_handlers: Exceptions to catch from setUp, runTest and
98         tearDown. This list is able to be modified at any time and consists of
99         (exception_class, handler(case, result, exception_value)) pairs.
100     :cvar run_tests_with: A factory to make the `RunTest` to run tests with.
101         Defaults to `RunTest`.  The factory is expected to take a test case
102         and an optional list of exception handlers.
103     """
104
105     skipException = TestSkipped
106
107     run_tests_with = RunTest
108
109     def __init__(self, *args, **kwargs):
110         """Construct a TestCase.
111
112         :param testMethod: The name of the method to run.
113         :param runTest: Optional class to use to execute the test. If not
114             supplied `testtools.runtest.RunTest` is used. The instance to be
115             used is created when run() is invoked, so will be fresh each time.
116             Overrides `run_tests_with` if given.
117         """
118         runTest = kwargs.pop('runTest', None)
119         unittest.TestCase.__init__(self, *args, **kwargs)
120         self._cleanups = []
121         self._unique_id_gen = itertools.count(1)
122         # Generators to ensure unique traceback ids.  Maps traceback label to
123         # iterators.
124         self._traceback_id_gens = {}
125         self.__setup_called = False
126         self.__teardown_called = False
127         # __details is lazy-initialized so that a constructed-but-not-run
128         # TestCase is safe to use with clone_test_with_new_id.
129         self.__details = None
130         test_method = self._get_test_method()
131         if runTest is None:
132             runTest = getattr(
133                 test_method, '_run_test_with', self.run_tests_with)
134         self.__RunTest = runTest
135         self.__exception_handlers = []
136         self.exception_handlers = [
137             (self.skipException, self._report_skip),
138             (self.failureException, self._report_failure),
139             (_ExpectedFailure, self._report_expected_failure),
140             (_UnexpectedSuccess, self._report_unexpected_success),
141             (Exception, self._report_error),
142             ]
143         if sys.version_info < (2, 6):
144             # Catch old-style string exceptions with None as the instance
145             self.exception_handlers.append((type(None), self._report_error))
146
147     def __eq__(self, other):
148         eq = getattr(unittest.TestCase, '__eq__', None)
149         if eq is not None and not unittest.TestCase.__eq__(self, other):
150             return False
151         return self.__dict__ == other.__dict__
152
153     def __repr__(self):
154         # We add id to the repr because it makes testing testtools easier.
155         return "<%s id=0x%0x>" % (self.id(), id(self))
156
157     def addDetail(self, name, content_object):
158         """Add a detail to be reported with this test's outcome.
159
160         For more details see pydoc testtools.TestResult.
161
162         :param name: The name to give this detail.
163         :param content_object: The content object for this detail. See
164             testtools.content for more detail.
165         """
166         if self.__details is None:
167             self.__details = {}
168         self.__details[name] = content_object
169
170     def getDetails(self):
171         """Get the details dict that will be reported with this test's outcome.
172
173         For more details see pydoc testtools.TestResult.
174         """
175         if self.__details is None:
176             self.__details = {}
177         return self.__details
178
179     def patch(self, obj, attribute, value):
180         """Monkey-patch 'obj.attribute' to 'value' while the test is running.
181
182         If 'obj' has no attribute, then the monkey-patch will still go ahead,
183         and the attribute will be deleted instead of restored to its original
184         value.
185
186         :param obj: The object to patch. Can be anything.
187         :param attribute: The attribute on 'obj' to patch.
188         :param value: The value to set 'obj.attribute' to.
189         """
190         self.addCleanup(patch(obj, attribute, value))
191
192     def shortDescription(self):
193         return self.id()
194
195     def skipTest(self, reason):
196         """Cause this test to be skipped.
197
198         This raises self.skipException(reason). skipException is raised
199         to permit a skip to be triggered at any point (during setUp or the
200         testMethod itself). The run() method catches skipException and
201         translates that into a call to the result objects addSkip method.
202
203         :param reason: The reason why the test is being skipped. This must
204             support being cast into a unicode string for reporting.
205         """
206         raise self.skipException(reason)
207
208     # skipTest is how python2.7 spells this. Sometime in the future
209     # This should be given a deprecation decorator - RBC 20100611.
210     skip = skipTest
211
212     def _formatTypes(self, classOrIterable):
213         """Format a class or a bunch of classes for display in an error."""
214         className = getattr(classOrIterable, '__name__', None)
215         if className is None:
216             className = ', '.join(klass.__name__ for klass in classOrIterable)
217         return className
218
219     def addCleanup(self, function, *arguments, **keywordArguments):
220         """Add a cleanup function to be called after tearDown.
221
222         Functions added with addCleanup will be called in reverse order of
223         adding after tearDown, or after setUp if setUp raises an exception.
224
225         If a function added with addCleanup raises an exception, the error
226         will be recorded as a test error, and the next cleanup will then be
227         run.
228
229         Cleanup functions are always called before a test finishes running,
230         even if setUp is aborted by an exception.
231         """
232         self._cleanups.append((function, arguments, keywordArguments))
233
234     def addOnException(self, handler):
235         """Add a handler to be called when an exception occurs in test code.
236
237         This handler cannot affect what result methods are called, and is
238         called before any outcome is called on the result object. An example
239         use for it is to add some diagnostic state to the test details dict
240         which is expensive to calculate and not interesting for reporting in
241         the success case.
242
243         Handlers are called before the outcome (such as addFailure) that
244         the exception has caused.
245
246         Handlers are called in first-added, first-called order, and if they
247         raise an exception, that will propogate out of the test running
248         machinery, halting test processing. As a result, do not call code that
249         may unreasonably fail.
250         """
251         self.__exception_handlers.append(handler)
252
253     def _add_reason(self, reason):
254         self.addDetail('reason', content.Content(
255             content.ContentType('text', 'plain'),
256             lambda: [reason.encode('utf8')]))
257
258     def assertEqual(self, expected, observed, message=''):
259         """Assert that 'expected' is equal to 'observed'.
260
261         :param expected: The expected value.
262         :param observed: The observed value.
263         :param message: An optional message to include in the error.
264         """
265         matcher = Equals(expected)
266         if message:
267             matcher = Annotate(message, matcher)
268         self.assertThat(observed, matcher)
269
270     failUnlessEqual = assertEquals = assertEqual
271
272     def assertIn(self, needle, haystack):
273         """Assert that needle is in haystack."""
274         self.assertTrue(
275             needle in haystack, '%r not in %r' % (needle, haystack))
276
277     def assertIs(self, expected, observed, message=''):
278         """Assert that 'expected' is 'observed'.
279
280         :param expected: The expected value.
281         :param observed: The observed value.
282         :param message: An optional message describing the error.
283         """
284         if message:
285             message = ': ' + message
286         self.assertTrue(
287             expected is observed,
288             '%r is not %r%s' % (expected, observed, message))
289
290     def assertIsNot(self, expected, observed, message=''):
291         """Assert that 'expected' is not 'observed'."""
292         if message:
293             message = ': ' + message
294         self.assertTrue(
295             expected is not observed,
296             '%r is %r%s' % (expected, observed, message))
297
298     def assertNotIn(self, needle, haystack):
299         """Assert that needle is not in haystack."""
300         self.assertTrue(
301             needle not in haystack, '%r in %r' % (needle, haystack))
302
303     def assertIsInstance(self, obj, klass, msg=None):
304         if msg is None:
305             msg = '%r is not an instance of %s' % (
306                 obj, self._formatTypes(klass))
307         self.assertTrue(isinstance(obj, klass), msg)
308
309     def assertRaises(self, excClass, callableObj, *args, **kwargs):
310         """Fail unless an exception of class excClass is thrown
311            by callableObj when invoked with arguments args and keyword
312            arguments kwargs. If a different type of exception is
313            thrown, it will not be caught, and the test case will be
314            deemed to have suffered an error, exactly as for an
315            unexpected exception.
316         """
317         try:
318             ret = callableObj(*args, **kwargs)
319         except excClass:
320             return sys.exc_info()[1]
321         else:
322             excName = self._formatTypes(excClass)
323             self.fail("%s not raised, %r returned instead." % (excName, ret))
324     failUnlessRaises = assertRaises
325
326     def assertThat(self, matchee, matcher):
327         """Assert that matchee is matched by matcher.
328
329         :param matchee: An object to match with matcher.
330         :param matcher: An object meeting the testtools.Matcher protocol.
331         :raises self.failureException: When matcher does not match thing.
332         """
333         mismatch = matcher.match(matchee)
334         if not mismatch:
335             return
336         existing_details = self.getDetails()
337         for (name, content) in mismatch.get_details().items():
338             full_name = name
339             suffix = 1
340             while full_name in existing_details:
341                 full_name = "%s-%d" % (name, suffix)
342                 suffix += 1
343             self.addDetail(full_name, content)
344         self.fail('Match failed. Matchee: "%s"\nMatcher: %s\nDifference: %s\n'
345             % (matchee, matcher, mismatch.describe()))
346
347     def defaultTestResult(self):
348         return TestResult()
349
350     def expectFailure(self, reason, predicate, *args, **kwargs):
351         """Check that a test fails in a particular way.
352
353         If the test fails in the expected way, a KnownFailure is caused. If it
354         succeeds an UnexpectedSuccess is caused.
355
356         The expected use of expectFailure is as a barrier at the point in a
357         test where the test would fail. For example:
358         >>> def test_foo(self):
359         >>>    self.expectFailure("1 should be 0", self.assertNotEqual, 1, 0)
360         >>>    self.assertEqual(1, 0)
361
362         If in the future 1 were to equal 0, the expectFailure call can simply
363         be removed. This separation preserves the original intent of the test
364         while it is in the expectFailure mode.
365         """
366         self._add_reason(reason)
367         try:
368             predicate(*args, **kwargs)
369         except self.failureException:
370             # GZ 2010-08-12: Don't know how to avoid exc_info cycle as the new
371             #                unittest _ExpectedFailure wants old traceback
372             exc_info = sys.exc_info()
373             try:
374                 self._report_traceback(exc_info)
375                 raise _ExpectedFailure(exc_info)
376             finally:
377                 del exc_info
378         else:
379             raise _UnexpectedSuccess(reason)
380
381     def getUniqueInteger(self):
382         """Get an integer unique to this test.
383
384         Returns an integer that is guaranteed to be unique to this instance.
385         Use this when you need an arbitrary integer in your test, or as a
386         helper for custom anonymous factory methods.
387         """
388         return advance_iterator(self._unique_id_gen)
389
390     def getUniqueString(self, prefix=None):
391         """Get a string unique to this test.
392
393         Returns a string that is guaranteed to be unique to this instance. Use
394         this when you need an arbitrary string in your test, or as a helper
395         for custom anonymous factory methods.
396
397         :param prefix: The prefix of the string. If not provided, defaults
398             to the id of the tests.
399         :return: A bytestring of '<prefix>-<unique_int>'.
400         """
401         if prefix is None:
402             prefix = self.id()
403         return '%s-%d' % (prefix, self.getUniqueInteger())
404
405     def onException(self, exc_info, tb_label='traceback'):
406         """Called when an exception propogates from test code.
407
408         :seealso addOnException:
409         """
410         if exc_info[0] not in [
411             TestSkipped, _UnexpectedSuccess, _ExpectedFailure]:
412             self._report_traceback(exc_info, tb_label=tb_label)
413         for handler in self.__exception_handlers:
414             handler(exc_info)
415
416     @staticmethod
417     def _report_error(self, result, err):
418         result.addError(self, details=self.getDetails())
419
420     @staticmethod
421     def _report_expected_failure(self, result, err):
422         result.addExpectedFailure(self, details=self.getDetails())
423
424     @staticmethod
425     def _report_failure(self, result, err):
426         result.addFailure(self, details=self.getDetails())
427
428     @staticmethod
429     def _report_skip(self, result, err):
430         if err.args:
431             reason = err.args[0]
432         else:
433             reason = "no reason given."
434         self._add_reason(reason)
435         result.addSkip(self, details=self.getDetails())
436
437     def _report_traceback(self, exc_info, tb_label='traceback'):
438         id_gen = self._traceback_id_gens.setdefault(
439             tb_label, itertools.count(0))
440         tb_id = advance_iterator(id_gen)
441         if tb_id:
442             tb_label = '%s-%d' % (tb_label, tb_id)
443         self.addDetail(tb_label, content.TracebackContent(exc_info, self))
444
445     @staticmethod
446     def _report_unexpected_success(self, result, err):
447         result.addUnexpectedSuccess(self, details=self.getDetails())
448
449     def run(self, result=None):
450         return self.__RunTest(self, self.exception_handlers).run(result)
451
452     def _run_setup(self, result):
453         """Run the setUp function for this test.
454
455         :param result: A testtools.TestResult to report activity to.
456         :raises ValueError: If the base class setUp is not called, a
457             ValueError is raised.
458         """
459         ret = self.setUp()
460         if not self.__setup_called:
461             raise ValueError(
462                 "TestCase.setUp was not called. Have you upcalled all the "
463                 "way up the hierarchy from your setUp? e.g. Call "
464                 "super(%s, self).setUp() from your setUp()."
465                 % self.__class__.__name__)
466         return ret
467
468     def _run_teardown(self, result):
469         """Run the tearDown function for this test.
470
471         :param result: A testtools.TestResult to report activity to.
472         :raises ValueError: If the base class tearDown is not called, a
473             ValueError is raised.
474         """
475         ret = self.tearDown()
476         if not self.__teardown_called:
477             raise ValueError(
478                 "TestCase.tearDown was not called. Have you upcalled all the "
479                 "way up the hierarchy from your tearDown? e.g. Call "
480                 "super(%s, self).tearDown() from your tearDown()."
481                 % self.__class__.__name__)
482         return ret
483
484     def _get_test_method(self):
485         absent_attr = object()
486         # Python 2.5+
487         method_name = getattr(self, '_testMethodName', absent_attr)
488         if method_name is absent_attr:
489             # Python 2.4
490             method_name = getattr(self, '_TestCase__testMethodName')
491         return getattr(self, method_name)
492
493     def _run_test_method(self, result):
494         """Run the test method for this test.
495
496         :param result: A testtools.TestResult to report activity to.
497         :return: None.
498         """
499         return self._get_test_method()()
500
501     def useFixture(self, fixture):
502         """Use fixture in a test case.
503
504         The fixture will be setUp, and self.addCleanup(fixture.cleanUp) called.
505
506         :param fixture: The fixture to use.
507         :return: The fixture, after setting it up and scheduling a cleanup for
508            it.
509         """
510         fixture.setUp()
511         self.addCleanup(fixture.cleanUp)
512         self.addCleanup(self._gather_details, fixture.getDetails)
513         return fixture
514
515     def _gather_details(self, getDetails):
516         """Merge the details from getDetails() into self.getDetails()."""
517         details = getDetails()
518         my_details = self.getDetails()
519         for name, content_object in details.items():
520             new_name = name
521             disambiguator = itertools.count(1)
522             while new_name in my_details:
523                 new_name = '%s-%d' % (name, advance_iterator(disambiguator))
524             name = new_name
525             content_bytes = list(content_object.iter_bytes())
526             content_callback = lambda:content_bytes
527             self.addDetail(name,
528                 content.Content(content_object.content_type, content_callback))
529
530     def setUp(self):
531         unittest.TestCase.setUp(self)
532         self.__setup_called = True
533
534     def tearDown(self):
535         unittest.TestCase.tearDown(self)
536         self.__teardown_called = True
537
538
539 class PlaceHolder(object):
540     """A placeholder test.
541
542     `PlaceHolder` implements much of the same interface as `TestCase` and is
543     particularly suitable for being added to `TestResult`s.
544     """
545
546     def __init__(self, test_id, short_description=None):
547         """Construct a `PlaceHolder`.
548
549         :param test_id: The id of the placeholder test.
550         :param short_description: The short description of the place holder
551             test. If not provided, the id will be used instead.
552         """
553         self._test_id = test_id
554         self._short_description = short_description
555
556     def __call__(self, result=None):
557         return self.run(result=result)
558
559     def __repr__(self):
560         internal = [self._test_id]
561         if self._short_description is not None:
562             internal.append(self._short_description)
563         return "<%s.%s(%s)>" % (
564             self.__class__.__module__,
565             self.__class__.__name__,
566             ", ".join(map(repr, internal)))
567
568     def __str__(self):
569         return self.id()
570
571     def countTestCases(self):
572         return 1
573
574     def debug(self):
575         pass
576
577     def id(self):
578         return self._test_id
579
580     def run(self, result=None):
581         if result is None:
582             result = TestResult()
583         result.startTest(self)
584         result.addSuccess(self)
585         result.stopTest(self)
586
587     def shortDescription(self):
588         if self._short_description is None:
589             return self.id()
590         else:
591             return self._short_description
592
593
594 class ErrorHolder(PlaceHolder):
595     """A placeholder test that will error out when run."""
596
597     failureException = None
598
599     def __init__(self, test_id, error, short_description=None):
600         """Construct an `ErrorHolder`.
601
602         :param test_id: The id of the test.
603         :param error: The exc info tuple that will be used as the test's error.
604         :param short_description: An optional short description of the test.
605         """
606         super(ErrorHolder, self).__init__(
607             test_id, short_description=short_description)
608         self._error = error
609
610     def __repr__(self):
611         internal = [self._test_id, self._error]
612         if self._short_description is not None:
613             internal.append(self._short_description)
614         return "<%s.%s(%s)>" % (
615             self.__class__.__module__,
616             self.__class__.__name__,
617             ", ".join(map(repr, internal)))
618
619     def run(self, result=None):
620         if result is None:
621             result = TestResult()
622         result.startTest(self)
623         result.addError(self, self._error)
624         result.stopTest(self)
625
626
627 # Python 2.4 did not know how to copy functions.
628 if types.FunctionType not in copy._copy_dispatch:
629     copy._copy_dispatch[types.FunctionType] = copy._copy_immutable
630
631
632 def clone_test_with_new_id(test, new_id):
633     """Copy a TestCase, and give the copied test a new id.
634
635     This is only expected to be used on tests that have been constructed but
636     not executed.
637     """
638     newTest = copy.copy(test)
639     newTest.id = lambda: new_id
640     return newTest
641
642
643 def skip(reason):
644     """A decorator to skip unit tests.
645
646     This is just syntactic sugar so users don't have to change any of their
647     unit tests in order to migrate to python 2.7, which provides the
648     @unittest.skip decorator.
649     """
650     def decorator(test_item):
651         if wraps is not None:
652             @wraps(test_item)
653             def skip_wrapper(*args, **kwargs):
654                 raise TestCase.skipException(reason)
655         else:
656             def skip_wrapper(test_item):
657                 test_item.skip(reason)
658         return skip_wrapper
659     return decorator
660
661
662 def skipIf(condition, reason):
663     """Skip a test if the condition is true."""
664     if condition:
665         return skip(reason)
666     def _id(obj):
667         return obj
668     return _id
669
670
671 def skipUnless(condition, reason):
672     """Skip a test unless the condition is true."""
673     if not condition:
674         return skip(reason)
675     def _id(obj):
676         return obj
677     return _id