from __future__ import annotations import glob import os import unittest from pathlib import Path import soundfile as sf import app from src.lyrics_generator import generate_micro_lyrics from src.music_generator import generate_music from src.safety import sanitize_generated_lyrics from src.tts import generate_dj_voice class TurntableTimeMachineLogicTests(unittest.TestCase): def tearDown(self): os.environ.pop("TTM_TEST_MODEL_CALLS", None) os.environ.pop("TTM_DISABLE_TTS_MODEL", None) patterns = [ "outputs/test_*.wav", "outputs/final_mix_*.wav", "outputs/music_raw_*.wav", "outputs/music_processed_*.wav", "outputs/dj_intro_*.wav", ] for path in [item for pattern in patterns for item in glob.glob(pattern)]: try: Path(path).unlink() except OSError: pass def test_taxonomy_files_loaded(self): self.assertGreaterEqual(len(app.ERAS), 4) self.assertGreaterEqual(len(app.PERSONAS), 3) self.assertGreaterEqual(len(app.TEXTURES), 4) self.assertGreaterEqual(len(app.LANGUAGES), 4) self.assertGreaterEqual(len(app.THEMES), 4) def test_genre_dropdowns_update_from_selected_era(self): source_update = app.update_source_genres("1980s") remix_update = app.update_remix_genres("2020s") self.assertIn("Synth-pop", source_update["choices"]) self.assertIn("Amapiano-inspired groove", remix_update["choices"]) self.assertEqual(source_update["value"], source_update["choices"][0]) self.assertEqual(remix_update["value"], remix_update["choices"][0]) def test_lyric_theme_visibility_tracks_vocal_mode(self): self.assertTrue(app.update_lyric_theme_visibility("Original micro-lyrics")["visible"]) self.assertFalse(app.update_lyric_theme_visibility("Instrumental only")["visible"]) self.assertFalse(app.update_lyric_theme_visibility("Wordless vocal texture")["visible"]) def test_micro_lyrics_sanitizer_removes_instruction_text(self): clean, warnings = sanitize_generated_lyrics( "No explanation, no punctuation\n" "Vapor dreams drift on neon haze\n" "We chase tomorrow through glass streets" ) self.assertEqual(clean, "Vapor dreams drift on neon haze\nWe chase tomorrow through glass streets") self.assertTrue(any("instruction" in warning for warning in warnings)) def test_compact_control_css_does_not_restyle_radio_inputs(self): self.assertIn('input:not([type="radio"]):not([type="checkbox"])', app.CSS) self.assertIn('input[type="radio"]', app.CSS) def test_surprise_me_returns_complete_valid_state(self): result = app.surprise_me() self.assertEqual(len(result), 15) self.assertIn(result[0], app.ERA_LABEL_TO_ID) self.assertIn(result[2], app.ERA_LABEL_TO_ID) self.assertIn(result[4], app.PERSONA_LABEL_TO_ID) self.assertIn(result[5], app.TEXTURE_LABEL_TO_ID) self.assertIn(result[7], app.LANGUAGE_LABEL_TO_ID) self.assertIn(result[8], app.PROMPT_MODES) self.assertIn(result[9], app.VOCAL_MODES) self.assertIn(result[11], app.DURATIONS) self.assertIn("era-card", result[14]) def test_prompt_mode_labels_are_plain_language(self): self.assertEqual( app.PROMPT_MODES, ["Best stability (English)", "Broadcast language", "English + broadcast language"], ) def test_music_wrapper_attempts_model_by_default_then_falls_back(self): result = generate_music( "Create an original music clip. No vocals. No lyrics.", duration=1, bpm=110, seed=101, output_path="outputs/test_default_model_attempt.wav", ) self.assertTrue(result["model_attempted"]) self.assertEqual(result["model_id"], "ACE-Step/Ace-Step1.5") self.assertTrue(result["fallback_used"]) audio, sr = sf.read(result["path"]) self.assertEqual(sr, 44100) self.assertGreater(audio.shape[0], 0) def test_fallback_music_varies_by_prompt_route(self): first = generate_music( "Create an original music clip. Start from 1960s Motown-inspired soul. Transform it into 2000s filtered disco house.", duration=1, bpm=118, seed=123, output_path="outputs/test_route_one.wav", ) second = generate_music( "Create an original music clip. Start from 2020s lo-fi chill. Transform it into 1970s funk.", duration=1, bpm=118, seed=123, output_path="outputs/test_route_two.wav", ) audio_one, _ = sf.read(first["path"]) audio_two, _ = sf.read(second["path"]) self.assertGreater(float(abs(audio_one - audio_two).mean()), 0.001) def test_audio_model_calls_can_be_exercised_with_test_switch(self): os.environ["TTM_TEST_MODEL_CALLS"] = "1" music = generate_music( "Create an original music clip. Optional wordless, non-lexical vocal texture only.", duration=1, bpm=122, vocal_mode="Wordless vocal texture", seed=202, output_path="outputs/test_model_music.wav", ) tts = generate_dj_voice( "Signal detected. This is a fictional DJ intro.", "en", "neon_signal_host", "outputs/test_model_tts.wav", ) lyrics = generate_micro_lyrics( app.ERAS[2], app.ERAS[2]["genres"][0], app.ERAS[4], app.ERAS[4]["genres"][0], "nostalgic and warm", app.THEMES[0], "en", seed=303, ) self.assertFalse(music["fallback_used"]) self.assertEqual(music["vocal_rendering_status"], "wordless") self.assertTrue(tts["tts_succeeded"]) self.assertFalse(lyrics["fallback_used"]) self.assertIn("simulated for tests", music["status"]) self.assertIn("simulated for tests", tts["status"]) self.assertEqual(tts["model_id"], "facebook/mms-tts-eng") self.assertIn("simulated for tests", lyrics["status"]) def test_tts_model_routes_by_language(self): os.environ["TTM_TEST_MODEL_CALLS"] = "1" tts = generate_dj_voice( "Bonsoir, voyageurs du temps.", "fr", "warehouse_announcer", "outputs/test_model_tts_fr.wav", ) self.assertTrue(tts["tts_succeeded"]) self.assertEqual(tts["model_id"], "facebook/mms-tts-fra") def test_generate_pipeline_all_vocal_modes_create_final_audio(self): os.environ["TTM_DISABLE_TTS_MODEL"] = "1" for index, vocal_mode in enumerate(app.VOCAL_MODES): with self.subTest(vocal_mode=vocal_mode): result = app.generate_time_machine_mix( "1980s", "Synth-pop", "2000s", "Filtered disco house", "The Neon Signal Host", "Clean digital master", "nostalgic and warm", "English", "Best stability (English)", vocal_mode, "Night drive", 1, 400 + index, False, ) ( final_path, music_path, intro_path, intro_status, _intro_text, lyrics_text, prompt, _card, status, ) = result[:9] self.assertTrue(Path(final_path).exists()) self.assertTrue(Path(music_path).exists()) self.assertIsNone(intro_path) self.assertIn("Spoken DJ intro is unavailable", intro_status) self.assertIn("ACE-Step", status) self.assertIn("Spoken DJ intro unavailable", status) audio, sr = sf.read(final_path) self.assertEqual(sr, 44100) self.assertGreater(audio.shape[0], 0) if vocal_mode == "Original micro-lyrics": self.assertGreaterEqual(len(lyrics_text.splitlines()), 2) self.assertIn("Lyrics:", prompt) elif vocal_mode == "Wordless vocal texture": self.assertIn("non-lexical vocal texture", prompt) else: self.assertIn("No vocals. No lyrics.", prompt) def test_generate_pipeline_uses_mocked_model_outputs_when_available(self): os.environ["TTM_TEST_MODEL_CALLS"] = "1" result = app.generate_time_machine_mix( "1980s", "Synth-pop", "2000s", "Filtered disco house", "The Neon Signal Host", "Clean digital master", "nostalgic and warm", "English", "Best stability (English)", "Original micro-lyrics", "Night drive", 1, 777, False, ) ( final_path, music_path, intro_path, intro_status, _intro_text, lyrics_text, prompt, _card, status, ) = result[:9] self.assertTrue(Path(final_path).exists()) self.assertTrue(Path(music_path).exists()) self.assertTrue(Path(intro_path).exists()) self.assertEqual(intro_status, "Spoken DJ intro generated.") self.assertIn("ACE-Step model call simulated", status) self.assertIn("MMS TTS spoken-DJ model call simulated", status) self.assertIn("tiny-aya-global text model call simulated", status) self.assertIn("Lyrics:", prompt) self.assertGreaterEqual(len(lyrics_text.splitlines()), 2) if __name__ == "__main__": unittest.main()