diff options
author | Frank Kühndel <frank.kuehndel@embedded-brains.de> | 2021-09-20 16:42:26 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2021-10-28 18:59:27 +0200 |
commit | ac67f6709f22862a9a1fd841a38259bc9a865f9a (patch) | |
tree | f14f70f8169a09d477e7f7720b328837da594ca6 | |
parent | 6c547593149e322121da5a68fd0a9ee2461589ae (diff) |
unit: Add tests timecounter and message queues
-rw-r--r-- | spec/build/testsuites/unit/unit-no-clock-0.yml | 1 | ||||
-rw-r--r-- | testsuites/unit/tc-score-kern-tc.c | 258 | ||||
-rw-r--r-- | testsuites/unit/tc-score-msgq.c | 379 |
3 files changed, 632 insertions, 6 deletions
diff --git a/spec/build/testsuites/unit/unit-no-clock-0.yml b/spec/build/testsuites/unit/unit-no-clock-0.yml index 98e8764fb9..1c13d4e47c 100644 --- a/spec/build/testsuites/unit/unit-no-clock-0.yml +++ b/spec/build/testsuites/unit/unit-no-clock-0.yml @@ -11,6 +11,7 @@ includes: [] ldflags: [] links: [] source: +- testsuites/unit/tc-score-kern-tc.c - testsuites/unit/tc-score-msgq.c - testsuites/unit/ts-unit-no-clock-0.c stlib: [] diff --git a/testsuites/unit/tc-score-kern-tc.c b/testsuites/unit/tc-score-kern-tc.c new file mode 100644 index 0000000000..03fe373759 --- /dev/null +++ b/testsuites/unit/tc-score-kern-tc.c @@ -0,0 +1,258 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSTestCaseScoreTimecounterUnitKernTc + */ + +/* + * 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 <rtems/score/timecounter.h> +#include <rtems/score/todimpl.h> + +#include <rtems/test.h> + +/** + * @defgroup RTEMSTestCaseScoreTimecounterUnitKernTc \ + * spec:/score/timecounter/unit/kern-tc + * + * @ingroup RTEMSTestSuiteTestsuitesUnitNoClock0 + * + * @brief Unit tests for the time counter kern_tc part. + * + * Parts of the file ``cpukit/score/src/kern_tc.c`` are only executed by the + * POSIX API and certain device driver code. The space qualified code subset + * does not contain those features. This test exercises the code parts + * otherwise not reached in order to achieve full code coverage. + * + * This test case performs the following actions: + * + * - Call function _Timecounter_Getbintime(). + * + * - Check value returned by function call. + * + * - Call function _Timecounter_Getbinuptime(). + * + * - Check value returned by function call. + * + * - Call function _Timecounter_Getboottime(). + * + * - Check value returned by function call. + * + * - Call function _Timecounter_Getboottimebin(). + * + * - Check value returned by function call. + * + * - Call function _Timecounter_Getmicrotime(). + * + * - Check value returned by function call. + * + * - Call function _Timecounter_Getmicrouptime(). + * + * - Check value returned by function call. + * + * - Call function _Timecounter_Microuptime(). + * + * - Check value returned by function call. + * + * - Call function _Timecounter_Nanotime(). + * + * - Check value returned by function call. + * + * - Call function _Timecounter_Tick_simple(). + * + * @{ + */ + +/** + * @brief Call function _Timecounter_Getbintime(). + */ +static void ScoreTimecounterUnitKernTc_Action_0( void ) +{ + struct bintime bt; + _Timecounter_Getbintime( &bt ); + + /* + * Check value returned by function call. + */ + T_eq_u64( bt.sec, TOD_SECONDS_1970_THROUGH_1988 ); + T_eq_long( bt.frac, 0 ); +} + +/** + * @brief Call function _Timecounter_Getbinuptime(). + */ +static void ScoreTimecounterUnitKernTc_Action_1( void ) +{ + struct bintime bt; + _Timecounter_Getbinuptime( &bt ); + + /* + * Check value returned by function call. + */ + T_eq_u64( bt.sec, 1 ); + T_eq_long( bt.frac, 0 ); +} + +/** + * @brief Call function _Timecounter_Getboottime(). + */ +static void ScoreTimecounterUnitKernTc_Action_2( void ) +{ + struct timeval tv; + _Timecounter_Getboottime( &tv ); + + /* + * Check value returned by function call. + */ + T_eq_u64( tv.tv_sec, TOD_SECONDS_1970_THROUGH_1988 - 1 ); + T_eq_long( tv.tv_usec, 0 ); +} + +/** + * @brief Call function _Timecounter_Getboottimebin(). + */ +static void ScoreTimecounterUnitKernTc_Action_3( void ) +{ + struct bintime bt; + _Timecounter_Getboottimebin( &bt ); + + /* + * Check value returned by function call. + */ + T_eq_u64( bt.sec, TOD_SECONDS_1970_THROUGH_1988 - 1 ); + T_eq_long( bt.frac, 0 ); +} + +/** + * @brief Call function _Timecounter_Getmicrotime(). + */ +static void ScoreTimecounterUnitKernTc_Action_4( void ) +{ + struct timeval tv; + _Timecounter_Getmicrotime( &tv ); + + /* + * Check value returned by function call. + */ + T_eq_u64( tv.tv_sec, TOD_SECONDS_1970_THROUGH_1988 ); + T_eq_long( tv.tv_usec, 0 ); +} + +/** + * @brief Call function _Timecounter_Getmicrouptime(). + */ +static void ScoreTimecounterUnitKernTc_Action_5( void ) +{ + struct timeval tv; + _Timecounter_Getmicrouptime( &tv ); + + /* + * Check value returned by function call. + */ + T_eq_u64( tv.tv_sec, 1 ); + T_eq_long( tv.tv_usec, 0 ); +} + +/** + * @brief Call function _Timecounter_Microuptime(). + */ +static void ScoreTimecounterUnitKernTc_Action_6( void ) +{ + struct timeval tv; + _Timecounter_Microuptime( &tv ); + + /* + * Check value returned by function call. + */ + T_eq_u64( tv.tv_sec, 1 ); + /* T_eq_long( tv.tv_usec, 0 ); is increased by other sources */ +} + +/** + * @brief Call function _Timecounter_Nanotime(). + */ +static void ScoreTimecounterUnitKernTc_Action_7( void ) +{ + struct timespec ts; + _Timecounter_Nanotime( &ts ); + + /* + * Check value returned by function call. + */ + T_eq_u64( ts.tv_sec, TOD_SECONDS_1970_THROUGH_1988 ); + /* T_eq_long( ts.tv_nsec, 0 ); is increased by other sources */ +} + +/** + * @brief Call function _Timecounter_Tick_simple(). + */ +static void ScoreTimecounterUnitKernTc_Action_8( void ) +{ + ISR_lock_Context lock_context; + _Timecounter_Acquire( &lock_context ); + _Timecounter_Tick_simple( 0, 0, &lock_context ); +} + +/** + * @fn void T_case_body_ScoreTimecounterUnitKernTc( void ) + */ +T_TEST_CASE( ScoreTimecounterUnitKernTc ) +{ + ScoreTimecounterUnitKernTc_Action_0(); + ScoreTimecounterUnitKernTc_Action_1(); + ScoreTimecounterUnitKernTc_Action_2(); + ScoreTimecounterUnitKernTc_Action_3(); + ScoreTimecounterUnitKernTc_Action_4(); + ScoreTimecounterUnitKernTc_Action_5(); + ScoreTimecounterUnitKernTc_Action_6(); + ScoreTimecounterUnitKernTc_Action_7(); + ScoreTimecounterUnitKernTc_Action_8(); +} + +/** @} */ diff --git a/testsuites/unit/tc-score-msgq.c b/testsuites/unit/tc-score-msgq.c index 05b9adad69..25bbb7d9c8 100644 --- a/testsuites/unit/tc-score-msgq.c +++ b/testsuites/unit/tc-score-msgq.c @@ -52,8 +52,13 @@ #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> /** @@ -63,27 +68,389 @@ * * @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: * - * - TODO. + * - 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; + +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 +}; + +#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 ); +} + +/** + * @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 TODO. + * @brief Submit() messages in the queue from within an ISR. */ -static void ScoreMsgqUnitMsgq_Action_0( void ) +static void ScoreMsgqUnitMsgq_Action_2( ScoreMsgqUnitMsgq_Context *ctx ) { - /* TODO */ + 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( ScoreMsgqUnitMsgq ) +T_TEST_CASE_FIXTURE( ScoreMsgqUnitMsgq, &ScoreMsgqUnitMsgq_Fixture ) { - ScoreMsgqUnitMsgq_Action_0(); + ScoreMsgqUnitMsgq_Context *ctx; + + ctx = T_fixture_context(); + + ScoreMsgqUnitMsgq_Action_0( ctx ); + ScoreMsgqUnitMsgq_Action_1( ctx ); + ScoreMsgqUnitMsgq_Action_2( ctx ); } /** @} */ |