# План: модульные тесты 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`.
