stable
This commit is contained in:
parent
ef7b492b24
commit
ace9659ef5
9 changed files with 445 additions and 21 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -42,6 +42,8 @@
|
||||||
*.idb
|
*.idb
|
||||||
*.pdb
|
*.pdb
|
||||||
|
|
||||||
|
*.gc*
|
||||||
|
|
||||||
# Kernel Module Compile Results
|
# Kernel Module Compile Results
|
||||||
*.mod*
|
*.mod*
|
||||||
*.cmd
|
*.cmd
|
||||||
|
|
|
||||||
44
src/Makefile
44
src/Makefile
|
|
@ -1,4 +1,4 @@
|
||||||
.PHONY: all clean install uninstall test gcov_report dvi dist run style format
|
.PHONY: all clean install uninstall test test_coverage gcov_report dvi dist run style format
|
||||||
|
|
||||||
CC ?= gcc
|
CC ?= gcc
|
||||||
CFLAGS ?= -Wall -Wextra -Werror -std=c11 -g -D_POSIX_C_SOURCE=199309L -MMD -MP
|
CFLAGS ?= -Wall -Wextra -Werror -std=c11 -g -D_POSIX_C_SOURCE=199309L -MMD -MP
|
||||||
|
|
@ -25,6 +25,7 @@ TETRIS_SRC = $(shell find $(TETRISDIR) -name "*.c")
|
||||||
TETRIS_OBJ = $(TETRIS_SRC:.c=.o)
|
TETRIS_OBJ = $(TETRIS_SRC:.c=.o)
|
||||||
CLI_SRC = $(shell find $(CLIDIR) -name "*.c")
|
CLI_SRC = $(shell find $(CLIDIR) -name "*.c")
|
||||||
CLI_OBJ = $(CLI_SRC:.c=.o)
|
CLI_OBJ = $(CLI_SRC:.c=.o)
|
||||||
|
TEST_SRC = $(filter-out $(TESTDIR)/test.c, $(shell find $(TESTDIR) -name "*.c"))
|
||||||
|
|
||||||
LIB_TETRIS = $(BUILDDIR)/libtetris.a
|
LIB_TETRIS = $(BUILDDIR)/libtetris.a
|
||||||
TARGET = $(BUILDDIR)/tetris_bin.out
|
TARGET = $(BUILDDIR)/tetris_bin.out
|
||||||
|
|
@ -36,7 +37,7 @@ BINDIR = $(PREFIX)/bin
|
||||||
|
|
||||||
all: $(TARGET)
|
all: $(TARGET)
|
||||||
|
|
||||||
run: $(TARGET)
|
run: clean $(TARGET)
|
||||||
./$(TARGET)
|
./$(TARGET)
|
||||||
|
|
||||||
install: $(TARGET)
|
install: $(TARGET)
|
||||||
|
|
@ -51,24 +52,38 @@ uninstall:
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(CLI_OBJ) $(TETRIS_OBJ) $(TARGET) $(LIB_TETRIS) $(TEST_TARGET)
|
rm -rf $(CLI_OBJ) $(TETRIS_OBJ) $(TARGET) $(LIB_TETRIS) $(TEST_TARGET)
|
||||||
rm -rf $(TETRISDIR)/*.d $(CLIDIR)/*.d
|
rm -rf $(TETRISDIR)/*.d $(CLIDIR)/*.d
|
||||||
|
rm -rf $(TETRISDIR)/*.gcda $(TETRISDIR)/*.gcno $(TETRISDIR)/*.gcov
|
||||||
rm -rf $(GCOV_DIR) $(DVI_DIR)
|
rm -rf $(GCOV_DIR) $(DVI_DIR)
|
||||||
|
|
||||||
test: $(LIB_TETRIS)
|
test: clean $(LIB_TETRIS)
|
||||||
$(CC) $(CFLAGS) $(TESTDIR)/test.c -L$(BUILDDIR) -ltetris $(LDFLAGS) -o $(TEST_TARGET)
|
$(CC) $(CFLAGS) $(TEST_SRC) -L$(BUILDDIR) -ltetris $(LDFLAGS) -o $(TEST_TARGET)
|
||||||
./$(TEST_TARGET)
|
./$(TEST_TARGET)
|
||||||
|
|
||||||
gcov_report: CFLAGS += --coverage
|
test_coverage: CFLAGS += --coverage
|
||||||
gcov_report: LDFLAGS += --coverage
|
test_coverage: LDFLAGS += --coverage
|
||||||
gcov_report: clean test
|
test_coverage: clean $(LIB_TETRIS)
|
||||||
@mkdir -p $(GCOV_DIR)
|
$(CC) $(CFLAGS) $(TEST_SRC) -L$(BUILDDIR) -ltetris $(LDFLAGS) -o $(TEST_TARGET)
|
||||||
gcov $(TETRIS_SRC) -o $(TETRISDIR)
|
./$(TEST_TARGET)
|
||||||
lcov --capture --directory $(TETRISDIR) --output-file $(GCOV_DIR)/coverage.info --ignore-errors unused
|
|
||||||
lcov --extract $(GCOV_DIR)/coverage.info '*/brick_game/tetris/*' -o $(GCOV_DIR)/coverage.info --ignore-errors unused
|
gcov_report: test_coverage
|
||||||
genhtml $(GCOV_DIR)/coverage.info --output-directory $(GCOV_DIR)
|
@mkdir -p $(GCOV_DIR)/obj
|
||||||
@mv *.gcov $(GCOV_DIR)/ 2>/dev/null || true
|
@find $(TETRISDIR) -name "*.gcda" -exec mv {} $(GCOV_DIR)/obj/ \;
|
||||||
|
@find $(TETRISDIR) -name "*.gcno" -exec mv {} $(GCOV_DIR)/obj/ \;
|
||||||
|
@cd $(GCOV_DIR) && for src in $(addprefix ../,$(TETRIS_SRC)); do \
|
||||||
|
gcov $$src -o obj/ 2>/dev/null; \
|
||||||
|
done
|
||||||
|
lcov --capture --directory $(GCOV_DIR)/obj \
|
||||||
|
--output-file $(GCOV_DIR)/coverage.info \
|
||||||
|
--ignore-errors unused
|
||||||
|
lcov --extract $(GCOV_DIR)/coverage.info '*/brick_game/tetris/*' \
|
||||||
|
-o $(GCOV_DIR)/coverage.info \
|
||||||
|
--ignore-errors unused
|
||||||
|
genhtml $(GCOV_DIR)/coverage.info \
|
||||||
|
--output-directory $(GCOV_DIR)
|
||||||
@echo "Report: $(GCOV_DIR)/index.html"
|
@echo "Report: $(GCOV_DIR)/index.html"
|
||||||
xdg-open $(GCOV_DIR)/index.html
|
xdg-open $(GCOV_DIR)/index.html
|
||||||
|
|
||||||
|
|
||||||
dvi:
|
dvi:
|
||||||
@mkdir -p $(DVI_DIR)
|
@mkdir -p $(DVI_DIR)
|
||||||
@echo "Generating documentation with Doxygen..."
|
@echo "Generating documentation with Doxygen..."
|
||||||
|
|
@ -79,8 +94,7 @@ dvi:
|
||||||
echo "Copying doc.md as fallback..."; \
|
echo "Copying doc.md as fallback..."; \
|
||||||
cp doc.md $(DVI_DIR)/; \
|
cp doc.md $(DVI_DIR)/; \
|
||||||
fi
|
fi
|
||||||
xdg-open dvi/html/index.html
|
xdg-open $(DVI_DIR)/html/index.html
|
||||||
|
|
||||||
|
|
||||||
dist: clean
|
dist: clean
|
||||||
tar -czf tetris.tar.gz Makefile $(TETRISDIR) $(CLIDIR) $(TESTDIR) README.md doc.md
|
tar -czf tetris.tar.gz Makefile $(TETRISDIR) $(CLIDIR) $(TESTDIR) README.md doc.md
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
#include <check.h>
|
|
||||||
#include "test_helper.h"
|
#include "test_helper.h"
|
||||||
|
|
||||||
START_TEST(test_collision_bottom) {
|
START_TEST(test_collision_bottom) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
#include <check.h>
|
|
||||||
#include "test_helper.h"
|
#include "test_helper.h"
|
||||||
|
|
||||||
START_TEST(test_generate_figure) {
|
START_TEST(test_generate_figure) {
|
||||||
|
|
|
||||||
409
src/test/test_fsm.c
Normal file
409
src/test/test_fsm.c
Normal file
|
|
@ -0,0 +1,409 @@
|
||||||
|
#include <check.h>
|
||||||
|
#include "test_helper.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Тесты FSM и интеграции
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
START_TEST(test_game_start) {
|
||||||
|
userInput(Start, false);
|
||||||
|
GameInfo_t state = updateCurrentState();
|
||||||
|
|
||||||
|
// После Start игра должна инициализироваться
|
||||||
|
ck_assert_int_eq(state.level, 1);
|
||||||
|
ck_assert_int_eq(state.score, 0);
|
||||||
|
ck_assert_int_eq(state.pause, 0);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_pause_toggle) {
|
||||||
|
userInput(Start, false);
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
// Включаем паузу
|
||||||
|
userInput(Pause, false);
|
||||||
|
GameInfo_t state = updateCurrentState();
|
||||||
|
ck_assert_int_eq(state.pause, 1);
|
||||||
|
|
||||||
|
// Выключаем паузу
|
||||||
|
userInput(Pause, false);
|
||||||
|
state = updateCurrentState();
|
||||||
|
ck_assert_int_eq(state.pause, 0);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_field_initialization) {
|
||||||
|
userInput(Start, false);
|
||||||
|
GameInfo_t state = updateCurrentState();
|
||||||
|
|
||||||
|
// Поле должно быть чистым в начале
|
||||||
|
int non_zero_count = 0;
|
||||||
|
for (int i = 0; i < FIELD_HEIGHT; i++) {
|
||||||
|
for (int j = 0; j < FIELD_WIDTH; j++) {
|
||||||
|
if (state.field[i][j] != 0) {
|
||||||
|
non_zero_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Только активная фигура (максимум 4 блока)
|
||||||
|
ck_assert_int_le(non_zero_count, 4);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_next_figure_exists) {
|
||||||
|
userInput(Start, false);
|
||||||
|
GameInfo_t state = updateCurrentState();
|
||||||
|
|
||||||
|
// Должна быть следующая фигура
|
||||||
|
int has_blocks = 0;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
if (state.next[i][j]) {
|
||||||
|
has_blocks = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ck_assert_int_eq(has_blocks, 1);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_movement_left) {
|
||||||
|
userInput(Start, false);
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
// Двигаем влево (движение происходит мгновенно через Moving)
|
||||||
|
userInput(Left, false);
|
||||||
|
GameInfo_t state = updateCurrentState();
|
||||||
|
|
||||||
|
// Проверяем что состояние изменилось (игра не в паузе)
|
||||||
|
ck_assert_int_eq(state.pause, 0);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_movement_right) {
|
||||||
|
userInput(Start, false);
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
// Двигаем вправо
|
||||||
|
userInput(Right, false);
|
||||||
|
GameInfo_t state = updateCurrentState();
|
||||||
|
|
||||||
|
// Проверяем что состояние валидное
|
||||||
|
ck_assert_int_eq(state.pause, 0);
|
||||||
|
ck_assert_int_ge(state.level, 1);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_rotation) {
|
||||||
|
userInput(Start, false);
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
// Вращаем фигуру
|
||||||
|
userInput(Action, false);
|
||||||
|
GameInfo_t state = updateCurrentState();
|
||||||
|
|
||||||
|
// Фигура должна остаться на поле
|
||||||
|
int has_figure = 0;
|
||||||
|
for (int i = 0; i < FIELD_HEIGHT; i++) {
|
||||||
|
for (int j = 0; j < FIELD_WIDTH; j++) {
|
||||||
|
if (state.field[i][j] == 1) {
|
||||||
|
has_figure = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ck_assert_int_eq(has_figure, 1);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_user_input_actions) {
|
||||||
|
// Просто проверяем что userInput не крашит
|
||||||
|
userInput(Start, false);
|
||||||
|
userInput(Left, false);
|
||||||
|
userInput(Right, false);
|
||||||
|
userInput(Action, false);
|
||||||
|
userInput(Pause, false);
|
||||||
|
userInput(Terminate, false);
|
||||||
|
|
||||||
|
// Если дошли сюда - всё ок
|
||||||
|
ck_assert_int_eq(1, 1);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_multiple_updates) {
|
||||||
|
userInput(Start, false);
|
||||||
|
|
||||||
|
// Многократный вызов updateCurrentState не должен крашить
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
updateCurrentState();
|
||||||
|
}
|
||||||
|
|
||||||
|
GameInfo_t state = updateCurrentState();
|
||||||
|
ck_assert_int_eq(state.level, 1);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Тесты для повышения покрытия
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
START_TEST(test_instant_drop_down_key) {
|
||||||
|
userInput(Start, false);
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
// Устанавливаем флаг что клавиша была отжата
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
state->down_key_was_released = 1;
|
||||||
|
|
||||||
|
// Нажимаем Down (instant drop)
|
||||||
|
userInput(Down, false);
|
||||||
|
|
||||||
|
// Проверяем что флаг сброшен
|
||||||
|
ck_assert_int_eq(state->down_key_was_released, 0);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_up_key_release) {
|
||||||
|
userInput(Start, false);
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
state->down_key_was_released = 0;
|
||||||
|
|
||||||
|
// Нажимаем Up (release)
|
||||||
|
userInput(Up, false);
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
// Флаг должен установиться
|
||||||
|
ck_assert_int_eq(state->down_key_was_released, 1);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_terminate_with_high_score) {
|
||||||
|
userInput(Start, false);
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
state->info->high_score = 100;
|
||||||
|
state->info->score = 500; // Больше рекорда
|
||||||
|
|
||||||
|
// Terminate должен сохранить рекорд
|
||||||
|
userInput(Terminate, false);
|
||||||
|
|
||||||
|
// Проверяем что файл сохранён (загружаем обратно)
|
||||||
|
int loaded = load_high_score();
|
||||||
|
ck_assert_int_ge(loaded, 500);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_game_over_state) {
|
||||||
|
userInput(Start, false);
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
|
||||||
|
// Заполняем верхние две строки почти полностью
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
for (int j = 0; j < FIELD_WIDTH; j++) {
|
||||||
|
state->field[i][j] = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вызываем spawn, который должен обнаружить game over
|
||||||
|
state->state = Spawn;
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
// Состояние должно перейти в GameOver
|
||||||
|
ck_assert_int_eq(state->state, GameOver);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_do_gameover_clears_next) {
|
||||||
|
userInput(Start, false);
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
|
||||||
|
// Заполняем поле для trigger GameOver
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
for (int j = 0; j < FIELD_WIDTH; j++) {
|
||||||
|
state->field[i][j] = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state->state = Spawn;
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
// После GameOver next должна быть пустой
|
||||||
|
state->state = GameOver;
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
int has_blocks = 0;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
if (state->next.mtrx[i][j]) {
|
||||||
|
has_blocks = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ck_assert_int_eq(has_blocks, 0);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_place_figure_directly) {
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
|
||||||
|
// Очищаем поле
|
||||||
|
for (int i = 0; i < FIELD_HEIGHT; i++)
|
||||||
|
for (int j = 0; j < FIELD_WIDTH; j++)
|
||||||
|
state->field[i][j] = 0;
|
||||||
|
|
||||||
|
// Устанавливаем фигуру
|
||||||
|
state->curr.x = 5;
|
||||||
|
state->curr.y = 10;
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
state->curr.mtrx[i][j] = 0;
|
||||||
|
state->curr.mtrx[0][0] = 1;
|
||||||
|
state->curr.mtrx[0][1] = 1;
|
||||||
|
|
||||||
|
// Размещаем
|
||||||
|
place_figure();
|
||||||
|
|
||||||
|
// Проверяем что блоки размещены
|
||||||
|
ck_assert_int_eq(state->field[10][5], 2);
|
||||||
|
ck_assert_int_eq(state->field[10][6], 2);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_attach_state_transition) {
|
||||||
|
userInput(Start, false);
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
|
||||||
|
// Принудительно переводим в Attaching
|
||||||
|
state->state = Attaching;
|
||||||
|
state->attach_start_time = 0;
|
||||||
|
state->attach_completed = 0;
|
||||||
|
|
||||||
|
// Первый вызов должен разместить фигуру
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
// Проверяем что таймер запущен
|
||||||
|
ck_assert_int_gt(state->attach_start_time, 0);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_moving_to_down) {
|
||||||
|
userInput(Start, false);
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
|
||||||
|
// Устанавливаем фигуру с валидной матрицей
|
||||||
|
state->curr.y = 5;
|
||||||
|
state->curr.x = 5;
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
state->curr.mtrx[i][j] = 0;
|
||||||
|
state->curr.mtrx[0][0] = 1; // Один блок
|
||||||
|
|
||||||
|
int initial_y = state->curr.y;
|
||||||
|
|
||||||
|
// Вызываем handle_to_down_move через Moving
|
||||||
|
state->state = Moving;
|
||||||
|
state->moving_type = ToDown;
|
||||||
|
|
||||||
|
// ВАЖНО: updateCurrentState вызовет do_moving -> handle_to_down_move
|
||||||
|
// Защита от бесконечного цикла: ограничиваем время
|
||||||
|
long long start = get_current_time_ms();
|
||||||
|
updateCurrentState();
|
||||||
|
long long elapsed = get_current_time_ms() - start;
|
||||||
|
|
||||||
|
// Проверяем что:
|
||||||
|
// 1. Фигура упала ниже (или перешли в Attaching)
|
||||||
|
// 2. Не было timeout (< 1 секунды)
|
||||||
|
ck_assert(state->curr.y > initial_y || state->state == Attaching);
|
||||||
|
ck_assert_int_lt(elapsed, 1000);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
|
START_TEST(test_moving_do_nothing) {
|
||||||
|
userInput(Start, false);
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
|
||||||
|
// Вызываем DoNothing
|
||||||
|
state->state = Moving;
|
||||||
|
state->moving_type = DoNothing;
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
// Должны перейти в Move
|
||||||
|
ck_assert_int_eq(state->state, Move);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_automatic_falling) {
|
||||||
|
userInput(Start, false);
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
GameState_t* state = get_game_state();
|
||||||
|
|
||||||
|
// Устанавливаем фигуру
|
||||||
|
state->curr.y = 5;
|
||||||
|
state->curr.x = 5;
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
state->curr.mtrx[i][j] = 0;
|
||||||
|
state->curr.mtrx[0][0] = 1;
|
||||||
|
|
||||||
|
// Принудительно переводим в Move с обнулённым таймером
|
||||||
|
state->state = Move;
|
||||||
|
state->last_move_time = 0; // Обнуляем время последнего движения
|
||||||
|
|
||||||
|
int initial_y = state->curr.y;
|
||||||
|
|
||||||
|
// Вызываем updateCurrentState, который должен вызвать do_move()
|
||||||
|
// Поскольку last_move_time = 0, условие should_move сработает
|
||||||
|
updateCurrentState();
|
||||||
|
|
||||||
|
// Проверяем что фигура упала или перешла в Attaching
|
||||||
|
ck_assert(state->curr.y > initial_y || state->state == Attaching);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
|
Suite* fsm_suite(void) {
|
||||||
|
Suite* s = suite_create("FSM");
|
||||||
|
TCase* tc = tcase_create("Core");
|
||||||
|
|
||||||
|
// Существующие тесты
|
||||||
|
tcase_add_test(tc, test_game_start);
|
||||||
|
tcase_add_test(tc, test_pause_toggle);
|
||||||
|
tcase_add_test(tc, test_field_initialization);
|
||||||
|
tcase_add_test(tc, test_next_figure_exists);
|
||||||
|
tcase_add_test(tc, test_movement_left);
|
||||||
|
tcase_add_test(tc, test_movement_right);
|
||||||
|
tcase_add_test(tc, test_rotation);
|
||||||
|
tcase_add_test(tc, test_user_input_actions);
|
||||||
|
tcase_add_test(tc, test_multiple_updates);
|
||||||
|
|
||||||
|
tcase_add_test(tc, test_instant_drop_down_key);
|
||||||
|
tcase_add_test(tc, test_up_key_release);
|
||||||
|
tcase_add_test(tc, test_terminate_with_high_score);
|
||||||
|
tcase_add_test(tc, test_game_over_state);
|
||||||
|
tcase_add_test(tc, test_do_gameover_clears_next);
|
||||||
|
tcase_add_test(tc, test_place_figure_directly);
|
||||||
|
tcase_add_test(tc, test_attach_state_transition);
|
||||||
|
tcase_add_test(tc, test_moving_to_down);
|
||||||
|
tcase_add_test(tc, test_moving_do_nothing);
|
||||||
|
tcase_add_test(tc, test_automatic_falling);
|
||||||
|
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef TEST_HELPER_H
|
#ifndef TEST_HELPER_H
|
||||||
#define TEST_HELPER_H
|
#define TEST_HELPER_H
|
||||||
|
|
||||||
|
#include <check.h>
|
||||||
#include "../brick_game/tetris/01_automato.h"
|
#include "../brick_game/tetris/01_automato.h"
|
||||||
|
|
||||||
// Хелпер для очистки поля
|
// Хелпер для очистки поля
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
#include <check.h>
|
|
||||||
#include "test_helper.h"
|
#include "test_helper.h"
|
||||||
|
|
||||||
START_TEST(test_clear_one_line) {
|
START_TEST(test_clear_one_line) {
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,14 @@ Suite* collision_suite(void);
|
||||||
Suite* lines_suite(void);
|
Suite* lines_suite(void);
|
||||||
Suite* figures_suite(void);
|
Suite* figures_suite(void);
|
||||||
Suite* score_suite(void);
|
Suite* score_suite(void);
|
||||||
|
Suite* fsm_suite(void); // <- Эта строка уже есть?
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
SRunner* sr = srunner_create(collision_suite());
|
SRunner* sr = srunner_create(collision_suite());
|
||||||
srunner_add_suite(sr, lines_suite());
|
srunner_add_suite(sr, lines_suite());
|
||||||
srunner_add_suite(sr, figures_suite());
|
srunner_add_suite(sr, figures_suite());
|
||||||
srunner_add_suite(sr, score_suite());
|
srunner_add_suite(sr, score_suite());
|
||||||
|
srunner_add_suite(sr, fsm_suite()); // <- Эта строка добавлена?
|
||||||
|
|
||||||
srunner_run_all(sr, CK_VERBOSE);
|
srunner_run_all(sr, CK_VERBOSE);
|
||||||
int failed = srunner_ntests_failed(sr);
|
int failed = srunner_ntests_failed(sr);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
#include <check.h>
|
|
||||||
#include "test_helper.h"
|
#include "test_helper.h"
|
||||||
|
|
||||||
START_TEST(test_level_up) {
|
START_TEST(test_level_up) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue