tests: add C TAP harness
This commit is contained in:
parent
33ded5fc57
commit
8fe5ef5a23
13 changed files with 3450 additions and 0 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,6 +1,7 @@
|
|||
tags
|
||||
Makefile
|
||||
*~
|
||||
*.a
|
||||
*.o
|
||||
*.so
|
||||
*.lo
|
||||
|
@ -55,6 +56,7 @@ ircd/version.c
|
|||
ircd/version.c.last
|
||||
ssld/ssld
|
||||
wsockd/wsockd
|
||||
tests/runtests
|
||||
testsuite/ircd.pid.*
|
||||
tools/charybdis-mkpasswd
|
||||
tools/charybdis-mkfingerprint
|
||||
|
|
|
@ -12,6 +12,7 @@ SUBDIRS += ircd \
|
|||
wsockd \
|
||||
authd \
|
||||
bandb \
|
||||
tests \
|
||||
tools \
|
||||
modules \
|
||||
extensions \
|
||||
|
|
|
@ -660,6 +660,7 @@ AC_CONFIG_FILES( \
|
|||
extensions/Makefile \
|
||||
ircd/Makefile \
|
||||
modules/Makefile \
|
||||
tests/Makefile \
|
||||
tools/Makefile \
|
||||
tools/genssl \
|
||||
doc/Makefile \
|
||||
|
|
17
tests/Makefile.am
Normal file
17
tests/Makefile.am
Normal file
|
@ -0,0 +1,17 @@
|
|||
check_PROGRAMS = runtests
|
||||
AM_CFLAGS=$(WARNFLAGS)
|
||||
AM_CPPFLAGS = $(DEFAULT_INCLUDES) -I../librb/include -I..
|
||||
AM_LDFLAGS = -no-install
|
||||
LDADD = tap/libtap.a ../librb/src/librb.la ../ircd/libircd.la
|
||||
|
||||
# Override -rpath or programs will be linked to installed libraries
|
||||
libdir=$(abs_top_builddir)
|
||||
|
||||
runtests_CPPFLAGS = -DC_TAP_SOURCE='"$(abs_top_srcdir)/tests"' \
|
||||
-DC_TAP_BUILD='"$(abs_top_builddir)/tests"'
|
||||
check_LIBRARIES = tap/libtap.a
|
||||
tap_libtap_a_SOURCES = tap/basic.c tap/basic.h \
|
||||
tap/float.c tap/float.h tap/macros.h
|
||||
|
||||
check-local: $(check_PROGRAMS)
|
||||
./runtests -l $(abs_top_srcdir)/tests/TESTS
|
250
tests/README
Normal file
250
tests/README
Normal file
|
@ -0,0 +1,250 @@
|
|||
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 C_TAP_SOURCE and
|
||||
C_TAP_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 C_TAP_BUILD and C_TAP_SOURCE environment variables,
|
||||
and return the full path to the file. This can be used to locate
|
||||
supporting data files. They also support a test_tmpdir() function
|
||||
that returns a directory that can be used for temporary files during
|
||||
tests.
|
||||
|
||||
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:
|
||||
|
||||
<https://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:
|
||||
|
||||
<https://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:
|
||||
|
||||
. "$C_TAP_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
|
||||
|
||||
. "$C_TAP_SOURCE"/tap/libtap.sh
|
||||
cd "$C_TAP_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 <https://www.eyrie.org/~eagle/software/c-tap-harness/>.
|
||||
|
||||
Copyright 2010, 2016 Russ Allbery <eagle@eyrie.org>
|
||||
|
||||
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
1
tests/TESTS
Normal file
|
@ -0,0 +1 @@
|
|||
#
|
1599
tests/runtests.c
Normal file
1599
tests/runtests.c
Normal file
File diff suppressed because it is too large
Load diff
945
tests/tap/basic.c
Normal file
945
tests/tap/basic.c
Normal file
|
@ -0,0 +1,945 @@
|
|||
/*
|
||||
* 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 <https://www.eyrie.org/~eagle/software/c-tap-harness/>.
|
||||
*
|
||||
* Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016
|
||||
* Russ Allbery <eagle@eyrie.org>
|
||||
* Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012, 2013, 2014
|
||||
* 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 <limits.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. This is exported to callers of the library.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
static unsigned long _planned = 0;
|
||||
static unsigned long _failed = 0;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static pid_t _process = 0;
|
||||
|
||||
/*
|
||||
* If true, we're doing lazy planning and will print out the plan based on the
|
||||
* last test number at the end of testing.
|
||||
*/
|
||||
static int _lazy = 0;
|
||||
|
||||
/*
|
||||
* If true, the test was aborted by calling bail(). Currently, this is only
|
||||
* used to ensure that we pass a false value to any cleanup functions even if
|
||||
* all tests to that point have passed.
|
||||
*/
|
||||
static int _aborted = 0;
|
||||
|
||||
/*
|
||||
* Registered cleanup functions. These are stored as a linked list and run in
|
||||
* registered order by finish when the test program exits. Each function is
|
||||
* passed a boolean value indicating whether all tests were successful.
|
||||
*/
|
||||
struct cleanup_func {
|
||||
test_cleanup_func func;
|
||||
struct cleanup_func *next;
|
||||
};
|
||||
static struct cleanup_func *cleanup_funcs = NULL;
|
||||
|
||||
/*
|
||||
* Registered diag files. Any output found in these files will be printed out
|
||||
* as if it were passed to diag() before any other output we do. This allows
|
||||
* background processes to log to a file and have that output interleaved with
|
||||
* the test output.
|
||||
*/
|
||||
struct diag_file {
|
||||
char *name;
|
||||
FILE *file;
|
||||
char *buffer;
|
||||
size_t bufsize;
|
||||
struct diag_file *next;
|
||||
};
|
||||
static struct diag_file *diag_files = NULL;
|
||||
|
||||
/*
|
||||
* Print a specified prefix and then the test description. Handles turning
|
||||
* the argument list into a va_args structure suitable for passing to
|
||||
* print_desc, which has to be done in a macro. Assumes that format is the
|
||||
* argument immediately before the variadic arguments.
|
||||
*/
|
||||
#define PRINT_DESC(prefix, format) \
|
||||
do { \
|
||||
if (format != NULL) { \
|
||||
va_list args; \
|
||||
if (prefix != NULL) \
|
||||
printf("%s", prefix); \
|
||||
va_start(args, format); \
|
||||
vprintf(format, args); \
|
||||
va_end(args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
* Form a new string by concatenating multiple strings. The arguments must be
|
||||
* terminated by (const char *) 0.
|
||||
*
|
||||
* This function only exists because we can't assume asprintf. We can't
|
||||
* simulate asprintf with snprintf because we're only assuming SUSv3, which
|
||||
* does not require that snprintf with a NULL buffer return the required
|
||||
* length. When those constraints are relaxed, this should be ripped out and
|
||||
* replaced with asprintf or a more trivial replacement with snprintf.
|
||||
*/
|
||||
static char *
|
||||
concat(const char *first, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *result;
|
||||
const char *string;
|
||||
size_t offset;
|
||||
size_t length = 0;
|
||||
|
||||
/*
|
||||
* Find the total memory required. Ensure we don't overflow length. See
|
||||
* the comment for breallocarray for why we're using UINT_MAX here.
|
||||
*/
|
||||
va_start(args, first);
|
||||
for (string = first; string != NULL; string = va_arg(args, const char *)) {
|
||||
if (length >= UINT_MAX - strlen(string))
|
||||
bail("strings too long in concat");
|
||||
length += strlen(string);
|
||||
}
|
||||
va_end(args);
|
||||
length++;
|
||||
|
||||
/* Create the string. */
|
||||
result = bmalloc(length);
|
||||
va_start(args, first);
|
||||
offset = 0;
|
||||
for (string = first; string != NULL; string = va_arg(args, const char *)) {
|
||||
memcpy(result + offset, string, strlen(string));
|
||||
offset += strlen(string);
|
||||
}
|
||||
va_end(args);
|
||||
result[offset] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check all registered diag_files for any output. We only print out the
|
||||
* output if we see a complete line; otherwise, we wait for the next newline.
|
||||
*/
|
||||
static void
|
||||
check_diag_files(void)
|
||||
{
|
||||
struct diag_file *file;
|
||||
fpos_t where;
|
||||
size_t length;
|
||||
int size, incomplete;
|
||||
|
||||
/*
|
||||
* Walk through each file and read each line of output available. The
|
||||
* general scheme here used is as follows: try to read a line of output at
|
||||
* a time. If we get NULL, check for EOF; on EOF, advance to the next
|
||||
* file.
|
||||
*
|
||||
* If we get some data, see if it ends in a newline. If it doesn't end in
|
||||
* a newline, we have one of two cases: our buffer isn't large enough, in
|
||||
* which case we resize it and try again, or we have incomplete data in
|
||||
* the file, in which case we rewind the file and will try again next
|
||||
* time.
|
||||
*/
|
||||
for (file = diag_files; file != NULL; file = file->next) {
|
||||
clearerr(file->file);
|
||||
|
||||
/* Store the current position in case we have to rewind. */
|
||||
if (fgetpos(file->file, &where) < 0)
|
||||
sysbail("cannot get position in %s", file->name);
|
||||
|
||||
/* Continue until we get EOF or an incomplete line of data. */
|
||||
incomplete = 0;
|
||||
while (!feof(file->file) && !incomplete) {
|
||||
size = file->bufsize > INT_MAX ? INT_MAX : (int) file->bufsize;
|
||||
if (fgets(file->buffer, size, file->file) == NULL) {
|
||||
if (ferror(file->file))
|
||||
sysbail("cannot read from %s", file->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* See if the line ends in a newline. If not, see which error
|
||||
* case we have. Use UINT_MAX as a substitute for SIZE_MAX (see
|
||||
* the comment for breallocarray).
|
||||
*/
|
||||
length = strlen(file->buffer);
|
||||
if (file->buffer[length - 1] != '\n') {
|
||||
if (length < file->bufsize - 1)
|
||||
incomplete = 1;
|
||||
else {
|
||||
if (file->bufsize >= UINT_MAX - BUFSIZ)
|
||||
sysbail("line too long in %s", file->name);
|
||||
file->bufsize += BUFSIZ;
|
||||
file->buffer = brealloc(file->buffer, file->bufsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* On either incomplete lines or too small of a buffer, rewind
|
||||
* and read the file again (on the next pass, if incomplete).
|
||||
* It's simpler than trying to double-buffer the file.
|
||||
*/
|
||||
if (fsetpos(file->file, &where) < 0)
|
||||
sysbail("cannot set position in %s", file->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We saw a complete line. Print it out. */
|
||||
printf("# %s", file->buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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), and running any
|
||||
* registered cleanup functions.
|
||||
*/
|
||||
static void
|
||||
finish(void)
|
||||
{
|
||||
int success, primary;
|
||||
struct cleanup_func *current;
|
||||
unsigned long highest = testnum - 1;
|
||||
struct diag_file *file, *tmp;
|
||||
|
||||
/* Check for pending diag_file output. */
|
||||
check_diag_files();
|
||||
|
||||
/* Free the diag_files. */
|
||||
file = diag_files;
|
||||
while (file != NULL) {
|
||||
tmp = file;
|
||||
file = file->next;
|
||||
fclose(tmp->file);
|
||||
free(tmp->name);
|
||||
free(tmp->buffer);
|
||||
free(tmp);
|
||||
}
|
||||
diag_files = NULL;
|
||||
|
||||
/*
|
||||
* Determine whether all tests were successful, which is needed before
|
||||
* calling cleanup functions since we pass that fact to the functions.
|
||||
*/
|
||||
if (_planned == 0 && _lazy)
|
||||
_planned = highest;
|
||||
success = (!_aborted && _planned == highest && _failed == 0);
|
||||
|
||||
/*
|
||||
* If there are any registered cleanup functions, we run those first. We
|
||||
* always run them, even if we didn't run a test. Don't do anything
|
||||
* except free the diag_files and call cleanup functions if we aren't the
|
||||
* primary process (the process in which plan or plan_lazy was called),
|
||||
* and tell the cleanup functions that fact.
|
||||
*/
|
||||
primary = (_process == 0 || getpid() == _process);
|
||||
while (cleanup_funcs != NULL) {
|
||||
cleanup_funcs->func(success, primary);
|
||||
current = cleanup_funcs;
|
||||
cleanup_funcs = cleanup_funcs->next;
|
||||
free(current);
|
||||
}
|
||||
if (!primary)
|
||||
return;
|
||||
|
||||
/* Don't do anything further if we never planned a test. */
|
||||
if (_planned == 0)
|
||||
return;
|
||||
|
||||
/* If we're aborting due to bail, don't print summaries. */
|
||||
if (_aborted)
|
||||
return;
|
||||
|
||||
/* Print out the lazy plan if needed. */
|
||||
fflush(stderr);
|
||||
if (_lazy && _planned > 0)
|
||||
printf("1..%lu\n", _planned);
|
||||
|
||||
/* Print out a summary of the results. */
|
||||
if (_planned > highest)
|
||||
diag("Looks like you planned %lu test%s but only ran %lu", _planned,
|
||||
(_planned > 1 ? "s" : ""), highest);
|
||||
else if (_planned < highest)
|
||||
diag("Looks like you planned %lu test%s but ran %lu extra", _planned,
|
||||
(_planned > 1 ? "s" : ""), highest - _planned);
|
||||
else if (_failed > 0)
|
||||
diag("Looks like you failed %lu test%s of %lu", _failed,
|
||||
(_failed > 1 ? "s" : ""), _planned);
|
||||
else if (_planned != 1)
|
||||
diag("All %lu tests successful or skipped", _planned);
|
||||
else
|
||||
diag("%lu test successful or skipped", _planned);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize things. Turns on line buffering on stdout and then prints out
|
||||
* the number of tests in the test suite. We intentionally don't check for
|
||||
* pending diag_file output here, since it should really come after the plan.
|
||||
*/
|
||||
void
|
||||
plan(unsigned long count)
|
||||
{
|
||||
if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0)
|
||||
sysdiag("cannot set stdout to line buffered");
|
||||
fflush(stderr);
|
||||
printf("1..%lu\n", count);
|
||||
testnum = 1;
|
||||
_planned = count;
|
||||
_process = getpid();
|
||||
if (atexit(finish) != 0) {
|
||||
sysdiag("cannot register exit handler");
|
||||
diag("cleanups will not be run");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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)
|
||||
sysdiag("cannot set stdout to line buffered");
|
||||
testnum = 1;
|
||||
_process = getpid();
|
||||
_lazy = 1;
|
||||
if (atexit(finish) != 0)
|
||||
sysbail("cannot register exit handler to display plan");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Skip the entire test suite and exits. Should be called instead of plan(),
|
||||
* not after it, since it prints out a special plan line. Ignore diag_file
|
||||
* output here, since it's not clear if it's allowed before the plan.
|
||||
*/
|
||||
void
|
||||
skip_all(const char *format, ...)
|
||||
{
|
||||
fflush(stderr);
|
||||
printf("1..0 # skip");
|
||||
PRINT_DESC(" ", format);
|
||||
putchar('\n');
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Takes a boolean success value and assumes the test passes if that value
|
||||
* is true and fails if that value is false.
|
||||
*/
|
||||
int
|
||||
ok(int success, const char *format, ...)
|
||||
{
|
||||
fflush(stderr);
|
||||
check_diag_files();
|
||||
printf("%sok %lu", success ? "" : "not ", testnum++);
|
||||
if (!success)
|
||||
_failed++;
|
||||
PRINT_DESC(" - ", format);
|
||||
putchar('\n');
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Same as ok(), but takes the format arguments as a va_list.
|
||||
*/
|
||||
int
|
||||
okv(int success, const char *format, va_list args)
|
||||
{
|
||||
fflush(stderr);
|
||||
check_diag_files();
|
||||
printf("%sok %lu", success ? "" : "not ", testnum++);
|
||||
if (!success)
|
||||
_failed++;
|
||||
if (format != NULL) {
|
||||
printf(" - ");
|
||||
vprintf(format, args);
|
||||
}
|
||||
putchar('\n');
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Skip a test.
|
||||
*/
|
||||
void
|
||||
skip(const char *reason, ...)
|
||||
{
|
||||
fflush(stderr);
|
||||
check_diag_files();
|
||||
printf("ok %lu # skip", testnum++);
|
||||
PRINT_DESC(" ", reason);
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Report the same status on the next count tests.
|
||||
*/
|
||||
int
|
||||
ok_block(unsigned long count, int success, const char *format, ...)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
fflush(stderr);
|
||||
check_diag_files();
|
||||
for (i = 0; i < count; i++) {
|
||||
printf("%sok %lu", success ? "" : "not ", testnum++);
|
||||
if (!success)
|
||||
_failed++;
|
||||
PRINT_DESC(" - ", format);
|
||||
putchar('\n');
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Skip the next count tests.
|
||||
*/
|
||||
void
|
||||
skip_block(unsigned long count, const char *reason, ...)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
fflush(stderr);
|
||||
check_diag_files();
|
||||
for (i = 0; i < count; i++) {
|
||||
printf("ok %lu # skip", testnum++);
|
||||
PRINT_DESC(" ", reason);
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Takes an expected boolean value and a seen boolean value and assumes the
|
||||
* test passes if the truth value of both match.
|
||||
*/
|
||||
int
|
||||
is_bool(int wanted, int seen, const char *format, ...)
|
||||
{
|
||||
int success;
|
||||
|
||||
fflush(stderr);
|
||||
check_diag_files();
|
||||
success = (!!wanted == !!seen);
|
||||
if (success)
|
||||
printf("ok %lu", testnum++);
|
||||
else {
|
||||
diag("wanted: %s", !!wanted ? "true" : "false");
|
||||
diag(" seen: %s", !!seen ? "true" : "false");
|
||||
printf("not ok %lu", testnum++);
|
||||
_failed++;
|
||||
}
|
||||
PRINT_DESC(" - ", format);
|
||||
putchar('\n');
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Takes an expected integer and a seen integer and assumes the test passes
|
||||
* if those two numbers match.
|
||||
*/
|
||||
int
|
||||
is_int(long wanted, long seen, const char *format, ...)
|
||||
{
|
||||
int success;
|
||||
|
||||
fflush(stderr);
|
||||
check_diag_files();
|
||||
success = (wanted == seen);
|
||||
if (success)
|
||||
printf("ok %lu", testnum++);
|
||||
else {
|
||||
diag("wanted: %ld", wanted);
|
||||
diag(" seen: %ld", seen);
|
||||
printf("not ok %lu", testnum++);
|
||||
_failed++;
|
||||
}
|
||||
PRINT_DESC(" - ", format);
|
||||
putchar('\n');
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Takes a string and what the string should be, and assumes the test passes
|
||||
* if those strings match (using strcmp).
|
||||
*/
|
||||
int
|
||||
is_string(const char *wanted, const char *seen, const char *format, ...)
|
||||
{
|
||||
int success;
|
||||
|
||||
if (wanted == NULL)
|
||||
wanted = "(null)";
|
||||
if (seen == NULL)
|
||||
seen = "(null)";
|
||||
fflush(stderr);
|
||||
check_diag_files();
|
||||
success = (strcmp(wanted, seen) == 0);
|
||||
if (success)
|
||||
printf("ok %lu", testnum++);
|
||||
else {
|
||||
diag("wanted: %s", wanted);
|
||||
diag(" seen: %s", seen);
|
||||
printf("not ok %lu", testnum++);
|
||||
_failed++;
|
||||
}
|
||||
PRINT_DESC(" - ", format);
|
||||
putchar('\n');
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
int
|
||||
is_hex(unsigned long wanted, unsigned long seen, const char *format, ...)
|
||||
{
|
||||
int success;
|
||||
|
||||
fflush(stderr);
|
||||
check_diag_files();
|
||||
success = (wanted == seen);
|
||||
if (success)
|
||||
printf("ok %lu", testnum++);
|
||||
else {
|
||||
diag("wanted: %lx", (unsigned long) wanted);
|
||||
diag(" seen: %lx", (unsigned long) seen);
|
||||
printf("not ok %lu", testnum++);
|
||||
_failed++;
|
||||
}
|
||||
PRINT_DESC(" - ", format);
|
||||
putchar('\n');
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Bail out with an error.
|
||||
*/
|
||||
void
|
||||
bail(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
_aborted = 1;
|
||||
fflush(stderr);
|
||||
check_diag_files();
|
||||
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;
|
||||
|
||||
_aborted = 1;
|
||||
fflush(stderr);
|
||||
check_diag_files();
|
||||
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. Always returns 1 to allow embedding in
|
||||
* compound statements.
|
||||
*/
|
||||
int
|
||||
diag(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
fflush(stderr);
|
||||
check_diag_files();
|
||||
fflush(stdout);
|
||||
printf("# ");
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
printf("\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Report a diagnostic to stderr, appending strerror(errno). Always returns 1
|
||||
* to allow embedding in compound statements.
|
||||
*/
|
||||
int
|
||||
sysdiag(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
int oerrno = errno;
|
||||
|
||||
fflush(stderr);
|
||||
check_diag_files();
|
||||
fflush(stdout);
|
||||
printf("# ");
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
printf(": %s\n", strerror(oerrno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Register a new file for diag_file processing.
|
||||
*/
|
||||
void
|
||||
diag_file_add(const char *name)
|
||||
{
|
||||
struct diag_file *file, *prev;
|
||||
|
||||
file = bcalloc(1, sizeof(struct diag_file));
|
||||
file->name = bstrdup(name);
|
||||
file->file = fopen(file->name, "r");
|
||||
if (file->file == NULL)
|
||||
sysbail("cannot open %s", name);
|
||||
file->buffer = bmalloc(BUFSIZ);
|
||||
file->bufsize = BUFSIZ;
|
||||
if (diag_files == NULL)
|
||||
diag_files = file;
|
||||
else {
|
||||
for (prev = diag_files; prev->next != NULL; prev = prev->next)
|
||||
;
|
||||
prev->next = file;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Remove a file from diag_file processing. If the file is not found, do
|
||||
* nothing, since there are some situations where it can be removed twice
|
||||
* (such as if it's removed from a cleanup function, since cleanup functions
|
||||
* are called after freeing all the diag_files).
|
||||
*/
|
||||
void
|
||||
diag_file_remove(const char *name)
|
||||
{
|
||||
struct diag_file *file;
|
||||
struct diag_file **prev = &diag_files;
|
||||
|
||||
for (file = diag_files; file != NULL; file = file->next) {
|
||||
if (strcmp(file->name, name) == 0) {
|
||||
*prev = file->next;
|
||||
fclose(file->file);
|
||||
free(file->name);
|
||||
free(file->buffer);
|
||||
free(file);
|
||||
return;
|
||||
}
|
||||
prev = &file->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The same as brealloc, but determine the size by multiplying an element
|
||||
* count by a size, similar to calloc. The multiplication is checked for
|
||||
* integer overflow.
|
||||
*
|
||||
* We should technically use SIZE_MAX here for the overflow check, but
|
||||
* SIZE_MAX is C99 and we're only assuming C89 + SUSv3, which does not
|
||||
* guarantee that it exists. They do guarantee that UINT_MAX exists, and we
|
||||
* can assume that UINT_MAX <= SIZE_MAX.
|
||||
*
|
||||
* (In theory, C89 and C99 permit size_t to be smaller than unsigned int, but
|
||||
* I disbelieve in the existence of such systems and they will have to cope
|
||||
* without overflow checks.)
|
||||
*/
|
||||
void *
|
||||
breallocarray(void *p, size_t n, size_t size)
|
||||
{
|
||||
if (n > 0 && UINT_MAX / n <= size)
|
||||
bail("reallocarray too large");
|
||||
p = realloc(p, n * size);
|
||||
if (p == NULL)
|
||||
sysbail("failed to realloc %lu bytes", (unsigned long) (n * 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 = (size_t) (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
|
||||
* C_TAP_BUILD and then C_TAP_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().
|
||||
*/
|
||||
char *
|
||||
test_file_path(const char *file)
|
||||
{
|
||||
char *base;
|
||||
char *path = NULL;
|
||||
const char *envs[] = { "C_TAP_BUILD", "C_TAP_SOURCE", NULL };
|
||||
int i;
|
||||
|
||||
for (i = 0; envs[i] != NULL; i++) {
|
||||
base = getenv(envs[i]);
|
||||
if (base == NULL)
|
||||
continue;
|
||||
path = concat(base, "/", file, (const char *) 0);
|
||||
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)
|
||||
{
|
||||
free(path);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a temporary directory, tmp, under C_TAP_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;
|
||||
|
||||
build = getenv("C_TAP_BUILD");
|
||||
if (build == NULL)
|
||||
build = ".";
|
||||
path = concat(build, "/tmp", (const char *) 0);
|
||||
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)
|
||||
{
|
||||
if (path != NULL)
|
||||
rmdir(path);
|
||||
free(path);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Register a cleanup function that is called when testing ends. All such
|
||||
* registered functions will be run by finish.
|
||||
*/
|
||||
void
|
||||
test_cleanup_register(test_cleanup_func func)
|
||||
{
|
||||
struct cleanup_func *cleanup, **last;
|
||||
|
||||
cleanup = bmalloc(sizeof(struct cleanup_func));
|
||||
cleanup->func = func;
|
||||
cleanup->next = NULL;
|
||||
last = &cleanup_funcs;
|
||||
while (*last != NULL)
|
||||
last = &(*last)->next;
|
||||
*last = cleanup;
|
||||
}
|
175
tests/tap/basic.h
Normal file
175
tests/tap/basic.h
Normal file
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Basic utility routines for the TAP protocol.
|
||||
*
|
||||
* This file is part of C TAP Harness. The current version plus supporting
|
||||
* documentation is at <https://www.eyrie.org/~eagle/software/c-tap-harness/>.
|
||||
*
|
||||
* Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016
|
||||
* Russ Allbery <eagle@eyrie.org>
|
||||
* Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012, 2014
|
||||
* 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 <stddef.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. ok() and okv() return the
|
||||
* value of the success argument.
|
||||
*/
|
||||
int ok(int success, const char *format, ...)
|
||||
__attribute__((__format__(printf, 2, 3)));
|
||||
int okv(int success, const char *format, va_list args)
|
||||
__attribute__((__format__(printf, 2, 0)));
|
||||
void skip(const char *reason, ...)
|
||||
__attribute__((__format__(printf, 1, 2)));
|
||||
|
||||
/*
|
||||
* Report the same status on, or skip, the next count tests. ok_block()
|
||||
* returns the value of the success argument.
|
||||
*/
|
||||
int 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. Returns true if the test
|
||||
* passes and false if it fails. is_bool takes an int since the bool type
|
||||
* isn't fully portable yet, but interprets both arguments for their truth
|
||||
* value, not for their numeric value.
|
||||
*/
|
||||
int is_bool(int wanted, int seen, const char *format, ...)
|
||||
__attribute__((__format__(printf, 3, 4)));
|
||||
int is_int(long wanted, long seen, const char *format, ...)
|
||||
__attribute__((__format__(printf, 3, 4)));
|
||||
int is_string(const char *wanted, const char *seen, const char *format, ...)
|
||||
__attribute__((__format__(printf, 3, 4)));
|
||||
int 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 #. */
|
||||
int diag(const char *format, ...)
|
||||
__attribute__((__nonnull__, __format__(printf, 1, 2)));
|
||||
int sysdiag(const char *format, ...)
|
||||
__attribute__((__nonnull__, __format__(printf, 1, 2)));
|
||||
|
||||
/*
|
||||
* Register or unregister a file that contains supplementary diagnostics.
|
||||
* Before any other output, all registered files will be read, line by line,
|
||||
* and each line will be reported as a diagnostic as if it were passed to
|
||||
* diag(). Nul characters are not supported in these files and will result in
|
||||
* truncated output.
|
||||
*/
|
||||
void diag_file_add(const char *file)
|
||||
__attribute__((__nonnull__));
|
||||
void diag_file_remove(const char *file)
|
||||
__attribute__((__nonnull__));
|
||||
|
||||
/* Allocate memory, reporting a fatal error with bail on failure. */
|
||||
void *bcalloc(size_t, size_t)
|
||||
__attribute__((__alloc_size__(1, 2), __malloc__, __warn_unused_result__));
|
||||
void *bmalloc(size_t)
|
||||
__attribute__((__alloc_size__(1), __malloc__, __warn_unused_result__));
|
||||
void *breallocarray(void *, size_t, size_t)
|
||||
__attribute__((__alloc_size__(2, 3), __malloc__, __warn_unused_result__));
|
||||
void *brealloc(void *, size_t)
|
||||
__attribute__((__alloc_size__(2), __malloc__, __warn_unused_result__));
|
||||
char *bstrdup(const char *)
|
||||
__attribute__((__malloc__, __nonnull__, __warn_unused_result__));
|
||||
char *bstrndup(const char *, size_t)
|
||||
__attribute__((__malloc__, __nonnull__, __warn_unused_result__));
|
||||
|
||||
/*
|
||||
* Find a test file under C_TAP_BUILD or C_TAP_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__, __warn_unused_result__));
|
||||
void test_file_path_free(char *path);
|
||||
|
||||
/*
|
||||
* Create a temporary directory relative to C_TAP_BUILD and return the path.
|
||||
* The returned path should be freed with test_tmpdir_free().
|
||||
*/
|
||||
char *test_tmpdir(void)
|
||||
__attribute__((__malloc__, __warn_unused_result__));
|
||||
void test_tmpdir_free(char *path);
|
||||
|
||||
/*
|
||||
* Register a cleanup function that is called when testing ends. All such
|
||||
* registered functions will be run during atexit handling (and are therefore
|
||||
* subject to all the same constraints and caveats as atexit functions).
|
||||
*
|
||||
* The function must return void and will be passed two arguments: an int that
|
||||
* will be true if the test completed successfully and false otherwise, and an
|
||||
* int that will be true if the cleanup function is run in the primary process
|
||||
* (the one that called plan or plan_lazy) and false otherwise.
|
||||
*/
|
||||
typedef void (*test_cleanup_func)(int, int);
|
||||
void test_cleanup_register(test_cleanup_func)
|
||||
__attribute__((__nonnull__));
|
||||
|
||||
END_DECLS
|
||||
|
||||
#endif /* TAP_BASIC_H */
|
74
tests/tap/float.c
Normal file
74
tests/tap/float.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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 <https://www.eyrie.org/~eagle/software/c-tap-harness/>.
|
||||
*
|
||||
* Copyright 2008, 2010, 2012, 2013, 2014, 2015, 2016
|
||||
* Russ Allbery <eagle@eyrie.org>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
int
|
||||
is_double(double wanted, double seen, double epsilon, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
int success;
|
||||
|
||||
va_start(args, format);
|
||||
fflush(stderr);
|
||||
if ((isnan(wanted) && isnan(seen))
|
||||
|| (isinf(wanted) && isinf(wanted) == isinf(seen))
|
||||
|| fabs(wanted - seen) <= epsilon) {
|
||||
success = 1;
|
||||
okv(1, format, args);
|
||||
} else {
|
||||
success = 0;
|
||||
diag("wanted: %g", wanted);
|
||||
diag(" seen: %g", seen);
|
||||
okv(0, format, args);
|
||||
}
|
||||
va_end(args);
|
||||
return success;
|
||||
}
|
42
tests/tap/float.h
Normal file
42
tests/tap/float.h
Normal 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 <https://www.eyrie.org/~eagle/software/c-tap-harness/>.
|
||||
*
|
||||
* Copyright 2008, 2010, 2012, 2014 Russ Allbery <eagle@eyrie.org>
|
||||
*
|
||||
* 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. */
|
||||
int 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
246
tests/tap/libtap.sh
Normal 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
|
||||
# <https://www.eyrie.org/~eagle/software/c-tap-harness/>.
|
||||
#
|
||||
# Written by Russ Allbery <eagle@eyrie.org>
|
||||
# Copyright 2009, 2010, 2011, 2012, 2016 Russ Allbery <eagle@eyrie.org>
|
||||
# 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 $C_TAP_BUILD and then in $C_TAP_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 "$C_TAP_BUILD" ] && [ -f "$C_TAP_BUILD/$1" ] ; then
|
||||
puts "$C_TAP_BUILD/$1"
|
||||
elif [ -n "$C_TAP_SOURCE" ] && [ -f "$C_TAP_SOURCE/$1" ] ; then
|
||||
puts "$C_TAP_SOURCE/$1"
|
||||
else
|
||||
echo ''
|
||||
fi
|
||||
}
|
||||
|
||||
# Create $C_TAP_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 "$C_TAP_BUILD" ] ; then
|
||||
tap_tmpdir="./tmp"
|
||||
else
|
||||
tap_tmpdir="$C_TAP_BUILD"/tmp
|
||||
fi
|
||||
if [ ! -d "$tap_tmpdir" ] ; then
|
||||
mkdir "$tap_tmpdir" || bail "Error creating $tap_tmpdir"
|
||||
fi
|
||||
puts "$tap_tmpdir"
|
||||
}
|
97
tests/tap/macros.h
Normal file
97
tests/tap/macros.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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 <https://www.eyrie.org/~eagle/software/c-tap-harness/>.
|
||||
*
|
||||
* Copyright 2008, 2012, 2013, 2015 Russ Allbery <eagle@eyrie.org>
|
||||
*
|
||||
* 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 defined(__GNUC__) && !defined(__clang__)
|
||||
# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)
|
||||
# define __alloc_size__(spec, args...) /* empty */
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Suppress __warn_unused_result__ if gcc is too old. */
|
||||
#if !defined(__attribute__) && !defined(__warn_unused_result__)
|
||||
# if __GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
|
||||
# define __warn_unused_result__ /* 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 */
|
Loading…
Reference in a new issue