/* * t_phys.c — характеризация физики Кида (pop_map + pop_kid + pop_ctrl). * * Назначение набора — СТРАХОВКА ПОД РЕФАКТОРИНГ, а не проверка * правильности. Эталоны здесь сняты с текущего билда, то есть * консервируют и текущие баги; правильность по-прежнему проверяется * против SDLPoP. Смысл в другом: перенос физики с `Kid.` на `Char.` * обязан быть поведение-сохраняющим, и «трасса до == трасса после» ловит * ровно тот класс ошибок, который там возможен. * * Поэтому при осознанном изменении поведения эталон ПЕРЕСНИМАЕТСЯ (см. * SC_RECORD ниже), а в коммите должно быть написано, почему трасса * поменялась. Молча обновлённый эталон обесценивает весь набор. * * Снятие эталона: * make -C applications/PoP/roomtest/tests-host t_phys SC_RECORD=1 * Трассы уходят в отчёт tcheck готовыми C-строками — вставить в golden_*. */ #include "tcheck.h" #include "scene.h" #include "stubs.h" #include "pop_kid.h" #include "pop_map.h" /* Коды тайлов (pop_map): 0 пусто, 1 пол, 3 колонна, 11 loose, 20 стена. */ #define E 0 #define F 1 #define P 3 #define L 11 #define W 20 /* Ровный пол во всю комнату: базовая сцена для ходьбы/бега/разворота. */ static const uint8_t room_flat[30] = { W, E, E, E, E, E, E, E, E, E, W, F, F, F, F, F, F, F, F, F, W, W, W, W, W, W, W, W, W, W, }; /* Пол с обрывом после колонки 5: падение с ряда 1 на ряд 2. */ static const uint8_t room_ledge[30] = { W, E, E, E, E, E, E, E, E, E, W, F, F, F, F, F, E, E, E, E, W, F, F, F, F, F, F, F, F, W, }; /* Проваливающаяся плита на (1,4) — сцена под loose-механику. */ static const uint8_t room_loose[30] = { W, E, E, E, E, E, E, E, E, E, W, F, F, F, L, F, F, F, F, F, W, F, F, F, F, F, F, F, F, W, }; /* Провал в ТРИ пустых тайла (колонки 2,3,4) — геометрия из BUG-RJUMP-1 * (ур. 2, комнаты 1 и 9). Разбег-прыжок влево с колонки 5 обязан * доставать до колонки 1; у нас Кид сваливается в провал. */ static const uint8_t room_gap3[30] = { W, E, E, E, E, E, E, E, E, E, W, F, E, E, E, F, F, F, F, F, W, F, F, F, F, F, F, F, F, W, }; /* SC_RECORD= — снять эталон СЦЕНАРИЯ n (нумерация в порядке вызовов в * main). По одному за прогон: лог tcheck 768 байт, на все трассы сразу * его не хватает. Перебором занимается record_golden.py. */ #ifndef SC_RECORD #define SC_RECORD 0 #endif /* Снять или сверить — одной строкой в каждом сценарии, чтобы режимы не * разъезжались. Имя сценария = имя файла эталона. */ #if SC_RECORD static uint8_t sc_idx; #define SC_CHECK(g, nm) \ do { if (++sc_idx == (SC_RECORD)) sc_dump(nm); } while (0) #else #define SC_CHECK(g, nm) sc_expect((g), (uint8_t)(sizeof(g) / SC_REC_BYTES)) #endif /* ---- эталоны (сняты SC_RECORD=1) ------------------------------------ * * В режиме снятия эталонов файлов golden/ ещё нет — и не должно быть: * первый прогон их и порождает. */ #if !SC_RECORD static const uint8_t golden_stand[] = { #include "golden/stand.h" }; static const uint8_t golden_run_right[] = { #include "golden/run_right.h" }; static const uint8_t golden_step_shift[] = { #include "golden/step_shift.h" }; static const uint8_t golden_turn[] = { #include "golden/turn.h" }; static const uint8_t golden_crouch[] = { #include "golden/crouch.h" }; static const uint8_t golden_jump_up[] = { #include "golden/jump_up.h" }; static const uint8_t golden_fall_ledge[] = { #include "golden/fall_ledge.h" }; static const uint8_t golden_loose[] = { #include "golden/loose.h" }; static const uint8_t golden_rjump_gap3[] = { #include "golden/rjump_gap3.h" }; #else /* SC_RECORD: имена нужны только чтобы SC_CHECK() раскрылся. */ #define golden_stand 0 #define golden_run_right 0 #define golden_step_shift 0 #define golden_turn 0 #define golden_crouch 0 #define golden_jump_up 0 #define golden_fall_ledge 0 #define golden_loose 0 #define golden_rjump_gap3 0 #endif /* ---- сценарии -------------------------------------------------------- */ TC_TEST(phys_stand_is_stable) { /* Никакого ввода: Кид на ровном полу обязан стоять неподвижно. Тест * дешёвый, но ловит самое обидное — «сам поехал» после правки * геометрии или окна Char. */ sc_room(room_flat, 1); sc_kid_at(3, 1, -1); sc_trace_clear(); sc_run(0, 12); SC_CHECK(golden_stand, "stand"); } TC_TEST(phys_run_right) { sc_room(room_flat, 1); sc_kid_at(1, 1, 0); sc_trace_clear(); sc_run(SC_R, 24); SC_CHECK(golden_run_right, "run_right"); } TC_TEST(phys_step_with_shift) { /* Shift + вперёд — осторожный шаг: одна из самых чувствительных к * distance_to_edge веток. */ sc_room(room_flat, 1); sc_kid_at(3, 1, 0); sc_trace_clear(); sc_run(SC_R | SC_SHIFT, 20); SC_CHECK(golden_step_shift, "step_shift"); } TC_TEST(phys_turn_around) { sc_room(room_flat, 1); sc_kid_at(4, 1, 0); sc_trace_clear(); sc_run(SC_L, 16); SC_CHECK(golden_turn, "turn"); } TC_TEST(phys_crouch_and_stand_up) { sc_room(room_flat, 1); sc_kid_at(4, 1, 0); sc_trace_clear(); sc_run(SC_D, 8); sc_run(0, 12); SC_CHECK(golden_crouch, "crouch"); } TC_TEST(phys_jump_up_nothing_above) { /* Над головой пусто — seq_28; проверяем и смещение по x, которое * оригинал делает перед прыжком. */ sc_room(room_flat, 1); sc_kid_at(4, 1, -1); sc_trace_clear(); sc_run(SC_U, 20); SC_CHECK(golden_jump_up, "jump_up"); } TC_TEST(phys_run_off_ledge) { /* Бег в обрыв: check_on_floor -> start_fall -> land на ряду 2. * Сценарий, вокруг которого крутился BUG-FALL-SWORD-1. */ sc_room(room_ledge, 1); sc_kid_at(1, 1, 0); sc_trace_clear(); sc_run(SC_R, 34); SC_CHECK(golden_fall_ledge, "fall_ledge"); } TC_TEST(phys_loose_floor_breaks) { /* Пробежка по loose-плите: тайл обязан затрястись и провалиться, а * физика — сообщить об этом соседям (trob + перерисовка). */ sc_room(room_loose, 1); sc_kid_at(1, 1, 0); sc_trace_clear(); sc_run(SC_R, 34); SC_CHECK(golden_loose, "loose"); #if !SC_RECORD /* Журнал заглушек: плита обязана была отвалиться (кусок отрывается — * pop_loose_mob_spawn) и попросить перерисовку. Это уже не * характеризация, а содержательная проверка — заглушка-пустышка её не * дала бы. Через trob loose у нас НЕ идёт (только пики/ворота), у него * свой pop_loose_modif — поэтому спрашиваем именно про кусок. */ TC_TRUE(tk_log_count(TK_MOB_SPAWN) > 0); TC_TRUE(tk_log_count(TK_REDRAW) > 0); #endif } TC_TEST(phys_running_jump_over_3tile_gap) { /* BUG-RJUMP-1. Разбег влево от колонки 8, дальше Up — разбег-прыжок. * Ожидание оригинала: перелёт провала в три тайла и посадка на (1,1). * Сейчас эталон фиксирует НАШЕ поведение (падение в провал) — когда * баг починим, трасса поменяется осознанно и эталон переснимется. */ sc_room(room_gap3, 1); sc_kid_at(8, 1, -1); sc_trace_clear(); sc_run(SC_L, 8); /* разбег */ sc_run(SC_L | SC_U, 26); /* прыжок и посадка */ SC_CHECK(golden_rjump_gap3, "rjump_gap3"); } /* ---- точка входа ----------------------------------------------------- */ void main(void) { sc_init(); TC_RUN(phys_stand_is_stable); TC_RUN(phys_run_right); TC_RUN(phys_step_with_shift); TC_RUN(phys_turn_around); TC_RUN(phys_crouch_and_stand_up); TC_RUN(phys_jump_up_nothing_above); TC_RUN(phys_run_off_ledge); TC_RUN(phys_loose_floor_breaks); TC_RUN(phys_running_jump_over_3tile_gap); }