/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup ScoreMsgqUnitMsgq
*/
/*
* Copyright (C) 2021 embedded brains GmbH & Co. KG
*
* 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 <rtems.h>
#include <rtems/rtems/messageimpl.h>
#include <rtems/rtems/statusimpl.h>
#include <rtems/score/coremsgimpl.h>
#include "../validation/tx-support.h"
#include <rtems/test.h>
/**
* @defgroup ScoreMsgqUnitMsgq spec:/score/msgq/unit/msgq
*
* @ingroup TestsuitesUnitNoClock0
*
* @brief Unit tests for the Message Queue Handler.
*
* Parts of 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.
* Currently, the pre-qualified subset of RTEMS does not contain the POSIX API.
* 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 the 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 the 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 );
}
/** @} */