Skip to content

Commit

Permalink
Changed tstrun() to tstsuite().
Browse files Browse the repository at this point in the history
  • Loading branch information
rdentato committed Nov 18, 2023
1 parent acaa0df commit c509905
Show file tree
Hide file tree
Showing 18 changed files with 172 additions and 104 deletions.
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
             
<img height="150" src="https://github.com/rdentato/tst/assets/48629/248f5856-13bd-4e35-8d9f-0b74a0ecb010"> <br/>
# tst
A vary simple, single-header, unit test framework for C (and C++). (Join us on [Discord](https://discord.gg/BqsZjDaUxg)!)
A very simple, small, single-header, unit test framework for C (and C++).<br/>
(Join us on our [Discord](https://discord.gg/BqsZjDaUxg) channel for queries and feedback)

## Introduction
`tst` is a lightweight unit testing framework designed for C programs (but works for C++ as well).
It provides a suite of functionalities to define, group, and validate test cases, while offering utilities
for expressive reporting and diagnostic messaging. With minimal syntax, `tst` fosters easy test integration into C projects.

I never understood why some of the most common unit test frameworks had so many files and complex build.
I never understood why some of the most common unit test frameworks had so many files and complex build dependencies so
I decided to write a small (less than 300 lines of code), no-dependencies unit testing framework that still has most of the
feautures you can find in much more complex ones.

If you want to use `tst`, just include `tst.h` and you're ready to write your test cases.

Check the [**tutorial**](tutorial/) for a detailed description of how to use `tst` or the [**reference manual**](tutorial/reference.md)
for a short description of each function.
for a short description of each function.\

Fell free to provide ideas, bugs, suggestions or even full Pull Requests if you feel inclined to do so!

Expand All @@ -33,7 +37,7 @@ any issue on using `tst.h` with your compiler, please let me know.
```c
#include "tst.h" // Ensure the tst framework is included

tstrun("Primary Test Suite")
tstsuite("Primary Test Suite")
{
tstcase("Equality Checks %d, %d", 1, 1) {
tstcheck(1 == 1, "Mismatch: %d != %d", 1, 1);
Expand Down Expand Up @@ -66,7 +70,7 @@ Compile and run the above program (no need for a `main()` function)
to execute all the tests and generate a log like this:
```
----- FILE ▷ t_small_example.c "Primary Test Suite"
----- SUIT ▷ t_small_example.c "Primary Test Suite"
5 CASE┬── Equality Checks 1, 1
6 PASS│ 1 == 1
7 FAIL├┬ 1 != 1
Expand All @@ -88,5 +92,5 @@ to execute all the tests and generate a log like this:
There is no limitation on how you organize and run your tests.
Just for the purpose of self-testing (and to provide an example on how you could
organize your testing workflow) the `tstrun` script (in the `src` directory) provides
a convenient way to launch groups of tests and pass them argument.
organize your testing workflow) the `tstrun` script in the `src` directory provides
a convenient way to launch groups of tests and pass them arguments.
93 changes: 51 additions & 42 deletions src/tst.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: © 2023 Remo Dentato <rdentato@gmail.com>
// SPDX-License-Identifier: MIT
// SPDX-PackageVersion: 0.5.4-rc
// SPDX-PackageVersion: 0.7.1-rc

#ifndef TST_VERSION
#define TST_VERSION 0x0005004C
#define TST_VERSION 0x0007001C

#ifdef __cplusplus
extern "C" {
Expand All @@ -13,12 +13,12 @@ extern "C" {
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>

static volatile short tst_zero = 0;
static short tst_result = 0;
static short tst_color = 1;
static short tst_report_err = 1;
static short tst_result = 0;
static short tst_color = 0;
static short tst_report_err = 0;

static int tst_pass = 0;
static int tst_fail = 0;
Expand All @@ -28,7 +28,9 @@ static const char* tst_title = NULL;
static const char *tst_str_red = "\0\033[1;31m";
static const char *tst_str_green = "\0\033[0;32m";
static const char *tst_str_yellow = "\0\033[0;33m";
static const char *tst_str_cyan = "\0\033[1;36m";
static const char *tst_str_normal = "\0\033[0m";
//static const char *tst_str_bold = "\0\033[1m";

const char *tst_str_skip = "SKIP| ";
const char *tst_str_fail = "FAIL| ";
Expand All @@ -37,9 +39,9 @@ const char *tst_str_skip_tst = "SKPT|,-(%s)";
const char *tst_str_skip_end = " |`---";
const char *tst_str_case = "CASE,--";
const char *tst_str_case_end = " `--- ";
const char *tst_str_file = "----- FILE >";
const char *tst_str_file_end = "^^^^^ RSLT > ";
const char *tst_str_file_abr = "^^^^^ ABRT > ";
const char *tst_str_file = "SUIT /";
const char *tst_str_file_end = "^^^^^ RSLT \\ ";
const char *tst_str_file_abr = "^^^^^ ABRT \\ ";
const char *tst_str_clck = "CLCK: %ld %ss ";
const char *tst_str_note = "NOTE:";
const char *tst_str_sctn = "SCTN|,--";
Expand Down Expand Up @@ -98,41 +100,46 @@ static unsigned char tst_tags_val = 0x00; // All tags are "off" by default

static inline int tst_tags_zero(); // tst_tags_zero() always returns 0 and is used just to avoid compiler warnings.

#define TST_STR_HELP_NOTAGS "[--help] [--color-off] [--report-error] [--list]"
#define TST_STR_HELP_NOTAGS "[--help] [--color] [--report-error] [--list]"
#define TST_STR_HELP_TAGS " [+/-]tag ... ]\ntags:"

static inline short tst_parse_tags(int argc, const char **argv, int ntags, const char **names) {
unsigned char v;
const char *arg;
short report_error = 0;
short report_error = tst_report_err;
if (names[0][0] == '\0') ntags=tst_tags_zero();
if (*argv == NULL) return report_error;
for (int n=0; n<argc; n++) {
arg = argv[n];
v = 0xFF;
if ((arg[0] == '-') && (arg[1] == '-')) {
switch (arg[2]) {
case 'r': report_error = 1; break;
case 'c': tst_color = 0; break;
case 'h': fprintf(stderr,"Test suite: \"%s\"\n%s %s", tst_title, argv[0], TST_STR_HELP_NOTAGS);
if (ntags>0) fputs(TST_STR_HELP_TAGS,stderr);
goto prttags;
case 'l': fprintf(stderr,"%s \"%s\"", argv[0], tst_title);
prttags: for (int k=0; k<ntags; k++) fprintf(stderr," %s",names[k]);
fputc('\n',stderr);
exit(0);
while (*arg) { // Allow a single string to contain multiple options (e.g. "--color +NoDB")
v = 0xFF;
if ((arg[0] == '-') && (arg[1] == '-')) {
switch (arg[2]) {
case 'r': report_error = 1; break;
case 'c': tst_color ^= 1; break;
case 'h': fprintf(stderr,"Test suite: \"%s\"\n%s %s", tst_title, argv[0], TST_STR_HELP_NOTAGS);
if (ntags>0) fputs(TST_STR_HELP_TAGS,stderr);
goto prttags;
case 'l': fprintf(stderr,"%s \"%s\"", argv[0], tst_title);
prttags: for (int k=0; k<ntags; k++) fprintf(stderr," %s",names[k]);
fputc('\n',stderr);
exit(0);
}
goto nextarg;
}
continue;
}
if (*arg == '-') {arg++; v=0x00;}
if (*arg == '+') {arg++; v=0xFF;}
if (*arg == '*') {tst_tags_val = v; continue;}
if (*arg == '\0') continue;

for (int k=0; k<ntags; k++) {
if (strcmp(arg,names[k])==0) {
if (v) tst_tags_val |= (unsigned char)(1<<k);
else tst_tags_val &= (unsigned char)(~(1<<k));
if (*arg == '-') {arg++; v=0x00;}
if (*arg == '+') {arg++; v=0xFF;}
if (*arg == '*') {tst_tags_val = v; goto nextarg;}
if (*arg == '\0') goto nextarg;

for (int k=0; k<ntags; k++) {
if (strcmp(arg,names[k])==0) {
if (v) tst_tags_val |= (unsigned char)(1<<k);
else tst_tags_val &= (unsigned char)(~(1<<k));
}
}
nextarg: while(*arg && !isspace(*arg)) arg++;
while(*arg && isspace(*arg)) arg++;
}
}
// Return 1 if errors are to be reported as program failure
Expand All @@ -153,19 +160,21 @@ static inline char *tst_time(void)
#define tstrun_(tst_, title_,...) \
tst_tags(0,__VA_ARGS__); void tst__run(int n); \
int main(int argc, char **argv) { \
tst_title = title_; \
tst_title = getenv("TSTOPTIONS"); \
tst_report_err = (short)tst_parsetags(1,(const char **)&tst_title); \
tst_report_err = (short)tst_parsetags(argc,(const char **)argv); \
tst_title = title_; \
if (CLOCKS_PER_SEC > ((clock_t)1000000) + tst_zero) tst_clock_unit = "n"; \
else if(CLOCKS_PER_SEC > ((clock_t)1000) + tst_zero) tst_clock_unit = "u"; \
else tst_clock_unit = "m"; \
fprintf(stderr, "%s %s \"%s\" %s%s\n", tst_str_file, __FILE__, tst_title, tst_time(), (tst_?"":" (disabled)"));\
fprintf(stderr, "----- %s%s %s \"%s\" %s%s%s\n", tst_color+tst_str_cyan,tst_str_file, __FILE__, tst_title, tst_time(), tst_color+tst_str_normal,(tst_?"":" (disabled)"));\
if (tst_) tst__run(tst_usestatic); \
fputs(tst_str_file_end,stderr); tst_prt_results(tst_fail, tst_pass, tst_skip); fprintf(stderr," %s\n",tst_time());\
return ((tst_fail > 0) * tst_report_err); \
} void tst__run(int tst_n)

#define tstrun(title_,...) tstrun_((!tst_zero), title_, __VA_ARGS__)
#define tst_run(title_,...) tstrun_(( tst_zero), title_, __VA_ARGS__)
#define tstsuite(title_,...) tstrun_((!tst_zero), title_, __VA_ARGS__)
#define tst_suite(title_,...) tstrun_(( tst_zero), title_, __VA_ARGS__)

static short tst_vars[6] = {0}; // Ensures that `tstcheck` can be used outside a `tstcase` block.

Expand All @@ -179,18 +188,18 @@ static inline int tstfailed(void) {return !tst_result;}
static inline int tstpassed(void) {return tst_result;}
static inline int tstskipped(void) {return (tst_result < 0);}

#define tstcheck_(tst_a,tst_s,tst_r,...) \
#define tstcheck_(tst_abrt,tst_str,tst_res,...) \
do { \
tst_result = (short)(tst_skip_test? -1 : !!(tst_r)); \
tst_result = (short)(tst_skip_test? -1 : !!(tst_res)); \
switch (tst_result) { \
case -1: tst_skip++; tst_case_skip++; tst_prtln(tst_str_skip); fputs(tst_color+tst_str_yellow, stderr); break; \
case 0: tst_fail++; tst_case_fail++; tst_prtln(tst_str_fail); fputs(tst_color+tst_str_red , stderr); break; \
case 1: tst_pass++; tst_case_pass++; tst_prtln(tst_str_pass); fputs(tst_color+tst_str_green , stderr); break; \
} \
fprintf(stderr, "%s%s", tst_s, tst_str_normal+tst_color); \
fprintf(stderr, "%s%s", tst_str, tst_str_normal+tst_color); \
if (tst_result == 0) { \
fprintf(stderr," \"" __VA_ARGS__); fputc('"',stderr); \
if (tst_a) { \
if (tst_abrt) { \
fputs(tst_str_file_abr,stderr); tst_prt_results(tst_fail, tst_pass, tst_skip); fprintf(stderr," %s\n",tst_time()); \
exit(0);\
} \
Expand Down
61 changes: 45 additions & 16 deletions src/tstrun
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ usage () {
echo "OPTIONS"
echo " -h | --help this help"
echo " -l | --list prints the list of available tests"
echo " -c | --color-off turns off coloured messages"
echo " -c | --color toggles coloured messages on/off"
echo " -d | --test-directory dir cd to the directory dir with tests"
echo " -w | --wildcard '*x[yz]' specify a file pattern to match the tests to execute"
echo " -o | --output filename the name of the generated logfile"
Expand All @@ -27,12 +27,34 @@ usage () {
WILDCARD="*"
COLOR=""
OUTFILE="test.log"
TAGS=""

COLOR_TOGGLE=1
DEFAULT_OPTIONS=0

toggle_color () {
if [ $COLOR_TOGGLE -eq 0 ]; then
COLOR_TOGGLE=1
## First character is \033 (ESC)
COLOR_RED=""
COLOR_GREEN=""
COLOR_YELLOW=""
COLOR_NONE=""
else
COLOR_TOGGLE=0
COLOR_RED=""
COLOR_GREEN=""
COLOR_YELLOW=""
COLOR_NONE=""
fi
}

toggle_color # Set default to colors off

## First character is \033 (ESC)
COLOR_RED=""
COLOR_GREEN=""
COLOR_YELLOW=""
COLOR_NONE=""
if [ "$TSTOPTIONS" != "" ] ; then
set -- $TSTOPTIONS "@@" "$@"
DEFAULT_OPTIONS=1
fi

while [ "$#" -gt 0 ]; do
case $1 in
Expand All @@ -56,23 +78,30 @@ while [ "$#" -gt 0 ]; do
cd $1
shift ;;

-c | --color-off)
COLOR="--c"
COLOR_RED=""
COLOR_GREEN=""
COLOR_YELLOW=""
COLOR_NONE=""
shift ;;
@@)
DEFAULT_OPTIONS=0
shift
;;

-c | --color)
toggle_color
if [ $DEFAULT_OPTIONS -eq 0 ] ; then COLOR="--c" ; fi
shift
;;

-o | --output)
shift;
if [ "$#" -eq 0 ]; then echo "ERROR: Missing output file name"; usage ; fi
OUTFILE=$1;
shift ;;

--*) echo "Invalid option"; usage ;;
--*) echo "Invalid option $1"; usage ;;

+*|-*) break ;;
+*|-*)
if [ $DEFAULT_OPTIONS -eq 0 ] ; then break ; fi
TAGS="$TAGS $1" #collect tags that may be in default options
shift
;;

*) WILDCARD=$1
shift
Expand All @@ -83,7 +112,7 @@ done

echo "output: $OUTFILE ($WILDCARD)"
echo "STIME `date`" > $OUTFILE
find . -name "t_$WILDCARD" -print | sed -e '/\.c$/d' -e '/\.cpp$/d' -e '/\.o$/d' | while read -r f ; do $f $COLOR $* ; done 2>> $OUTFILE
find . -name "t_$WILDCARD" -print | sed -e '/\.c$/d' -e '/\.cpp$/d' -e '/\.o$/d' | while read -r f ; do $f $COLOR $TAGS $* ; done 2>> $OUTFILE

FAIL=`grep '^[0-9 ]*FAIL' $OUTFILE | wc -l`
PASS=`grep '^[0-9 ]*PASS' $OUTFILE | wc -l`
Expand Down
2 changes: 1 addition & 1 deletion test/t_tst00.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include "tst.h" // Ensure the tst framework is included

tstrun("Primary Test Suite")
tstsuite("Primary Test Suite")
{
tstcase("Equality Checks %d, %d", 1, 1) {
tstcheck(1 == 1, "Mismatch: %d != %d", 1, 1);
Expand Down
2 changes: 1 addition & 1 deletion test/t_tst01.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include "tst.h"

tstrun("Switching groups on and off",NoDB, FileOnly, SimpleRun)
tstsuite("Switching groups on and off",NoDB, FileOnly, SimpleRun)
{
tstskipif(tsttag(NoDB) && !tsttag(SimpleRun)) {
tstcheck("Test 1 (NoDB && !SimpleRun)")
Expand Down
2 changes: 1 addition & 1 deletion test/t_tst02.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include "tst.h"

tstrun("Sections") {
tstsuite("Sections") {
tstcase("All sections") {
int a = -1;

Expand Down
2 changes: 1 addition & 1 deletion test/t_tst03.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ int f(int n, const char *s) {
return 1;
}

tstrun("Data driven tests") {
tstsuite("Data driven tests") {
tstcase("Use static data") {

struct {int n; const char *s;} tstdata[] = {
Expand Down
2 changes: 1 addition & 1 deletion test/t_tst04.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include "tst.h" // Ensure the tst framework is included

tstrun("Primary Test Suite")
tstsuite("Primary Test Suite")
{
tstcase("Equality Checks %d, %d", 1, 1) {
tstcheck(1 == 1, "Mismatch: %d != %d", 1, 1);
Expand Down
2 changes: 1 addition & 1 deletion test/t_tst05.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include "tst.h"

tstrun("Switching groups on and off",NoDB, FileOnly, SimpleRun)
tstsuite("Switching groups on and off",NoDB, FileOnly, SimpleRun)
{
tstcheck("Test NoDB 0 (always)");

Expand Down
4 changes: 2 additions & 2 deletions test/t_tst06.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include "tst.h"


tstrun("Data driven tests") {
tstsuite("Data driven tests") {
srand(time(0));
tstcase("A random integer array in the range [-10 10]") {
int tstdata[4]; // array size must be specified
Expand All @@ -14,7 +14,7 @@ tstrun("Data driven tests") {
tstprintf("[%d] = %d\n",k,tstdata[k]);
}
}
tstsection( "Cehck 1") {
tstsection( "Check 1") {
tstcheck (-10 <= tstcurdata && tstcurdata <= 10);
}
tstsection( "Check 2") {
Expand Down
Loading

0 comments on commit c509905

Please sign in to comment.