• Stars
    star
    351
  • Rank 120,906 (Top 3 %)
  • Language
    C
  • License
    MIT License
  • Created almost 7 years ago
  • Updated 11 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A testing library for C.

Build Status

Snow

Snow is a header-only unit testing library for C. Just include the file snow/snow.h.

IRC channel: #snow on Freenode. If you have any questions, or just want to chat, just ping me (@mort) :)

Screenshot

Snow 2

Snow 2 is a complete rewrite of Snow. Here are the highlights:

  • Blocks have moved from inside of macro arguments (i.e describe(foo, { ... })) to outside of macro arguments (i.e describe(foo) { ... }). This applies to describe, subdesc, it/test, before_each, and after_each.
    • This means that it's possible to show line numbers, that compiler error messages are nicer, and syntax highlighters and auto indenters should be more happy.
  • asserteq and assertneq works slightly differently, but most code which worked before should continue to work.
  • All assertion macros have gotten an extra, optional argument, which is an explanation of what the assertion means. For example, you can now write asserteq(foo, bar, "Some explanation").
  • You can select what tests to run with glob-style matches, not just filter based on the name of the top-level describe.

About

Some miscellaneous points:

  • Snow uses some GNU extensions, so it might not work with all ISO C compatible compilers. It's confirmed to work with at least GCC and Clang. It should even work on GCC and Clang versions too old to support C11 (or even C99), but the convenience asserteq and assertneq macros require C11.
  • I really recommend running the test executable with valgrind. That will help you find memory issues such as memory leaks, out of bounds array reads/writes, etc.
  • Windows is supported through MinGW or cygwin, with the caveat that it assumes your terminal supports UTF-8. CMD.exe and Powershell will print mangled ✓ and ✕ characters. (Git Bash and Cygwin's terminal should be fine though)
    • Windows also generally doesn't have the <fnmatch.h> header. Snow defaults to compile without fnmatch under MinGW (and instead uses plain strcmp). You can control this with -DSNOW_USE_FNMATCH=1 or -DSNOW_USE_FNMATCH=0. Gnulib implements fnmatch, and supports Windows under Cygwin.

Usage

When creating the main function using the snow_main macro, your executable will take these arguments. The --no- prefixed arguments will disable the relevant function:

  • --version, -v: Show the current version and exit.
  • --help, -h: Show usage and exit.
  • --list, -l: List available tests and exit.
  • --color, -c, or --no-color: Enable the use of color. Default: on when output is a TTY, off otherwise.
  • --quiet, -q, or --no-quiet: Suppress most messages, only test faulures and the 'Total: Passed X/Y tests' line will still print. Default: off.
  • --log <file>: Output to a log file instead of stdout.
  • --timer, -t, or --no-timer: Print the number of miliseconds CPU time spent on each test alongside its success message. Default: on.
  • --maybes, -m, or --no-maybes: Print out messages when beginning a test rather than just when it completed. Default: on when output is a TTY, off otherwise.
  • --cr, or --no-cr: Print a \r after maybe messages instead of \n. This will override them with successes or failures as they are printed out. Default: on when output is a TTY, off otherwise.

Example

Here's a simple example which tests a couple of filesystem functions, and has a subdescription for testing fread-related stuff.

  • Compile: gcc -Isnow -DSNOW_ENABLED -g -o test example.c
    • -Isnow: add snow to our include path, to make #include <snow/snow.h> work. (That assumes snow/snow/snow.h exists, like if you clone this repo.)
    • -DSNOW_ENABLED: Enable snow (otherwise describe(...) would be compiled down to nothing).
    • -g: Add debug symbols for valgrind.
  • Run: valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --error-exitcode=1 ./test
#include <stdio.h>
#include <snow/snow.h>

describe(files) {
	it("opens files") {
		FILE *f = fopen("test", "r");
		assertneq(f, NULL);
		defer(fclose(f));
	}

	it("writes to files") {
		FILE *f = fopen("testfile", "w");
		assertneq(f, NULL);
		defer(remove("testfile"));
		defer(fclose(f));

		char str[] = "hello there";
		asserteq(fwrite(str, 1, sizeof(str), f), sizeof(str));
	}

	subdesc(fread) {
		it("reads 10 bytes") {
			FILE *f = fopen("/dev/zero", "r");
			assertneq(f, NULL);
			defer(fclose(f));

			char buf[10];
			asserteq(fread(buf, 1, 10, f), 10);
		}

		it("reads 20 bytes") {
			FILE *f = fopen("/dev/zero", "r");
			assertneq(f, NULL);
			defer(fclose(f));

			char buf[20];
			asserteq(fread(buf, 1, 20, f), 20);
		}
	}
}

snow_main();

Compile options

  • SNOW_ENABLED: Define to enable Snow.
  • SNOW_USE_FNMATCH: Set to 0 to not use fnmatch for test name matching, and instead just compare literal strings. (Useful for systems without fnmatch)
  • SNOW_COLOR_SUCCESS: The escape sequence before printing success.
  • SNOW_COLOR_FAIL: The escape sequence before printing failure.
  • SNOW_COLOR_MAYBE: The escape sequence before printing maybes.
  • SNOW_COLOR_DESC: The escape sequence before printing the test description.
  • SNOW_COLOR_BOLD: The escape sequence for bold text.
  • SNOW_COLOR_RESET: The escape sequence to reset formatting.
  • SNOW_DEFAULT_ARGS: A comma seperated list of strings to pass as arguments to snow before the command-line arguments.

Structure Macros

describe(testname) <block>

A top-level description of a component, which can contain subdescs and its. A describe(testname, block) will define a function void test_##testname(), which the main function created by snow_main will call automatically.

subdesc(testname) <block>

A description of a sub-component, which can contain nested subdescs and its. It's similar to describe, but doesn't define a function.

it(description) <block>

A particular test case. It can contain asserts and defers, as well as just regular code. A failing assert (or direct call to fail(...)) will mark the test as failed, but if it completes normally, it's marked as successful.

test(description) <block> is an alias, for cases where using it would read awkwardly.

defer(expr)

defer is used for tearing down, and is inspired by Go's defer statement.

Once the test case completes, each deferred expression will be executed, in the reverse order of their definitions (i.e defer(printf("World")); defer(printf("Hello ")); will print "Hello World"). If the test case fails, only deferred expressions defined before the point of failure will be executed.

before_each() <block>

Code to run before each test case.

after_each() <block>

Code to run after each test case.

snow_main()

This macro expands to a main function which handless stuff like parsing arguments and freeing memory allocated by Snow. All described functions will automatically be called by the main functions.

If you want more control over the main function, you can use the snow_main_decls macro to create the necessary global variables and functions, and then call the snow_main_function(int argc, char **argv) function.

This is essentially how snow_main() works:

snow_main_decls;
int main(int argc, char **argv) {
	return snow_main_function(argc, argv);
}

Assert Macros

fail(fmt, ...)

Just directly fail the test case. The arguments are a printf-style format, optionally followed by arguments, just like printf.

assert(x [, explanation])

Fail if the expression x returns 0. explanation is an optional string which will be printed if the assertion fails, and can be used to provide some context.

asserteq(a, b [, explanation])

Fail unless a equals b. If b is a string, strcmp will be used to check for equality; otherwise, == will be used.

asserteq requires C11. If you can't use C11, or want to explicitly state what type your arguments are (say you want to compare strings by pointer instead of by content), you can use the asserteq_int, asserteq_ptr, asserteq_dbl, and asserteq_str macros instead of asserteq.

assertneq(a, b [, explanation])

Fail if a equals b. If b is a string, strcmp will be used to check for equality; otherwise, == will be used.

assertneq requires C11. If you can't use C11, or want to explicitly state what type your arguments are (say you want to compare strings by pointer instead of by content), you can use the assertneq_int, assertneq_ptr, assertneq_dbl, and asserteq_str macros instead of assertneq.

asserteq_buf(a, b, n [, explanation])

Fail unless the first n bytes of a and b are the same.

assertneq_buf(a, b, n [, explanation])

Fail if the first n bytes of a and b are the same.

snow_fail(fmt, ...), snow_fail_update()

snow_fail_update saves the current file/line, while snow_fail fails the currently executing test case and prints the saved file/line from the last snow_fail_update. This allows for implementing new checks to fail tests. All assertion functions from Snow are implemented using snow_fail and snow_fail_update.

How to test

Exactly how to test your code might not be as obvious with C as it is for other languages. I haven't yet used Snow for any big projects, but here's how I would do it

Testing a library's interface

When testing a library's interface, you can just create a test folder which is completely decoupled from the library's source code, and just compile your test code with a flag to link against your library.

Testing a program or library internals

Testing anything that's not exposed as a library's public API is is possible because unless SNOW_ENABLED is defined, describe will just be an empty macro, and all uses of it will be removed by the preprocessor. Therfore, you can include tests directly in your C source files, and regular builds won't use snow, while builds with -DSNOW_ENABLED will be your test suite.

Since all test macros are compiled down to nothing, this will have no runtime performance or binary size impact.

Note that since snow.h defines macros with pretty general names (it, describe, assert), it's probably a good idea to put your tests at the bottom of the source and only include snow.h right above the test code, to avoid name conflicts.

The exampleproject directory is an example of a program tested this way.

More Repositories

1

jcof

An efficient drop-in replacement for JSON.
JavaScript
153
star
2

dedaemon

Desktop Environment-like functionality in a daemon.
JavaScript
64
star
3

housecat

A static site generator, written in C.
C
63
star
4

easy-makefile

An easily configurable Makefile.
Makefile
44
star
5

strliteral

Embed files into C/C++ projects.
C
44
star
6

mauncher

Launcher for Wayland.
C
41
star
7

mortup-js

Markdown-inspired markup language.
JavaScript
33
star
8

mouseless-plugin

For a mouseless future.
JavaScript
32
star
9

CPU-16

Logisim CPU.
JavaScript
31
star
10

rv32i-logisim-cpu

Implementation of RV32I in Logisim-evolution.
Makefile
19
star
11

greylang

The Grey programming language
C
18
star
12

langbot

Run code from many programming languages in Discord!
Rust
17
star
13

tlsproxy

A web/proxy server with automatic https.
JavaScript
16
star
14

squirrelWords-plugin

JavaScript
13
star
15

beanbar

Status bar using web technologies.
C
12
star
16

hconfig

HConfig, better javascript config files
JavaScript
11
star
17

pixpeep

Load raw image data and see what it means when interprets as various pixel formats.
JavaScript
11
star
18

gilia

The Gilia programming language
C
10
star
19

smake

Stupid simple Makefile generator tool.
Shell
10
star
20

json5cpp

A JSON5 parser for C++ built on JsonCpp.
C++
10
star
21

mmenu

A dmenu wrapper which works like dmenu_run, but evaluates math you give it too.
Shell
9
star
22

makeschem

Generate a Minecraft .schematic file based on an easy-to-write text file.
C
8
star
23

osyris

A lisp
Rust
8
star
24

mmpc-media-streamer

For streaming media on a media PC.
JavaScript
7
star
25

mmpc

Mort's Media PC.
JavaScript
7
star
26

bnf-railroad-generator

Generate railroad diagrams from a BNF-like language.
JavaScript
6
star
27

sercom

Nice and simple serial console.
Python
6
star
28

lafun-language

LaFuN programming language
C++
6
star
29

xxsh

Extra small, self-contained shell.
C
5
star
30

dots

Dotfiles 2.0
Vim Script
5
star
31

JC3MM

Mod Manager for Just Cause 3.
C#
5
star
32

shoelips

Stack based postfix programming language.
JavaScript
5
star
33

lograt

Log explorer
C++
4
star
34

scar

Seekable compressed tar
C
4
star
35

msoak

Soak up compiler output and display it nicely.
C
4
star
36

jsSiteBuilder

Thing to build and maintain static HTML files. Written in node.js and PHP.
PHP
3
star
37

nouwell

Content management system.
JavaScript
3
star
38

binpretty

A tool to view mixed binary/textual data.
C
3
star
39

axa-cpu

Supporting software for the Axa CPU architecture.
Rust
2
star
40

thelounge-theme-mortified

A dark grey theme for The Lounge.
CSS
2
star
41

phonecam

Use phone as web cam.
C++
2
star
42

ladbot

A modular IRC bot framework written in javascript for node.js.
JavaScript
2
star
43

dev-refresh

A utility for watching for changes in directories while doing web development.
JavaScript
2
star
44

steambly

Mod.
Java
1
star
45

mmpc2

JavaScript
1
star
46

advent-of-code-2019

Advent of code 2019
Python
1
star
47

timeBall

JavaScript
1
star
48

gamejam-client

JavaScript
1
star
49

blogsoftware

Making blog software!
PHP
1
star
50

pbin.in

Pastebin clone.
PHP
1
star
51

gamejam-server

JavaScript
1
star
52

pree

Process tree implementation. Shows memory usage, uses fancy unicode, written in Go.
Go
1
star
53

mort-st

Fork of suckless' st with my own config and patches applied.
C
1
star
54

mmpc-remote-desktop

For remote desktop for a media PC.
JavaScript
1
star
55

threadviz

ThreadViz: Visualize your program's threads
HTML
1
star
56

project-swan

Game.
C++
1
star
57

starstruck

Prompt printer
Rust
1
star
58

hims-frontend

Frontend for OpenMRS-2.3
JavaScript
1
star
59

octornado-cpu

8-bit CPU
Python
1
star
60

socksugar

Websockets with sugar on top.
JavaScript
1
star
61

tequilaJumper

game I made for Ludum Dare 26 Jam.
1
star
62

circuitgame

A game with digital circuits.
JavaScript
1
star
63

llvm

Fork of clang.
C++
1
star
64

waitland

Wait for a wayland compositor connection to die.
C
1
star
65

llvm-axa-backend

LLVM with a back-end for the Axa ISA: https://docs.google.com/spreadsheets/d/1LIplJOF0Cd7MD3LWmlF01pfKx2tIuGzZWag7-Bvwp6I
1
star