mirror of
https://github.com/cryb-to/cryb-to.git
synced 2024-12-20 19:41:08 +00:00
d4ae7a43cb
Travis forces _FORTIFY_SOURCE, which enables warn_unused_result annotations in glibc. Some of those annotations are of dubious value; in the case of asprintf(3) and vasprintf(3), they flag code that doesn't check the return value as unsafe even if it checks the pointer instead (which is guaranteed to be NULL in case of failure, and arguably more useful than the return value). Unfortunately, gcc intentionally ignores (void) casts, so we have no choice but to quench the warning with -Wno-unused-result. However, some of the compilers we wish to support don't recognize it, so we move it from the developer flags to the Travis environment. While there, switch Travis from Precious to Trusty.
324 lines
7.2 KiB
C
324 lines
7.2 KiB
C
/*-
|
|
* Copyright (c) 2012-2017 Dag-Erling Smørgrav
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote
|
|
* products derived from this software without specific prior written
|
|
* permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "cryb/impl.h"
|
|
|
|
#include <err.h>
|
|
#include <setjmp.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cryb/test.h>
|
|
|
|
/* program name */
|
|
const char *t_progname;
|
|
|
|
/* verbose flag */
|
|
int t_verbose;
|
|
|
|
/* whether to check for leaks */
|
|
static int leaktest;
|
|
|
|
/*
|
|
* If verbose flag is set, print an array of bytes in hex
|
|
*/
|
|
void
|
|
t_printv_hex(const uint8_t *buf, size_t len)
|
|
{
|
|
|
|
if (t_verbose) {
|
|
while (len--) {
|
|
fprintf(stderr, "%02x", *buf++);
|
|
if (len > 0 && len % 4 == 0)
|
|
fprintf(stderr, " ");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If verbose flag is set, print a message
|
|
*/
|
|
void
|
|
t_printv(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (t_verbose) {
|
|
va_start(ap, fmt);
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Assertion failed - test cannot proceed
|
|
*/
|
|
void
|
|
t_assertion_failed(const char *func, const char *file, unsigned int line,
|
|
const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
fprintf(stderr, "assertion failed in %s() on %s:%u\n",
|
|
func, file, line);
|
|
va_start(ap, fmt);
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
fprintf(stderr, "\n");
|
|
raise(SIGABRT);
|
|
_exit(1);
|
|
}
|
|
|
|
/*
|
|
* Test plan
|
|
*/
|
|
static struct t_test **t_plan;
|
|
static size_t t_plan_len, t_plan_size;
|
|
|
|
/*
|
|
* Grow the test plan to accomodate at least n more entries.
|
|
*/
|
|
static void
|
|
t_grow(int n)
|
|
{
|
|
struct t_test **p;
|
|
|
|
if (t_plan_len + n < t_plan_size)
|
|
return;
|
|
if (t_plan_size == 0)
|
|
t_plan_size = 16;
|
|
while (t_plan_len + n >= t_plan_size)
|
|
t_plan_size *= 2;
|
|
if ((p = realloc(t_plan, t_plan_size * sizeof *p)) == NULL)
|
|
err(1, "realloc()");
|
|
t_plan = p;
|
|
}
|
|
|
|
/*
|
|
* Add a single entry to the test plan.
|
|
*/
|
|
void
|
|
t_add_test(t_func *func, void *arg, const char *fmt, ...)
|
|
{
|
|
struct t_test *t;
|
|
va_list ap;
|
|
|
|
if ((t = malloc(sizeof *t)) == NULL)
|
|
err(1, "malloc()");
|
|
t->func = func;
|
|
va_start(ap, fmt);
|
|
if (vasprintf(&t->desc, fmt, ap) < 0)
|
|
err(1, "vasprintf()");
|
|
va_end(ap);
|
|
t->arg = arg;
|
|
t_grow(1);
|
|
t_plan[t_plan_len++] = t;
|
|
t_plan[t_plan_len] = NULL;
|
|
}
|
|
|
|
/*
|
|
* Add multiple entries to the test plan.
|
|
*/
|
|
void
|
|
t_add_tests(struct t_test *t, int n)
|
|
{
|
|
int i;
|
|
|
|
t_grow(n);
|
|
for (i = 0; i < n; ++i)
|
|
t_plan[t_plan_len++] = &t[i];
|
|
t_plan[t_plan_len] = NULL;
|
|
}
|
|
|
|
/*
|
|
* Signal handler for tests
|
|
*/
|
|
static jmp_buf sigjmp;
|
|
static int sigs[] = {
|
|
/* it is not safe to catch SIGABRT if raised by abort(3) */
|
|
/* SIGABRT, */
|
|
SIGBUS, SIGSEGV, SIGSYS, SIGPIPE, SIGALRM, SIGTERM,
|
|
0
|
|
};
|
|
|
|
static void
|
|
t_handle_signal(int signo)
|
|
{
|
|
|
|
longjmp(sigjmp, signo);
|
|
}
|
|
|
|
/*
|
|
* Run a single test
|
|
*/
|
|
static int
|
|
t_run_test(struct t_test *t, int n)
|
|
{
|
|
unsigned long snap1[16], snap2[16];
|
|
size_t snaplen;
|
|
char *desc;
|
|
int i, ret, signo;
|
|
|
|
if (leaktest && t_verbose)
|
|
t_malloc_snapshot(snap1, sizeof snap1);
|
|
for (i = 0; sigs[i] > 0; ++i)
|
|
signal(sigs[i], t_handle_signal);
|
|
desc = t->desc;
|
|
ret = 0;
|
|
if ((signo = setjmp(sigjmp)) == 0)
|
|
ret = (*t->func)(&desc, t->arg);
|
|
if (t_malloc_fail != 0) {
|
|
t_printv("WARNING: test case left t_malloc_fail = %d\n",
|
|
t_malloc_fail);
|
|
t_malloc_fail = 0;
|
|
}
|
|
if (t_malloc_fail_after != 0) {
|
|
t_printv("WARNING: test case left t_malloc_fail_after = %d\n",
|
|
t_malloc_fail_after);
|
|
t_malloc_fail_after = 0;
|
|
}
|
|
if (ret > 0)
|
|
printf("ok %d - %s\n", n, desc ? desc : "no description");
|
|
else if (ret < 0)
|
|
printf("ok %d - # skip\n", n);
|
|
else if (signo == 0)
|
|
printf("not ok %d - %s\n", n, desc ? desc : "no description");
|
|
else
|
|
printf("not ok %d - caught signal %d\n", n, signo);
|
|
if (desc != t->desc)
|
|
free(desc);
|
|
for (i = 0; sigs[i] > 0; ++i)
|
|
signal(sigs[i], SIG_DFL);
|
|
if (leaktest && t_verbose) {
|
|
snaplen = t_malloc_snapshot(snap2, sizeof snap2);
|
|
if (snaplen > sizeof snap2)
|
|
snaplen = sizeof snap2;
|
|
t_compare_mem(snap1, snap2, snaplen);
|
|
if (memcmp(snap1, snap2, snaplen) != 0)
|
|
t_printv("WARNING: allocator state changed\n");
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Print usage string and exit.
|
|
*/
|
|
static void
|
|
usage(void)
|
|
{
|
|
|
|
fprintf(stderr, "usage: %s [-Llv]\n", t_progname);
|
|
exit(1);
|
|
}
|
|
|
|
void
|
|
t_main(t_prepare_func t_prepare, t_cleanup_func t_cleanup,
|
|
int argc, char *argv[])
|
|
{
|
|
unsigned int n, pass, fail;
|
|
size_t nt;
|
|
int opt;
|
|
|
|
/* make all unintentional allocation failures fatal */
|
|
t_malloc_fatal = 1;
|
|
|
|
/* make stdout line-buffered to preserve ordering */
|
|
setvbuf(stdout, NULL, _IOLBF, 0);
|
|
|
|
/* clean up temp files in case of premature exit */
|
|
atexit(t_fcloseall);
|
|
|
|
/* determine our own name, for naming temp files etc. */
|
|
if ((t_progname = strrchr(argv[0], '/')) != NULL)
|
|
t_progname++; /* one past the slash */
|
|
else
|
|
t_progname = argv[0];
|
|
|
|
/* check for leaks, unless doing coverage analysis */
|
|
#if CRYB_COVERAGE
|
|
leaktest = t_str_is_true(getenv("CRYB_LEAKTEST"));
|
|
#else
|
|
leaktest = !t_str_is_false(getenv("CRYB_LEAKTEST"));
|
|
#endif
|
|
|
|
/* parse command line options */
|
|
while ((opt = getopt(argc, argv, "Llv")) != -1)
|
|
switch (opt) {
|
|
case 'L':
|
|
leaktest = 0;
|
|
break;
|
|
case 'l':
|
|
leaktest = 1;
|
|
break;
|
|
case 'v':
|
|
t_verbose = 1;
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
/* prepare the test plan */
|
|
if (t_prepare != NULL)
|
|
t_prepare(argc, argv);
|
|
if (t_plan_len == 0)
|
|
errx(1, "no plan");
|
|
|
|
/* run the tests */
|
|
nt = leaktest ? t_plan_len + 1 : t_plan_len;
|
|
printf("1..%zu\n", nt);
|
|
for (n = pass = fail = 0; n < t_plan_len; ++n)
|
|
t_run_test(t_plan[n], n + 1) ? ++pass : ++fail;
|
|
|
|
/* clean up as much as possible before we exit */
|
|
if (t_cleanup != NULL)
|
|
t_cleanup();
|
|
t_fcloseall();
|
|
for (n = 0; n < t_plan_len; ++n) {
|
|
free(t_plan[n]->desc);
|
|
free(t_plan[n]);
|
|
}
|
|
free(t_plan);
|
|
setvbuf(stdout, NULL, _IONBF, 0);
|
|
if (leaktest) {
|
|
if (t_verbose)
|
|
t_malloc_printstats(stderr);
|
|
t_run_test(&t_memory_leak, nt) ? ++pass : ++fail;
|
|
}
|
|
t_printv("%d out of %zd tests passed\n", pass, nt);
|
|
exit(fail > 0 ? 1 : 0);
|
|
}
|