Ticket #8363: 8363.exclude-tests.diff

File 8363.exclude-tests.diff, 19.3 KB (added by Julien Phalip, 13 years ago)
  • django/core/management/commands/test.py

    diff --git a/django/core/management/commands/test.py b/django/core/management/commands/test.py
    index 1b3f2be..17fdba0 100644
    a b class Command(BaseCommand):  
    1212            help='Tells Django to stop running the test suite after first failed test.'),
    1313        make_option('--testrunner', action='store', dest='testrunner',
    1414            help='Tells Django to use specified test runner class instead of the one '+
    15                  'specified by the TEST_RUNNER setting.')
     15                 'specified by the TEST_RUNNER setting.'),
     16        make_option('-e', '--exclude', dest='exclude_labels', action='append', default=[],
     17            help='Test to exclude (use multiple --exclude to exclude multiple tests).'),
    1618    )
    1719    help = 'Runs the test suite for the specified applications, or the entire site if no apps are specified.'
    1820    args = '[appname ...]'
    class Command(BaseCommand):  
    5153        options.setdefault('failfast', False)
    5254
    5355        test_runner = TestRunner(**options)
    54         failures = test_runner.run_tests(test_labels)
     56        exclude_labels = options.get('exclude_labels')
     57        failures = test_runner.run_tests(test_labels,
     58                                         exclude_labels=exclude_labels)
    5559
    5660        if failures:
    5761            sys.exit(bool(failures))
  • django/test/simple.py

    diff --git a/django/test/simple.py b/django/test/simple.py
    index c9adfd2..d345d2e 100644
    a b from django.test import _doctest as doctest  
    77from django.test.utils import setup_test_environment, teardown_test_environment
    88from django.test.testcases import OutputChecker, DocTestRunner, TestCase
    99from django.utils import unittest
     10from django.utils.datastructures import SortedDict
    1011from django.utils.importlib import import_module
    1112from django.utils.module_loading import module_has_submodule
    1213
    def get_tests(app_module):  
    5051    return test_module
    5152
    5253def build_suite(app_module):
    53     "Create a complete Django test suite for the provided application module"
     54    """
     55    Create a complete Django test suite for the provided application module
     56    """
    5457    suite = unittest.TestSuite()
    5558
    56     # Load unit and doctests in the models.py module. If module has
    57     # a suite() method, use it. Otherwise build the test suite ourselves.
    58     if hasattr(app_module, 'suite'):
    59         suite.addTest(app_module.suite())
    60     else:
    61         suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(app_module))
    62         try:
    63             suite.addTest(doctest.DocTestSuite(app_module,
    64                                                checker=doctestOutputChecker,
    65                                                runner=DocTestRunner))
    66         except ValueError:
    67             # No doc tests in models.py
    68             pass
    69 
    70     # Check to see if a separate 'tests' module exists parallel to the
    71     # models module
    7259    test_module = get_tests(app_module)
    73     if test_module:
    74         # Load unit and doctests in the tests.py module. If module has
    75         # a suite() method, use it. Otherwise build the test suite ourselves.
    76         if hasattr(test_module, 'suite'):
    77             suite.addTest(test_module.suite())
    78         else:
    79             suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(test_module))
    80             try:
    81                 suite.addTest(doctest.DocTestSuite(test_module,
    82                                                    checker=doctestOutputChecker,
    83                                                    runner=DocTestRunner))
    84             except ValueError:
    85                 # No doc tests in tests.py
    86                 pass
     60
     61    for module in (app_module, test_module):
     62        if module:
     63            # Load unit and doctests in the module. If the module has a
     64            # suite() method, use it. Otherwise build the test suite ourselves.
     65            if hasattr(module, 'suite'):
     66                suite.addTest(module.suite())
     67            else:
     68                suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(module))
     69                try:
     70                    suite.addTest(doctest.DocTestSuite(module,
     71                                                       checker=doctestOutputChecker,
     72                                                       runner=DocTestRunner))
     73                except ValueError:
     74                    # No doc tests
     75                    pass
    8776    return suite
    8877
    89 def build_test(label):
    90     """Construct a test case with the specified label. Label should be of the
    91     form model.TestClass or model.TestClass.test_method. Returns an
    92     instantiated test or test suite corresponding to the label provided.
     78def get_test_cases(module, test_label, app_label, TestClass=None, test_name=None):
     79    test_cases = SortedDict()
     80    if TestClass is None:
     81        for test_name in dir(module):
     82            obj = getattr(module, test_name)
     83            if isinstance(obj, type) and issubclass(obj, unittest.TestCase):
     84                test_cases.update(get_test_cases(test_label, module, app_label, obj))
     85    else:
     86        try:
     87            if issubclass(TestClass, (unittest.TestCase, real_unittest.TestCase)):
     88                if test_name: # label is app.TestClass.test_method
     89                    label = '%s.%s.%s' % (app_label, TestClass.__name__, test_name)
     90                    test_cases[label] = TestClass(test_name)
     91                else: # label is app.TestClass
     92                    try:
     93                        for test_name in unittest.TestLoader().getTestCaseNames(TestClass):
     94                            label = '%s.%s.%s' % (app_label, TestClass.__name__, test_name)
     95                            test_cases[label] = TestClass(test_name)
     96                    except TypeError:
     97                        raise ValueError("Test label '%s' does not refer to a test class" % test_label)
     98        except TypeError:
     99            # TestClass isn't a TestClass - it must be a method or normal class
     100            print "aa"
     101    return test_cases
     102
     103def get_doctests(module, test_label):
     104    parts = test_label.split('.')
     105    doctests = SortedDict()
     106    try:
     107        existing_doctests = doctest.DocTestSuite(module,
     108                                        checker=doctestOutputChecker,
     109                                        runner=DocTestRunner)
     110        # Now iterate over the suite, looking for doctests whose name
     111        # matches the pattern that was given
     112        for test in existing_doctests:
     113            if test._dt_test.name in (
     114                    '%s.%s' % (module.__name__, '.'.join(parts[1:])),
     115                    '%s.__test__.%s' % (module.__name__, '.'.join(parts[1:]))):
     116                doctests[test._dt_test.name] = test
     117    except ValueError:
     118        # No doctests found.
     119        pass
     120    return doctests
    93121
     122def get_test_dict(label):
     123    """
     124    Returns a SortedDict with the associations label->test, where label is a
     125    full string reference to a test (of the form app.TestClass.test_method) and
     126    test is either a test case or a doctest. For example:
     127
     128        {
     129            'auth.AnonymousUserBackendTest.test_get_all_permissions':
     130                <django.contrib.auth.tests.auth_backends.AnonymousUserBackendTest testMethod=test_get_all_permissions>,
     131            'auth.AnonymousUserBackendTest.test_has_module_perms':
     132                <django.contrib.auth.tests.auth_backends.AnonymousUserBackendTest testMethod=test_has_module_perms>,
     133            ...
     134        }
    94135    """
    95136    parts = label.split('.')
    96     if len(parts) < 2 or len(parts) > 3:
    97         raise ValueError("Test label '%s' should be of the form app.TestCase or app.TestCase.test_method" % label)
    98137
    99     #
    100     # First, look for TestCase instances with a name that matches
    101     #
    102     app_module = get_app(parts[0])
    103     test_module = get_tests(app_module)
    104     TestClass = getattr(app_module, parts[1], None)
     138    if len(parts) < 1 or len(parts) > 3:
     139        raise ValueError("Test label '%s' should be of the form app, app.TestCase or app.TestCase.test_method" % label)
    105140
    106     # Couldn't find the test class in models.py; look in tests.py
    107     if TestClass is None:
    108         if test_module:
    109             TestClass = getattr(test_module, parts[1], None)
     141    # First, parse the label to figure out the app, the TestCase and the test_method.
     142    app_label = parts[0]
     143    app_module = get_app(app_label)
     144    test_module = get_tests(app_module)
    110145
    111     try:
    112         if issubclass(TestClass, (unittest.TestCase, real_unittest.TestCase)):
    113             if len(parts) == 2: # label is app.TestClass
    114                 try:
    115                     return unittest.TestLoader().loadTestsFromTestCase(TestClass)
    116                 except TypeError:
    117                     raise ValueError("Test label '%s' does not refer to a test class" % label)
    118             else: # label is app.TestClass.test_method
    119                 return TestClass(parts[2])
    120     except TypeError:
    121         # TestClass isn't a TestClass - it must be a method or normal class
    122         pass
     146    if len(parts) == 1:
     147        # Test label of the form: app
     148        TestClass = None
     149        test_name = None
     150    else:
     151        TestClass = getattr(app_module, parts[1], None)
     152        # Couldn't find the test class in models.py; look in tests.py
     153        if TestClass is None:
     154            if test_module:
     155                TestClass = getattr(test_module, parts[1], None)
     156
     157        if len(parts) == 2:
     158            # Test label of the form: app.TestCase
     159            test_name = None
     160        else:
     161            # Test label of the form: app.TestCase.test_method
     162            test_name = parts[2]
     163
     164    # Then, generate the SortedDict of tests.
     165    test_dict = SortedDict()
     166    for module in (app_module, test_module):
     167        if hasattr(module, 'suite'):
     168            # If the module defines a suite() method then use it instead.
     169            test_dict.update({
     170                '%s.%s' % (app_label, module.__name__): module.suite()
     171            })
     172            # TODO: introspect the custom suite to unfold the whole test names.
     173        else:
     174            # Get the module's test cases
     175            test_cases = get_test_cases(module, label, app_label, TestClass, test_name)
     176            test_dict.update(test_cases)
     177            # Get the module's doctests
     178            doctests = get_doctests(module, label)
     179            test_dict.update(doctests)
     180    return test_dict
    123181
    124     #
    125     # If there isn't a TestCase, look for a doctest that matches
    126     #
    127     tests = []
    128     for module in app_module, test_module:
    129         try:
    130             doctests = doctest.DocTestSuite(module,
    131                                             checker=doctestOutputChecker,
    132                                             runner=DocTestRunner)
    133             # Now iterate over the suite, looking for doctests whose name
    134             # matches the pattern that was given
    135             for test in doctests:
    136                 if test._dt_test.name in (
    137                         '%s.%s' % (module.__name__, '.'.join(parts[1:])),
    138                         '%s.__test__.%s' % (module.__name__, '.'.join(parts[1:]))):
    139                     tests.append(test)
    140         except ValueError:
    141             # No doctests found.
    142             pass
    143 
    144     # If no tests were found, then we were given a bad test label.
    145     if not tests:
    146         raise ValueError("Test label '%s' does not refer to a test" % label)
    147 
    148     # Construct a suite out of the tests that matched.
    149     return unittest.TestSuite(tests)
    150182
    151183def partition_suite(suite, classes, bins):
    152184    """
    class DjangoTestSuiteRunner(object):  
    234266    def build_suite(self, test_labels, extra_tests=None, **kwargs):
    235267        suite = unittest.TestSuite()
    236268
    237         if test_labels:
    238             for label in test_labels:
    239                 if '.' in label:
    240                     suite.addTest(build_test(label))
    241                 else:
    242                     app = get_app(label)
    243                     suite.addTest(build_suite(app))
    244         else:
    245             for app in get_apps():
    246                 suite.addTest(build_suite(app))
     269        if not test_labels:
     270            test_labels = []
     271            for app_label in settings.INSTALLED_APPS:
     272                test_labels.append(app_label.split('.')[-1])
     273
     274        test_dict = SortedDict()
     275        for label in test_labels:
     276            test_dict.update(get_test_dict(label))
     277
     278        excluded_dict = SortedDict()
     279        for label in kwargs.get('exclude_labels'):
     280            excluded_dict.update(get_test_dict(label))
     281
     282        for label, test in excluded_dict.iteritems():
     283            try:
     284                # Remove from the dictionary of executable tests
     285                del test_dict[label]
     286            except KeyError:
     287                # It's OK if we're trying to exclude a test label that isn't
     288                # part of the original suite. Simply ignore.
     289                pass
     290            if self.verbosity >= 1:
     291                print 'Excluding test: %s' % label
     292
     293        for label, test in test_dict.iteritems():
     294            suite.addTest(test)
    247295
    248296        if extra_tests:
    249297            for test in extra_tests:
    class DjangoTestSuiteRunner(object):  
    347395        A list of 'extra' tests may also be provided; these tests
    348396        will be added to the test suite.
    349397
     398        It's also possible to specify a list of labels to exclude from the
     399        test suite by using the exclude_labels parameter.
     400
    350401        Returns the number of tests that failed.
    351402        """
     403        exclude_labels = kwargs.get('exclude_labels')
    352404        self.setup_test_environment()
    353         suite = self.build_suite(test_labels, extra_tests)
     405        suite = self.build_suite(test_labels, extra_tests, exclude_labels=exclude_labels)
    354406        old_config = self.setup_databases()
    355407        result = self.run_suite(suite)
    356408        self.teardown_databases(old_config)
  • tests/runtests.py

    diff --git a/tests/runtests.py b/tests/runtests.py
    index ba66d2a..264b1d7 100755
    a b class InvalidModelTestCase(unittest.TestCase):  
    7979
    8080        try:
    8181            module = load_app(self.module_label)
    82         except Exception, e:
     82        except Exception:
    8383            self.fail('Unable to load invalid model module')
    8484
    8585        # Make sure sys.stdout is not a tty so that we get errors without
    class InvalidModelTestCase(unittest.TestCase):  
    8888        orig_stdout = sys.stdout
    8989        s = StringIO()
    9090        sys.stdout = s
    91         count = get_validation_errors(s, module)
     91        get_validation_errors(s, module)
    9292        sys.stdout = orig_stdout
    9393        s.seek(0)
    9494        error_log = s.read()
    class InvalidModelTestCase(unittest.TestCase):  
    101101        self.assertTrue(not unexpected, "Unexpected Errors: " + '\n'.join(unexpected))
    102102        self.assertTrue(not missing, "Missing Errors: " + '\n'.join(missing))
    103103
    104 def setup(verbosity, test_labels):
     104def setup(verbosity, test_labels, exclude_labels):
    105105    from django.conf import settings
    106106    state = {
    107107        'INSTALLED_APPS': settings.INSTALLED_APPS,
    def setup(verbosity, test_labels):  
    136136    # in our tests.
    137137    settings.MANAGERS = ("admin@djangoproject.com",)
    138138
     139    if exclude_labels is None:
     140        exclude_labels = []
     141
    139142    # Load all the ALWAYS_INSTALLED_APPS.
    140143    # (This import statement is intentionally delayed until after we
    141144    # access settings because of the USE_I18N dependency.)
    142145    from django.db.models.loading import get_apps, load_app
    143146    get_apps()
    144147
     148    # Only avoid loading an app if it is fully excluded, if testscases or test
     149    # methods names were excluded then load its apps normally.
     150    exclude_apps = [label for label in exclude_labels if '.' not in label]
     151
    145152    # Load all the test model apps.
    146     test_labels_set = set([label.split('.')[0] for label in test_labels])
    147153    test_modules = get_test_modules()
     154    test_labels_set = set([label.split('.')[0] for label in test_labels if label not in exclude_apps])
    148155
    149156    # If GeoDjango, then we'll want to add in the test applications
    150157    # that are a part of its test suite.
    def setup(verbosity, test_labels):  
    154161
    155162    for module_dir, module_name in test_modules:
    156163        module_label = '.'.join([module_dir, module_name])
    157         # if the module was named on the command line, or
    158         # no modules were named (i.e., run all), import
    159         # this module and add it to the list to test.
     164        # if the model was named on the command line, or no models were named
     165        # (i.e., run all), import this model and add it to the list to test.
     166        # Also, skip importing the test apps explicitly excluded by the user.
    160167        if not test_labels or module_name in test_labels_set:
     168            if module_name in exclude_apps:
     169                if verbosity >= 2:
     170                    print "Skipping import of app %s" % module_name
     171                    continue
    161172            if verbosity >= 2:
    162173                print "Importing application %s" % module_name
    163174            mod = load_app(module_label)
    def teardown(state):  
    175186    for key, value in state.items():
    176187        setattr(settings, key, value)
    177188
    178 def django_tests(verbosity, interactive, failfast, test_labels):
     189def django_tests(verbosity, interactive, failfast, test_labels, exclude_labels=None):
    179190    from django.conf import settings
    180     state = setup(verbosity, test_labels)
     191    state = setup(verbosity, test_labels, exclude_labels)
    181192
    182193    # Add tests for invalid models apps.
    183194    extra_tests = []
    def django_tests(verbosity, interactive, failfast, test_labels):  
    198209        from django.contrib.gis.tests import geodjango_suite
    199210        extra_tests.append(geodjango_suite(apps=False))
    200211
    201     # Run the test suite, including the extra validation tests.
     212    # Run the test suite, including the extra validation tests and skipping
     213    # the test explicitely excluded.
    202214    from django.test.utils import get_runner
    203215    if not hasattr(settings, 'TEST_RUNNER'):
    204216        settings.TEST_RUNNER = 'django.test.simple.DjangoTestSuiteRunner'
    205217    TestRunner = get_runner(settings)
    206218
    207219    test_runner = TestRunner(verbosity=verbosity, interactive=interactive, failfast=failfast)
    208     failures = test_runner.run_tests(test_labels, extra_tests=extra_tests)
     220    failures = test_runner.run_tests(test_labels, extra_tests=extra_tests, exclude_labels=exclude_labels)
    209221
    210222    teardown(state)
    211223    return failures
    if __name__ == "__main__":  
    324336        help="Bisect the test suite to discover a test that causes a test failure when combined with the named test.")
    325337    parser.add_option('--pair', action='store', dest='pair', default=None,
    326338        help="Run the test suite in pairs with the named test to find problem pairs.")
     339    parser.add_option('-e', '--exclude', action='append', dest='exclude', default=[],
     340        help='Test to exclude (use multiple --exclude to exclude multiple tests).')
    327341    options, args = parser.parse_args()
    328342    if options.settings:
    329343        os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
    if __name__ == "__main__":  
    338352    elif options.pair:
    339353        paired_tests(options.pair, options, args)
    340354    else:
    341         failures = django_tests(int(options.verbosity), options.interactive, options.failfast, args)
     355        failures = django_tests(
     356                int(options.verbosity),
     357                options.interactive,
     358                options.failfast,
     359                args,
     360                options.exclude
     361            )
    342362        if failures:
    343363            sys.exit(bool(failures))
Back to Top