summaryrefslogtreecommitdiff
path: root/tests/test_compose_mml_tool.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_compose_mml_tool.py')
-rw-r--r--tests/test_compose_mml_tool.py153
1 files changed, 153 insertions, 0 deletions
diff --git a/tests/test_compose_mml_tool.py b/tests/test_compose_mml_tool.py
new file mode 100644
index 0000000..af1e3e2
--- /dev/null
+++ b/tests/test_compose_mml_tool.py
@@ -0,0 +1,153 @@
+"""Exhaustive tests for the compose_mml MCP tool."""
+
+from pathlib import Path
+
+VALID_PPMCK = """#TITLE "Test"
+A t120 l4 o4 v15 q2
+ c e g c
+B o3
+ c e g
+T l4 o3 v7
+ c2 g2
+N l4 v10
+ c r c r
+"""
+
+VALID_PYXEL = """0: T120 L4 O4 V100 @1
+ C E G C
+1: O3
+ C E G
+2: L4 O3 V60
+ C2 G2
+3: L4 V80
+ C R C R
+"""
+
+
+def test_compose_ppmck(compose_mml, tmp_output_dir):
+ result = compose_mml(action="compose", mml=VALID_PPMCK, mode="ppmck")
+ assert result["success"] is True
+ assert result["wav_path"] is not None
+ assert Path(result["wav_path"]).exists()
+ assert result["duration_sec"] > 0
+ assert result["note_sequence"] is not None
+ assert "validation" in result
+ assert "errors" in result["validation"]
+ assert "warnings" in result["validation"]
+
+
+def test_compose_pyxel(compose_mml, tmp_output_dir):
+ result = compose_mml(action="compose", mml=VALID_PYXEL, mode="pyxel")
+ assert result["success"] is True
+ assert result["wav_path"] is not None
+ assert Path(result["wav_path"]).exists()
+ assert result["duration_sec"] > 0
+
+
+def test_validate_ppmck(compose_mml):
+ result = compose_mml(action="validate", mml=VALID_PPMCK, mode="ppmck")
+ assert result["valid"] is True
+ assert "warnings" in result
+ assert result["channel_summary"]
+ for ch in result["channel_summary"]:
+ assert "channel" in ch
+ assert "note_count" in ch
+ assert "octave_range" in ch
+ assert "duration_ticks" in ch
+
+
+def test_validate_pyxel(compose_mml):
+ result = compose_mml(action="validate", mml=VALID_PYXEL, mode="pyxel")
+ assert result["valid"] is True
+ assert "warnings" in result
+ assert result["channel_summary"]
+
+
+def test_template_all(compose_mml):
+ for mode in ("ppmck", "pyxel"):
+ for template in ("basic", "melody", "chord", "drum", "empty"):
+ result = compose_mml(action="template", mode=mode, template=template)
+ assert "mml" in result
+ assert "description" in result
+ assert result["mml"]
+ assert result["description"]
+ validated = compose_mml(action="validate", mml=result["mml"], mode=mode)
+ assert validated["valid"] is True, (
+ f"{mode}/{template} template invalid: {validated['errors']}"
+ )
+
+
+def test_template_invalid_fallback(compose_mml):
+ result = compose_mml(action="template", mode="pyxel", template="unknown")
+ assert result["description"] == "基本的な4ch構成(メロディ+和音+ベース+リズム)"
+
+
+def test_compose_missing_mml(compose_mml):
+ result = compose_mml(action="compose", mml="", mode="ppmck")
+ assert result["success"] is False
+ assert any(e["severity"] == "error" for e in result["errors"])
+
+
+def test_compose_missing_mode(compose_mml):
+ result = compose_mml(action="compose", mml="A c", mode="")
+ assert result["success"] is False
+ assert any(e["severity"] == "error" for e in result["errors"])
+
+
+def test_validate_missing_mode(compose_mml):
+ result = compose_mml(action="validate", mml="A c", mode="")
+ assert result["valid"] is False
+
+
+def test_compose_unknown_mode(compose_mml):
+ result = compose_mml(action="compose", mml="A c", mode="xyz")
+ assert result["success"] is False
+ assert any(
+ e["code"] == "SYNTAX_INVALID_TOKEN" for e in result["validation"]["errors"]
+ )
+
+
+def test_compose_with_syntax_error(compose_mml):
+ result = compose_mml(action="compose", mml="A t0\n c", mode="ppmck")
+ assert result["success"] is False
+ assert result["wav_path"] is None
+ assert result["duration_sec"] == 0
+ assert any(e["severity"] == "error" for e in result["validation"]["errors"])
+
+
+def test_compose_with_warning(compose_mml, tmp_output_dir):
+ mml = """2: T120 L4 O3 V60 @1
+ C4
+"""
+ result = compose_mml(action="compose", mml=mml, mode="pyxel")
+ assert result["success"] is True
+ assert result["validation"]["warnings"]
+
+
+def test_action_invalid(compose_mml):
+ result = compose_mml(action="unknown")
+ assert result["success"] is False
+ assert any("action" in e["message"] for e in result["errors"])
+
+
+def test_compose_sample_rates(compose_mml, tmp_output_dir):
+ for rate in (22050, 44100, 48000):
+ result = compose_mml(
+ action="compose",
+ mml="A t120 l4 o4 v15 q2\n c4",
+ mode="ppmck",
+ sample_rate=rate,
+ )
+ assert result["success"] is True, f"sample_rate={rate} failed"
+ assert result["duration_sec"] > 0
+
+
+def test_compose_normalize(compose_mml, tmp_output_dir):
+ base = """0: T120 L4 O4 V100 @1
+ C4
+"""
+ on = compose_mml(action="compose", mml=base, mode="pyxel", normalize=True)
+ off = compose_mml(action="compose", mml=base, mode="pyxel", normalize=False)
+ assert on["success"] and off["success"]
+ assert Path(on["wav_path"]).exists()
+ assert Path(off["wav_path"]).exists()