mirror of
https://git.openafs.org/openafs.git
synced 2025-01-19 15:30:14 +00:00
92825d6d65
Includes the following upstream changes: Add a more complete usage message to runtests and add support for a -h command-line flag to display the usage message. is_double() now takes a third argument, an epsilon. Two numbers are considered equal if their absolute difference is less than epsilon. is_double() also now treats wanted and seen values of NaN (not a number) as equal. Thanks to PICCA Frédéric-Emmanuel for the proposed changes. The ok_program function in the shell libtap.sh library no longer strips text after a colon and a space from the program output if the expected status is non-zero. Instead, if program output may contain system-specific error messages after a colon and a space, put the new function strip_colon_error before the program to do this stripping. Thanks to Carsten Hey for the idea. strip_colon_error is now smarter about preserving an initial word ending in a colon (which is generally the program name) while still stripping error messages later in the line. The test_file_path function in the shell libtap.sh library now always returns the empty string, rather than possible absolute paths starting at /, if $BUILD and $SOURCE are not set. Flush standard error in the C TAP library before printing results for more deterministic output. Thanks to Carsten Hey for the idea. All of C TAP Harness now compiles with gcc -ansi -pedantic and should be fully C89-compatible. Note that either C99 or SUSv3 is required to build C TAP Harness. (This should not be a problem on any modern platform.) Based on work by Carsten Hey. Simplify and improve output formatting in the summary of failing tests in some edge cases. Add explicit license statements to the files meant to be copied into other packages rather than referring to LICENSE. Add a test_file_path() function to the basic C and shell TAP libraries, which searches the build and source directories for a particular file and returns the full path. This is a utility function that can be used to find test data files. Change-Id: I3ef84218f0e3a8b75f550c8b629b058330659b31 Reviewed-on: http://gerrit.openafs.org/4589 Reviewed-by: Derrick Brashear <shadow@dementia.org> Tested-by: BuildBot <buildbot@rampaginggeek.com>
525 lines
13 KiB
C
525 lines
13 KiB
C
/*
|
|
* 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, 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/>.
|
|
*
|
|
* Copyright 2009, 2010 Russ Allbery <rra@stanford.edu>
|
|
* 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.
|
|
*
|
|
* 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(). */
|
|
#ifndef _XOPEN_SOURCE
|
|
# define _XOPEN_SOURCE 600
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <math.h>
|
|
#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;
|
|
fflush(stderr);
|
|
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));
|
|
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 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, ...)
|
|
{
|
|
fflush(stderr);
|
|
if ((isnan(wanted) && isnan(seen))
|
|
|| (isinf(wanted) && isinf(seen) && wanted == seen)
|
|
|| fabs(wanted - seen) <= epsilon)
|
|
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, ...)
|
|
{
|
|
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(1);
|
|
}
|
|
|
|
|
|
/*
|
|
* 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(1);
|
|
}
|
|
|
|
|
|
/*
|
|
* 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));
|
|
}
|
|
|
|
|
|
/*
|
|
* 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);
|
|
}
|