Ticket #1518: exitfirst.patch

File exitfirst.patch, 11.0 KB (added by Julian, 18 months ago)
  • doc/core/man/trial.1

    diff --git a/doc/core/man/trial.1 b/doc/core/man/trial.1
    index 1bbdd58..086b401 100644
    a b Set Python's recursion limit. See sys.setrecursionlimit(). 
    154154Select the reporter to use for trial's output.  Use the --help-reporters 
    155155option to see a list of valid reporters. 
    156156.TP 
     157\fB-x\fR, \fB--exitfirst\fR 
     158Stop the test run after the first test which does not succeed. This includes 
     159failures, errors, or unexpected successes. 
     160.TP 
    157161\fB--spew\fR 
    158162Print an insanely verbose log of everything that happens. Useful when 
    159163debugging freezes or locks in complex code. 
  • twisted/scripts/trial.py

    diff --git a/twisted/scripts/trial.py b/twisted/scripts/trial.py
    index 46859d5..9a3c31c 100644
    a b class _BasicOptions(object): 
    114114                 "Turn dirty reactor errors into warnings"], 
    115115                ["force-gc", None, "Have Trial run gc.collect() before and " 
    116116                 "after each test case."], 
     117                ["exitfirst", "x", 
     118                 "Exit after the first non-successful result."], 
    117119                ] 
    118120 
    119121    optParameters = [ 
    class Options(_BasicOptions, usage.Options, app.ReactorSelectionMixin): 
    375377    def postOptions(self): 
    376378        _BasicOptions.postOptions(self) 
    377379        if self['jobs']: 
    378             for option in ['debug', 'profile', 'debug-stacktraces']: 
     380            conflicts = ['debug', 'profile', 'debug-stacktraces', 'exitfirst'] 
     381            for option in conflicts: 
    379382                if self[option]: 
    380383                    raise usage.UsageError( 
    381384                        "You can't specify --%s when using --jobs" % option) 
    def _makeRunner(config): 
    485488            else: 
    486489                args['debugger'] = _wrappedPdb() 
    487490 
     491        args['exitFirst'] = config['exitfirst'] 
    488492        args['profile'] = config['profile'] 
    489493        args['forceGarbageCollection'] = config['force-gc'] 
    490494 
  • new file twisted/topfiles/1518.feature

    diff --git a/twisted/topfiles/1518.feature b/twisted/topfiles/1518.feature
    new file mode 100644
    index 0000000..0628b66
    - +  
     1trial now has --stop and --stop-failed flags which stop the test run after the first non-success or first failure respectively. 
  • twisted/trial/reporter.py

    diff --git a/twisted/trial/reporter.py b/twisted/trial/reporter.py
    index fe5a832..c57cb86 100644
    a b class UncleanWarningsReporterWrapper(TestResultDecorator): 
    234234 
    235235 
    236236 
     237@implementer(itrial.IReporter) 
     238class _ExitWrapper(TestResultDecorator): 
     239    """ 
     240    A wrapper for a reporter that causes the reporter to stop after 
     241    unsuccessful tests. 
     242    """ 
     243 
     244    def addError(self, *args, **kwargs): 
     245        """ 
     246        See L{itrial.IReporter}. 
     247        """ 
     248        self.shouldStop = True 
     249        return self._originalReporter.addError(*args, **kwargs) 
     250 
     251 
     252    def addFailure(self, *args, **kwargs): 
     253        """ 
     254        See L{itrial.IReporter}. 
     255        """ 
     256        self.shouldStop = True 
     257        return self._originalReporter.addFailure(*args, **kwargs) 
     258 
     259 
     260    def addUnexpectedSuccess(self, *args, **kwargs): 
     261        """ 
     262        See L{itrial.IReporter}. 
     263        """ 
     264        self.shouldStop = True 
     265        return self._originalReporter.addUnexpectedSuccess(*args, **kwargs) 
     266 
     267 
     268 
    237269class _AdaptedReporter(TestResultDecorator): 
    238270    """ 
    239271    TestResult decorator that makes sure that addError only gets tests that 
  • twisted/trial/runner.py

    diff --git a/twisted/trial/runner.py b/twisted/trial/runner.py
    index be06d5a..37c5f2b 100644
    a b from twisted.python.versions import Version 
    2828from twisted.internet import defer 
    2929from twisted.trial import util, unittest 
    3030from twisted.trial.itrial import ITestCase 
    31 from twisted.trial.reporter import UncleanWarningsReporterWrapper 
     31from twisted.trial.reporter import _ExitWrapper, UncleanWarningsReporterWrapper 
    3232 
    3333# These are imported so that they remain in the public API for t.trial.runner 
    3434from twisted.trial.unittest import TestSuite 
    class TrialRunner(object): 
    669669    def _makeResult(self): 
    670670        reporter = self.reporterFactory(self.stream, self.tbformat, 
    671671                                        self.rterrors, self._log) 
     672        if self._exitFirst: 
     673            reporter = _ExitWrapper(reporter) 
    672674        if self.uncleanWarnings: 
    673675            reporter = UncleanWarningsReporterWrapper(reporter) 
    674676        return reporter 
    class TrialRunner(object): 
    683685                 uncleanWarnings=False, 
    684686                 workingDirectory=None, 
    685687                 forceGarbageCollection=False, 
    686                  debugger=None): 
     688                 debugger=None, 
     689                 exitFirst=False): 
    687690        self.reporterFactory = reporterFactory 
    688691        self.logfile = logfile 
    689692        self.mode = mode 
    class TrialRunner(object): 
    697700        self._logFileObject = None 
    698701        self._forceGarbageCollection = forceGarbageCollection 
    699702        self.debugger = debugger 
     703        self._exitFirst = exitFirst 
    700704        if profile: 
    701705            self.run = util.profiled(self.run, 'profile.data') 
    702706 
  • twisted/trial/test/test_reporter.py

    diff --git a/twisted/trial/test/test_reporter.py b/twisted/trial/test/test_reporter.py
    index c9b33e8..cc0fa06 100644
    a b from twisted.internet.utils import suppressWarnings 
    1515from twisted.python import log 
    1616from twisted.python.failure import Failure 
    1717from twisted.trial import itrial, unittest, runner, reporter, util 
    18 from twisted.trial.reporter import UncleanWarningsReporterWrapper 
     18from twisted.trial.reporter import _ExitWrapper, UncleanWarningsReporterWrapper 
    1919from twisted.trial.test import erroneous 
    2020from twisted.trial.unittest import makeTodo, SkipTest, Todo 
    2121from twisted.trial.test import sample 
    class AnsiColorizerTests(unittest.SynchronousTestCase): 
    16741674        sys.modules['curses'] = fakecurses() 
    16751675        self.assertFalse(reporter._AnsiColorizer.supported(FakeStream())) 
    16761676        self.assertEqual(sys.modules['curses'].setUp, 1) 
     1677 
     1678 
     1679 
     1680class ExitWrapperTests(unittest.SynchronousTestCase): 
     1681    """ 
     1682    Tests for L{reporter._ExitWrapper}. 
     1683    """ 
     1684 
     1685    def setUp(self): 
     1686        self.failure = Failure(Exception("I am a Failure")) 
     1687        self.test = sample.FooTest('test_foo') 
     1688        self.result = reporter.TestResult() 
     1689        self.wrapped = _ExitWrapper(self.result) 
     1690        self.assertFalse(self.wrapped.shouldStop) 
     1691 
     1692 
     1693    def test_stopOnFailure(self): 
     1694        """ 
     1695        L{reporter._ExitWrapper} causes a wrapped reporter to stop after its 
     1696        first failure. 
     1697        """ 
     1698 
     1699        self.wrapped.addFailure(self.test, self.failure) 
     1700        self.assertTrue(self.wrapped.shouldStop) 
     1701        self.assertEqual(self.result.failures, [(self.test, self.failure)]) 
     1702 
     1703 
     1704    def test_stopOnError(self): 
     1705        """ 
     1706        L{reporter._ExitWrapper} causes a wrapped reporter to stop after its 
     1707        first error. 
     1708        """ 
     1709 
     1710        self.wrapped.addError(self.test, self.failure) 
     1711        self.assertTrue(self.wrapped.shouldStop) 
     1712        self.assertEqual(self.result.errors, [(self.test, self.failure)]) 
     1713 
     1714 
     1715    def test_stopOnUnexpectedSuccess(self): 
     1716        """ 
     1717        L{reporter._StopWrapper} causes a wrapped reporter to stop after an 
     1718        unexpected success if C{onlyAfterFailure} is C{False}. 
     1719        """ 
     1720 
     1721        self.wrapped.addUnexpectedSuccess(self.test, self.failure) 
     1722        self.assertTrue(self.wrapped.shouldStop) 
     1723        self.assertEqual( 
     1724            self.result.unexpectedSuccesses, [(self.test, self.failure)]) 
  • twisted/trial/test/test_runner.py

    diff --git a/twisted/trial/test/test_runner.py b/twisted/trial/test/test_runner.py
    index f0a2fef..985e7b1 100644
    a b class TestRunner(unittest.SynchronousTestCase): 
    562562        self.assertEqual(['runcall'], my_runner.debugger._calls) 
    563563 
    564564 
     565    def test_exitfirst(self): 
     566        """ 
     567        If trial was passed the C{--exitfirst} option, the constructed test 
     568        result object is wrapped with L{reporter._ExitWrapper}. 
     569        """ 
     570 
     571        self.parseOptions(["--exitfirst"]) 
     572        runner = self.getRunner() 
     573        result = runner._makeResult() 
     574        self.assertIsInstance(result, reporter._ExitWrapper) 
     575 
     576 
    565577 
    566578class TestTrialSuite(unittest.SynchronousTestCase): 
    567579 
  • twisted/trial/test/test_script.py

    diff --git a/twisted/trial/test/test_script.py b/twisted/trial/test/test_script.py
    index 45a254b..60bdb9e 100644
    a b class OptionsTestCase(unittest.TestCase): 
    518518            str(error)) 
    519519 
    520520 
     521    def test_jobsConflictWithExitFirst(self): 
     522        """ 
     523        C{parseOptions} raises a C{UsageError} when C{--exitfirst} is passed 
     524        along C{--jobs} as it's not supported yet. 
     525 
     526        @see: U{http://twistedmatrix.com/trac/ticket/6436} 
     527        """ 
     528        error = self.assertRaises( 
     529            UsageError, self.options.parseOptions, 
     530            ["--jobs", "4", "--exitfirst"]) 
     531        self.assertEqual( 
     532            "You can't specify --exitfirst when using --jobs", 
     533            str(error)) 
     534 
     535 
    521536 
    522537class MakeRunnerTestCase(unittest.TestCase): 
    523538    """ 
    524539    Tests for the L{_makeRunner} helper. 
    525540    """ 
    526541 
     542    def setUp(self): 
     543        self.options = trial.Options() 
     544 
    527545    def test_jobs(self): 
    528546        """ 
    529547        L{_makeRunner} returns a L{DistTrialRunner} instance when the C{--jobs} 
    530548        option is passed, and passes the C{workerNumber} and C{workerArguments} 
    531549        parameters to it. 
    532550        """ 
    533         options = trial.Options() 
    534         options.parseOptions(["--jobs", "4", "--force-gc"]) 
    535         runner = trial._makeRunner(options) 
     551        self.options.parseOptions(["--jobs", "4", "--force-gc"]) 
     552        runner = trial._makeRunner(self.options) 
    536553        self.assertIsInstance(runner, DistTrialRunner) 
    537554        self.assertEqual(4, runner._workerNumber) 
    538555        self.assertEqual(["--force-gc"], runner._workerArguments) 
    class MakeRunnerTestCase(unittest.TestCase): 
    543560        L{_makeRunner} returns a L{TrialRunner} instance in C{DRY_RUN} mode 
    544561        when the C{--dry-run} option is passed, even if C{--jobs} is set. 
    545562        """ 
    546         options = trial.Options() 
    547         options.parseOptions(["--jobs", "4", "--dry-run"]) 
    548         runner = trial._makeRunner(options) 
     563        self.options.parseOptions(["--jobs", "4", "--dry-run"]) 
     564        runner = trial._makeRunner(self.options) 
    549565        self.assertIsInstance(runner, TrialRunner) 
    550566        self.assertEqual(TrialRunner.DRY_RUN, runner.mode) 
    551567 
    class MakeRunnerTestCase(unittest.TestCase): 
    566582        self.assertRaises(trial._DebuggerNotFound, trial._makeRunner, options) 
    567583 
    568584 
     585    def test_exitfirst(self): 
     586        """ 
     587        Passing C{--exitfirst} wraps the reporter with a 
     588        L{reporter._ExitWrapper} that stops on any non-success. 
     589        """ 
     590        self.options.parseOptions(["--exitfirst"]) 
     591        runner = trial._makeRunner(self.options) 
     592        self.assertTrue(runner._exitFirst) 
     593 
     594 
    569595class TestRun(unittest.TestCase): 
    570596    """ 
    571597    Tests for the L{run} function.