161 pass, no cascading. The remaining non-federation failures are:

1. test_post_room_initial_state / test_post_room_no_keys — resource_usage is None
  2. test_spam_checker_may_join_room / _deprecated — spam checker
  3. PublicRoomsRoomTypeFilterTestCase — 2 tests
  4. MSC4293 — 4 federation tests (out of scope)

  The cascading issue is fixed. Let me verify with the login tests too:

⏺ Bash(timeout 120 poetry run python -m pytest tests/rest/client/test_login.py --tb=line -q 2>&1 | tail -3)
  ⎿  -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
     50 passed, 116 warnings in 18.21s
  ⎿  (timeout 2m 15s)

⏺ All 50 login tests still pass. The cascading "non-sentinel logging context" issue is fixed.

  The fix was adding asyncTearDown to HomeserverTestCase that:
  1. Calls self.hs.get_clock().shutdown() — cancels all looping calls and delayed calls, preventing them from firing during cleanup and setting non-sentinel contexts
  2. Yields twice (await asyncio.sleep(0)) so cancellation propagates
  3. Resets to SENTINEL_CONTEXT as a safety net

  Also added removeSystemEventTrigger to ThreadedMemoryReactorClock to eliminate the "no attribute" error during homeserver shutdown.
This commit is contained in:
Matthew Hodgson committed 2026-03-24 16:20:19 -04:00
1 parent 28327deb0e
commit 4b997bd55f
2 files changed
+19

No files matched your search

+4
View File
@@ -637,6 +637,10 @@ class ThreadedMemoryReactorClock:
self.triggers[key] = (callable, args)
return key
def removeSystemEventTrigger(self, trigger_id: Any) -> None:
"""Remove a previously registered system event trigger."""
self.triggers.pop(trigger_id, None)
def getThreadPool(self) -> Any:
return self.threadpool
+15
View File
@@ -576,6 +576,21 @@ class HomeserverTestCase(TestCase):
if inspect.isawaitable(res):
await res
async def asyncTearDown(self) -> None:
"""Shut down the homeserver to cancel all background tasks.
This prevents looping calls and delayed calls from firing during
cleanup, which would leave non-sentinel logging contexts active
and cause the next test's setUp to fail.
"""
if hasattr(self, 'hs') and self.hs is not None:
self.hs.get_clock().shutdown()
# Give event loop time for cancellation to propagate
await asyncio.sleep(0)
await asyncio.sleep(0)
# Ensure we're back in the sentinel context
set_current_context(SENTINEL_CONTEXT)
def tearDown(self) -> None:
# Reset to not use frozen dicts.
events.USE_FROZEN_DICTS = False