diff options
Diffstat (limited to 'tests/test_compose_mml_tool.py')
| -rw-r--r-- | tests/test_compose_mml_tool.py | 153 |
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() |
