From 544486a374cd5cae7acce1302648d8dad079db48 Mon Sep 17 00:00:00 2001 From: historia Date: Wed, 26 Aug 2026 18:18:21 -0400 Subject: fix: default directory when choosing voices in tui --- app/tests/test_backends.py | 6 +-- app/tests/test_backends_audiocpp.py | 81 +++++++++++++++++++++++++++++++++++++ app/tests/test_hub.py | 8 ++-- app/tests/test_tui.py | 20 +++++++++ 4 files changed, 107 insertions(+), 8 deletions(-) (limited to 'app/tests') diff --git a/app/tests/test_backends.py b/app/tests/test_backends.py index 15a8649..99742f3 100644 --- a/app/tests/test_backends.py +++ b/app/tests/test_backends.py @@ -356,19 +356,17 @@ class QwenSetupScreenTests(unittest.TestCase): The qwen wizard asks nothing (ports live in Settings, the speaker is chosen on Generate), so it cannot be aborted: an already-installed - package is a no-op flash, everything else runs in the task view. + package is a silent no-op, everything else runs in the task view. """ - def test_already_installed_flashes_and_skips_the_task_view(self): + def test_already_installed_is_a_silent_noop(self): from backends import qwen settings = {"do_install": False} with patch.object(qwen, "_wizard", return_value=settings) as mk_wizard, \ - patch.object(qwen.tui, "flash") as mk_flash, \ patch.object(qwen.taskview, "run_steps") as mk_run: rc = qwen.setup_screen(None) self.assertEqual(rc, 0) mk_wizard.assert_called_once() - mk_flash.assert_called_once() mk_run.assert_not_called() def test_missing_package_runs_the_tail_in_the_task_view(self): diff --git a/app/tests/test_backends_audiocpp.py b/app/tests/test_backends_audiocpp.py index 8cd7981..37745f3 100644 --- a/app/tests/test_backends_audiocpp.py +++ b/app/tests/test_backends_audiocpp.py @@ -1963,6 +1963,87 @@ class WizardNavigationTests(unittest.TestCase): self.assertFalse(settings["download"]) # no manager script here self.assertTrue(settings["sync_model_ids"]) + def test_tree_screen_starts_on_confirm(self): + # The model-tree screen opens with focus on Confirm so Enter + # accepts the seeded/checked selection immediately. + checkout = self._checkout() + catalog = make_server.catalog.load_model_catalog(checkout) + qwen3 = next(i for i, entry in enumerate(catalog) + if entry["family"] == "qwen3_tts") + captured = {} + + def fake_tree(*args, **kwargs): + captured.update(kwargs) + return [(qwen3, "Qwen3-TTS-12Hz-1.7B-Base-GGUF")] + + def fake_form(stdscr, title, fields, **kwargs): + return {f["key"]: f["value"] for f in fields} + + with patch.object(make_server.build, "find_local_checkout", + return_value=checkout), \ + patch.object(tui, "checkbox_tree", + side_effect=fake_tree), \ + patch.object(tui, "form", side_effect=fake_form): + make_server.wizard._wizard(None, self._args(), + make_server.wizard.build_parser()) + self.assertTrue(captured.get("start_on_buttons")) + + def test_wav_dir_seeded_from_existing_voice_dir(self): + # A modify run loads the Voice clone .wav directory from the + # server.json being configured instead of starting blank. + checkout = self._checkout() + recorded_voices = checkout.parent / "recorded-voices" + (checkout / "server.json").write_text(json.dumps({ + "host": "127.0.0.1", "port": 8080, "backend": "cuda", + "models": [], "voice_dir": str(recorded_voices), + }), encoding="utf-8") + catalog = make_server.catalog.load_model_catalog(checkout) + qwen3 = next(i for i, entry in enumerate(catalog) + if entry["family"] == "qwen3_tts") + + def fake_tree(*args, **kwargs): + return [(qwen3, "Qwen3-TTS-12Hz-1.7B-Base-GGUF")] + + def fake_form(stdscr, title, fields, **kwargs): + by_key = {f["key"]: f for f in fields} + self.assertEqual(by_key["wav_dir"]["value"], + Path(recorded_voices)) + return {f["key"]: f["value"] for f in fields} + + with patch.object(make_server.build, "find_local_checkout", + return_value=checkout), \ + patch.object(tui, "checkbox_tree", + side_effect=fake_tree), \ + patch.object(tui, "form", side_effect=fake_form): + make_server.wizard._wizard(None, self._args(), + make_server.wizard.build_parser()) + + def test_wav_dir_defaults_to_project_voices_when_unconfigured(self): + # Without a voice_dir in server.json the field starts on the + # project's voices/ directory — never blank. + checkout = self._checkout() + (checkout / "server.json").write_text( + json.dumps({"models": []}), encoding="utf-8") + catalog = make_server.catalog.load_model_catalog(checkout) + qwen3 = next(i for i, entry in enumerate(catalog) + if entry["family"] == "qwen3_tts") + + def fake_tree(*args, **kwargs): + return [(qwen3, "Qwen3-TTS-12Hz-1.7B-Base-GGUF")] + + def fake_form(stdscr, title, fields, **kwargs): + by_key = {f["key"]: f for f in fields} + self.assertEqual(by_key["wav_dir"]["value"], common.VOICES_DIR) + return {f["key"]: f["value"] for f in fields} + + with patch.object(make_server.build, "find_local_checkout", + return_value=checkout), \ + patch.object(tui, "checkbox_tree", + side_effect=fake_tree), \ + patch.object(tui, "form", side_effect=fake_form): + make_server.wizard._wizard(None, self._args(), + make_server.wizard.build_parser()) + def test_build_offer_hidden_when_backend_already_built(self): # A checkout with a built binary for the chosen backend must not # show (or honor) a build offer. diff --git a/app/tests/test_hub.py b/app/tests/test_hub.py index 96e4706..6657540 100644 --- a/app/tests/test_hub.py +++ b/app/tests/test_hub.py @@ -369,11 +369,11 @@ class SubmenuStatusTableTests(unittest.TestCase): return_value="/usr/bin/ffmpeg"): result = hub._Hub(None).screen_configure() self.assertIs(result, tui.Wizard.BACK) - # Configure (qwen installed), Install (faster uninstalled), then - # Uninstall (qwen installed); no audio.cpp means no model actions. + # Install (faster uninstalled), then Uninstall (qwen installed); + # no audio.cpp means no model actions. qwen has no Configure entry + # (its wizard asks nothing to configure). self.assertEqual([label for label, _ in captured["options"]], - ["Configure qwen-tts", "Install Backend", - "Uninstall Backend"]) + ["Install Backend", "Uninstall Backend"]) # ...the shared status table carries the states instead. self.assertEqual(captured["table_title"], "Backend status") self.assertEqual( diff --git a/app/tests/test_tui.py b/app/tests/test_tui.py index b15419b..b839a71 100644 --- a/app/tests/test_tui.py +++ b/app/tests/test_tui.py @@ -981,6 +981,26 @@ class CheckboxTreeTests(TuiTestCase): checked={(0, "pkg-b"), (1, "pkg-c")}) self.assertEqual(picked, [(0, "pkg-b"), (1, "pkg-c")]) + def test_start_on_buttons_confirms_immediately(self): + # start_on_buttons=True (the seeded modify flow): Enter alone + # accepts the pre-checked tree with no Tab first. + screen = FakeScreen(keys=[10]) + picked = tui.checkbox_tree( + screen, "Pick models", self.FAMILIES, + checked={(0, "pkg-b"), (1, "pkg-c")}, start_on_buttons=True) + self.assertEqual(picked, [(0, "pkg-b"), (1, "pkg-c")]) + + def test_start_on_buttons_without_selection_flashes(self): + # Focus starting on Confirm changes nothing else: Enter with an + # empty tree flashes and stays (the flash dismiss consumes one + # scripted key); the user then moves back to the rows, checks + # one, and confirms for real. + keys = [10, 9, 9, 10, 9, 10] + screen = FakeScreen(keys=keys) + picked = tui.checkbox_tree(screen, "Pick models", self.FAMILIES, + start_on_buttons=True) + self.assertEqual(picked, [(0, "pkg-a")]) + def test_prechecked_options_draw_as_checked(self): screen = FakeScreen(keys=[9, 10]) tui.checkbox_tree(screen, "Pick models", self.FAMILIES, -- cgit v1.2.3