| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- # -*- coding: utf-8 -*-
- # test/scaffold.py
- # Part of python-daemon, an implementation of PEP 3143.
- #
- # Copyright © 2007–2009 Ben Finney <ben+python@benfinney.id.au>
- # This is free software; you may copy, modify and/or distribute this work
- # under the terms of the GNU General Public License, version 2 or later.
- # No warranty expressed or implied. See the file LICENSE.GPL-2 for details.
- """ Scaffolding for unit test modules.
- """
- import unittest
- import doctest
- import logging
- import os
- import sys
- import operator
- import textwrap
- from minimock import (
- Mock,
- TraceTracker as MockTracker,
- mock,
- restore as mock_restore,
- )
- test_dir = os.path.dirname(os.path.abspath(__file__))
- parent_dir = os.path.dirname(test_dir)
- if not test_dir in sys.path:
- sys.path.insert(1, test_dir)
- if not parent_dir in sys.path:
- sys.path.insert(1, parent_dir)
- # Disable all but the most critical logging messages
- logging.disable(logging.CRITICAL)
- def get_python_module_names(file_list, file_suffix='.py'):
- """ Return a list of module names from a filename list. """
- module_names = [m[:m.rfind(file_suffix)] for m in file_list
- if m.endswith(file_suffix)]
- return module_names
- def get_test_module_names(module_list, module_prefix='test_'):
- """ Return the list of module names that qualify as test modules. """
- module_names = [m for m in module_list
- if m.startswith(module_prefix)]
- return module_names
- def make_suite(path=test_dir):
- """ Create the test suite for the given path. """
- loader = unittest.TestLoader()
- python_module_names = get_python_module_names(os.listdir(path))
- test_module_names = get_test_module_names(python_module_names)
- suite = loader.loadTestsFromNames(test_module_names)
- return suite
- def get_function_signature(func):
- """ Get the function signature as a mapping of attributes. """
- arg_count = func.func_code.co_argcount
- arg_names = func.func_code.co_varnames[:arg_count]
- arg_defaults = {}
- func_defaults = ()
- if func.func_defaults is not None:
- func_defaults = func.func_defaults
- for (name, value) in zip(arg_names[::-1], func_defaults[::-1]):
- arg_defaults[name] = value
- signature = {
- 'name': func.__name__,
- 'arg_count': arg_count,
- 'arg_names': arg_names,
- 'arg_defaults': arg_defaults,
- }
- non_pos_names = list(func.func_code.co_varnames[arg_count:])
- COLLECTS_ARBITRARY_POSITIONAL_ARGS = 0x04
- if func.func_code.co_flags & COLLECTS_ARBITRARY_POSITIONAL_ARGS:
- signature['var_args'] = non_pos_names.pop(0)
- COLLECTS_ARBITRARY_KEYWORD_ARGS = 0x08
- if func.func_code.co_flags & COLLECTS_ARBITRARY_KEYWORD_ARGS:
- signature['var_kw_args'] = non_pos_names.pop(0)
- return signature
- def format_function_signature(func):
- """ Format the function signature as printable text. """
- signature = get_function_signature(func)
- args_text = []
- for arg_name in signature['arg_names']:
- if arg_name in signature['arg_defaults']:
- arg_default = signature['arg_defaults'][arg_name]
- arg_text_template = "%(arg_name)s=%(arg_default)r"
- else:
- arg_text_template = "%(arg_name)s"
- args_text.append(arg_text_template % vars())
- if 'var_args' in signature:
- args_text.append("*%(var_args)s" % signature)
- if 'var_kw_args' in signature:
- args_text.append("**%(var_kw_args)s" % signature)
- signature_args_text = ", ".join(args_text)
- func_name = signature['name']
- signature_text = (
- "%(func_name)s(%(signature_args_text)s)" % vars())
- return signature_text
- class TestCase(unittest.TestCase):
- """ Test case behaviour. """
- def failUnlessRaises(self, exc_class, func, *args, **kwargs):
- """ Fail unless the function call raises the expected exception.
- Fail the test if an instance of the exception class
- ``exc_class`` is not raised when calling ``func`` with the
- arguments ``*args`` and ``**kwargs``.
- """
- try:
- super(TestCase, self).failUnlessRaises(
- exc_class, func, *args, **kwargs)
- except self.failureException:
- exc_class_name = exc_class.__name__
- msg = (
- "Exception %(exc_class_name)s not raised"
- " for function call:"
- " func=%(func)r args=%(args)r kwargs=%(kwargs)r"
- ) % vars()
- raise self.failureException(msg)
- def failIfIs(self, first, second, msg=None):
- """ Fail if the two objects are identical.
- Fail the test if ``first`` and ``second`` are identical,
- as determined by the ``is`` operator.
- """
- if first is second:
- if msg is None:
- msg = "%(first)r is %(second)r" % vars()
- raise self.failureException(msg)
- def failUnlessIs(self, first, second, msg=None):
- """ Fail unless the two objects are identical.
- Fail the test unless ``first`` and ``second`` are
- identical, as determined by the ``is`` operator.
- """
- if first is not second:
- if msg is None:
- msg = "%(first)r is not %(second)r" % vars()
- raise self.failureException(msg)
- assertIs = failUnlessIs
- assertNotIs = failIfIs
- def failIfIn(self, first, second, msg=None):
- """ Fail if the second object is in the first.
- Fail the test if ``first`` contains ``second``, as
- determined by the ``in`` operator.
- """
- if second in first:
- if msg is None:
- msg = "%(second)r is in %(first)r" % vars()
- raise self.failureException(msg)
- def failUnlessIn(self, first, second, msg=None):
- """ Fail unless the second object is in the first.
- Fail the test unless ``first`` contains ``second``, as
- determined by the ``in`` operator.
- """
- if second not in first:
- if msg is None:
- msg = "%(second)r is not in %(first)r" % vars()
- raise self.failureException(msg)
- assertIn = failUnlessIn
- assertNotIn = failIfIn
- def failUnlessOutputCheckerMatch(self, want, got, msg=None):
- """ Fail unless the specified string matches the expected.
- Fail the test unless ``want`` matches ``got``, as
- determined by a ``doctest.OutputChecker`` instance. This
- is not an equality check, but a pattern match according to
- the ``OutputChecker`` rules.
- """
- checker = doctest.OutputChecker()
- want = textwrap.dedent(want)
- source = ""
- example = doctest.Example(source, want)
- got = textwrap.dedent(got)
- checker_optionflags = reduce(operator.or_, [
- doctest.ELLIPSIS,
- ])
- if not checker.check_output(want, got, checker_optionflags):
- if msg is None:
- diff = checker.output_difference(
- example, got, checker_optionflags)
- msg = "\n".join([
- "Output received did not match expected output",
- "%(diff)s",
- ]) % vars()
- raise self.failureException(msg)
- assertOutputCheckerMatch = failUnlessOutputCheckerMatch
- def failUnlessMockCheckerMatch(self, want, tracker=None, msg=None):
- """ Fail unless the mock tracker matches the wanted output.
- Fail the test unless `want` matches the output tracked by
- `tracker` (defaults to ``self.mock_tracker``. This is not
- an equality check, but a pattern match according to the
- ``minimock.MinimockOutputChecker`` rules.
- """
- if tracker is None:
- tracker = self.mock_tracker
- if not tracker.check(want):
- if msg is None:
- diff = tracker.diff(want)
- msg = "\n".join([
- "Output received did not match expected output",
- "%(diff)s",
- ]) % vars()
- raise self.failureException(msg)
- def failIfMockCheckerMatch(self, want, tracker=None, msg=None):
- """ Fail if the mock tracker matches the specified output.
- Fail the test if `want` matches the output tracked by
- `tracker` (defaults to ``self.mock_tracker``. This is not
- an equality check, but a pattern match according to the
- ``minimock.MinimockOutputChecker`` rules.
- """
- if tracker is None:
- tracker = self.mock_tracker
- if tracker.check(want):
- if msg is None:
- diff = tracker.diff(want)
- msg = "\n".join([
- "Output received matched specified undesired output",
- "%(diff)s",
- ]) % vars()
- raise self.failureException(msg)
- assertMockCheckerMatch = failUnlessMockCheckerMatch
- assertNotMockCheckerMatch = failIfMockCheckerMatch
- def failIfIsInstance(self, obj, classes, msg=None):
- """ Fail if the object is an instance of the specified classes.
- Fail the test if the object ``obj`` is an instance of any
- of ``classes``.
- """
- if isinstance(obj, classes):
- if msg is None:
- msg = (
- "%(obj)r is an instance of one of %(classes)r"
- ) % vars()
- raise self.failureException(msg)
- def failUnlessIsInstance(self, obj, classes, msg=None):
- """ Fail unless the object is an instance of the specified classes.
- Fail the test unless the object ``obj`` is an instance of
- any of ``classes``.
- """
- if not isinstance(obj, classes):
- if msg is None:
- msg = (
- "%(obj)r is not an instance of any of %(classes)r"
- ) % vars()
- raise self.failureException(msg)
- assertIsInstance = failUnlessIsInstance
- assertNotIsInstance = failIfIsInstance
- def failUnlessFunctionInTraceback(self, traceback, function, msg=None):
- """ Fail if the function is not in the traceback.
- Fail the test if the function ``function`` is not at any
- of the levels in the traceback object ``traceback``.
- """
- func_in_traceback = False
- expect_code = function.func_code
- current_traceback = traceback
- while current_traceback is not None:
- if expect_code is current_traceback.tb_frame.f_code:
- func_in_traceback = True
- break
- current_traceback = current_traceback.tb_next
- if not func_in_traceback:
- if msg is None:
- msg = (
- "Traceback did not lead to original function"
- " %(function)s"
- ) % vars()
- raise self.failureException(msg)
- assertFunctionInTraceback = failUnlessFunctionInTraceback
- def failUnlessFunctionSignatureMatch(self, first, second, msg=None):
- """ Fail if the function signatures do not match.
- Fail the test if the function signature does not match
- between the ``first`` function and the ``second``
- function.
- The function signature includes:
- * function name,
- * count of named parameters,
- * sequence of named parameters,
- * default values of named parameters,
- * collector for arbitrary positional arguments,
- * collector for arbitrary keyword arguments.
- """
- first_signature = get_function_signature(first)
- second_signature = get_function_signature(second)
- if first_signature != second_signature:
- if msg is None:
- first_signature_text = format_function_signature(first)
- second_signature_text = format_function_signature(second)
- msg = (textwrap.dedent("""\
- Function signatures do not match:
- %(first_signature)r != %(second_signature)r
- Expected:
- %(first_signature_text)s
- Got:
- %(second_signature_text)s""")
- ) % vars()
- raise self.failureException(msg)
- assertFunctionSignatureMatch = failUnlessFunctionSignatureMatch
- class Exception_TestCase(TestCase):
- """ Test cases for exception classes. """
- def __init__(self, *args, **kwargs):
- """ Set up a new instance """
- self.valid_exceptions = NotImplemented
- super(Exception_TestCase, self).__init__(*args, **kwargs)
- def setUp(self):
- """ Set up test fixtures. """
- for exc_type, params in self.valid_exceptions.items():
- args = (None, ) * params['min_args']
- params['args'] = args
- instance = exc_type(*args)
- params['instance'] = instance
- super(Exception_TestCase, self).setUp()
- def test_exception_instance(self):
- """ Exception instance should be created. """
- for params in self.valid_exceptions.values():
- instance = params['instance']
- self.failIfIs(None, instance)
- def test_exception_types(self):
- """ Exception instances should match expected types. """
- for params in self.valid_exceptions.values():
- instance = params['instance']
- for match_type in params['types']:
- match_type_name = match_type.__name__
- fail_msg = (
- "%(instance)r is not an instance of"
- " %(match_type_name)s"
- ) % vars()
- self.failUnless(
- isinstance(instance, match_type),
- msg=fail_msg)
|