You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
280 lines
7.5 KiB
280 lines
7.5 KiB
// test_etcp_link_id.c - Unit test for etcp_find_free_local_link_id function |
|
// Tests: empty connection, full allocation, random deletion/addition cycles |
|
|
|
#include "../src/etcp_connections.h" |
|
#include "../src/etcp.h" |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <time.h> |
|
#include <assert.h> |
|
#include "../lib/mem.h" |
|
|
|
// Mock ETCP_LINK structure for testing (minimal version) |
|
struct TEST_LINK { |
|
struct TEST_LINK* next; |
|
uint8_t local_link_id; |
|
}; |
|
|
|
// Helper function to create a mock ETCP_CONN with empty links list |
|
static struct ETCP_CONN* create_test_conn(void) { |
|
struct ETCP_CONN* conn = u_calloc(1, sizeof(struct ETCP_CONN)); |
|
if (!conn) { |
|
fprintf(stderr, "Failed to allocate test connection\n"); |
|
exit(1); |
|
} |
|
conn->links = NULL; |
|
return conn; |
|
} |
|
|
|
// Helper function to add a link with specific id to connection |
|
static void add_link_with_id(struct ETCP_CONN* conn, uint8_t id) { |
|
struct ETCP_LINK* link = u_calloc(1, sizeof(struct ETCP_LINK)); |
|
if (!link) { |
|
fprintf(stderr, "Failed to allocate test link\n"); |
|
exit(1); |
|
} |
|
link->local_link_id = id; |
|
link->next = conn->links; |
|
conn->links = link; |
|
} |
|
|
|
// Helper function to remove link with specific id |
|
static int remove_link_with_id(struct ETCP_CONN* conn, uint8_t id) { |
|
struct ETCP_LINK** pp = &conn->links; |
|
while (*pp) { |
|
if ((*pp)->local_link_id == id) { |
|
struct ETCP_LINK* to_remove = *pp; |
|
*pp = (*pp)->next; |
|
u_free(to_remove); |
|
return 1; // Success |
|
} |
|
pp = &(*pp)->next; |
|
} |
|
return 0; // Not found |
|
} |
|
|
|
// Helper function to count links |
|
static int count_links(struct ETCP_CONN* conn) { |
|
int count = 0; |
|
struct ETCP_LINK* link = conn->links; |
|
while (link) { |
|
count++; |
|
link = link->next; |
|
} |
|
return count; |
|
} |
|
|
|
// Helper function to free all links |
|
static void free_all_links(struct ETCP_CONN* conn) { |
|
struct ETCP_LINK* link = conn->links; |
|
while (link) { |
|
struct ETCP_LINK* next = link->next; |
|
u_free(link); |
|
link = next; |
|
} |
|
conn->links = NULL; |
|
} |
|
|
|
// Test 1: NULL argument should return -1 |
|
static void test_null_argument(void) { |
|
printf("Test 1: NULL argument... "); |
|
int result = etcp_find_free_local_link_id(NULL); |
|
assert(result == -1); |
|
printf("PASSED\n"); |
|
} |
|
|
|
// Test 2: Empty connection should return 0 |
|
static void test_empty_connection(void) { |
|
printf("Test 2: Empty connection... "); |
|
struct ETCP_CONN* conn = create_test_conn(); |
|
int result = etcp_find_free_local_link_id(conn); |
|
assert(result == 0); |
|
u_free(conn); |
|
printf("PASSED\n"); |
|
} |
|
|
|
// Test 3: Fill all 256, randomly delete and add back (30 cycles) |
|
static void test_random_deletion_addition(void) { |
|
printf("Test 3: Random deletion/addition cycles (30 iterations)...\n"); |
|
|
|
for (int cycle = 0; cycle < 30; cycle++) { |
|
struct ETCP_CONN* conn = create_test_conn(); |
|
uint8_t used[256] = {0}; |
|
|
|
// Fill all 256 ids |
|
for (int i = 0; i < 256; i++) { |
|
add_link_with_id(conn, i); |
|
used[i] = 1; |
|
} |
|
|
|
assert(count_links(conn) == 256); |
|
|
|
// Random number of links to delete (30-100) |
|
int num_to_delete = 30 + (rand() % 71); |
|
int deleted_ids[100]; |
|
int deleted_count = 0; |
|
|
|
// Randomly delete links |
|
while (deleted_count < num_to_delete) { |
|
int id = rand() % 256; |
|
if (used[id] && remove_link_with_id(conn, id)) { |
|
used[id] = 0; |
|
deleted_ids[deleted_count++] = id; |
|
} |
|
} |
|
|
|
assert(count_links(conn) == 256 - num_to_delete); |
|
|
|
// Add back the same number of links |
|
int added_ids[100]; |
|
int added_count = 0; |
|
int prev_id = -1; |
|
|
|
while (added_count < num_to_delete) { |
|
int new_id = etcp_find_free_local_link_id(conn); |
|
|
|
// Verify the id is actually free |
|
assert(new_id >= 0 && new_id < 256); |
|
assert(used[new_id] == 0); |
|
|
|
// Verify ids are returned in ascending order (filling gaps from smallest) |
|
assert(new_id > prev_id); |
|
prev_id = new_id; |
|
|
|
// Add the link |
|
add_link_with_id(conn, new_id); |
|
used[new_id] = 1; |
|
added_ids[added_count++] = new_id; |
|
} |
|
|
|
assert(count_links(conn) == 256); |
|
|
|
// Verify all ids are marked as used |
|
for (int i = 0; i < 256; i++) { |
|
assert(used[i] == 1); |
|
} |
|
|
|
// Verify no duplicates |
|
uint8_t check[256] = {0}; |
|
struct ETCP_LINK* link = conn->links; |
|
while (link) { |
|
assert(check[link->local_link_id] == 0); // No duplicate |
|
check[link->local_link_id] = 1; |
|
link = link->next; |
|
} |
|
|
|
// Cleanup |
|
free_all_links(conn); |
|
u_free(conn); |
|
|
|
if ((cycle + 1) % 10 == 0) { |
|
printf(" Completed %d cycles...\n", cycle + 1); |
|
} |
|
} |
|
|
|
printf("Test 3: PASSED (all 30 cycles)\n"); |
|
} |
|
|
|
// Test 4: All 256 occupied should return -1 |
|
static void test_all_occupied(void) { |
|
printf("Test 4: All 256 occupied... "); |
|
struct ETCP_CONN* conn = create_test_conn(); |
|
|
|
// Fill all 256 |
|
for (int i = 0; i < 256; i++) { |
|
add_link_with_id(conn, i); |
|
} |
|
|
|
int result = etcp_find_free_local_link_id(conn); |
|
assert(result == -1); |
|
|
|
free_all_links(conn); |
|
u_free(conn); |
|
printf("PASSED\n"); |
|
} |
|
|
|
// Test 5: Delete specific ids (5, 10, 100), should return 5 |
|
static void test_specific_deletion(void) { |
|
printf("Test 5: Delete ids 5, 10, 100... "); |
|
struct ETCP_CONN* conn = create_test_conn(); |
|
|
|
// Fill all 256 |
|
for (int i = 0; i < 256; i++) { |
|
add_link_with_id(conn, i); |
|
} |
|
|
|
// Delete specific ids |
|
assert(remove_link_with_id(conn, 5)); |
|
assert(remove_link_with_id(conn, 10)); |
|
assert(remove_link_with_id(conn, 100)); |
|
|
|
// Should return 5 (smallest free) |
|
int result = etcp_find_free_local_link_id(conn); |
|
assert(result == 5); |
|
|
|
free_all_links(conn); |
|
u_free(conn); |
|
printf("PASSED\n"); |
|
} |
|
|
|
// Test 6: Delete id 0, should return 0 |
|
static void test_delete_zero(void) { |
|
printf("Test 6: Delete id 0... "); |
|
struct ETCP_CONN* conn = create_test_conn(); |
|
|
|
// Fill all 256 |
|
for (int i = 0; i < 256; i++) { |
|
add_link_with_id(conn, i); |
|
} |
|
|
|
// Delete id 0 |
|
assert(remove_link_with_id(conn, 0)); |
|
|
|
// Should return 0 |
|
int result = etcp_find_free_local_link_id(conn); |
|
assert(result == 0); |
|
|
|
free_all_links(conn); |
|
u_free(conn); |
|
printf("PASSED\n"); |
|
} |
|
|
|
// Test 7: Delete all, should return 0 |
|
static void test_delete_all(void) { |
|
printf("Test 7: Delete all... "); |
|
struct ETCP_CONN* conn = create_test_conn(); |
|
|
|
// Fill all 256 |
|
for (int i = 0; i < 256; i++) { |
|
add_link_with_id(conn, i); |
|
} |
|
|
|
// Delete all |
|
free_all_links(conn); |
|
|
|
// Should return 0 |
|
int result = etcp_find_free_local_link_id(conn); |
|
assert(result == 0); |
|
|
|
u_free(conn); |
|
printf("PASSED\n"); |
|
} |
|
|
|
int main(void) { |
|
printf("=== ETCP Link ID Unit Tests ===\n\n"); |
|
|
|
// Initialize random seed |
|
srand(time(NULL)); |
|
|
|
test_null_argument(); |
|
test_empty_connection(); |
|
test_random_deletion_addition(); |
|
test_all_occupied(); |
|
test_specific_deletion(); |
|
test_delete_zero(); |
|
test_delete_all(); |
|
|
|
printf("\n=== All tests PASSED ===\n"); |
|
return 0; |
|
}
|
|
|