Files
battleship/test/host/test_game_lifecycle.c

160 lines
8.4 KiB
C

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "game_lifecycle.h"
typedef struct { uint32_t value; } test_random_t;
static uint32_t next_random(void *context) {
test_random_t *random = context;
random->value = random->value * 1664525U + 1013904223U;
return random->value;
}
static game_lifecycle_t new_lifecycle(test_random_t *random) {
game_lifecycle_t lifecycle;
game_lifecycle_init(&lifecycle, (random_source_t){.next_u32 = next_random, .context = random});
return lifecycle;
}
static void join_players(game_lifecycle_t *lifecycle, uint8_t *player_1, uint8_t *player_2) {
assert(game_lifecycle_join(lifecycle, ROLE_PLAYER_1, "Alice", player_1) == LIFECYCLE_RESULT_OK);
assert(game_lifecycle_join(lifecycle, ROLE_PLAYER_2, "Bob", player_2) == LIFECYCLE_RESULT_OK);
assert(*player_1 == 0U && *player_2 == 1U);
}
static void finish_for_player_1(game_lifecycle_t *lifecycle, uint8_t player_1) {
game_state_t *state = &lifecycle->game.state;
state->current_player = 0U;
memset(&state->boards[1], 0, sizeof(state->boards[1]));
state->boards[1].ships_alive = 1U;
state->boards[1].ships[0] = (ship_t){.x = 0, .y = 0, .length = 1, .horizontal = true};
state->boards[1].cells[0] = CELL_SHIP;
shot_result_t result = {0};
assert(game_lifecycle_shot(lifecycle, player_1, state->game_id, (coordinate_t){0, 0}, &result) == LIFECYCLE_RESULT_OK);
assert(result.finished && state->phase == PHASE_FINISHED && state->winner == 0U);
}
static void test_capacity_sanitize_and_resume(void) {
test_random_t random = {.value = 1};
game_lifecycle_t lifecycle = new_lifecycle(&random);
uint8_t player_1 = 0;
uint8_t player_2 = 0;
assert(game_lifecycle_join(&lifecycle, ROLE_PLAYER_2, "Bob", &player_2) == LIFECYCLE_RESULT_NO_PLAYER_SLOT);
join_players(&lifecycle, &player_1, &player_2);
assert(game_lifecycle_join(&lifecycle, ROLE_PLAYER_1, "Other", &player_1) == LIFECYCLE_RESULT_NO_PLAYER_SLOT);
assert(game_lifecycle_join(&lifecycle, ROLE_SPECTATOR, "<bad>", &player_1) == LIFECYCLE_RESULT_INVALID_NAME);
for (uint8_t index = 0; index < kSpectatorCapacity; ++index) {
assert(game_lifecycle_join(&lifecycle, ROLE_SPECTATOR, "Watch", &player_1) == LIFECYCLE_RESULT_OK);
}
assert(game_lifecycle_join(&lifecycle, ROLE_SPECTATOR, "Extra", &player_1) == LIFECYCLE_RESULT_NO_SPECTATOR_SLOT);
const uint8_t token[kSessionTokenBytes] = {0};
uint8_t saved_token[kSessionTokenBytes] = {0};
memcpy(saved_token, lifecycle.sessions.entries[player_2].token, sizeof(saved_token));
assert(memcmp(token, saved_token, sizeof(token)) != 0);
assert(game_lifecycle_disconnect(&lifecycle, player_2) == LIFECYCLE_RESULT_OK);
assert(!lifecycle.sessions.entries[player_2].connected);
uint8_t resumed = kSessionCapacity;
assert(game_lifecycle_resume(&lifecycle, saved_token, &resumed) == LIFECYCLE_RESULT_OK);
assert(resumed == player_2 && lifecycle.sessions.entries[resumed].role == ROLE_PLAYER_2);
}
static void test_human_lifecycle_and_guards(void) {
test_random_t random = {.value = 9};
game_lifecycle_t lifecycle = new_lifecycle(&random);
uint8_t player_1 = 0;
uint8_t player_2 = 0;
join_players(&lifecycle, &player_1, &player_2);
const uint32_t game_id = lifecycle.game.state.game_id;
game_lifecycle_t before = lifecycle;
assert(game_lifecycle_configure(&lifecycle, player_2, game_id, MODE_BOT) == LIFECYCLE_RESULT_FORBIDDEN_ROLE);
assert(memcmp(&before, &lifecycle, sizeof(lifecycle)) == 0);
assert(game_lifecycle_start(&lifecycle, player_1, game_id + 1U) == LIFECYCLE_RESULT_STALE_GAME);
assert(game_lifecycle_start(&lifecycle, player_1, game_id) == LIFECYCLE_RESULT_OK);
assert(lifecycle.game.state.phase == PHASE_IN_PROGRESS);
finish_for_player_1(&lifecycle, player_1);
assert(lifecycle.cumulative[0].games == 1U && lifecycle.cumulative[0].wins == 1U);
assert(lifecycle.cumulative[1].games == 1U && lifecycle.cumulative[1].losses == 1U);
assert(game_lifecycle_rematch(&lifecycle, player_1, game_id) == LIFECYCLE_RESULT_OK);
assert(lifecycle.game.state.phase == PHASE_REMATCH_WAIT);
assert(game_lifecycle_rematch(&lifecycle, player_2, game_id) == LIFECYCLE_RESULT_OK);
assert(lifecycle.game.state.phase == PHASE_IN_PROGRESS && lifecycle.game.state.game_id != game_id);
assert(lifecycle.cumulative[0].wins == 1U);
assert(game_lifecycle_disconnect(&lifecycle, player_2) == LIFECYCLE_RESULT_OK);
assert(game_lifecycle_abort(&lifecycle, player_1, lifecycle.game.state.game_id) == LIFECYCLE_RESULT_OK);
assert(lifecycle.game.state.phase == PHASE_LOBBY);
}
static void test_bot_lifecycle_and_stale_actions(void) {
test_random_t random = {.value = 17};
game_lifecycle_t lifecycle = new_lifecycle(&random);
uint8_t player_1 = 0;
assert(game_lifecycle_join(&lifecycle, ROLE_PLAYER_1, "Alice", &player_1) == LIFECYCLE_RESULT_OK);
const uint32_t game_id = lifecycle.game.state.game_id;
assert(game_lifecycle_configure(&lifecycle, player_1, game_id, MODE_BOT) == LIFECYCLE_RESULT_OK);
assert(lifecycle.bot_reserved);
assert(game_lifecycle_join(&lifecycle, ROLE_PLAYER_2, "Bob", &player_1) == LIFECYCLE_RESULT_NO_PLAYER_SLOT);
assert(game_lifecycle_start(&lifecycle, player_1, game_id) == LIFECYCLE_RESULT_OK);
finish_for_player_1(&lifecycle, player_1);
assert(game_lifecycle_rematch(&lifecycle, player_1, game_id) == LIFECYCLE_RESULT_OK);
assert(lifecycle.game.state.phase == PHASE_IN_PROGRESS && lifecycle.game.state.game_id != game_id);
const game_lifecycle_t before = lifecycle;
assert(game_lifecycle_shot(&lifecycle, player_1, game_id, (coordinate_t){0, 0}, NULL) == LIFECYCLE_RESULT_STALE_GAME);
assert(memcmp(&before, &lifecycle, sizeof(lifecycle)) == 0);
}
static void test_leave_and_full_reset(void) {
test_random_t random = {.value = 23U};
game_lifecycle_t lifecycle = new_lifecycle(&random);
uint8_t player_1 = 0;
uint8_t player_2 = 0;
uint8_t spectator = 0;
join_players(&lifecycle, &player_1, &player_2);
assert(game_lifecycle_join(&lifecycle, ROLE_SPECTATOR, "Watch", &spectator) == LIFECYCLE_RESULT_OK);
const uint32_t lobby_game_id = lifecycle.game.state.game_id;
assert(game_lifecycle_leave(&lifecycle, spectator) == LIFECYCLE_RESULT_OK);
assert(!lifecycle.sessions.entries[spectator].occupied && lifecycle.game.state.game_id == lobby_game_id);
assert(game_lifecycle_start(&lifecycle, player_1, lobby_game_id) == LIFECYCLE_RESULT_OK);
assert(game_lifecycle_leave(&lifecycle, player_2) == LIFECYCLE_RESULT_OK);
assert(!lifecycle.sessions.entries[player_2].occupied && lifecycle.sessions.entries[player_1].occupied);
assert(lifecycle.game.state.phase == PHASE_LOBBY && lifecycle.game.state.game_id != lobby_game_id);
const uint32_t reset_game_id = lifecycle.game.state.game_id;
lifecycle.cumulative[0].games = 3U;
assert(game_lifecycle_reset(&lifecycle, player_1, reset_game_id) == LIFECYCLE_RESULT_OK);
assert(lifecycle.game.state.phase == PHASE_LOBBY && lifecycle.game.state.game_id != reset_game_id);
assert(!lifecycle.sessions.entries[0].occupied && !lifecycle.sessions.entries[1].occupied);
assert(lifecycle.cumulative[0].games == 0U && lifecycle.recovery_reason == RECOVERY_REASON_GAME_RESET);
assert(game_lifecycle_reset(&lifecycle, player_1, reset_game_id) == LIFECYCLE_RESULT_FORBIDDEN_ROLE);
}
static void test_fifty_mixed_recovery_cycles(void) {
test_random_t random = {.value = 41U};
game_lifecycle_t lifecycle = new_lifecycle(&random);
for (uint8_t cycle = 0; cycle < 50U; ++cycle) {
uint8_t player = kSessionCapacity;
assert(game_lifecycle_join(&lifecycle, ROLE_AUTO_PLAYER, "Alice", &player) == LIFECYCLE_RESULT_OK && player == 0U);
const uint32_t game_id = lifecycle.game.state.game_id;
if (cycle % 3U == 0U) {
assert(game_lifecycle_leave(&lifecycle, player) == LIFECYCLE_RESULT_OK);
} else if (cycle % 3U == 1U) {
assert(game_lifecycle_configure(&lifecycle, player, game_id, MODE_BOT) == LIFECYCLE_RESULT_OK);
assert(game_lifecycle_start(&lifecycle, player, game_id) == LIFECYCLE_RESULT_OK);
assert(game_lifecycle_leave(&lifecycle, player) == LIFECYCLE_RESULT_OK);
} else {
assert(game_lifecycle_reset(&lifecycle, player, game_id) == LIFECYCLE_RESULT_OK);
}
assert(lifecycle.game.state.phase == PHASE_LOBBY && !lifecycle.sessions.entries[0].occupied);
}
}
int main(void) {
test_capacity_sanitize_and_resume();
test_human_lifecycle_and_guards();
test_bot_lifecycle_and_stale_actions();
test_leave_and_full_reset();
test_fifty_mixed_recovery_cycles();
puts("game lifecycle tests passed");
return 0;
}