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