aboutsummaryrefslogtreecommitdiff
path: root/app/tests/test_hub.py
diff options
context:
space:
mode:
Diffstat (limited to 'app/tests/test_hub.py')
-rw-r--r--app/tests/test_hub.py92
1 files changed, 82 insertions, 10 deletions
diff --git a/app/tests/test_hub.py b/app/tests/test_hub.py
index 3547ba8..ebae82b 100644
--- a/app/tests/test_hub.py
+++ b/app/tests/test_hub.py
@@ -651,7 +651,8 @@ class ConvertFlowTests(unittest.TestCase):
def _form_values(self, **overrides):
"""A fully-populated form result, with sensible defaults."""
values = {"output_format": "m4b", "speed": "1.0",
- "single_file": False, "debug": False}
+ "single_file": False, "debug": False,
+ "stop_and_exit": True}
values.update(overrides)
return values
@@ -666,23 +667,26 @@ class ConvertFlowTests(unittest.TestCase):
"""A backend status that is ready to convert with."""
return BackendStatus(key, label, installed=True, configured=True)
- def _convert(self, stdscr, statuses):
+ def _convert(self, stdscr, statuses, run_result=None):
"""Run the convert flow with STATUSES, returning the command tuple.
``_run_conversion`` is stubbed so the accepted command is captured
- instead of launching the run view; None is returned when the flow
- aborts before reaching a conversion (nothing ready, a flash).
+ instead of launching the run view (and reporting RUN_RESULT — True
+ when the user answered "stop the server and exit"); None is returned
+ when the flow aborts before reaching a conversion (nothing ready, a
+ flash). The screen's navigation result lands on ``self.nav``.
"""
captured = {}
def fake_run_conversion(self_, backend, kwargs):
captured["backend"] = backend
captured["kwargs"] = kwargs
+ return run_result
with patch.object(hub, "detect_all", return_value=statuses), \
patch.object(hub._Hub, "_run_conversion",
fake_run_conversion):
- hub._Hub(None).screen_convert()
+ self.nav = hub._Hub(None).screen_convert()
if "backend" not in captured:
return None
return ("convert", captured["backend"], captured["kwargs"])
@@ -742,9 +746,14 @@ class ConvertFlowTests(unittest.TestCase):
self.assertEqual([f["key"] for f in fields],
["backend", "model_id", "audiocpp_voice",
"instructions", "output_format", "speed",
- "single_file", "debug"])
+ "single_file", "debug", "stop_and_exit"])
self.assertEqual(form_kwargs["buttons"], ("Generate!", "Cancel"))
self.assertTrue(form_kwargs["start_on_buttons"])
+ # The stop-and-exit toggle ships on by default.
+ stop_field = self._field("stop_and_exit")
+ self.assertEqual(stop_field["kind"], "bool")
+ self.assertTrue(stop_field["value"])
+ self.assertTrue(cmd[2]["stop_and_exit"])
# The backend field offers the remote entry under a [remote] label.
self.assertEqual(fields[0]["choices"],
[("audio.cpp [remote]", "audiocpp-remote")])
@@ -1011,7 +1020,8 @@ class ConvertFlowTests(unittest.TestCase):
fields = self.tui.forms_seen[0][1]
self.assertEqual([f["key"] for f in fields],
["backend", "mode", "speaker", "clone",
- "output_format", "speed", "single_file", "debug"])
+ "output_format", "speed", "single_file", "debug",
+ "stop_and_exit"])
mode_field = self._field("mode")
self.assertEqual(mode_field["choices"],
[("Built-in speaker", "custom"),
@@ -1080,6 +1090,24 @@ class ConvertFlowTests(unittest.TestCase):
self.assertEqual(cmd[2]["api_url"], "http://10.0.0.5:8000")
self.assertEqual(self._field("faster_voice")["kind"], "text")
+ def test_stop_and_exit_answer_quits_the_tui(self):
+ # Yes on the run view's stop-and-exit prompt ends the whole TUI:
+ # screen_convert returns None instead of Wizard.BACK.
+ st = self._remote("faster", "faster-qwen3-tts",
+ url="http://10.0.0.5:8000")
+ self._answer_form(backend="faster-remote", faster_voice="obama")
+ cmd = self._convert(None, [st], run_result=True)
+ self.assertIsNotNone(cmd)
+ self.assertIsNone(self.nav)
+
+ def test_normal_finish_returns_to_the_menu(self):
+ st = self._remote("faster", "faster-qwen3-tts",
+ url="http://10.0.0.5:8000")
+ self._answer_form(backend="faster-remote", faster_voice="obama")
+ cmd = self._convert(None, [st])
+ self.assertIsNotNone(cmd)
+ self.assertIs(self.nav, hub.tui.Wizard.BACK)
+
def test_qwen_remote_limited_modes_and_api_url(self):
# A remote qwen with only the Base (clone) demo answering: the form
# offers only clone mode and targets the clone remote URL.
@@ -1130,7 +1158,7 @@ class ConvertFlowTests(unittest.TestCase):
[f["key"] for f in fields],
["backend", "model_id", "audiocpp_voice", "instructions",
"mode", "speaker", "clone", "output_format", "speed",
- "single_file", "debug"])
+ "single_file", "debug", "stop_and_exit"])
# The form opens on the configured default (audio.cpp): its fields
# show, the other backend's hide. (Instructions is hidden too: the
# default higgs entry is clone-only, which ignores instructions.)
@@ -1216,6 +1244,24 @@ class PrepareRunConfigTests(unittest.TestCase):
self.assertEqual(cfg.server_name, "qwen-custom")
self.assertNotIn("autostart", kwargs)
+ def test_stop_and_exit_travels_on_the_config_not_the_kwargs(self):
+ # The run-view toggle is not a converter kwarg: it moves onto the
+ # config (and defaults to off when the form did not send it).
+ with tempfile.TemporaryDirectory() as tmp:
+ with patch.object(hub, "LOGS_FOLDER", Path(tmp)), \
+ patch.object(hub, "detect_all", return_value=[]):
+ cfg = hub._prepare_run_config(
+ "audiocpp", {"stop_and_exit": True})
+ self.assertTrue(cfg.stop_and_exit)
+ cfg = hub._prepare_run_config("audiocpp", {})
+ self.assertFalse(cfg.stop_and_exit)
+
+ def test_remote_config_carries_stop_and_exit(self):
+ cfg = hub._prepare_run_config(
+ "audiocpp", {"api_url": "http://10.0.0.5:8080",
+ "stop_and_exit": True})
+ self.assertTrue(cfg.stop_and_exit)
+
def test_autostart_with_missing_spec_continues_with_notice(self):
kwargs = {"autostart": "gone"}
with patch.object(hub, "detect_all", return_value=[]), \
@@ -1336,11 +1382,14 @@ class DispatchConversionTests(unittest.TestCase):
timeouts.append(ms)
class FakeView:
+ run_result = None
+
def __init__(self, scr, config):
self.config = config
self.scr = scr
+
def run(self):
- pass
+ return type(self).run_result
screen = Screen()
with patch.object(hub, "_prepare_run_config",
@@ -1348,11 +1397,34 @@ class DispatchConversionTests(unittest.TestCase):
backend="qwen", backend_label="qwen-tts",
kwargs={}, book_files=[], planned=[])) as mk_cfg, \
patch.object(hub.runview, "RunView", FakeView):
- hub._Hub(screen)._run_conversion("qwen", {})
+ result = hub._Hub(screen)._run_conversion("qwen", {})
mk_cfg.assert_called_once()
# The run view leaves a timed getch behind; it is reset so the hub
# menus block for keys again.
self.assertEqual(timeouts, [-1])
+ # A normal finish lands back on the menu (no quit signal).
+ self.assertFalse(result)
+
+ def test_stop_and_exit_answer_propagates_from_the_view(self):
+ class Screen:
+ def timeout(self, ms):
+ pass
+
+ class FakeView:
+ def __init__(self, scr, config):
+ self.config = config
+ self.scr = scr
+
+ def run(self):
+ return True
+
+ with patch.object(hub, "_prepare_run_config",
+ return_value=hub.runview.RunConfig(
+ backend="qwen", backend_label="qwen-tts",
+ kwargs={}, book_files=[], planned=[])), \
+ patch.object(hub.runview, "RunView", FakeView):
+ result = hub._Hub(Screen())._run_conversion("qwen", {})
+ self.assertTrue(result)
def test_no_run_config_skips_the_view(self):
with patch.object(hub, "_prepare_run_config", return_value=None) as mk_cfg, \