#include #include #include "AllocationTestHarness.h" #include "osi/include/allocator.h" #include "osi/include/fixed_queue.h" #include "osi/include/future.h" #include "osi/include/osi.h" #include "osi/include/thread.h" static const size_t TEST_QUEUE_SIZE = 10; static const char* DUMMY_DATA_STRING = "Dummy data string"; static const char* DUMMY_DATA_STRING1 = "Dummy data string1"; static const char* DUMMY_DATA_STRING2 = "Dummy data string2"; static const char* DUMMY_DATA_STRING3 = "Dummy data string3"; static future_t* received_message_future = NULL; static int test_queue_entry_free_counter = 0; // Test whether a file descriptor |fd| is readable. // Return true if the file descriptor is readable, otherwise false. static bool is_fd_readable(int fd) { fd_set rfds; struct timeval tv; FD_ZERO(&rfds); tv.tv_sec = 0; tv.tv_usec = 0; FD_SET(fd, &rfds); // Only the enqueue_fd should be readable int result = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); EXPECT_TRUE(result >= 0); return FD_ISSET(fd, &rfds); } // Function for performing dequeue operations from the queue when is ready static void fixed_queue_ready(fixed_queue_t* queue, UNUSED_ATTR void* context) { void* msg = fixed_queue_try_dequeue(queue); EXPECT_TRUE(msg != NULL); future_ready(received_message_future, msg); } static void test_queue_entry_free_cb(void* data) { // Don't free the data, because we are testing only whether the callback // is called. test_queue_entry_free_counter++; } class FixedQueueTest : public AllocationTestHarness {}; TEST_F(FixedQueueTest, test_fixed_queue_new_free) { fixed_queue_t* queue; // Test a corner case: queue of size 0 queue = fixed_queue_new(0); EXPECT_TRUE(queue != NULL); fixed_queue_free(queue, NULL); // Test a corner case: queue of size 1 queue = fixed_queue_new(1); EXPECT_TRUE(queue != NULL); fixed_queue_free(queue, NULL); // Test a corner case: queue of maximum size queue = fixed_queue_new((size_t)-1); EXPECT_TRUE(queue != NULL); fixed_queue_free(queue, NULL); // Test a queue of some size queue = fixed_queue_new(TEST_QUEUE_SIZE); EXPECT_TRUE(queue != NULL); fixed_queue_free(queue, NULL); // Test free-ing a NULL queue fixed_queue_free(NULL, NULL); fixed_queue_free(NULL, osi_free); } TEST_F(FixedQueueTest, test_fixed_queue_flush) { fixed_queue_t* queue; // Test a corner case: queue of size 0 and no callback to free entries queue = fixed_queue_new(0); EXPECT_TRUE(queue != NULL); fixed_queue_flush(queue, NULL); EXPECT_TRUE(fixed_queue_is_empty(queue)); fixed_queue_free(queue, osi_free); // Test a corner case: queue of size 0 and a callback to free entries queue = fixed_queue_new(0); EXPECT_TRUE(queue != NULL); fixed_queue_flush(queue, osi_free); EXPECT_TRUE(fixed_queue_is_empty(queue)); fixed_queue_free(queue, osi_free); // Test a queue of some size and no callback to free entries queue = fixed_queue_new(TEST_QUEUE_SIZE); EXPECT_TRUE(queue != NULL); fixed_queue_try_enqueue(queue, (void*)DUMMY_DATA_STRING1); fixed_queue_try_enqueue(queue, (void*)DUMMY_DATA_STRING2); fixed_queue_try_enqueue(queue, (void*)DUMMY_DATA_STRING3); EXPECT_FALSE(fixed_queue_is_empty(queue)); fixed_queue_flush(queue, NULL); EXPECT_TRUE(fixed_queue_is_empty(queue)); fixed_queue_free(queue, osi_free); // Test a queue of some size and a callback to free entries test_queue_entry_free_counter = 0; queue = fixed_queue_new(TEST_QUEUE_SIZE); EXPECT_TRUE(queue != NULL); fixed_queue_try_enqueue(queue, (void*)DUMMY_DATA_STRING1); fixed_queue_try_enqueue(queue, (void*)DUMMY_DATA_STRING2); fixed_queue_try_enqueue(queue, (void*)DUMMY_DATA_STRING3); EXPECT_FALSE(fixed_queue_is_empty(queue)); fixed_queue_flush(queue, test_queue_entry_free_cb); EXPECT_TRUE(test_queue_entry_free_counter == 3); EXPECT_TRUE(fixed_queue_is_empty(queue)); fixed_queue_free(queue, osi_free); } TEST_F(FixedQueueTest, test_fixed_queue_is_empty) { fixed_queue_t* queue; // Test a NULL queue EXPECT_TRUE(fixed_queue_is_empty(NULL)); // Test an empty queue queue = fixed_queue_new(TEST_QUEUE_SIZE); ASSERT_TRUE(queue != NULL); EXPECT_TRUE(fixed_queue_is_empty(queue)); // Test a non-empty queue fixed_queue_try_enqueue(queue, (void*)DUMMY_DATA_STRING); EXPECT_FALSE(fixed_queue_is_empty(queue)); // Test an empty dequeued queue ASSERT_EQ(DUMMY_DATA_STRING, fixed_queue_try_dequeue(queue)); EXPECT_TRUE(fixed_queue_is_empty(queue)); fixed_queue_free(queue, NULL); } TEST_F(FixedQueueTest, test_fixed_queue_length) { fixed_queue_t* queue; // Test a NULL queue EXPECT_EQ((size_t)0, fixed_queue_length(NULL)); // Test an empty queue queue = fixed_queue_new(TEST_QUEUE_SIZE); ASSERT_TRUE(queue != NULL); EXPECT_EQ((size_t)0, fixed_queue_length(queue)); // Test a non-empty queue fixed_queue_try_enqueue(queue, (void*)DUMMY_DATA_STRING); EXPECT_EQ((size_t)1, fixed_queue_length(queue)); // Test an empty dequeued queue ASSERT_EQ(DUMMY_DATA_STRING, fixed_queue_try_dequeue(queue)); EXPECT_EQ((size_t)0, fixed_queue_length(queue)); fixed_queue_free(queue, NULL); } TEST_F(FixedQueueTest, test_fixed_queue_capacity) { fixed_queue_t* queue; // Test a corner case: queue of size 0 queue = fixed_queue_new(0); ASSERT_TRUE(queue != NULL); EXPECT_EQ((size_t)0, fixed_queue_capacity(queue)); fixed_queue_free(queue, NULL); // Test a corner case: queue of size 1 queue = fixed_queue_new(1); ASSERT_TRUE(queue != NULL); EXPECT_EQ((size_t)1, fixed_queue_capacity(queue)); fixed_queue_free(queue, NULL); // Test a corner case: queue of maximum size queue = fixed_queue_new((size_t)-1); ASSERT_TRUE(queue != NULL); EXPECT_EQ((size_t)-1, fixed_queue_capacity(queue)); fixed_queue_free(queue, NULL); // Test a queue of some size queue = fixed_queue_new(TEST_QUEUE_SIZE); ASSERT_TRUE(queue != NULL); EXPECT_EQ(TEST_QUEUE_SIZE, fixed_queue_capacity(queue)); fixed_queue_free(queue, NULL); } TEST_F(FixedQueueTest, test_fixed_queue_enqueue_dequeue) { fixed_queue_t* queue = fixed_queue_new(TEST_QUEUE_SIZE); ASSERT_TRUE(queue != NULL); // Test blocking enqueue and blocking dequeue fixed_queue_enqueue(queue, (void*)DUMMY_DATA_STRING); EXPECT_EQ((size_t)1, fixed_queue_length(queue)); EXPECT_EQ(DUMMY_DATA_STRING, fixed_queue_dequeue(queue)); EXPECT_EQ((size_t)0, fixed_queue_length(queue)); // Test non-blocking enqueue and non-blocking dequeue EXPECT_TRUE(fixed_queue_try_enqueue(queue, (void*)DUMMY_DATA_STRING)); EXPECT_EQ((size_t)1, fixed_queue_length(queue)); EXPECT_EQ(DUMMY_DATA_STRING, fixed_queue_try_dequeue(queue)); EXPECT_EQ((size_t)0, fixed_queue_length(queue)); // Test non-blocking enqueue beyond queue capacity for (size_t i = 0; i < TEST_QUEUE_SIZE; i++) { EXPECT_TRUE(fixed_queue_try_enqueue(queue, (void*)DUMMY_DATA_STRING)); } // The next enqueue operation is beyond the queue capacity, so it should fail EXPECT_FALSE(fixed_queue_try_enqueue(queue, (void*)DUMMY_DATA_STRING)); // Test non-blocking dequeue from a queue that is full to max capacity for (size_t i = 0; i < TEST_QUEUE_SIZE; i++) { EXPECT_EQ(DUMMY_DATA_STRING, fixed_queue_try_dequeue(queue)); } // Test non-blocking dequeue from an empty queue EXPECT_EQ(NULL, fixed_queue_try_dequeue(queue)); // Test non-blocking dequeue from a NULL queue EXPECT_EQ(NULL, fixed_queue_try_dequeue(NULL)); fixed_queue_free(queue, NULL); } TEST_F(FixedQueueTest, test_fixed_queue_try_peek_first_last) { fixed_queue_t* queue = fixed_queue_new(TEST_QUEUE_SIZE); ASSERT_TRUE(queue != NULL); // Test peek first/last from a NULL queue EXPECT_EQ(NULL, fixed_queue_try_peek_first(NULL)); EXPECT_EQ(NULL, fixed_queue_try_peek_last(NULL)); // Test peek first/last from an empty queue EXPECT_EQ(NULL, fixed_queue_try_peek_first(queue)); EXPECT_EQ(NULL, fixed_queue_try_peek_last(queue)); // Test peek first/last from a queue with one element fixed_queue_enqueue(queue, (void*)DUMMY_DATA_STRING1); EXPECT_EQ(DUMMY_DATA_STRING1, fixed_queue_try_peek_first(queue)); EXPECT_EQ(DUMMY_DATA_STRING1, fixed_queue_try_peek_last(queue)); // Test peek first/last from a queue with two elements fixed_queue_enqueue(queue, (void*)DUMMY_DATA_STRING2); EXPECT_EQ(DUMMY_DATA_STRING1, fixed_queue_try_peek_first(queue)); EXPECT_EQ(DUMMY_DATA_STRING2, fixed_queue_try_peek_last(queue)); // Test peek first/last from a queue with three elements fixed_queue_enqueue(queue, (void*)DUMMY_DATA_STRING3); EXPECT_EQ(DUMMY_DATA_STRING1, fixed_queue_try_peek_first(queue)); EXPECT_EQ(DUMMY_DATA_STRING3, fixed_queue_try_peek_last(queue)); fixed_queue_free(queue, NULL); } TEST_F(FixedQueueTest, test_fixed_queue_try_remove_from_queue) { fixed_queue_t* queue = fixed_queue_new(TEST_QUEUE_SIZE); ASSERT_TRUE(queue != NULL); // Test removing from a NULL queue EXPECT_EQ(NULL, fixed_queue_try_remove_from_queue(NULL, (void*)DUMMY_DATA_STRING)); // Test removing from an empty queue EXPECT_EQ(NULL, fixed_queue_try_remove_from_queue(queue, (void*)DUMMY_DATA_STRING)); // Test removing a queued string from a queue fixed_queue_enqueue(queue, (void*)DUMMY_DATA_STRING1); fixed_queue_enqueue(queue, (void*)DUMMY_DATA_STRING2); fixed_queue_enqueue(queue, (void*)DUMMY_DATA_STRING3); EXPECT_EQ((size_t)3, fixed_queue_length(queue)); EXPECT_EQ(DUMMY_DATA_STRING2, fixed_queue_try_remove_from_queue( queue, (void*)DUMMY_DATA_STRING2)); EXPECT_EQ((size_t)2, fixed_queue_length(queue)); // Removing again should fail EXPECT_EQ(NULL, fixed_queue_try_remove_from_queue(queue, (void*)DUMMY_DATA_STRING2)); // Test removing a non-queued string from a queue EXPECT_EQ(NULL, fixed_queue_try_remove_from_queue(queue, (void*)DUMMY_DATA_STRING)); fixed_queue_free(queue, NULL); } TEST_F(FixedQueueTest, test_fixed_queue_get_enqueue_dequeue_fd) { fixed_queue_t* queue = fixed_queue_new(TEST_QUEUE_SIZE); ASSERT_TRUE(queue != NULL); // Test validity of enqueue and dequeue file descriptors int enqueue_fd = fixed_queue_get_enqueue_fd(queue); int dequeue_fd = fixed_queue_get_dequeue_fd(queue); EXPECT_TRUE(enqueue_fd >= 0); EXPECT_TRUE(dequeue_fd >= 0); EXPECT_TRUE(enqueue_fd < FD_SETSIZE); EXPECT_TRUE(dequeue_fd < FD_SETSIZE); // Test the file descriptors of an empty queue // Only the enqueue_fd should be readable EXPECT_TRUE(is_fd_readable(enqueue_fd)); EXPECT_FALSE(is_fd_readable(dequeue_fd)); // Test the file descriptors of a non-empty queue // Both the enqueue_fd and dequeue_fd should be readable fixed_queue_enqueue(queue, (void*)DUMMY_DATA_STRING); EXPECT_TRUE(is_fd_readable(enqueue_fd)); EXPECT_TRUE(is_fd_readable(dequeue_fd)); fixed_queue_dequeue(queue); // Test the file descriptors of a full queue // Only the dequeue_fd should be readable for (size_t i = 0; i < TEST_QUEUE_SIZE; i++) { EXPECT_TRUE(fixed_queue_try_enqueue(queue, (void*)DUMMY_DATA_STRING)); } EXPECT_FALSE(is_fd_readable(enqueue_fd)); EXPECT_TRUE(is_fd_readable(dequeue_fd)); fixed_queue_free(queue, NULL); } TEST_F(FixedQueueTest, test_fixed_queue_register_dequeue) { fixed_queue_t* queue = fixed_queue_new(TEST_QUEUE_SIZE); ASSERT_TRUE(queue != NULL); received_message_future = future_new(); ASSERT_TRUE(received_message_future != NULL); thread_t* worker_thread = thread_new("test_fixed_queue_worker_thread"); ASSERT_TRUE(worker_thread != NULL); fixed_queue_register_dequeue(queue, thread_get_reactor(worker_thread), fixed_queue_ready, NULL); // Add a message to the queue, and expect to receive it fixed_queue_enqueue(queue, (void*)DUMMY_DATA_STRING); const char* msg = (const char*)future_await(received_message_future); EXPECT_EQ(DUMMY_DATA_STRING, msg); fixed_queue_unregister_dequeue(queue); thread_free(worker_thread); fixed_queue_free(queue, NULL); }