web/lib/django/test/simple.py
author ymh <ymh.work@gmail.com>
Wed, 02 Jun 2010 18:57:35 +0200
changeset 38 77b6da96e6f1
permissions -rw-r--r--
update django
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
38
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
import sys
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
import signal
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
import unittest
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
from django.conf import settings
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
from django.db.models import get_app, get_apps
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
from django.test import _doctest as doctest
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
from django.test.utils import setup_test_environment, teardown_test_environment
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
from django.test.testcases import OutputChecker, DocTestRunner, TestCase
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
# The module name for tests outside models.py
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
TEST_MODULE = 'tests'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
doctestOutputChecker = OutputChecker()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
class DjangoTestRunner(unittest.TextTestRunner):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
    def __init__(self, verbosity=0, failfast=False, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
        super(DjangoTestRunner, self).__init__(verbosity=verbosity, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
        self.failfast = failfast
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
        self._keyboard_interrupt_intercepted = False
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
    def run(self, *args, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
        Runs the test suite after registering a custom signal handler
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
        that triggers a graceful exit when Ctrl-C is pressed.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
        self._default_keyboard_interrupt_handler = signal.signal(signal.SIGINT,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
            self._keyboard_interrupt_handler)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
        try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
            result = super(DjangoTestRunner, self).run(*args, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
        finally:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
            signal.signal(signal.SIGINT, self._default_keyboard_interrupt_handler)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
        return result
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
    def _keyboard_interrupt_handler(self, signal_number, stack_frame):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
        Handles Ctrl-C by setting a flag that will stop the test run when
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
        the currently running test completes.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
        self._keyboard_interrupt_intercepted = True
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
        sys.stderr.write(" <Test run halted by Ctrl-C> ")
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
        # Set the interrupt handler back to the default handler, so that
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
        # another Ctrl-C press will trigger immediate exit.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
        signal.signal(signal.SIGINT, self._default_keyboard_interrupt_handler)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
    def _makeResult(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
        result = super(DjangoTestRunner, self)._makeResult()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
        failfast = self.failfast
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
        def stoptest_override(func):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
            def stoptest(test):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
                # If we were set to failfast and the unit test failed,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
                # or if the user has typed Ctrl-C, report and quit
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
                if (failfast and not result.wasSuccessful()) or \
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
                    self._keyboard_interrupt_intercepted:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
                    result.stop()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
                func(test)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
            return stoptest
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
        setattr(result, 'stopTest', stoptest_override(result.stopTest))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
        return result
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
def get_tests(app_module):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
    try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
        app_path = app_module.__name__.split('.')[:-1]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
        test_module = __import__('.'.join(app_path + [TEST_MODULE]), {}, {}, TEST_MODULE)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
    except ImportError, e:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
        # Couldn't import tests.py. Was it due to a missing file, or
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
        # due to an import error in a tests.py that actually exists?
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
        import os.path
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
        from imp import find_module
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
        try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
            mod = find_module(TEST_MODULE, [os.path.dirname(app_module.__file__)])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
        except ImportError:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
            # 'tests' module doesn't exist. Move on.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
            test_module = None
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
            # The module exists, so there must be an import error in the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
            # test module itself. We don't need the module; so if the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
            # module was a single file module (i.e., tests.py), close the file
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
            # handle returned by find_module. Otherwise, the test module
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
            # is a directory, and there is nothing to close.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
            if mod[0]:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
                mod[0].close()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
            raise
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
    return test_module
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
def build_suite(app_module):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
    "Create a complete Django test suite for the provided application module"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
    suite = unittest.TestSuite()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
    # Load unit and doctests in the models.py module. If module has
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
    # a suite() method, use it. Otherwise build the test suite ourselves.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
    if hasattr(app_module, 'suite'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
        suite.addTest(app_module.suite())
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
    else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
        suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(app_module))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
        try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
            suite.addTest(doctest.DocTestSuite(app_module,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
                                               checker=doctestOutputChecker,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
                                               runner=DocTestRunner))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
        except ValueError:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
            # No doc tests in models.py
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
            pass
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
    # Check to see if a separate 'tests' module exists parallel to the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
    # models module
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
    test_module = get_tests(app_module)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
    if test_module:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
        # Load unit and doctests in the tests.py module. If module has
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
        # a suite() method, use it. Otherwise build the test suite ourselves.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
        if hasattr(test_module, 'suite'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
            suite.addTest(test_module.suite())
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
            suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(test_module))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
            try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
                suite.addTest(doctest.DocTestSuite(test_module,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119
                                                   checker=doctestOutputChecker,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   120
                                                   runner=DocTestRunner))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   121
            except ValueError:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   122
                # No doc tests in tests.py
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   123
                pass
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   124
    return suite
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   125
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   126
def build_test(label):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   127
    """Construct a test case with the specified label. Label should be of the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   128
    form model.TestClass or model.TestClass.test_method. Returns an
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   129
    instantiated test or test suite corresponding to the label provided.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   130
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   131
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   132
    parts = label.split('.')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   133
    if len(parts) < 2 or len(parts) > 3:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   134
        raise ValueError("Test label '%s' should be of the form app.TestCase or app.TestCase.test_method" % label)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   135
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   136
    #
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   137
    # First, look for TestCase instances with a name that matches
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   138
    #
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   139
    app_module = get_app(parts[0])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   140
    test_module = get_tests(app_module)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   141
    TestClass = getattr(app_module, parts[1], None)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   142
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   143
    # Couldn't find the test class in models.py; look in tests.py
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   144
    if TestClass is None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   145
        if test_module:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   146
            TestClass = getattr(test_module, parts[1], None)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   147
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   148
    try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   149
        if issubclass(TestClass, unittest.TestCase):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   150
            if len(parts) == 2: # label is app.TestClass
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   151
                try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   152
                    return unittest.TestLoader().loadTestsFromTestCase(TestClass)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   153
                except TypeError:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   154
                    raise ValueError("Test label '%s' does not refer to a test class" % label)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   155
            else: # label is app.TestClass.test_method
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   156
                return TestClass(parts[2])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   157
    except TypeError:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   158
        # TestClass isn't a TestClass - it must be a method or normal class
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   159
        pass
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   160
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   161
    #
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   162
    # If there isn't a TestCase, look for a doctest that matches
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   163
    #
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   164
    tests = []
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   165
    for module in app_module, test_module:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   166
        try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   167
            doctests = doctest.DocTestSuite(module,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   168
                                            checker=doctestOutputChecker,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   169
                                            runner=DocTestRunner)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   170
            # Now iterate over the suite, looking for doctests whose name
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   171
            # matches the pattern that was given
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   172
            for test in doctests:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   173
                if test._dt_test.name in (
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   174
                        '%s.%s' % (module.__name__, '.'.join(parts[1:])),
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   175
                        '%s.__test__.%s' % (module.__name__, '.'.join(parts[1:]))):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   176
                    tests.append(test)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   177
        except ValueError:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   178
            # No doctests found.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   179
            pass
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   180
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   181
    # If no tests were found, then we were given a bad test label.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   182
    if not tests:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   183
        raise ValueError("Test label '%s' does not refer to a test" % label)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   184
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   185
    # Construct a suite out of the tests that matched.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   186
    return unittest.TestSuite(tests)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   187
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   188
def partition_suite(suite, classes, bins):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   189
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   190
    Partitions a test suite by test type.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   191
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   192
    classes is a sequence of types
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   193
    bins is a sequence of TestSuites, one more than classes
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   194
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   195
    Tests of type classes[i] are added to bins[i],
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   196
    tests with no match found in classes are place in bins[-1]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   197
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   198
    for test in suite:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   199
        if isinstance(test, unittest.TestSuite):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   200
            partition_suite(test, classes, bins)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   201
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   202
            for i in range(len(classes)):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   203
                if isinstance(test, classes[i]):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   204
                    bins[i].addTest(test)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   205
                    break
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   206
            else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   207
                bins[-1].addTest(test)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   208
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   209
def reorder_suite(suite, classes):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   210
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   211
    Reorders a test suite by test type.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   212
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   213
    classes is a sequence of types
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   214
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   215
    All tests of type clases[0] are placed first, then tests of type classes[1], etc.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   216
    Tests with no match in classes are placed last.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   217
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   218
    class_count = len(classes)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   219
    bins = [unittest.TestSuite() for i in range(class_count+1)]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   220
    partition_suite(suite, classes, bins)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   221
    for i in range(class_count):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   222
        bins[0].addTests(bins[i+1])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   223
    return bins[0]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   224
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   225
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   226
class DjangoTestSuiteRunner(object):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   227
    def __init__(self, verbosity=1, interactive=True, failfast=True, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   228
        self.verbosity = verbosity
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   229
        self.interactive = interactive
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   230
        self.failfast = failfast
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   231
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   232
    def setup_test_environment(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   233
        setup_test_environment()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   234
        settings.DEBUG = False
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   235
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   236
    def build_suite(self, test_labels, extra_tests=None, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   237
        suite = unittest.TestSuite()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   238
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   239
        if test_labels:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   240
            for label in test_labels:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   241
                if '.' in label:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   242
                    suite.addTest(build_test(label))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   243
                else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   244
                    app = get_app(label)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   245
                    suite.addTest(build_suite(app))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   246
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   247
            for app in get_apps():
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   248
                suite.addTest(build_suite(app))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   249
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   250
        if extra_tests:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   251
            for test in extra_tests:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   252
                suite.addTest(test)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   253
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   254
        return reorder_suite(suite, (TestCase,))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   255
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   256
    def setup_databases(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   257
        from django.db import connections
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   258
        old_names = []
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   259
        mirrors = []
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   260
        for alias in connections:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   261
            connection = connections[alias]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   262
            # If the database is a test mirror, redirect it's connection
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   263
            # instead of creating a test database.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   264
            if connection.settings_dict['TEST_MIRROR']:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   265
                mirrors.append((alias, connection))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   266
                mirror_alias = connection.settings_dict['TEST_MIRROR']
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   267
                connections._connections[alias] = connections[mirror_alias]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   268
            else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   269
                old_names.append((connection, connection.settings_dict['NAME']))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   270
                connection.creation.create_test_db(self.verbosity, autoclobber=not self.interactive)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   271
        return old_names, mirrors
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   272
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   273
    def run_suite(self, suite, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   274
        return DjangoTestRunner(verbosity=self.verbosity, failfast=self.failfast).run(suite)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   275
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   276
    def teardown_databases(self, old_config, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   277
        from django.db import connections
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   278
        old_names, mirrors = old_config
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   279
        # Point all the mirrors back to the originals
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   280
        for alias, connection in mirrors:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   281
            connections._connections[alias] = connection
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   282
        # Destroy all the non-mirror databases
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   283
        for connection, old_name in old_names:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   284
            connection.creation.destroy_test_db(old_name, self.verbosity)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   285
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   286
    def teardown_test_environment(self, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   287
        teardown_test_environment()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   288
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   289
    def suite_result(self, suite, result, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   290
        return len(result.failures) + len(result.errors)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   291
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   292
    def run_tests(self, test_labels, extra_tests=None, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   293
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   294
        Run the unit tests for all the test labels in the provided list.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   295
        Labels must be of the form:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   296
         - app.TestClass.test_method
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   297
            Run a single specific test method
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   298
         - app.TestClass
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   299
            Run all the test methods in a given class
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   300
         - app
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   301
            Search for doctests and unittests in the named application.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   302
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   303
        When looking for tests, the test runner will look in the models and
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   304
        tests modules for the application.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   305
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   306
        A list of 'extra' tests may also be provided; these tests
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   307
        will be added to the test suite.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   308
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   309
        Returns the number of tests that failed.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   310
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   311
        self.setup_test_environment()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   312
        suite = self.build_suite(test_labels, extra_tests)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   313
        old_config = self.setup_databases()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   314
        result = self.run_suite(suite)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   315
        self.teardown_databases(old_config)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   316
        self.teardown_test_environment()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   317
        return self.suite_result(suite, result)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   318
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   319
def run_tests(test_labels, verbosity=1, interactive=True, failfast=False, extra_tests=None):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   320
    import warnings
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   321
    warnings.warn(
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   322
        'The run_tests() test runner has been deprecated in favor of DjangoTestSuiteRunner.',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   323
        PendingDeprecationWarning
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   324
    )
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   325
    test_runner = DjangoTestSuiteRunner(verbosity=verbosity, interactive=interactive, failfast=failfast)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   326
    return test_runner.run_tests(test_labels, extra_tests=extra_tests)