|
|
|
/*
|
|
|
|
Check: a unit test framework for C
|
|
|
|
Copyright (C) 2001, Arien Malec
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
|
|
# include <sys/types.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_WAIT_H
|
|
|
|
# include <sys/wait.h>
|
|
|
|
#endif
|
|
|
|
#ifndef WIFSIGNALED
|
|
|
|
# define WIFSIGNALED(stat_val) \
|
|
|
|
(((stat_val) & 255) != 255 && \
|
|
|
|
((stat_val) & 255) != 0)
|
|
|
|
#endif
|
|
|
|
#ifndef WTERMSIG
|
|
|
|
# define WTERMSIG(stat_val) ((stat_val) & 255)
|
|
|
|
#endif
|
|
|
|
#ifndef WIFEXITED
|
|
|
|
# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
|
|
|
|
#endif
|
|
|
|
#ifndef WEXITSTATUS
|
|
|
|
# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
# include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#ifdef STDC_HEADERS
|
|
|
|
# include <stdlib.h>
|
|
|
|
# include <stdarg.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "error.h"
|
|
|
|
#include "list.h"
|
|
|
|
#include "check.h"
|
|
|
|
#include "check_impl.h"
|
|
|
|
#include "check_msg.h"
|
|
|
|
#include "check_log.h"
|
|
|
|
|
|
|
|
#ifndef USE_FORKWAITMSG
|
|
|
|
int nofork_exit_status;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void srunner_run_tcase (SRunner *sr, TCase *tc);
|
|
|
|
static void srunner_add_failure (SRunner *sr, TestResult *tf);
|
|
|
|
static TestResult *tfun_run (int msqid, const char *tcname, TF *tf);
|
|
|
|
static TestResult *receive_result_info (int msqid, int status,
|
|
|
|
const char *tcname,
|
|
|
|
const char *tfname);
|
|
|
|
static void receive_last_loc_info (int msqid, TestResult *tr);
|
|
|
|
static void receive_failure_info (int msqid, int status, TestResult *tr);
|
|
|
|
static List *srunner_resultlst (SRunner *sr);
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef USE_FORKWAITMSG
|
|
|
|
static char *signal_msg (int sig);
|
|
|
|
#endif
|
|
|
|
static char *exit_msg (int exitstatus);
|
|
|
|
static int non_pass (int val);
|
|
|
|
|
|
|
|
|
|
|
|
SRunner *srunner_create (Suite *s)
|
|
|
|
{
|
|
|
|
SRunner *sr = emalloc (sizeof(SRunner)); /* freed in srunner_free */
|
|
|
|
sr->slst = list_create();
|
|
|
|
list_add_end(sr->slst, s);
|
|
|
|
sr->stats = emalloc (sizeof(TestStats)); /* freed in srunner_free */
|
|
|
|
sr->stats->n_checked = sr->stats->n_failed = sr->stats->n_errors = 0;
|
|
|
|
sr->resultlst = list_create();
|
|
|
|
sr->log_fname = NULL;
|
|
|
|
sr->loglst = NULL;
|
|
|
|
return sr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void srunner_add_suite (SRunner *sr, Suite *s)
|
|
|
|
{
|
|
|
|
list_add_end(sr->slst, s);
|
|
|
|
}
|
|
|
|
|
|
|
|
void srunner_free (SRunner *sr)
|
|
|
|
{
|
|
|
|
List *l;
|
|
|
|
TestResult *tr;
|
|
|
|
if (sr == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
free (sr->stats);
|
|
|
|
list_free(sr->slst);
|
|
|
|
|
|
|
|
l = sr->resultlst;
|
|
|
|
for (list_front(l); !list_at_end(l); list_advance(l)) {
|
|
|
|
tr = list_val(l);
|
|
|
|
free(tr->file);
|
|
|
|
free(tr->msg);
|
|
|
|
free(tr);
|
|
|
|
}
|
|
|
|
list_free (sr->resultlst);
|
|
|
|
|
|
|
|
free (sr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void srunner_run_all (SRunner *sr, int print_mode)
|
|
|
|
{
|
|
|
|
List *slst;
|
|
|
|
List *tcl;
|
|
|
|
TCase *tc;
|
|
|
|
if (sr == NULL)
|
|
|
|
return;
|
|
|
|
if (print_mode < 0 || print_mode >= CRLAST)
|
|
|
|
eprintf("Bad print_mode argument to srunner_run_all: %d", print_mode);
|
|
|
|
|
|
|
|
srunner_init_logging (sr, print_mode);
|
|
|
|
|
|
|
|
log_srunner_start (sr);
|
|
|
|
|
|
|
|
slst = sr->slst;
|
|
|
|
|
|
|
|
for (list_front(slst); !list_at_end(slst); list_advance(slst)) {
|
|
|
|
Suite *s = list_val(slst);
|
|
|
|
|
|
|
|
log_suite_start (sr, s);
|
|
|
|
|
|
|
|
tcl = s->tclst;
|
|
|
|
|
|
|
|
for (list_front(tcl);!list_at_end (tcl); list_advance (tcl)) {
|
|
|
|
tc = list_val (tcl);
|
|
|
|
srunner_run_tcase (sr, tc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
log_srunner_end (sr);
|
|
|
|
|
|
|
|
srunner_end_logging (sr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void srunner_add_failure (SRunner *sr, TestResult *tr)
|
|
|
|
{
|
|
|
|
sr->stats->n_checked++;
|
|
|
|
list_add_end (sr->resultlst, tr);
|
|
|
|
switch (tr->rtype) {
|
|
|
|
|
|
|
|
case CRPASS:
|
|
|
|
return;
|
|
|
|
case CRFAILURE:
|
|
|
|
sr->stats->n_failed++;
|
|
|
|
return;
|
|
|
|
case CRERROR:
|
|
|
|
sr->stats->n_errors++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void srunner_run_tcase (SRunner *sr, TCase *tc)
|
|
|
|
{
|
|
|
|
List *tfl;
|
|
|
|
TF *tfun;
|
|
|
|
TestResult *tr;
|
|
|
|
int msqid;
|
|
|
|
|
|
|
|
if (tc->setup)
|
|
|
|
tc->setup();
|
|
|
|
msqid = create_msq();
|
|
|
|
tfl = tc->tflst;
|
|
|
|
|
|
|
|
for (list_front(tfl); !list_at_end (tfl); list_advance (tfl)) {
|
|
|
|
tfun = list_val (tfl);
|
|
|
|
tr = tfun_run (msqid, tc->name, tfun);
|
|
|
|
srunner_add_failure (sr, tr);
|
|
|
|
log_test_end(sr, tr);
|
|
|
|
}
|
|
|
|
delete_msq(msqid);
|
|
|
|
if (tc->teardown)
|
|
|
|
tc->teardown();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void receive_last_loc_info (int msqid, TestResult *tr)
|
|
|
|
{
|
|
|
|
LastLocMsg *lmsg;
|
|
|
|
lmsg = receive_last_loc_msg (msqid);
|
|
|
|
tr->file = last_loc_file (lmsg);
|
|
|
|
tr->line = last_loc_line (lmsg);
|
|
|
|
free (lmsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void receive_failure_info (int msqid, int status, TestResult *tr)
|
|
|
|
{
|
|
|
|
FailureMsg *fmsg;
|
|
|
|
#ifdef USE_FORKWAITMSG
|
|
|
|
if (WIFSIGNALED(status)) {
|
|
|
|
tr->rtype = CRERROR;
|
|
|
|
tr->msg = signal_msg (WTERMSIG(status));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WIFEXITED(status)) {
|
|
|
|
|
|
|
|
if (WEXITSTATUS(status) == 0) {
|
|
|
|
tr->rtype = CRPASS;
|
|
|
|
tr->msg = emalloc(strlen("Test passed") + 1);
|
|
|
|
strcpy (tr->msg, "Test passed");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
fmsg = receive_failure_msg (msqid);
|
|
|
|
if (fmsg == NULL) { /* implies early exit */
|
|
|
|
tr->rtype = CRERROR;
|
|
|
|
tr->msg = exit_msg (WEXITSTATUS(status));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
tr->rtype = CRFAILURE;
|
|
|
|
tr->msg = emalloc(strlen(fmsg->msg) + 1);
|
|
|
|
strcpy (tr->msg, fmsg->msg);
|
|
|
|
free (fmsg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
eprintf ("Bad status from wait() call\n");
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (status == 0) {
|
|
|
|
tr->rtype = CRPASS;
|
|
|
|
tr->msg = emalloc(strlen("Test passed") + 1);
|
|
|
|
strcpy (tr->msg, "Test passed");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fmsg = receive_failure_msg (msqid);
|
|
|
|
if (fmsg == NULL) { /* implies early exit */
|
|
|
|
tr->rtype = CRERROR;
|
|
|
|
tr->msg = exit_msg (status);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
tr->rtype = CRFAILURE;
|
|
|
|
tr->msg = emalloc(strlen(fmsg->msg) + 1);
|
|
|
|
strcpy (tr->msg, fmsg->msg);
|
|
|
|
free (fmsg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static TestResult *receive_result_info (int msqid, int status,
|
|
|
|
const char *tcname, const char *tfname)
|
|
|
|
{
|
|
|
|
TestResult *tr = emalloc (sizeof(TestResult));
|
|
|
|
|
|
|
|
tr->tcname = tcname;
|
|
|
|
tr->tfname = tfname;
|
|
|
|
receive_last_loc_info (msqid, tr);
|
|
|
|
receive_failure_info (msqid, status, tr);
|
|
|
|
return tr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static TestResult *tfun_run (int msqid, const char *tcname, TF *tfun)
|
|
|
|
{
|
|
|
|
#ifdef USE_FORKWAITMSG
|
|
|
|
pid_t pid;
|
|
|
|
#endif
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
#ifdef USE_FORKWAITMSG
|
|
|
|
pid = fork();
|
|
|
|
if (pid == -1)
|
|
|
|
eprintf ("Unable to fork:");
|
|
|
|
if (pid == 0) {
|
|
|
|
tfun->fn(msqid);
|
|
|
|
_exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
(void) wait(&status);
|
|
|
|
#else
|
|
|
|
nofork_exit_status = 0;
|
|
|
|
tfun->fn(msqid);
|
|
|
|
status = nofork_exit_status;
|
|
|
|
#endif
|
|
|
|
return receive_result_info(msqid, status, tcname, tfun->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int srunner_ntests_failed (SRunner *sr)
|
|
|
|
{
|
|
|
|
return sr->stats->n_failed + sr->stats->n_errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
int srunner_ntests_run (SRunner *sr)
|
|
|
|
{
|
|
|
|
return sr->stats->n_checked;
|
|
|
|
}
|
|
|
|
|
|
|
|
TestResult **srunner_failures (SRunner *sr)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
TestResult **trarray;
|
|
|
|
List *rlst;
|
|
|
|
trarray = malloc (sizeof(trarray[0]) * srunner_ntests_failed (sr));
|
|
|
|
|
|
|
|
rlst = srunner_resultlst (sr);
|
|
|
|
for (list_front(rlst); !list_at_end(rlst); list_advance(rlst)) {
|
|
|
|
TestResult *tr = list_val(rlst);
|
|
|
|
if (non_pass(tr->rtype))
|
|
|
|
trarray[i++] = tr;
|
|
|
|
|
|
|
|
}
|
|
|
|
return trarray;
|
|
|
|
}
|
|
|
|
|
|
|
|
TestResult **srunner_results (SRunner *sr)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
TestResult **trarray;
|
|
|
|
List *rlst;
|
|
|
|
|
|
|
|
trarray = malloc (sizeof(trarray[0]) * srunner_ntests_run (sr));
|
|
|
|
|
|
|
|
rlst = srunner_resultlst (sr);
|
|
|
|
for (list_front(rlst); !list_at_end(rlst); list_advance(rlst)) {
|
|
|
|
trarray[i++] = list_val(rlst);
|
|
|
|
}
|
|
|
|
return trarray;
|
|
|
|
}
|
|
|
|
|
|
|
|
static List *srunner_resultlst (SRunner *sr)
|
|
|
|
{
|
|
|
|
return sr->resultlst;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *tr_msg (TestResult *tr)
|
|
|
|
{
|
|
|
|
return tr->msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
int tr_lno (TestResult *tr)
|
|
|
|
{
|
|
|
|
return tr->line;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *tr_lfile (TestResult *tr)
|
|
|
|
{
|
|
|
|
return tr->file;
|
|
|
|
}
|
|
|
|
|
|
|
|
int tr_rtype (TestResult *tr)
|
|
|
|
{
|
|
|
|
return tr->rtype;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *tr_tcname (TestResult *tr)
|
|
|
|
{
|
|
|
|
return tr->tcname;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef USE_FORKWAITMSG
|
|
|
|
static char *signal_msg (int signal)
|
|
|
|
{
|
|
|
|
char *msg = emalloc (CMAXMSG); /* free'd by caller */
|
|
|
|
#ifdef HAVE_SNPRINTF
|
|
|
|
snprintf(msg, CMAXMSG, "Received signal %d", signal);
|
|
|
|
#else
|
|
|
|
sprintf(msg, "Received signal %d", signal);
|
|
|
|
#endif
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static char *exit_msg (int exitval)
|
|
|
|
{
|
|
|
|
char *msg = emalloc(CMAXMSG); /* free'd by caller */
|
|
|
|
#ifdef HAVE_SNPRINTF
|
|
|
|
snprintf(msg, CMAXMSG,
|
|
|
|
"Early exit with return value %d", exitval);
|
|
|
|
#else
|
|
|
|
sprintf(msg, "Early exit with return value %d", exitval);
|
|
|
|
#endif
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int non_pass (int val)
|
|
|
|
{
|
|
|
|
return val == CRFAILURE || val == CRERROR;
|
|
|
|
}
|
|
|
|
|