//$file${src::qs::qutest.c} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
//
// Model: qpc.qm
// File:  ${src::qs::qutest.c}
//
// This code has been generated by QM 5.3.0 <www.state-machine.com/qm>.
// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
//
// This code is covered by the following QP license:
// License #    : LicenseRef-QL-dual
// Issued to    : Any user of the QP/C real-time embedded framework
// Framework(s) : qpc
// Support ends : 2024-12-31
// License scope:
//
// Copyright (C) 2005 Quantum Leaps, LLC <state-machine.com>.
//
//                    Q u a n t u m  L e a P s
//                    ------------------------
//                    Modern Embedded Software
//
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
//
// This software is dual-licensed under the terms of the open source GNU
// General Public License version 3 (or any later version), or alternatively,
// under the terms of one of the closed source Quantum Leaps commercial
// licenses.
//
// The terms of the open source GNU General Public License version 3
// can be found at: <www.gnu.org/licenses/gpl-3.0>
//
// The terms of the closed source Quantum Leaps commercial licenses
// can be found at: <www.state-machine.com/licensing>
//
// Redistributions in source code must retain this top-level comment block.
// Plagiarizing this software to sidestep the license obligations is illegal.
//
// Contact information:
// <www.state-machine.com/licensing>
// <info@state-machine.com>
//
//$endhead${src::qs::qutest.c} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// only build when Q_UTEST is defined
#ifdef Q_UTEST

#define QP_IMPL       // this is QP implementation
#include "qp_port.h"  // QP port
#include "qp_pkg.h"   // QP package-scope interface
#include "qsafe.h"    // QP Functional Safety (FuSa) Subsystem
#include "qs_port.h"  // include QS port
#include "qs_pkg.h"   // QS facilities for pre-defined trace records

//============================================================================
// QUTest unit testing harness
//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Check for the minimum required QP version
#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U))
#error qpc version 7.3.0 or higher required
#endif
//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

//$define${QS::QUTest} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
//$enddef${QS::QUTest} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

//============================================================================
//! @cond INTERNAL

QSTestAttr QS_tstPriv_;

//............................................................................
void QS_test_pause_(void) {
    QS_beginRec_((uint_fast8_t)QS_TEST_PAUSED);
    QS_endRec_();
    QS_onTestLoop();
}

//............................................................................
uint32_t QS_getTestProbe_(QSpyFunPtr const api) {
    uint32_t data = 0U;
    for (uint_fast8_t i = 0U; i < QS_tstPriv_.tpNum; ++i) {
        if (QS_tstPriv_.tpBuf[i].addr == (QSFun)api) {
            data = QS_tstPriv_.tpBuf[i].data;

            QS_CRIT_STAT
            QS_CRIT_ENTRY();
            QS_MEM_SYS();
            QS_beginRec_((uint_fast8_t)QS_TEST_PROBE_GET);
                QS_TIME_PRE_();    // timestamp
                QS_FUN_PRE_(api);  // the calling API
                QS_U32_PRE_(data); // the Test-Probe data
            QS_endRec_();

            QS_REC_DONE(); // user callback (if defined)

            --QS_tstPriv_.tpNum; // one less Test-Probe
            // move all remaining entries in the buffer up by one
            for (uint_fast8_t j = i; j < QS_tstPriv_.tpNum; ++j) {
                QS_tstPriv_.tpBuf[j] = QS_tstPriv_.tpBuf[j + 1U];
            }
            QS_MEM_APP();
            QS_CRIT_EXIT();
            break; // we are done (Test-Probe retreived)
        }
    }
    return data;
}

//............................................................................
QSTimeCtr QS_onGetTime(void) {
    return (++QS_tstPriv_.testTime);
}

//............................................................................
Q_NORETURN Q_onError(
    char const * const module,
    int_t const id)
{
    QS_beginRec_((uint_fast8_t)QS_ASSERT_FAIL);
        QS_TIME_PRE_();
        QS_U16_PRE_(id);
        QS_STR_PRE_((module != (char *)0) ? module : "?");
    QS_endRec_();

    QS_onFlush();   // flush the assertion record to the host
    QS_onCleanup(); // cleanup after the failure
    QS_onReset();   // reset the target to prevent the code from continuing
    for (;;) { // QS_onReset() should not return, but to ensure no-return
    }
}

//! @endcond
//============================================================================

// QP-stub for QUTest
// NOTE: The QP-stub is needed for unit testing QP applications, but might
// NOT be needed for testing QP itself. In that case, the build process
// can define Q_UTEST=0 to exclude the QP-stub from the build.
#if (Q_UTEST != 0)

Q_DEFINE_THIS_MODULE("qutest")

//............................................................................
void QS_processTestEvts_(void) {
    QS_TEST_PROBE_DEF(&QS_processTestEvts_)

    // return immediately (do nothing) for Test Probe != 0
    QS_TEST_PROBE(return;)

    while (QPSet_notEmpty(&QS_tstPriv_.readySet)) {
        uint_fast8_t const p = QPSet_findMax(&QS_tstPriv_.readySet);
        QActive * const a = QActive_registry_[p];

        QEvt const * const e = QActive_get_(a);
        QASM_DISPATCH(a, e, a->prio);
#if (QF_MAX_EPOOL > 0U)
        QF_gc(e);
#endif
        if (a->eQueue.frontEvt == (QEvt *)0) { // empty queue?
            QPSet_remove(&QS_tstPriv_.readySet, p);
#ifndef Q_UNSAFE
            QPSet_update_(&QS_tstPriv_.readySet, &QS_tstPriv_.readySet_dis);
#endif
        }
    }
}

//$define${QS::QUTest-stub} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

//${QS::QUTest-stub::QF::init} ...............................................
void QF_init(void) {
    // Clear the internal QF variables, so that the framework can start
    // correctly even if the startup code fails to clear the uninitialized
    // data (as is required by the C Standard).
    QF_bzero_(&QF_priv_,             sizeof(QF_priv_));
    QF_bzero_(&QS_tstPriv_,          sizeof(QS_tstPriv_));
    QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_));

    #ifndef Q_UNSAFE
    QPSet_update_(&QS_tstPriv_.readySet, &QS_tstPriv_.readySet_dis);
    #endif
}

//${QS::QUTest-stub::QF::stop} ...............................................
void QF_stop(void) {
    QS_onReset();
}

//${QS::QUTest-stub::QF::run} ................................................
int_t QF_run(void) {
    QS_CRIT_STAT
    QS_CRIT_ENTRY();
    QS_MEM_SYS();

    // function dictionaries for the standard API
    QS_FUN_DICTIONARY(&QActive_post_);
    QS_FUN_DICTIONARY(&QActive_postLIFO_);
    QS_FUN_DICTIONARY(&QS_processTestEvts_);

    // produce the QS_QF_RUN trace record
    QS_BEGIN_PRE_(QS_QF_RUN, 0U)
    QS_END_PRE_()

    QS_MEM_APP();
    QS_CRIT_EXIT();

    QS_processTestEvts_(); // process all events posted so far
    QS_onTestLoop(); // run the test loop
    QS_onCleanup();  // application cleanup
    return 0; // return no error
}

//${QS::QUTest-stub::QActive} ................................................

//${QS::QUTest-stub::QActive::start_} ........................................
//! @private @memberof QActive
void QActive_start_(QActive * const me,
    QPrioSpec const prioSpec,
    QEvt const * * const qSto,
    uint_fast16_t const qLen,
    void * const stkSto,
    uint_fast16_t const stkSize,
    void const * const par)
{
    Q_UNUSED_PAR(stkSto);
    Q_UNUSED_PAR(stkSize);

    me->prio  = (uint8_t)(prioSpec & 0xFFU); // QF-prio. of the AO
    me->pthre = (uint8_t)(prioSpec >> 8U);   // preemption-threshold
    QActive_register_(me); // make QF aware of this active object

    QEQueue_init(&me->eQueue, qSto, qLen);

    // top-most initial tran. (virtual call)
    (*me->super.vptr->init)(&me->super, par, me->prio);
}

//${QS::QUTest-stub::QActive::stop} ..........................................
#ifdef QACTIVE_CAN_STOP
//! @protected @memberof QActive
void QActive_stop(QActive * const me) {
    QActive_unsubscribeAll(me); // unsubscribe from all events
    QActive_unregister_(me); // un-register this active object
}
#endif // def QACTIVE_CAN_STOP

//${QS::QUTest-stub::QTimeEvt} ...............................................

//${QS::QUTest-stub::QTimeEvt::tick1_} .......................................
//! @private @memberof QTimeEvt
void QTimeEvt_tick1_(
    uint_fast8_t const tickRate,
    void const * const sender)
{
    QS_CRIT_STAT
    QS_CRIT_ENTRY();
    QS_MEM_SYS();

    QTimeEvt *prev = &QTimeEvt_timeEvtHead_[tickRate];

    QS_BEGIN_PRE_(QS_QF_TICK, 0U)
        ++prev->ctr;
        QS_TEC_PRE_(prev->ctr); // tick ctr
        QS_U8_PRE_(tickRate);   // tick rate
    QS_END_PRE_()

    // is current Time Event object provided?
    QTimeEvt *t = (QTimeEvt *)QS_rxPriv_.currObj[TE_OBJ];
    if (t != (QTimeEvt *)0) {

        // the time event must be armed
        Q_ASSERT_INCRIT(810, t->ctr != 0U);

        QActive * const act = (QActive *)(t->act);

        // the recipient AO must be provided
        Q_ASSERT_INCRIT(820, act != (QActive *)0);

        // periodic time evt?
        if (t->interval != 0U) {
            t->ctr = t->interval; // rearm the time event
        }
        else { // one-shot time event: automatically disarm
            t->ctr = 0U; // auto-disarm
            // mark time event 't' as NOT linked
            t->super.refCtr_ &= (uint8_t)(~(uint8_t)QTE_IS_LINKED);

            QS_BEGIN_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, act->prio)
                QS_OBJ_PRE_(t);        // this time event object
                QS_OBJ_PRE_(act);      // the target AO
                QS_U8_PRE_(tickRate);  // tick rate
            QS_END_PRE_()
        }

        QS_BEGIN_PRE_(QS_QF_TIMEEVT_POST, act->prio)
            QS_TIME_PRE_();            // timestamp
            QS_OBJ_PRE_(t);            // the time event object
            QS_SIG_PRE_(t->super.sig); // signal of this time event
            QS_OBJ_PRE_(act);          // the target AO
            QS_U8_PRE_(tickRate);      // tick rate
        QS_END_PRE_()
        QS_MEM_APP();
        QS_CRIT_EXIT(); // exit critical section before posting

        QACTIVE_POST(act, &t->super, sender); // asserts if queue overflows

        QS_CRIT_ENTRY();
        QS_MEM_SYS();
    }

    // update the linked list of time events
    for (;;) {
        t = prev->next;  // advance down the time evt. list

        // end of the list?
        if (t == (QTimeEvt *)0) {

            // any new time events armed since the last QTimeEvt_tick_()?
            if (QTimeEvt_timeEvtHead_[tickRate].act != (void *)0) {

                // sanity check
                Q_ASSERT_INCRIT(830, prev != (QTimeEvt *)0);
                prev->next = (QTimeEvt *)QTimeEvt_timeEvtHead_[tickRate].act;
                QTimeEvt_timeEvtHead_[tickRate].act = (void *)0;
                t = prev->next;  // switch to the new list
            }
            else {
                break; // all currently armed time evts. processed
            }
        }

        // time event scheduled for removal?
        if (t->ctr == 0U) {
            prev->next = t->next;
            // mark time event 't' as NOT linked
            t->super.refCtr_ &= (uint8_t)(~(uint8_t)QTE_IS_LINKED);
            // do NOT advance the prev pointer
            QS_MEM_APP();
            QS_CRIT_EXIT(); // exit crit. section to reduce latency
        }
        else {
            prev = t; // advance to this time event
            QS_MEM_APP();
            QS_CRIT_EXIT(); // exit crit. section to reduce latency
        }
        QS_CRIT_ENTRY(); // re-enter crit. section to continue
        QS_MEM_SYS();
    }

    QS_MEM_APP();
    QS_CRIT_EXIT();
}

//${QS::QUTest-stub::QHsmDummy} ..............................................

//${QS::QUTest-stub::QHsmDummy::ctor} ........................................
//! @public @memberof QHsmDummy
void QHsmDummy_ctor(QHsmDummy * const me) {
    static struct QAsmVtable const vtable = {
        &QHsmDummy_init_,
        &QHsmDummy_dispatch_
    #ifdef Q_SPY
        ,&QHsm_getStateHandler_
    #endif
    };
    me->super.vptr = &vtable;  // hook the vptr
}

//${QS::QUTest-stub::QHsmDummy::init_} .......................................
//! @private @memberof QHsmDummy
void QHsmDummy_init_(
    QAsm * const me,
    void const * const par,
    uint_fast8_t const qs_id)
{
    Q_UNUSED_PAR(par);

    #ifdef Q_SPY
    if ((QS_priv_.flags & 0x01U) == 0U) {
        QS_priv_.flags |= 0x01U;
        QS_FUN_DICTIONARY(&QHsm_top);
    }
    #endif

    QS_CRIT_STAT
    QS_CRIT_ENTRY();
    QS_MEM_SYS();
    QS_BEGIN_PRE_(QS_QEP_STATE_INIT, qs_id)
        QS_OBJ_PRE_(me);            // this state machine object
        QS_FUN_PRE_(me->state.fun); // the source state
        QS_FUN_PRE_(me->temp.fun);  // the target of the initial tran.
    QS_END_PRE_()
    QS_MEM_APP();
    QS_CRIT_EXIT();
}

//${QS::QUTest-stub::QHsmDummy::dispatch_} ...................................
//! @private @memberof QHsmDummy
void QHsmDummy_dispatch_(
    QAsm * const me,
    QEvt const * const e,
    uint_fast8_t const qs_id)
{
    QS_CRIT_STAT
    QS_CRIT_ENTRY();
    QS_MEM_SYS();
    QS_BEGIN_PRE_(QS_QEP_DISPATCH, qs_id)
        QS_TIME_PRE_();             // time stamp
        QS_SIG_PRE_(e->sig);        // the signal of the event
        QS_OBJ_PRE_(me);            // this state machine object
        QS_FUN_PRE_(me->state.fun); // the current state
    QS_END_PRE_()
    QS_MEM_APP();
    QS_CRIT_EXIT();
}

//${QS::QUTest-stub::QActiveDummy} ...........................................

//${QS::QUTest-stub::QActiveDummy::ctor} .....................................
//! @public @memberof QActiveDummy
void QActiveDummy_ctor(QActiveDummy * const me) {
    // superclass' ctor
    QActive_ctor(&me->super, Q_STATE_CAST(0));

    static struct QAsmVtable const vtable = {
        &QActiveDummy_init_,
        &QActiveDummy_dispatch_
    #ifdef Q_SPY
        ,&QHsm_getStateHandler_
    #endif
    };
    me->super.super.vptr = &vtable;  // hook the vptr
}

//${QS::QUTest-stub::QActiveDummy::init_} ....................................
//! @private @memberof QActiveDummy
void QActiveDummy_init_(
    QAsm * const me,
    void const * const par,
    uint_fast8_t const qs_id)
{
    Q_UNUSED_PAR(qs_id);

    QHsmDummy_init_(me, par, ((QActive const *)me)->prio);
}

//${QS::QUTest-stub::QActiveDummy::dispatch_} ................................
//! @private @memberof QActiveDummy
void QActiveDummy_dispatch_(
    QAsm * const me,
    QEvt const * const e,
    uint_fast8_t const qs_id)
{
    Q_UNUSED_PAR(qs_id);

    QHsmDummy_dispatch_(me, e, ((QActive const *)me)->prio);
}

//${QS::QUTest-stub::QActiveDummy::fakePost_} ................................
//! @private @memberof QActiveDummy
bool QActiveDummy_fakePost_(
    QActive * const me,
    QEvt const * const e,
    uint_fast16_t const margin,
    void const * const sender)
{
    QS_TEST_PROBE_DEF(&QActive_post_)

    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    QF_MEM_SYS();

    // test-probe#1 for faking queue overflow
    bool status = true;
    QS_TEST_PROBE_ID(1,
        status = false;
        if (margin == QF_NO_MARGIN) {
            // fake assertion Mod=qf_actq,Loc=110
            Q_onError("qf_actq", 110);
        }
    )

    // is it a mutable event?
    if (QEvt_getPoolId_(e) != 0U) {
        QEvt_refCtr_inc_(e);
    }

    uint_fast8_t const rec = (status ? (uint_fast8_t)QS_QF_ACTIVE_POST
                             : (uint_fast8_t)QS_QF_ACTIVE_POST_ATTEMPT);
    QS_BEGIN_PRE_(rec, me->prio)
        QS_TIME_PRE_();      // timestamp
        QS_OBJ_PRE_(sender); // the sender object
        QS_SIG_PRE_(e->sig); // the signal of the event
        QS_OBJ_PRE_(me);     // this active object
        QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr
        QS_EQC_PRE_(0U);     // # free entries
        QS_EQC_PRE_(margin); // margin requested
    QS_END_PRE_()

    // callback to examine the posted event under the same conditions
    // as producing the #QS_QF_ACTIVE_POST trace record, which are:
    // the local filter for this AO ('me->prio') is set
    if (QS_LOC_CHECK_(me->prio)) {
        QS_onTestPost(sender, me, e, status);
    }
    QF_MEM_APP();
    QF_CRIT_EXIT();

    // recycle the event immediately, because it was not really posted
    #if (QF_MAX_EPOOL > 0U)
    QF_gc(e);
    #endif

    return status; // the event is "posted" correctly
}

//${QS::QUTest-stub::QActiveDummy::fakePostLIFO_} ............................
//! @private @memberof QActiveDummy
void QActiveDummy_fakePostLIFO_(
    QActive * const me,
    QEvt const * const e)
{
    QS_TEST_PROBE_DEF(&QActive_postLIFO_)

    QF_CRIT_STAT
    QF_CRIT_ENTRY();
    QF_MEM_SYS();

    // test-probe#1 for faking queue overflow
    QS_TEST_PROBE_ID(1,
        // fake assertion Mod=qf_actq,Loc=210
        Q_onError("qf_actq", 210);
    )

    // is it a mutable event?
    if (QEvt_getPoolId_(e) != 0U) {
        QEvt_refCtr_inc_(e);
    }

    QS_BEGIN_PRE_(QS_QF_ACTIVE_POST_LIFO, me->prio)
        QS_TIME_PRE_();      // timestamp
        QS_SIG_PRE_(e->sig); // the signal of this event
        QS_OBJ_PRE_(me);     // this active object
        QS_2U8_PRE_(QEvt_getPoolId_(e), e->refCtr_); // poolId & refCtr
        QS_EQC_PRE_(0U);     // # free entries
        QS_EQC_PRE_(0U);     // min # free entries
    QS_END_PRE_()

    // callback to examine the posted event under the same conditions
    // as producing the #QS_QF_ACTIVE_POST trace record, which are:
    // the local filter for this AO ('me->prio') is set
    if (QS_LOC_CHECK_(me->prio)) {
        QS_onTestPost((QActive *)0, me, e, true);
    }
    QF_MEM_APP();
    QF_CRIT_EXIT();

    // recycle the event immediately, because it was not really posted
    #if (QF_MAX_EPOOL > 0U)
    QF_gc(e);
    #endif
}
//$enddef${QS::QUTest-stub} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

#endif // Q_UTEST != 0

#endif // Q_UTEST
