From bf71f069d4861ba2285c96dd98bdf1bbe0322fc2 Mon Sep 17 00:00:00 2001 From: sanine Date: Fri, 30 Dec 2022 21:22:50 -0600 Subject: begin documentation and add lily_begin() and lily_finish() --- doc/01-introduction.html | 24 +++++++++ doc/02-writing-tests.html | 119 +++++++++++++++++++++++++++++++++++++++++++++ doc/03-assertions.html | 89 +++++++++++++++++++++++++++++++++ doc/style.css | 48 ++++++++++++++++++ doc/table-of-contents.html | 17 +++++++ doc/template.html | 22 +++++++++ example/sum.c | 2 + lily-test.h | 21 ++++++++ 8 files changed, 342 insertions(+) create mode 100644 doc/01-introduction.html create mode 100644 doc/02-writing-tests.html create mode 100644 doc/03-assertions.html create mode 100644 doc/style.css create mode 100644 doc/table-of-contents.html create mode 100644 doc/template.html diff --git a/doc/01-introduction.html b/doc/01-introduction.html new file mode 100644 index 0000000..156994b --- /dev/null +++ b/doc/01-introduction.html @@ -0,0 +1,24 @@ + + + + + + + Introduction | lily-test + + +
+ +
+ +

Introduction

+ +

lily-test is a single-header midsize unit testing framework written in C89. + +

+
+ + + diff --git a/doc/02-writing-tests.html b/doc/02-writing-tests.html new file mode 100644 index 0000000..152ccac --- /dev/null +++ b/doc/02-writing-tests.html @@ -0,0 +1,119 @@ + + + + + + + Writing Tests | lily-test + + +
+ +
+ +

Writing Tests

+ +

A basic unit test file looks like this:

+ +

+#include "lily-test.h"
+
+LILY_FILE_BEGIN(example_file)
+
+
+LILY_TEST("example test 1")
+{
+	CHECK_EQ(1+1, 2, "%d");
+}
+#include LILY_PUSH_TEST()
+
+
+LILY_TEST("example test 2")
+{
+	CHECK_LT(3.0, 4.0, "%0.2f");
+}
+#include LILY_PUSH_TEST()
+
+
+#define LILY_FILE_END
+#include LILY_REGISTER_TESTS()
+	
+ +

This will define a function pointer example_file with signature void () that, when called, will execute the two tests and display to stderr any errors that they generate. While there is more boilerplate than in comparable C++ libraries, this means that lily-test supports test auto-registration. To add a new test, just use the LILY_TEST macro as shown, and then follow the function body with #include LILY_PUSH_TEST() to register it. These macros will only work correctly when used between a LILY_FILE_BEGIN and LILY_FILE_END block, and only when used together; using LILY_TEST without a LILY_PUSH_TEST is probably going to throw up a ton of compile errors.

+ +

Using a single file

+ +

For very small projects, you may be able to get away with using only a single unit test file. In this case, you should set it up along the following lines:

+ +

+#define LILY_IMPLEMENTATION
+#include "lily-test.h"
+
+/* any includes or code to get your tests working */
+
+LILY_FILE_BEGIN(tests)
+
+/* test definitions... */
+
+#define LILY_FILE_END
+#include LILY_REGISTER_TESTS()
+
+int main()
+{
+	lily_begin();
+	tests();
+	lily_finish();
+	return 0;
+}
+	
+ + However, in most cases you will want to have multiple test files. + + +

Multiple test files

+ +

You will need, in addition to your unit test files, a header file that contains extern declarations of the function pointers defined by your files and a main C source file that contains a main function to run all of the function pointers. A nice way to set this up is to use X-macros, like this:

+ +

+/* tests.h */
+
+#define TESTS \
+	X(suite_1) \
+	X(suite_2) \
+	X(suite_3) \
+
+
+#define X(suite) extern void (*suite)();
+TESTS
+#undef X
+	
+ +

+/* tests_main.c */
+
+#define LILY_IMPLEMENTATION
+#include "lily-test.h"
+
+int main()
+{
+	lily_begin();
+	#define X(suite) suite();
+	TESTS
+	#undef X
+	lily_finish();
+	return 0;
+}
+	
+ +

This is convenient, because it means that when you add a new test file you need only add a single line to the definition of the TESTS macro in tests.h and all of the other relevant code is added for you automatically.

+ +

Note that exactly ONE file should define LILY_IMPLEMENTATION. I find it reasonable to make this the same file with the implementation of main but YMMV.

+ +
+
+ + + diff --git a/doc/03-assertions.html b/doc/03-assertions.html new file mode 100644 index 0000000..92126a8 --- /dev/null +++ b/doc/03-assertions.html @@ -0,0 +1,89 @@ + + + + + + + Assertions | lily-test + + +
+ +
+ + +

Assertions

+ +

There are two different basic types of assertions: checks and requirements. Both checks and requires will mark the overall test as failed if they are false, but a require will also immediately stop executing the test in addition.

+ +

Check Macros

+ +

CHECK(x)

+ +

Marks the test as failed if x is false and prints stringified x.

+ +

CHECK_EQ(x, y, fmt)

+ +

Marks the test as failed if x == y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. + +

CHECK_NEQ(x, y, fmt)

+ +

Marks the test as failed if x != y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. + +

CHECK_LT(x, y, fmt)

+ +

Marks the test as failed if x < y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. + +

CHECK_LE(x, y, fmt)

+ +

Marks the test as failed if x <= y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. + +

CHECK_GT(x, y, fmt)

+ +

Marks the test as failed if x > y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. + +

CHECK_GE(x, y, fmt)

+ +

Marks the test as failed if x >= y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. + +
+ +

Require Macros

+ +

REQUIRE(x)

+ +

Marks the test as failed and immediately ends test execution if x is false and prints stringified x.

+ +

REQUIRE_EQ(x, y, fmt)

+ +

Marks the test as failed and immediately ends test execution if x == y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. + +

REQUIRE_NEQ(x, y, fmt)

+ +

Marks the test as failed and immediately ends test execution if x != y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. + +

REQUIRE_LT(x, y, fmt)

+ +

Marks the test as failed and immediately ends test execution if x < y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. + +

REQUIRE_LE(x, y, fmt)

+ +

Marks the test as failed and immediately ends test execution if x <= y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. + +

REQUIRE_GT(x, y, fmt)

+ +

Marks the test as failed and immediately ends test execution if x > y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. + +

REQUIRE_GE(x, y, fmt)

+ +

Marks the test as failed and immediately ends test execution if x >= y is false. Prints both the stringified names of x and y as well as their values, using the printf format code provided in fmt. + + +

+
+ + + diff --git a/doc/style.css b/doc/style.css new file mode 100644 index 0000000..d6ff59a --- /dev/null +++ b/doc/style.css @@ -0,0 +1,48 @@ +body { + color: #f6f5f4; + background: #181a1b; +} + +#flex-root { + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 100%; +} + +a { + color: #f57c85; +} + +a:visited { + color: #fa6571; +} + +#toc { + border-style: none solid none none; + border-width: 1px; + border-color: #f57c85; + flex: 1 15 0; + max-width: 15em; + min-height: 15em; +} + +#content { + margin: 0 1em; + flex: 1 1 0; +} + +#footer { + border-style: solid none none none; + border-width: 1px; + border-color: #f57c85; + padding: 1em; +} + +#footer #prev { + float: left; +} + +#footer #next { + float: right; +} diff --git a/doc/table-of-contents.html b/doc/table-of-contents.html new file mode 100644 index 0000000..c450da8 --- /dev/null +++ b/doc/table-of-contents.html @@ -0,0 +1,17 @@ + + + + + + + Table of Contents + + +

Contents

+
    +
  1. Introduction
  2. +
  3. Writing Tests
  4. +
  5. Assertions
  6. +
+ + diff --git a/doc/template.html b/doc/template.html new file mode 100644 index 0000000..6d4337b --- /dev/null +++ b/doc/template.html @@ -0,0 +1,22 @@ + + + + + + + TEMPLATE + + +
+ +
+ + +
+
+ + + diff --git a/example/sum.c b/example/sum.c index d9d1221..645ed8e 100644 --- a/example/sum.c +++ b/example/sum.c @@ -24,6 +24,8 @@ LILY_TEST("sums are correct") int main() { + lily_begin(); sum_suite(); + lily_finish(); return 0; } diff --git a/lily-test.h b/lily-test.h index ac956aa..e6925aa 100644 --- a/lily-test.h +++ b/lily-test.h @@ -170,6 +170,8 @@ void lily_require(int x, const char *location, const char *fmt, ...); #define REQUIRE_GE(x, y, fmt) LILY_REQUIRE_CMP(x, >=, y, fmt) +void lily_begin(); +void lily_finish(); void lily_run_test(void (*test)()); @@ -183,6 +185,7 @@ struct lily_global_t { int n_assertions_failed; int n_tests; int n_tests_failed; + double epsilon; } extern lily_g; #ifdef LILY_IMPLEMENTATION @@ -242,6 +245,7 @@ void lily_run_test(void (*test)()) } if (lily_g.failed) { + lily_g.n_tests_failed += 1; printf("================================================================================\n"); printf("test \"%s\" failed!\n", lily_g.test_name); lily_test_msg_t *n = lily_g.HEAD.next; @@ -304,6 +308,23 @@ void lily_require(int x, const char *location, const char *fmt, ...) longjmp(lily_g.env, 1); } } + + +void lily_begin() +{ + printf("================================================================================\n"); + printf("lily-test version %d.%d.%d\n", LILY_VERSION_MAJOR, LILY_VERSION_MINOR, LILY_VERSION_PATCH); +} + + +void lily_finish() +{ + printf("================================================================================\n"); + printf("ran %d tests (%d failed)\n", + lily_g.n_tests, lily_g.n_tests_failed); + printf("checked %d asserts (%d failed)\n", + lily_g.n_assertions, lily_g.n_assertions_failed); +} #endif /* ifdef LILY_TEST_H */ -- cgit v1.2.1