diff --git a/tests/rest/client/test_versions.py b/tests/rest/client/test_versions.py index f1aecce44a..223f8490b8 100644 --- a/tests/rest/client/test_versions.py +++ b/tests/rest/client/test_versions.py @@ -74,7 +74,9 @@ class VersionsTestCase(unittest.HomeserverTestCase): "GET", "/_matrix/client/versions", content={}, + await_result=False, ) + channel.await_rust_result() self.assertEqual(channel.code, 200, channel.result) self._sanity_check_versions_response(channel.json_body) @@ -87,7 +89,9 @@ class VersionsTestCase(unittest.HomeserverTestCase): "/_matrix/client/versions", content={}, access_token=user1_tok, + await_result=False, ) + channel.await_rust_result() self.assertEqual(channel.code, 200, channel.result) self._sanity_check_versions_response(channel.json_body) @@ -103,7 +107,9 @@ class VersionsTestCase(unittest.HomeserverTestCase): "/_matrix/client/versions", content={}, access_token=user1_tok, + await_result=False, ) + channel.await_rust_result() self.assertEqual(channel.code, 200, channel.result) self._sanity_check_versions_response(channel.json_body) self.assertEqual( @@ -121,7 +127,9 @@ class VersionsTestCase(unittest.HomeserverTestCase): "/_matrix/client/versions", content={}, access_token=user1_tok, + await_result=False, ) + channel.await_rust_result() self.assertEqual(channel.code, 200, channel.result) self._sanity_check_versions_response(channel.json_body) self.assertEqual( @@ -136,7 +144,9 @@ class VersionsTestCase(unittest.HomeserverTestCase): "/_matrix/client/versions", content={}, access_token=user2_tok, + await_result=False, ) + channel.await_rust_result() self.assertEqual(channel.code, 200, channel.result) self._sanity_check_versions_response(channel.json_body) self.assertEqual( diff --git a/tests/server.py b/tests/server.py index 20fcc42081..c749c4b534 100644 --- a/tests/server.py +++ b/tests/server.py @@ -311,6 +311,34 @@ class FakeChannel: self._reactor.advance(0.1) + def await_rust_result(self, timeout_ms: int = 1000) -> None: + """ + Wait until the request is finished (a request that includes async Rust work). + + This is separate from `await_result` because we don't want to slow down the + entire test suite with real sleeps. Prefer `await_result` if possible. + + The default `await_result` only advances the reactor's *virtual* clock in a + tight loop, never yielding real wall-clock time, so the Tokio threads never get + a chance to run and the request times out. Instead we pump the reactor while + also sleeping a little real time each iteration, the same way + `HomeserverTestCase.wait_on_thread()` does. + """ + end_time = self._reactor.seconds() + timeout_ms / 1000.0 + self._reactor.run() + + while not self.is_finished(): + if self._reactor.seconds() > end_time: + raise TimedOutException("Timed out waiting for request to finish.") + + # Pump the Twisted reactor + self._reactor.advance(0.1) + + # Give some real wall-clock time for other threads to do work. This could be + # things spawned on the Twisted reactor threadpool but this is primarily + # meant for the Tokio thread pool (async Rust code). + time.sleep(0.01) + def extract_cookies(self, cookies: MutableMapping[str, str]) -> None: """Process the contents of any Set-Cookie headers in the response diff --git a/tests/unittest.py b/tests/unittest.py index 03db9a4282..5fdec2f9ea 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -483,7 +483,11 @@ class HomeserverTestCase(TestCase): while not deferred.called: if start_time + timeout < time.time(): raise ValueError("Timed out waiting for threadpool") + # Pump the Twisted reactor self.reactor.advance(0.01) + # Give some real wall-clock time for other threads to do work. This could be + # things spawned on the Twisted reactor threadpool or Tokio thread pool + # (async Rust code). time.sleep(0.01) def wait_for_background_updates(self) -> None: