1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
"""Tests for the TUI hub (hub.py) menu and helpers.
The hub drives the same curses widgets as tui.py, so these tests reuse the
fake curses/screen from test_tui to run the menu without a terminal.
"""
import unittest
from pathlib import Path
from unittest.mock import patch
import hub
import tui
from tests.test_tui import FakeCurses, FakeScreen
class HubHelperTests(unittest.TestCase):
"""Pure helpers in hub.py (no curses)."""
def test_is_float(self):
self.assertTrue(hub._is_float("1.0"))
self.assertTrue(hub._is_float("2"))
self.assertFalse(hub._is_float("abc"))
self.assertFalse(hub._is_float(""))
def test_list_voices_from_dir(self):
with __import__("tempfile").TemporaryDirectory() as td:
d = Path(td)
(d / "Narrator.wav").write_bytes(b"x")
(d / "Alpha.WAV").write_bytes(b"x")
(d / "notes.txt").write_bytes(b"x")
voices = hub._list_voices(str(d))
# Stems preserve case; sorting is case-insensitive.
self.assertEqual(voices, ["Alpha", "Narrator"])
def test_list_voices_missing_dir(self):
self.assertEqual(hub._list_voices("/no/such/dir"), [])
def test_status_mark(self):
from backends import BackendStatus
ready = BackendStatus("k", "l", installed=True, configured=True)
half = BackendStatus("k", "l", installed=True, configured=False)
none = BackendStatus("k", "l", installed=False, configured=False)
self.assertEqual(hub._status_mark("k", [ready]), "ready")
self.assertEqual(hub._status_mark("k", [half]), "installed")
self.assertEqual(hub._status_mark("k", [none]), "not set up")
self.assertEqual(hub._status_mark("missing", []), "not set up")
class HubMenuTests(unittest.TestCase):
"""Drive _hub_menu with a fake screen (no terminal)."""
def setUp(self):
tui._THEME.clear()
self.curses = FakeCurses()
from unittest.mock import patch as _patch
self._patcher = _patch.dict("sys.modules", {"curses": self.curses})
self._patcher.start()
self.addCleanup(self._patcher.stop)
self.addCleanup(tui._THEME.clear)
def test_quit_returns_none(self):
# Main menu: move to "Quit" (4th option, index 3) and press Enter.
screen = FakeScreen(keys=[FakeCurses.KEY_DOWN, FakeCurses.KEY_DOWN,
FakeCurses.KEY_DOWN, 10])
with patch.object(hub, "detect_all", return_value=[]):
result = hub._hub_menu(screen)
self.assertIsNone(result)
def test_convert_with_no_ready_backend_offers_setup(self):
# Convert -> "Set up a backend..." is the only entry -> Enter selects
# it -> setup menu lists 3 backends; press Esc to go back -> convert
# returns None -> main menu loops. Then quit (Down x3 + Enter).
from backends import BackendInfo, BackendStatus
none = BackendStatus("k", "l", installed=False, configured=False)
infos = [BackendInfo("audiocpp", "audio.cpp", lambda: none,
lambda: 0),
BackendInfo("qwen", "Qwen", lambda: none, lambda: 0),
BackendInfo("faster", "faster", lambda: none, lambda: 0)]
with patch.object(hub, "detect_all", return_value=[none, none, none]), \
patch.object(hub, "REGISTRY", infos):
# Convert(Enter), setup-entry(Enter), Esc on setup menu,
# back at main menu -> Down x3 -> Enter (Quit).
screen = FakeScreen(keys=[10, 10, 27,
FakeCurses.KEY_DOWN, FakeCurses.KEY_DOWN,
FakeCurses.KEY_DOWN, 10])
result = hub._hub_menu(screen)
self.assertIsNone(result)
if __name__ == "__main__":
unittest.main()
|