Files
Sprinter-SDCC/docs/mame-autotest.md
T
snark13 3bf50f7ff7 docs: единый справочник по автотестам в MAME; убрать метод AUTORUN.BAT
- 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>
2026-07-08 16:41:45 +03:00

14 KiB
Raw Permalink Blame History

Автотестирование в 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

Инструмент сам:

  1. проверит, что нет висящих копий MAME;
  2. положит .exe на дискету A: (mame/v306/IMG/mc.img);
  3. запустит MAME с драйвером sprinter;
  4. дождётся приглашения C:\> и напечатает a:\bgitest.exe + Enter (эмулируя нажатия клавиш);
  5. снимет скриншоты в указанные секунды эмулированного времени;
  6. завершит сессию по таймауту;
  7. выведет пути к 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 >= timeoutmanager.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.