Skip to content

Commit

Permalink
Add dynamic asserts (Design by Contract)
Browse files Browse the repository at this point in the history
  • Loading branch information
6arms1leg committed Apr 6, 2022
1 parent 9ea0649 commit 760b6b2
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 22 deletions.
2 changes: 1 addition & 1 deletion project.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
:project:
:use_exceptions: FALSE
:use_exceptions: TRUE
:use_test_preprocessor: TRUE
:use_auxiliary_dependencies: TRUE
:build_root: ./build/ceedling/
Expand Down
6 changes: 4 additions & 2 deletions src/TKLcs0.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ static volatile uint8_t pv_lockCnt;
void TKLcs0_enter(void) {
pv_lockCnt++; /* Atomic operation (no concurrency issues); must be first */
TKLCS0CFG_DIS_INT();
assert(0u != pv_lockCnt); /* No rollover must occur */
assert(0u != pv_lockCnt); /* Sanity check (Design by Contract); no rollover
must occur */
}

void TKLcs0_exit(void) {
assert(0u != pv_lockCnt); /* Crit. section must have been entered prev. */
assert(0u != pv_lockCnt); /* Sanity check (Design by Contract); crit.
section must have been entered prev. */
pv_lockCnt--;
if (0u == pv_lockCnt) {
TKLCS0CFG_ENA_INT();
Expand Down
2 changes: 1 addition & 1 deletion src/TKLcs0.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
/* `"` used intentionally. This allows the user to override and provide his
own implementation before falling back to libc. */
#include "stdint.h"
#include "assert.h"
#include "assert.h" /* For sanity checks (Design by Contract) */

/** \brief User-provided macros to dis-/enable all relevant interrupts */
#include "TKLcs0Cfg.h"
Expand Down
30 changes: 30 additions & 0 deletions src/TKLsdlr.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,30 @@ static volatile uint8_t pv_tskOverrunCnt;
* ==========
*/

/* "Invisible" API for unit tests to reset internal state (private vars.) */
#ifdef TEST
void TKLsdlr_utClrTickSrc(void) {
pv_p_getTick = NULL;
}
void TKLsdlr_utClrTskLst(void) {
pv_p_tskLst = NULL;
}
void TKLsdlr_utClrTskCnt(void) {
pv_tskCnt = 0u;
}
#endif /* TEST */

void TKLsdlr_setTickSrc(const TKLtyp_p_getTick_t p_getTick) {
assert(NULL != p_getTick); /* Sanity check (Design by Contract) */

pv_p_getTick = p_getTick;
}

void TKLsdlr_setTskLst(TKLtyp_tsk_t* const p_tskLst, const uint8_t tskCnt) {
/* Sanity check (Design by Contract) */
assert((NULL != p_tskLst) &&
(0u < tskCnt));

pv_p_tskLst = p_tskLst;
pv_tskCnt = tskCnt;
}
Expand All @@ -50,6 +69,12 @@ void TKLsdlr_clrTskOverrun(void) {
void TKLsdlr_setTskAct(const TKLtyp_p_tskRunner_t p_tskRunner,
const bool active,
const bool updLastRun) {
/* Sanity check (Design by Contract) */
assert((NULL != p_tskRunner) &&
(NULL != pv_p_getTick) &&
(NULL != pv_p_tskLst) &&
(0u < pv_tskCnt));

TKLtyp_tsk_t* const p_tskLst = pv_p_tskLst; /* Set ptr. to task list */
const uint8_t tskCnt = pv_tskCnt; /* Number of tasks in task list */

Expand All @@ -66,6 +91,11 @@ void TKLsdlr_setTskAct(const TKLtyp_p_tskRunner_t p_tskRunner,
}

void TKLsdlr_exec(void) {
/* Sanity check (Design by Contract) */
assert((NULL != pv_p_getTick) &&
(NULL != pv_p_tskLst) &&
(0u < pv_tskCnt));

TKLtyp_tsk_t* const p_tskLst = pv_p_tskLst; /* Set ptr. to task list */
const uint8_t tskCnt = pv_tskCnt; /* Number of tasks in task list */
const uint32_t tickCnt = (*pv_p_getTick)(); /* Get curr. tick count */
Expand Down
1 change: 1 addition & 0 deletions src/TKLsdlr.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "stddef.h"
#include "stdbool.h"
#include "limits.h"
#include "assert.h" /* For sanity checks (Design by Contract) */

#include "TKLtyp.h"
#include "TKLsdlrCfg.h"
Expand Down
36 changes: 36 additions & 0 deletions test/support/assert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/** \file */

#ifndef ASSERT_H
#define ASSERT_H

#include <stdbool.h>

#include "CException.h" /* Exception handling lib. interface */

/* `assert` replacement for unit testing */
#define assert(condition_) if (false == (condition_)) Throw(0)

/* Unit testing assertion that `assert` is triggered */
#define TEST_ASSERT_FAIL_ASSERT(codeUnderTest_) \
do { \
CEXCEPTION_T exception_; \
Try { \
codeUnderTest_; \
TEST_FAIL_MESSAGE("Code under test did not assert."); \
} \
Catch(exception_) {;} \
} while (false)

/* Unit testing assertion that `assert` is *not* triggered */
#define TEST_ASSERT_PASS_ASSERT(codeUnderTest_) \
do { \
CEXCEPTION_T exception_; \
Try { \
codeUnderTest_; \
} \
Catch(exception_) { \
TEST_FAIL_MESSAGE("Code under test failed an assertion."); \
} \
} while (false)

#endif /* ASSERT_H */
105 changes: 87 additions & 18 deletions test/test_TKLsdlr.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <stddef.h>
#include <stdbool.h>
#include <limits.h>
#include "assert.h" /* Sanity checks (Design by Contract); replaced for unit
testing */

#include "unity.h"

Expand All @@ -16,6 +18,11 @@

#include "mock_TKLtsk.h"

/* "Invisible" API for unit tests to reset internal state (private vars.) */
extern void TKLsdlr_utClrTickSrc(void);
extern void TKLsdlr_utClrTskLst(void);
extern void TKLsdlr_utClrTskCnt(void);

/* OPERATIONS
* ==========
*/
Expand All @@ -27,9 +34,10 @@ void setUp(void) {

/** \brief Run after every test */
void tearDown(void) {
/* Reset all private variables of module under test back to init. values */
TKLsdlr_setTickSrc(NULL);
TKLsdlr_setTskLst(NULL, 0u);
/* Reset internal state (private vars.) */
TKLsdlr_utClrTickSrc();
TKLsdlr_utClrTskLst();
TKLsdlr_utClrTskCnt();
TKLsdlr_clrTskOverrun();
}

Expand All @@ -54,6 +62,79 @@ void test_TKLsdlr_checkUintTickRolloverArith(void) {
TEST_ASSERT_EQUAL_UINT8(resExpAMinusB, (a - b));
}

/**
* \brief Test that assert fires on attempt to set relative system time tick
* count source to `NULL`
*/
void test_TKLsdlr_assertNoNullPtrOnSetTickSrc(void) {
TEST_ASSERT_FAIL_ASSERT(TKLsdlr_setTickSrc(NULL));
}

/**
* \brief Test that assert fires on attempt to set task list to `NULL` or its
* length to `0`
*/
void test_TKLsdlr_assertNoNullPtrNo0TskCntOnSetTskLst(void) {
TKLtyp_tsk_t tskLst[1] = {0};

TEST_ASSERT_FAIL_ASSERT(TKLsdlr_setTskLst(NULL, 1u));
TEST_ASSERT_FAIL_ASSERT(TKLsdlr_setTskLst(tskLst, 0u));
}

/**
* \brief Test that assert fires on attempt to set task activation status with
* `NULL` pointer task runner or incomplete initialization
*/
void test_TKLsdlr_assertNoNullPtrNo0TskCntOnSetTskAct(void) {
TKLtyp_tsk_t tskLst[1] = {0};

/* Fully init. */
TKLsdlr_setTickSrc(&TKLtick_getTick);
TKLsdlr_setTskLst(tskLst, 1u);

TEST_ASSERT_FAIL_ASSERT(TKLsdlr_setTskAct(NULL, false, false));

TKLsdlr_utClrTickSrc(); /* Tick source not set */

TEST_ASSERT_FAIL_ASSERT(TKLsdlr_setTskAct(&TKLtsk_runner, false, false));

/* Task list not set */
TKLsdlr_setTickSrc(&TKLtick_getTick);
TKLsdlr_utClrTskLst();

TEST_ASSERT_FAIL_ASSERT(TKLsdlr_setTskAct(&TKLtsk_runner, false, false));

/* Task count not set */
TKLsdlr_setTskLst(tskLst, 1u);
TKLsdlr_utClrTskCnt();

TEST_ASSERT_FAIL_ASSERT(TKLsdlr_setTskAct(&TKLtsk_runner, false, false));
}

/**
* \brief Test that assert fires on attempt to execute scheduler cycle with
* incomplete initialization
*/
void test_TKLsdlr_assertNoNullPtrNo0TskCntOnExec(void) {
TKLtyp_tsk_t tskLst[1] = {0};

TKLsdlr_setTskLst(tskLst, 1u); /* Tick source not set */

TEST_ASSERT_FAIL_ASSERT(TKLsdlr_exec());

/* Task list not set */
TKLsdlr_setTickSrc(&TKLtick_getTick);
TKLsdlr_utClrTskLst();

TEST_ASSERT_FAIL_ASSERT(TKLsdlr_exec());

/* Task count not set */
TKLsdlr_setTskLst(tskLst, 1u);
TKLsdlr_utClrTskCnt();

TEST_ASSERT_FAIL_ASSERT(TKLsdlr_exec());
}

/** \brief Test if relative system time tick count source is set correctly */
void test_TKLsdlr_setTickSrc(void) {
TKLtyp_tsk_t tskLst[] = {
Expand All @@ -71,11 +152,6 @@ void test_TKLsdlr_setTickSrc(void) {
TKLsdlr_exec(); /* ... and test if it gets called (exactly once) */
}

/** \brief Test that task list is initialized with `Null` pointer */
void test_TKLsdlr_initTskLstToNull(void) {
TEST_ASSERT_NULL(TKLsdlr_getTskLst());
}

/** \brief Test if task list is set and returned correctly */
void test_TKLsdlr_setAndReturnTskLst(void) {
TKLtyp_tsk_t tskLstExp[1] = {0};
Expand All @@ -87,16 +163,6 @@ void test_TKLsdlr_setAndReturnTskLst(void) {
TEST_ASSERT_EQUAL_PTR(tskLstExp, p_tskLstAct);
}

/**
* \brief Test that task count (number of task entries in the connected task
* list) is initialized to `0`
*/
void test_TKLsdlr_initTskCntTo0(void) {
const uint8_t tskCntExp = 0u;

TEST_ASSERT_EQUAL_UINT8(tskCntExp, TKLsdlr_cntTsk());
}

/**
* \brief Test if task count (number of task entries in the connected task
* list) is set and returned correctly
Expand Down Expand Up @@ -347,6 +413,7 @@ void test_TKLsdlr_enaSingleTsk(void) {
.p_tskRunner = &TKLtsk_runner}
};

TKLsdlr_setTickSrc(&TKLtick_getTick);
TKLsdlr_setTskLst(tskLst, 1u);
TKLsdlr_setTskAct(TKLtsk_runner, true, false);

Expand All @@ -363,6 +430,7 @@ void test_TKLsdlr_disSingleTsk(void) {
.p_tskRunner = &TKLtsk_runner}
};

TKLsdlr_setTickSrc(&TKLtick_getTick);
TKLsdlr_setTskLst(tskLst, 1u);
TKLsdlr_setTskAct(TKLtsk_runner, false, false);

Expand All @@ -388,6 +456,7 @@ void test_TKLsdlr_enaAndDisMultiSameTsk(void) {
const bool activeExpA = false;
const bool activeExpB = true;

TKLsdlr_setTickSrc(&TKLtick_getTick);
TKLsdlr_setTskLst(tskLst, 7u);
TKLsdlr_setTskAct(TKLtsk_runner0, activeExpA, false);
TKLsdlr_setTskAct(TKLtsk_runner1, activeExpB, false);
Expand Down

0 comments on commit 760b6b2

Please sign in to comment.