/* * 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 #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