diff options
| author | historia <historiavg@proton.me> | 2026-08-25 20:10:40 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-08-25 20:10:40 -0400 |
| commit | ca3c78ef577f5d8435dd10db0296c3e2fd8e69e2 (patch) | |
| tree | cff4b84c14d0ac88bc53f2aef63115352d57de90 /app/tests/test_hub.py | |
| parent | 757588321d4c27889be6e8e3c12b75873ad1218d (diff) | |
| download | tts-audiobook-generator-ca3c78ef577f5d8435dd10db0296c3e2fd8e69e2.tar.gz | |
feat: stop server and exit after generating
Diffstat (limited to 'app/tests/test_hub.py')
| -rw-r--r-- | app/tests/test_hub.py | 92 |
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, \ |
