Ticket #5787: toptobottom-take-2.patch

File toptobottom-take-2.patch, 9.9 KB (added by Julian Berman, 9 years ago)
  • doc/core/man/trial.1

    diff --git a/doc/core/man/trial.1 b/doc/core/man/trial.1
    index 009c25d..8a2ebd6 100644
    a b the default, except it makes tests run about ten times slower. 
    116116\fB-h\fR, \fB--help\fR
    117117Print a usage message to standard output, then exit.
    118118.TP
     119\fB--help-order\fR
     120Print a list of possible orders that TestCase test methods can be run in, then
     121exit. The orders can be used with the --order option described below.
     122.TP
    119123\fB--help-reporters\fR
    120124Print a list of valid reporters to standard output, then exit. Reporters can
    121125be selected with the --reporter option described below.
    every subpackage. Unless, that is, you specify this option. 
    140144Don't automatically jump into debugger for post-mortem analysis of
    141145exceptions.  Only usable in conjunction with --debug.
    142146.TP
     147\fB--order\fR \fIorder\fR
     148Specify what order to run the individual test methods within the given
     149TestCases. By default, they are run alphabetically. See --help-order for a list
     150of other valid values.
     151.TP
    143152\fB--profile\fR
    144153Run tests under the Python profiler.
    145154.TP
  • twisted/scripts/trial.py

    diff --git a/twisted/scripts/trial.py b/twisted/scripts/trial.py
    index f317841..a5eea75 100644
    a b  
    44# See LICENSE for details.
    55
    66
    7 import sys, os, random, gc, pdb, time, warnings
     7from __future__ import print_function
     8import sys, os, random, gc, pdb, time, warnings, inspect
    89
    910from twisted.internet import defer
    1011from twisted.application import app
    def _reporterAction(): 
    9596
    9697
    9798
     99# orders which can be passed to trial --order
     100_run_orders = [
     101    ("alphabetical",
     102     "alphabetical order for test methods, arbitrary order for test cases"),
     103    ("toptobottom",
     104     "attempt to run test cases and methods in the order they were defined"),
     105]
     106
     107
     108
    98109class _BasicOptions(object):
    99110    """
    100111    Basic options shared between trial and its local workers.
    class _BasicOptions(object): 
    107118
    108119    optFlags = [["help", "h"],
    109120                ["no-recurse", "N", "Don't recurse into packages"],
     121                ['help-orders', None, "Help on available test running orders"],
    110122                ['help-reporters', None,
    111123                 "Help on available output plugins (reporters)"],
    112124                ["rterrors", "e", "realtime errors, print out tracebacks as "
    class _BasicOptions(object): 
    118130                ]
    119131
    120132    optParameters = [
     133        ["order", "o", None, "Specify what order to run test cases and methods"
     134         ". See --help-orders for more info."],
    121135        ["random", "z", None,
    122136         "Run tests in random order using the specified seed"],
    123137        ['temp-directory', None, '_trial_temp',
    class _BasicOptions(object): 
    127141         'more info.']]
    128142
    129143    compData = usage.Completions(
    130         optActions={"reporter": _reporterAction,
     144        optActions={"order": usage.CompleteList(
     145                        name for name, _ in _run_orders),
     146                    "reporter": _reporterAction,
    131147                    "logfile": usage.CompleteFiles(descr="log file name"),
    132148                    "random": usage.Completer(descr="random seed")},
    133149        extraActions=[usage.CompleteFiles(
    class _BasicOptions(object): 
    150166        """
    151167        coverdir = 'coverage'
    152168        result = FilePath(self['temp-directory']).child(coverdir)
    153         print "Setting coverage directory to %s." % (result.path,)
     169        print("Setting coverage directory to %s." % (result.path,))
    154170        return result
    155171
    156172
    class _BasicOptions(object): 
    197213        sys.settrace(spewer)
    198214
    199215
     216    def opt_help_orders(self):
     217        synopsis = ("Trial can attempt to run test cases and their methods in "
     218                    "a few different\n orders. You can select any of the "
     219                    "following options using --order=<foo>.\n")
     220
     221        print(synopsis)
     222        for name, description in _run_orders:
     223            print('   ', name, '\t', description)
     224        sys.exit(0)
     225
     226
    200227    def opt_help_reporters(self):
    201228        synopsis = ("Trial's output can be customized using plugins called "
    202229                    "Reporters. You can\nselect any of the following "
    203230                    "reporters using --reporter=<foo>\n")
    204         print synopsis
     231        print(synopsis)
    205232        for p in plugin.getPlugins(itrial.IReporter):
    206             print '   ', p.longOpt, '\t', p.description
    207         print
     233            print('   ', p.longOpt, '\t', p.description)
    208234        sys.exit(0)
    209235
    210236
    class _BasicOptions(object): 
    229255                "tbformat must be 'plain', 'emacs', or 'cgitb'.")
    230256
    231257
     258    def opt_order(self, order):
     259        """
     260        Run the tests in the given order.
     261
     262        """
     263
     264        if order == "toptobottom":
     265            self['order'] = _maybeFindSourceLine
     266        elif order == "alphabetical":
     267            self['order'] = runner.name
     268        else:
     269            orders = ", ".join(repr(order) for order, _ in _run_orders)
     270            raise usage.UsageError("order must be one of " + orders)
     271
     272
    232273    def opt_recursionlimit(self, arg):
    233274        """
    234275        see sys.setrecursionlimit()
    def _getSuite(config): 
    404445
    405446
    406447
     448def _maybeFindSourceLine(thing):
     449    """
     450    Try to find the source line of the given test thing.
     451
     452    """
     453
     454    method = getattr(thing, "_testMethodName", None)
     455    if method is not None:
     456        thing = getattr(thing, method)
     457
     458    try:
     459        return inspect.getsourcelines(thing)[1]
     460    except (IOError, TypeError):
     461        # either thing is a module, which raised a TypeError, or the file
     462        # couldn't be read
     463        return -1
     464
     465
     466
    407467def _getLoader(config):
    408468    loader = runner.TestLoader()
    409469    if config['random']:
    410470        randomer = random.Random()
    411471        randomer.seed(config['random'])
    412472        loader.sorter = lambda x : randomer.random()
    413         print 'Running tests shuffled with seed %d\n' % config['random']
     473        print('Running tests shuffled with seed %d\n' % config['random'])
     474    elif config['order']:
     475        loader.sorter = config['order']
    414476    if not config['until-failure']:
    415477        loader.suiteFactory = runner.DestructiveTestSuite
    416478    return loader
    def _wrappedPdb(): 
    426488    try:
    427489        import readline
    428490    except ImportError:
    429         print "readline module not available"
     491        print("readline module not available")
    430492        sys.exc_clear()
    431493    for path in ('.pdbrc', 'pdbrc'):
    432494        if os.path.exists(path):
  • new file twisted/topfiles/5787.feature

    diff --git a/twisted/topfiles/5787.feature b/twisted/topfiles/5787.feature
    new file mode 100644
    index 0000000..7a7d97b
    - +  
     1trial now accepts a --order option that specifies what order to run TestCase methods in.
  • new file twisted/trial/test/ordertests.py

    diff --git a/twisted/trial/test/ordertests.py b/twisted/trial/test/ordertests.py
    new file mode 100644
    index 0000000..09c6e0d
    - +  
     1from twisted.trial import unittest
     2
     3
     4class FooTest(unittest.TestCase):
     5    """
     6    Used to make assertions about the order its tests will be run in.
     7    """
     8
     9    def test_first(self):
     10        pass
     11
     12
     13    def test_second(self):
     14        pass
     15
     16
     17    def test_third(self):
     18        pass
     19
     20
     21    def test_fourth(self):
     22        pass
     23
     24
     25
     26class BazTest(unittest.TestCase):
     27    def test_baz(self):
     28        pass
     29
     30
     31
     32class BarTest(unittest.TestCase):
     33    def test_bar(self):
     34        pass
  • twisted/trial/test/test_script.py

    diff --git a/twisted/trial/test/test_script.py b/twisted/trial/test/test_script.py
    index d92d9f8..5659a3b 100644
    a b class TestRun(unittest.TestCase): 
    594594            self.assertIn("foo", str(e))
    595595        else:
    596596            self.fail("Should have exited due to non-existent debugger!")
     597
     598
     599
     600class OrderTests(unittest.TestCase):
     601    """
     602    Tests for the --order option.
     603    """
     604
     605
     606    def setUp(self):
     607        self.config = trial.Options()
     608
     609
     610    def tearDown(self):
     611        self.config = None
     612
     613
     614    def test_alphabetical(self):
     615        """
     616        --alphabetical runs test methods alphabetically.
     617        """
     618
     619        self.config.parseOptions([
     620            "--order", "alphabetical",
     621            "twisted.trial.test.ordertests.FooTest"])
     622
     623        loader = trial._getLoader(self.config)
     624        suite = loader.loadByNames(self.config['tests'])
     625        testCase, = suite._tests
     626
     627        self.assertEqual(
     628            ['test_first', 'test_fourth', 'test_second', 'test_third'],
     629            [test._testMethodName for test in testCase._tests])
     630
     631
     632    def test_toptobottom(self):
     633        """
     634        --toptobottom runs test methods in the order defined in their source.
     635        """
     636
     637        self.config.parseOptions([
     638            "--order", "toptobottom",
     639            "twisted.trial.test.ordertests.FooTest"])
     640
     641        loader = trial._getLoader(self.config)
     642        suite = loader.loadByNames(self.config['tests'])
     643        testCase, = suite._tests
     644
     645        self.assertEqual(
     646            ['test_first', 'test_second', 'test_third', 'test_fourth'],
     647            [test._testMethodName for test in testCase._tests])
     648
     649
     650    def test_toptobottom_module(self):
     651        """
     652        --toptobottom sorts test cases in a module from top to bottom.
     653        """
     654
     655        self.config.parseOptions([
     656            "--order", "toptobottom", "twisted.trial.test.ordertests"])
     657        loader = trial._getLoader(self.config)
     658        suite = loader.loadByNames(self.config['tests'])
     659
     660        self.assertEqual(
     661            testNames(suite), [
     662            'twisted.trial.test.ordertests.FooTest.test_first',
     663            'twisted.trial.test.ordertests.FooTest.test_second',
     664            'twisted.trial.test.ordertests.FooTest.test_third',
     665            'twisted.trial.test.ordertests.FooTest.test_fourth',
     666            'twisted.trial.test.ordertests.BazTest.test_baz',
     667            'twisted.trial.test.ordertests.BarTest.test_bar'])