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.
175 lines
4.5 KiB
175 lines
4.5 KiB
#include "thread/TaskQueue.h" |
|
|
|
#include <stdlib.h> |
|
#include <stdbool.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
|
|
// Testing macros: |
|
#define RUN_TEST(test_function) \ |
|
do {test_setup();test_function();test_teardown();} while (0) |
|
#define CHECK(type, value, operator, expected, message) \ |
|
do {if (!(value operator expected)) { \ |
|
printf("-- Failed: %s\n---- Failed expression: %" #type " %s %" #type "\n", \ |
|
message, value, #operator, expected); ++failCounter;\ |
|
} else {printf("-- PASSED (%s %s %s)\n", \ |
|
#value, #operator, #expected);}} while (0) |
|
static int failCounter = 0; |
|
|
|
// usage: |
|
// CHECK(type, actual, operator, expected, message); |
|
// RUN_TEST(test_function); |
|
|
|
enum { |
|
IDX_RESULT_TASK_1 = 0, |
|
IDX_RESULT_TASK_2, |
|
IDX_RESULT_TASK_ORDER, |
|
IDX_RESULT_TASK_STOP, |
|
IDX_RESULT_BLOCK, |
|
IDX_RESULT_NOTIFY, |
|
IDX_RESULT_COUNT |
|
}; |
|
|
|
static int taskResults[IDX_RESULT_COUNT] = {0}; |
|
|
|
static BlockerHandle blockerHnd = 99; |
|
static TaskQueuePtr globalQueue = NULL; |
|
|
|
void testTask1(void*); |
|
void testTask2(void*); |
|
void testTaskStop(void*); |
|
void testTaskPostItself(void*); |
|
void blockMock(BlockerHandle); |
|
void notifyMock(BlockerHandle); |
|
|
|
static void test_setup(void){} |
|
static void test_teardown(void){} |
|
|
|
static void test_createTaskQueue(void) |
|
{ |
|
TaskQueuePtr queue = createTaskQueue(blockerHnd, &blockMock, ¬ifyMock); |
|
CHECK(p, (void*)queue, !=, NULL, "createTaskQueue should return a valid task queue"); |
|
freeTaskQueue(&queue); |
|
} |
|
|
|
static void test_runQueue(void) |
|
{ |
|
const char* msg = "runQueue should execute enqueued tasks in order with no loop"; |
|
TaskQueuePtr queue = createTaskQueue(blockerHnd, &blockMock, ¬ifyMock); |
|
enqueueTask(queue, &testTask1, NULL); |
|
enqueueTask(queue, &testTask2, NULL); |
|
enqueueTask(queue, &testTaskStop, queue); |
|
runQueue(queue); |
|
|
|
CHECK(i, taskResults[IDX_RESULT_TASK_1] , ==, 1, msg); |
|
CHECK(i, taskResults[IDX_RESULT_TASK_2] , ==, 2, msg); |
|
CHECK(i, taskResults[IDX_RESULT_TASK_ORDER], ==, 3, msg); |
|
CHECK(i, taskResults[IDX_RESULT_TASK_STOP] , ==, 1, msg); |
|
|
|
freeTaskQueue(&queue); |
|
} |
|
|
|
static void test_enqueueNotify(void) |
|
{ |
|
const char* msg = "Enqueueing new task should notify the blocker"; |
|
TaskQueuePtr queue = createTaskQueue(blockerHnd, &blockMock, ¬ifyMock); |
|
memset(taskResults, 0, sizeof(taskResults)); |
|
|
|
enqueueTask(queue, &testTask1, NULL); |
|
enqueueTask(queue, &testTask1, NULL); |
|
|
|
CHECK(i, taskResults[IDX_RESULT_NOTIFY], ==, 2, msg); |
|
freeTaskQueue(&queue); |
|
} |
|
|
|
static void test_stopNotify(void) |
|
{ |
|
const char* msg = "Stopping the queue should notify the blocker"; |
|
TaskQueuePtr queue = createTaskQueue(blockerHnd, &blockMock, ¬ifyMock); |
|
memset(taskResults, 0, sizeof(taskResults)); |
|
|
|
enqueueTask(queue, &testTaskStop, queue); |
|
runQueue(queue); |
|
|
|
CHECK(i, taskResults[IDX_RESULT_NOTIFY], ==, 2, msg); // enqueue + stop |
|
|
|
freeTaskQueue(&queue); |
|
} |
|
|
|
static void test_blockOnEmpty(void) |
|
{ |
|
const char* msg = "Empty queue should lock the blocker"; |
|
TaskQueuePtr queue = createTaskQueue(blockerHnd, &blockMock, ¬ifyMock); |
|
memset(taskResults, 0, sizeof(taskResults)); |
|
|
|
globalQueue = queue; |
|
enqueueTask(queue, &testTask1, NULL); |
|
runQueue(queue); |
|
|
|
CHECK(i, taskResults[IDX_RESULT_BLOCK], ==, 1, msg); |
|
freeTaskQueue(&queue); |
|
} |
|
|
|
static void test_leak(void) |
|
{ |
|
// This is a leak test for valgrind. |
|
// The scenario is that a task enqueueing itself again |
|
// shouldn't cause infinite loop nor memory leak. |
|
|
|
TaskQueuePtr queue = createTaskQueue(blockerHnd, &blockMock, ¬ifyMock); |
|
enqueueTask(queue, &testTaskPostItself, queue); |
|
enqueueTask(queue, &testTaskStop, queue); |
|
runQueue(queue); |
|
freeTaskQueue(&queue); |
|
} |
|
|
|
int main(void) { |
|
RUN_TEST(test_createTaskQueue); |
|
RUN_TEST(test_runQueue); |
|
RUN_TEST(test_enqueueNotify); |
|
RUN_TEST(test_stopNotify); |
|
RUN_TEST(test_blockOnEmpty); |
|
RUN_TEST(test_leak); |
|
return failCounter; |
|
} |
|
|
|
// --- |
|
|
|
void blockMock(BlockerHandle blocker) |
|
{ |
|
(void)blocker; |
|
taskResults[IDX_RESULT_BLOCK] += 1; |
|
if (globalQueue) { |
|
stopQueue(globalQueue); |
|
} |
|
} |
|
|
|
void notifyMock(BlockerHandle blocker) |
|
{ |
|
(void)blocker; |
|
taskResults[IDX_RESULT_NOTIFY] += 1; |
|
} |
|
|
|
void testTask1(void* data) |
|
{ |
|
(void)data; |
|
taskResults[IDX_RESULT_TASK_1] += 1; |
|
taskResults[IDX_RESULT_TASK_ORDER] += 6; |
|
} |
|
void testTask2(void* data) |
|
{ |
|
(void)data; |
|
taskResults[IDX_RESULT_TASK_2] += 2; |
|
taskResults[IDX_RESULT_TASK_ORDER] /= 2; |
|
} |
|
|
|
void testTaskStop(void* data) |
|
{ |
|
taskResults[IDX_RESULT_TASK_STOP] += 1; |
|
stopQueue((TaskQueuePtr)data); |
|
} |
|
|
|
void testTaskPostItself(void* data) |
|
{ |
|
enqueueTask((TaskQueuePtr)data, testTaskPostItself, data); |
|
}
|
|
|