Files
voculinux/tests/test_model_keepalive.py
T
xavierk 80a3792576
CI / security (pull_request) Failing after 1m25s
CI / lint (pull_request) Failing after 34s
CI / ai-review (pull_request) Successful in 25s
chore: import vocalinux project
Import the vocalinux source tree (tray app, Next.js site in web/,
packaging, installers, docs and tests) on top of the Gitea template
commit. The project README replaces the template README, and the
template CI files (.gitea/workflows/ci.yml, .jscpd.json, CI.md) are kept.
2026-11-06 11:51:44 +05:30

157 lines
4.8 KiB
Python

"""Tests for model keep-alive idle unload helper."""
from __future__ import annotations
import unittest
from unittest.mock import MagicMock, patch
from vocalinux.model_keepalive import (
DEFAULT_IDLE_TIMEOUT_SECONDS,
ModelKeepAlive,
clamp_idle_timeout,
)
from vocalinux.ui.config_manager import DEFAULT_CONFIG
class TestClampIdleTimeout(unittest.TestCase):
def test_clamps_low_and_high(self):
self.assertEqual(clamp_idle_timeout(10), 60)
self.assertEqual(clamp_idle_timeout(99999), 3600)
self.assertEqual(clamp_idle_timeout(300), 300)
def test_invalid_falls_back_to_default(self):
self.assertEqual(clamp_idle_timeout("nope"), float(DEFAULT_IDLE_TIMEOUT_SECONDS))
self.assertEqual(clamp_idle_timeout(None), float(DEFAULT_IDLE_TIMEOUT_SECONDS))
class TestModelKeepAlive(unittest.TestCase):
def test_bump_when_disabled_cancels(self):
fired = []
ka = ModelKeepAlive(
get_config=lambda: (False, 300),
on_idle_unload=lambda: fired.append(1),
use_glib=False,
)
ka.start()
ka.bump()
self.assertFalse(ka.armed)
self.assertFalse(ka.fire_if_due())
self.assertEqual(fired, [])
def test_bump_arms_and_fire_unloads(self):
fired = []
ka = ModelKeepAlive(
get_config=lambda: (True, 300),
on_idle_unload=lambda: fired.append(1),
is_safe_to_unload=lambda: True,
use_glib=False,
)
ka.start()
ka.bump()
self.assertTrue(ka.armed)
self.assertTrue(ka.fire_if_due())
self.assertEqual(fired, [1])
self.assertFalse(ka.armed)
# Second fire without re-arm is a no-op
self.assertFalse(ka.fire_if_due())
def test_cancel_prevents_fire(self):
fired = []
ka = ModelKeepAlive(
get_config=lambda: (True, 120),
on_idle_unload=lambda: fired.append(1),
use_glib=False,
)
ka.start()
ka.bump()
ka.cancel()
self.assertFalse(ka.armed)
self.assertFalse(ka.fire_if_due())
self.assertEqual(fired, [])
def test_unsafe_predicate_skips_unload(self):
fired = []
ka = ModelKeepAlive(
get_config=lambda: (True, 120),
on_idle_unload=lambda: fired.append(1),
is_safe_to_unload=lambda: False,
use_glib=False,
)
ka.start()
ka.bump()
self.assertFalse(ka.fire_if_due())
self.assertEqual(fired, [])
def test_callback_error_is_swallowed(self):
def boom():
raise RuntimeError("unload failed")
ka = ModelKeepAlive(
get_config=lambda: (True, 120),
on_idle_unload=boom,
is_safe_to_unload=lambda: True,
use_glib=False,
)
ka.start()
ka.bump()
self.assertFalse(ka.fire_if_due())
def test_config_error_disables(self):
def bad_config():
raise RuntimeError("config boom")
ka = ModelKeepAlive(get_config=bad_config, use_glib=False)
ka.start()
ka.bump()
self.assertFalse(ka.armed)
def test_stop_and_shutdown(self):
ka = ModelKeepAlive(get_config=lambda: (True, 120), use_glib=False)
ka.start()
ka.bump()
ka.shutdown()
self.assertFalse(ka.active)
self.assertFalse(ka.armed)
def test_start_idempotent(self):
ka = ModelKeepAlive(get_config=lambda: (True, 120), use_glib=False)
ka.start()
ka.start()
self.assertTrue(ka.active)
def test_glib_schedule_and_timeout(self):
"""Drive GLib scheduling via mocked gi.repository (same pattern as auto-pause)."""
mock_glib = MagicMock()
mock_glib.timeout_add_seconds.return_value = 42
fired = []
with patch.dict(
"sys.modules",
{"gi": MagicMock(), "gi.repository": MagicMock(GLib=mock_glib)},
):
ka = ModelKeepAlive(
get_config=lambda: (True, 90),
on_idle_unload=lambda: fired.append(1),
is_safe_to_unload=lambda: True,
use_glib=True,
)
ka.start()
ka.bump()
mock_glib.timeout_add_seconds.assert_called()
# Invoke the scheduled callback
cb = mock_glib.timeout_add_seconds.call_args[0][1]
self.assertFalse(cb())
self.assertEqual(fired, [1])
# Callback clears _timeout_id; shutdown should be a no-op cancel
ka.shutdown()
self.assertFalse(ka.active)
self.assertFalse(ka.armed)
def test_default_config_section(self):
self.assertIn("model_keepalive", DEFAULT_CONFIG)
self.assertFalse(DEFAULT_CONFIG["model_keepalive"]["enabled"])
if __name__ == "__main__":
unittest.main()