diff options
| author | robot-piglet <[email protected]> | 2025-06-22 18:50:56 +0300 |
|---|---|---|
| committer | robot-piglet <[email protected]> | 2025-06-22 19:04:42 +0300 |
| commit | c7cbc6d480c5488ff6e921c709680fd2c1340a10 (patch) | |
| tree | 10843f44b67c0fb5717ad555556064095f701d8c /contrib/python/Twisted/py3/twisted/trial | |
| parent | 26d391cdb94d2ce5efc8d0cc5cea7607dc363c0b (diff) | |
Intermediate changes
commit_hash:28750b74281710ec1ab5bdc2403c8ab24bdd164b
Diffstat (limited to 'contrib/python/Twisted/py3/twisted/trial')
4 files changed, 140 insertions, 104 deletions
diff --git a/contrib/python/Twisted/py3/twisted/trial/_asynctest.py b/contrib/python/Twisted/py3/twisted/trial/_asynctest.py index 048e8dc0378..6765c703f66 100644 --- a/contrib/python/Twisted/py3/twisted/trial/_asynctest.py +++ b/contrib/python/Twisted/py3/twisted/trial/_asynctest.py @@ -4,10 +4,9 @@ """ Things likely to be used by writers of unit tests. - -Maintainer: Jonathan Lange """ +from __future__ import annotations import inspect import warnings @@ -21,6 +20,7 @@ from typing_extensions import ParamSpec # installs a user-specified reactor, installing the default reactor and # breaking reactor installation. See also #6047. from twisted.internet import defer, utils +from twisted.internet.defer import _T from twisted.python import failure from twisted.trial import itrial, util from twisted.trial._synctest import FailTest, SkipTest, SynchronousTestCase @@ -58,7 +58,21 @@ class TestCase(SynchronousTestCase): """ super().__init__(methodName) - def assertFailure(self, deferred, *expectedFailures): + # Acquire and store real reactor so that it does not interfere with any + # patching to the reactor done by the user. + from twisted.internet import reactor + + # A unique name must be used in order not to clash with user code that + # sets their own attributes. + self._twistedPrivateScheduler = reactor + + # Define whether tearDown needs to run. + # It is run only if setUp succeeded + self._twistedPrivateNeedsTearDown = False + + def assertFailure( + self, deferred: defer.Deferred[_T], *expectedFailures: type[BaseException] + ) -> defer.Deferred[_T]: """ Fail if C{deferred} does not errback with one of C{expectedFailures}. Returns the original Deferred with callbacks added. You will need @@ -83,14 +97,12 @@ class TestCase(SynchronousTestCase): failUnlessFailure = assertFailure - def _run(self, methodName, result): - from twisted.internet import reactor - + def _run(self, func, funcDescription, result): timeout = self.getTimeout() def onTimeout(d): e = defer.TimeoutError( - f"{self!r} ({methodName}) still running at {timeout} secs" + f"{self!r} ({funcDescription}) still running at {timeout} secs" ) f = failure.Failure(e) # try to errback the deferred that the test returns (for no gorram @@ -102,7 +114,7 @@ class TestCase(SynchronousTestCase): # if the deferred has been called already but the *back chain # is still unfinished, crash the reactor and report timeout # error ourself. - reactor.crash() + self._twistedPrivateScheduler.crash() self._timedOut = True # see self._wait todo = self.getTodo() if todo is not None and todo.expected(f): @@ -113,61 +125,61 @@ class TestCase(SynchronousTestCase): onTimeout = utils.suppressWarnings( onTimeout, util.suppress(category=DeprecationWarning) ) - method = getattr(self, methodName) - if inspect.isgeneratorfunction(method): + if inspect.isgeneratorfunction(func): exc = TypeError( - "{!r} is a generator function and therefore will never run".format( - method - ) + "{!r} is a generator function and therefore will never run".format(func) ) return defer.fail(exc) d = defer.maybeDeferred( - utils.runWithWarningsSuppressed, self._getSuppress(), method + utils.runWithWarningsSuppressed, self._getSuppress(), func ) - call = reactor.callLater(timeout, onTimeout, d) + call = self._twistedPrivateScheduler.callLater(timeout, onTimeout, d) d.addBoth(lambda x: call.active() and call.cancel() or x) return d def __call__(self, *args, **kwargs): return self.run(*args, **kwargs) - def deferSetUp(self, ignored, result): - d = self._run("setUp", result) - d.addCallbacks( - self.deferTestMethod, - self._ebDeferSetUp, - callbackArgs=(result,), - errbackArgs=(result,), - ) - return d + async def _deferSetUp(self, result): + try: + try: + await self._deferSetUpAndRun(result) + finally: + await self._deferRunCleanups(result) + finally: + if self._twistedPrivateNeedsTearDown: + await self._deferTearDown(result) - def _ebDeferSetUp(self, failure, result): - if failure.check(SkipTest): - result.addSkip(self, self._getSkipReason(self.setUp, failure.value)) - else: - result.addError(self, failure) - if failure.check(KeyboardInterrupt): - result.stop() - return self.deferRunCleanups(None, result) - - def deferTestMethod(self, ignored, result): - d = self._run(self._testMethodName, result) - d.addCallbacks( - self._cbDeferTestMethod, - self._ebDeferTestMethod, - callbackArgs=(result,), - errbackArgs=(result,), - ) - d.addBoth(self.deferRunCleanups, result) - d.addBoth(self.deferTearDown, result) - return d + async def _deferSetUpAndRun(self, result): + """ + Execute the setUp and run part of a test. Teardown and cleanups are not executed. + """ + try: + await self._run(self.setUp, "setUp", result) + except SkipTest as e: + result.addSkip(self, self._getSkipReason(self.setUp, e)) + return + except KeyboardInterrupt as e: + result.addError(self, failure.Failure(e)) + result.stop() + return + except BaseException as e: + result.addError(self, failure.Failure(e)) + return - def _cbDeferTestMethod(self, ignored, result): - if self.getTodo() is not None: - result.addUnexpectedSuccess(self, self.getTodo()) - else: - self._passed = True - return ignored + self._twistedPrivateNeedsTearDown = True + + try: + await self._run( + getattr(self, self._testMethodName), self._testMethodName, result + ) + if self.getTodo() is not None: + result.addUnexpectedSuccess(self, self.getTodo()) + else: + self._passed = True + except BaseException as e: + self._ebDeferTestMethod(failure.Failure(e), result) + raise def _ebDeferTestMethod(self, f, result): todo = self.getTodo() @@ -185,19 +197,19 @@ class TestCase(SynchronousTestCase): else: result.addError(self, f) - def deferTearDown(self, ignored, result): - d = self._run("tearDown", result) - d.addErrback(self._ebDeferTearDown, result) - return d - - def _ebDeferTearDown(self, failure, result): - result.addError(self, failure) - if failure.check(KeyboardInterrupt): + async def _deferTearDown(self, result): + try: + await self._run(self.tearDown, "tearDown", result) + except KeyboardInterrupt as e: + result.addError(self, failure.Failure(e)) result.stop() - self._passed = False + self._passed = False + except BaseException as e: + result.addError(self, failure.Failure(e)) + self._passed = False @defer.inlineCallbacks - def deferRunCleanups(self, ignored, result): + def _deferRunCleanups(self, result): """ Run any scheduled cleanups and report errors (if any) to the result. object. @@ -206,7 +218,11 @@ class TestCase(SynchronousTestCase): while len(self._cleanups) > 0: func, args, kwargs = self._cleanups.pop() try: - yield func(*args, **kwargs) + yield self._run( + lambda: func(*args, **kwargs), + f"cleanup function {func.__name__}", + result, + ) except Exception: failures.append(failure.Failure()) @@ -290,7 +306,7 @@ class TestCase(SynchronousTestCase): self._deprecateReactor(reactor) self._timedOut = False try: - d = self.deferSetUp(None, result) + d = defer.Deferred.fromCoroutine(self._deferSetUp(result)) try: self._wait(d) finally: @@ -310,6 +326,9 @@ class TestCase(SynchronousTestCase): If the function C{f} returns a Deferred, C{TestCase} will wait until the Deferred has fired before proceeding to the next function. + + If the function takes more than C{timeout} settings, then the test will + raise an error. """ return super().addCleanup(f, *args, **kwargs) diff --git a/contrib/python/Twisted/py3/twisted/trial/_dist/functional.py b/contrib/python/Twisted/py3/twisted/trial/_dist/functional.py index 8bb5ecf7a4f..f0258bf44c2 100644 --- a/contrib/python/Twisted/py3/twisted/trial/_dist/functional.py +++ b/contrib/python/Twisted/py3/twisted/trial/_dist/functional.py @@ -30,7 +30,7 @@ def fromOptional(default: _A, optional: Optional[_A]) -> _A: def takeWhile(condition: Callable[[_A], bool], xs: Iterable[_A]) -> Iterable[_A]: """ - :return: An iterable over C{xs} that stops when C{condition} returns + @return: An iterable over C{xs} that stops when C{condition} returns ``False`` based on the value of iterated C{xs}. """ for x in xs: diff --git a/contrib/python/Twisted/py3/twisted/trial/_dist/workerreporter.py b/contrib/python/Twisted/py3/twisted/trial/_dist/workerreporter.py index 5f2d7a0cab1..f266d727e36 100644 --- a/contrib/python/Twisted/py3/twisted/trial/_dist/workerreporter.py +++ b/contrib/python/Twisted/py3/twisted/trial/_dist/workerreporter.py @@ -38,11 +38,11 @@ async def addError( Then, L{managercommands.AddError} is called with the rest of the information and the stream IDs. - :param amp: The connection to use. - :param testName: The name (or ID) of the test the error relates to. - :param errorClass: The fully qualified name of the error type. - :param error: The string representation of the error. - :param frames: The lines of the traceback associated with the error. + @param amp: The connection to use. + @param testName: The name (or ID) of the test the error relates to. + @param errorClass: The fully qualified name of the error type. + @param error: The string representation of the error. + @param frames: The lines of the traceback associated with the error. """ errorStreamId = await stream(amp, chunk(error.encode("utf-8"), MAX_VALUE_LENGTH)) @@ -63,12 +63,12 @@ async def addFailure( """ Like L{addError} but for failures. - :param amp: See L{addError} - :param testName: See L{addError} - :param failClass: The fully qualified name of the exception associated + @param amp: See L{addError} + @param testName: See L{addError} + @param failClass: The fully qualified name of the exception associated with the failure. - :param fail: The string representation of the failure. - :param frames: The lines of the traceback associated with the error. + @param fail: The string representation of the failure. + @param frames: The lines of the traceback associated with the error. """ failStreamId = await stream(amp, chunk(fail.encode("utf-8"), MAX_VALUE_LENGTH)) framesStreamId = await stream(amp, (frame.encode("utf-8") for frame in frames)) @@ -86,10 +86,10 @@ async def addExpectedFailure(amp: AMP, testName: str, error: str, todo: str) -> """ Like L{addError} but for expected failures. - :param amp: See L{addError} - :param testName: See L{addError} - :param error: The string representation of the expected failure. - :param todo: The string description of the expectation. + @param amp: See L{addError} + @param testName: See L{addError} + @param error: The string representation of the expected failure. + @param todo: The string description of the expectation. """ errorStreamId = await stream(amp, chunk(error.encode("utf-8"), MAX_VALUE_LENGTH)) @@ -113,10 +113,10 @@ class ReportingResults: runner believes the test is otherwise complete, it can collect the results and do something with any errors. - :ivar _reporter: The L{WorkerReporter} this object is associated with. + @ivar _reporter: The L{WorkerReporter} this object is associated with. This is the object doing the result reporting. - :ivar _results: A list of L{Deferred} instances representing the results + @ivar _results: A list of L{Deferred} instances representing the results of reporting operations. This is expected to grow over the course of the test run and then be inspected by the runner once the test is over. The public interface to this list is via the context manager @@ -130,7 +130,7 @@ class ReportingResults: """ Begin a new reportable context in which results can be collected. - :return: A sequence which will contain the L{Deferred} instances + @return: A sequence which will contain the L{Deferred} instances representing the results of all test result reporting that happens while the context manager is active. The sequence is extended as the test runs so its value should not be consumed until the test diff --git a/contrib/python/Twisted/py3/twisted/trial/reporter.py b/contrib/python/Twisted/py3/twisted/trial/reporter.py index 4ee67ebcf67..2034a83e261 100644 --- a/contrib/python/Twisted/py3/twisted/trial/reporter.py +++ b/contrib/python/Twisted/py3/twisted/trial/reporter.py @@ -105,6 +105,9 @@ class TestResult(pyunit.TestResult): # The duration of the test. It is None until the test completes. _lastTime: Optional[int] + # Make pytest not think this is test class + __test__ = False + def __init__(self): super().__init__() self.skips = [] @@ -532,29 +535,37 @@ class Reporter(TestResult): When a C{SynchronousTestCase} method fails synchronously, the stack looks like this: - - [0]: C{SynchronousTestCase._run} + - [0]: C{TestCase._run} - [1]: C{util.runWithWarningsSuppressed} - [2:-2]: code in the test method which failed - [-1]: C{_synctest.fail} When a C{TestCase} method fails synchronously, the stack looks like this: - - [0]: C{defer.maybeDeferred} - - [1]: C{utils.runWithWarningsSuppressed} - - [2]: C{utils.runWithWarningsSuppressed} - - [3:-2]: code in the test method which failed + - [0]: C{TestCase._deferSetUpAndRun} + - [1]: C{defer.__iter__} + - [2]: C{defer.raiseException} + - [3]: C{defer.maybeDeferred} + - [4]: C{utils.runWithWarningsSuppressed} + - [5]: C{utils.runWithWarningsSuppressed} + - [6:-2]: code in the test method which failed - [-1]: C{_synctest.fail} When a method fails inside a C{Deferred} (i.e., when the test method returns a C{Deferred}, and that C{Deferred}'s errback fires), the stack captured inside the resulting C{Failure} looks like this: - - [0]: C{defer.Deferred._runCallbacks} - - [1:-2]: code in the testmethod which failed + + - [0]: C{defer._deferSetUpAndRun} + - [1]: C{defer.__iter__} + - [2]: C{defer.Deferred._runCallbacks} + - [3:-2]: code in the testmethod which failed - [-1]: C{_synctest.fail} - As a result, we want to trim either [maybeDeferred, runWWS, runWWS] or - [Deferred._runCallbacks] or [SynchronousTestCase._run, runWWS] from the - front, and trim the [unittest.fail] from the end. + As a result, we want to trim either + [deferTestMethod, __iter__, raiseException, maybeDeferred, runWWS, runWWS] or + [defer.deferTestMethod, __iter__, Deferred._runCallbacks] or + [SynchronousTestCase._run, runWWS] from the front, and trim the [unittest.fail] + from the end. There is also another case, when the test method is badly defined and contains extra arguments. @@ -568,19 +579,25 @@ class Reporter(TestResult): """ newFrames = list(frames) - if len(frames) < 2: + if len(frames) < 3: return newFrames - firstMethod = newFrames[0][0] - firstFile = os.path.splitext(os.path.basename(newFrames[0][1]))[0] - - secondMethod = newFrames[1][0] - secondFile = os.path.splitext(os.path.basename(newFrames[1][1]))[0] - - syncCase = (("_run", "_synctest"), ("runWithWarningsSuppressed", "util")) - asyncCase = (("maybeDeferred", "defer"), ("runWithWarningsSuppressed", "utils")) + frames = [ + (frame[0], os.path.splitext(os.path.basename(frame[1]))[0]) + for frame in newFrames[:3] + ] - twoFrames = ((firstMethod, firstFile), (secondMethod, secondFile)) + syncCase = [("_run", "_synctest"), ("runWithWarningsSuppressed", "util")] + asyncCase = [ + ("_deferSetUpAndRun", "_asynctest"), + ("__iter__", "defer"), + ("raiseException", "failure"), + ] + deferCase = [ + ("_deferSetUpAndRun", "_asynctest"), + ("__iter__", "defer"), + ("_runCallbacks", "defer"), + ] # On PY3, we have an extra frame which is reraising the exception for frame in newFrames: @@ -589,12 +606,12 @@ class Reporter(TestResult): # If it's in the compat module and is reraise, BLAM IT newFrames.pop(newFrames.index(frame)) - if twoFrames == syncCase: + if frames[:2] == syncCase: newFrames = newFrames[2:] - elif twoFrames == asyncCase: + elif frames[:3] == asyncCase: + newFrames = newFrames[6:] + elif frames[:3] == deferCase: newFrames = newFrames[3:] - elif (firstMethod, firstFile) == ("_runCallbacks", "defer"): - newFrames = newFrames[1:] if not newFrames: # The method fails before getting called, probably an argument |
