From e6d1ce57f1b5facc4c1b468a90d94b66679ced98 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Wed, 17 Apr 2024 13:50:22 -0400 Subject: [PATCH] Initial commit --- Makefile | 54 ++++++++++++++++++++++++++++++++++++++ README.md | 62 ++++++++++++++++++++++++++++++++++++++++++++ runner.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 runner.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6db105c --- /dev/null +++ b/Makefile @@ -0,0 +1,54 @@ +# _,.---._ .-._ .--.-. ,--.--------. +# _,..---._ ,-.' , - `. /==/ \ .-._/==/ //==/, - , -\ +# /==/, - \ /==/_, , - \|==|, \/ /, |==\ -\\==\.-. - ,-./ +# |==| _ _\==| .=. |==|- \| | \==\- \`--`\==\- \ +# |==| .=. |==|_ : ;=: - |==| , | -| `--`-' \==\_ \ +# |==|,| | -|==| , '=' |==| - _ | |==|- | +# |==| '=' /\==\ - ,_ /|==| /\ , | |==|, | +# |==|-, _`/ '.='. - .' /==/, | |- | /==/ -/ +# `-.`.____.' `--`--'' `--`./ `--` `--`--` +# _ __ ,---. .-._ .=-.-. _,.----. +# .-`.' ,`..--.' \ /==/ \ .-._ /==/_ /.' .' - \ +# /==/, - \==\-/\ \ |==|, \/ /, /==|, |/==/ , ,-' +# |==| _ .=. /==/-|_\ | |==|- \| ||==| ||==|- | . +# |==| , '=',\==\, - \ |==| , | -||==|- ||==|_ `-' \ +# |==|- '..'/==/ - ,| |==| - _ ||==| ,||==| _ , | +# |==|, | /==/- /\ - \|==| /\ , ||==|- |\==\. / +# /==/ - | \==\ _.\=\.-'/==/, | |- |/==/. / `-.`.___.-' +# `--`---' `--` `--`./ `--``--`-` +# +# @(#)Copyright (c) 2023, Nathan D. Fisher. +# +# This is free software. It comes with NO WARRANTY. +# Permission to use, modify and distribute this source code +# is granted subject to the following conditions. +# 1/ that the above copyright notice and this notice +# are preserved in all copies and that due credit be given +# to the author. +# 2/ that any changes to this code are clearly commented +# as such so that the author does not get blamed for bugs +# other than his own. +# + +CFLAGS += -I../include +# Add your library to $(LDLIBS) +#LDLIBS += ../lib.a +# Any other libraries in $(LIBS) also get linked in +LDLIBS += $(LIBS) + +# Add the names of your tests to the array $(tests) +#tests += + +.PHONY: test +test: $(tests) runner output + ./runner $(tests) + +output: + @ [-d $@ ] 2>/dev/null || install -d $@ + +runner: runner.c + $(CC) -o runner runner.c + +.PHONY: clean +clean: + rm -rf $(tests) runner output/* diff --git a/README.md b/README.md new file mode 100644 index 0000000..2b253f9 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +Contents +======== +- [Rationale](#rationale) +- [Usage](#usage) +- [Writing Tests](#writing_tests) + +## Rationale +The purpose of this package is both practical and as a demonstration for just how +simple a test harness can be, in order to encourage both more testing in C code +as well as the use of simple tools. As such, the **Roadrunner** test runner consists +of a test runner written in a single file of highly portable C, totaling 43 lines, +and a simple, POSIX compatible Makefile which works with both GNU and BSD make. + +The original version of this project was written entirely in POSIX make, but due to +the variations in POSIX shell behavior, especially in regards to the variety of ways +that both the built in *echo* command and */bin/echo* screw up sending terminal escape +sequences, something more robust and portable was desirable. There were simply too +many corner cases to be bothered to find them all. + +## Usage +The best way to use this is to stuff it into a `test` subdirectory and call it via +`make -C test`, or invoke it from your project's `Makefile` as a sub-make. If you +are using something other than plain *make* to build your project then you probably +don't want this, but also, why? That said, let's not get sidetracked. + +The example Makefile also goes into the `test` subdirectory, and you will want to add +the name of the library that you are testing on the appropriate line as well as the +names of all of the tests that you wish to run. Add those to the `tests` array. +```Makefile +tests += my_test +``` +For each member of `tests` there should be a corresponding file `.c` which +will be compiled when you run `make` in this directory, along with the small helper +program `runner.c`. Make will then invoke `./runner` with the list of tests as it's +arguments. The *runner* program will keep track of successes and failures, as well +as skipped tests, giving colored output and a nice summary at the end. If there are +no failures the program exits with a value of **0**, otherwise the number of failures +is returned. + +Your library will have to be built as a static library and statically linked to each +test program. All you have to do (after building the static library) is add it to +the `$(LDLIBS)` array in the Makefile. + +## Writing Tests +### Return values +The return values for each test shouuld follow these conventions. +- 0 for success +- 255 or -1 indicates this test was skipped +- any other value indicates failure + +### Skips +If your test requires elevated privileges, you can simply test for uid == 0 and then +bail with code 255 if the runner is run by a different user. Similarly, if your test +needs the network then you can test programmatically for a working connection and bail +with 255 if it isn't available. + +### Don't use *assert* +The `assert` function from `assert.h` will not cause your test to fail, because it +simply aborts the program after printing a message to stderr. Instead, check for +proper results and return an exit code if the test fails. You can also print error +messages in your tests, which *runner* will capture, assume it means failure, and +print to stderr in the terminal session. diff --git a/runner.c b/runner.c new file mode 100644 index 0000000..45382b2 --- /dev/null +++ b/runner.c @@ -0,0 +1,77 @@ +/* _,.---._ .-._ .--.-. ,--.--------. + * _,..---._ ,-.' , - `. /==/ \ .-._/==/ //==/, - , -\ + * /==/, - \ /==/_, , - \|==|, \/ /, |==\ -\\==\.-. - ,-./ + * |==| _ _\==| .=. |==|- \| | \==\- \`--`\==\- \ + * |==| .=. |==|_ : ;=: - |==| , | -| `--`-' \==\_ \ + * |==|,| | -|==| , '=' |==| - _ | |==|- | + * |==| '=' /\==\ - ,_ /|==| /\ , | |==|, | + * |==|-, _`/ '.='. - .' /==/, | |- | /==/ -/ + * `-.`.____.' `--`--'' `--`./ `--` `--`--` + * _ __ ,---. .-._ .=-.-. _,.----. + * .-`.' ,`..--.' \ /==/ \ .-._ /==/_ /.' .' - \ + * /==/, - \==\-/\ \ |==|, \/ /, /==|, |/==/ , ,-' + * |==| _ .=. /==/-|_\ | |==|- \| ||==| ||==|- | . + * |==| , '=',\==\, - \ |==| , | -||==|- ||==|_ `-' \ + * |==|- '..'/==/ - ,| |==| - _ ||==| ,||==| _ , | + * |==|, | /==/- /\ - \|==| /\ , ||==|- |\==\. / + * /==/ - | \==\ _.\=\.-'/==/, | |- |/==/. / `-.`.___.-' + * `--`---' `--` `--`./ `--``--`-` + * + * @(#)Copyright (c) 2024, Nathan D. Fisher. + * + * This is free software. It comes with NO WARRANTY. + * Permission to use, modify and distribute this source code + * is granted subject to the following conditions. + * 1/ that the above copyright notice and this notice + * are preserved in all copies and that due credit be given + * to the author. + * 2/ that any changes to this code are clearly commented + * as such so that the author does not get blamed for bugs + * other than his own. + */ + +#include +#include + +int main(int argc, char *argv[]) { + int i, pass = 0, fail = 0, skip = 0, total, ret, read = 0; + FILE *pipe; + char cmd[100], output[500]; + + total = argc-1; + printf("\n\t=== \033[0;33mRunning %i tests\033[0m ===\n\n", total); + for (i = 1; i < argc; i++) { + snprintf(cmd, 100, "./%s", argv[i]); + printf("[%03i/%03i] %-25s", i, total, argv[i]); + pipe = popen(cmd, "w"); + read = fread(&output, 1, 500, pipe); + ret = pclose(pipe); + if (read) { + fail++; + printf("\033[0;31mFailed\033[0m\n"); + fprintf(stderr, "%s\n", &output); + continue; + } + switch (WEXITSTATUS(ret)) { + case 0: + pass++; + printf("\033[0;32mSuccess\033[0m\n"); + break; + case 255: + skip++; + printf("Skipped\n"); + break; + default: + fail++; + printf("\033[0;31mFailed\033[0m\n"); + } + } + if (fail) { + printf("\nResults: \033[0;31mFAILED\033[0m %i succeeded; %i failed; %i skipped\n\n", + pass, fail, skip); + } else { + printf("\nResults: \033[0;32mOk\033[0m %i succeeded; %i failed; %i skipped\n\n", + pass, fail, skip); + } + return fail; +}