Files
maze32/test/host/test_lobby_protocol.c

184 lines
9.4 KiB
C

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "maze32_lobby.h"
#include "maze32_protocol.h"
static const char* TOKEN_1 = "000102030405060708090a0b0c0d0e0f";
static const char* TOKEN_2 = "101112131415161718191a1b1c1d1e1f";
static const char* OBSERVER_TOKENS[] = {
"202122232425262728292a2b2c2d2e2f", "303132333435363738393a3b3c3d3e3f",
"404142434445464748494a4b4c4d4e4f", "505152535455565758595a5b5c5d5e5f",
"606162636465666768696a6b6c6d6e6f",
};
static void test_protocol_schema(void) {
maze32_client_message_t message;
const char* join = "{\"v\":1,\"type\":\"join\",\"seq\":1,\"requestId\":\"join-1\","
"\"payload\":{\"role\":\"player\",\"name\":\"Лорд Лабиринт\"}}";
assert(maze32_protocol_parse(join, strlen(join), &message) == MAZE32_PROTOCOL_OK);
assert(message.type == MAZE32_MESSAGE_JOIN);
assert(message.sequence == 1U);
assert(message.role == MAZE32_ROLE_PLAYER);
assert(strcmp(message.name, "Лорд Лабиринт") == 0);
const char* restore = "{\"v\":1,\"type\":\"restore\",\"seq\":2,\"payload\":{\"token\":"
"\"000102030405060708090a0b0c0d0e0f\"}}";
assert(maze32_protocol_parse(restore, strlen(restore), &message) == MAZE32_PROTOCOL_OK);
assert(message.type == MAZE32_MESSAGE_RESTORE);
assert(strcmp(message.token, TOKEN_1) == 0);
const char* move = "{\"v\":1,\"type\":\"move\",\"seq\":3,\"payload\":{\"direction\":\"west\"}}";
assert(maze32_protocol_parse(move, strlen(move), &message) == MAZE32_PROTOCOL_OK);
assert(message.type == MAZE32_MESSAGE_MOVE);
assert(strcmp(message.direction, "west") == 0);
const char* drop_key = "{\"v\":1,\"type\":\"drop_key\",\"seq\":4,\"payload\":{}}";
assert(maze32_protocol_parse(drop_key, strlen(drop_key), &message) == MAZE32_PROTOCOL_OK);
assert(message.type == MAZE32_MESSAGE_DROP_KEY);
const char* restart = "{\"v\":1,\"type\":\"restart\",\"seq\":5,\"payload\":{}}";
assert(maze32_protocol_parse(restart, strlen(restart), &message) == MAZE32_PROTOCOL_OK);
assert(message.type == MAZE32_MESSAGE_RESTART);
const char* leave = "{\"v\":1,\"type\":\"leave\",\"seq\":6,\"payload\":{}}";
assert(maze32_protocol_parse(leave, strlen(leave), &message) == MAZE32_PROTOCOL_OK);
assert(message.type == MAZE32_MESSAGE_LEAVE);
const char* diagonal =
"{\"v\":1,\"type\":\"move\",\"seq\":7,\"payload\":{\"direction\":\"northwest\"}}";
assert(maze32_protocol_parse(diagonal, strlen(diagonal), &message) ==
MAZE32_PROTOCOL_INVALID_PAYLOAD);
const char* bad_version = "{\"v\":2,\"type\":\"start\",\"seq\":1,\"payload\":{}}";
assert(maze32_protocol_parse(bad_version, strlen(bad_version), &message) ==
MAZE32_PROTOCOL_UNSUPPORTED_VERSION);
const char* unknown = "{\"v\":1,\"type\":\"teleport\",\"seq\":1,\"payload\":{}}";
assert(maze32_protocol_parse(unknown, strlen(unknown), &message) ==
MAZE32_PROTOCOL_UNKNOWN_TYPE);
const char* extra = "{\"v\":1,\"type\":\"start\",\"seq\":1,\"payload\":{\"power\":\"yes\"}}";
assert(maze32_protocol_parse(extra, strlen(extra), &message) ==
MAZE32_PROTOCOL_INVALID_PAYLOAD);
const char* duplicate = "{\"v\":1,\"v\":1,\"type\":\"start\",\"seq\":1,\"payload\":{}}";
assert(maze32_protocol_parse(duplicate, strlen(duplicate), &message) ==
MAZE32_PROTOCOL_MALFORMED);
char oversized[MAZE32_PROTOCOL_MESSAGE_LIMIT + 2U];
memset(oversized, 'x', sizeof(oversized));
assert(maze32_protocol_parse(oversized, sizeof(oversized), &message) ==
MAZE32_PROTOCOL_TOO_LARGE);
}
static void test_lobby_limits_restoration_and_authority(void) {
maze32_lobby_t lobby;
maze32_lobby_init(&lobby);
maze32_session_t* player_1 = NULL;
maze32_session_t* player_2 = NULL;
maze32_session_t* extra = NULL;
assert(maze32_lobby_join_player(&lobby, 10, "Капитан Клавиша", TOKEN_1, &player_1) ==
MAZE32_LOBBY_OK);
assert(player_1->player_slot == 1U);
uint32_t revision = lobby.revision;
assert(maze32_lobby_join_player(&lobby, 11, "Капитан Клавиша", TOKEN_2, &player_2) ==
MAZE32_LOBBY_DUPLICATE_NAME);
assert(lobby.revision == revision);
assert(maze32_lobby_join_player(&lobby, 11, "Мышь в доспехах", TOKEN_2, &player_2) ==
MAZE32_LOBBY_OK);
assert(player_2->player_slot == 2U);
assert(maze32_lobby_ready(&lobby));
for (unsigned int index = 0U; index < MAZE32_OBSERVER_LIMIT; ++index) {
maze32_session_t* observer = NULL;
assert(maze32_lobby_join_observer(&lobby, 20 + (int)index, OBSERVER_TOKENS[index],
&observer) == MAZE32_LOBBY_OK);
assert(observer->role == MAZE32_ROLE_OBSERVER);
}
assert(maze32_lobby_connected_observers(&lobby) == MAZE32_OBSERVER_LIMIT);
maze32_lobby_phase_t phase_before_observer_disconnect = lobby.phase;
maze32_lobby_disconnect(&lobby, 20);
assert(lobby.phase == phase_before_observer_disconnect);
assert(maze32_lobby_connected_observers(&lobby) == MAZE32_OBSERVER_LIMIT - 1U);
assert(maze32_lobby_restore(&lobby, 20, OBSERVER_TOKENS[0], &extra) == MAZE32_LOBBY_OK);
revision = lobby.revision;
assert(maze32_lobby_join_observer(&lobby, 30, OBSERVER_TOKENS[4], &extra) ==
MAZE32_LOBBY_ROLE_FULL);
assert(lobby.revision == revision);
assert(maze32_lobby_start(&lobby, 11, 1000U) == MAZE32_LOBBY_NOT_OWNER);
assert(lobby.phase == MAZE32_LOBBY_WAITING);
maze32_lobby_disconnect(&lobby, 11);
assert(!maze32_lobby_ready(&lobby));
assert(maze32_lobby_start(&lobby, 10, 1000U) == MAZE32_LOBBY_NOT_READY);
assert(maze32_lobby_restore(&lobby, 31, TOKEN_2, &player_2) == MAZE32_LOBBY_OK);
assert(player_2->player_slot == 2U && player_2->connection_id == 31);
assert(maze32_lobby_start(&lobby, 10, 2000U) == MAZE32_LOBBY_OK);
assert(lobby.phase == MAZE32_LOBBY_COUNTDOWN);
assert(lobby.activation_ms == 5000U);
assert(!maze32_lobby_can_reconfigure(&lobby));
assert(!maze32_lobby_activate_if_due(&lobby, 4999U));
assert(maze32_lobby_activate_if_due(&lobby, 5000U));
assert(lobby.phase == MAZE32_LOBBY_ACTIVE);
maze32_session_t sessions_before_reset[MAZE32_SESSION_LIMIT];
memcpy(sessions_before_reset, lobby.sessions, sizeof(sessions_before_reset));
for (uint64_t cycle = 0U; cycle < 128U; ++cycle) {
revision = lobby.revision;
maze32_lobby_reset_match(&lobby);
assert(lobby.phase == MAZE32_LOBBY_WAITING);
assert(lobby.countdown_started_ms == 0U && lobby.activation_ms == 0U);
assert(lobby.revision == revision + 1U);
assert(maze32_lobby_ready(&lobby));
assert(memcmp(lobby.sessions, sessions_before_reset, sizeof(sessions_before_reset)) == 0);
uint64_t started_ms = 10000U + cycle * 4000U;
assert(maze32_lobby_start(&lobby, 10, started_ms) == MAZE32_LOBBY_OK);
assert(maze32_lobby_activate_if_due(&lobby, started_ms + MAZE32_COUNTDOWN_DURATION_MS));
}
puts("lobby repeated-match reset: PASS (128 cycles)");
}
static void test_explicit_leave_releases_only_departing_session(void) {
maze32_lobby_t lobby;
maze32_lobby_init(&lobby);
maze32_session_t* player_1 = NULL;
maze32_session_t* player_2 = NULL;
maze32_session_t* observer = NULL;
assert(maze32_lobby_join_player(&lobby, 10, "Первый", TOKEN_1, &player_1) ==
MAZE32_LOBBY_OK);
assert(maze32_lobby_join_player(&lobby, 11, "Второй", TOKEN_2, &player_2) ==
MAZE32_LOBBY_OK);
assert(maze32_lobby_join_observer(&lobby, 20, OBSERVER_TOKENS[0], &observer) ==
MAZE32_LOBBY_OK);
maze32_role_t role = MAZE32_ROLE_NONE;
unsigned int player_slot = 0U;
assert(maze32_lobby_leave(&lobby, 10, &role, &player_slot));
assert(role == MAZE32_ROLE_PLAYER && player_slot == 1U);
assert(maze32_lobby_session_for_connection(&lobby, 10) == NULL);
assert(maze32_lobby_session_for_connection(&lobby, 11) == player_2);
assert(maze32_lobby_session_for_connection(&lobby, 20) == observer);
assert(maze32_lobby_restore(&lobby, 30, TOKEN_1, &player_1) == MAZE32_LOBBY_INVALID_TOKEN);
assert(maze32_lobby_join_player(&lobby, 30, "Новый первый", OBSERVER_TOKENS[1], &player_1) ==
MAZE32_LOBBY_OK);
assert(player_1->player_slot == 1U);
assert(!maze32_lobby_leave(&lobby, 10, NULL, NULL));
assert(maze32_lobby_leave(&lobby, 11, &role, &player_slot));
assert(role == MAZE32_ROLE_PLAYER && player_slot == 2U);
assert(maze32_lobby_session_for_connection(&lobby, 30) == player_1);
assert(maze32_lobby_session_for_connection(&lobby, 20) == observer);
assert(maze32_lobby_restore(&lobby, 31, TOKEN_2, &player_2) == MAZE32_LOBBY_INVALID_TOKEN);
assert(maze32_lobby_join_player(&lobby, 31, "Новый второй", OBSERVER_TOKENS[2], &player_2) ==
MAZE32_LOBBY_OK);
assert(player_2->player_slot == 2U);
assert(maze32_lobby_leave(&lobby, 20, &role, &player_slot));
assert(role == MAZE32_ROLE_OBSERVER && player_slot == 0U);
assert(maze32_lobby_session_for_connection(&lobby, 30) == player_1);
assert(maze32_lobby_session_for_connection(&lobby, 31) == player_2);
puts("explicit leave session revocation and slot reuse: PASS");
}
int main(void) {
test_protocol_schema();
test_lobby_limits_restoration_and_authority();
test_explicit_leave_releases_only_departing_session();
puts("lobby and protocol tests: PASS");
return 0;
}