|
8 | 8 | import threading |
9 | 9 | import time |
10 | 10 | import unittest |
11 | | -from ctypes import c_long, py_object, pythonapi |
12 | 11 | from datetime import datetime |
13 | 12 | from functools import partial |
14 | 13 | from unittest.mock import patch |
|
31 | 30 | "This plugin does not support raise()", |
32 | 31 | ] |
33 | 32 |
|
34 | | -_TIMEOUT_CONTEXT = { |
35 | | - "testId": None, |
36 | | - "timeout": None, |
37 | | -} |
38 | 33 |
|
39 | | - |
40 | | -class TestTimeoutError(BaseException): |
41 | | - pass |
42 | | - |
43 | | - |
44 | | -def _raiseAsyncException(threadId: int, exceptionType: type): |
45 | | - result = pythonapi.PyThreadState_SetAsyncExc(c_long(threadId), py_object(exceptionType)) |
46 | | - if result > 1: |
47 | | - pythonapi.PyThreadState_SetAsyncExc(c_long(threadId), py_object(None)) |
48 | | - |
49 | | - |
50 | | -def _triggerTestTimeout(mainThreadId: int, testId: str, timeoutSeconds: float): |
51 | | - _TIMEOUT_CONTEXT["testId"] = testId |
52 | | - _TIMEOUT_CONTEXT["timeout"] = timeoutSeconds |
53 | | - _raiseAsyncException(mainThreadId, TestTimeoutError) |
54 | | - |
55 | | - |
56 | | -class TimeoutTextTestResult(unittest.TextTestResult): |
57 | | - def addError(self, test, err): |
58 | | - errorType, errorValue, errorTraceback = err |
59 | | - if issubclass(errorType, TestTimeoutError): |
60 | | - testId = _TIMEOUT_CONTEXT.get("testId") or test.id() |
61 | | - timeoutSeconds = _TIMEOUT_CONTEXT.get("timeout") |
62 | | - timeoutMessage = f"Test timed out after {timeoutSeconds}s: {testId}" |
63 | | - super().addFailure( |
64 | | - test, |
65 | | - (AssertionError, AssertionError(timeoutMessage), errorTraceback) |
66 | | - ) |
67 | | - self.stop() |
68 | | - return |
69 | | - super().addError(test, err) |
70 | | - |
71 | | - |
72 | | -class TimeoutTextTestRunner(unittest.TextTestRunner): |
73 | | - resultclass = TimeoutTextTestResult |
74 | | - |
75 | | - |
76 | | -unittest.TextTestRunner = TimeoutTextTestRunner |
| 34 | +def _timeoutHandler(testId: str, timeoutSeconds: float): |
| 35 | + """Force exit when test timeout fires.""" |
| 36 | + msg = f"\nTEST TIMEOUT: {testId} exceeded {timeoutSeconds}s\n" |
| 37 | + sys.stderr.write(msg) |
| 38 | + sys.stderr.flush() |
| 39 | + os._exit(1) |
77 | 40 |
|
78 | 41 |
|
79 | 42 | def _qt_message_handler(type: QtMsgType, context: QMessageLogContext, msg: str): |
@@ -199,8 +162,8 @@ def run(self, result=None): |
199 | 162 |
|
200 | 163 | watchdog = threading.Timer( |
201 | 164 | timeoutSeconds, |
202 | | - _triggerTestTimeout, |
203 | | - args=(threading.main_thread().ident, self.id(), timeoutSeconds), |
| 165 | + _timeoutHandler, |
| 166 | + args=(self.id(), timeoutSeconds), |
204 | 167 | ) |
205 | 168 | watchdog.daemon = True |
206 | 169 | watchdog.start() |
|
0 commit comments