ud3tn/test/unit/test_simple_queue.c
Felix Walter 17aec0f909 test/unit: Free used heap-allocated data
Closes: #104

Signed-off-by: Felix Walter <felix.walter@d3tn.com>
2026-03-18 09:57:10 +01:00

392 lines
10 KiB
C

// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
#ifdef PLATFORM_POSIX
#include "platform/hal_semaphore.h"
#include "platform/posix/hal_types.h"
#include "platform/posix/simple_queue.h"
#include "testud3tn_unity.h"
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <time.h>
#ifndef __APPLE__
#include <semaphore.h>
#endif //__APPLE__
TEST_GROUP(simple_queue);
TEST_SETUP(simple_queue)
{
}
TEST_TEAR_DOWN(simple_queue)
{
}
TEST(simple_queue, test_createQueue)
{
// create a queue
Queue_t *q = queueCreate(2, 4);
TEST_ASSERT_NOT_NULL(q);
TEST_ASSERT_EQUAL_UINT(q->item_length, 2);
TEST_ASSERT_EQUAL_UINT(q->item_size, 4);
TEST_ASSERT_NOT_NULL(q->abs_start);
TEST_ASSERT_NOT_NULL(q->abs_end);
TEST_ASSERT_NOT_NULL(q->current_start);
TEST_ASSERT_NOT_NULL(q->current_end);
TEST_ASSERT_EQUAL_PTR(q->abs_start, q->current_start);
TEST_ASSERT_EQUAL_PTR(q->abs_start, q->current_end);
TEST_ASSERT_EQUAL_PTR(q->abs_end, q->abs_start + 8);
queueDelete(q);
}
TEST(simple_queue, test_PushPopBasic)
{
// create a queue
Queue_t *q = queueCreate(1, sizeof(int));
const int i = 42;
TEST_ASSERT_EQUAL_PTR(q->abs_start, q->current_start);
TEST_ASSERT_EQUAL_PTR(q->abs_start, q->current_end);
TEST_ASSERT_EQUAL_INT(0, queuePush(q, &i, 0, false));
TEST_ASSERT_EQUAL_INT(i, (int)(q->current_start)[0]);
int j;
TEST_ASSERT_EQUAL_INT(0, queuePop(q, &j, 0));
TEST_ASSERT_EQUAL_INT(i, j);
queueDelete(q);
}
TEST(simple_queue, test_PushFullPopFull)
{
// create a queue
Queue_t *q = queueCreate(10, sizeof(int));
int i, j;
for (i = 0; i <= 9; i++) {
// the first ten insertions should be successful
TEST_ASSERT_EQUAL_INT(0, queuePush(q, &i, 0, false));
}
// the eleventh insertion (without force) should fail
TEST_ASSERT_EQUAL_INT(1, queuePush(q, &i, 0, false));
for (i = 0; i <= 9; i++) {
// the first ten removals should be successful
TEST_ASSERT_EQUAL_INT(0, queuePop(q, &j, 0));
// the removed values should have the correct order
TEST_ASSERT_EQUAL_INT(i, j);
}
// the eleventh removal should fail
TEST_ASSERT_EQUAL_INT(1, queuePop(q, &j, 0));
queueDelete(q);
}
TEST(simple_queue, test_PushEndPopEnd)
{
// create a queue
Queue_t *q = queueCreate(10, sizeof(int));
int i, j;
for (i = 0; i <= 9; i++) {
// the first ten insertions should be successful
TEST_ASSERT_EQUAL_INT(0, queuePush(q, &i, 0, false));
}
for (i = 0; i <= 9; i++) {
// the first ten removals should be successful
TEST_ASSERT_EQUAL_INT(0, queuePop(q, &j, 0));
// the removed values should have the correct order
TEST_ASSERT_EQUAL_INT(i, j);
}
// from here on we should start at the abs_start of the queue again!
for (i = 20; i <= 29; i++) {
// the first ten insertions should be successful
TEST_ASSERT_EQUAL_INT(0, queuePush(q, &i, 0, false));
}
for (i = 20; i <= 29; i++) {
// the first ten removals should be successful
TEST_ASSERT_EQUAL_INT(0, queuePop(q, &j, 0));
// the removed values should have the correct order
TEST_ASSERT_EQUAL_INT(i, j);
}
queueDelete(q);
}
TEST(simple_queue, test_CircularBehaviour)
{
// create a queue
Queue_t *q = queueCreate(10, sizeof(int));
int i, j, k, l, m, n;
// test for two circular rounds with two elements
for (i = 0; i <= 19; i += 2) {
j = i + 1;
TEST_ASSERT_EQUAL_INT(0, queuePush(q, &i, 0, false));
TEST_ASSERT_EQUAL_INT(0, queuePush(q, &j, 0, false));
TEST_ASSERT_EQUAL_INT(0, queuePop(q, &k, 0));
TEST_ASSERT_EQUAL_INT(0, queuePop(q, &l, 0));
// the removed values should have the correct values
TEST_ASSERT_EQUAL_INT(i, k);
TEST_ASSERT_EQUAL_INT(j, l);
}
// test for three circular rounds with three elements
for (i = 0; i <= 29; i += 3) {
j = i + 1;
k = j + 1;
TEST_ASSERT_EQUAL_INT(0, queuePush(q, &i, 0, false));
TEST_ASSERT_EQUAL_INT(0, queuePush(q, &j, 0, false));
TEST_ASSERT_EQUAL_INT(0, queuePush(q, &k, 0, false));
TEST_ASSERT_EQUAL_INT(0, queuePop(q, &l, 0));
TEST_ASSERT_EQUAL_INT(0, queuePop(q, &m, 0));
TEST_ASSERT_EQUAL_INT(0, queuePop(q, &n, 0));
// the removed values should have the correct values
TEST_ASSERT_EQUAL_INT(i, l);
TEST_ASSERT_EQUAL_INT(j, m);
TEST_ASSERT_EQUAL_INT(k, n);
}
queueDelete(q);
}
TEST(simple_queue, test_ResetQueue)
{
// create a queue
Queue_t *q = queueCreate(10, sizeof(int));
int i, j;
for (i = 0; i <= 5; i++)
TEST_ASSERT_EQUAL_INT(0, queuePush(q, &i, 0, false));
for (i = 0; i <= 3; i++) {
TEST_ASSERT_EQUAL_INT(0, queuePop(q, &j, 0));
TEST_ASSERT_EQUAL_INT(i, j);
}
// check that the ptrs are not equal
TEST_ASSERT_NOT_EQUAL(q->abs_start, q->current_start);
TEST_ASSERT_NOT_EQUAL(q->abs_start, q->current_end);
#ifndef __APPLE__
// Apple systems use another implementation of the semaphore.
int value_pop, value_push;
sem_getvalue(&q->sem_pop->sem, &value_pop);
sem_getvalue(&q->sem_push->sem, &value_push);
TEST_ASSERT_EQUAL_INT(2, value_pop);
TEST_ASSERT_EQUAL_INT(8, value_push);
#endif //__APPLE__
queueReset(q);
TEST_ASSERT_EQUAL_PTR(q->abs_start, q->current_start);
TEST_ASSERT_EQUAL_PTR(q->abs_start, q->current_end);
#ifndef __APPLE__
sem_getvalue(&q->sem_pop->sem, &value_pop);
sem_getvalue(&q->sem_push->sem, &value_push);
TEST_ASSERT_EQUAL_INT(0, value_pop);
TEST_ASSERT_EQUAL_INT(10, value_push);
#endif //__APPLE__
queueDelete(q);
}
TEST(simple_queue, test_NrOfWaitingElements)
{
// create a queue
Queue_t *q = queueCreate(10, sizeof(int));
int i, j;
for (i = 0; i <= 8; i++)
TEST_ASSERT_EQUAL_INT(0, queuePush(q, &i, 0, false));
#ifndef __APPLE__
int value;
sem_getvalue(&q->sem_pop->sem, &value);
TEST_ASSERT_EQUAL_UINT(9, value);
#endif //__APPLE__
for (i = 0; i <= 3; i++) {
TEST_ASSERT_EQUAL_INT(0, queuePop(q, &j, 0));
TEST_ASSERT_EQUAL_INT(i, j);
}
#ifndef __APPLE__
sem_getvalue(&q->sem_pop->sem, &value);
TEST_ASSERT_EQUAL_UINT(5, value);
#endif //__APPLE__
for (i = 4; i <= 8; i++) {
TEST_ASSERT_EQUAL_INT(0, queuePop(q, &j, 0));
TEST_ASSERT_EQUAL_INT(i, j);
}
#ifndef __APPLE__
sem_getvalue(&q->sem_pop->sem, &value);
TEST_ASSERT_EQUAL_UINT(0, value);
#endif //__APPLE__
queueDelete(q);
}
TEST(simple_queue, test_ForcePush)
{
// create a queue
Queue_t *q = queueCreate(10, sizeof(int));
int i, j;
for (i = 0; i <= 9; i++) {
// the first ten insertions should be successful
TEST_ASSERT_EQUAL_INT(0, queuePush(q, &i, 0, false));
}
// the eleventh insertion (without force) should fail
TEST_ASSERT_EQUAL_INT(1, queuePush(q, &i, 0, false));
i = 42;
// the eleventh insertion (with force) should succeed
TEST_ASSERT_EQUAL_INT(0, queuePush(q, &i, 0, true));
for (i = 0; i <= 8; i++) {
// the first nine removals should be standard
TEST_ASSERT_EQUAL_INT(0, queuePop(q, &j, 0));
// the removed values should have the correct order
TEST_ASSERT_EQUAL_INT(i, j);
}
// the last removal should still be successful
TEST_ASSERT_EQUAL_INT(0, queuePop(q, &j, 0));
// the removed value should have the forced value
TEST_ASSERT_EQUAL_INT(42, j);
queueDelete(q);
}
// returns difference in ms
int ms_diff(struct timespec *start, struct timespec *stop)
{
struct timespec result;
if ((stop->tv_nsec - start->tv_nsec) < 0) {
result.tv_sec = stop->tv_sec - start->tv_sec - 1;
result.tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000;
} else {
result.tv_sec = stop->tv_sec - start->tv_sec;
result.tv_nsec = stop->tv_nsec - start->tv_nsec;
}
return result.tv_sec * 1000 + (result.tv_nsec / 1000000);
}
#define MAX_DELAY_INCREASE_MS 200
TEST(simple_queue, test_SemaphoreTimingBehaviour)
{
struct timespec ts1, ts2;
// create a queue
Queue_t *q = queueCreate(10, sizeof(int));
int i, j;
// a removal with timeout 0 should fail immediately
clock_gettime(CLOCK_REALTIME, &ts1);
TEST_ASSERT_EQUAL_INT(1, queuePop(q, &j, 0));
clock_gettime(CLOCK_REALTIME, &ts2);
// allow a little deviation due to the overhead
TEST_ASSERT_TRUE(ms_diff(&ts1, &ts2) < MAX_DELAY_INCREASE_MS);
// a removal with timeout 100ms should fail eventually
clock_gettime(CLOCK_REALTIME, &ts1);
TEST_ASSERT_EQUAL_INT(1, queuePop(q, &j, 100));
clock_gettime(CLOCK_REALTIME, &ts2);
// allow a little deviation due to the overhead
TEST_ASSERT_TRUE(ms_diff(&ts1, &ts2) > 98);
TEST_ASSERT_TRUE(ms_diff(&ts1, &ts2) < 100 + MAX_DELAY_INCREASE_MS);
// a removal with timeout 2000ms should fail eventually
clock_gettime(CLOCK_REALTIME, &ts1);
TEST_ASSERT_EQUAL_INT(1, queuePop(q, &j, 2000));
clock_gettime(CLOCK_REALTIME, &ts2);
// allow a little deviation due to the overhead
TEST_ASSERT_TRUE(ms_diff(&ts1, &ts2) > 1998);
TEST_ASSERT_TRUE(ms_diff(&ts1, &ts2) < 2000 + MAX_DELAY_INCREASE_MS);
for (i = 0; i <= 9; i++) {
// the first ten insertions should be successful
TEST_ASSERT_EQUAL_INT(0, queuePush(q, &i, 0, false));
}
// a insertion with timeout 0 should fail immediately
clock_gettime(CLOCK_REALTIME, &ts1);
TEST_ASSERT_EQUAL_INT(1, queuePush(q, &i, 0, false));
clock_gettime(CLOCK_REALTIME, &ts2);
// allow a little deviation due to the overhead
TEST_ASSERT_TRUE(ms_diff(&ts1, &ts2) < MAX_DELAY_INCREASE_MS);
// a insertion with timeout 100ms should fail eventually
clock_gettime(CLOCK_REALTIME, &ts1);
TEST_ASSERT_EQUAL_INT(1, queuePush(q, &i, 100, false));
clock_gettime(CLOCK_REALTIME, &ts2);
// allow a little deviation due to the overhead
TEST_ASSERT_TRUE(ms_diff(&ts1, &ts2) > 98);
TEST_ASSERT_TRUE(ms_diff(&ts1, &ts2) < 100 + MAX_DELAY_INCREASE_MS);
// a insertion with timeout 2000ms should fail eventually
clock_gettime(CLOCK_REALTIME, &ts1);
TEST_ASSERT_EQUAL_INT(1, queuePush(q, &i, 2000, false));
clock_gettime(CLOCK_REALTIME, &ts2);
// allow a little deviation due to the overhead
TEST_ASSERT_TRUE(ms_diff(&ts1, &ts2) > 1998);
TEST_ASSERT_TRUE(ms_diff(&ts1, &ts2) < 2000 + MAX_DELAY_INCREASE_MS);
queueDelete(q);
}
TEST_GROUP_RUNNER(simple_queue)
{
RUN_TEST_CASE(simple_queue, test_createQueue);
RUN_TEST_CASE(simple_queue, test_PushPopBasic);
RUN_TEST_CASE(simple_queue, test_PushFullPopFull);
RUN_TEST_CASE(simple_queue, test_PushEndPopEnd);
RUN_TEST_CASE(simple_queue, test_CircularBehaviour);
RUN_TEST_CASE(simple_queue, test_ResetQueue);
RUN_TEST_CASE(simple_queue, test_NrOfWaitingElements);
RUN_TEST_CASE(simple_queue, test_ForcePush);
RUN_TEST_CASE(simple_queue, test_SemaphoreTimingBehaviour);
}
#endif // PLATFORM_POSIX