Files
Sprinter-SDCC/docs/host-tests-plan.md
T
Александр Петров 61d4255091 testkit: модульные тесты под ucsim_z80 + планы покрытия
Обвязка для быстрых тестов plain-C логики: секунды вместо прогона в MAME,
без образа диска.  ucsim_z80 идёт в комплекте нашего SDCC — новых
зависимостей нет.

ПОЧЕМУ ПОД Z80, А НЕ ХОСТОВЫМ GCC.  У SDCC z80 int 16 бит, у хоста 32, и
расходится это НЕ в объявлениях, а в выражениях: integer promotion
повышает операнды до int независимо от того, объявлены они как uint8_t
или uint16_t.  Перевод кода на фиксированные типы разницу не убирает —
убирает только исполнение с z80-семантикой.  Побочно проверяется
кодогенерация SDCC и модули с inline-asm, которых хостовая сборка не
видит в принципе.

Устройство: crt0_ucsim.s (SP, зануление, main, halt), tcheck.* (итог в
структуру в ОЗУ), run_ucsim.py (гоняет ucsim, дампит tc_result, печатает
отчёт), host-tests.mk (общие правила).  Вывода через printf нет: тестовый
бинарь линкуется без Sprinter-libc.  Через ucsim-simif не идём — номера
его команд плавают между версиями, halt + dump работают везде.

Наборы лежат РЯДОМ с проверяемым кодом, обвязка общая:
  testkit/t_selftest.c                     — самопроверка (sizeof(int)==2)
  applications/PoP/roomtest/tests-host/    — движок PoP

Первый содержательный набор — t_geom: сверяет рукописный asm-LCG из
pop_geom.c с наивной 32-битной формулой на 128 шагах.  Заявка «бит-в-бит
как в SDLPoP» до сих пор держалась на комментарии.  Тест проверен
мутацией: порча эталонной константы даёт красный.

Планы дальнейшего покрытия:
  docs/host-tests-plan.md                    — libc и libbgi (не начато)
  applications/PoP/docs/host_tests_plan.md   — движок PoP

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 22:36:16 +03:00

130 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# План: модульные тесты libc и libbgi под ucsim_z80
Статус: **не начато**, задача на будущее. Обвязка уже готова и обкатана —
`testkit/` (см. `testkit/README.md`); первый потребитель —
`applications/PoP/roomtest/tests-host/`. Этот документ — про то, как накрыть
тем же способом основной продукт репозитория.
## Что это НЕ заменяет
В `tests/` уже лежат 62 каталога — это **интеграционные** тесты: одна фича =
одна программа, которая пакуется на дискету и гоняется в MAME
(`docs/mame-autotest.md`). Они проверяют, что API работает на живой машине:
ESTEX, BIOS, диск, экран, тайминги.
Модульные тесты их не отменяют, а дополняют с другой стороны:
| | `tests/` (MAME) | `tests-host` (ucsim) |
|---|---|---|
| что проверяет | работает ли на машине | верна ли логика |
| граничные случаи | 1–2 на фичу | десятки, дёшево |
| время прогона | десятки секунд | миллисекунды |
| ловит | железо, тайминги, банки | арифметику, краевые условия, регрессии |
Правило разделения то же, что уже записано для PoP: **что можно проверить
без железа — проверять в ucsim, MAME оставить железу.**
## Три группы модулей
### 1. Чистая логика — тестируется как есть, швов не нужно
Здесь можно начинать в тот же день, когда задачу возьмут в работу.
**libc:**
- `time/``_tm_is_leap`, `_tm_mdays`, `_tm_month_days`, `_tm_year_days`,
`mktime`, `gmtime`, `localtime`, `asctime`, `ctime`. Классическая
календарная арифметика: високосные годы, границы месяцев, переходы через
год, круговой прогон `mktime(gmtime(t)) == t`. Идеальный первый набор —
много краевых случаев и ноль зависимостей.
- `stdio/``dec_print`, `hex8/16/32`, `_scanf_core`, `sscanf`. Формат и
разбор: ширина, знак, переполнение, мусор на входе.
- `string/`, `stdlib/``strlwr`, `strupr`, `max`, `min`.
**libbgi/common** (115 модулей, почти всё mode-agnostic):
- `_bgi_isqrt`, `_bgi_trig` — сверить с эталонной формулой на всём
диапазоне, ровно как сделано для LCG в `t_geom` у PoP;
- `_bgi_lineseg`, `_bgi_styled_line`, `_bgi_poly_edge`, `_bgi_arc_draw`
геометрия и отсечение: линия целиком вне окна, по диагонали через угол,
вырожденная в точку;
- `_spr_ysort` — порядок сортировки спрайтов;
- `_bgi_hspan`, `_bgi_fill_span` — заливка: краевые span'ы, нулевая ширина.
### 2. Нужны швы — но швы дешёвые
**Фейковый ESTEX и BIOS прямо в тестовом crt0.** Оба вызываются через
`rst #0x10` и `rst #0x08`, то есть через фиксированные векторы в первых
байтах памяти. В тестовом бинаре эти адреса наши: можно положить туда
обработчик, который эмулирует крошечную файловую систему в ОЗУ и текстовый
экран в буфере. Это открывает:
- `file/` (32 модуля) и `io/` (21) — `fopen`/`fread`/`fwrite`/`fseek`,
буферизация (`docs/file-buffering-design.md`), поведение на EOF и
ошибках, `errno`;
- `conio/` (38) — вывод в буфер вместо экрана, проверка атрибутов,
скроллинга, границ окна.
Отдельно ценно: **гарды**, которые обязаны быть в обеих сборках. `_fd_guard`
(девятый `OPEN` вешает DSS) — это ровно тот случай, где нужен тест, а не
вера в комментарий: открыть восемь, убедиться, что девятый вернул ошибку и
не дошёл до ESTEX.
**Кадровый буфер в ОЗУ для libbgi.** Если рисующие ядра умеют писать в
обычный буфер, а не только в видеопамять, растеризацию можно проверять
снимком: нарисовать фигуру, сравнить с эталонным массивом. Начинать с
маленьких (8×8, 16×16) — эталон читаемый прямо в исходнике.
### 3. Только MAME
Банки и W-окна, EMM, реальный ESTEX/DSS, клавиатурный трамплин и IM2,
CBL-звук (`cbl/`), мышь (`mouse/`), `mem/` (это банки и страницы, а не
куча), тайминги и бюджет кадра, ускоритель.
## Обе сборки: fast и safe
Библиотеки собираются в двух вариантах (`-D*_NOCHECK` вырезает
валидацию параметров). Наборы стоит гонять **против обоих**:
- в safe — что валидация ловит мусорные аргументы и ставит `errno`;
- в fast — что вырезание валидации не поменяло поведение на корректных
входах.
Это дешёвая параметризация Makefile (тот же набор, два `OBJS_*`), и она
пресекает целый класс расхождений между вариантами.
## Фазы
1. **Календарь и формат.** `time/`, `stdio/`. Нулевые швы, максимальная
плотность краевых случаев. Цель — обкатать поток работы на libc.
2. **Геометрия libbgi.** `common/`: isqrt, тригонометрия, отсечение линий,
рёбра полигонов. Тоже без швов.
3. **Фейковые ESTEX/BIOS в crt0.** Открывает `file/`, `io/`, `conio/` и
гарды. Самый крупный кусок работы и самая большая отдача.
4. **Растр libbgi по снимкам** — если окажется, что ядра можно нацелить на
буфер в ОЗУ без правок продукта; иначе отложить.
5. **Параметризация fast/safe** — после того, как наборов станет заметно.
## Критерии
- Тест не считается написанным, пока не проверен мутацией: сломать
проверяемое место, убедиться, что набор краснеет. Пустые тесты хуже
отсутствующих.
- Каждый закрытый баг libc/libbgi получает регрессионный кейс, если он
ловится без железа.
- `make host-tests` остаётся быстрым: если суммарно перевалит за несколько
секунд, делить на быстрый и полный прогон.
## Организация
Наборы кладутся рядом с кодом, обвязка общая:
```
libc/tests-host/ наборы по областям: t_time.c, t_stdio.c, …
libbgi/tests-host/ t_geom.c, t_raster.c, …
```
Каждому — `Makefile` на пять строк (`TESTKIT`, `ENGINE_DIR`, `OBJS_*`,
`include`), и добавить каталог в `HOST_TEST_DIRS` корневого `Makefile`.
Подробности — `testkit/README.md`.