/** * @file * @brief QF/C stub for QUTEST unit testing * @ingroup qs * @cond ****************************************************************************** * Last updated for version 5.9.3 * Last updated on 2017-06-17 * * Q u a n t u m L e a P s * --------------------------- * innovating embedded systems * * Copyright (C) 2005-2017 Quantum Leaps, LLC. All rights reserved. * * This program is open source software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alternatively, this program may be distributed and modified under the * terms of Quantum Leaps commercial licenses, which expressly supersede * the GNU General Public License and are specifically designed for * licensees interested in retaining the proprietary status of their code. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Contact information: * https://state-machine.com * mailto:info@state-machine.com ****************************************************************************** * @endcond */ /* only build when Q_UTEST is defined */ #ifdef Q_UTEST #define QP_IMPL /* this is QP implementation */ #include "qf_port.h" /* QF port */ #include "qf_pkg.h" /* QF package-scope interface */ #include "qassert.h" /* QP embedded systems-friendly assertions */ #include "qs_port.h" /* include QS port */ Q_DEFINE_THIS_MODULE("qutest") /* Global objects ==========================================================*/ uint8_t volatile QF_intNest; /* QF functions ============================================================*/ void QF_init(void) { QF_maxPool_ = (uint_fast8_t)0; QF_subscrList_ = (QSubscrList *)0; QF_maxPubSignal_ = (enum_t)0; QF_intNest = (uint8_t)0; QF_bzero(&QF_active_[0], (uint_fast16_t)sizeof(QF_active_)); } /*..........................................................................*/ int_t QF_run(void) { /* function dictionaries for the standard API */ QS_FUN_DICTIONARY(&QActive_post_); QS_FUN_DICTIONARY(&QActive_postLIFO_); QS_onTestLoop(); /* run the test loop */ QS_onCleanup(); /* application cleanup */ return (int_t)0; /* return no error */ } /*..........................................................................*/ bool QActive_post_(QActive * const me, QEvt const * const e, uint_fast16_t const margin, void const * const sender) { QS_TEST_PROBE_DEF(&QActive_post_) bool status; QEQueueCtr nFree; /* temporary to avoid UB for volatile access */ QF_CRIT_STAT_ /** @pre event pointer must be valid */ Q_REQUIRE_ID(100, e != (QEvt const *)0); QF_CRIT_ENTRY_(); nFree = me->eQueue.nFree; /* get volatile into the temporary */ /* test probe for reaching the margin */ QS_TEST_PROBE( nFree = (QEQueueCtr)(qs_tp_ - (uint32_t)1); ) /* margin available? */ if (((margin == QF_NO_MARGIN) && (nFree > (QEQueueCtr)0)) || (nFree > (QEQueueCtr)margin)) { QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_POST_FIFO, QS_priv_.locFilter[AO_OBJ], me) /*printf("QS_QF_ACTIVE_POST_FIFO\n");*/ QS_TIME_(); /* timestamp */ QS_OBJ_(sender); /* the sender object */ QS_SIG_(e->sig); /* the signal of the event */ QS_OBJ_(me); /* this active object (recipient) */ QS_2U8_(e->poolId_, e->refCtr_); /* pool Id & ref Count */ QS_EQC_(nFree); /* number of free entries */ QS_EQC_(me->eQueue.nMin); /* min number of free entries */ QS_END_NOCRIT_() /* is it a pool event? */ if (e->poolId_ != (uint8_t)0) { QF_EVT_REF_CTR_INC_(e); /* increment the reference counter */ } QF_CRIT_EXIT_(); QF_gc(e); /* recycle the event to avoid a leak */ status = true; /* event "posted" successfully */ } else { /** @note assert if event cannot be posted while dropping events is * not acceptable */ Q_ASSERT_ID(110, margin != QF_NO_MARGIN); QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_POST_ATTEMPT, QS_priv_.locFilter[AO_OBJ], me) QS_TIME_(); /* timestamp */ QS_OBJ_(sender); /* the sender object */ QS_SIG_(e->sig); /* the signal of the event */ QS_OBJ_(me); /* this active object (recipient) */ QS_2U8_(e->poolId_, e->refCtr_); /* pool Id & ref Count */ QS_EQC_(nFree); /* number of free entries */ QS_EQC_(margin); /* margin requested */ QS_END_NOCRIT_() QF_CRIT_EXIT_(); QF_gc(e); /* recycle the event to avoid a leak */ status = false; /* event not posted */ } return status; } /*..........................................................................*/ void QActive_postLIFO_(QActive * const me, QEvt const * const e) { QS_TEST_PROBE_DEF(&QActive_postLIFO_) QEQueueCtr nFree; /* temporary to avoid UB for volatile access */ QF_CRIT_STAT_ QF_CRIT_ENTRY_(); nFree = me->eQueue.nFree; /* get volatile into the temporary */ /* test probe for queue overflow */ QS_TEST_PROBE_ID(1, Q_ERROR_ID(210); ) QS_BEGIN_NOCRIT_(QS_QF_ACTIVE_POST_LIFO, QS_priv_.locFilter[AO_OBJ], me) QS_TIME_(); /* timestamp */ QS_SIG_(e->sig); /* the signal of this event */ QS_OBJ_(me); /* this active object */ QS_2U8_(e->poolId_, e->refCtr_);/* pool Id & ref Count of the event */ QS_EQC_(nFree); /* number of free entries */ QS_EQC_(me->eQueue.nMin); /* min number of free entries */ QS_END_NOCRIT_() /* is it a pool event? */ if (e->poolId_ != (uint8_t)0) { QF_EVT_REF_CTR_INC_(e); /* increment the reference counter */ } QF_CRIT_EXIT_(); } /*..........................................................................*/ void QActive_start_(QActive * const me, uint_fast8_t prio, QEvt const *qSto[], uint_fast16_t qLen, void *stkSto, uint_fast16_t stkSize, QEvt const *ie) { Q_REQUIRE_ID(500, ((uint_fast8_t)0 < prio) /* priority must be in range */ && (prio <= (uint_fast8_t)QF_MAX_ACTIVE)); (void)stkSto; (void)stkSize; QEQueue_init(&me->eQueue, qSto, qLen); /* initialize the built-in queue */ me->prio = prio; /* set the current priority of the AO */ QF_add_(me); /* make QF aware of this active object */ QHSM_INIT(&me->super, ie); /* take the top-most initial tran. */ QS_FLUSH(); /* flush the trace buffer to the host */ } /*..........................................................................*/ void QActive_stop(QActive * const me) { QF_remove_(me); /* remove this active object from the framework */ } /****************************************************************************/ QTimeEvtCtr QTimeEvt_ctr(QTimeEvt const * const me) { QTimeEvtCtr ret; QF_CRIT_STAT_ QF_CRIT_ENTRY_(); ret = me->ctr; QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_CTR, QS_priv_.locFilter[TE_OBJ], me) QS_TIME_(); /* timestamp */ QS_OBJ_(me); /* this time event object */ QS_OBJ_(me->act); /* the target AO */ QS_TEC_(ret); /* the current counter */ QS_TEC_(me->interval); /* the interval */ QS_U8_((uint8_t)(me->super.refCtr_ & (uint8_t)0x7F)); /* tick rate */ QS_END_NOCRIT_() QF_CRIT_EXIT_(); return ret; } /*..........................................................................*/ void QTimeEvt_ctorX(QTimeEvt * const me, QActive * const act, enum_t const sig, uint_fast8_t tickRate) { /** @pre The signal must be valid and the tick rate in range */ Q_REQUIRE_ID(300, (sig >= (enum_t)Q_USER_SIG) && (tickRate < (uint_fast8_t)QF_MAX_TICK_RATE)); me->next = (QTimeEvt *)0; me->ctr = (QTimeEvtCtr)0; me->interval = (QTimeEvtCtr)0; me->super.sig = (QSignal)sig; me->act = act; me->super.poolId_ = (uint8_t)0; me->super.refCtr_ = (uint8_t)tickRate; } /*..........................................................................*/ void QTimeEvt_armX(QTimeEvt * const me, QTimeEvtCtr const nTicks, QTimeEvtCtr const interval) { uint_fast8_t tickRate = (uint_fast8_t)me->super.refCtr_ & (uint_fast8_t)0x7F; QTimeEvtCtr ctr = me->ctr; QF_CRIT_STAT_ /** @pre the host AO must be valid, time evnet must be disarmed, * number of clock ticks cannot be zero, and the signal must be valid. */ Q_REQUIRE_ID(400, (me->act != (void *)0) && (ctr == (QTimeEvtCtr)0) && (nTicks != (QTimeEvtCtr)0) && (tickRate < (uint_fast8_t)QF_MAX_TICK_RATE) && (me->super.sig >= (QSignal)Q_USER_SIG)); QF_CRIT_ENTRY_(); me->ctr = nTicks; me->interval = interval; QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_ARM, QS_priv_.locFilter[TE_OBJ], me) QS_TIME_(); /* timestamp */ QS_OBJ_(me); /* this time event object */ QS_OBJ_(me->act); /* the active object */ QS_TEC_(nTicks); /* the number of ticks */ QS_TEC_(interval); /* the interval */ QS_U8_((uint8_t)tickRate); /* tick rate */ QS_END_NOCRIT_() QF_CRIT_EXIT_(); } /*..........................................................................*/ bool QTimeEvt_disarm(QTimeEvt * const me) { bool wasArmed; QF_CRIT_STAT_ QF_CRIT_ENTRY_(); /* is the time evt running? */ if (me->ctr != (QTimeEvtCtr)0) { wasArmed = true; QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_DISARM, QS_priv_.locFilter[TE_OBJ], me) QS_TIME_(); /* timestamp */ QS_OBJ_(me); /* this time event object */ QS_OBJ_(me->act); /* the target AO */ QS_TEC_(me->ctr); /* the number of ticks */ QS_TEC_(me->interval); /* the interval */ QS_U8_((uint8_t)(me->super.refCtr_ & (uint8_t)0x7F));/*tick rate*/ QS_END_NOCRIT_() me->ctr = (QTimeEvtCtr)0; /* schedule removal from the list */ } /* the time event was already not running */ else { wasArmed = false; QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_DISARM_ATTEMPT, QS_priv_.locFilter[TE_OBJ], me) QS_TIME_(); /* timestamp */ QS_OBJ_(me); /* this time event object */ QS_OBJ_(me->act); /* the target AO */ QS_U8_((uint8_t)(me->super.refCtr_ & (uint8_t)0x7F));/*tick rate*/ QS_END_NOCRIT_() } QF_CRIT_EXIT_(); return wasArmed; } /*..........................................................................*/ bool QTimeEvt_rearm(QTimeEvt * const me, QTimeEvtCtr const nTicks) { uint_fast8_t tickRate = (uint_fast8_t)me->super.refCtr_ & (uint_fast8_t)0x7F; bool isArmed; QF_CRIT_STAT_ /** @pre AO must be valid, tick rate must be in range, nTicks must * not be zero, and the signal of this time event must be valid */ Q_REQUIRE_ID(600, (me->act != (void *)0) && (tickRate < (uint_fast8_t)QF_MAX_TICK_RATE) && (nTicks != (QTimeEvtCtr)0) && (me->super.sig >= (QSignal)Q_USER_SIG)); QF_CRIT_ENTRY_(); /* is the time evt not running? */ if (me->ctr == (QTimeEvtCtr)0) { isArmed = false; /* is the time event unlinked? * NOTE: For a duration of a single clock tick of the specified * tick rate a time event can be disarmed and yet still linked into * the list, because unlinking is performed exclusively in the * QF_tickX() function. */ if ((me->super.refCtr_ & (uint8_t)0x80) == (uint8_t)0) { me->super.refCtr_ |= (uint8_t)0x80; /* mark as linked */ } } /* the time event is armed */ else { isArmed = true; } me->ctr = nTicks; /* re-load the tick counter (shift the phasing) */ QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_REARM, QS_priv_.locFilter[TE_OBJ], me) QS_TIME_(); /* timestamp */ QS_OBJ_(me); /* this time event object */ QS_OBJ_(me->act); /* the target AO */ QS_TEC_(me->ctr); /* the number of ticks */ QS_TEC_(me->interval); /* the interval */ QS_2U8_((uint8_t)tickRate, ((isArmed != false) ? (uint8_t)1 : (uint8_t)0)); QS_END_NOCRIT_() QF_CRIT_EXIT_(); return isArmed; } /*..........................................................................*/ void QF_tickX_(uint_fast8_t const tickRate, void const * const sender) { QF_CRIT_STAT_ QF_CRIT_ENTRY_(); if (QS_rxPriv_.currObj[TE_OBJ] != (void *)0) { QTimeEvt *t = (QTimeEvt *)QS_rxPriv_.currObj[TE_OBJ]; QActive *act = (QActive *)t->act; /* temp. for volatile */ if (t->interval == (QTimeEvtCtr)0) { /* single-shot TE? */ t->ctr = (QTimeEvtCtr)0; /* auto-disarm */ t->super.refCtr_ &= (uint8_t)0x7F; /* mark as unlinked */ QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_AUTO_DISARM, QS_priv_.locFilter[TE_OBJ], t) QS_OBJ_(t); /* this time event object */ QS_OBJ_(act); /* the target AO */ QS_U8_((uint8_t)tickRate); /* tick rate */ QS_END_NOCRIT_() } QS_BEGIN_NOCRIT_(QS_QF_TIMEEVT_POST, QS_priv_.locFilter[TE_OBJ], t) QS_TIME_(); /* timestamp */ QS_OBJ_(t); /* the time event object */ QS_SIG_(t->super.sig); /* signal of this time event */ QS_OBJ_(act); /* the target AO */ QS_U8_((uint8_t)tickRate); /* tick rate */ QS_END_NOCRIT_() QF_CRIT_EXIT_(); /* exit critical section before posting */ /* Post to the 'act' AO (post does not actually happen) * NOTE: QACTIVE_POST() asserts internally if the queue overflows */ QACTIVE_POST(act, &t->super, sender); /* explicitly dispatch the time event */ QHSM_DISPATCH(&act->super, &t->super); } else { QF_CRIT_EXIT_(); } } /****************************************************************************/ void Q_onAssert(char_t const * const module, int_t location) { QS_ASSERTION(module, location, (uint32_t)0); /* report to QSPY */ QS_onTestLoop(); /* loop to wait for commands (typically reset) */ QS_onReset(); /* in case the QUTEST loop ever returns, reset manually */ } #endif /* Q_UTEST */