/*
 * tcheck.h — микро-фреймворк для тестов, исполняемых под ucsim_z80.
 *
 * Почему не printf: тестовый бинарь линкуется БЕЗ Sprinter-libc (там ESTEX,
 * BIOS, файлы — ничего этого в ucsim нет).  Поэтому результат складывается в
 * структуру tc_result в ОЗУ, а раннер (run_ucsim.py) читает её командой
 * `dump` после остановки по halt и печатает по-человечески.
 *
 * Использование:
 *
 *     #include "tcheck.h"
 *     TC_TEST(имя_теста) { ... TC_EQ(получено, ожидание); ... }
 *     TC_MAIN(имя_теста, другой_тест, ...)
 *
 * Значения сравниваются как int16_t — этого хватает на всё, чем оперирует
 * движок (координаты, тайлы, кадры, счётчики).
 */
#ifndef TCHECK_H
#define TCHECK_H

#include <stdint.h>

#define TC_LOGSZ 768
#define TC_MAGIC 0x5A5Au

typedef struct {
    uint16_t magic;              /* TC_MAGIC — раннер убеждается, что нашёл нас */
    uint16_t total;              /* сколько проверок выполнено */
    uint16_t failed;             /* сколько провалилось */
    uint16_t logn;               /* занято в log */
    uint8_t  log[TC_LOGSZ];      /* ASCII-отчёт (только про провалы) */
} tc_result_t;

extern tc_result_t tc_result;

/* Текущий тест — чтобы в отчёте было видно, где именно упало. */
extern const char *tc_curr;

void tc_begin(const char *name);
void tc_check(int16_t got, int16_t want, const char *expr, uint16_t line);
void tc_checkp(const void *got, const void *want, const char *expr, uint16_t line);

/* Основные макросы.  __LINE__ хватает для локализации: имя теста уже в
 * отчёте, а выражение печатается текстом. */
#define TC_EQ(got, want)   tc_check((int16_t)(got), (int16_t)(want), #got, __LINE__)
#define TC_TRUE(cond)      tc_check((int16_t)((cond) ? 1 : 0), 1, #cond, __LINE__)
#define TC_FALSE(cond)     tc_check((int16_t)((cond) ? 1 : 0), 0, #cond, __LINE__)
#define TC_PEQ(got, want)  tc_checkp((got), (want), #got, __LINE__)

#define TC_TEST(name) static void name(void)

/* main() тестового бинаря: перечислить функции тестов.  Вариадика через
 * список вызовов — у SDCC нет проблем с таким раскрытием. */
#define TC_RUN(fn)    do { tc_begin(#fn); fn(); } while (0)

#endif
