Temporal_Exploration / tests /test_rhythma_ui_copy.py
ciaochris's picture
Make automatic Tone Center read as automatic in examples
93d65fd
import importlib
import sys
import types
from unittest import mock
import pytest
def import_app_with_gradio_stub():
sys.modules.pop("app", None)
sys.modules["gradio"] = types.ModuleType("gradio")
return importlib.import_module("app")
def import_app_without_gradio():
original_import = __import__
def blocked_import(name, globals=None, locals=None, fromlist=(), level=0):
if name == "gradio":
raise ModuleNotFoundError("No module named 'gradio'")
return original_import(name, globals, locals, fromlist, level)
sys.modules.pop("app", None)
sys.modules.pop("gradio", None)
with mock.patch("builtins.__import__", side_effect=blocked_import):
return importlib.import_module("app")
def test_normalize_rhythm_override_treats_automatic_as_none():
app = import_app_with_gradio_stub()
assert app.normalize_rhythm_override("Automatic") is None
def test_session_examples_leave_tone_center_blank_for_automatic_mode():
app = import_app_with_gradio_stub()
assert all(example[2] is None for example in app.SESSION_EXAMPLES)
def test_build_session_copy_uses_human_labels():
app = import_app_with_gradio_stub()
analysis = {
"emotional_state": "anxious",
"session_profile": {
"title": "Grounding Tide",
"emotional_tone": "Settling and steady",
"guidance": "Let your breath fall behind the pulse until the session feels steady.",
"reflection": "This session favors stability over intensity.",
},
}
card = app.build_session_copy(analysis)
assert card["session_name"] == "Grounding Tide"
assert card["listening_path"].startswith("Settling and steady")
assert "stability over intensity" in card["session_reflection"]
def test_import_without_gradio_uses_interface_guard():
app = import_app_without_gradio()
assert app.gr is None
with pytest.raises(ModuleNotFoundError, match="gradio is required"):
app.create_interface()
def test_rhythma_experience_returns_session_led_copy(monkeypatch):
app = import_app_with_gradio_stub()
analysis = {
"emotional_state": "anxious",
"transcription": "",
"session_profile": {
"title": "Grounding Tide",
"emotional_tone": "Settling and steady",
"guidance": "Let your breath fall behind the pulse until the session feels steady.",
"reflection": "This session favors stability over intensity.",
},
}
monkeypatch.setattr(app, "analyze_input", lambda text, audio: analysis)
monkeypatch.setattr(
app,
"generate_modulated_experience",
lambda *args, **kwargs: ("legacy analysis", "session.wav", "plot", "image", "legacy symbolic"),
)
outputs = app.rhythma_experience(
"I feel anxious and need to settle down",
None,
override_freq=0,
override_modulation="sine",
override_rhythm="Automatic",
duration=5,
)
assert outputs[0] == "### Grounding Tide"
assert outputs[1] == "Settling and steady"
assert outputs[2].startswith("Settling and steady")
assert outputs[3] == "session.wav"
assert outputs[6] == "This session favors stability over intensity."
assert outputs[7] == ""
def test_rhythma_experience_degrades_copy_consistently_on_generation_failure(monkeypatch):
app = import_app_with_gradio_stub()
analysis = {
"emotional_state": "anxious",
"transcription": "",
"session_profile": {
"title": "Grounding Tide",
"emotional_tone": "Settling and steady",
"guidance": "Let your breath fall behind the pulse until the session feels steady.",
"reflection": "This session favors stability over intensity.",
},
}
monkeypatch.setattr(app, "analyze_input", lambda text, audio: analysis)
monkeypatch.setattr(
app,
"generate_modulated_experience",
lambda *args, **kwargs: ("Generation unavailable", None, None, None, "legacy symbolic"),
)
outputs = app.rhythma_experience(
"I feel anxious and need to settle down",
None,
override_freq=0,
override_modulation="sine",
override_rhythm="Automatic",
duration=5,
)
assert outputs[2] == "Generation unavailable"
assert outputs[6] == "Generation unavailable"
def test_rhythma_experience_uses_real_session_profile_across_pipeline(monkeypatch):
app = import_app_with_gradio_stub()
captured = {}
class FakeEngine:
def __init__(self, **kwargs):
captured["init_kwargs"] = kwargs
self.generated_audio = None
def render_session(self, profile, duration):
captured["render_profile"] = profile
captured["render_duration"] = duration
return "layered-session-audio"
def generate_modulated_wave(self, duration):
captured["generated_duration"] = duration
return "legacy-generated-audio"
def save_audio(self, duration, file_path):
captured["save_duration"] = duration
captured["save_path"] = file_path
self.generated_audio = self.generate_modulated_wave(duration)
return file_path
def visualize_waveform(self, duration):
captured["visualize_duration"] = duration
return "plot"
def get_waveform_image(self):
return "image"
def get_complete_analysis(self):
return "legacy analysis"
def get_symbolic_interpretation(self):
return "legacy symbolic"
monkeypatch.setattr(app, "RhythmaModulationEngine", FakeEngine)
outputs = app.rhythma_experience(
"I want to focus on deep work",
None,
override_freq=0,
override_modulation="sine",
override_rhythm="Automatic",
duration=5,
)
assert outputs[0] == "### Clear Horizon"
assert outputs[1] == "Attentive and composed"
assert outputs[2].startswith("Attentive and composed")
assert outputs[6] == "This session narrows motion to support sustained attention."
assert outputs[7] == ""
assert captured["init_kwargs"] == {
"base_freq": 512.0,
"modulation_type": "sine",
"rhythm_pattern": "focused",
"emotional_state": None,
}
assert captured["render_profile"]["title"] == "Clear Horizon"
assert captured["render_profile"]["tone_center"] == 512.0
assert captured["render_profile"]["pattern"] == "focused"
assert captured["render_duration"] == 5
assert captured["save_duration"] == 5
assert captured["visualize_duration"] == 5
def test_generate_modulated_experience_prefers_explicit_rhythm_override_without_session_profile(monkeypatch):
app = import_app_with_gradio_stub()
captured = {}
class FakeEngine:
def __init__(self, **kwargs):
captured["init_kwargs"] = kwargs
def save_audio(self, duration, file_path):
return file_path
def visualize_waveform(self, duration):
return "plot"
def get_waveform_image(self):
return "image"
def get_complete_analysis(self):
return "legacy analysis"
def get_symbolic_interpretation(self):
return "legacy symbolic"
monkeypatch.setattr(app, "RhythmaModulationEngine", FakeEngine)
analysis = {
"emotional_state": "neutral",
"rhythm_pattern": "calm",
"transcription": "",
}
result = app.generate_modulated_experience(
analysis,
modulation_type="sine",
rhythm_pattern="focused",
duration=5,
)
assert result[0] == "legacy analysis"
assert captured["init_kwargs"]["rhythm_pattern"] == "focused"