gstreamer/libs/gst/check/libcheck/check_run.c
Nirbheek Chauhan 0bb6d590cc libcheck: port to latest check git
Upstream seems to have stopped doing releases, but we need to update for better
Windows and Visual Studio support.

This patch only updates the libcheck sources and ignores the compatibility
sources for now.

https://bugzilla.gnome.org/show_bug.cgi?id=775870
2016-12-09 15:31:01 +05:30

800 lines
21 KiB
C

/*
* Check: a unit test framework for C
* Copyright (C) 2001, 2002 Arien Malec
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include "libcompat/libcompat.h"
#include <sys/types.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <signal.h>
#include <setjmp.h>
#include "internal-check.h"
#include "check_error.h"
#include "check_list.h"
#include "check_impl.h"
#include "check_msg.h"
#include "check_log.h"
enum rinfo
{
CK_R_SIG,
CK_R_PASS,
CK_R_EXIT,
CK_R_FAIL_TEST,
CK_R_FAIL_FIXTURE
};
enum tf_type
{
CK_FORK_TEST,
CK_NOFORK_TEST,
CK_NOFORK_FIXTURE
};
/* all functions are defined in the same order they are declared.
functions that depend on forking are gathered all together.
non-static functions are at the end of the file. */
static void srunner_run_init (SRunner * sr, enum print_output print_mode);
static void srunner_run_end (SRunner * sr, enum print_output print_mode);
static void srunner_iterate_suites (SRunner * sr,
const char *sname, const char *tcname,
const char *include_tags,
const char *exclude_tags, enum print_output print_mode);
static void srunner_iterate_tcase_tfuns (SRunner * sr, TCase * tc);
static void srunner_add_failure (SRunner * sr, TestResult * tf);
static TestResult *srunner_run_setup (List * func_list,
enum fork_status fork_usage, const char *test_name, const char *setup_name);
static int srunner_run_unchecked_setup (SRunner * sr, TCase * tc);
static TestResult *tcase_run_checked_setup (SRunner * sr, TCase * tc);
static void srunner_run_teardown (List * fixture_list,
enum fork_status fork_usage);
static void srunner_run_unchecked_teardown (SRunner * sr, TCase * tc);
static void tcase_run_checked_teardown (TCase * tc);
static void srunner_run_tcase (SRunner * sr, TCase * tc);
static TestResult *tcase_run_tfun_nofork (SRunner * sr, TCase * tc, TF * tf,
int i);
static TestResult *receive_result_info_nofork (const char *tcname,
const char *tname, int iter, int duration);
static void set_nofork_info (TestResult * tr);
static char *pass_msg (void);
#if defined(HAVE_FORK) && HAVE_FORK==1
static TestResult *tcase_run_tfun_fork (SRunner * sr, TCase * tc, TF * tf,
int i);
static TestResult *receive_result_info_fork (const char *tcname,
const char *tname, int iter,
int status, int expected_signal, signed char allowed_exit_value);
static void set_fork_info (TestResult * tr, int status, int expected_signal,
signed char allowed_exit_value);
static char *signal_msg (int sig);
static char *signal_error_msg (int signal_received, int signal_expected);
static char *exit_msg (int exitstatus);
static int waserror (int status, int expected_signal);
static int alarm_received;
static pid_t group_pid;
static struct sigaction sigint_old_action;
static struct sigaction sigterm_old_action;
static void CK_ATTRIBUTE_UNUSED
sig_handler (int sig_nr)
{
switch (sig_nr) {
case SIGALRM:
alarm_received = 1;
killpg (group_pid, SIGKILL);
break;
case SIGTERM:
case SIGINT:
{
pid_t own_group_pid;
int child_sig = SIGTERM;
if (sig_nr == SIGINT) {
child_sig = SIGKILL;
sigaction (SIGINT, &sigint_old_action, NULL);
} else {
sigaction (SIGTERM, &sigterm_old_action, NULL);
}
killpg (group_pid, child_sig);
/* POSIX says that calling killpg(0)
* does not necessarily mean to call it on the callers
* group pid! */
own_group_pid = getpgrp ();
killpg (own_group_pid, sig_nr);
break;
}
default:
eprintf ("Unhandled signal: %d", __FILE__, __LINE__, sig_nr);
break;
}
}
#endif /* HAVE_FORK */
#define MSG_LEN 100
static void
srunner_run_init (SRunner * sr, enum print_output print_mode)
{
set_fork_status (srunner_fork_status (sr));
setup_messaging ();
srunner_init_logging (sr, print_mode);
log_srunner_start (sr);
}
static void
srunner_run_end (SRunner * sr, enum print_output CK_ATTRIBUTE_UNUSED print_mode)
{
log_srunner_end (sr);
srunner_end_logging (sr);
teardown_messaging ();
set_fork_status (CK_FORK);
}
static void
srunner_iterate_suites (SRunner * sr,
const char *sname, const char *tcname,
const char *include_tags,
const char *exclude_tags, enum print_output CK_ATTRIBUTE_UNUSED print_mode)
{
List *include_tag_lst;
List *exclude_tag_lst;
List *slst;
List *tcl;
TCase *tc;
slst = sr->slst;
include_tag_lst = tag_string_to_list (include_tags);
exclude_tag_lst = tag_string_to_list (exclude_tags);
for (check_list_front (slst); !check_list_at_end (slst);
check_list_advance (slst)) {
Suite *s = (Suite *) check_list_val (slst);
if (((sname != NULL) && (strcmp (sname, s->name) != 0))
|| ((tcname != NULL) && (!suite_tcase (s, tcname))))
continue;
log_suite_start (sr, s);
tcl = s->tclst;
for (check_list_front (tcl); !check_list_at_end (tcl);
check_list_advance (tcl)) {
tc = (TCase *) check_list_val (tcl);
if ((tcname != NULL) && (strcmp (tcname, tc->name) != 0)) {
continue;
}
if (include_tags != NULL) {
if (!tcase_matching_tag (tc, include_tag_lst)) {
continue;
}
}
if (exclude_tags != NULL) {
if (tcase_matching_tag (tc, exclude_tag_lst)) {
continue;
}
}
srunner_run_tcase (sr, tc);
}
log_suite_end (sr, s);
}
check_list_apply (include_tag_lst, free);
check_list_apply (exclude_tag_lst, free);
check_list_free (include_tag_lst);
check_list_free (exclude_tag_lst);
}
static void
srunner_iterate_tcase_tfuns (SRunner * sr, TCase * tc)
{
List *tfl;
TF *tfun;
TestResult *tr = NULL;
tfl = tc->tflst;
for (check_list_front (tfl); !check_list_at_end (tfl);
check_list_advance (tfl)) {
int i;
tfun = (TF *) check_list_val (tfl);
for (i = tfun->loop_start; i < tfun->loop_end; i++) {
log_test_start (sr, tc, tfun);
switch (srunner_fork_status (sr)) {
case CK_FORK:
#if defined(HAVE_FORK) && HAVE_FORK==1
tr = tcase_run_tfun_fork (sr, tc, tfun, i);
#else /* HAVE_FORK */
eprintf ("This version does not support fork", __FILE__, __LINE__);
#endif /* HAVE_FORK */
break;
case CK_NOFORK:
tr = tcase_run_tfun_nofork (sr, tc, tfun, i);
break;
case CK_FORK_GETENV:
default:
eprintf ("Bad fork status in SRunner", __FILE__, __LINE__);
}
if (NULL != tr) {
srunner_add_failure (sr, tr);
log_test_end (sr, tr);
}
}
}
}
static void
srunner_add_failure (SRunner * sr, TestResult * tr)
{
check_list_add_end (sr->resultlst, tr);
sr->stats->n_checked++; /* count checks during setup, test, and teardown */
if (tr->rtype == CK_FAILURE)
sr->stats->n_failed++;
else if (tr->rtype == CK_ERROR)
sr->stats->n_errors++;
}
static TestResult *
srunner_run_setup (List * fixture_list, enum fork_status fork_usage,
const char *test_name, const char *setup_name)
{
TestResult *tr = NULL;
Fixture *setup_fixture;
if (fork_usage == CK_FORK) {
send_ctx_info (CK_CTX_SETUP);
}
for (check_list_front (fixture_list); !check_list_at_end (fixture_list);
check_list_advance (fixture_list)) {
setup_fixture = (Fixture *) check_list_val (fixture_list);
if (fork_usage == CK_NOFORK) {
send_ctx_info (CK_CTX_SETUP);
if (0 == setjmp (error_jmp_buffer)) {
setup_fixture->fun ();
}
/* Stop the setup and return the failure in nofork mode. */
tr = receive_result_info_nofork (test_name, setup_name, 0, -1);
if (tr->rtype != CK_PASS) {
break;
}
free (tr->file);
free (tr->msg);
free (tr);
tr = NULL;
} else {
setup_fixture->fun ();
}
}
return tr;
}
static int
srunner_run_unchecked_setup (SRunner * sr, TCase * tc)
{
TestResult *tr = NULL;
int rval = 1;
set_fork_status (CK_NOFORK);
tr = srunner_run_setup (tc->unch_sflst, CK_NOFORK, tc->name,
"unchecked_setup");
set_fork_status (srunner_fork_status (sr));
if (tr != NULL && tr->rtype != CK_PASS) {
srunner_add_failure (sr, tr);
rval = 0;
}
return rval;
}
static TestResult *
tcase_run_checked_setup (SRunner * sr, TCase * tc)
{
TestResult *tr = srunner_run_setup (tc->ch_sflst, srunner_fork_status (sr),
tc->name, "checked_setup");
return tr;
}
static void
srunner_run_teardown (List * fixture_list, enum fork_status fork_usage)
{
Fixture *fixture;
for (check_list_front (fixture_list); !check_list_at_end (fixture_list);
check_list_advance (fixture_list)) {
fixture = (Fixture *) check_list_val (fixture_list);
send_ctx_info (CK_CTX_TEARDOWN);
if (fork_usage == CK_NOFORK) {
if (0 == setjmp (error_jmp_buffer)) {
fixture->fun ();
} else {
/* Abort the remaining teardowns */
break;
}
} else {
fixture->fun ();
}
}
}
static void
srunner_run_unchecked_teardown (SRunner * sr, TCase * tc)
{
srunner_run_teardown (tc->unch_tflst, srunner_fork_status (sr));
}
static void
tcase_run_checked_teardown (TCase * tc)
{
srunner_run_teardown (tc->ch_tflst, CK_NOFORK);
}
static void
srunner_run_tcase (SRunner * sr, TCase * tc)
{
if (srunner_run_unchecked_setup (sr, tc)) {
srunner_iterate_tcase_tfuns (sr, tc);
srunner_run_unchecked_teardown (sr, tc);
}
}
static TestResult *
tcase_run_tfun_nofork (SRunner * sr, TCase * tc, TF * tfun, int i)
{
TestResult *tr;
struct timespec ts_start = { 0, 0 }, ts_end = {
0, 0};
tr = tcase_run_checked_setup (sr, tc);
if (tr == NULL) {
clock_gettime (check_get_clockid (), &ts_start);
if (0 == setjmp (error_jmp_buffer)) {
tfun->fn (i);
}
clock_gettime (check_get_clockid (), &ts_end);
tcase_run_checked_teardown (tc);
return receive_result_info_nofork (tc->name, tfun->name, i,
DIFF_IN_USEC (ts_start, ts_end));
}
return tr;
}
static TestResult *
receive_result_info_nofork (const char *tcname,
const char *tname, int iter, int duration)
{
TestResult *tr;
tr = receive_test_result (0);
if (tr == NULL) {
eprintf ("Failed to receive test result", __FILE__, __LINE__);
} else {
tr->tcname = tcname;
tr->tname = tname;
tr->iter = iter;
tr->duration = duration;
set_nofork_info (tr);
}
return tr;
}
static void
set_nofork_info (TestResult * tr)
{
if (tr->msg == NULL) {
tr->rtype = CK_PASS;
tr->msg = pass_msg ();
} else {
tr->rtype = CK_FAILURE;
}
}
static char *
pass_msg (void)
{
return strdup ("Passed");
}
#if defined(HAVE_FORK) && HAVE_FORK==1
static TestResult *
tcase_run_tfun_fork (SRunner * sr, TCase * tc, TF * tfun, int i)
{
pid_t pid_w;
pid_t pid;
int status = 0;
struct timespec ts_start = { 0, 0 }, ts_end = {
0, 0};
timer_t timerid;
struct itimerspec timer_spec;
TestResult *tr;
pid = fork ();
if (pid == -1)
eprintf ("Error in call to fork:", __FILE__, __LINE__ - 2);
if (pid == 0) {
setpgid (0, 0);
group_pid = getpgrp ();
tr = tcase_run_checked_setup (sr, tc);
free (tr);
clock_gettime (check_get_clockid (), &ts_start);
tfun->fn (i);
clock_gettime (check_get_clockid (), &ts_end);
tcase_run_checked_teardown (tc);
send_duration_info (DIFF_IN_USEC (ts_start, ts_end));
exit (EXIT_SUCCESS);
} else {
group_pid = pid;
}
alarm_received = 0;
if (timer_create (check_get_clockid (),
NULL /* fire SIGALRM if timer expires */ ,
&timerid) == 0) {
/* Set the timer to fire once */
timer_spec.it_value = tc->timeout;
timer_spec.it_interval.tv_sec = 0;
timer_spec.it_interval.tv_nsec = 0;
if (timer_settime (timerid, 0, &timer_spec, NULL) == 0) {
do {
pid_w = waitpid (pid, &status, 0);
}
while (pid_w == -1);
} else {
eprintf ("Error in call to timer_settime:", __FILE__, __LINE__);
}
/* If the timer has not fired, disable it */
timer_delete (timerid);
} else {
eprintf ("Error in call to timer_create:", __FILE__, __LINE__);
}
killpg (pid, SIGKILL); /* Kill remaining processes. */
return receive_result_info_fork (tc->name, tfun->name, i, status,
tfun->signal, tfun->allowed_exit_value);
}
static TestResult *
receive_result_info_fork (const char *tcname,
const char *tname,
int iter, int status, int expected_signal, signed char allowed_exit_value)
{
TestResult *tr;
tr = receive_test_result (waserror (status, expected_signal));
if (tr == NULL) {
eprintf ("Failed to receive test result", __FILE__, __LINE__);
} else {
tr->tcname = tcname;
tr->tname = tname;
tr->iter = iter;
set_fork_info (tr, status, expected_signal, allowed_exit_value);
}
return tr;
}
static void
set_fork_info (TestResult * tr, int status, int signal_expected,
signed char allowed_exit_value)
{
int was_sig = WIFSIGNALED (status);
int was_exit = WIFEXITED (status);
signed char exit_status = WEXITSTATUS (status);
int signal_received = WTERMSIG (status);
if (was_sig) {
if (signal_expected == signal_received) {
if (alarm_received) {
/* Got alarm instead of signal */
tr->rtype = CK_ERROR;
if (tr->msg != NULL) {
free (tr->msg);
}
tr->msg = signal_error_msg (signal_received, signal_expected);
} else {
tr->rtype = CK_PASS;
if (tr->msg != NULL) {
free (tr->msg);
}
tr->msg = pass_msg ();
}
} else if (signal_expected != 0) {
/* signal received, but not the expected one */
tr->rtype = CK_ERROR;
if (tr->msg != NULL) {
free (tr->msg);
}
tr->msg = signal_error_msg (signal_received, signal_expected);
} else {
/* signal received and none expected */
tr->rtype = CK_ERROR;
if (tr->msg != NULL) {
free (tr->msg);
}
tr->msg = signal_msg (signal_received);
}
} else if (signal_expected == 0) {
if (was_exit && exit_status == allowed_exit_value) {
tr->rtype = CK_PASS;
if (tr->msg != NULL) {
free (tr->msg);
}
tr->msg = pass_msg ();
} else if (was_exit && exit_status != allowed_exit_value) {
if (tr->msg == NULL) { /* early exit */
tr->rtype = CK_ERROR;
tr->msg = exit_msg (exit_status);
} else {
tr->rtype = CK_FAILURE;
}
}
} else { /* a signal was expected and none raised */
if (was_exit) {
if (tr->msg != NULL) {
free (tr->msg);
}
tr->msg = exit_msg (exit_status);
if (exit_status == allowed_exit_value) {
tr->rtype = CK_FAILURE; /* normal exit status */
} else {
tr->rtype = CK_FAILURE; /* early exit */
}
}
}
}
static char *
signal_msg (int signal)
{
char *msg = (char *) emalloc (MSG_LEN); /* free'd by caller */
if (alarm_received) {
snprintf (msg, MSG_LEN, "Test timeout expired");
} else {
snprintf (msg, MSG_LEN, "Received signal %d (%s)",
signal, strsignal (signal));
}
return msg;
}
static char *
signal_error_msg (int signal_received, int signal_expected)
{
char *sig_r_str;
char *sig_e_str;
char *msg = (char *) emalloc (MSG_LEN); /* free'd by caller */
sig_r_str = strdup (strsignal (signal_received));
sig_e_str = strdup (strsignal (signal_expected));
if (alarm_received) {
snprintf (msg, MSG_LEN,
"Test timeout expired, expected signal %d (%s)",
signal_expected, sig_e_str);
} else {
snprintf (msg, MSG_LEN, "Received signal %d (%s), expected %d (%s)",
signal_received, sig_r_str, signal_expected, sig_e_str);
}
free (sig_r_str);
free (sig_e_str);
return msg;
}
static char *
exit_msg (int exitval)
{
char *msg = (char *) emalloc (MSG_LEN); /* free'd by caller */
snprintf (msg, MSG_LEN, "Early exit with return value %d", exitval);
return msg;
}
static int
waserror (int status, int signal_expected)
{
int was_sig = WIFSIGNALED (status);
int was_exit = WIFEXITED (status);
int exit_status = WEXITSTATUS (status);
int signal_received = WTERMSIG (status);
return ((was_sig && (signal_received != signal_expected)) ||
(was_exit && exit_status != 0));
}
#endif /* HAVE_FORK */
enum fork_status
srunner_fork_status (SRunner * sr)
{
if (sr->fstat == CK_FORK_GETENV) {
char *env = getenv ("CK_FORK");
if (env == NULL)
#if defined(HAVE_FORK) && HAVE_FORK==1
return CK_FORK;
#else
return CK_NOFORK;
#endif
if (strcmp (env, "no") == 0)
return CK_NOFORK;
else {
#if defined(HAVE_FORK) && HAVE_FORK==1
return CK_FORK;
#else /* HAVE_FORK */
eprintf ("This version does not support fork", __FILE__, __LINE__);
/* Ignoring, as Check is not compiled with fork support. */
return CK_NOFORK;
#endif /* HAVE_FORK */
}
} else
return sr->fstat;
}
void
srunner_set_fork_status (SRunner * sr, enum fork_status fstat)
{
#if !defined(HAVE_FORK) || HAVE_FORK==0
/* If fork() is unavailable, do not allow a fork mode to be set */
if (fstat != CK_NOFORK) {
eprintf ("This version does not support fork", __FILE__, __LINE__);
/* Overriding, as Check is not compiled with fork support. */
fstat = CK_NOFORK;
}
#endif /* ! HAVE_FORK */
sr->fstat = fstat;
}
void
srunner_run_all (SRunner * sr, enum print_output print_mode)
{
srunner_run (sr, NULL, /* All test suites. */
NULL, /* All test cases. */
print_mode);
}
void
srunner_run_tagged (SRunner * sr, const char *sname, const char *tcname,
const char *include_tags, const char *exclude_tags,
enum print_output print_mode)
{
#if defined(HAVE_SIGACTION) && defined(HAVE_FORK)
static struct sigaction sigalarm_old_action;
static struct sigaction sigalarm_new_action;
static struct sigaction sigint_new_action;
static struct sigaction sigterm_new_action;
#endif /* HAVE_SIGACTION && HAVE_FORK */
/* Get the selected test suite and test case from the
environment. */
if (!tcname)
tcname = getenv ("CK_RUN_CASE");
if (!sname)
sname = getenv ("CK_RUN_SUITE");
if (!include_tags)
include_tags = getenv ("CK_INCLUDE_TAGS");
if (!exclude_tags)
exclude_tags = getenv ("CK_EXCLUDE_TAGS");
if (sr == NULL)
return;
if (print_mode >= CK_LAST) {
eprintf ("Bad print_mode argument to srunner_run_all: %d",
__FILE__, __LINE__, print_mode);
}
#if defined(HAVE_SIGACTION) && defined(HAVE_FORK)
memset (&sigalarm_new_action, 0, sizeof (sigalarm_new_action));
sigalarm_new_action.sa_handler = sig_handler;
sigaction (SIGALRM, &sigalarm_new_action, &sigalarm_old_action);
memset (&sigint_new_action, 0, sizeof (sigint_new_action));
sigint_new_action.sa_handler = sig_handler;
sigaction (SIGINT, &sigint_new_action, &sigint_old_action);
memset (&sigterm_new_action, 0, sizeof (sigterm_new_action));
sigterm_new_action.sa_handler = sig_handler;
sigaction (SIGTERM, &sigterm_new_action, &sigterm_old_action);
#endif /* HAVE_SIGACTION && HAVE_FORK */
srunner_run_init (sr, print_mode);
srunner_iterate_suites (sr, sname, tcname, include_tags, exclude_tags,
print_mode);
srunner_run_end (sr, print_mode);
#if defined(HAVE_SIGACTION) && defined(HAVE_FORK)
sigaction (SIGALRM, &sigalarm_old_action, NULL);
sigaction (SIGINT, &sigint_old_action, NULL);
sigaction (SIGTERM, &sigterm_old_action, NULL);
#endif /* HAVE_SIGACTION && HAVE_FORK */
}
void
srunner_run (SRunner * sr, const char *sname, const char *tcname,
enum print_output print_mode)
{
srunner_run_tagged (sr, sname, tcname, NULL, NULL, print_mode);
}
pid_t
check_fork (void)
{
#if defined(HAVE_FORK) && HAVE_FORK==1
pid_t pid = fork ();
/* Set the process to a process group to be able to kill it easily. */
if (pid >= 0) {
setpgid (pid, group_pid);
}
return pid;
#else /* HAVE_FORK */
eprintf ("This version does not support fork", __FILE__, __LINE__);
return 0;
#endif /* HAVE_FORK */
}
void
check_waitpid_and_exit (pid_t pid CK_ATTRIBUTE_UNUSED)
{
#if defined(HAVE_FORK) && HAVE_FORK==1
pid_t pid_w;
int status;
if (pid > 0) {
do {
pid_w = waitpid (pid, &status, 0);
}
while (pid_w == -1);
if (waserror (status, 0)) {
exit (EXIT_FAILURE);
}
}
exit (EXIT_SUCCESS);
#else /* HAVE_FORK */
eprintf ("This version does not support fork", __FILE__, __LINE__);
/* Ignoring, as Check is not compiled with fork support. */
exit (EXIT_FAILURE);
#endif /* HAVE_FORK */
}