diff --git a/tools/test-backend b/tools/test-backend index 6de73db69a..7cf9b86464 100755 --- a/tools/test-backend +++ b/tools/test-backend @@ -101,7 +101,6 @@ not_yet_fully_covered = [ "zerver/lib/queue.py", "zerver/lib/sqlalchemy_utils.py", "zerver/lib/storage.py", - "zerver/lib/timeout.py", "zerver/lib/unminify.py", "zerver/lib/utils.py", "zerver/lib/zephyr.py", diff --git a/zerver/tests/test_timeout.py b/zerver/tests/test_timeout.py new file mode 100644 index 0000000000..8593d85d13 --- /dev/null +++ b/zerver/tests/test_timeout.py @@ -0,0 +1,53 @@ +import time +import traceback + +from zerver.lib.test_classes import ZulipTestCase +from zerver.lib.timeout import TimeoutExpired, timeout + + +class TimeoutTestCase(ZulipTestCase): + # We can't use assertRaises because that doesn't store the + # traceback, which we want to verify + + def something_exceptional(self) -> int: + raise ValueError("Something went wrong") + + def sleep_x_seconds_y_times(self, x: float, y: int) -> int: + for i in range(y): + time.sleep(x) + return 42 # nocoverage + + def test_timeout_returns(self) -> None: + ret = timeout(1, lambda: 42) + self.assertEqual(ret, 42) + + def test_timeout_exceeded(self) -> None: + try: + timeout(1, lambda: self.sleep_x_seconds_y_times(0.1, 50)) + raise AssertionError("Failed to raise a timeout") + except TimeoutExpired as exc: + tb = traceback.format_tb(exc.__traceback__) + self.assertIn("in sleep_x_seconds_y_times", tb[-1]) + self.assertIn("time.sleep(x)", tb[-1]) + + def test_timeout_raises(self) -> None: + try: + timeout(1, lambda: self.something_exceptional()) + raise AssertionError("Failed to raise an exception") + except ValueError as exc: + tb = traceback.format_tb(exc.__traceback__) + self.assertIn("in something_exceptional", tb[-1]) + self.assertIn("raise ValueError", tb[-1]) + + def test_timeout_warn(self) -> None: + # If the sleep is long enough, it will outlast the attempts to + # kill it + with self.assertLogs(level="WARNING") as m: + try: + timeout(1, lambda: self.sleep_x_seconds_y_times(5, 1)) + raise AssertionError("Failed to raise a timeout") + except TimeoutExpired as exc: + tb = traceback.format_tb(exc.__traceback__) + self.assertNotIn("in sleep_x_seconds_y_times", tb[-1]) + self.assertIn("raise TimeoutExpired", tb[-1]) + self.assertEqual(m.output, ["WARNING:root:Failed to time out backend thread"])