summaryrefslogtreecommitdiff
path: root/src/common/libtap
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/libtap')
-rw-r--r--src/common/libtap/README231
-rw-r--r--src/common/libtap/tap.c313
-rw-r--r--src/common/libtap/tap.h101
-rw-r--r--src/common/libtap/tap_unit.h94
4 files changed, 739 insertions, 0 deletions
diff --git a/src/common/libtap/README b/src/common/libtap/README
new file mode 100644
index 0000000..d57b81d
--- /dev/null
+++ b/src/common/libtap/README
@@ -0,0 +1,231 @@
+NAME
+====
+
+libtap - Write tests in C
+
+SYNOPSIS
+========
+
+ #include <tap.h>
+
+ int foo () {return 3;}
+ char *bar () {return "fnord";}
+
+ int main () {
+ plan(5);
+ ok(foo() == 3);
+ is(bar(), "eek");
+ ok(foo() <= 8732, "foo <= %d", 8732);
+ like(bar(), "f(yes|no)r*[a-f]$", "is like");
+ cmp_ok(foo(), ">=", 10, "foo is greater than ten");
+ return exit_status();
+ }
+
+results in:
+
+ 1..5
+ ok 1
+ not ok 2
+ # Failed test at synopsis.c line 9.
+ # got: 'fnord'
+ # expected: 'eek'
+ ok 3 - foo <= 8732
+ ok 4 - is like
+ not ok 5 - foo is greater than ten
+ # Failed test 'foo is greater than ten'
+ # at synopsis.c line 12.
+ # 3
+ # >=
+ # 10
+ # Looks like you failed 2 tests of 5 run.
+
+DESCRIPTION
+===========
+
+tap is an easy to read and easy to write way of creating tests for your
+software. This library creates functions that can be used to generate it for
+your C programs. It is mostly based on the Test::More Perl module.
+
+FUNCTIONS
+=========
+
+- plan(tests)
+- plan(NO_PLAN)
+
+ Use this to start a series of tests. When you know how many tests there
+ will be, you can put a number as a number of tests you expect to run. If
+ you do not know how many tests there will be, you can use plan(NO_PLAN)
+ or not call this function. When you pass it a number of tests to run, a
+ message similar to the following will appear in the output:
+
+ 1..5
+
+- ok(test)
+- ok(test, fmt, ...)
+
+ Specify a test. the test can be any statement returning a true or false
+ value. You may optionally pass a format string describing the test.
+
+ ok(r = reader_new("Of Mice and Men"), "create a new reader");
+ ok(reader_go_to_page(r, 55), "can turn the page");
+ ok(r->page == 55, "page turned to the right one");
+
+ Should print out:
+
+ ok 1 - create a new reader
+ ok 2 - can turn the page
+ ok 3 - page turned to the right one
+
+ On failure, a diagnostic message will be printed out.
+
+ not ok 3 - page turned to the right one
+ # Failed test 'page turned to the right one'
+ # at reader.c line 13.
+
+- is(got, expected)
+- is(got, expected, fmt, ...)
+- isnt(got, expected)
+- isnt(got, expected, fmt, ...)
+
+ Tests that the string you got is what you expected. with isnt, it is the
+ reverse.
+
+ is("this", "that", "this is that");
+
+ prints:
+
+ not ok 1 - this is that
+ # Failed test 'this is that'
+ # at is.c line 6.
+ # got: 'this'
+ # expected: 'that'
+
+- cmp_ok(a, op, b)
+- cmp_ok(a, op, b, fmt, ...)
+
+ Compares two ints with any binary operator that doesn't require an lvalue.
+ This is nice to use since it provides a better error message than an
+ equivalent ok.
+
+ cmp_ok(420, ">", 666);
+
+ prints:
+
+ not ok 1
+ # Failed test at cmpok.c line 5.
+ # 420
+ # >
+ # 666
+
+- like(got, expected)
+- like(got, expected, fmt, ...)
+- unlike(got, expected)
+- unlike(got, expected, fmt, ...)
+
+ Tests that the string you got matches the expected extended POSIX regex.
+ unlike is the reverse. These macros are the equivalent of a skip on
+ Windows.
+
+ like("stranger", "^s.(r).*\\1$", "matches the regex");
+
+ prints:
+
+ ok 1 - matches the regex
+
+- pass()
+- pass(fmt, ...)
+- fail()
+- fail(fmt, ...)
+
+ Speciy that a test succeeded or failed. Use these when the statement is
+ longer than you can fit into the argument given to an ok() test.
+
+- dies_ok(code)
+- dies_ok(code, fmt, ...)
+- lives_ok(code)
+- lives_ok(code, fmt, ...)
+
+ Tests whether the given code causes your program to exit. The code gets
+ passed to a macro that will test it in a forked process. If the code
+ succeeds it will be executed in the parent process. You can test things
+ like passing a function a null pointer and make sure it doesnt
+ dereference it and crash.
+
+ dies_ok({abort();}, "abort does close your program");
+ dies_ok({int x = 0/0;}, "divide by zero crash");
+ lives ok({pow(3.0, 5.0)}, "nothing wrong with taking 3**5");
+
+ On Windows, these macros are the equivalent of a skip.
+
+- exit_status()
+
+ Summarizes the tests that occurred. If there was no plan, it will print
+ out the number of tests as.
+
+ 1..5
+
+ It will also print a diagnostic message about how many
+ failures there were.
+
+ # Looks like you failed 2 tests of 3 run.
+
+ If all planned tests were successful, it will return 0. If any test fails,
+ it will return the number of failed tests (including ones that were
+ missing). If they all passed, but there were missing tests, it will return
+ 255.
+
+- note(fmt, ...)
+- diag(fmt, ...)
+
+ print out a message to the tap output. note prints to stdout and diag
+ prints to stderr. Each line is preceeded by a "# " so that you know its a
+ diagnostic message.
+
+ note("This is\na note\nto describe\nsomething.");
+
+ prints:
+
+ # This is
+ # a note
+ # to describe
+ # something
+
+ ok() and these functions return ints so you can use them like:
+
+ ok(1) && note("yo!");
+ ok(0) || diag("I have no idea what just happened");
+
+- skip(test, n)
+- skip(test, n, fmt, ...)
+- endskip
+
+ Skip a series of n tests if test is true. You may give a reason why you are
+ skipping them or not. The (possibly) skipped tests must occur between the
+ skip and endskip macros.
+
+ skip(TRUE, 2);
+ ok(1);
+ ok(0);
+ endskip;
+
+ prints:
+
+ ok 1 # skip
+ ok 2 # skip
+
+- todo()
+- todo(fmt, ...)
+- endtodo
+
+ Specifies a series of tests that you expect to fail because they are not
+ yet implemented.
+
+ todo()
+ ok(0);
+ endtodo;
+
+ prints:
+
+ not ok 1 # TODO
+ # Failed (TODO) test at todo.c line 7
+
diff --git a/src/common/libtap/tap.c b/src/common/libtap/tap.c
new file mode 100644
index 0000000..61e0528
--- /dev/null
+++ b/src/common/libtap/tap.c
@@ -0,0 +1,313 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+//#include "common.h"
+#include "tap.h"
+
+static int expected_tests = NO_PLAN;
+static int failed_tests;
+static int current_test;
+static char *todo_mesg;
+
+void
+plan (int tests) {
+ expected_tests = tests;
+ if (tests != NO_PLAN)
+ printf("1..%d\n", tests);
+}
+
+static char *
+vstrdupf (const char *fmt, va_list args) {
+ char *str;
+ int size;
+ va_list args2;
+ va_copy(args2, args);
+ if (!fmt)
+ fmt = "";
+ size = vsnprintf(NULL, 0, fmt, args2) + 2;
+ str = malloc(size);
+ vsprintf(str, fmt, args);
+ va_end(args2);
+ return str;
+}
+
+int
+vok_at_loc (const char *file, int line, int test, const char *fmt,
+ va_list args)
+{
+ char *name = vstrdupf(fmt, args);
+ printf("%sok %d", test ? "" : "not ", ++current_test);
+ if (*name)
+ printf(" - %s", name);
+ if (todo_mesg) {
+ printf(" # TODO");
+ if (*todo_mesg)
+ printf(" %s", todo_mesg);
+ }
+ printf("\n");
+ if (!test) {
+ if (*name)
+ diag(" Failed%s test '%s'\n at %s line %d.",
+ todo_mesg ? " (TODO)" : "", name, file, line);
+ else
+ diag(" Failed%s test at %s line %d.",
+ todo_mesg ? " (TODO)" : "", file, line);
+ if (!todo_mesg)
+ failed_tests++;
+ }
+ free(name);
+ return test;
+}
+
+int
+ok_at_loc (const char *file, int line, int test, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ return test;
+}
+
+static int
+mystrcmp (const char *a, const char *b) {
+ return a == b ? 0 : !a ? -1 : !b ? 1 : strcmp(a, b);
+}
+
+#define eq(a, b) (!mystrcmp(a, b))
+#define ne(a, b) (mystrcmp(a, b))
+
+int
+is_at_loc (const char *file, int line, const char *got, const char *expected,
+ const char *fmt, ...)
+{
+ int test = eq(got, expected);
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ diag(" got: '%s'", got);
+ diag(" expected: '%s'", expected);
+ }
+ return test;
+}
+
+int
+isnt_at_loc (const char *file, int line, const char *got, const char *expected,
+ const char *fmt, ...)
+{
+ int test = ne(got, expected);
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ diag(" got: '%s'", got);
+ diag(" expected: anything else");
+ }
+ return test;
+}
+
+int
+cmp_ok_at_loc (const char *file, int line, int a, const char *op, int b,
+ const char *fmt, ...)
+{
+ int test = eq(op, "||") ? a || b
+ : eq(op, "&&") ? a && b
+ : eq(op, "|") ? a | b
+ : eq(op, "^") ? a ^ b
+ : eq(op, "&") ? a & b
+ : eq(op, "==") ? a == b
+ : eq(op, "!=") ? a != b
+ : eq(op, "<") ? a < b
+ : eq(op, ">") ? a > b
+ : eq(op, "<=") ? a <= b
+ : eq(op, ">=") ? a >= b
+ : eq(op, "<<") ? a << b
+ : eq(op, ">>") ? a >> b
+ : eq(op, "+") ? a + b
+ : eq(op, "-") ? a - b
+ : eq(op, "*") ? a * b
+ : eq(op, "/") ? a / b
+ : eq(op, "%") ? a % b
+ : diag("unrecognized operator '%s'", op);
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ diag(" %d", a);
+ diag(" %s", op);
+ diag(" %d", b);
+ }
+ return test;
+}
+
+static void
+vdiag_to_fh (FILE *fh, const char *fmt, va_list args) {
+ char *mesg, *line;
+ int i;
+ if (!fmt)
+ return;
+ mesg = vstrdupf(fmt, args);
+ line = mesg;
+ for (i = 0; *line; i++) {
+ char c = mesg[i];
+ if (!c || c == '\n') {
+ mesg[i] = '\0';
+ fprintf(fh, "# %s\n", line);
+ if (!c) break;
+ mesg[i] = c;
+ line = &mesg[i+1];
+ }
+ }
+ free(mesg);
+ return;
+}
+
+int
+diag (const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vdiag_to_fh(stderr, fmt, args);
+ va_end(args);
+ return 0;
+}
+
+int
+note (const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vdiag_to_fh(stdout, fmt, args);
+ va_end(args);
+ return 0;
+}
+
+int
+exit_status () {
+ int retval = 0;
+ if (expected_tests == NO_PLAN) {
+ printf("1..%d\n", current_test);
+ }
+ else if (current_test != expected_tests) {
+ diag("Looks like you planned %d test%s but ran %d.",
+ expected_tests, expected_tests > 1 ? "s" : "", current_test);
+ retval = 255;
+ }
+ if (failed_tests) {
+ diag("Looks like you failed %d test%s of %d run.",
+ failed_tests, failed_tests > 1 ? "s" : "", current_test);
+ if (expected_tests == NO_PLAN)
+ retval = failed_tests;
+ else
+ retval = expected_tests - current_test + failed_tests;
+ }
+ return retval;
+}
+
+void
+skippy (int n, const char *fmt, ...) {
+ char *why;
+ va_list args;
+ va_start(args, fmt);
+ why = vstrdupf(fmt, args);
+ va_end(args);
+ while (n --> 0) {
+ printf("ok %d ", ++current_test);
+ note("skip %s\n", why);
+ }
+ free(why);
+}
+
+void
+ctodo (int ignore, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ todo_mesg = vstrdupf(fmt, args);
+ va_end(args);
+}
+
+void
+cendtodo () {
+ free(todo_mesg);
+ todo_mesg = NULL;
+}
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#include <regex.h>
+
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+/* Create a shared memory int to keep track of whether a piece of code executed
+dies. to be used in the dies_ok and lives_ok macros */
+int
+tap_test_died (int status) {
+ static int *test_died = NULL;
+ int prev;
+ if (!test_died) {
+ test_died = mmap(0, sizeof (int), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ *test_died = 0;
+ }
+ prev = *test_died;
+ *test_died = status;
+ return prev;
+}
+
+int
+like_at_loc (int for_match, const char *file, int line, const char *got,
+ const char *expected, const char *fmt, ...)
+{
+ int test;
+ regex_t re;
+ int err = regcomp(&re, expected, REG_EXTENDED);
+ if (err) {
+ char errbuf[256];
+ regerror(err, &re, errbuf, sizeof errbuf);
+ fprintf(stderr, "Unable to compile regex '%s': %s at %s line %d\n",
+ expected, errbuf, file, line);
+ exit(255);
+ }
+ err = regexec(&re, got, 0, NULL, 0);
+ regfree(&re);
+ test = for_match ? !err : err;
+ va_list args;
+ va_start(args, fmt);
+ vok_at_loc(file, line, test, fmt, args);
+ va_end(args);
+ if (!test) {
+ if (for_match) {
+ diag(" '%s'", got);
+ diag(" doesn't match: '%s'", expected);
+ }
+ else {
+ diag(" '%s'", got);
+ diag(" matches: '%s'", expected);
+ }
+ }
+ return test;
+}
+#endif
+
diff --git a/src/common/libtap/tap.h b/src/common/libtap/tap.h
new file mode 100644
index 0000000..2e89b90
--- /dev/null
+++ b/src/common/libtap/tap.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TAP_H__
+#define __TAP_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define NO_PLAN -1
+#define ok(...) ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define pass(...) ok(1, ## __VA_ARGS__)
+#define fail(...) ok(0, ## __VA_ARGS__)
+#define is(...) is_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define isnt(...) isnt_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define cmp_ok(...) cmp_ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+
+int vok_at_loc (const char *file, int line, int test, const char *fmt,
+ va_list args);
+void plan (int tests);
+int ok_at_loc (const char *file, int line, int test, const char *fmt,
+ ...);
+int diag (const char *fmt, ...);
+int note (const char *fmt, ...);
+int exit_status (void);
+void skippy (int n, const char *fmt, ...);
+void ctodo (int ignore, const char *fmt, ...);
+void cendtodo (void);
+int is_at_loc (const char *file, int line, const char *got,
+ const char *expected, const char *fmt, ...);
+int isnt_at_loc (const char *file, int line, const char *got,
+ const char *expected, const char *fmt, ...);
+int cmp_ok_at_loc (const char *file, int line, int a, const char *op,
+ int b, const char *fmt, ...);
+
+#ifdef _WIN32
+#define like(...) skippy(1, "like is not implemented on MSWin32")
+#define unlike(...) like()
+#else
+#define like(...) like_at_loc(1, __FILE__, __LINE__, __VA_ARGS__, NULL)
+#define unlike(...) like_at_loc(0, __FILE__, __LINE__, __VA_ARGS__, NULL)
+int like_at_loc (int for_match, const char *file, int line,
+ const char *got, const char *expected,
+ const char *fmt, ...);
+#endif
+
+#define skip(test, ...) do {if (test) {skippy(__VA_ARGS__, NULL); break;}
+#define endskip } while (0)
+
+#define todo(...) ctodo(0, ## __VA_ARGS__, NULL)
+#define endtodo cendtodo()
+
+#define dies_ok(code, ...) dies_ok_common(code, 1, ## __VA_ARGS__)
+#define lives_ok(code, ...) dies_ok_common(code, 0, ## __VA_ARGS__)
+
+#ifdef _WIN32
+#define dies_ok_common(...) \
+ skippy(1, "Death detection is not supported on MSWin32")
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+int tap_test_died (int status);
+#define dies_ok_common(code, for_death, ...) \
+ do { \
+ tap_test_died(1); \
+ int cpid = fork(); \
+ switch (cpid) { \
+ case -1: \
+ perror("fork error"); \
+ exit(EXIT_FAILURE); \
+ case 0: /* child */ \
+ close(1); close(2); \
+ code \
+ tap_test_died(0); \
+ exit(EXIT_SUCCESS); \
+ } \
+ if (waitpid(cpid, NULL, 0) < 0) { \
+ perror("waitpid error"); \
+ exit(EXIT_FAILURE); \
+ } \
+ int it_died = tap_test_died(0); \
+ if (!it_died) {code} \
+ ok(for_death ? it_died : !it_died, ## __VA_ARGS__); \
+ } while (0)
+#endif
+#endif
diff --git a/src/common/libtap/tap_unit.h b/src/common/libtap/tap_unit.h
new file mode 100644
index 0000000..c248fde
--- /dev/null
+++ b/src/common/libtap/tap_unit.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/*!
+ * \file tap_unit.h
+ * \author Marek Vavrusa <marek.vavusa@nic.cz>
+ *
+ * \brief libtap test unit.
+ *
+ * Contains description of a single test unit API.
+ *
+ * Export unit_api in each module header file,
+ * and set function pointer to according test routines.
+ *
+ * <b>Example code for myunit.h</b>
+ * \code
+ * #ifndef MYUNIT_TEST_H
+ * #define MYUNIT_TEST_H
+ *
+ * // Export unittest symbol
+ * unit_api mymodule;
+ *
+ * #endif // MYUNIT_TEST_H
+ * \endcode
+ *
+ * <b>Example code for myunit.c</b>
+ * \code
+ * #include "myunit.h"
+ *
+ * // Function to return unit test count
+ * int myunit_count(int argc, char *argv[]) {
+ * return 1; // Number of tests in this unit
+ * }
+ *
+ * // Function to perform tests
+ * int myunit_run(int argc, char *argv[]) {
+ * // 1. test
+ * ok(1 == 1, "test OK");
+ * return 0;
+ * }
+ *
+ * // Declare module API
+ * unit_api mymodule = {
+ * "My module",
+ * &myunit_count,
+ * &myunit_run
+ * };
+ * \endcode
+ *
+ * To incorporate test, add it to unit tests main().
+ *
+ * See https://github.com/zorgnax/libtap for libtap API reference.
+ *
+ * \addtogroup tests
+ * @{
+ */
+
+#ifndef _TAP_UNIT_H_
+#define _TAP_UNIT_H_
+
+#include "common/libtap/tap.h"
+
+/*! \brief Pointer to function for unit_api. */
+typedef int(unitapi_f)(int, char*[]);
+
+
+/*!
+ * \brief Basic Unit APIs.
+ *
+ * Each unit should have one global variable with
+ * an initialized instance of unit_api.
+ */
+typedef struct {
+ const char *name; /*!< Test unit name. */
+ unitapi_f *count; /*!< Function to calculate number of tests. */
+ unitapi_f *run; /*!< Function to run unit tests. */
+} unit_api;
+
+#endif // _TAP_UNIT_H_
+
+/*! @} */
+