/* pop_app — протокол верхнего автомата: допустимые и запрещённые переходы. */ #include "tcheck.h" #include "pop_app.h" TC_TEST(boot_to_title) { pop_app_reset(); TC_EQ(pop_app_state, POP_APP_BOOT); TC_TRUE(pop_app_dispatch(POP_APP_EV_BOOT_READY)); TC_EQ(pop_app_state, POP_APP_BUILD_INFO); TC_TRUE(pop_app_dispatch(POP_APP_EV_BUILD_DONE)); TC_EQ(pop_app_state, POP_APP_TITLE); } TC_TEST(level_load_roundtrip) { pop_app_reset(); pop_app_dispatch(POP_APP_EV_BOOT_READY); pop_app_dispatch(POP_APP_EV_BUILD_DONE); TC_TRUE(pop_app_dispatch(POP_APP_EV_NEW_GAME)); TC_EQ(pop_app_state, POP_APP_LEVEL_LOAD); TC_TRUE(pop_app_dispatch(POP_APP_EV_LEVEL_READY)); TC_EQ(pop_app_state, POP_APP_PLAYING); TC_TRUE(pop_app_dispatch(POP_APP_EV_PAUSE)); TC_EQ(pop_app_state, POP_APP_PAUSE_MENU); TC_TRUE(pop_app_dispatch(POP_APP_EV_RESUME)); TC_EQ(pop_app_state, POP_APP_PLAYING); TC_TRUE(pop_app_dispatch(POP_APP_EV_LEVEL_FINISHED)); TC_EQ(pop_app_state, POP_APP_LEVEL_LOAD); TC_TRUE(pop_app_dispatch(POP_APP_EV_LEVEL_READY)); TC_EQ(pop_app_state, POP_APP_PLAYING); } TC_TEST(rejects_wrong_event) { pop_app_reset(); TC_FALSE(pop_app_dispatch(POP_APP_EV_LEVEL_READY)); TC_EQ(pop_app_state, POP_APP_BOOT); TC_TRUE(pop_app_dispatch(POP_APP_EV_QUIT)); TC_EQ(pop_app_state, POP_APP_QUIT); TC_FALSE(pop_app_dispatch(POP_APP_EV_BOOT_READY)); } TC_TEST(future_routes_are_declared) { pop_app_reset(); pop_app_state = POP_APP_TITLE; TC_TRUE(pop_app_dispatch(POP_APP_EV_NEW_GAME)); TC_EQ(pop_app_state, POP_APP_LEVEL_LOAD); TC_TRUE(pop_app_dispatch(POP_APP_EV_LEVEL_READY)); TC_EQ(pop_app_state, POP_APP_PLAYING); TC_TRUE(pop_app_dispatch(POP_APP_EV_TIME_EXPIRED)); TC_EQ(pop_app_state, POP_APP_TIME_EXPIRED); TC_TRUE(pop_app_dispatch(POP_APP_EV_CUTSCENE_FINISHED)); TC_EQ(pop_app_state, POP_APP_TITLE); } TC_TEST(prelevel_cutscene_route) { pop_app_state = POP_APP_PLAYING; TC_TRUE(pop_app_dispatch(POP_APP_EV_CUTSCENE_STARTED)); TC_EQ(pop_app_state, POP_APP_CUTSCENE); TC_TRUE(pop_app_dispatch(POP_APP_EV_CUTSCENE_FINISHED)); TC_EQ(pop_app_state, POP_APP_LEVEL_LOAD); } TC_TEST(demo_routes) { pop_app_reset(); pop_app_dispatch(POP_APP_EV_BOOT_READY); pop_app_dispatch(POP_APP_EV_BUILD_DONE); TC_TRUE(pop_app_dispatch(POP_APP_EV_INTRO_STARTED)); TC_EQ(pop_app_state, POP_APP_INTRO); TC_TRUE(pop_app_dispatch(POP_APP_EV_CUTSCENE_FINISHED)); TC_EQ(pop_app_state, POP_APP_DEMO); TC_TRUE(pop_app_dispatch(POP_APP_EV_DEMO_FINISHED)); TC_EQ(pop_app_state, POP_APP_TITLE); TC_TRUE(pop_app_dispatch(POP_APP_EV_DEMO_STARTED)); TC_TRUE(pop_app_dispatch(POP_APP_EV_NEW_GAME)); TC_EQ(pop_app_state, POP_APP_LEVEL_LOAD); } TC_TEST(intro_skip_starts_new_game) { pop_app_reset(); pop_app_dispatch(POP_APP_EV_BOOT_READY); pop_app_dispatch(POP_APP_EV_BUILD_DONE); pop_app_dispatch(POP_APP_EV_INTRO_STARTED); TC_EQ(pop_app_state, POP_APP_INTRO); TC_TRUE(pop_app_dispatch(POP_APP_EV_NEW_GAME)); TC_EQ(pop_app_state, POP_APP_LEVEL_LOAD); } TC_TEST(restart_game_returns_to_intro) { pop_app_state = POP_APP_PLAYING; TC_TRUE(pop_app_dispatch(POP_APP_EV_PAUSE)); TC_EQ(pop_app_state, POP_APP_PAUSE_MENU); TC_TRUE(pop_app_dispatch(POP_APP_EV_RESTART_INTRO)); TC_EQ(pop_app_state, POP_APP_TITLE); } /* Финал ВРЕМЕННО ведёт мимо Hall of Fame прямо в title: экран HOF после * победы показывает чёрное, и застревать в нём хуже, чем не показывать * (решение пользователя 2026-08-24, BUGS_OPEN.md#hof-after-ending). Сам * маршрут HOF -> TITLE остаётся живым: в него ходит title-экран. */ TC_TEST(ending_to_title_route) { pop_app_state = POP_APP_PLAYING; TC_TRUE(pop_app_dispatch(POP_APP_EV_ENDING_STARTED)); TC_EQ(pop_app_state, POP_APP_ENDING); TC_TRUE(pop_app_dispatch(POP_APP_EV_ENDING_FINISHED)); TC_EQ(pop_app_state, POP_APP_TITLE); } TC_TEST(hof_route_still_exits_to_title) { pop_app_state = POP_APP_HALL_OF_FAME; TC_TRUE(pop_app_dispatch(POP_APP_EV_HOF_FINISHED)); TC_EQ(pop_app_state, POP_APP_TITLE); } void main(void) { TC_RUN(boot_to_title); TC_RUN(level_load_roundtrip); TC_RUN(rejects_wrong_event); TC_RUN(future_routes_are_declared); TC_RUN(prelevel_cutscene_route); TC_RUN(demo_routes); TC_RUN(intro_skip_starts_new_game); TC_RUN(restart_game_returns_to_intro); TC_RUN(ending_to_title_route); TC_RUN(hof_route_still_exits_to_title); }