Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ deps += $(LIB_OBJS:%.o=%.o.d)
APPS := coop echo hello mqueues semaphore mutex cond \
pipes pipes_small pipes_struct prodcons progress \
rtsched suspend test64 timer timer_kill \
cpubench test_libc
cpubench test_utils

# Output files for __link target
IMAGE_BASE := $(BUILD_DIR)/image
Expand Down
84 changes: 83 additions & 1 deletion app/test_libc.c → app/test_utils.c
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
/* LibC Test Suite - Comprehensive tests for standard library functions.
/* Utility Test Suite - Comprehensive tests for utilities and helpers.
*
* Current Coverage:
* - vsnprintf/snprintf: Buffer overflow protection
* * C99 semantics, truncation behavior, ISR safety
* * Format specifiers: %s, %d, %u, %x, %p, %c, %%
* * Edge cases: size=0, size=1, truncation, null termination
*
* - list operations: pushback node, remove node
*
* Future Tests (Planned):
* - String functions: strlen, strcmp, strcpy, strncpy, memcpy, memset
* - Memory allocation: malloc, free, realloc
Expand Down Expand Up @@ -298,6 +300,81 @@ void test_mixed_formats(void)
ASSERT_TEST(buf[test_strlen(buf)] == '\0', "Mixed format null termination");
}

/* Test 11: List node helpers behavior */
void test_list_node_pushback_and_remove(void)
{
list_node_t node1 = {0};
list_node_t node2 = {0};

node1.next = &node2; /* make node1 artificially “linked” */
list_t *list = list_create();

/* Check node push back normally - unlinked and linked */
list_pushback_node(list, &node1);
ASSERT_TEST(list_is_empty(list), "Linked node pushback fail");

node1.next = NULL;
list_pushback_node(list, &node1);
ASSERT_TEST(list->length == 1, "Unlinked node pushback success ");
ASSERT_TEST(list->head->next == &node1 && node1.next == list->tail,
"List consistent after pushback first node ");

/* Check node push back order */
node2.next = NULL;
list_pushback_node(list, &node2);
ASSERT_TEST(list->length == 2 && list->head->next == &node1 &&
node1.next == &node2 && node2.next == list->tail,
"Insertion order preserved ");

/* Remove last node */
list_remove_node(list, &node2);
ASSERT_TEST(
list->length == 1 && node2.next == NULL && node1.next == list->tail,
"Removing last node must keep list structure consistent");

/* Remove non-existing node (second time) */
list_remove_node(list, &node2);
ASSERT_TEST(
list->length == 1 && node2.next == NULL && node1.next == list->tail,
"Removing non-existing node must not change the list");

/* Remove only node */
list_remove_node(list, &node1);
ASSERT_TEST(list->length == 0 && list->head->next == list->tail,
"Removing only node ");
ASSERT_TEST(list_is_empty(list), "Empty list check ");
}

/* Test 12: List helpers behavior */
void test_list_pushback_and_remove(void)
{
list_t *list = list_create();

int node1 = 1;
int node2 = 2;

/* Check node push back normally - unlinked and linked */
list_pushback(list, &node1);
ASSERT_TEST(list->length == 1 && *(int *) (list->head->next->data) == 1,
"Data push back into a new list ");

list_pushback(list, &node2);
ASSERT_TEST(
list->length == 2 && *(int *) (list->head->next->next->data) == 2,
"Second data pushback successful ");

/* Remove last node */
list_remove(list, list->head->next);
ASSERT_TEST(list->length == 1 && *(int *) (list->head->next->data) == 2,
"Remove first data ");

/* Remove non-existing node (second time) */
list_remove(list, list->head->next);
ASSERT_TEST(list->length == 0, "Unlinked node pushback success ");

ASSERT_TEST(list_is_empty(list), "Empty list check ");
}

void test_runner(void)
{
printf("\n=== LibC Test Suite ===\n");
Expand All @@ -314,6 +391,11 @@ void test_runner(void)
test_isr_safety();
test_mixed_formats();


printf("\n=== List Test Suite ===\n");
test_list_node_pushback_and_remove();
test_list_pushback_and_remove();

printf("\n=== Test Summary ===\n");
printf("Tests run: %d\n", tests_run);
printf("Tests passed: %d\n", tests_passed);
Expand Down
50 changes: 37 additions & 13 deletions include/lib/list.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,24 @@ static inline list_node_t *list_cnext(const list_t *list,

/* Push and pop */

/* Pushback list node into list without new node memory allocation */
static inline void list_pushback_node(list_t *list, list_node_t *target)
{
if (unlikely(!list || !target || target->next))
return;

target->next = list->tail;

/* Insert before tail sentinel */
list_node_t *prev = list->head;
while (prev->next != list->tail)
prev = prev->next;

prev->next = target;
list->length++;
return;
}

static inline list_node_t *list_pushback(list_t *list, void *data)
{
if (unlikely(!list))
Expand All @@ -88,15 +106,9 @@ static inline list_node_t *list_pushback(list_t *list, void *data)
return NULL;

node->data = data;
node->next = list->tail;

/* Insert before tail sentinel */
list_node_t *prev = list->head;
while (prev->next != list->tail)
prev = prev->next;
prev->next = node;
node->next = NULL;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An initialized node should represent a standalone element; its next pointer must be set to NULL.


list->length++;
list_pushback_node(list, node);
return node;
}

Expand All @@ -114,23 +126,35 @@ static inline void *list_pop(list_t *list)
return data;
}

/* Remove a specific node; returns its data */
static inline void *list_remove(list_t *list, list_node_t *target)
/* Remove a node from list without freeing */
static inline void list_remove_node(list_t *list, list_node_t *target)
{
if (unlikely(!list || !target || list_is_empty(list)))
return NULL;
return;

list_node_t *prev = list->head;
while (prev->next != list->tail && prev->next != target)
prev = prev->next;

if (unlikely(prev->next != target))
return NULL; /* node not found */
return; /* node not found */

prev->next = target->next;
target->next = NULL;
list->length--;
return;
}

/* Remove a specific node; returns its data */
static inline void *list_remove(list_t *list, list_node_t *target)
{
if (unlikely(!list || !target || list_is_empty(list)))
return NULL;

list_remove_node(list, target);

void *data = target->data;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only free and return data operations are kept in the below section.

free(target);
list->length--;
return data;
}

Expand Down
Loading