mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-03 16:09:39 +00:00
0bb6d590cc
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
800 lines
21 KiB
C
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 */
|
|
}
|