2010-05-28 17:35:28 +01:00
|
|
|
/*
|
|
|
|
* Some utility routines for writing tests.
|
|
|
|
*
|
2011-04-28 21:00:43 +01:00
|
|
|
* 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, or setting up the TAP
|
|
|
|
* output format.
|
|
|
|
*
|
|
|
|
* 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/>.
|
2010-05-28 17:35:28 +01:00
|
|
|
*
|
|
|
|
* Copyright 2009, 2010 Russ Allbery <rra@stanford.edu>
|
2011-04-28 21:00:43 +01:00
|
|
|
* Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008
|
|
|
|
* 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.
|
2010-05-28 17:35:28 +01:00
|
|
|
*
|
2011-04-28 21:00:43 +01:00
|
|
|
* 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.
|
2010-05-28 17:35:28 +01:00
|
|
|
*/
|
|
|
|
|
2011-04-28 21:00:43 +01:00
|
|
|
/* Required for isnan() and isinf(). */
|
|
|
|
#ifndef _XOPEN_SOURCE
|
|
|
|
# define _XOPEN_SOURCE 600
|
|
|
|
#endif
|
|
|
|
|
2010-05-28 17:35:28 +01:00
|
|
|
#include <errno.h>
|
2011-04-28 21:00:43 +01:00
|
|
|
#include <math.h>
|
2010-05-28 17:35:28 +01:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <tap/basic.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
finish(void)
|
|
|
|
{
|
|
|
|
unsigned long highest = testnum - 1;
|
|
|
|
|
|
|
|
if (_planned == 0 && !_lazy)
|
|
|
|
return;
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-05-28 17:35:28 +01:00
|
|
|
if (_process != 0 && getpid() == _process) {
|
|
|
|
if (_lazy) {
|
|
|
|
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));
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-05-28 17:35:28 +01:00
|
|
|
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, ...)
|
|
|
|
{
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-05-28 17:35:28 +01:00
|
|
|
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, ...)
|
|
|
|
{
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-05-28 17:35:28 +01:00
|
|
|
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');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-06-09 02:40:11 +01:00
|
|
|
/*
|
|
|
|
* Same as ok(), but takes the format arguments as a va_list.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
okv(int success, const char *format, va_list args)
|
|
|
|
{
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-06-09 02:40:11 +01:00
|
|
|
printf("%sok %lu", success ? "" : "not ", testnum++);
|
|
|
|
if (!success)
|
|
|
|
_failed++;
|
|
|
|
if (format != NULL)
|
|
|
|
print_desc(format, args);
|
|
|
|
putchar('\n');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-05-28 17:35:28 +01:00
|
|
|
/*
|
|
|
|
* Skip a test.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
skip(const char *reason, ...)
|
|
|
|
{
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-05-28 17:35:28 +01:00
|
|
|
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;
|
|
|
|
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-05-28 17:35:28 +01:00
|
|
|
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;
|
|
|
|
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-05-28 17:35:28 +01:00
|
|
|
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, ...)
|
|
|
|
{
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-05-28 17:35:28 +01:00
|
|
|
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)";
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-05-28 17:35:28 +01:00
|
|
|
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 double and a seen double and assumes the test passes if
|
2011-04-28 21:00:43 +01:00
|
|
|
* those two numbers are within delta of each other.
|
2010-05-28 17:35:28 +01:00
|
|
|
*/
|
|
|
|
void
|
2011-04-28 21:00:43 +01:00
|
|
|
is_double(double wanted, double seen, double epsilon, const char *format, ...)
|
2010-05-28 17:35:28 +01:00
|
|
|
{
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
|
|
|
if ((isnan(wanted) && isnan(seen))
|
|
|
|
|| (isinf(wanted) && isinf(seen) && wanted == seen)
|
|
|
|
|| fabs(wanted - seen) <= epsilon)
|
2010-05-28 17:35:28 +01:00
|
|
|
printf("ok %lu", testnum++);
|
|
|
|
else {
|
|
|
|
printf("# wanted: %g\n# seen: %g\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, ...)
|
|
|
|
{
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-05-28 17:35:28 +01:00
|
|
|
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;
|
|
|
|
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-05-28 17:35:28 +01:00
|
|
|
fflush(stdout);
|
|
|
|
printf("Bail out! ");
|
|
|
|
va_start(args, format);
|
|
|
|
vprintf(format, args);
|
|
|
|
va_end(args);
|
|
|
|
printf("\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Bail out with an error, appending strerror(errno).
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
sysbail(const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
int oerrno = errno;
|
|
|
|
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-05-28 17:35:28 +01:00
|
|
|
fflush(stdout);
|
|
|
|
printf("Bail out! ");
|
|
|
|
va_start(args, format);
|
|
|
|
vprintf(format, args);
|
|
|
|
va_end(args);
|
|
|
|
printf(": %s\n", strerror(oerrno));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Report a diagnostic to stderr.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
diag(const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-05-28 17:35:28 +01:00
|
|
|
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;
|
|
|
|
|
2011-04-28 21:00:43 +01:00
|
|
|
fflush(stderr);
|
2010-05-28 17:35:28 +01:00
|
|
|
fflush(stdout);
|
|
|
|
printf("# ");
|
|
|
|
va_start(args, format);
|
|
|
|
vprintf(format, args);
|
|
|
|
va_end(args);
|
|
|
|
printf(": %s\n", strerror(oerrno));
|
|
|
|
}
|
2011-04-28 21:00:43 +01:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 = malloc(length);
|
|
|
|
if (path == NULL)
|
|
|
|
sysbail("cannot allocate memory");
|
|
|
|
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);
|
|
|
|
}
|