Files
blackbird/mtc/tests/test_emit.py
T
qing 01126a5786 feat(mtc): .mt 脚本编译器落地 + pybot 扬声器静音语义
mtc 新模块:命令式动作脚本(Python DSL)→ .mt 矩阵编译器,非阻塞时间轴 IR(重叠截断/rest 静止值)+ 10ms 栅格化,产物可由 pybot/bot4cj 直接执行;34 测试全绿(含 pybot parser e2e)。

pybot speaker.py:frequency<=0 改为静音节拍(仅 sleep 保时序,不再钳到 min_f 低鸣),对齐仓颉版 sleep 占位语义;mtc Beep 通道 rest=0,段前/段间/段后回落静音,两侧语义闭环。

修复 test_actpred_compatibility 过时断言(99c1bc1 首行分解为重复微步 19→20 帧时漏改,14→15)。

文档三处同步:根 README 回填 memdb/pybot/actpred 并新增 mtc(模块表+端口一览+章节),MODULES/ARCHITECTURE 同步 mtc,新建 mtc/README.md。
2026-08-15 13:54:47 +08:00

98 lines
2.7 KiB
Python

"""emit 测试:数值格式化 / .mt 文本结构 / 用户设计示例逐字比对"""
from mtc.dsl import compile_script
from mtc.emit import fmt_num
# 用户设计示例(修正后):非阻塞时间轴,s2 第 6 行起步
EXAMPLE = '''
Servo("s1", 180, 0, 0, "pca9685-0", 0)
Servo("s2", 180, 0, 0, "pca9685-0", 1)
Speaker("spk", 2, 48000, 0.6, 5000, 20)
Action("s1", 10, 100)
Delay(50)
Action("s2", 20, 50)
'''
EXPECTED_MT = """\
@Servo
name max min offset controller channel;
s1 180 0 0 pca9685-0 0;
s2 180 0 0 pca9685-0 1;
@
@Speaker
name channels sample_rate volumn max_f min_f;
spk 2 48000 0.6 5000 20;
@
@Action
s1.angle s2.angle spk.frequency spk.delay;
1 0 0 10;
2 0 0 10;
3 0 0 10;
4 0 0 10;
5 0 0 10;
6 4 0 10;
7 8 0 10;
8 12 0 10;
9 16 0 10;
10 20 0 10;
@
"""
def test_example_matches_user_design_exactly():
text, warnings = compile_script(EXAMPLE)
assert warnings == []
assert text == EXPECTED_MT
def test_fmt_num_integer_and_decimal():
assert fmt_num(10.0) == "10"
assert fmt_num(0) == "0"
assert fmt_num(-3.0) == "-3"
assert fmt_num(1.666666) == "1.667"
assert fmt_num(2.500) == "2.5"
def test_non_integer_step_values():
"""10°/6 步:栅格值保留 3 位小数(pybot parse_mt float 可解析)"""
text, _ = compile_script(
'Servo("s1", 180, 0, 0, "pca9685-0", 0)\n'
'Speaker("spk", 2, 48000, 0.6, 5000, 20)\n'
'Action("s1", 10, 60)\n')
rows = [l for l in text.splitlines() if l.endswith("10;")]
assert len(rows) == 6
assert rows[0].startswith("1.667 ")
assert rows[-1].startswith("10 ")
def test_no_speaker_no_delay_column_and_warns():
text, warnings = compile_script(
'Servo("s1", 180, 0, 0, "pca9685-0", 0)\n'
'Action("s1", 10, 20)\n')
assert any("无 Speaker" in w for w in warnings)
assert "s1.angle;" in text # 表头无 frequency/delay 列
assert "frequency" not in text
def test_beep_gap_falls_back_to_zero_silence():
text, _ = compile_script(
'Speaker("spk", 2, 48000, 0.6, 5000, 20)\n'
'Beep(1000, 30)\n'
'Delay(50)\n') # T = max(段尾30, 游标50) = 50 → 5 行
lines = text.splitlines()
assert "spk.frequency spk.delay;" in lines
rows = [l for l in lines if l.endswith(" 10;")]
# 段内 1000;段后回落 0 = 静音(对齐 pybot speaker freq<=0 语义)
assert [r.split()[0] for r in rows] == ["1000", "1000", "1000", "0", "0"]
def test_pure_delay_script_fills_freq_zero_column():
"""纯延时:自动补 spk.frequency 恒 0 列(SpeakerVar.play 两键契约)"""
text, _ = compile_script(
'Speaker("spk", 2, 48000, 0.6, 5000, 20)\n'
'Delay(50)\n')
assert "spk.frequency spk.delay;" in text
assert text.count("0 10;") == 5