212 lines
4.6 KiB
C
212 lines
4.6 KiB
C
#include "test_queue.h"
|
|
#include "queue.h"
|
|
#include "test_utils.h"
|
|
#include <stdlib.h>
|
|
#include <threads.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
|
|
static void test_queue_basic() {
|
|
Queue* q = queue_create(10, NULL);
|
|
EXPECT_NOT_NULL(q);
|
|
EXPECT_TRUE(queue_is_empty(q));
|
|
EXPECT_FALSE(queue_is_full(q));
|
|
|
|
int* vals[5];
|
|
for (int i = 0; i < 5; i++) {
|
|
vals[i] = malloc(sizeof(int));
|
|
*vals[i] = (i + 1) * 10;
|
|
queue_enqueue(q, vals[i]);
|
|
}
|
|
|
|
EXPECT_FALSE(queue_is_empty(q));
|
|
EXPECT_FALSE(queue_is_full(q));
|
|
EXPECT_EQ_INT(q->size, 5);
|
|
|
|
int* v1 = (int*)queue_dequeue(q);
|
|
EXPECT_NOT_NULL(v1);
|
|
EXPECT_EQ_INT(*v1, 10);
|
|
free(v1);
|
|
|
|
int* v2 = (int*)queue_dequeue(q);
|
|
EXPECT_NOT_NULL(v2);
|
|
EXPECT_EQ_INT(*v2, 20);
|
|
free(v2);
|
|
|
|
EXPECT_EQ_INT(q->size, 3);
|
|
|
|
int* vals2[3];
|
|
for (int i = 0; i < 3; i++) {
|
|
vals2[i] = malloc(sizeof(int));
|
|
*vals2[i] = (i + 6) * 10;
|
|
queue_enqueue(q, vals2[i]);
|
|
}
|
|
|
|
EXPECT_EQ_INT(q->size, 6);
|
|
|
|
const int expected_vals[] = {30, 40, 50, 60, 70, 80};
|
|
for (int i = 0; i < 6; i++) {
|
|
int* v = (int*)queue_dequeue(q);
|
|
EXPECT_NOT_NULL(v);
|
|
EXPECT_EQ_INT(*v, expected_vals[i]);
|
|
free(v);
|
|
}
|
|
|
|
EXPECT_TRUE(queue_is_empty(q));
|
|
queue_destroy(q);
|
|
}
|
|
|
|
static void test_queue_rejects_invalid_capacity() {
|
|
EXPECT_NULL(queue_create(0, NULL));
|
|
EXPECT_NULL(queue_create(-1, NULL));
|
|
}
|
|
|
|
static void test_queue_resize() {
|
|
Queue* q = queue_create(3, NULL);
|
|
EXPECT_NOT_NULL(q);
|
|
EXPECT_EQ_INT(q->capacity, 3);
|
|
|
|
int a = 1, b = 2, c = 3, d = 4, e = 5;
|
|
|
|
queue_enqueue(q, &a);
|
|
queue_enqueue(q, &b);
|
|
queue_enqueue(q, &c);
|
|
|
|
EXPECT_TRUE(queue_is_full(q));
|
|
|
|
const int* v1 = (const int*)queue_dequeue(q);
|
|
EXPECT_NOT_NULL(v1);
|
|
EXPECT_EQ_INT(*v1, 1);
|
|
|
|
// Now front = 1, rear = 0, size = 2 (wrapped state)
|
|
queue_enqueue(q, &d);
|
|
EXPECT_TRUE(queue_is_full(q));
|
|
|
|
// This enqueue triggers capacity doubling
|
|
queue_enqueue(q, &e);
|
|
EXPECT_FALSE(queue_is_full(q));
|
|
EXPECT_EQ_INT(q->capacity, 6);
|
|
EXPECT_EQ_INT(q->size, 4);
|
|
|
|
// Dequeue all and check order: B, C, D, E
|
|
const int* v2 = (const int*)queue_dequeue(q);
|
|
EXPECT_NOT_NULL(v2);
|
|
EXPECT_EQ_INT(*v2, 2);
|
|
|
|
const int* v3 = (const int*)queue_dequeue(q);
|
|
EXPECT_NOT_NULL(v3);
|
|
EXPECT_EQ_INT(*v3, 3);
|
|
|
|
const int* v4 = (const int*)queue_dequeue(q);
|
|
EXPECT_NOT_NULL(v4);
|
|
EXPECT_EQ_INT(*v4, 4);
|
|
|
|
const int* v5 = (const int*)queue_dequeue(q);
|
|
EXPECT_NOT_NULL(v5);
|
|
EXPECT_EQ_INT(*v5, 5);
|
|
|
|
EXPECT_TRUE(queue_is_empty(q));
|
|
queue_destroy(q);
|
|
}
|
|
|
|
static int destroyer_calls = 0;
|
|
static void my_destroyer(void* item) {
|
|
destroyer_calls++;
|
|
free(item);
|
|
}
|
|
|
|
static void test_queue_destroyer() {
|
|
destroyer_calls = 0;
|
|
Queue* q = queue_create(5, my_destroyer);
|
|
EXPECT_NOT_NULL(q);
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
int* val = malloc(sizeof(int));
|
|
*val = i;
|
|
queue_enqueue(q, val);
|
|
}
|
|
|
|
int* v = (int*)queue_dequeue(q);
|
|
EXPECT_NOT_NULL(v);
|
|
EXPECT_EQ_INT(*v, 0);
|
|
free(v);
|
|
|
|
queue_destroy(q);
|
|
EXPECT_EQ_INT(destroyer_calls, 2);
|
|
}
|
|
|
|
typedef struct {
|
|
Queue* q;
|
|
mtx_t* mutex;
|
|
cnd_t* cnd_empty;
|
|
cnd_t* cnd_full;
|
|
bool done;
|
|
int sum;
|
|
} ThreadContext;
|
|
|
|
static int consumer_func(void* arg) {
|
|
ThreadContext* ctx = (ThreadContext*)arg;
|
|
while (true) {
|
|
int* val = (int*)queue_dequeue_multithreaded(ctx->q, ctx->mutex, ctx->cnd_empty, ctx->cnd_full,
|
|
&ctx->done);
|
|
if (val == NULL) {
|
|
break;
|
|
}
|
|
ctx->sum += *val;
|
|
free(val);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void test_queue_multithreaded() {
|
|
Queue* q = queue_create(2, NULL);
|
|
mtx_t mutex;
|
|
cnd_t cnd_empty;
|
|
cnd_t cnd_full;
|
|
|
|
mtx_init(&mutex, mtx_plain);
|
|
cnd_init(&cnd_empty);
|
|
cnd_init(&cnd_full);
|
|
|
|
ThreadContext ctx = {.q = q,
|
|
.mutex = &mutex,
|
|
.cnd_empty = &cnd_empty,
|
|
.cnd_full = &cnd_full,
|
|
.done = false,
|
|
.sum = 0};
|
|
|
|
thrd_t consumer;
|
|
int res = thrd_create(&consumer, consumer_func, &ctx);
|
|
EXPECT_EQ_INT(res, thrd_success);
|
|
|
|
for (int i = 1; i <= 100; i++) {
|
|
int* val = malloc(sizeof(int));
|
|
*val = i;
|
|
queue_enqueue_multithreaded(q, val, &mutex, &cnd_empty, &cnd_full);
|
|
}
|
|
|
|
mtx_lock(&mutex);
|
|
ctx.done = true;
|
|
cnd_signal(&cnd_empty);
|
|
mtx_unlock(&mutex);
|
|
|
|
int join_res;
|
|
thrd_join(consumer, &join_res);
|
|
|
|
EXPECT_EQ_INT(ctx.sum, 5050);
|
|
EXPECT_TRUE(queue_is_empty(q));
|
|
|
|
queue_destroy(q);
|
|
mtx_destroy(&mutex);
|
|
cnd_destroy(&cnd_empty);
|
|
cnd_destroy(&cnd_full);
|
|
}
|
|
|
|
void test_queue() {
|
|
test_queue_basic();
|
|
test_queue_rejects_invalid_capacity();
|
|
test_queue_resize();
|
|
test_queue_destroyer();
|
|
test_queue_multithreaded();
|
|
}
|