#include "thread/TaskQueue.h" #include #include #include #include // 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); }