From d2aa0716e36b7e604968e54f8947f77278d3dcb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20K=C3=BChndel?= Date: Thu, 9 Dec 2021 14:26:58 +0100 Subject: unit: Test POSIX-specific message queue impl --- spec/build/testsuites/unit/unit-no-clock-0.yml | 1 + testsuites/unit/tc-score-msgq.c | 456 +++++++++++++++++++++++++ 2 files changed, 457 insertions(+) create mode 100644 testsuites/unit/tc-score-msgq.c diff --git a/spec/build/testsuites/unit/unit-no-clock-0.yml b/spec/build/testsuites/unit/unit-no-clock-0.yml index 6f44ff478c..008ade2443 100644 --- a/spec/build/testsuites/unit/unit-no-clock-0.yml +++ b/spec/build/testsuites/unit/unit-no-clock-0.yml @@ -12,6 +12,7 @@ ldflags: [] links: [] source: - testsuites/unit/tc-misaligned-builtin-memcpy.c +- testsuites/unit/tc-score-msgq.c - testsuites/unit/tc-score-rbtree.c - testsuites/unit/ts-unit-no-clock-0.c stlib: [] diff --git a/testsuites/unit/tc-score-msgq.c b/testsuites/unit/tc-score-msgq.c new file mode 100644 index 0000000000..9ceaf2553b --- /dev/null +++ b/testsuites/unit/tc-score-msgq.c @@ -0,0 +1,456 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSTestCaseScoreMsgqUnitMsgq + */ + +/* + * Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This file is part of the RTEMS quality process and was automatically + * generated. If you find something that needs to be fixed or + * worded better please post a report or patch to an RTEMS mailing list + * or raise a bug report: + * + * https://www.rtems.org/bugs.html + * + * For information on updating and regenerating please refer to the How-To + * section in the Software Requirements Engineering chapter of the + * RTEMS Software Engineering manual. The manual is provided as a part of + * a release. For development sources please refer to the online + * documentation at: + * + * https://docs.rtems.org + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "../validation/tx-support.h" + +#include + +/** + * @defgroup RTEMSTestCaseScoreMsgqUnitMsgq spec:/score/msgq/unit/msgq + * + * @ingroup RTEMSTestSuiteTestsuitesUnitNoClock0 + * + * @brief Unit tests for the Message Queue Handler. + * + * The files + * + * * ``cpukit/score/src/coremsginsert.c``, * + * ``cpukit/score/src/coremsgseize.c``, and * + * ``cpukit/score/src/coremsgsubmit.c`` + * + * are only executed by the POSIX API. The space qualified code subset does + * not contain the POSIX API (see *Space Profile*). This test exercises the + * code parts otherwise only reached by the POSIX API to achieve full code + * coverage. + * + * This test case performs the following actions: + * + * - Use _CORE_message_queue_Insert_message() to insert two messages into a + * message queue and use the POSIX message priority to define their order in + * the queue. + * + * - Check that _CORE_message_queue_Submit() was executed successfully. + * + * - Check that the messages are in right order in the message queue. + * + * - Submit() three messages into a message queue which can only store two and + * have the third submit() blocked till a seize() occurs. + * + * - Check that the third _CORE_message_queue_Submit() did actually block + * till there was room for the message in the message queue. + * + * - Submit() messages in the queue from within an ISR. + * + * - Check that the first two messages were successfully send. + * + * - Check that trying to send the third message from ISR when the message + * queue was full was rejected. + * + * @{ + */ + +#define MAXIMUM_PENDING_MESSAGES 2 +#define MAXIMUM_MESSAGE_SIZE 3 + +static void WorkerTask( rtems_task_argument argument ); + +/** + * @brief Test context for spec:/score/msgq/unit/msgq test case. + */ +typedef struct { + /** + * @brief This member contains a valid ID of a message queue. + */ + rtems_id message_queue_id; + + /** + * @brief This member is used as storage area for the message queue. + */ + RTEMS_MESSAGE_QUEUE_BUFFER( MAXIMUM_MESSAGE_SIZE ) + storage_area[ MAXIMUM_PENDING_MESSAGES]; + + /** + * @brief This member contains the task identifier of the worker task. + */ + rtems_id worker_id; + + /** + * @brief This member indicated whether the worker task is currently sending + * a message (``true``) or whether it is waiting to receive an event + * (``false``).. + */ + bool is_worker_working; + + /** + * @brief This member contains the returned status code of the SendMessage() + * function. + */ + rtems_status_code send_status; +} ScoreMsgqUnitMsgq_Context; + +static ScoreMsgqUnitMsgq_Context + ScoreMsgqUnitMsgq_Instance; + +#define EVENT_SEND RTEMS_EVENT_17 +#define MESSAGE_CONTENT_LOW { 1, 2, 3 } +#define MESSAGE_CONTENT_HIGH { 4, 5 } +#define MESSAGE_PRIORITY_LOW 5 +#define MESSAGE_PRIORITY_HIGH 7 +#define DO_WAIT true + +typedef ScoreMsgqUnitMsgq_Context Context; + +/* + * This is a code fragment from rtems_message_queue_send() with the + * specialty that it uses a POSIX priority and the sender + * task will wait in case the queue is full. + */ +static rtems_status_code SubmitMessage( + rtems_id id, + uint8_t *message, + size_t message_size, + unsigned int posix_piority +) +{ + rtems_status_code status; + Thread_queue_Context queue_context; + Message_queue_Control *the_message_queue; + + T_assert_lt_uint( posix_piority, MQ_PRIO_MAX ); + + the_message_queue = _Message_queue_Get( + id, + &queue_context + ); + T_assert_not_null( the_message_queue ); + + /* The next two calls are from _POSIX_Message_queue_Send_support() */ + _Thread_queue_Context_set_enqueue_callout( + &queue_context, + _Thread_queue_Enqueue_do_nothing_extra + ); + _Thread_queue_Context_set_timeout_argument( &queue_context, NULL, true ); + + _CORE_message_queue_Acquire_critical( + &the_message_queue->message_queue, + &queue_context + ); + + status = _CORE_message_queue_Submit( + &the_message_queue->message_queue, + _Thread_Executing, + message, + message_size, + (CORE_message_queue_Submit_types) ( posix_piority * -1 ), + DO_WAIT, + &queue_context + ); + + return _Status_Get( status ); +} + +static rtems_status_code ReceiveMessage( + rtems_id id, + void *buffer, + size_t *size +) +{ + return rtems_message_queue_receive( + id, + buffer, + size, + RTEMS_LOCAL | RTEMS_NO_WAIT, + RTEMS_NO_TIMEOUT + ); +} + +static rtems_status_code ReceiveOneMessages( Context *ctx ) +{ + uint8_t message_buffer[ MAXIMUM_MESSAGE_SIZE ]; + size_t message_size; + + return ReceiveMessage( + ctx->message_queue_id, + &message_buffer, + &message_size + ); +} + +static void SendMessage( Context *ctx ) +{ + uint8_t message[] = { 100, 101, 102 }; + ctx->send_status = SubmitMessage( + ctx->message_queue_id, + message, + sizeof( message ), + MESSAGE_PRIORITY_LOW + ); +} + +static void WorkerTask( rtems_task_argument argument ) +{ + Context *ctx = (Context *) argument; + + while ( true ) { + ctx->is_worker_working = false; + ReceiveAnyEvents(); + ctx->is_worker_working = true; + SendMessage( ctx ); + T_assert_rsc_success( ctx->send_status ); + } +} + +static void WorkerSendMessage( Context *ctx ) +{ + SendEvents( ctx->worker_id, EVENT_SEND ); +} + +static void ScoreMsgqUnitMsgq_Setup( ScoreMsgqUnitMsgq_Context *ctx ) +{ + rtems_status_code status; + rtems_message_queue_config config = { + .name = rtems_build_name( 'M', 'S', 'G', 'Q' ), + .maximum_pending_messages = MAXIMUM_PENDING_MESSAGES, + .maximum_message_size = MAXIMUM_MESSAGE_SIZE, + .storage_area = ctx->storage_area, + .storage_size = sizeof( ctx->storage_area ), + .storage_free = NULL, + .attributes = RTEMS_DEFAULT_ATTRIBUTES + }; + + status = rtems_message_queue_construct( + &config, + &ctx->message_queue_id + ); + T_rsc_success( status ); + + SetSelfPriority( PRIO_NORMAL ); + + ctx->worker_id = CreateTask( "WORK", PRIO_HIGH ); + StartTask( ctx->worker_id, WorkerTask, ctx ); +} + +static void ScoreMsgqUnitMsgq_Setup_Wrap( void *arg ) +{ + ScoreMsgqUnitMsgq_Context *ctx; + + ctx = arg; + ScoreMsgqUnitMsgq_Setup( ctx ); +} + +static void ScoreMsgqUnitMsgq_Teardown( ScoreMsgqUnitMsgq_Context *ctx ) +{ + DeleteTask( ctx->worker_id ); + RestoreRunnerPriority(); + T_rsc_success( rtems_message_queue_delete( ctx->message_queue_id ) ); +} + +static void ScoreMsgqUnitMsgq_Teardown_Wrap( void *arg ) +{ + ScoreMsgqUnitMsgq_Context *ctx; + + ctx = arg; + ScoreMsgqUnitMsgq_Teardown( ctx ); +} + +static T_fixture ScoreMsgqUnitMsgq_Fixture = { + .setup = ScoreMsgqUnitMsgq_Setup_Wrap, + .stop = NULL, + .teardown = ScoreMsgqUnitMsgq_Teardown_Wrap, + .scope = NULL, + .initial_context = &ScoreMsgqUnitMsgq_Instance +}; + +/** + * @brief Use _CORE_message_queue_Insert_message() to insert two messages into + * a message queue and use the POSIX message priority to define their order + * in the queue. + */ +static void ScoreMsgqUnitMsgq_Action_0( ScoreMsgqUnitMsgq_Context *ctx ) +{ + rtems_status_code status_submit_low; + rtems_status_code status_submit_high; + rtems_status_code status_receive_low; + rtems_status_code status_receive_high; + uint8_t message_low[] = MESSAGE_CONTENT_LOW; + uint8_t message_high[] = MESSAGE_CONTENT_HIGH; + uint8_t message_buffer_low[ MAXIMUM_MESSAGE_SIZE ]; + uint8_t message_buffer_high[ MAXIMUM_MESSAGE_SIZE ]; + size_t message_size_low; + size_t message_size_high; + + status_submit_low = SubmitMessage( + ctx->message_queue_id, + message_low, + sizeof( message_low ), + MESSAGE_PRIORITY_LOW + ); + + status_submit_high = SubmitMessage( + ctx->message_queue_id, + message_high, + sizeof( message_high ), + MESSAGE_PRIORITY_HIGH + ); + + status_receive_high = ReceiveMessage( + ctx->message_queue_id, + &message_buffer_high, + &message_size_high + ); + + status_receive_low = ReceiveMessage( + ctx->message_queue_id, + &message_buffer_low, + &message_size_low + ); + + /* + * Check that _CORE_message_queue_Submit() was executed successfully. + */ + T_rsc_success( status_submit_low ); + T_rsc_success( status_submit_high ); + + /* + * Check that the messages are in right order in the message queue. + */ + T_rsc_success( status_receive_high ); + T_eq_sz( message_size_high, sizeof( message_high ) ); + T_eq_mem( message_buffer_high, message_high, message_size_high ); + + T_rsc_success( status_receive_low ); + T_eq_sz( message_size_low, sizeof( message_low ) ); + T_eq_mem( message_buffer_low, message_low, message_size_low ); +} + +/** + * @brief Submit() three messages into a message queue which can only store two + * and have the third submit() blocked till a seize() occurs. + */ +static void ScoreMsgqUnitMsgq_Action_1( ScoreMsgqUnitMsgq_Context *ctx ) +{ + bool is_worker_blocked_after_third_send; + bool is_worker_blocked_after_first_receive; + + WorkerSendMessage( ctx ); + WorkerSendMessage( ctx ); + WorkerSendMessage( ctx ); + is_worker_blocked_after_third_send = ctx->is_worker_working; + + T_rsc_success( ReceiveOneMessages( ctx ) ); + is_worker_blocked_after_first_receive = ctx->is_worker_working; + + T_rsc_success( ReceiveOneMessages( ctx ) ); + T_rsc_success( ReceiveOneMessages( ctx ) ); + + /* + * Check that the third _CORE_message_queue_Submit() did actually block till + * there was room for the message in the message queue. + */ + T_true( is_worker_blocked_after_third_send ); + T_true( !is_worker_blocked_after_first_receive ); +} + +/** + * @brief Submit() messages in the queue from within an ISR. + */ +static void ScoreMsgqUnitMsgq_Action_2( ScoreMsgqUnitMsgq_Context *ctx ) +{ + rtems_status_code status_send_first_message; + rtems_status_code status_send_second_message; + rtems_status_code status_send_third_message; + + CallWithinISR( ( void (*)(void*) ) SendMessage, ctx ); + status_send_first_message = ctx->send_status; + CallWithinISR( ( void (*)(void*) ) SendMessage, ctx ); + status_send_second_message = ctx->send_status; + CallWithinISR( ( void (*)(void*) ) SendMessage, ctx ); + status_send_third_message = ctx->send_status; + + T_rsc_success( ReceiveOneMessages( ctx ) ); + T_rsc_success( ReceiveOneMessages( ctx ) ); + + /* + * Check that the first two messages were successfully send. + */ + T_assert_rsc_success( status_send_first_message ); + T_assert_rsc_success( status_send_second_message ); + + /* + * Check that trying to send the third message from ISR when the message + * queue was full was rejected. + */ + T_rsc( status_send_third_message, STATUS_CLASSIC_INTERNAL_ERROR ); +} + +/** + * @fn void T_case_body_ScoreMsgqUnitMsgq( void ) + */ +T_TEST_CASE_FIXTURE( ScoreMsgqUnitMsgq, &ScoreMsgqUnitMsgq_Fixture ) +{ + ScoreMsgqUnitMsgq_Context *ctx; + + ctx = T_fixture_context(); + + ScoreMsgqUnitMsgq_Action_0( ctx ); + ScoreMsgqUnitMsgq_Action_1( ctx ); + ScoreMsgqUnitMsgq_Action_2( ctx ); +} + +/** @} */ -- cgit v1.2.3