Start using C TAP Harness and convert journal tests to the new system

This commit is contained in:
Ondřej Surý 2013-10-15 14:44:31 +02:00
parent 1413ac86f9
commit 8a2cec3cff
14 changed files with 3134 additions and 2 deletions

View file

@ -1,3 +1,5 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src samples doc man
EXTRA_DIST = resource.sh
include $(srcdir)/tests/Makefile.inc

View file

@ -318,7 +318,6 @@ AC_CONFIG_FILES([Makefile
man/Makefile
src/Makefile
samples/Makefile
src/tests/Makefile
src/zscanner/Makefile
src/zscanner/test/cases/06-3_INCLUDE.in:src/zscanner/test/cases/06-3_INCLUDE.inin
src/zscanner/test/cases/06-4_INCLUDE.in:src/zscanner/test/cases/06-4_INCLUDE.inin

View file

@ -1,5 +1,5 @@
ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4
SUBDIRS = zscanner . tests
SUBDIRS = zscanner .
sbin_PROGRAMS = knotc knotd
bin_PROGRAMS = kdig khost knsupdate knsec3hash

23
tests/Makefile.inc Normal file
View file

@ -0,0 +1,23 @@
check_PROGRAMS = tests/runtests tests/journal
check_LIBRARIES = tests/tap/libtap.a
AM_CPPFLAGS = -I$(top_srcdir)/src \
-DSYSCONFDIR='"$(sysconfdir)"' -DSBINDIR='"$(sbindir)"'
tests_runtests_CPPFLAGS = \
-DSOURCE='"$(abs_top_srcdir)/tests"' \
-DBUILD='"$(abs_top_builddir)/tests"'
tests_tap_libtap_a_CPPFLAGS = -I$(abs_top_srcdir)/t
tests_tap_libtap_a_SOURCES = \
tests/tap/basic.c tests/tap/basic.h \
tests/tap/float.c tests/tap/float.h \
tests/tap/macros.h
check-local: $(check_PROGRAMS)
cd tests && ./runtests -l $(abs_top_srcdir)/tests/TESTS
tests_journal_LDADD = \
tests/tap/libtap.a \
src/libknotd.la src/libknots.la \
@LIBOBJS@

248
tests/README Normal file
View file

@ -0,0 +1,248 @@
Writing TAP Tests
Introduction
This is a guide for users of the C TAP Harness package or similar
TAP-based test harnesses explaining how to write tests. If your
package uses C TAP Harness as the test suite driver, you may want to
copy this document to an appropriate file name in your test suite as
documentation for contributors.
About TAP
TAP is the Test Anything Protocol, a protocol for communication
between test cases and a test harness. This is the protocol used by
Perl for its internal test suite and for nearly all Perl modules,
since it's the format used by the build tools for Perl modules to run
tests and report their results.
A TAP-based test suite works with a somewhat different set of
assumptions than an xUnit test suite. In TAP, each test case is a
separate program. That program, when run, must produce output in the
following format:
1..4
ok 1 - the first test
ok 2
# a diagnostic, ignored by the harness
not ok 3 - a failing test
ok 4 # skip a skipped test
The output should all go to standard output. The first line specifies
the number of tests to be run, and then each test produces output that
looks like either "ok <n>" or "not ok <n>" depending on whether the
test succeeded or failed. Additional information about the test can
be provided after the "ok <n>" or "not ok <n>", but is optional.
Additional diagnostics and information can be provided in lines
beginning with a "#".
Processing directives are supported after the "ok <n>" or "not ok <n>"
and start with a "#". The main one of interest is "# skip" which says
that the test was skipped rather than successful and optionally gives
the reason. Also supported is "# todo", which normally annotates a
failing test and indicates that test is expected to fail, optionally
providing a reason for why.
There are three more special cases. First, the initial line stating
the number of tests to run, called the plan, may appear at the end of
the output instead of the beginning. This can be useful if the number
of tests to run is not known in advance. Second, a plan in the form:
1..0 # skip entire test case skipped
can be given instead, which indicates that this entire test case has
been skipped (generally because it depends on facilities or optional
configuration which is not present). Finally, if the test case
encounters a fatal error, it should print the text:
Bail out!
on standard output, optionally followed by an error message, and then
exit. This tells the harness that the test aborted unexpectedly.
The exit status of a successful test case should always be 0. The
harness will report the test as "dubious" if all the tests appeared to
succeed but it exited with a non-zero status.
Writing TAP Tests
Environment
One of the special features of C TAP Harness is the environment that
it sets up for your test cases. If your test program is called under
the runtests driver, the environment variables SOURCE and BUILD will
be set to the top of the test directory in the source tree and the top
of the build tree, respectively. You can use those environment
variables to locate additional test data, programs and libraries built
as part of your software build, and other supporting information
needed by tests.
The C and shell TAP libraries support a test_file_path() function,
which looks for a file under the build tree and then under the source
tree, using the BUILD and SOURCE environment variables, and return the
full path to the file. This can be used to locate supporting data
files.
Perl
Since TAP is the native test framework for Perl, writing TAP tests in
Perl is very easy and extremely well-supported. If you've never
written tests in Perl before, start by reading the documentation for
Test::Tutorial and Test::Simple, which walks you through the basics,
including the TAP output syntax. Then, the best Perl module to use
for serious testing is Test::More, which provides a lot of additional
functions over Test::Simple including support for skipping tests,
bailing out, and not planning tests in advance. See the documentation
of Test::More for all the details and lots of examples.
C TAP Harness can run Perl test scripts directly and interpret the
results correctly, and similarly the Perl Test::Harness module and
prove command can run TAP tests written in other languages using, for
example, the TAP library that comes with C TAP Harness. You can, if
you wish, use the library that comes with C TAP Harness but use prove
instead of runtests for running the test suite.
C
C TAP Harness provides a basic TAP library that takes away most of the
pain of writing TAP test cases in C. A C test case should start with
a call to plan(), passing in the number of tests to run. Then, each
test should use is_int(), is_string(), is_double(), or is_hex() as
appropriate to compare expected and seen values, or ok() to do a
simpler boolean test. The is_*() functions take expected and seen
values and then a printf-style format string explaining the test
(which may be NULL). ok() takes a boolean and then the printf-style
string.
Here's a complete example test program that uses the C TAP library:
#include <stddef.h>
#include <tap/basic.h>
int
main(void)
{
plan(4);
ok(1, "the first test");
is_int(42, 42, NULL);
diag("a diagnostic, ignored by the harness");
ok(0, "a failing test");
skip("a skipped test");
return 0;
}
This test program produces the output shown above in the section on
TAP and demonstrates most of the functions. The other functions of
interest are sysdiag() (like diag() but adds strerror() results),
bail() and sysbail() for fatal errors, skip_block() to skip a whole
block of tests, and skip_all() which is called instead of plan() to
skip an entire test case.
The C TAP library also provides plan_lazy(), which can be called
instead of plan(). If plan_lazy() is called, the library will keep
track of how many test results are reported and will print out the
plan at the end of execution of the program. This should normally be
avoided since the test may appear to be successful even if it exits
prematurely, but it can make writing tests easier in some
circumstances.
Complete API documentation for the basic C TAP library that comes with
C TAP Harness is available at:
<http://www.eyrie.org/~eagle/software/c-tap-harness/>
It's common to need additional test functions and utility functions
for your C tests, particularly if you have to set up and tear down a
test environment for your test programs, and it's useful to have them
all in the libtap library so that you only have to link your test
programs with one library. Rather than editing tap/basic.c and
tap/basic.h to add those additional functions, add additional *.c and
*.h files into the tap directory with the function implementations and
prototypes, and then add those additional objects to the library.
That way, you can update tap/basic.c and tap/basic.h from subsequent
releases of C TAP Harness without having to merge changes with your
own code.
Libraries of additional useful TAP test functions are available in
rra-c-util at:
<http://www.eyrie.org/~eagle/software/rra-c-util/>
Some of the code there is particularly useful when testing programs
that require Kerberos keys.
If you implement new test functions that compare an expected and seen
value, it's best to name them is_<something> and take the expected
value, the seen value, and then a printf-style format string and
possible arguments to match the calling convention of the functions
provided by C TAP Harness.
Shell
C TAP Harness provides a library of shell functions to make it easier
to write TAP tests in shell. That library includes much of the same
functionality as the C TAP library, but takes its parameters in a
somewhat different order to make better use of shell features.
The libtap.sh file should be installed in a directory named tap in
your test suite area. It can then be loaded by tests written in shell
using the environment set up by runtests with:
. "$SOURCE"/tap/libtap.sh
Here is a complete test case written in shell which produces the same
output as the TAP sample above:
#!/bin/sh
. "$SOURCE"/tap/libtap.sh
cd "$BUILD"
plan 4
ok 'the first test' true
ok '' [ 42 -eq 42 ]
diag a diagnostic, ignored by the harness
ok '' false
skip 'a skipped test'
The shell framework doesn't provide the is_* functions, so you'll use
the ok function more. It takes a string describing the text and then
treats all of its remaining arguments as a condition, evaluated the
same way as the arguments to the "if" statement. If that condition
evaluates to true, the test passes; otherwise, the test fails.
The plan, plan_lazy, diag, and bail functions work the same as with
the C library. skip takes a string and skips the next test with that
explanation. skip_block takes a count and a string and skips that
many tests with that explanation. skip_all takes an optional reason
and skips the entire test case.
Since it's common for shell programs to want to test the output of
commands, there's an additional function ok_program provided by the
shell test library. It takes the test description string, the
expected exit status, the expected program output, and then treats the
rest of its arguments as the program to run. That program is run with
standard error and standard output combined, and then its exit status
and output are tested against the provided values.
A utility function, strip_colon_error, is provided that runs the
command given as its arguments and strips text following a colon and a
space from the output (unless there is no whitespace on the line
before the colon and the space, normally indicating a prefix of the
program name). This function can be used to wrap commands that are
expected to fail with output that has a system- or locale-specific
error message appended, such as the output of strerror().
License
This file is part of the documentation of C TAP Harness, which can be
found at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
Copyright 2010 Russ Allbery <rra@stanford.edu>
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without any warranty.

1
tests/TESTS Normal file
View file

@ -0,0 +1 @@
journal

273
tests/journal.c Normal file
View file

@ -0,0 +1,273 @@
/* 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 <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <unistd.h>
#include <tests/tap/basic.h>
#include "knot/server/journal.h"
#include "knot/knot.h"
static int journal_tests_count(int argc, char *argv[]);
static int journal_tests_run(int argc, char *argv[]);
/*! \brief Generate random string with given length. */
static int randstr(char* dst, size_t len)
{
for (int i = 0; i < len - 1; ++i) {
dst[i] = '0' + (int) (('Z'-'0') * (rand() / (RAND_MAX + 1.0)));
}
dst[len - 1] = '\0';
return 0;
}
/*! \brief Walk journal of chars into buffer. */
static int _wbi = 0;
static char _walkbuf[7];
static int walkchars_cmp(uint64_t k1, uint64_t k2) {
return k1 - k2;
}
static int walkchars(journal_t *j, journal_node_t *n) {
journal_read(j, n->id, walkchars_cmp, _walkbuf + _wbi);
++_wbi;
return 0;
}
int main(int argc, char *argv[])
{
plan(21);
/* Create tmpdir */
int fsize = 8092;
int jsize = 6;
char *tmpdir = test_tmpdir();
char jfn_buf[4096];
snprintf(jfn_buf, 4096 - 1, "%s/%s", tmpdir, "journal.XXXXXX");
/* Test 1: Create tmpfile. */
int tmp_fd = mkstemp(jfn_buf);
ok(tmp_fd >= 0, "journal: create temporary file");
if (tmp_fd < 0) {
skip_block(20, "journal: create temporary file failed");
}
close(tmp_fd);
/* Test 2: Create journal. */
const char *jfilename = jfn_buf;
int ret = journal_create(jfilename, jsize);
ok(ret == KNOT_EOK, "journal: create journal '%s'", jfilename);
/* Test 3: Open journal. */
journal_t *journal = journal_open(jfilename, fsize, JOURNAL_LAZY, 0);
ok(journal != 0, "journal: open");
/* Retain journal. */
journal_t *j = journal_retain(journal);
/* Test 4: Write entry to log. */
const char *sample = "deadbeef";
ret = journal_write(j, 0x0a, sample, strlen(sample));
ok(ret == KNOT_EOK, "journal: write");
/* Test 5: Read entry from log. */
char tmpbuf[64] = {'\0'};
ret = journal_read(j, 0x0a, 0, tmpbuf);
ok(ret == KNOT_EOK, "journal: read entry");
/* Test 6: Compare read data. */
ret = strncmp(sample, tmpbuf, strlen(sample));
ok(ret == 0, "journal: read data integrity check");
/* Append several characters. */
journal_write(j, 0, "X", 1); /* Dummy */
char word[7] = { 'w', 'o', 'r', 'd', '0', '\0', '\0' };
for (int i = 0; i < strlen(word); ++i) {
journal_write(j, i, word+i, 1);
}
/* Test 7: Compare journal_walk() result. */
_wbi = 0;
journal_walk(j, walkchars);
_walkbuf[_wbi] = '\0';
ret = strcmp(word, _walkbuf);
ok(ret == 0, "journal: read data integrity check 2 '%s'", _walkbuf);
_wbi = 0;
/* Test 8: Change single letter and compare. */
word[5] = 'X';
journal_write(j, 5, word+5, 1); /* append 'X', shifts out 'w' */
journal_walk(j, walkchars);
_walkbuf[_wbi] = '\0';
ret = strcmp(word + 1, _walkbuf);
ok(ret == 0, "journal: read data integrity check 3 '%s'", _walkbuf);
_wbi = 0;
/* Test 9: Attempt to retain and release. */
journal_t *tmp = journal_retain(j);
ok(tmp == j, "journal: tested journal retaining");
journal_release(tmp);
/* Release journal. */
journal_release(j);
/* Close journal. */
journal_close(journal);
/* Recreate journal = NORMAL mode. */
if (remove(jfilename) < 0) {
diag("journal: couldn't remove filename");
}
fsize = 8092;
jsize = 512;
ret = journal_create(jfilename, jsize);
j = journal_open(jfilename, fsize, 0, 0);
/* Test 10: Write random data. */
int chk_key = 0;
char chk_buf[64] = {'\0'};
ret = 0;
const int itcount = jsize * 5 + 5;
for (int i = 0; i < itcount; ++i) {
int key = rand() % 65535;
randstr(tmpbuf, sizeof(tmpbuf));
if (journal_write(j, key, tmpbuf, sizeof(tmpbuf)) != KNOT_EOK) {
ret = -1;
break;
}
/* Store some key on the end. */
if (i == itcount - 2) {
chk_key = key;
memcpy(chk_buf, tmpbuf, sizeof(chk_buf));
}
}
ok(j && ret == 0, "journal: sustained looped writes");
/* Test 11: Check data integrity. */
memset(tmpbuf, 0, sizeof(tmpbuf));
journal_read(j, chk_key, 0, tmpbuf);
ret = strncmp(chk_buf, tmpbuf, sizeof(chk_buf));
ok(j && ret == 0, "journal: read data integrity check");
/* Test 12: Reopen log and re-read value. */
memset(tmpbuf, 0, sizeof(tmpbuf));
journal_close(j);
j = journal_open(jfilename, fsize, 0, 0);
journal_read(j, chk_key, 0, tmpbuf);
ret = strncmp(chk_buf, tmpbuf, sizeof(chk_buf));
ok(j && ret == 0, "journal: read data integrity check after close/open");
/* Test 13: Map journal entry. */
char *mptr = NULL;
memset(chk_buf, 0xde, sizeof(chk_buf));
ret = journal_map(j, 0x12345, &mptr, sizeof(chk_buf));
ok(j && mptr && ret == 0, "journal: mapped journal entry");
if (ret != 0) {
skip_block(2, NULL);
}
/* Test 14: Write to mmaped entry and unmap. */
memcpy(mptr, chk_buf, sizeof(chk_buf));
ret = journal_unmap(j, 0x12345, mptr, 1);
ok(j && mptr && ret == 0, "journal: written to mapped entry and finished");
/* Test 15: Compare mmaped entry. */
memset(tmpbuf, 0, sizeof(tmpbuf));
journal_read(j, 0x12345, NULL, tmpbuf);
ret = strncmp(chk_buf, tmpbuf, sizeof(chk_buf));
ok(j && ret == 0, "journal: mapped entry data integrity check");
/* Test 16: Make a transaction. */
uint64_t tskey = 0x75750000;
ret = journal_trans_begin(j);
ok(j && ret == 0, "journal: TRANS begin");
for (int i = 0; i < 16; ++i) {
memset(tmpbuf, i, sizeof(tmpbuf));
journal_write(j, tskey + i, tmpbuf, sizeof(tmpbuf));
}
/* Test 17: Check if uncommited node exists. */
ret = journal_read(j, tskey + rand() % 16, NULL, chk_buf);
ok(j && ret != 0, "journal: check for uncommited node");
/* Test 18: Commit transaction. */
ret = journal_trans_commit(j);
int read_ret = journal_read(j, tskey + rand() % 16, NULL, chk_buf);
ok(j && ret == 0 && read_ret == 0, "journal: transaction commit");
/* Test 19: Rollback transaction. */
tskey = 0x6B6B0000;
journal_trans_begin(j);
for (int i = 0; i < 16; ++i) {
memset(tmpbuf, i, sizeof(tmpbuf));
journal_write(j, tskey + i, tmpbuf, sizeof(tmpbuf));
}
ret = journal_trans_rollback(j);
read_ret = journal_read(j, tskey + rand() % 16, NULL, chk_buf);
ok(j && ret == 0 && read_ret != 0, "journal: transaction rollback");
/* Test 20: Write random data. */
ret = 0;
for (int i = 0; i < 512; ++i) {
int key = i;
randstr(tmpbuf, sizeof(tmpbuf));
ret = journal_map(j, key, &mptr, sizeof(tmpbuf));
if (ret != KNOT_EOK) {
diag("journal_map failed: %s", knot_strerror(ret));
break;
}
memcpy(mptr, tmpbuf, sizeof(tmpbuf));
if ((ret = journal_unmap(j, key, mptr, 1)) != KNOT_EOK) {
diag("journal_unmap failed: %s", knot_strerror(ret));
break;
}
/* Store some key on the end. */
memset(chk_buf, 0, sizeof(chk_buf));
ret = journal_read(j, key, 0, chk_buf);
if (ret != 0) {
diag("journal_map integrity check failed %s",
knot_strerror(ret));
break;
}
ret = strncmp(chk_buf, tmpbuf, sizeof(chk_buf));
if (ret != 0) {
diag("journal_map integrity check failed");
break;
}
}
ok(j && ret == 0, "journal: sustained mmap r/w");
/* Test 21: Open + create journal. */
journal_close(j);
remove(jfilename);
j = journal_open(jfilename, fsize, 0, 0);
ok(j != NULL, "journal: open+create from scratch");
/* Close journal. */
journal_close(j);
/* Delete journal. */
remove(jfilename);
return 0;
}

1380
tests/runtests.c Normal file

File diff suppressed because it is too large Load diff

629
tests/tap/basic.c Normal file
View file

@ -0,0 +1,629 @@
/*
* Some utility routines for writing tests.
*
* Here are a variety of utility routines for writing tests compatible with
* the TAP protocol. All routines of the form ok() or is*() take a test
* number and some number of appropriate arguments, check to be sure the
* results match the expected output using the arguments, and print out
* something appropriate for that test number. Other utility routines help in
* constructing more complex tests, skipping tests, reporting errors, setting
* up the TAP output format, or finding things in the test environment.
*
* This file is part of C TAP Harness. The current version plus supporting
* documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
*
* Copyright 2009, 2010, 2011, 2012 Russ Allbery <rra@stanford.edu>
* Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012, 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
# include <direct.h>
#else
# include <sys/stat.h>
#endif
#include <sys/types.h>
#include <unistd.h>
#include <tests/tap/basic.h>
/* Windows provides mkdir and rmdir under different names. */
#ifdef _WIN32
# define mkdir(p, m) _mkdir(p)
# define rmdir(p) _rmdir(p)
#endif
/*
* The test count. Always contains the number that will be used for the next
* test status.
*/
unsigned long testnum = 1;
/*
* Status information stored so that we can give a test summary at the end of
* the test case. We store the planned final test and the count of failures.
* We can get the highest test count from testnum.
*
* We also store the PID of the process that called plan() and only summarize
* results when that process exits, so as to not misreport results in forked
* processes.
*
* If _lazy is true, we're doing lazy planning and will print out the plan
* based on the last test number at the end of testing.
*/
static unsigned long _planned = 0;
static unsigned long _failed = 0;
static pid_t _process = 0;
static int _lazy = 0;
/*
* Our exit handler. Called on completion of the test to report a summary of
* results provided we're still in the original process. This also handles
* printing out the plan if we used plan_lazy(), although that's suppressed if
* we never ran a test (due to an early bail, for example).
*/
static void
finish(void)
{
unsigned long highest = testnum - 1;
if (_planned == 0 && !_lazy)
return;
fflush(stderr);
if (_process != 0 && getpid() == _process) {
if (_lazy && highest > 0) {
printf("1..%lu\n", highest);
_planned = highest;
}
if (_planned > highest)
printf("# Looks like you planned %lu test%s but only ran %lu\n",
_planned, (_planned > 1 ? "s" : ""), highest);
else if (_planned < highest)
printf("# Looks like you planned %lu test%s but ran %lu extra\n",
_planned, (_planned > 1 ? "s" : ""), highest - _planned);
else if (_failed > 0)
printf("# Looks like you failed %lu test%s of %lu\n", _failed,
(_failed > 1 ? "s" : ""), _planned);
else if (_planned > 1)
printf("# All %lu tests successful or skipped\n", _planned);
else
printf("# %lu test successful or skipped\n", _planned);
}
}
/*
* Initialize things. Turns on line buffering on stdout and then prints out
* the number of tests in the test suite.
*/
void
plan(unsigned long count)
{
if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0)
fprintf(stderr, "# cannot set stdout to line buffered: %s\n",
strerror(errno));
fflush(stderr);
printf("1..%lu\n", count);
testnum = 1;
_planned = count;
_process = getpid();
atexit(finish);
}
/*
* Initialize things for lazy planning, where we'll automatically print out a
* plan at the end of the program. Turns on line buffering on stdout as well.
*/
void
plan_lazy(void)
{
if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0)
fprintf(stderr, "# cannot set stdout to line buffered: %s\n",
strerror(errno));
testnum = 1;
_process = getpid();
_lazy = 1;
atexit(finish);
}
/*
* Skip the entire test suite and exits. Should be called instead of plan(),
* not after it, since it prints out a special plan line.
*/
void
skip_all(const char *format, ...)
{
fflush(stderr);
printf("1..0 # skip");
if (format != NULL) {
va_list args;
putchar(' ');
va_start(args, format);
vprintf(format, args);
va_end(args);
}
putchar('\n');
exit(0);
}
/*
* Print the test description.
*/
static void
print_desc(const char *format, va_list args)
{
printf(" - ");
vprintf(format, args);
}
/*
* Takes a boolean success value and assumes the test passes if that value
* is true and fails if that value is false.
*/
void
ok(int success, const char *format, ...)
{
fflush(stderr);
printf("%sok %lu", success ? "" : "not ", testnum++);
if (!success)
_failed++;
if (format != NULL) {
va_list args;
va_start(args, format);
print_desc(format, args);
va_end(args);
}
putchar('\n');
}
/*
* Same as ok(), but takes the format arguments as a va_list.
*/
void
okv(int success, const char *format, va_list args)
{
fflush(stderr);
printf("%sok %lu", success ? "" : "not ", testnum++);
if (!success)
_failed++;
if (format != NULL)
print_desc(format, args);
putchar('\n');
}
/*
* Skip a test.
*/
void
skip(const char *reason, ...)
{
fflush(stderr);
printf("ok %lu # skip", testnum++);
if (reason != NULL) {
va_list args;
va_start(args, reason);
putchar(' ');
vprintf(reason, args);
va_end(args);
}
putchar('\n');
}
/*
* Report the same status on the next count tests.
*/
void
ok_block(unsigned long count, int status, const char *format, ...)
{
unsigned long i;
fflush(stderr);
for (i = 0; i < count; i++) {
printf("%sok %lu", status ? "" : "not ", testnum++);
if (!status)
_failed++;
if (format != NULL) {
va_list args;
va_start(args, format);
print_desc(format, args);
va_end(args);
}
putchar('\n');
}
}
/*
* Skip the next count tests.
*/
void
skip_block(unsigned long count, const char *reason, ...)
{
unsigned long i;
fflush(stderr);
for (i = 0; i < count; i++) {
printf("ok %lu # skip", testnum++);
if (reason != NULL) {
va_list args;
va_start(args, reason);
putchar(' ');
vprintf(reason, args);
va_end(args);
}
putchar('\n');
}
}
/*
* Takes an expected integer and a seen integer and assumes the test passes
* if those two numbers match.
*/
void
is_int(long wanted, long seen, const char *format, ...)
{
fflush(stderr);
if (wanted == seen)
printf("ok %lu", testnum++);
else {
printf("# wanted: %ld\n# seen: %ld\n", wanted, seen);
printf("not ok %lu", testnum++);
_failed++;
}
if (format != NULL) {
va_list args;
va_start(args, format);
print_desc(format, args);
va_end(args);
}
putchar('\n');
}
/*
* Takes a string and what the string should be, and assumes the test passes
* if those strings match (using strcmp).
*/
void
is_string(const char *wanted, const char *seen, const char *format, ...)
{
if (wanted == NULL)
wanted = "(null)";
if (seen == NULL)
seen = "(null)";
fflush(stderr);
if (strcmp(wanted, seen) == 0)
printf("ok %lu", testnum++);
else {
printf("# wanted: %s\n# seen: %s\n", wanted, seen);
printf("not ok %lu", testnum++);
_failed++;
}
if (format != NULL) {
va_list args;
va_start(args, format);
print_desc(format, args);
va_end(args);
}
putchar('\n');
}
/*
* Takes an expected unsigned long and a seen unsigned long and assumes the
* test passes if the two numbers match. Otherwise, reports them in hex.
*/
void
is_hex(unsigned long wanted, unsigned long seen, const char *format, ...)
{
fflush(stderr);
if (wanted == seen)
printf("ok %lu", testnum++);
else {
printf("# wanted: %lx\n# seen: %lx\n", (unsigned long) wanted,
(unsigned long) seen);
printf("not ok %lu", testnum++);
_failed++;
}
if (format != NULL) {
va_list args;
va_start(args, format);
print_desc(format, args);
va_end(args);
}
putchar('\n');
}
/*
* Bail out with an error.
*/
void
bail(const char *format, ...)
{
va_list args;
fflush(stderr);
fflush(stdout);
printf("Bail out! ");
va_start(args, format);
vprintf(format, args);
va_end(args);
printf("\n");
exit(255);
}
/*
* Bail out with an error, appending strerror(errno).
*/
void
sysbail(const char *format, ...)
{
va_list args;
int oerrno = errno;
fflush(stderr);
fflush(stdout);
printf("Bail out! ");
va_start(args, format);
vprintf(format, args);
va_end(args);
printf(": %s\n", strerror(oerrno));
exit(255);
}
/*
* Report a diagnostic to stderr.
*/
void
diag(const char *format, ...)
{
va_list args;
fflush(stderr);
fflush(stdout);
printf("# ");
va_start(args, format);
vprintf(format, args);
va_end(args);
printf("\n");
}
/*
* Report a diagnostic to stderr, appending strerror(errno).
*/
void
sysdiag(const char *format, ...)
{
va_list args;
int oerrno = errno;
fflush(stderr);
fflush(stdout);
printf("# ");
va_start(args, format);
vprintf(format, args);
va_end(args);
printf(": %s\n", strerror(oerrno));
}
/*
* Allocate cleared memory, reporting a fatal error with bail on failure.
*/
void *
bcalloc(size_t n, size_t size)
{
void *p;
p = calloc(n, size);
if (p == NULL)
sysbail("failed to calloc %lu", (unsigned long)(n * size));
return p;
}
/*
* Allocate memory, reporting a fatal error with bail on failure.
*/
void *
bmalloc(size_t size)
{
void *p;
p = malloc(size);
if (p == NULL)
sysbail("failed to malloc %lu", (unsigned long) size);
return p;
}
/*
* Reallocate memory, reporting a fatal error with bail on failure.
*/
void *
brealloc(void *p, size_t size)
{
p = realloc(p, size);
if (p == NULL)
sysbail("failed to realloc %lu bytes", (unsigned long) size);
return p;
}
/*
* Copy a string, reporting a fatal error with bail on failure.
*/
char *
bstrdup(const char *s)
{
char *p;
size_t len;
len = strlen(s) + 1;
p = malloc(len);
if (p == NULL)
sysbail("failed to strdup %lu bytes", (unsigned long) len);
memcpy(p, s, len);
return p;
}
/*
* Copy up to n characters of a string, reporting a fatal error with bail on
* failure. Don't use the system strndup function, since it may not exist and
* the TAP library doesn't assume any portability support.
*/
char *
bstrndup(const char *s, size_t n)
{
const char *p;
char *copy;
size_t length;
/* Don't assume that the source string is nul-terminated. */
for (p = s; (size_t) (p - s) < n && *p != '\0'; p++)
;
length = p - s;
copy = malloc(length + 1);
if (p == NULL)
sysbail("failed to strndup %lu bytes", (unsigned long) length);
memcpy(copy, s, length);
copy[length] = '\0';
return copy;
}
/*
* Locate a test file. Given the partial path to a file, look under BUILD and
* then SOURCE for the file and return the full path to the file. Returns
* NULL if the file doesn't exist. A non-NULL return should be freed with
* test_file_path_free().
*
* This function uses sprintf because it attempts to be independent of all
* other portability layers. The use immediately after a memory allocation
* should be safe without using snprintf or strlcpy/strlcat.
*/
char *
test_file_path(const char *file)
{
char *base;
char *path = NULL;
size_t length;
const char *envs[] = { "BUILD", "SOURCE", NULL };
int i;
for (i = 0; envs[i] != NULL; i++) {
base = getenv(envs[i]);
if (base == NULL)
continue;
length = strlen(base) + 1 + strlen(file) + 1;
path = bmalloc(length);
sprintf(path, "%s/%s", base, file);
if (access(path, R_OK) == 0)
break;
free(path);
path = NULL;
}
return path;
}
/*
* Free a path returned from test_file_path(). This function exists primarily
* for Windows, where memory must be freed from the same library domain that
* it was allocated from.
*/
void
test_file_path_free(char *path)
{
if (path != NULL)
free(path);
}
/*
* Create a temporary directory, tmp, under BUILD if set and the current
* directory if it does not. Returns the path to the temporary directory in
* newly allocated memory, and calls bail on any failure. The return value
* should be freed with test_tmpdir_free.
*
* This function uses sprintf because it attempts to be independent of all
* other portability layers. The use immediately after a memory allocation
* should be safe without using snprintf or strlcpy/strlcat.
*/
char *
test_tmpdir(void)
{
const char *build;
char *path = NULL;
size_t length;
build = getenv("BUILD");
if (build == NULL)
build = ".";
length = strlen(build) + strlen("/tmp") + 1;
path = bmalloc(length);
sprintf(path, "%s/tmp", build);
if (access(path, X_OK) < 0)
if (mkdir(path, 0777) < 0)
sysbail("error creating temporary directory %s", path);
return path;
}
/*
* Free a path returned from test_tmpdir() and attempt to remove the
* directory. If we can't delete the directory, don't worry; something else
* that hasn't yet cleaned up may still be using it.
*/
void
test_tmpdir_free(char *path)
{
rmdir(path);
if (path != NULL)
free(path);
}

134
tests/tap/basic.h Normal file
View file

@ -0,0 +1,134 @@
/*
* Basic utility routines for the TAP protocol.
*
* This file is part of C TAP Harness. The current version plus supporting
* documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
*
* Copyright 2009, 2010, 2011, 2012 Russ Allbery <rra@stanford.edu>
* Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012
* The Board of Trustees of the Leland Stanford Junior University
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef TAP_BASIC_H
#define TAP_BASIC_H 1
#include <tests/tap/macros.h>
#include <stdarg.h> /* va_list */
#include <sys/types.h> /* size_t */
/*
* Used for iterating through arrays. ARRAY_SIZE returns the number of
* elements in the array (useful for a < upper bound in a for loop) and
* ARRAY_END returns a pointer to the element past the end (ISO C99 makes it
* legal to refer to such a pointer as long as it's never dereferenced).
*/
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
#define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)])
BEGIN_DECLS
/*
* The test count. Always contains the number that will be used for the next
* test status.
*/
extern unsigned long testnum;
/* Print out the number of tests and set standard output to line buffered. */
void plan(unsigned long count);
/*
* Prepare for lazy planning, in which the plan will be printed automatically
* at the end of the test program.
*/
void plan_lazy(void);
/* Skip the entire test suite. Call instead of plan. */
void skip_all(const char *format, ...)
__attribute__((__noreturn__, __format__(printf, 1, 2)));
/*
* Basic reporting functions. The okv() function is the same as ok() but
* takes the test description as a va_list to make it easier to reuse the
* reporting infrastructure when writing new tests.
*/
void ok(int success, const char *format, ...)
__attribute__((__format__(printf, 2, 3)));
void okv(int success, const char *format, va_list args);
void skip(const char *reason, ...)
__attribute__((__format__(printf, 1, 2)));
/* Report the same status on, or skip, the next count tests. */
void ok_block(unsigned long count, int success, const char *format, ...)
__attribute__((__format__(printf, 3, 4)));
void skip_block(unsigned long count, const char *reason, ...)
__attribute__((__format__(printf, 2, 3)));
/* Check an expected value against a seen value. */
void is_int(long wanted, long seen, const char *format, ...)
__attribute__((__format__(printf, 3, 4)));
void is_string(const char *wanted, const char *seen, const char *format, ...)
__attribute__((__format__(printf, 3, 4)));
void is_hex(unsigned long wanted, unsigned long seen, const char *format, ...)
__attribute__((__format__(printf, 3, 4)));
/* Bail out with an error. sysbail appends strerror(errno). */
void bail(const char *format, ...)
__attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2)));
void sysbail(const char *format, ...)
__attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2)));
/* Report a diagnostic to stderr prefixed with #. */
void diag(const char *format, ...)
__attribute__((__nonnull__, __format__(printf, 1, 2)));
void sysdiag(const char *format, ...)
__attribute__((__nonnull__, __format__(printf, 1, 2)));
/* Allocate memory, reporting a fatal error with bail on failure. */
void *bcalloc(size_t, size_t)
__attribute__((__alloc_size__(1, 2), __malloc__));
void *bmalloc(size_t)
__attribute__((__alloc_size__(1), __malloc__));
void *brealloc(void *, size_t)
__attribute__((__alloc_size__(2), __malloc__));
char *bstrdup(const char *)
__attribute__((__malloc__, __nonnull__));
char *bstrndup(const char *, size_t)
__attribute__((__malloc__, __nonnull__));
/*
* Find a test file under BUILD or SOURCE, returning the full path. The
* returned path should be freed with test_file_path_free().
*/
char *test_file_path(const char *file)
__attribute__((__malloc__, __nonnull__));
void test_file_path_free(char *path);
/*
* Create a temporary directory relative to BUILD and return the path. The
* returned path should be freed with test_tmpdir_free.
*/
char *test_tmpdir(void)
__attribute__((__malloc__));
void test_tmpdir_free(char *path);
END_DECLS
#endif /* TAP_BASIC_H */

67
tests/tap/float.c Normal file
View file

@ -0,0 +1,67 @@
/*
* Utility routines for writing floating point tests.
*
* Currently provides only one function, which checks whether a double is
* equal to an expected value within a given epsilon. This is broken into a
* separate source file from the rest of the basic C TAP library because it
* may require linking with -lm on some platforms, and the package may not
* otherwise care about floating point.
*
* This file is part of C TAP Harness. The current version plus supporting
* documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
*
* Copyright 2008, 2010, 2012 Russ Allbery <rra@stanford.edu>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/* Required for isnan() and isinf(). */
#if defined(__STRICT_ANSI__) || defined(PEDANTIC)
# ifndef _XOPEN_SOURCE
# define _XOPEN_SOURCE 600
# endif
#endif
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <tests/tap/basic.h>
#include <tests/tap/float.h>
/*
* Takes an expected double and a seen double and assumes the test passes if
* those two numbers are within delta of each other.
*/
void
is_double(double wanted, double seen, double epsilon, const char *format, ...)
{
va_list args;
va_start(args, format);
fflush(stderr);
if ((isnan(wanted) && isnan(seen))
|| (isinf(wanted) && isinf(seen) && wanted == seen)
|| fabs(wanted - seen) <= epsilon)
okv(1, format, args);
else {
printf("# wanted: %g\n# seen: %g\n", wanted, seen);
okv(0, format, args);
}
}

42
tests/tap/float.h Normal file
View file

@ -0,0 +1,42 @@
/*
* Floating point check function for the TAP protocol.
*
* This file is part of C TAP Harness. The current version plus supporting
* documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
*
* Copyright 2008, 2010, 2012 Russ Allbery <rra@stanford.edu>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef TAP_FLOAT_H
#define TAP_FLOAT_H 1
#include <tests/tap/macros.h>
BEGIN_DECLS
/* Check an expected value against a seen value within epsilon. */
void is_double(double wanted, double seen, double epsilon,
const char *format, ...)
__attribute__((__format__(printf, 4, 5)));
END_DECLS
#endif /* TAP_FLOAT_H */

246
tests/tap/libtap.sh Normal file
View file

@ -0,0 +1,246 @@
# Shell function library for test cases.
#
# Note that while many of the functions in this library could benefit from
# using "local" to avoid possibly hammering global variables, Solaris /bin/sh
# doesn't support local and this library aspires to be portable to Solaris
# Bourne shell. Instead, all private variables are prefixed with "tap_".
#
# This file provides a TAP-compatible shell function library useful for
# writing test cases. It is part of C TAP Harness, which can be found at
# <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
#
# Written by Russ Allbery <rra@stanford.edu>
# Copyright 2009, 2010, 2011, 2012 Russ Allbery <rra@stanford.edu>
# Copyright 2006, 2007, 2008, 2013
# The Board of Trustees of the Leland Stanford Junior University
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
# Print out the number of test cases we expect to run.
plan () {
count=1
planned="$1"
failed=0
echo "1..$1"
trap finish 0
}
# Prepare for lazy planning.
plan_lazy () {
count=1
planned=0
failed=0
trap finish 0
}
# Report the test status on exit.
finish () {
tap_highest=`expr "$count" - 1`
if [ "$planned" = 0 ] ; then
echo "1..$tap_highest"
planned="$tap_highest"
fi
tap_looks='# Looks like you'
if [ "$planned" -gt 0 ] ; then
if [ "$planned" -gt "$tap_highest" ] ; then
if [ "$planned" -gt 1 ] ; then
echo "$tap_looks planned $planned tests but only ran" \
"$tap_highest"
else
echo "$tap_looks planned $planned test but only ran" \
"$tap_highest"
fi
elif [ "$planned" -lt "$tap_highest" ] ; then
tap_extra=`expr "$tap_highest" - "$planned"`
if [ "$planned" -gt 1 ] ; then
echo "$tap_looks planned $planned tests but ran" \
"$tap_extra extra"
else
echo "$tap_looks planned $planned test but ran" \
"$tap_extra extra"
fi
elif [ "$failed" -gt 0 ] ; then
if [ "$failed" -gt 1 ] ; then
echo "$tap_looks failed $failed tests of $planned"
else
echo "$tap_looks failed $failed test of $planned"
fi
elif [ "$planned" -gt 1 ] ; then
echo "# All $planned tests successful or skipped"
else
echo "# $planned test successful or skipped"
fi
fi
}
# Skip the entire test suite. Should be run instead of plan.
skip_all () {
tap_desc="$1"
if [ -n "$tap_desc" ] ; then
echo "1..0 # skip $tap_desc"
else
echo "1..0 # skip"
fi
exit 0
}
# ok takes a test description and a command to run and prints success if that
# command is successful, false otherwise. The count starts at 1 and is
# updated each time ok is printed.
ok () {
tap_desc="$1"
if [ -n "$tap_desc" ] ; then
tap_desc=" - $tap_desc"
fi
shift
if "$@" ; then
echo ok "$count$tap_desc"
else
echo not ok "$count$tap_desc"
failed=`expr $failed + 1`
fi
count=`expr $count + 1`
}
# Skip the next test. Takes the reason why the test is skipped.
skip () {
echo "ok $count # skip $*"
count=`expr $count + 1`
}
# Report the same status on a whole set of tests. Takes the count of tests,
# the description, and then the command to run to determine the status.
ok_block () {
tap_i=$count
tap_end=`expr $count + $1`
shift
while [ "$tap_i" -lt "$tap_end" ] ; do
ok "$@"
tap_i=`expr $tap_i + 1`
done
}
# Skip a whole set of tests. Takes the count and then the reason for skipping
# the test.
skip_block () {
tap_i=$count
tap_end=`expr $count + $1`
shift
while [ "$tap_i" -lt "$tap_end" ] ; do
skip "$@"
tap_i=`expr $tap_i + 1`
done
}
# Portable variant of printf '%s\n' "$*". In the majority of cases, this
# function is slower than printf, because the latter is often implemented
# as a builtin command. The value of the variable IFS is ignored.
#
# This macro must not be called via backticks inside double quotes, since this
# will result in bizarre escaping behavior and lots of extra backslashes on
# Solaris.
puts () {
cat << EOH
$@
EOH
}
# Run a program expected to succeed, and print ok if it does and produces the
# correct output. Takes the description, expected exit status, the expected
# output, the command to run, and then any arguments for that command.
# Standard output and standard error are combined when analyzing the output of
# the command.
#
# If the command may contain system-specific error messages in its output,
# add strip_colon_error before the command to post-process its output.
ok_program () {
tap_desc="$1"
shift
tap_w_status="$1"
shift
tap_w_output="$1"
shift
tap_output=`"$@" 2>&1`
tap_status=$?
if [ $tap_status = $tap_w_status ] \
&& [ x"$tap_output" = x"$tap_w_output" ] ; then
ok "$tap_desc" true
else
echo "# saw: ($tap_status) $tap_output"
echo "# not: ($tap_w_status) $tap_w_output"
ok "$tap_desc" false
fi
}
# Strip a colon and everything after it off the output of a command, as long
# as that colon comes after at least one whitespace character. (This is done
# to avoid stripping the name of the program from the start of an error
# message.) This is used to remove system-specific error messages (coming
# from strerror, for example).
strip_colon_error() {
tap_output=`"$@" 2>&1`
tap_status=$?
tap_output=`puts "$tap_output" | sed 's/^\([^ ]* [^:]*\):.*/\1/'`
puts "$tap_output"
return $tap_status
}
# Bail out with an error message.
bail () {
echo 'Bail out!' "$@"
exit 255
}
# Output a diagnostic on standard error, preceded by the required # mark.
diag () {
echo '#' "$@"
}
# Search for the given file first in $BUILD and then in $SOURCE and echo the
# path where the file was found, or the empty string if the file wasn't
# found.
#
# This macro uses puts, so don't run it using backticks inside double quotes
# or bizarre quoting behavior will happen with Solaris sh.
test_file_path () {
if [ -n "$BUILD" ] && [ -f "$BUILD/$1" ] ; then
puts "$BUILD/$1"
elif [ -n "$SOURCE" ] && [ -f "$SOURCE/$1" ] ; then
puts "$SOURCE/$1"
else
echo ''
fi
}
# Create $BUILD/tmp for use by tests for storing temporary files and return
# the path (via standard output).
#
# This macro uses puts, so don't run it using backticks inside double quotes
# or bizarre quoting behavior will happen with Solaris sh.
test_tmpdir () {
if [ -z "$BUILD" ] ; then
tap_tmpdir="./tmp"
else
tap_tmpdir="$BUILD"/tmp
fi
if [ ! -d "$tap_tmpdir" ] ; then
mkdir "$tap_tmpdir" || bail "Error creating $tap_tmpdir"
fi
puts "$tap_tmpdir"
}

88
tests/tap/macros.h Normal file
View file

@ -0,0 +1,88 @@
/*
* Helpful macros for TAP header files.
*
* This is not, strictly speaking, related to TAP, but any TAP add-on is
* probably going to need these macros, so define them in one place so that
* everyone can pull them in.
*
* This file is part of C TAP Harness. The current version plus supporting
* documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
*
* Copyright 2008, 2012 Russ Allbery <rra@stanford.edu>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef TAP_MACROS_H
#define TAP_MACROS_H 1
/*
* __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7
* could you use the __format__ form of the attributes, which is what we use
* (to avoid confusion with other macros), and only with gcc 2.96 can you use
* the attribute __malloc__. 2.96 is very old, so don't bother trying to get
* the other attributes to work with GCC versions between 2.7 and 2.96.
*/
#ifndef __attribute__
# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
# define __attribute__(spec) /* empty */
# endif
#endif
/*
* We use __alloc_size__, but it was only available in fairly recent versions
* of GCC. Suppress warnings about the unknown attribute if GCC is too old.
* We know that we're GCC at this point, so we can use the GCC variadic macro
* extension, which will still work with versions of GCC too old to have C99
* variadic macro support.
*/
#if !defined(__attribute__) && !defined(__alloc_size__)
# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)
# define __alloc_size__(spec, args...) /* empty */
# endif
#endif
/*
* LLVM and Clang pretend to be GCC but don't support all of the __attribute__
* settings that GCC does. For them, suppress warnings about unknown
* attributes on declarations. This unfortunately will affect the entire
* compilation context, but there's no push and pop available.
*/
#if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__))
# pragma GCC diagnostic ignored "-Wattributes"
#endif
/* Used for unused parameters to silence gcc warnings. */
#define UNUSED __attribute__((__unused__))
/*
* BEGIN_DECLS is used at the beginning of declarations so that C++
* compilers don't mangle their names. END_DECLS is used at the end.
*/
#undef BEGIN_DECLS
#undef END_DECLS
#ifdef __cplusplus
# define BEGIN_DECLS extern "C" {
# define END_DECLS }
#else
# define BEGIN_DECLS /* empty */
# define END_DECLS /* empty */
#endif
#endif /* TAP_MACROS_H */