- docs/mame-autotest.md — исчерпывающий документ: запуск, ввод команд, скриншоты, завершение сессий, анализ, раскладка клавиатуры, все квирки. Одного этого документа достаточно, чтобы работать с MAME в режиме автотестирования. - mame_interactive.py теперь единственный инструмент: авто-запускает exe вводом пути (a:\<exe>+Enter), --step опционален (доп. ввод в программу), умные дефолты снимков/таймаута. - удалён mame_auto_test.py (старый метод через AUTORUN.BAT chainload) и все упоминания AUTORUN.BAT в доках; интерактивный ввод его заменил. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
14 KiB
Автотестирование в MAME
Единый справочник: как запускать программы Sprinter в эмуляторе MAME без участия человека, вводить команды, снимать скриншоты, завершать сессию и анализировать результат. Если нужно что-то про автотесты в MAME — смотреть сюда.
Весь механизм собран в одном инструменте: toolchain/mame_interactive.py.
1. TL;DR
# собрать .exe (пример)
make -C tests/bgitest
# запустить в MAME, снять экран, выйти по таймауту
python3 toolchain/mame_interactive.py tests/bgitest/bgitest.exe \
--snap 12,14 --timeout 16
Инструмент сам:
- проверит, что нет висящих копий MAME;
- положит
.exeна дискету A: (mame/v306/IMG/mc.img); - запустит MAME с драйвером
sprinter; - дождётся приглашения
C:\>и напечатаетa:\bgitest.exe+ Enter (эмулируя нажатия клавиш); - снимет скриншоты в указанные секунды эмулированного времени;
- завершит сессию по таймауту;
- выведет пути к PNG-скриншотам.
Скриншоты лежат в mame/v306/snap_auto/sprinter/ (0000.png, 0001.png,
…). Их читает Claude визуально (инструментом Read) — текст с экрана
программно НЕ распознаётся.
2. Инструмент: mame_interactive.py
python3 toolchain/mame_interactive.py [exe] [--data f ...] \
[--launch-at T] [--step "T:TEXT" ...] [--snap t1,t2,...] [--timeout N]
| Аргумент | Назначение |
|---|---|
exe |
.exe кладётся на A: и авто-запускается (печатается a:\<exe>+Enter в момент --launch-at). Без exe работаем на голой командной строке. |
--data f ... |
доп. файлы на дискету A: (данные для теста). |
--launch-at T |
секунда, когда печатается запуск exe (по умолчанию 8). |
--step "T:TEXT" |
в момент T сек напечатать TEXT. Можно много раз — диалог с уже запущенной программой. В TEXT: \n=Enter, \t=Tab; заглавные и символы через Shift — автоматически. |
--snap t1,t2,... |
секунды эмуляции для скриншотов. По умолчанию: launch_at+4 и +6 (для exe), либо сразу после последнего ввода. |
--timeout N |
секунд эмуляции до принудительного выхода. По умолчанию — чуть позже последнего скриншота. |
Важно: все времена — это секунды эмулированного времени от старта
машины (не от нажатий, их «нет»). Загрузка DSS до C:\> занимает
~7 секунд, поэтому --launch-at 8 и скриншоты с ~12 с.
Типовые рецепты
# 1. Запустить тест и снять результат (самый частый случай)
python3 toolchain/mame_interactive.py tests/rt_test/rt_test.exe \
--snap 12,14 --timeout 16
# 2. Набрать команду на голой командной строке (без exe)
python3 toolchain/mame_interactive.py --step "8:dir\n" \
--snap 10,11 --timeout 12
# 3. Запустить программу и ответить на её ввод (например, выбор пункта меню)
python3 toolchain/mame_interactive.py tests/menu/menu.exe \
--step "13:2\n" --snap 15 --timeout 17
# 4. Тест с файлом-данными на дискете
python3 toolchain/mame_interactive.py examples/mdview2/mdview2.exe \
--data doc.md --step "13:mdview2 doc.md\n" --snap 16 --timeout 18
3. Как это работает внутри
3.1 Запуск MAME
Нативный mame.arm (arm64) в mame/v306/, драйвер sprinter, BIOS
v3.06. Полный набор аргументов зашит в COMMON_ARGS внутри скрипта:
две дискеты (A: наш mc.img, B: DSS 1.71u), два HDD-образа (система +
медиа), CD-ROM, ZX-Bus карта NeoGS, -video opengl -window,
-snapshot_directory, -autoboot_script <сгенерированный .lua>.
3.2 Ввод с клавиатуры — ключевой момент
У Sprinter в MAME две клавиатуры:
IO_LINE0..7— легаси ZX-Spectrum-матрица (порт0xFE). DSS её для командной строки НЕ читает.root:kbd:ms_naturl— настоящая AT/PS-2 клавиатура, подключённая последовательно к SIO Z84C015 (sprinter.cpp:2037). Именно её DSS читает как поток scancode'ов.
Поэтому не работают (проверено многократно): natkeyboard:post,
-autoboot_command, а также set_value по полям :IO_LINE*. Всё это
бьёт в ZX-матрицу, которую DSS игнорирует.
Работает — прямое управление полями AT-клавиатуры из Lua:
manager.machine.ioport.ports[":kbd:ms_naturl:P1.4"].fields["D"]:set_value(1) -- нажать
... подождать ~0.06с ...
manager.machine.ioport.ports[":kbd:ms_naturl:P1.4"].fields["D"]:set_value(0) -- отпустить
at_keyboard сам сгенерит make/break scancode'ы → SIO → DSS.
Скрипт хранит раскладку char → (порт, битовая маска) (словарь PHYS +
SHIFTED для Shift-символов) и разворачивает строку в список
timed-событий (время, порт, маска, значение). Backslash \ в
AT-клавиатуре есть (поле P2.1/0x4) — путь a:\name.exe вводится
полностью.
3.3 Тайминг (Lua)
Генерируется _interactive_gen.lua, который через
emu.register_periodic на каждом кадре сверяет эмулированное время и
проигрывает события ввода, снимает скриншоты и завершает сессию.
Время берётся как t.seconds + t.attoseconds/1e18, потому что
attotime.seconds — целое (дробную часть отбрасывает); если считать
по нему, все события схлопнутся в 1-секундную сетку.
Старт отсчёта — emu.add_machine_reset_notifier (НЕ emu.register_start
— он deprecated).
3.4 Скриншоты
manager.machine.video:snapshot() пишет PNG в каталог из
-snapshot_directory (mame/v306/snap_auto/sprinter/). Скрипт перед
запуском чистит этот каталог, потом печатает пути к готовым файлам.
Claude открывает их инструментом Read (визуальный анализ).
3.5 Завершение сессии
Два рубежа, чтобы MAME гарантированно не «завис»:
- в Lua: при
elapsed >= timeout→manager.machine:exit()(чистый выход); - снаружи: shell-обёртка
timeout <N+8> ./mame.arm …как страховка.
4. Предпосылки (окружение)
- MAME:
mame/v306/mame.arm+ образы вmame/v306/IMG/(mc.img— наш перезаписываемый A:,dss171u.img, HDD.chd, CD.iso). Каталогmame/целиком в.gitignore— поэтому инструмент живёт вtoolchain/, а не рядом с MAME. - Загрузка должна доходить до
C:\>.system.batна системном диске НЕ должен автоматически запускать Flex Navigator или приложение — иначе мы не попадём на командную строку и ввод уйдёт в чужую программу. (Это файл на HDD-образе, вне репозитория; правится один раз.) - Нет висящих копий MAME. Несколько одновременных инстансов пишут в
один
mc.imgи дают недостоверный результат. Скрипт проверяет это сам (pgrep), но при ручных запусках MAME — проверяйтеps aux | grep mame.
5. Как выбирать времена
- Загрузка до
C:\>: ~7 секунд →--launch-at 8безопасно. - Набор пути
a:\name.exe: ~13 символов × 0.14с ≈ 1.8с → команда уходит около 9.8с, программа стартует ~10с. - Скриншот: давайте программе дорисоваться. Быстрая программа —
снимать с ~12с; если рисует долго/по частям, снимайте несколько кадров
(
--snap 12,16,20) и смотрите, где картинка «дособралась». - Диалог с программой (
--step): времена шагов ставьте ПОСЛЕ старта программы (например, запуск на 8с, ответ на ввод на 13–15с).
6. Анализ результата
- Скриншоты — единственный способ проверки: программного чтения текстового/графического VRAM нет, OCR нет. Claude читает PNG глазами (инструмент Read с картинкой).
- Лог MAME фильтруется по строкам
[interactive](моменты снимков и выхода) — видно, в какие секунды сделаны кадры. - Если картинка «не дособралась» — снять более поздний кадр (увеличить
--snap/--timeout).
7. Раскладка клавиатуры (справочно)
Раскладка снята дампом ioport-полей :kbd:ms_naturl:* живой машины.
Она зашита в PHYS/SHIFTED внутри mame_interactive.py. Поддержаны:
буквы (a–z, A–Z через Shift), цифры, пробел, Enter (\n), Tab (\t),
и символы - = [ ] \ ; ' , . / ` плюс их Shift-версии
! @ # $ % ^ & * ( ) _ + { } | : " < > ? ~.
Если понадобится клавиша вне списка — снять её поле дампом (пример
Lua-пробы ниже) и добавить в PHYS:
-- дамп всех полей клавиатуры в лог
for tag, port in pairs(manager.machine.ioport.ports) do
if tostring(tag):find("kbd") then
for fname, field in pairs(port.fields) do
print(string.format("%s mask=0x%x %q", tag, field.mask, fname))
end
end
end
8. Квирки и грабли (все, на которые уже наступали)
attotime.seconds— целое. Субсекундный тайминг только черезseconds + attoseconds/1e18.- Автоповтор (typematic). Клавишу держать коротко (~0.06с). Если
держать ~1с —
dпревратится вdddddd. - Слипание scancode'ов. Между символами ~0.14с.
- Не та клавиатура. Ввод — только в
:kbd:ms_naturl, НЕ в:IO_LINE*, НЕ черезnatkeyboard/-autoboot_command. - Загрузка мимо
C:\>. Еслиsystem.batчто-то автозапускает — ввод уходит в чужую программу; вернуть чистую командную строку. - Висящие копии MAME. Всегда проверять перед запуском.
- macOS-специфика (справочно): известный баг MAME
(mamedev/mame#10612 — потеря ввода в fullscreen при движении мыши на
старте) к нам НЕ относится: работаем в
-window, ввод скриптовый.
9. На будущее (заметки, ещё не в инструменте)
- Быстрый накопитель. Тестам, которым важна скорость диска (напр.
потоковое чтение), имеет смысл копировать файлы с медленной дискеты A:
на HDD
C:\TEMPперед запуском. - Видео+звук. MAME умеет писать AVI (
-aviwrite) — для тестов с анимацией/звуком, где скриншотов мало. Пока не подключено к скрипту. - Ручная отладка ввода. Запуск MAME с
-consoleдаёт интерактивный Lua-REPL — удобно нащупывать поля/тайминги вживую перед скриптованием. - Второй видеорежим/варианты BIOS — при необходимости менять
COMMON_ARGS.