diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_lexer.py | 27 | ||||
| -rw-r--r-- | tests/test_parser.py | 53 | ||||
| -rw-r--r-- | tests/test_server.py | 33 |
3 files changed, 113 insertions, 0 deletions
diff --git a/tests/test_lexer.py b/tests/test_lexer.py new file mode 100644 index 0000000..1bb2213 --- /dev/null +++ b/tests/test_lexer.py @@ -0,0 +1,27 @@ +"""Tests for the MML lexer.""" + +from mml_composemusic_mcp.lexer import TokenType, tokenize + + +def test_ppmck_tokens(): + source = "A t150 l8 o4 v15 q2\nc d e f" + tokens = tokenize(source, "ppmck") + types = [t.type for t in tokens] + assert TokenType.TRACK_HEADER in types + assert TokenType.TEMPO in types + assert TokenType.NOTE in types + + +def test_pyxel_tokens(): + source = "0: T150 L8 O4 V100 @1\nC D E F" + tokens = tokenize(source, "pyxel") + types = [t.type for t in tokens] + assert TokenType.TRACK_HEADER in types + assert TokenType.AT in types + assert TokenType.NOTE in types + + +def test_pyxel_repeat(): + tokens = tokenize("[ C D ]2", "pyxel") + assert any(t.type == TokenType.REPEAT_START for t in tokens) + assert any(t.type == TokenType.REPEAT_END for t in tokens) diff --git a/tests/test_parser.py b/tests/test_parser.py new file mode 100644 index 0000000..b263aac --- /dev/null +++ b/tests/test_parser.py @@ -0,0 +1,53 @@ +"""Tests for PPMCK and Pyxel parsers.""" + +from mml_composemusic_mcp.ir import NoteEvent +from mml_composemusic_mcp.lexer import tokenize +from mml_composemusic_mcp.parser_ppmck import parse_ppmck +from mml_composemusic_mcp.parser_pyxel import parse_pyxel + +PPMCK_EXAMPLE = """#TITLE "Test" +A t150 l8 o4 v15 q2 + c d e f +B o3 + c e g +""" + +PYXEL_EXAMPLE = """0: T150 L8 O4 V100 @1 + C D E F +1: O3 + C E G +""" + + +def test_ppmck_parse(): + tokens = tokenize(PPMCK_EXAMPLE, "ppmck") + ns, errors = parse_ppmck(PPMCK_EXAMPLE, tokens) + assert not any(e.severity == "error" for e in errors) + assert ns.channels["Pulse1"].events + assert ns.channels["Pulse2"].events + notes = [e for e in ns.channels["Pulse1"].events if isinstance(e, NoteEvent)] + assert notes[0].note_number == 60 # C4 + + +def test_pyxel_parse(): + tokens = tokenize(PYXEL_EXAMPLE, "pyxel") + ns, errors = parse_pyxel(PYXEL_EXAMPLE, tokens) + assert not any(e.severity == "error" for e in errors) + assert ns.channels["Pulse1"].events + notes = [e for e in ns.channels["Pulse1"].events if isinstance(e, NoteEvent)] + assert notes[0].note_number == 60 # C4 + + +def test_pyxel_repeat_expansion(): + source = "0: T120 L4 O4\n[ C D ]2" + tokens = tokenize(source, "pyxel") + ns, errors = parse_pyxel(source, tokens) + notes = [e for e in ns.channels["Pulse1"].events if isinstance(e, NoteEvent)] + assert len(notes) == 4 + + +def test_invalid_mode_error(): + from mml_composemusic_mcp.server import _parse_mml + + seq, errors = _parse_mml("A c", "unknown") + assert any(e.severity == "error" for e in errors) diff --git a/tests/test_server.py b/tests/test_server.py new file mode 100644 index 0000000..533c88b --- /dev/null +++ b/tests/test_server.py @@ -0,0 +1,33 @@ +"""Tests for MCP server compose_mml tool.""" + +from mml_composemusic_mcp.server import compose_mml + +PPMCK_MML = """#TITLE "Test" +A t120 l4 o4 v15 q2 + c e g c +""" + + +def test_compose_ppmck(): + result = compose_mml(action="compose", mml=PPMCK_MML, mode="ppmck") + assert result["success"] is True + assert result["wav_path"] is not None + assert result["duration_sec"] > 0 + + +def test_validate_ppmck(): + result = compose_mml(action="validate", mml=PPMCK_MML, mode="ppmck") + assert result["valid"] is True + assert result["channel_summary"] + + +def test_template(): + result = compose_mml(action="template", mode="pyxel", template="basic") + assert "mml" in result + assert "0:" in result["mml"] + + +def test_compose_error(): + result = compose_mml(action="compose", mml="A t0\n c", mode="ppmck") + assert result["success"] is False + assert any(e["severity"] == "error" for e in result["validation"]["errors"]) |
