--- /dev/null
+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
--- /dev/null
+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.
--- /dev/null
+/*
+ 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);
+ }
+}
--- /dev/null
+#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 */
--- /dev/null
+#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 */
--- /dev/null
+#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;
+}
--- /dev/null
+#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 */
--- /dev/null
+/*
+ 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;
+}
+
--- /dev/null
+#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 */
--- /dev/null
+/*
+ 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;
+ }
+}
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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;
+}
+
--- /dev/null
+#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;
+}
--- /dev/null
+#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*/
--- /dev/null
+#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++;
+}
+
+
+
+
--- /dev/null
+#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*/