s3:smbd: also log the "offline" flag when debugging the dos-mode
[Samba/gebeck_regimport.git] / lib / testtools / testtools / testcase.py
blob07278be0e4f6664fe7e21d890cc3b9bcf378577b
1 # Copyright (c) 2008-2011 testtools developers. See LICENSE for details.
3 """Test case related stuff."""
5 __metaclass__ = type
6 __all__ = [
7 'clone_test_with_new_id',
8 'ExpectedException',
9 'gather_details',
10 'run_test_with',
11 'skip',
12 'skipIf',
13 'skipUnless',
14 'TestCase',
17 import copy
18 import itertools
19 import sys
20 import types
21 import unittest
23 from testtools import (
24 content,
25 try_import,
27 from testtools.compat import (
28 advance_iterator,
29 reraise,
31 from testtools.matchers import (
32 Annotate,
33 Contains,
34 Equals,
35 MatchesAll,
36 MatchesException,
37 MismatchError,
38 Is,
39 IsInstance,
40 Not,
41 Raises,
43 from testtools.monkey import patch
44 from testtools.runtest import RunTest
45 from testtools.testresult import TestResult
47 wraps = try_import('functools.wraps')
49 class TestSkipped(Exception):
50 """Raised within TestCase.run() when a test is skipped."""
51 testSkipped = try_import('unittest2.case.SkipTest', TestSkipped)
52 TestSkipped = try_import('unittest.case.SkipTest', TestSkipped)
55 class _UnexpectedSuccess(Exception):
56 """An unexpected success was raised.
58 Note that this exception is private plumbing in testtools' testcase
59 module.
60 """
61 _UnexpectedSuccess = try_import(
62 'unittest2.case._UnexpectedSuccess', _UnexpectedSuccess)
63 _UnexpectedSuccess = try_import(
64 'unittest.case._UnexpectedSuccess', _UnexpectedSuccess)
66 class _ExpectedFailure(Exception):
67 """An expected failure occured.
69 Note that this exception is private plumbing in testtools' testcase
70 module.
71 """
72 _ExpectedFailure = try_import(
73 'unittest2.case._ExpectedFailure', _ExpectedFailure)
74 _ExpectedFailure = try_import(
75 'unittest.case._ExpectedFailure', _ExpectedFailure)
78 def run_test_with(test_runner, **kwargs):
79 """Decorate a test as using a specific ``RunTest``.
81 e.g.::
83 @run_test_with(CustomRunner, timeout=42)
84 def test_foo(self):
85 self.assertTrue(True)
87 The returned decorator works by setting an attribute on the decorated
88 function. `TestCase.__init__` looks for this attribute when deciding on a
89 ``RunTest`` factory. If you wish to use multiple decorators on a test
90 method, then you must either make this one the top-most decorator, or you
91 must write your decorators so that they update the wrapping function with
92 the attributes of the wrapped function. The latter is recommended style
93 anyway. ``functools.wraps``, ``functools.wrapper`` and
94 ``twisted.python.util.mergeFunctionMetadata`` can help you do this.
96 :param test_runner: A ``RunTest`` factory that takes a test case and an
97 optional list of exception handlers. See ``RunTest``.
98 :param kwargs: Keyword arguments to pass on as extra arguments to
99 'test_runner'.
100 :return: A decorator to be used for marking a test as needing a special
101 runner.
103 def decorator(function):
104 # Set an attribute on 'function' which will inform TestCase how to
105 # make the runner.
106 function._run_test_with = (
107 lambda case, handlers=None:
108 test_runner(case, handlers=handlers, **kwargs))
109 return function
110 return decorator
113 def _copy_content(content_object):
114 """Make a copy of the given content object.
116 The content within ``content_object`` is iterated and saved. This is
117 useful when the source of the content is volatile, a log file in a
118 temporary directory for example.
120 :param content_object: A `content.Content` instance.
121 :return: A `content.Content` instance with the same mime-type as
122 ``content_object`` and a non-volatile copy of its content.
124 content_bytes = list(content_object.iter_bytes())
125 content_callback = lambda: content_bytes
126 return content.Content(content_object.content_type, content_callback)
129 def gather_details(source_dict, target_dict):
130 """Merge the details from ``source_dict`` into ``target_dict``.
132 :param source_dict: A dictionary of details will be gathered.
133 :param target_dict: A dictionary into which details will be gathered.
135 for name, content_object in source_dict.items():
136 new_name = name
137 disambiguator = itertools.count(1)
138 while new_name in target_dict:
139 new_name = '%s-%d' % (name, advance_iterator(disambiguator))
140 name = new_name
141 target_dict[name] = _copy_content(content_object)
144 class TestCase(unittest.TestCase):
145 """Extensions to the basic TestCase.
147 :ivar exception_handlers: Exceptions to catch from setUp, runTest and
148 tearDown. This list is able to be modified at any time and consists of
149 (exception_class, handler(case, result, exception_value)) pairs.
150 :cvar run_tests_with: A factory to make the ``RunTest`` to run tests with.
151 Defaults to ``RunTest``. The factory is expected to take a test case
152 and an optional list of exception handlers.
155 skipException = TestSkipped
157 run_tests_with = RunTest
159 def __init__(self, *args, **kwargs):
160 """Construct a TestCase.
162 :param testMethod: The name of the method to run.
163 :keyword runTest: Optional class to use to execute the test. If not
164 supplied ``RunTest`` is used. The instance to be used is created
165 when run() is invoked, so will be fresh each time. Overrides
166 ``TestCase.run_tests_with`` if given.
168 runTest = kwargs.pop('runTest', None)
169 super(TestCase, self).__init__(*args, **kwargs)
170 self._cleanups = []
171 self._unique_id_gen = itertools.count(1)
172 # Generators to ensure unique traceback ids. Maps traceback label to
173 # iterators.
174 self._traceback_id_gens = {}
175 self.__setup_called = False
176 self.__teardown_called = False
177 # __details is lazy-initialized so that a constructed-but-not-run
178 # TestCase is safe to use with clone_test_with_new_id.
179 self.__details = None
180 test_method = self._get_test_method()
181 if runTest is None:
182 runTest = getattr(
183 test_method, '_run_test_with', self.run_tests_with)
184 self.__RunTest = runTest
185 self.__exception_handlers = []
186 self.exception_handlers = [
187 (self.skipException, self._report_skip),
188 (self.failureException, self._report_failure),
189 (_ExpectedFailure, self._report_expected_failure),
190 (_UnexpectedSuccess, self._report_unexpected_success),
191 (Exception, self._report_error),
193 if sys.version_info < (2, 6):
194 # Catch old-style string exceptions with None as the instance
195 self.exception_handlers.append((type(None), self._report_error))
197 def __eq__(self, other):
198 eq = getattr(unittest.TestCase, '__eq__', None)
199 if eq is not None and not unittest.TestCase.__eq__(self, other):
200 return False
201 return self.__dict__ == other.__dict__
203 def __repr__(self):
204 # We add id to the repr because it makes testing testtools easier.
205 return "<%s id=0x%0x>" % (self.id(), id(self))
207 def addDetail(self, name, content_object):
208 """Add a detail to be reported with this test's outcome.
210 For more details see pydoc testtools.TestResult.
212 :param name: The name to give this detail.
213 :param content_object: The content object for this detail. See
214 testtools.content for more detail.
216 if self.__details is None:
217 self.__details = {}
218 self.__details[name] = content_object
220 def getDetails(self):
221 """Get the details dict that will be reported with this test's outcome.
223 For more details see pydoc testtools.TestResult.
225 if self.__details is None:
226 self.__details = {}
227 return self.__details
229 def patch(self, obj, attribute, value):
230 """Monkey-patch 'obj.attribute' to 'value' while the test is running.
232 If 'obj' has no attribute, then the monkey-patch will still go ahead,
233 and the attribute will be deleted instead of restored to its original
234 value.
236 :param obj: The object to patch. Can be anything.
237 :param attribute: The attribute on 'obj' to patch.
238 :param value: The value to set 'obj.attribute' to.
240 self.addCleanup(patch(obj, attribute, value))
242 def shortDescription(self):
243 return self.id()
245 def skipTest(self, reason):
246 """Cause this test to be skipped.
248 This raises self.skipException(reason). skipException is raised
249 to permit a skip to be triggered at any point (during setUp or the
250 testMethod itself). The run() method catches skipException and
251 translates that into a call to the result objects addSkip method.
253 :param reason: The reason why the test is being skipped. This must
254 support being cast into a unicode string for reporting.
256 raise self.skipException(reason)
258 # skipTest is how python2.7 spells this. Sometime in the future
259 # This should be given a deprecation decorator - RBC 20100611.
260 skip = skipTest
262 def _formatTypes(self, classOrIterable):
263 """Format a class or a bunch of classes for display in an error."""
264 className = getattr(classOrIterable, '__name__', None)
265 if className is None:
266 className = ', '.join(klass.__name__ for klass in classOrIterable)
267 return className
269 def addCleanup(self, function, *arguments, **keywordArguments):
270 """Add a cleanup function to be called after tearDown.
272 Functions added with addCleanup will be called in reverse order of
273 adding after tearDown, or after setUp if setUp raises an exception.
275 If a function added with addCleanup raises an exception, the error
276 will be recorded as a test error, and the next cleanup will then be
277 run.
279 Cleanup functions are always called before a test finishes running,
280 even if setUp is aborted by an exception.
282 self._cleanups.append((function, arguments, keywordArguments))
284 def addOnException(self, handler):
285 """Add a handler to be called when an exception occurs in test code.
287 This handler cannot affect what result methods are called, and is
288 called before any outcome is called on the result object. An example
289 use for it is to add some diagnostic state to the test details dict
290 which is expensive to calculate and not interesting for reporting in
291 the success case.
293 Handlers are called before the outcome (such as addFailure) that
294 the exception has caused.
296 Handlers are called in first-added, first-called order, and if they
297 raise an exception, that will propogate out of the test running
298 machinery, halting test processing. As a result, do not call code that
299 may unreasonably fail.
301 self.__exception_handlers.append(handler)
303 def _add_reason(self, reason):
304 self.addDetail('reason', content.Content(
305 content.ContentType('text', 'plain'),
306 lambda: [reason.encode('utf8')]))
308 def assertEqual(self, expected, observed, message=''):
309 """Assert that 'expected' is equal to 'observed'.
311 :param expected: The expected value.
312 :param observed: The observed value.
313 :param message: An optional message to include in the error.
315 matcher = Equals(expected)
316 self.assertThat(observed, matcher, message)
318 failUnlessEqual = assertEquals = assertEqual
320 def assertIn(self, needle, haystack):
321 """Assert that needle is in haystack."""
322 self.assertThat(haystack, Contains(needle))
324 def assertIsNone(self, observed, message=''):
325 """Assert that 'observed' is equal to None.
327 :param observed: The observed value.
328 :param message: An optional message describing the error.
330 matcher = Is(None)
331 self.assertThat(observed, matcher, message)
333 def assertIsNotNone(self, observed, message=''):
334 """Assert that 'observed' is not equal to None.
336 :param observed: The observed value.
337 :param message: An optional message describing the error.
339 matcher = Not(Is(None))
340 self.assertThat(observed, matcher, message)
342 def assertIs(self, expected, observed, message=''):
343 """Assert that 'expected' is 'observed'.
345 :param expected: The expected value.
346 :param observed: The observed value.
347 :param message: An optional message describing the error.
349 matcher = Is(expected)
350 self.assertThat(observed, matcher, message)
352 def assertIsNot(self, expected, observed, message=''):
353 """Assert that 'expected' is not 'observed'."""
354 matcher = Not(Is(expected))
355 self.assertThat(observed, matcher, message)
357 def assertNotIn(self, needle, haystack):
358 """Assert that needle is not in haystack."""
359 matcher = Not(Contains(needle))
360 self.assertThat(haystack, matcher)
362 def assertIsInstance(self, obj, klass, msg=None):
363 if isinstance(klass, tuple):
364 matcher = IsInstance(*klass)
365 else:
366 matcher = IsInstance(klass)
367 self.assertThat(obj, matcher, msg)
369 def assertRaises(self, excClass, callableObj, *args, **kwargs):
370 """Fail unless an exception of class excClass is thrown
371 by callableObj when invoked with arguments args and keyword
372 arguments kwargs. If a different type of exception is
373 thrown, it will not be caught, and the test case will be
374 deemed to have suffered an error, exactly as for an
375 unexpected exception.
377 class ReRaiseOtherTypes(object):
378 def match(self, matchee):
379 if not issubclass(matchee[0], excClass):
380 reraise(*matchee)
381 class CaptureMatchee(object):
382 def match(self, matchee):
383 self.matchee = matchee[1]
384 capture = CaptureMatchee()
385 matcher = Raises(MatchesAll(ReRaiseOtherTypes(),
386 MatchesException(excClass), capture))
388 self.assertThat(lambda: callableObj(*args, **kwargs), matcher)
389 return capture.matchee
390 failUnlessRaises = assertRaises
392 def assertThat(self, matchee, matcher, message='', verbose=False):
393 """Assert that matchee is matched by matcher.
395 :param matchee: An object to match with matcher.
396 :param matcher: An object meeting the testtools.Matcher protocol.
397 :raises MismatchError: When matcher does not match thing.
399 matcher = Annotate.if_message(message, matcher)
400 mismatch = matcher.match(matchee)
401 if not mismatch:
402 return
403 existing_details = self.getDetails()
404 for (name, content) in mismatch.get_details().items():
405 full_name = name
406 suffix = 1
407 while full_name in existing_details:
408 full_name = "%s-%d" % (name, suffix)
409 suffix += 1
410 self.addDetail(full_name, content)
411 raise MismatchError(matchee, matcher, mismatch, verbose)
413 def defaultTestResult(self):
414 return TestResult()
416 def expectFailure(self, reason, predicate, *args, **kwargs):
417 """Check that a test fails in a particular way.
419 If the test fails in the expected way, a KnownFailure is caused. If it
420 succeeds an UnexpectedSuccess is caused.
422 The expected use of expectFailure is as a barrier at the point in a
423 test where the test would fail. For example:
424 >>> def test_foo(self):
425 >>> self.expectFailure("1 should be 0", self.assertNotEqual, 1, 0)
426 >>> self.assertEqual(1, 0)
428 If in the future 1 were to equal 0, the expectFailure call can simply
429 be removed. This separation preserves the original intent of the test
430 while it is in the expectFailure mode.
432 # TODO: implement with matchers.
433 self._add_reason(reason)
434 try:
435 predicate(*args, **kwargs)
436 except self.failureException:
437 # GZ 2010-08-12: Don't know how to avoid exc_info cycle as the new
438 # unittest _ExpectedFailure wants old traceback
439 exc_info = sys.exc_info()
440 try:
441 self._report_traceback(exc_info)
442 raise _ExpectedFailure(exc_info)
443 finally:
444 del exc_info
445 else:
446 raise _UnexpectedSuccess(reason)
448 def getUniqueInteger(self):
449 """Get an integer unique to this test.
451 Returns an integer that is guaranteed to be unique to this instance.
452 Use this when you need an arbitrary integer in your test, or as a
453 helper for custom anonymous factory methods.
455 return advance_iterator(self._unique_id_gen)
457 def getUniqueString(self, prefix=None):
458 """Get a string unique to this test.
460 Returns a string that is guaranteed to be unique to this instance. Use
461 this when you need an arbitrary string in your test, or as a helper
462 for custom anonymous factory methods.
464 :param prefix: The prefix of the string. If not provided, defaults
465 to the id of the tests.
466 :return: A bytestring of '<prefix>-<unique_int>'.
468 if prefix is None:
469 prefix = self.id()
470 return '%s-%d' % (prefix, self.getUniqueInteger())
472 def onException(self, exc_info, tb_label='traceback'):
473 """Called when an exception propogates from test code.
475 :seealso addOnException:
477 if exc_info[0] not in [
478 TestSkipped, _UnexpectedSuccess, _ExpectedFailure]:
479 self._report_traceback(exc_info, tb_label=tb_label)
480 for handler in self.__exception_handlers:
481 handler(exc_info)
483 @staticmethod
484 def _report_error(self, result, err):
485 result.addError(self, details=self.getDetails())
487 @staticmethod
488 def _report_expected_failure(self, result, err):
489 result.addExpectedFailure(self, details=self.getDetails())
491 @staticmethod
492 def _report_failure(self, result, err):
493 result.addFailure(self, details=self.getDetails())
495 @staticmethod
496 def _report_skip(self, result, err):
497 if err.args:
498 reason = err.args[0]
499 else:
500 reason = "no reason given."
501 self._add_reason(reason)
502 result.addSkip(self, details=self.getDetails())
504 def _report_traceback(self, exc_info, tb_label='traceback'):
505 id_gen = self._traceback_id_gens.setdefault(
506 tb_label, itertools.count(0))
507 tb_id = advance_iterator(id_gen)
508 if tb_id:
509 tb_label = '%s-%d' % (tb_label, tb_id)
510 self.addDetail(tb_label, content.TracebackContent(exc_info, self))
512 @staticmethod
513 def _report_unexpected_success(self, result, err):
514 result.addUnexpectedSuccess(self, details=self.getDetails())
516 def run(self, result=None):
517 return self.__RunTest(self, self.exception_handlers).run(result)
519 def _run_setup(self, result):
520 """Run the setUp function for this test.
522 :param result: A testtools.TestResult to report activity to.
523 :raises ValueError: If the base class setUp is not called, a
524 ValueError is raised.
526 ret = self.setUp()
527 if not self.__setup_called:
528 raise ValueError(
529 "TestCase.setUp was not called. Have you upcalled all the "
530 "way up the hierarchy from your setUp? e.g. Call "
531 "super(%s, self).setUp() from your setUp()."
532 % self.__class__.__name__)
533 return ret
535 def _run_teardown(self, result):
536 """Run the tearDown function for this test.
538 :param result: A testtools.TestResult to report activity to.
539 :raises ValueError: If the base class tearDown is not called, a
540 ValueError is raised.
542 ret = self.tearDown()
543 if not self.__teardown_called:
544 raise ValueError(
545 "TestCase.tearDown was not called. Have you upcalled all the "
546 "way up the hierarchy from your tearDown? e.g. Call "
547 "super(%s, self).tearDown() from your tearDown()."
548 % self.__class__.__name__)
549 return ret
551 def _get_test_method(self):
552 absent_attr = object()
553 # Python 2.5+
554 method_name = getattr(self, '_testMethodName', absent_attr)
555 if method_name is absent_attr:
556 # Python 2.4
557 method_name = getattr(self, '_TestCase__testMethodName')
558 return getattr(self, method_name)
560 def _run_test_method(self, result):
561 """Run the test method for this test.
563 :param result: A testtools.TestResult to report activity to.
564 :return: None.
566 return self._get_test_method()()
568 def useFixture(self, fixture):
569 """Use fixture in a test case.
571 The fixture will be setUp, and self.addCleanup(fixture.cleanUp) called.
573 :param fixture: The fixture to use.
574 :return: The fixture, after setting it up and scheduling a cleanup for
577 try:
578 fixture.setUp()
579 except:
580 gather_details(fixture.getDetails(), self.getDetails())
581 raise
582 else:
583 self.addCleanup(fixture.cleanUp)
584 self.addCleanup(
585 gather_details, fixture.getDetails(), self.getDetails())
586 return fixture
588 def setUp(self):
589 super(TestCase, self).setUp()
590 self.__setup_called = True
592 def tearDown(self):
593 super(TestCase, self).tearDown()
594 unittest.TestCase.tearDown(self)
595 self.__teardown_called = True
598 class PlaceHolder(object):
599 """A placeholder test.
601 `PlaceHolder` implements much of the same interface as TestCase and is
602 particularly suitable for being added to TestResults.
605 def __init__(self, test_id, short_description=None):
606 """Construct a `PlaceHolder`.
608 :param test_id: The id of the placeholder test.
609 :param short_description: The short description of the place holder
610 test. If not provided, the id will be used instead.
612 self._test_id = test_id
613 self._short_description = short_description
615 def __call__(self, result=None):
616 return self.run(result=result)
618 def __repr__(self):
619 internal = [self._test_id]
620 if self._short_description is not None:
621 internal.append(self._short_description)
622 return "<%s.%s(%s)>" % (
623 self.__class__.__module__,
624 self.__class__.__name__,
625 ", ".join(map(repr, internal)))
627 def __str__(self):
628 return self.id()
630 def countTestCases(self):
631 return 1
633 def debug(self):
634 pass
636 def id(self):
637 return self._test_id
639 def run(self, result=None):
640 if result is None:
641 result = TestResult()
642 result.startTest(self)
643 result.addSuccess(self)
644 result.stopTest(self)
646 def shortDescription(self):
647 if self._short_description is None:
648 return self.id()
649 else:
650 return self._short_description
653 class ErrorHolder(PlaceHolder):
654 """A placeholder test that will error out when run."""
656 failureException = None
658 def __init__(self, test_id, error, short_description=None):
659 """Construct an `ErrorHolder`.
661 :param test_id: The id of the test.
662 :param error: The exc info tuple that will be used as the test's error.
663 :param short_description: An optional short description of the test.
665 super(ErrorHolder, self).__init__(
666 test_id, short_description=short_description)
667 self._error = error
669 def __repr__(self):
670 internal = [self._test_id, self._error]
671 if self._short_description is not None:
672 internal.append(self._short_description)
673 return "<%s.%s(%s)>" % (
674 self.__class__.__module__,
675 self.__class__.__name__,
676 ", ".join(map(repr, internal)))
678 def run(self, result=None):
679 if result is None:
680 result = TestResult()
681 result.startTest(self)
682 result.addError(self, self._error)
683 result.stopTest(self)
686 # Python 2.4 did not know how to copy functions.
687 if types.FunctionType not in copy._copy_dispatch:
688 copy._copy_dispatch[types.FunctionType] = copy._copy_immutable
691 def clone_test_with_new_id(test, new_id):
692 """Copy a `TestCase`, and give the copied test a new id.
694 This is only expected to be used on tests that have been constructed but
695 not executed.
697 newTest = copy.copy(test)
698 newTest.id = lambda: new_id
699 return newTest
702 def skip(reason):
703 """A decorator to skip unit tests.
705 This is just syntactic sugar so users don't have to change any of their
706 unit tests in order to migrate to python 2.7, which provides the
707 @unittest.skip decorator.
709 def decorator(test_item):
710 if wraps is not None:
711 @wraps(test_item)
712 def skip_wrapper(*args, **kwargs):
713 raise TestCase.skipException(reason)
714 else:
715 def skip_wrapper(test_item):
716 test_item.skip(reason)
717 return skip_wrapper
718 return decorator
721 def skipIf(condition, reason):
722 """Skip a test if the condition is true."""
723 if condition:
724 return skip(reason)
725 def _id(obj):
726 return obj
727 return _id
730 def skipUnless(condition, reason):
731 """Skip a test unless the condition is true."""
732 if not condition:
733 return skip(reason)
734 def _id(obj):
735 return obj
736 return _id
739 class ExpectedException:
740 """A context manager to handle expected exceptions.
742 In Python 2.5 or later::
744 def test_foo(self):
745 with ExpectedException(ValueError, 'fo.*'):
746 raise ValueError('foo')
748 will pass. If the raised exception has a type other than the specified
749 type, it will be re-raised. If it has a 'str()' that does not match the
750 given regular expression, an AssertionError will be raised. If no
751 exception is raised, an AssertionError will be raised.
754 def __init__(self, exc_type, value_re=None):
755 """Construct an `ExpectedException`.
757 :param exc_type: The type of exception to expect.
758 :param value_re: A regular expression to match against the
759 'str()' of the raised exception.
761 self.exc_type = exc_type
762 self.value_re = value_re
764 def __enter__(self):
765 pass
767 def __exit__(self, exc_type, exc_value, traceback):
768 if exc_type is None:
769 raise AssertionError('%s not raised.' % self.exc_type.__name__)
770 if exc_type != self.exc_type:
771 return False
772 if self.value_re:
773 matcher = MatchesException(self.exc_type, self.value_re)
774 mismatch = matcher.match((exc_type, exc_value, traceback))
775 if mismatch:
776 raise AssertionError(mismatch.describe())
777 return True
780 # Signal that this is part of the testing framework, and that code from this
781 # should not normally appear in tracebacks.
782 __unittest = True