Initial revision

svn path=/trunk/yasm/; revision=186
0.3
Peter Johnson 24 years ago
parent e649bada33
commit cd3528249d
  1. 77
      check/NEWS
  2. 9
      check/README
  3. 123
      check/check.c
  4. 295
      check/check.h
  5. 92
      check/check_impl.h
  6. 205
      check/check_log.c
  7. 23
      check/check_log.h
  8. 155
      check/check_msg.c
  9. 53
      check/check_msg.h
  10. 106
      check/check_print.c
  11. 27
      check/check_print.h
  12. 333
      check/check_run.c
  13. 60
      check/error.c
  14. 32
      check/error.h
  15. 113
      check/list.c
  16. 51
      check/list.h

@ -0,0 +1,77 @@
Thu Aug 23, 2001:
Released Check 0.7.3
Fixed the Autoconf Lyx check in acinclude.m4 so that configure works
on Solaris systems (and hopefully others), and cleaned up a minor
problem in Debian packaging.
Fri Aug 17, 2001:
Released Check 0.7.2
Automated RPM packaging, and included debian packaging. The makefiles
now has an rpm target (the RPMFLAGS variable can be set to add
additional flags to RPM). Debian packages are built the ordinary way
(dpkg-buildpackage).
Moved the example*.* files to tutorial*.*, since the docs really are
tutorials. Beefed up the tutorial docs to add clarity to the behavior
of fixture setup/teardown (based on a helpful critique by Fred Drake),
and to document the static nature of unit tests demanded by the bug
fix below.
Many bugfixes: added -Wall to the CCFLAGS for gcc, and fixed a mess of
warnings that resulted. Changed a bizarre naming mismatch in
tcase_set_fixture (masked by the lack of compile warnings), and made
unit tests static (both bugfixes suggested by Fred Drake). Also added
a more sophisticated test of Lyx to (hopefully) ensure that Lyx
supports linuxdoc (but it's not clear to me how to test that for
sure).
Wed Jul 30, 2001:
Released Check 0.7.1
Reorganized printing and logging functions to allow for a less
primitive logging function. Logging is now documented in the tutorial
documentation.
Wed Jul 11, 2001:
Released Check 0.7.0
Included a primitive logging function (at the moment, it only prints a
copy of the CRVERBOSE output to the log file), added the ability for
an SRunner to run multiple suites (and reorganized the Check tests to
take advantage of that), and added the magic to allow Check to be used
with C++.
Also added Doxygen markup to the header file, but I'm not terribly
satisfied withe clarity of the output. I may switch to CWEB... Next
release should include API docs and improved logging, if nothing else
comes up...
Wed Jun 27, 2001:
Released Check 0.6.1
Bug fix for srunner_failures (bad version actually returned all
results), added srunner_results to do what srunner_failures used to
do, and added corrected unit tests for both.
Also changed the API for reporting the number of failed tests from
srunner_nfailed to srunner_ntests_failed, to harmonized better with
new function srunner_ntests_run. This unfortunately may break some
unit tests slightly -- that's why the major release number is 0 :-)
Thu Jun 21, 2001:
Released Check 0.6.0
Features improved unit test reporting options, more complete unit
tests, and end-to-end test, and a full API into TestResults
Check 0.5.2
Minor edits
Check 0.5.1
GPL compliance release
Check 0.5.0
Initial public release

@ -0,0 +1,9 @@
Check is a unit test framework for C. It features a simple interface
for defining unit tests, putting little in the way of the
developer. Tests are run in a separate address space, so Check can
catch both assertion failures and code errors that cause segmentation
faults or other signals. The output from unit tests can be used within
source code editors and IDEs.
See http://check.sourceforge.net for more information, including a
tutorial.

@ -0,0 +1,123 @@
/*
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.
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "error.h"
#include "list.h"
#include "check.h"
#include "check_impl.h"
#include "check_msg.h"
Suite *suite_create (char *name)
{
Suite *s;
s = emalloc (sizeof(Suite)); /* freed in suite_free */
if (name == NULL)
s->name = "";
else
s->name = name;
s->tclst = list_create();
return s;
}
void suite_free (Suite *s)
{
List *l;
if (s == NULL)
return;
for (l = s->tclst; !list_at_end(l); list_advance (l)) {
tcase_free (list_val(l));
}
list_free (s->tclst);
free(s);
}
TCase *tcase_create (char *name)
{
TCase *tc = emalloc (sizeof(TCase)); /*freed in tcase_free */
if (name == NULL)
tc->name = "";
else
tc->name = name;
tc->tflst = list_create();
tc->setup = tc->teardown = NULL;
return tc;
}
void tcase_free (TCase *tc)
{
List *l;
l = tc->tflst;
for (list_front(l); !list_at_end(l); list_advance(l)) {
free (list_val(l));
}
list_free(tc->tflst);
free(tc);
}
void suite_add_tcase (Suite *s, TCase *tc)
{
if (s == NULL || tc == NULL)
return;
list_add_end (s->tclst, tc);
}
void _tcase_add_test (TCase *tc, TFun fn, char *name)
{
TF * tf;
if (tc == NULL || fn == NULL || name == NULL)
return;
tf = emalloc (sizeof(TF)); /* freed in tcase_free */
tf->fn = fn;
tf->name = name;
list_add_end (tc->tflst, tf);
}
void tcase_set_fixture (TCase *tc, SFun setup, SFun teardown)
{
tc->setup = setup;
tc->teardown = teardown;
}
void tcase_fn_start (int msqid, char *fname, char *file, int line)
{
send_last_loc_msg (msqid, file, line);
}
void _mark_point (int msqid, char *file, int line)
{
send_last_loc_msg (msqid, file, line);
}
void _fail_unless (int msqid, int result, char *file, int line, char * msg)
{
if (line > MAXLINE)
eprintf ("Line number %d too large to use", line);
send_last_loc_msg (msqid, file, line);
if (!result) {
send_failure_msg (msqid, msg);
exit(1);
}
}

@ -0,0 +1,295 @@
#ifndef CHECK_H
#define CHECK_H
/*
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.
*/
/* Comments are in Doxygen format: see http://www.stack.nl/~dimitri/doxygen/ */
/*! \mainpage Check: a unit test framework for C
\section overview
Overview Check is a unit test framework for C. It features a simple
interface for defining unit tests, putting little in the way of the
developer. Tests are run in a separate address space, so Check can
catch both assertion failures and code errors that cause segmentation
faults or other signals. The output from unit tests can be used within
source code editors and IDEs.
\section quickref Quick Reference
\subsection creating Creating
\par
Unit tests are created with the #START_TEST/#END_TEST macro pair. The
#fail_unless and #fail macros are used for creating checks within unit
tests; the #mark_point macro is useful for trapping the location of
signals and/or early exits.
\subsection managing Managing test cases and suites
\par
Test cases are created with #tcase_create, unit tests are added
with #tcase_add_test
\par
Suites are created with #suite_create, freed with #suite_free; test
cases are added with #suite_add_tcase
\subsection running Running suites
\par
Suites are run through an SRunner, which is created with
#srunner_create, freed with #srunner_free. Additional suites can be
added to an SRunner with #srunner_add_suite.
\par
Use #srunner_run_all to run a suite and print results.
*/
/*! \file check.h */
#ifdef __cplusplus
extern "C" {
#endif
/*! Magic values */
enum {
CMAXMSG = 100 /*!< maximum length of a message, including terminating nul */
};
/*! \defgroup check_core Check Core
Core suite/test case types and functions
@{
*/
/*! \brief opaque type for a test suite */
typedef struct Suite Suite;
/*! \brief opaque type for a test case
A TCase represents a test case. Create with #tcase_create, free with
#tcase_free. For the moment, test cases can only be run through a
suite
*/
typedef struct TCase TCase;
/*! type for a test function */
typedef void (*TFun) (int);
/*! type for a setup/teardown function */
typedef void (*SFun) (void);
/*! Create a test suite */
Suite *suite_create (char *name);
/*! Free a test suite
(For the moment, this also frees all contained test cases) */
void suite_free (Suite *s);
/*! Create a test case */
TCase *tcase_create (char *name);
/*! Free a test case
(Note that as it stands, one will normally free the contaning suite) */
void tcase_free (TCase *tc);
/*! Add a test case to a suite */
void suite_add_tcase (Suite *s, TCase *tc);
/*! Add a test function to a test case
(macro version) */
#define tcase_add_test(tc,tf) _tcase_add_test(tc,tf,"" # tf "")
/*! Add a test function to a test case
(function version -- use this when the macro won't work */
void _tcase_add_test (TCase *tc, TFun tf, char *fname);
/*!
Add fixture setup/teardown functions to a test case Note that
setup/teardown functions are not run in a separate address space, like
test functions, and so must not exit or signal (e.g., segfault)
*/
void tcase_set_fixture(TCase *tc, SFun setup, SFun teardown);
/*! Internal function to mark the start of a test function */
void tcase_fn_start (int msqid, char *fname, char *file, int line);
/*! Start a unit test with START_TEST(unit_name), end with END_TEST
One must use braces within a START_/END_ pair to declare new variables */
#define START_TEST(__testname)\
static void __testname (int __msqid)\
{\
tcase_fn_start (__msqid,""# __testname, __FILE__, __LINE__);
/*! End a unit test */
#define END_TEST }
/*! Fail the test case unless result is true */
#define fail_unless(result,msg) _fail_unless(__msqid,result,__FILE__,__LINE__,msg)
/*! Non macro version of #fail_unless, with more complicated interface */
void _fail_unless (int msqid, int result, char *file, int line, char *msg);
/*! Always fail */
#define fail(msg) _fail_unless(__msqid,0,__FILE__,__LINE__,msg)
/*! Mark the last point reached in a unit test
(useful for tracking down where a segfault, etc. occurs */
#define mark_point() _mark_point(__msqid,__FILE__,__LINE__)
/*! Non macro version of #mark_point */
void _mark_point (int msqid, char *file, int line);
/*! @} */
/*! \defgroup check_run Suite running functions
@{
*/
/*! Result of a test */
enum test_result {
CRPASS, /*!< Test passed*/
CRFAILURE, /*!< Test completed but failed */
CRERROR /*!< Test failed to complete (signal or non-zero early exit) */
};
/*! Specifies the verbosity of srunner printing */
enum print_verbosity {
CRSILENT, /*!< No output */
CRMINIMAL, /*!< Only summary output */
CRNORMAL, /*!< All failed tests */
CRVERBOSE, /*!< All tests */
CRLAST
};
/*! Holds state for a running of a test suite */
typedef struct SRunner SRunner;
/*! Opaque type for a test failure */
typedef struct TestResult TestResult;
/* accessors for tr fields */
/*! Type of result */
int tr_rtype (TestResult *tr);
/*! Failure message */
char *tr_msg (TestResult *tr);
/*! Line number at which failure occured */
int tr_lno (TestResult *tr);
/*! File name at which failure occured */
char *tr_lfile (TestResult *tr);
/*! Test case in which unit test was run */
char *tr_tcname (TestResult *tr);
/*! Creates an SRunner for the given suite */
SRunner *srunner_create (Suite *s);
/*! Adds a Suite to an SRunner */
void srunner_add_suite (SRunner *sr, Suite *s);
/*! Frees an SRunner */
void srunner_free (SRunner *sr);
/* Test running */
/*! Runs an SRunner, printing results as specified
(see enum #print_verbosity)*/
void srunner_run_all (SRunner *sr, int print_mode);
/* Next functions are valid only after the suite has been
completely run, of course */
/*! Number of failed tests in a run suite
Includes failures + errors */
int srunner_ntests_failed (SRunner *sr);
/*! Total number of tests run in a run suite */
int srunner_ntests_run (SRunner *sr);
/*! \brief Return an array of results for all failures
Number of failures is equal to #srunner_nfailed_tests. Memory is
alloc'ed and must be freed, but individual TestResults must not */
TestResult **srunner_failures (SRunner *sr);
/*! \brief Return an array of results for all run tests
Number of failrues is equal to #srunner_ntests_run Memory is alloc'ed
and must be freed, but individual TestResults must not */
TestResult **srunner_results (SRunner *sr);
/* Printing */
/*! Print the results contained in an SRunner
\param sr SRunner for which results are printed
\param print_mode Specification of print verbosity, constrainted to
enum #print_verbosity
*/
void srunner_print (SRunner *sr, int print_mode);
/*! @} */
/*! \defgroup check_log Logging functions
@{
*/
/*! Set a log file to which to write during test running.
Log file setting is an initialize only operation -- it should be done
immediatly after SRunner creation, and the log file can't be changed
after being set.
\param sr The SRunner for which to enable logging
\param fname The file name to which to write the log
*/
void srunner_set_log (SRunner *sr, char *fname);
/*! Does the SRunner have a log file?
\param sr The SRunner to test
\return True if logging, False otherwise
*/
int srunner_has_log (SRunner *sr);
/*! Return the name of the log file, or NULL if none
\param sr The SRunner to query
\return The current log file, or NULL if not logging
*/
char *srunner_log_fname (SRunner *sr);
/*! @} */
#ifdef __cplusplus
}
#endif
#endif /* CHECK_H */

@ -0,0 +1,92 @@
#ifndef CHECK_IMPL_H
#define CHECK_IMPL_H
/*
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.
*/
/* This header should be included by any module that needs
to know the implementation details of the check structures
Include stdio.h & list.h before this header
*/
/* magic values */
enum {
MAXLINE = 9999 /* maximum line no */
};
typedef struct TF {
TFun fn;
char *name;
} TF;
struct Suite {
char *name;
List *tclst; /* List of test cases */
};
struct TCase {
char *name;
List *tflst; /* list of test functions */
SFun setup;
SFun teardown;
};
typedef struct TestStats {
int n_checked;
int n_failed;
int n_errors;
} TestStats;
struct TestResult {
int rtype; /* Type of result */
char *file; /* File where the test occured */
int line; /* Line number where the test occurred */
char *tcname; /* Test case that generated the result */
char *msg; /* Failure message */
};
enum cl_event {
CLSTART_SR,
CLSTART_S,
CLEND_SR,
CLEND_S,
CLEND_T
};
typedef void (*LFun) (SRunner *, FILE*, enum print_verbosity,
void *, enum cl_event);
typedef struct Log {
FILE *lfile;
LFun lfun;
int close;
enum print_verbosity mode;
} Log;
struct SRunner {
List *slst;
TestStats *stats;
List *resultlst;
char *log_fname;
List *loglst;
};
#endif /* CHECK_IMPL_H */

@ -0,0 +1,205 @@
#include <stdlib.h>
#include <stdio.h>
#include <check.h>
#include "list.h"
#include "error.h"
#include "check_impl.h"
#include "check_log.h"
#include "check_print.h"
/*
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.
*/
static void srunner_send_evt (SRunner *sr, void *obj, enum cl_event evt);
void srunner_set_log (SRunner *sr, char *fname)
{
if (sr->log_fname)
return;
sr->log_fname = fname;
}
int srunner_has_log (SRunner *sr)
{
return sr->log_fname != NULL;
}
char *srunner_log_fname (SRunner *sr)
{
return sr->log_fname;
}
void srunner_register_lfun (SRunner *sr, FILE *lfile, int close,
LFun lfun, enum print_verbosity printmode)
{
Log *l = emalloc (sizeof(Log));
l->lfile = lfile;
l->lfun = lfun;
l->close = close;
l->mode = printmode;
list_add_end (sr->loglst, l);
return;
}
void log_srunner_start (SRunner *sr)
{
srunner_send_evt (sr, NULL, CLSTART_SR);
}
void log_srunner_end (SRunner *sr)
{
srunner_send_evt (sr, NULL, CLEND_SR);
}
void log_suite_start (SRunner *sr, Suite *s)
{
srunner_send_evt (sr, s, CLSTART_S);
}
void log_suite_end (SRunner *sr, Suite *s)
{
srunner_send_evt (sr, s, CLEND_S);
}
void log_test_end (SRunner *sr, TestResult *tr)
{
srunner_send_evt (sr, tr, CLEND_T);
}
static void srunner_send_evt (SRunner *sr, void *obj, enum cl_event evt)
{
List *l;
Log *lg;
l = sr->loglst;
for (list_front(l); !list_at_end(l); list_advance(l)) {
lg = list_val(l);
fflush(lg->lfile);
lg->lfun (sr, lg->lfile, lg->mode, obj, evt);
fflush(lg->lfile);
}
}
void stdout_lfun (SRunner *sr, FILE *file, enum print_verbosity printmode,
void *obj, enum cl_event evt)
{
TestResult *tr;
Suite *s;
switch (evt) {
case CLSTART_SR:
if (printmode > CRSILENT) {
fprintf(file, "Running suite(s):");
}
break;
case CLSTART_S:
s = obj;
if (printmode > CRSILENT) {
fprintf(file, " %s", s->name);
}
break;
case CLEND_SR:
if (printmode > CRSILENT) {
fprintf (file, "\n");
srunner_fprint (file, sr, printmode);
}
break;
case CLEND_S:
s = obj;
break;
case CLEND_T:
tr = obj;
break;
default:
eprintf("Bad event type received in stdout_lfun");
}
}
void lfile_lfun (SRunner *sr, FILE *file, enum print_verbosity printmode,
void *obj, enum cl_event evt)
{
TestResult *tr;
Suite *s;
switch (evt) {
case CLSTART_SR:
break;
case CLSTART_S:
s = obj;
fprintf(file, "Running suite %s\n", s->name);
break;
case CLEND_SR:
fprintf (file, "Results for all suites run:\n");
srunner_fprint (file, sr, CRMINIMAL);
break;
case CLEND_S:
s = obj;
break;
case CLEND_T:
tr = obj;
tr_fprint(file, tr, CRVERBOSE);
break;
default:
eprintf("Bad event type received in stdout_lfun");
}
}
FILE *srunner_open_lfile (SRunner *sr)
{
FILE *f = NULL;
if (srunner_has_log (sr)) {
f = fopen(sr->log_fname, "w");
if (f == NULL)
eprintf ("Could not open log file %s:", sr->log_fname);
}
return f;
}
void srunner_init_logging (SRunner *sr, enum print_verbosity print_mode)
{
FILE *f;
sr->loglst = list_create();
srunner_register_lfun (sr, stdout, 0, stdout_lfun, print_mode);
f = srunner_open_lfile (sr);
if (f) {
srunner_register_lfun (sr, f, 1, lfile_lfun, print_mode);
}
}
void srunner_end_logging (SRunner *sr)
{
List *l;
int rval;
l = sr->loglst;
for (list_front(l); !list_at_end(l); list_advance(l)) {
Log *lg = list_val(l);
if (lg->close) {
rval = fclose (lg->lfile);
if (rval != 0)
eprintf ("Error closing log file:");
}
free (lg);
}
list_free(l);
sr->loglst = NULL;
}

@ -0,0 +1,23 @@
#ifndef CHECK_LOG_H
#define CHECK_LOG_H
void log_srunner_start (SRunner *sr);
void log_srunner_end (SRunner *sr);
void log_suite_start (SRunner *sr, Suite *s);
void log_suite_end (SRunner *sr, Suite *s);
void log_test_end (SRunner *sr, TestResult *tr);
void stdout_lfun (SRunner *sr, FILE *file, enum print_verbosity,
void *obj, enum cl_event evt);
void lfile_lfun (SRunner *sr, FILE *file, enum print_verbosity,
void *obj, enum cl_event evt);
void srunner_register_lfun (SRunner *sr, FILE *lfile, int close,
LFun lfun, enum print_verbosity);
FILE *srunner_open_lfile (SRunner *sr);
void srunner_init_logging (SRunner *sr, enum print_verbosity print_mode);
void srunner_end_logging (SRunner *sr);
#endif /* CHECK_LOG_H */

@ -0,0 +1,155 @@
/*
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.
*/
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include "list.h"
#include "error.h"
#include "check.h"
#include "check_impl.h"
#include "check_msg.h"
enum {
LASTLOCMSG = 1,
FAILUREMSG = 2
};
static LastLocMsg *create_last_loc_msg (char *file, int line);
static FailureMsg *create_failure_msg (char *msg);
static FailureMsg *create_failure_msg (char *msg)
{
FailureMsg *m = emalloc (sizeof(FailureMsg));
m->message_type = (long int) FAILUREMSG;
strncpy(m->msg, msg, CMAXMSG);
return m;
}
static LastLocMsg *create_last_loc_msg (char *file, int line)
{
LastLocMsg *m = emalloc (sizeof(LastLocMsg));
m->message_type = (long int) LASTLOCMSG;
snprintf(m->msg, CMAXMSG, "%s\n%d", file, line);
return m;
}
char *last_loc_file (LastLocMsg *msg)
{
int i;
char *rmsg = emalloc (CMAXMSG); /* caller responsible for freeing */
char *mmsg = msg->msg;
if (msg == NULL)
return NULL;
for (i = 0; mmsg[i] != '\n'; i++) {
if (mmsg[i] == '\0')
eprintf ("Badly formated last loc message");
rmsg[i] = mmsg[i];
}
rmsg[i] = '\0';
return rmsg;
}
int last_loc_line (LastLocMsg *msg)
{
char *rmsg;
if (msg == NULL)
return -1;
rmsg = msg->msg;
while (*rmsg != '\n') {
if (*rmsg == '\0')
eprintf ("Badly formated last loc message");
rmsg++;
}
rmsg++; /*advance past \n */
return atoi (rmsg);
}
void send_last_loc_msg (int msqid, char * file, int line)
{
int rval;
LastLocMsg *rmsg = create_last_loc_msg(file, line);
rval = msgsnd(msqid, (void *) rmsg, CMAXMSG, IPC_NOWAIT);
if (rval == -1) {
eprintf ("send_last_loc_msg:Failed to send message, msqid = %d:",msqid);
}
free(rmsg);
}
int create_msq (void) {
int msqid;
msqid = msgget((key_t) 1, 0666 | IPC_CREAT);
if (msqid == -1)
eprintf ("Unable to create message queue:");
return msqid;
}
void delete_msq (int msqid)
{
if (msgctl (msqid, IPC_RMID, NULL) == -1)
eprintf ("Failed to free message queue:");
}
void send_failure_msg (int msqid, char *msg)
{
int rval;
FailureMsg *rmsg = create_failure_msg(msg);
rval = msgsnd(msqid, (void *) rmsg, CMAXMSG, IPC_NOWAIT);
if (rval == -1)
eprintf ("send_failure_msg:Failed to send message:");
free(rmsg);
}
LastLocMsg *receive_last_loc_msg (int msqid)
{
LastLocMsg *rmsg = emalloc(sizeof(LastLocMsg)); /* caller responsible for freeing */
while (1) {
int rval;
rval = msgrcv(msqid, (void *) rmsg, CMAXMSG, LASTLOCMSG, IPC_NOWAIT);
if (rval == -1) {
if (errno == ENOMSG)
break;
eprintf ("receive_last_loc_msg:Failed to receive message:");
}
}
return rmsg;
}
FailureMsg *receive_failure_msg (int msqid)
{
FailureMsg *rmsg = emalloc(sizeof(FailureMsg));
int rval;
rval = msgrcv(msqid, (void *) rmsg, CMAXMSG, FAILUREMSG, IPC_NOWAIT);
if (rval == -1) {
if (errno == ENOMSG)
return NULL;
eprintf ("receive_failure_msg:Failed to receive message:");
}
return rmsg;
}

@ -0,0 +1,53 @@
#ifndef CHECK_MSG_H
#define CHECK_MSG_H
/*
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.
*/
/* Functions implementing messaging during test runs */
/* check.h must be included before this header */
typedef struct LastLocMsg {
long int message_type;
char msg[CMAXMSG]; /* Format: filename\nlineno\0 */
} LastLocMsg;
typedef struct FailureMsg {
long int message_type;
char msg[CMAXMSG];
} FailureMsg;
int create_msq (void);
void delete_msq (int msqid);
void send_failure_msg (int msqid, char *msg);
void send_last_loc_msg (int msqid, char * file, int line);
/* malloc'd return value which caller is responsible for
freeing in each of the next two functions */
FailureMsg *receive_failure_msg (int msqid);
LastLocMsg *receive_last_loc_msg (int msqid);
/* file name contained in the LastLocMsg */
/* return value is malloc'd, caller responsible for freeing */
char *last_loc_file(LastLocMsg *msg);
int last_loc_line(LastLocMsg *msg);
#endif /*CHECK_MSG_H */

@ -0,0 +1,106 @@
/*
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.
*/
#include <stdio.h>
#include <check.h>
#include "list.h"
#include "check_impl.h"
#include "check_print.h"
#include "error.h"
static void srunner_fprint_summary (FILE *file, SRunner *sr, int print_mode);
static void srunner_fprint_results (FILE *file, SRunner *sr, int print_mode);
static int percent_passed (TestStats *t);
static char *rtype_to_string (int rtype);
void srunner_print (SRunner *sr, int print_mode)
{
srunner_fprint (stdout, sr, print_mode);
}
void srunner_fprint (FILE *file, SRunner *sr, int print_mode)
{
srunner_fprint_summary (file, sr, print_mode);
srunner_fprint_results (file, sr, print_mode);
}
static void srunner_fprint_summary (FILE *file, SRunner *sr, int print_mode)
{
TestStats *ts = sr->stats;
if (print_mode >= CRMINIMAL) {
fprintf (file, "%d%%: Checks: %d, Failures: %d, Errors: %d\n",
percent_passed (ts), ts->n_checked, ts->n_failed,
ts->n_errors);
}
return;
}
static void srunner_fprint_results (FILE *file, SRunner *sr, int print_mode)
{
List *resultlst;
resultlst = sr->resultlst;
for (list_front(resultlst); !list_at_end(resultlst); list_advance(resultlst)) {
TestResult *tr = list_val(resultlst);
tr_fprint (file, tr, print_mode);
}
return;
}
void tr_fprint (FILE *file, TestResult *tr, int print_mode)
{
char *exact_msg;
exact_msg = (tr->rtype == CRERROR) ? "(after this point) ": "";
if ((print_mode >= CRVERBOSE && tr->rtype == CRPASS) ||
(tr->rtype != CRPASS && print_mode >= CRNORMAL)) {
fprintf (file, "%s:%d:%s:%s: %s%s\n",
tr->file, tr->line,
rtype_to_string(tr->rtype), tr->tcname,
exact_msg, tr->msg);
}
}
static int percent_passed (TestStats *t)
{
if (t->n_failed == 0 && t->n_errors == 0)
return 100;
else
return (int) ( (float) (t->n_checked - (t->n_failed + t->n_errors)) /
(float) t->n_checked * 100);
}
static char *rtype_to_string (int rtype)
{
switch (rtype) {
case CRPASS:
return "P";
break;
case CRFAILURE:
return "F";
break;
case CRERROR:
return "E";
break;
default:
eprintf("Bad argument %d to rtype_to_string", rtype);
return NULL;
}
}

@ -0,0 +1,27 @@
/*
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.
*/
#ifndef CHECK_PRINT_H
#define CHECK_PRINT_H
void tr_fprint (FILE *file, TestResult *tr, int print_mode);
void srunner_fprint (FILE *file, SRunner *sr, int print_mode);
#endif /* CHECK_PRINT_H */

@ -0,0 +1,333 @@
/*
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.
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include "error.h"
#include "list.h"
#include "check.h"
#include "check_impl.h"
#include "check_msg.h"
#include "check_log.h"
static void srunner_run_tcase (SRunner *sr, TCase *tc);
static void srunner_add_failure (SRunner *sr, TestResult *tf);
static TestResult *tfun_run (int msqid, char *tcname, TF *tf);
static TestResult *receive_result_info (int msqid, int status, char *tcname);
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);
static char *signal_msg (int sig);
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);
if (tr->rtype == CRFAILURE || tr->rtype == CRERROR)
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;
if (WIFSIGNALED(status)) {
tr->rtype = CRERROR;
tr->msg = signal_msg (WTERMSIG(status));
return;
}
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == 0) {
tr->rtype = CRPASS;
/* TODO: It would be cleaner to strdup this &
not special case the free...*/
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");
}
}
static TestResult *receive_result_info (int msqid, int status, char *tcname)
{
TestResult *tr = emalloc (sizeof(TestResult));
tr->tcname = tcname;
receive_last_loc_info (msqid, tr);
receive_failure_info (msqid, status, tr);
return tr;
}
static TestResult *tfun_run (int msqid, char *tcname, TF *tfun)
{
pid_t pid;
int status = 0;
pid = fork();
if (pid == -1)
eprintf ("Unable to fork:");
if (pid == 0) {
tfun->fn(msqid);
_exit(EXIT_SUCCESS);
}
(void) wait(&status);
return receive_result_info(msqid, status, tcname);
}
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;
}
char *tr_tcname (TestResult *tr)
{
return tr->tcname;
}
static char *signal_msg (int signal)
{
char *msg = emalloc (CMAXMSG); /* free'd by caller */
snprintf(msg, CMAXMSG, "Received signal %d", signal);
return msg;
}
static char *exit_msg (int exitval)
{
char *msg = emalloc(CMAXMSG); /* free'd by caller */
snprintf(msg, CMAXMSG,
"Early exit with return value %d", exitval);
return msg;
}
static int non_pass (int val)
{
return val == CRFAILURE || val == CRERROR;
}

@ -0,0 +1,60 @@
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "error.h"
/*
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.
*/
void eprintf (char *fmt, ...)
{
va_list args;
fflush(stdout);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
/*include system error information if format ends in colon */
if (fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':')
fprintf(stderr, " %s", strerror(errno));
fprintf(stderr, "\n");
exit(2);
}
void *emalloc (size_t n)
{
void *p;
p = malloc(n);
if (p == NULL)
eprintf("malloc of %u bytes failed:", n);
return p;
}
void *erealloc (void * ptr, size_t n)
{
void *p;
p = realloc (ptr, n);
if (p == NULL)
eprintf("realloc of %u bytes failed:", n);
return p;
}

@ -0,0 +1,32 @@
#ifndef ERROR_H
#define ERROR_H
/*
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.
*/
/* Include stdlib.h beforehand */
/* Print error message and die
If fmt ends in colon, include system error information */
void eprintf (char *fmt, ...);
/* malloc or die */
void *emalloc(size_t n);
void *erealloc(void *, size_t n);
#endif /*ERROR_H*/

@ -0,0 +1,113 @@
#include <stdlib.h>
#include "list.h"
#include "error.h"
/*
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.
*/
enum {
LINIT = 1,
LGROW = 2
};
struct List {
int n_elts;
int max_elts;
int current; /* pointer to the current node */
int last; /* pointer to the node before END */
void **data;
};
static void maybe_grow (List *lp)
{
if (lp->n_elts >= lp->max_elts) {
lp->max_elts *= LGROW;
lp->data = erealloc (lp->data, lp->max_elts * sizeof(lp->data[0]));
}
}
List *list_create (void)
{
List *lp;
lp = emalloc (sizeof(List));
lp->n_elts = 0;
lp->max_elts = LINIT;
lp->data = emalloc(sizeof(lp->data[0]) * LINIT);
lp->current = lp->last = -1;
return lp;
}
void list_add_end (List *lp, void *val)
{
if (lp == NULL)
return;
maybe_grow(lp);
lp->last++;
lp->n_elts++;
lp->current = lp->last;
lp->data[lp->current] = val;
}
int list_at_end (List *lp)
{
if (lp->current == -1)
return 1;
else
return (lp->current > lp->last);
}
void list_front (List *lp)
{
if (lp->current == -1)
return;
lp->current = 0;
}
void list_free (List *lp)
{
if (lp == NULL)
return;
free(lp->data);
free (lp);
}
void *list_val (List *lp)
{
if (lp == NULL)
return NULL;
if (lp->current == -1 || lp->current > lp->last)
return NULL;
return lp->data[lp->current];
}
void list_advance (List *lp)
{
if (lp == NULL)
return;
if (list_at_end(lp))
return;
lp->current++;
}

@ -0,0 +1,51 @@
#ifndef LIST_H
#define LIST_H
/*
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.
*/
typedef struct List List;
/* Create an empty list */
List * list_create (void);
/* Is list at end? */
int list_at_end (List * lp);
/* Position list at front */
void list_front(List *lp);
/* Add a value to the end of the list,
positioning newly added value as current value */
void list_add_end (List *lp, void *val);
/* Give the value of the current node */
void *list_val (List * lp);
/* Position the list at the next node */
void list_advance (List * lp);
/* Free a list, but don't free values */
void list_free (List * lp);
/* Free a list, freeing values using a freeing function */
/* void list_vfree (List * lp, void (*fp) (void *)); */
#endif /*LIST_H*/
Loading…
Cancel
Save