mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-05-10 19:00:57 +08:00
test: add chat_room unit tests and integrate into build
- Add 11 unit tests for chat_room.c covering: create/destroy, message add/overflow, broadcast sequence, get_message bounds, client add/remove/capacity, and null argument handling - Add unit-test target to root Makefile so `make test` runs unit tests before integration tests - Add common.c to unit test link dependencies (needed for tnt_state_path) - Guard _DARWIN_C_SOURCE define to prevent -Wmacro-redefined warning
This commit is contained in:
parent
0de13a6314
commit
ecc45f285c
4 changed files with 232 additions and 6 deletions
8
Makefile
8
Makefile
|
|
@ -71,10 +71,14 @@ check:
|
||||||
@command -v clang-tidy >/dev/null 2>&1 && clang-tidy src/*.c -- -Iinclude $(INCLUDES) || echo "clang-tidy not installed"
|
@command -v clang-tidy >/dev/null 2>&1 && clang-tidy src/*.c -- -Iinclude $(INCLUDES) || echo "clang-tidy not installed"
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
test: all
|
test: all unit-test
|
||||||
@echo "Running tests..."
|
@echo "Running integration tests..."
|
||||||
@cd tests && ./test_basic.sh
|
@cd tests && ./test_basic.sh
|
||||||
|
|
||||||
|
unit-test:
|
||||||
|
@echo "Running unit tests..."
|
||||||
|
@$(MAKE) -C tests/unit run
|
||||||
|
|
||||||
# Show build info
|
# Show build info
|
||||||
info:
|
info:
|
||||||
@echo "Compiler: $(CC)"
|
@echo "Compiler: $(CC)"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef _DEFAULT_SOURCE
|
#ifndef _DEFAULT_SOURCE
|
||||||
#define _DEFAULT_SOURCE /* for timegm() on glibc */
|
#define _DEFAULT_SOURCE /* for timegm() on glibc */
|
||||||
#endif
|
#endif
|
||||||
#ifdef __APPLE__
|
#if defined(__APPLE__) && !defined(_DARWIN_C_SOURCE)
|
||||||
#define _DARWIN_C_SOURCE /* for timegm() on macOS */
|
#define _DARWIN_C_SOURCE /* for timegm() on macOS */
|
||||||
#endif
|
#endif
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,21 @@
|
||||||
# Unit Tests Makefile
|
# Unit Tests Makefile
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -Wall -Wextra -std=c11 -I../../include
|
CFLAGS = -Wall -Wextra -std=c11 -D_XOPEN_SOURCE=700 -I../../include
|
||||||
LDFLAGS = -pthread
|
LDFLAGS = -pthread
|
||||||
|
|
||||||
|
# Detect macOS for _DARWIN_C_SOURCE (needed for timegm)
|
||||||
|
UNAME_S := $(shell uname -s)
|
||||||
|
ifeq ($(UNAME_S),Darwin)
|
||||||
|
CFLAGS += -D_DARWIN_C_SOURCE
|
||||||
|
endif
|
||||||
|
|
||||||
# Source files
|
# Source files
|
||||||
UTF8_SRC = ../../src/utf8.c
|
UTF8_SRC = ../../src/utf8.c
|
||||||
MESSAGE_SRC = ../../src/message.c
|
MESSAGE_SRC = ../../src/message.c
|
||||||
|
COMMON_SRC = ../../src/common.c
|
||||||
|
CHAT_ROOM_SRC = ../../src/chat_room.c
|
||||||
|
|
||||||
TESTS = test_utf8 test_message
|
TESTS = test_utf8 test_message test_chat_room
|
||||||
|
|
||||||
.PHONY: all clean run
|
.PHONY: all clean run
|
||||||
|
|
||||||
|
|
@ -16,7 +24,10 @@ all: $(TESTS)
|
||||||
test_utf8: test_utf8.c $(UTF8_SRC)
|
test_utf8: test_utf8.c $(UTF8_SRC)
|
||||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
test_message: test_message.c $(MESSAGE_SRC) $(UTF8_SRC)
|
test_message: test_message.c $(MESSAGE_SRC) $(UTF8_SRC) $(COMMON_SRC)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
test_chat_room: test_chat_room.c $(CHAT_ROOM_SRC) $(MESSAGE_SRC) $(UTF8_SRC) $(COMMON_SRC)
|
||||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
run: all
|
run: all
|
||||||
|
|
@ -25,6 +36,9 @@ run: all
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "=== Running Message Tests ==="
|
@echo "=== Running Message Tests ==="
|
||||||
./test_message
|
./test_message
|
||||||
|
@echo ""
|
||||||
|
@echo "=== Running Chat Room Tests ==="
|
||||||
|
./test_chat_room
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(TESTS) *.o test_messages.log
|
rm -f $(TESTS) *.o test_messages.log
|
||||||
|
|
|
||||||
208
tests/unit/test_chat_room.c
Normal file
208
tests/unit/test_chat_room.c
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
/* Unit tests for chat_room functions */
|
||||||
|
|
||||||
|
/* Minimal client_t stub — only pointer identity matters for add/remove.
|
||||||
|
* We define `struct client` before including chat_room.h so the forward
|
||||||
|
* declaration resolves without pulling in ssh_server.h / libssh. */
|
||||||
|
#include "../../include/common.h"
|
||||||
|
|
||||||
|
struct client {
|
||||||
|
char username[MAX_USERNAME_LEN];
|
||||||
|
int dummy;
|
||||||
|
};
|
||||||
|
typedef struct client client_t;
|
||||||
|
|
||||||
|
#include "../../include/chat_room.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define TEST(name) static void test_##name()
|
||||||
|
#define RUN_TEST(name) do { \
|
||||||
|
printf("Running %s... ", #name); \
|
||||||
|
test_##name(); \
|
||||||
|
printf("✓\n"); \
|
||||||
|
tests_passed++; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
static int tests_passed = 0;
|
||||||
|
|
||||||
|
static message_t make_msg(const char *user, const char *content) {
|
||||||
|
message_t m = { .timestamp = time(NULL) };
|
||||||
|
strncpy(m.username, user, MAX_USERNAME_LEN - 1);
|
||||||
|
strncpy(m.content, content, MAX_MESSAGE_LEN - 1);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(room_create_destroy) {
|
||||||
|
chat_room_t *room = room_create();
|
||||||
|
assert(room != NULL);
|
||||||
|
assert(room->client_count == 0);
|
||||||
|
assert(room->client_capacity > 0);
|
||||||
|
room_destroy(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(room_add_message_single) {
|
||||||
|
chat_room_t *room = room_create();
|
||||||
|
message_t msg = make_msg("alice", "hello");
|
||||||
|
|
||||||
|
room_add_message(room, &msg);
|
||||||
|
assert(room->message_count == 1);
|
||||||
|
assert(strcmp(room->messages[0].username, "alice") == 0);
|
||||||
|
assert(strcmp(room->messages[0].content, "hello") == 0);
|
||||||
|
|
||||||
|
room_destroy(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(room_add_message_overflow) {
|
||||||
|
chat_room_t *room = room_create();
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_MESSAGES + 10; i++) {
|
||||||
|
char content[32];
|
||||||
|
snprintf(content, sizeof(content), "msg %d", i);
|
||||||
|
message_t msg = make_msg("user", content);
|
||||||
|
room_add_message(room, &msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(room->message_count == MAX_MESSAGES);
|
||||||
|
|
||||||
|
char expected[32];
|
||||||
|
snprintf(expected, sizeof(expected), "msg %d", 10);
|
||||||
|
assert(strcmp(room->messages[0].content, expected) == 0);
|
||||||
|
|
||||||
|
snprintf(expected, sizeof(expected), "msg %d", MAX_MESSAGES + 9);
|
||||||
|
assert(strcmp(room->messages[MAX_MESSAGES - 1].content, expected) == 0);
|
||||||
|
|
||||||
|
room_destroy(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(room_broadcast_increments_seq) {
|
||||||
|
chat_room_t *room = room_create();
|
||||||
|
g_room = room;
|
||||||
|
|
||||||
|
uint64_t seq1 = room_get_update_seq(room);
|
||||||
|
message_t msg = make_msg("bob", "hi");
|
||||||
|
room_broadcast(room, &msg);
|
||||||
|
uint64_t seq2 = room_get_update_seq(room);
|
||||||
|
|
||||||
|
assert(seq2 > seq1);
|
||||||
|
assert(room_get_message_count(room) == 1);
|
||||||
|
|
||||||
|
g_room = NULL;
|
||||||
|
room_destroy(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(room_get_message_valid) {
|
||||||
|
chat_room_t *room = room_create();
|
||||||
|
message_t msg = make_msg("carol", "test");
|
||||||
|
room_add_message(room, &msg);
|
||||||
|
|
||||||
|
message_t out;
|
||||||
|
assert(room_get_message(room, 0, &out) == true);
|
||||||
|
assert(strcmp(out.username, "carol") == 0);
|
||||||
|
assert(strcmp(out.content, "test") == 0);
|
||||||
|
|
||||||
|
room_destroy(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(room_get_message_invalid_index) {
|
||||||
|
chat_room_t *room = room_create();
|
||||||
|
|
||||||
|
message_t out;
|
||||||
|
assert(room_get_message(room, 0, &out) == false);
|
||||||
|
assert(room_get_message(room, -1, &out) == false);
|
||||||
|
assert(room_get_message(room, 999, &out) == false);
|
||||||
|
|
||||||
|
room_destroy(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(room_get_message_null_args) {
|
||||||
|
chat_room_t *room = room_create();
|
||||||
|
message_t out;
|
||||||
|
|
||||||
|
assert(room_get_message(NULL, 0, &out) == false);
|
||||||
|
assert(room_get_message(room, 0, NULL) == false);
|
||||||
|
|
||||||
|
room_destroy(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(room_client_count) {
|
||||||
|
chat_room_t *room = room_create();
|
||||||
|
assert(room_get_client_count(room) == 0);
|
||||||
|
|
||||||
|
client_t c1 = {0};
|
||||||
|
client_t c2 = {0};
|
||||||
|
assert(room_add_client(room, &c1) == 0);
|
||||||
|
assert(room_get_client_count(room) == 1);
|
||||||
|
assert(room_add_client(room, &c2) == 0);
|
||||||
|
assert(room_get_client_count(room) == 2);
|
||||||
|
|
||||||
|
room_remove_client(room, &c1);
|
||||||
|
assert(room_get_client_count(room) == 1);
|
||||||
|
|
||||||
|
room_remove_client(room, &c2);
|
||||||
|
assert(room_get_client_count(room) == 0);
|
||||||
|
|
||||||
|
room_destroy(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(room_remove_nonexistent_client) {
|
||||||
|
chat_room_t *room = room_create();
|
||||||
|
client_t c1 = {0};
|
||||||
|
client_t c2 = {0};
|
||||||
|
|
||||||
|
room_add_client(room, &c1);
|
||||||
|
room_remove_client(room, &c2);
|
||||||
|
assert(room_get_client_count(room) == 1);
|
||||||
|
|
||||||
|
room_destroy(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(room_add_client_full) {
|
||||||
|
chat_room_t *room = room_create();
|
||||||
|
client_t clients[MAX_CLIENTS + 1];
|
||||||
|
memset(clients, 0, sizeof(clients));
|
||||||
|
|
||||||
|
for (int i = 0; i < room->client_capacity; i++) {
|
||||||
|
assert(room_add_client(room, &clients[i]) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(room_add_client(room, &clients[room->client_capacity]) == -1);
|
||||||
|
assert(room_get_client_count(room) == room->client_capacity);
|
||||||
|
|
||||||
|
room_destroy(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(room_message_count_threadsafe) {
|
||||||
|
chat_room_t *room = room_create();
|
||||||
|
|
||||||
|
assert(room_get_message_count(room) == 0);
|
||||||
|
|
||||||
|
message_t msg = make_msg("dave", "msg");
|
||||||
|
room_broadcast(room, &msg);
|
||||||
|
assert(room_get_message_count(room) == 1);
|
||||||
|
|
||||||
|
room_broadcast(room, &msg);
|
||||||
|
room_broadcast(room, &msg);
|
||||||
|
assert(room_get_message_count(room) == 3);
|
||||||
|
|
||||||
|
room_destroy(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
printf("=== Chat Room Unit Tests ===\n");
|
||||||
|
|
||||||
|
RUN_TEST(room_create_destroy);
|
||||||
|
RUN_TEST(room_add_message_single);
|
||||||
|
RUN_TEST(room_add_message_overflow);
|
||||||
|
RUN_TEST(room_broadcast_increments_seq);
|
||||||
|
RUN_TEST(room_get_message_valid);
|
||||||
|
RUN_TEST(room_get_message_invalid_index);
|
||||||
|
RUN_TEST(room_get_message_null_args);
|
||||||
|
RUN_TEST(room_client_count);
|
||||||
|
RUN_TEST(room_remove_nonexistent_client);
|
||||||
|
RUN_TEST(room_add_client_full);
|
||||||
|
RUN_TEST(room_message_count_threadsafe);
|
||||||
|
|
||||||
|
printf("\nAll %d tests passed!\n", tests_passed);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue