mirror of
https://github.com/element-hq/synapse.git
synced 2026-11-04 22:07:34 +00:00
121 lines
4.0 KiB
Python
121 lines
4.0 KiB
Python
#
|
|
# This file is licensed under the Affero General Public License (AGPL) version 3.
|
|
#
|
|
# Copyright (C) 2025 Element Creations Ltd
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# See the GNU Affero General Public License for more details:
|
|
# <https://www.gnu.org/licenses/agpl-3.0.html>.
|
|
#
|
|
#
|
|
|
|
import weakref
|
|
|
|
from synapse.synapse_rust.runtime import RustRuntime
|
|
from synapse.util.duration import Duration
|
|
|
|
from tests.unittest import HomeserverTestCase
|
|
|
|
|
|
class ClockTestCase(HomeserverTestCase):
|
|
def test_looping_calls_are_gced(self) -> None:
|
|
"""Test that looping calls are garbage collected after being stopped.
|
|
|
|
The `Clock` tracks looping calls so to allow stopping of all looping
|
|
calls via the clock.
|
|
"""
|
|
clock = self.hs.get_clock()
|
|
|
|
# Create a new looping call, and take a weakref to it.
|
|
call = clock.looping_call(lambda: None, Duration(seconds=1))
|
|
|
|
weak_call = weakref.ref(call)
|
|
|
|
# Stop the looping call. It should get garbage collected after this.
|
|
call.stop()
|
|
|
|
# Delete our strong reference to the call (otherwise it won't get garbage collected).
|
|
del call
|
|
|
|
# Check that the call has been garbage collected.
|
|
self.assertIsNone(weak_call())
|
|
|
|
def test_looping_calls_stopped_on_clock_shutdown(self) -> None:
|
|
"""Test that looping calls are stopped when the clock is shut down."""
|
|
clock = self.hs.get_clock()
|
|
|
|
was_called = False
|
|
|
|
def on_call() -> None:
|
|
nonlocal was_called
|
|
was_called = True
|
|
|
|
# Create a new looping call.
|
|
call = clock.looping_call(on_call, Duration(seconds=1))
|
|
weak_call = weakref.ref(call)
|
|
del call # Remove our strong reference to the call.
|
|
|
|
# The call should still exist.
|
|
self.assertIsNotNone(weak_call())
|
|
|
|
# Advance the clock to trigger the call.
|
|
self.reactor.advance(2)
|
|
self.assertTrue(was_called)
|
|
|
|
# Shut down the clock, which should stop the looping call.
|
|
clock.shutdown()
|
|
|
|
# The call should have been garbage collected.
|
|
self.assertIsNone(weak_call())
|
|
|
|
# Advance the clock again; the call should not be called again.
|
|
was_called = False
|
|
self.reactor.advance(2)
|
|
self.assertFalse(was_called)
|
|
|
|
|
|
class RustClockTestCase(HomeserverTestCase):
|
|
"""Tests for the Rust side's view of the time (`RustRuntime.time_msec`)."""
|
|
|
|
def test_follows_the_reactor(self) -> None:
|
|
runtime = self.hs.get_rust_runtime()
|
|
before = runtime.time_msec()
|
|
|
|
self.reactor.advance(37)
|
|
|
|
self.assertEqual(runtime.time_msec(), before + 37 * 1000)
|
|
self.assertEqual(runtime.time_msec(), self.hs.get_clock().time_msec())
|
|
|
|
def test_is_up_to_date_inside_a_looping_call(self) -> None:
|
|
"""Rust must see the new time from callbacks fired *during* an advance.
|
|
|
|
This is why the test reactor moves the Rust clock *before* it calls
|
|
`super().advance()`, which is what fires those callbacks.
|
|
"""
|
|
runtime = self.hs.get_rust_runtime()
|
|
seen: list[int] = []
|
|
self.hs.get_clock().looping_call(
|
|
lambda: seen.append(runtime.time_msec()), Duration(seconds=1)
|
|
)
|
|
|
|
base_time = self.hs.get_clock().time_msec()
|
|
|
|
self.reactor.advance(1)
|
|
self.assertEqual(seen, [base_time + 1000])
|
|
|
|
self.reactor.advance(1)
|
|
self.assertEqual(seen, [base_time + 1000, base_time + 2000])
|
|
|
|
def test_every_runtime_on_the_reactor_follows_it(self) -> None:
|
|
"""Worker tests run several homeservers, each with its own runtime, on
|
|
one reactor. All of their clocks must move together."""
|
|
other_runtime = RustRuntime(hs=self.hs)
|
|
|
|
self.reactor.advance(5)
|
|
|
|
self.assertEqual(other_runtime.time_msec(), self.hs.get_clock().time_msec())
|