diff options
author | Tim-Philipp Müller <tim.muller@collabora.co.uk> | 2009-07-16 18:39:16 +0100 |
---|---|---|
committer | Tim-Philipp Müller <tim.muller@collabora.co.uk> | 2009-08-06 14:26:32 +0100 |
commit | a209779f1cab5ddf6b5096e4c654801428308d5e (patch) | |
tree | 219d088069aa9822f75659a6873b61f9e62fdfbc | |
parent | 459c2b9c79fafdfca85ca54be248124f9d9a1869 (diff) |
check: add internal copy of check-0.9.6
Not hooked up yet. See #577275.
-rw-r--r-- | libs/gst/check/libcheck/Makefile.am | 38 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check.c | 444 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check.h.in | 407 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_error.c | 67 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_error.h | 33 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_impl.h | 115 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_list.c | 141 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_list.h | 56 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_log.c | 323 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_log.h | 47 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_msg.c | 201 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_msg.h | 36 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_pack.c | 417 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_pack.h | 76 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_print.c | 156 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_print.h | 30 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_run.c | 548 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_str.c | 118 | ||||
-rw-r--r-- | libs/gst/check/libcheck/check_str.h | 36 |
19 files changed, 3289 insertions, 0 deletions
diff --git a/libs/gst/check/libcheck/Makefile.am b/libs/gst/check/libcheck/Makefile.am new file mode 100644 index 000000000..d522f33af --- /dev/null +++ b/libs/gst/check/libcheck/Makefile.am @@ -0,0 +1,38 @@ +noinst_LTLIBRARIES = libcheckinternal.la + +EXTRA_DIST = check.h.in + +CFILES =\ + check.c \ + check_error.c \ + check_list.c \ + check_log.c \ + check_msg.c \ + check_pack.c \ + check_print.c \ + check_run.c \ + check_str.c + +HFILES =\ + check.h \ + check_error.h \ + check_impl.h \ + check_list.h \ + check_log.h \ + check_msg.h \ + check_pack.h \ + check_print.h \ + check_str.h + +noinst_HEADERS = $(HFILES) + +EXPORT_SYM = exported.sym +$(EXPORT_SYM): check.h.in + sed -n -e 's/^..*CK_EXPORT[[:space:]][[:space:]]*\([[:alnum:]_][[:alnum:]_]*\)..*$$/\1/p' @top_srcdir@/src/check.h.in > $@ + +libcheckinternal_la_SOURCES = $(CFILES) $(HFILES) +libcheckinternal_la_LIBADD = + + +CLEANFILES = $(EXPORT_SYM) + diff --git a/libs/gst/check/libcheck/check.c b/libs/gst/check/libcheck/check.c new file mode 100644 index 000000000..b15971551 --- /dev/null +++ b/libs/gst/check/libcheck/check.c @@ -0,0 +1,444 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +#include "check.h" +#include "check_error.h" +#include "check_list.h" +#include "check_impl.h" +#include "check_msg.h" + +#ifndef DEFAULT_TIMEOUT +#define DEFAULT_TIMEOUT 4 +#endif + +int check_major_version = CHECK_MAJOR_VERSION; +int check_minor_version = CHECK_MINOR_VERSION; +int check_micro_version = CHECK_MICRO_VERSION; + +static int non_pass (int val); +static Fixture *fixture_create (SFun fun, int ischecked); +static void tcase_add_fixture (TCase *tc, SFun setup, SFun teardown, + int ischecked); +static void tr_init (TestResult *tr); +static void suite_free (Suite *s); +static void tcase_free (TCase *tc); + +#if HAVE_CONFIG_H +#include <config.h> +#endif +#undef malloc +#undef realloc +#undef strsignal + +#include <sys/types.h> + +void *malloc (size_t n); +void *realloc (void *p, size_t n); +char *strsignal(int sig); + +void *rpl_malloc (size_t n); +void *rpl_realloc (void *p, size_t n); +static const char *rpl_strsignal(int sig); + +/* Allocate an N-byte block of memory from the heap. If N is zero, + allocate a 1-byte block. */ +void * +rpl_malloc (size_t n) +{ + if (n == 0) + n = 1; + return malloc (n); +} + +/* AC_FUNC_REALLOC in configure defines realloc to rpl_realloc if + realloc(0,0) is NULL to make it GNU compatible and always return a + valid pointer, same for AC_FUNC_MALLOC, malloc, and rpl_malloc. + rpl means `replacement'. + + If this ever turns out to be a problem, it might be easiest to just + kill the configure macro calls. + */ +void * +rpl_realloc (void *p, size_t n) +{ + if (n == 0) + n = 1; + if (p == 0) + return malloc (n); + return realloc (p, n); +} + +/* We simply don't have strsignal on some platforms. This function + should get used if AC_REPLACE_FUNCS([strsignal]) cannot find + something acceptable. Note that Gnulib has a much much much more + advanced version of strsignal, but we don't really care. +*/ +static const char * +rpl_strsignal (int sig) +{ + static char signame[40]; + + sprintf(signame, "SIG #%d", sig); + return signame; +} + +Suite *suite_create (const char *name) +{ + Suite *s; + s = emalloc (sizeof(Suite)); /* freed in suite_free */ + if (name == NULL) + s->name = ""; + else + s->name = name; + s->tclst = check_list_create(); + return s; +} + +static void suite_free (Suite *s) +{ + List *l; + if (s == NULL) + return; + l = s->tclst; + for (list_front(l); !list_at_end(l); list_advance (l)) { + tcase_free (list_val(l)); + } + list_free (s->tclst); + free(s); +} + +TCase *tcase_create (const char *name) +{ + char *env; + int timeout = DEFAULT_TIMEOUT; + TCase *tc = emalloc (sizeof(TCase)); /*freed in tcase_free */ + if (name == NULL) + tc->name = ""; + else + tc->name = name; + + env = getenv("CK_DEFAULT_TIMEOUT"); + if (env != NULL) { + int tmp = atoi(env); + if (tmp >= 0) { + timeout = tmp; + } + } + + tc->timeout = timeout; + tc->tflst = check_list_create(); + tc->unch_sflst = check_list_create(); + tc->ch_sflst = check_list_create(); + tc->unch_tflst = check_list_create(); + tc->ch_tflst = check_list_create(); + + return tc; +} + + +static void tcase_free (TCase *tc) +{ + list_apply (tc->tflst, free); + list_apply (tc->unch_sflst, free); + list_apply (tc->ch_sflst, free); + list_apply (tc->unch_tflst, free); + list_apply (tc->ch_tflst, free); + list_free(tc->tflst); + list_free(tc->unch_sflst); + list_free(tc->ch_sflst); + list_free(tc->unch_tflst); + list_free(tc->ch_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, const char *name, int _signal, int start, int end) +{ + TF * tf; + if (tc == NULL || fn == NULL || name == NULL) + return; + tf = emalloc (sizeof(TF)); /* freed in tcase_free */ + tf->fn = fn; + tf->loop_start = start; + tf->loop_end = end; + tf->signal = _signal; /* 0 means no signal expected */ + tf->name = name; + list_add_end (tc->tflst, tf); +} + +static Fixture *fixture_create (SFun fun, int ischecked) +{ + Fixture *f; + f = emalloc (sizeof(Fixture)); + f->fun = fun; + f->ischecked = ischecked; + + return f; +} + +void tcase_add_unchecked_fixture (TCase *tc, SFun setup, SFun teardown) +{ + tcase_add_fixture(tc,setup,teardown,0); +} + +void tcase_add_checked_fixture (TCase *tc, SFun setup, SFun teardown) +{ + tcase_add_fixture (tc,setup,teardown,1); +} + +static void tcase_add_fixture (TCase *tc, SFun setup, SFun teardown, + int ischecked) +{ + if (setup) { + if (ischecked) + list_add_end (tc->ch_sflst, fixture_create(setup, ischecked)); + else + list_add_end (tc->unch_sflst, fixture_create(setup, ischecked)); + } + + /* Add teardowns at front so they are run in reverse order. */ + if (teardown) { + if (ischecked) + list_add_front (tc->ch_tflst, fixture_create(teardown, ischecked)); + else + list_add_front (tc->unch_tflst, fixture_create(teardown, ischecked)); + } +} + +void tcase_set_timeout (TCase *tc, int timeout) +{ + if (timeout >= 0) + tc->timeout = timeout; +} + +void tcase_fn_start (const char *fname, const char *file, int line) +{ + send_ctx_info (CK_CTX_TEST); + send_loc_info (file, line); +} + +void _mark_point (const char *file, int line) +{ + send_loc_info (file, line); +} + +void _fail_unless (int result, const char *file, + int line, const char *expr, ...) +{ + const char *msg; + + send_loc_info (file, line); + if (!result) { + va_list ap; + char buf[BUFSIZ]; + + va_start(ap,expr); + msg = (const char*)va_arg(ap, char *); + if (msg == NULL) + msg = expr; + vsnprintf(buf, BUFSIZ, msg, ap); + va_end(ap); + send_failure_info (buf); + if (cur_fork_status() == CK_FORK) + exit(1); + } +} + +SRunner *srunner_create (Suite *s) +{ + SRunner *sr = emalloc (sizeof(SRunner)); /* freed in srunner_free */ + sr->slst = check_list_create(); + if (s != NULL) + 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 = check_list_create(); + sr->log_fname = NULL; + sr->xml_fname = NULL; + sr->loglst = NULL; + sr->fstat = CK_FORK_GETENV; + return sr; +} + +void srunner_add_suite (SRunner *sr, Suite *s) +{ + if (s == NULL) + return; + + list_add_end(sr->slst, s); +} + +void srunner_free (SRunner *sr) +{ + List *l; + TestResult *tr; + if (sr == NULL) + return; + + free (sr->stats); + l = sr->slst; + for (list_front(l); !list_at_end(l); list_advance(l)) { + suite_free(list_val(l)); + } + list_free(sr->slst); + + l = sr->resultlst; + for (list_front(l); !list_at_end(l); list_advance(l)) { + tr = list_val(l); + free(tr->file); + free(tr->msg); + free(tr); + } + list_free (sr->resultlst); + + free (sr); +} + +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 = sr->resultlst; + 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 = sr->resultlst; + for (list_front(rlst); !list_at_end(rlst); list_advance(rlst)) { + trarray[i++] = list_val(rlst); + } + return trarray; +} + +static int non_pass (int val) +{ + return val != CK_PASS; +} + +TestResult *tr_create(void) +{ + TestResult *tr; + + tr = emalloc (sizeof(TestResult)); + tr_init (tr); + return tr; +} + +void tr_reset(TestResult *tr) +{ + tr_init(tr); +} + +static void tr_init (TestResult *tr) +{ + tr->ctx = CK_CTX_INVALID; + tr->line = -1; + tr->rtype = CK_TEST_RESULT_INVALID; + tr->msg = NULL; + tr->file = NULL; + tr->tcname = NULL; + tr->tname = NULL; +} + + +const char *tr_msg (TestResult *tr) +{ + return tr->msg; +} + +int tr_lno (TestResult *tr) +{ + return tr->line; +} + +const char *tr_lfile (TestResult *tr) +{ + return tr->file; +} + +int tr_rtype (TestResult *tr) +{ + return tr->rtype; +} + +enum ck_result_ctx tr_ctx (TestResult *tr) +{ + return tr->ctx; +} + +const char *tr_tcname (TestResult *tr) +{ + return tr->tcname; +} + +static int _fstat = CK_FORK; + +void set_fork_status (enum fork_status fstat) +{ + if (fstat == CK_FORK || fstat == CK_NOFORK || fstat == CK_FORK_GETENV) + _fstat = fstat; + else + eprintf ("Bad status in set_fork_status", __FILE__, __LINE__); +} + +enum fork_status cur_fork_status (void) +{ + return _fstat; +} diff --git a/libs/gst/check/libcheck/check.h.in b/libs/gst/check/libcheck/check.h.in new file mode 100644 index 000000000..57f4a5b4e --- /dev/null +++ b/libs/gst/check/libcheck/check.h.in @@ -0,0 +1,407 @@ +/*-*- mode: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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef CHECK_H +#define CHECK_H + +#include <stddef.h> +#include <string.h> + +/* Check: a unit test framework for C + + 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. + + 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. + + + Test cases are created with tcase_create, unit tests are added + with tcase_add_test + + + Suites are created with suite_create; test cases are added + with suite_add_tcase + + Suites are run through an SRunner, which is created with + srunner_create. Additional suites can be added to an SRunner with + srunner_add_suite. An SRunner is freed with srunner_free, which also + frees all suites added to the runner. + + Use srunner_run_all to run a suite and print results. + + Macros and functions starting with _ (underscore) are internal and + may change without notice. You have been warned!. + +*/ + + +#ifdef __cplusplus +#define CK_CPPSTART extern "C" { +#define CK_CPPEND } +CK_CPPSTART +#endif + +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +#define GCC_VERSION_AT_LEAST(major, minor) \ +((__GNUC__ > (major)) || \ + (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) +#else +#define GCC_VERSION_AT_LEAST(major, minor) 0 +#endif + +#if GCC_VERSION_AT_LEAST(2,95) +#define CK_ATTRIBUTE_UNUSED __attribute__ ((unused)) +#else +#define CK_ATTRIBUTE_UNUSED +#endif /* GCC 2.95 */ + +#include <sys/types.h> + +/* Used to create the linker script for hiding lib-local symbols. Shall + be put directly in front of the exported symbol. */ +#define CK_EXPORT + +/* check version numbers */ + +#define CHECK_MAJOR_VERSION (@CHECK_MAJOR_VERSION@) +#define CHECK_MINOR_VERSION (@CHECK_MINOR_VERSION@) +#define CHECK_MICRO_VERSION (@CHECK_MICRO_VERSION@) + +extern int CK_EXPORT check_major_version; +extern int CK_EXPORT check_minor_version; +extern int CK_EXPORT check_micro_version; + +#ifndef NULL +#define NULL ((void*)0) +#endif + +/* 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); + +/* Opaque type for a test suite */ +typedef struct Suite Suite; + +/* Creates a test suite with the given name */ +Suite * CK_EXPORT suite_create (const char *name); + +/* Add a test case to a suite */ +void CK_EXPORT suite_add_tcase (Suite *s, TCase *tc); + +/* Create a test case */ +TCase * CK_EXPORT tcase_create (const char *name); + +/* Add a test function to a test case (macro version) */ +#define tcase_add_test(tc,tf) tcase_add_test_raise_signal(tc,tf,0) + +/* Add a test function with signal handling to a test case (macro version) */ +#define tcase_add_test_raise_signal(tc,tf,signal) \ + _tcase_add_test((tc),(tf),"" # tf "",(signal), 0, 1) + +/* Add a looping test function to a test case (macro version) + + The test will be called in a for(i = s; i < e; i++) loop with each + iteration being executed in a new context. The loop variable 'i' is + available in the test. + */ +#define tcase_add_loop_test(tc,tf,s,e) \ + _tcase_add_test((tc),(tf),"" # tf "",0,(s),(e)) + +/* Signal version of loop test. + FIXME: add a test case; this is untested as part of Check's tests. + */ +#define tcase_add_loop_test_raise_signal(tc,tf,signal,s,e) \ + _tcase_add_test((tc),(tf),"" # tf "",(signal),(s),(e)) + +/* Add a test function to a test case + (function version -- use this when the macro won't work +*/ +void CK_EXPORT _tcase_add_test (TCase *tc, TFun tf, const char *fname, int _signal, int start, int end); + +/* Add unchecked fixture setup/teardown functions to a test case + + If unchecked fixture functions are run at the start and end of the + test case, and not before and after unit tests. Note that unchecked + setup/teardown functions are not run in a separate address space, + like test functions, and so must not exit or signal (e.g., + segfault) + + Also, when run in CK_NOFORK mode, unchecked fixture functions may + lead to different unit test behavior IF unit tests change data + setup by the fixture functions. +*/ +void CK_EXPORT tcase_add_unchecked_fixture (TCase *tc, SFun setup, SFun teardown); + +/* Add fixture setup/teardown functions to a test case + + Checked fixture functions are run before and after unit + tests. Unlike unchecked fixture functions, checked fixture + functions are run in the same separate address space as the test + program, and thus the test function will survive signals or + unexpected exits in the fixture function. Also, IF the setup + function is idempotent, unit test behavior will be the same in + CK_FORK and CK_NOFORK modes. + + However, since fixture functions are run before and after each unit + test, they should not be expensive code. + +*/ +void CK_EXPORT tcase_add_checked_fixture (TCase *tc, SFun setup, SFun teardown); + +/* Set the timeout for all tests in a test case. A test that lasts longer + than the timeout (in seconds) will be killed and thus fail with an error. + The timeout can also be set globaly with the environment variable + CK_DEFAULT_TIMEOUT, the specific setting always takes precedence. +*/ +void CK_EXPORT tcase_set_timeout (TCase *tc, int timeout); + +/* Internal function to mark the start of a test function */ +void CK_EXPORT tcase_fn_start (const char *fname, const 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 CK_ATTRIBUTE_UNUSED _i)\ +{\ + tcase_fn_start (""# __testname, __FILE__, __LINE__); + +/* End a unit test */ +#define END_TEST } + +/* Fail the test case unless expr is true */ +/* The space before the comma sign before ## is essential to be compatible + with gcc 2.95.3 and earlier. +*/ +#define fail_unless(expr, ...)\ + _fail_unless(expr, __FILE__, __LINE__,\ + "Assertion '"#expr"' failed" , ## __VA_ARGS__, NULL) + +/* Fail the test case if expr is true */ +/* The space before the comma sign before ## is essential to be compatible + with gcc 2.95.3 and earlier. +*/ + +/* FIXME: these macros may conflict with C89 if expr is + FIXME: strcmp (str1, str2) due to excessive string length. */ +#define fail_if(expr, ...)\ + _fail_unless(!(expr), __FILE__, __LINE__,\ + "Failure '"#expr"' occured" , ## __VA_ARGS__, NULL) + +/* Always fail */ +#define fail(...) _fail_unless(0, __FILE__, __LINE__, "Failed" , ## __VA_ARGS__, NULL) + +/* Non macro version of #fail_unless, with more complicated interface */ +void CK_EXPORT _fail_unless (int result, const char *file, + int line, const char *expr, ...); + +/* New check fail API. */ +#define ck_abort() ck_abort_msg(NULL) +#define ck_abort_msg fail +#define ck_assert(C) ck_assert_msg(C, NULL) +#define ck_assert_msg fail_unless + +/* Integer comparsion macros with improved output compared to fail_unless(). */ +/* O may be any comparion operator. */ +#define _ck_assert_int(X, O, Y) ck_assert_msg((X) O (Y), "Assertion '"#X#O#Y"' failed: "#X"==%d, "#Y"==%d", X, Y) +#define ck_assert_int_eq(X, Y) _ck_assert_int(X, ==, Y) +#define ck_assert_int_ne(X, Y) _ck_assert_int(X, !=, Y) + +/* String comparsion macros with improved output compared to fail_unless() */ +#define _ck_assert_str(C, X, O, Y) ck_assert_msg(C, "Assertion '"#X#O#Y"' failed: "#X"==\"%s\", "#Y"==\"%s\"", X, Y) +#define ck_assert_str_eq(X, Y) _ck_assert_str(!strcmp(X, Y), X, ==, Y) +#define ck_assert_str_ne(X, Y) _ck_assert_str(strcmp(X, Y), X, !=, Y) + + +/* Mark the last point reached in a unit test + (useful for tracking down where a segfault, etc. occurs) +*/ +#define mark_point() _mark_point(__FILE__,__LINE__) + +/* Non macro version of #mark_point */ +void CK_EXPORT _mark_point (const char *file, int line); + +/* Result of a test */ +enum test_result { + CK_TEST_RESULT_INVALID, /* Default value; should not encounter this */ + CK_PASS, /* Test passed*/ + CK_FAILURE, /* Test completed but failed */ + CK_ERROR /* Test failed to complete + (unexpected signal or non-zero early exit) */ +}; + +/* Specifies the how much output an SRunner should produce */ +enum print_output { + CK_SILENT, /* No output */ + CK_MINIMAL, /* Only summary output */ + CK_NORMAL, /* All failed tests */ + CK_VERBOSE, /* All tests */ + CK_ENV, /* Look at environment var */ + CK_LAST +}; + +/* 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 */ +enum ck_result_ctx { + CK_CTX_INVALID, /* Default value; should not encounter this */ + CK_CTX_SETUP, + CK_CTX_TEST, + CK_CTX_TEARDOWN +}; + +/* Type of result */ +int CK_EXPORT tr_rtype (TestResult *tr); +/* Context in which the result occurred */ +enum ck_result_ctx CK_EXPORT tr_ctx (TestResult *tr); +/* Failure message */ +const char * CK_EXPORT tr_msg (TestResult *tr); +/* Line number at which failure occured */ +int CK_EXPORT tr_lno (TestResult *tr); +/* File name at which failure occured */ +const char * CK_EXPORT tr_lfile (TestResult *tr); +/* Test case in which unit test was run */ +const char * CK_EXPORT tr_tcname (TestResult *tr); + +/* Creates an SRunner for the given suite */ +SRunner * CK_EXPORT srunner_create (Suite *s); + +/* Adds a Suite to an SRunner */ +void CK_EXPORT srunner_add_suite (SRunner *sr, Suite *s); + +/* Frees an SRunner, all suites added to it and all contained test cases */ +void CK_EXPORT srunner_free (SRunner *sr); + + +/* Test running */ + +/* Runs an SRunner, printing results as specified (see enum print_output) */ +void CK_EXPORT srunner_run_all (SRunner *sr, enum print_output 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 CK_EXPORT srunner_ntests_failed (SRunner *sr); + +/* Total number of tests run in a run suite */ +int CK_EXPORT srunner_ntests_run (SRunner *sr); + +/* Return an array of results for all failures + + Number of failures is equal to srunner_nfailed_tests. Memory for + the array is malloc'ed and must be freed, but individual TestResults + must not +*/ +TestResult ** CK_EXPORT srunner_failures (SRunner *sr); + +/* Return an array of results for all run tests + + Number of results is equal to srunner_ntests_run, and excludes + failures due to setup function failure. + + Memory is malloc'ed and must be freed, but individual TestResults + must not +*/ +TestResult ** CK_EXPORT srunner_results (SRunner *sr); + + +/* Printing */ + +/* Print the results contained in an SRunner */ +void CK_EXPORT srunner_print (SRunner *sr, enum print_output print_mode); + + +/* 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. +*/ +void CK_EXPORT srunner_set_log (SRunner *sr, const char *fname); + +/* Does the SRunner have a log file? */ +int CK_EXPORT srunner_has_log (SRunner *sr); + +/* Return the name of the log file, or NULL if none */ +const char * CK_EXPORT srunner_log_fname (SRunner *sr); + +/* Set a xml file to which to write during test running. + + XML file setting is an initialize only operation -- it should be + done immediatly after SRunner creation, and the XML file can't be + changed after being set. +*/ +void CK_EXPORT srunner_set_xml (SRunner *sr, const char *fname); + +/* Does the SRunner have an XML log file? */ +int CK_EXPORT srunner_has_xml (SRunner *sr); + +/* Return the name of the XML file, or NULL if none */ +const char * CK_EXPORT srunner_xml_fname (SRunner *sr); + + +/* Control forking */ +enum fork_status { + CK_FORK_GETENV, /* look in the environment for CK_FORK */ + CK_FORK, /* call fork to run tests */ + CK_NOFORK /* don't call fork */ +}; + +/* Get the current fork status */ +enum fork_status CK_EXPORT srunner_fork_status (SRunner *sr); + +/* Set the current fork status */ +void CK_EXPORT srunner_set_fork_status (SRunner *sr, enum fork_status fstat); + +/* Fork in a test and make sure messaging and tests work. */ +pid_t CK_EXPORT check_fork(void); + +/* Wait for the pid and exit. If pid is zero, just exit. */ +void CK_EXPORT check_waitpid_and_exit(pid_t pid); + +#ifdef __cplusplus +CK_CPPEND +#endif + +#endif /* CHECK_H */ diff --git a/libs/gst/check/libcheck/check_error.c b/libs/gst/check/libcheck/check_error.c new file mode 100644 index 000000000..5da8fb671 --- /dev/null +++ b/libs/gst/check/libcheck/check_error.c @@ -0,0 +1,67 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include "check_error.h" + + +/* FIXME: including a colon at the end is a bad way to indicate an error */ +void eprintf (const char *fmt, const char *file, int line, ...) +{ + va_list args; + fflush(stderr); + + fprintf(stderr,"%s:%d: ",file,line); + va_start(args, line); + 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:", __FILE__, __LINE__ - 2, 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:", __FILE__, __LINE__ - 2, n); + return p; +} diff --git a/libs/gst/check/libcheck/check_error.h b/libs/gst/check/libcheck/check_error.h new file mode 100644 index 000000000..2e1e211be --- /dev/null +++ b/libs/gst/check/libcheck/check_error.h @@ -0,0 +1,33 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef ERROR_H +#define ERROR_H + +/* Include stdlib.h beforehand */ + +/* Print error message and die + If fmt ends in colon, include system error information */ +void eprintf (const char *fmt, const char *file, int line,...); +/* malloc or die */ +void *emalloc(size_t n); +void *erealloc(void *, size_t n); + +#endif /*ERROR_H*/ diff --git a/libs/gst/check/libcheck/check_impl.h b/libs/gst/check/libcheck/check_impl.h new file mode 100644 index 000000000..c8293b56f --- /dev/null +++ b/libs/gst/check/libcheck/check_impl.h @@ -0,0 +1,115 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef CHECK_IMPL_H +#define CHECK_IMPL_H + + +/* 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 +*/ + +typedef struct TF { + TFun fn; + int loop_start; + int loop_end; + const char *name; + int signal; +} TF; + +struct Suite { + const char *name; + List *tclst; /* List of test cases */ +}; + +typedef struct Fixture +{ + int ischecked; + SFun fun; +} Fixture; + +struct TCase { + const char *name; + int timeout; + List *tflst; /* list of test functions */ + List *unch_sflst; + List *unch_tflst; + List *ch_sflst; + List *ch_tflst; +}; + +typedef struct TestStats { + int n_checked; + int n_failed; + int n_errors; +} TestStats; + +struct TestResult { + enum test_result rtype; /* Type of result */ + enum ck_result_ctx ctx; /* When the result occurred */ + char *file; /* File where the test occured */ + int line; /* Line number where the test occurred */ + int iter; /* The iteration value for looping tests */ + const char *tcname; /* Test case that generated the result */ + const char *tname; /* Test that generated the result */ + char *msg; /* Failure message */ +}; + +TestResult *tr_create(void); +void tr_reset(TestResult *tr); + +enum cl_event { + CLINITLOG_SR, + CLENDLOG_SR, + CLSTART_SR, + CLSTART_S, + CLEND_SR, + CLEND_S, + CLEND_T +}; + +typedef void (*LFun) (SRunner *, FILE*, enum print_output, + void *, enum cl_event); + +typedef struct Log { + FILE *lfile; + LFun lfun; + int close; + enum print_output mode; +} Log; + +struct SRunner { + List *slst; /* List of Suite objects */ + TestStats *stats; /* Run statistics */ + List *resultlst; /* List of unit test results */ + const char *log_fname; /* name of log file */ + const char *xml_fname; /* name of xml output file */ + List *loglst; /* list of Log objects */ + enum fork_status fstat; /* controls if suites are forked or not + NOTE: Don't use this value directly, + instead use srunner_fork_status */ +}; + + +void set_fork_status(enum fork_status fstat); +enum fork_status cur_fork_status (void); + +#endif /* CHECK_IMPL_H */ diff --git a/libs/gst/check/libcheck/check_list.c b/libs/gst/check/libcheck/check_list.c new file mode 100644 index 000000000..30cee01a6 --- /dev/null +++ b/libs/gst/check/libcheck/check_list.c @@ -0,0 +1,141 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> + +#include "check_list.h" +#include "check_error.h" + + +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 */ + const 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 *check_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_front (List *lp, const void *val) +{ + if (lp == NULL) + return; + maybe_grow(lp); + memmove(lp->data + 1, lp->data, lp->n_elts * sizeof lp->data[0]); + lp->last++; + lp->n_elts++; + lp->current = 0; + lp->data[lp->current] = val; +} + +void list_add_end (List *lp, const 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 (void*) lp->data[lp->current]; +} + +void list_advance (List *lp) +{ + if (lp == NULL) + return; + if (list_at_end(lp)) + return; + lp->current++; +} + + +void list_apply (List *lp, void (*fp) (void *)) +{ + if (lp == NULL || fp == NULL) + return; + + for (list_front(lp); !list_at_end(lp); list_advance(lp)) + fp (list_val(lp)); + +} + + + diff --git a/libs/gst/check/libcheck/check_list.h b/libs/gst/check/libcheck/check_list.h new file mode 100644 index 000000000..e40f4f821 --- /dev/null +++ b/libs/gst/check/libcheck/check_list.h @@ -0,0 +1,56 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef CHECK_LIST_H +#define CHECK_LIST_H + +typedef struct List List; + +/* Create an empty list */ +List * check_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 front of the list, + positioning newly added value as current value. + More expensive than list_add_end, as it uses memmove. */ +void list_add_front (List *lp, const void *val); + +/* Add a value to the end of the list, + positioning newly added value as current value */ +void list_add_end (List *lp, const 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); + +void list_apply (List *lp, void (*fp) (void *)); + + +#endif /* CHECK_LIST_H */ diff --git a/libs/gst/check/libcheck/check_log.c b/libs/gst/check/libcheck/check_log.c new file mode 100644 index 000000000..2cb9a77f1 --- /dev/null +++ b/libs/gst/check/libcheck/check_log.c @@ -0,0 +1,323 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <sys/time.h> +#include <time.h> +#include <check.h> + +#include "check_error.h" +#include "check_list.h" +#include "check_impl.h" +#include "check_log.h" +#include "check_print.h" + + +static void srunner_send_evt (SRunner *sr, void *obj, enum cl_event evt); + +void srunner_set_log (SRunner *sr, const char *fname) +{ + if (sr->log_fname) + return; + sr->log_fname = fname; +} + +int srunner_has_log (SRunner *sr) +{ + return sr->log_fname != NULL; +} + +const char *srunner_log_fname (SRunner *sr) +{ + return sr->log_fname; +} + + +void srunner_set_xml (SRunner *sr, const char *fname) +{ + if (sr->xml_fname) + return; + sr->xml_fname = fname; +} + +int srunner_has_xml (SRunner *sr) +{ + return sr->xml_fname != NULL; +} + +const char *srunner_xml_fname (SRunner *sr) +{ + return sr->xml_fname; +} + +void srunner_register_lfun (SRunner *sr, FILE *lfile, int close, + LFun lfun, enum print_output printmode) +{ + Log *l = emalloc (sizeof(Log)); + + if (printmode == CK_ENV) { + printmode = get_env_printmode(); + } + + 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_output printmode, + void *obj, enum cl_event evt) +{ + TestResult *tr; + Suite *s; + + if (printmode == CK_ENV) { + printmode = get_env_printmode(); + } + + switch (evt) { + case CLINITLOG_SR: + break; + case CLENDLOG_SR: + break; + case CLSTART_SR: + if (printmode > CK_SILENT) { + fprintf(file, "Running suite(s):"); + } + break; + case CLSTART_S: + s = obj; + if (printmode > CK_SILENT) { + fprintf(file, " %s\n", s->name); + } + break; + case CLEND_SR: + if (printmode > CK_SILENT) { + /* we don't want a newline before printing here, newlines should + come after printing a string, not before. it's better to add + the newline above in CLSTART_S. + */ + 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", __FILE__, __LINE__); + } + + +} + +void lfile_lfun (SRunner *sr, FILE *file, enum print_output printmode, + void *obj, enum cl_event evt) +{ + TestResult *tr; + Suite *s; + + switch (evt) { + case CLINITLOG_SR: + break; + case CLENDLOG_SR: + break; + 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, CK_MINIMAL); + break; + case CLEND_S: + s = obj; + break; + case CLEND_T: + tr = obj; + tr_fprint(file, tr, CK_VERBOSE); + break; + default: + eprintf("Bad event type received in stdout_lfun", __FILE__, __LINE__); + } + + +} + +void xml_lfun (SRunner *sr, FILE *file, enum print_output printmode, + void *obj, enum cl_event evt) +{ + TestResult *tr; + Suite *s; + static struct timeval inittv, endtv; + static char t[sizeof "yyyy-mm-dd hh:mm:ss"] = {0}; + + if (t[0] == 0) + { + struct tm now; + gettimeofday(&inittv, NULL); + localtime_r(&(inittv.tv_sec), &now); + strftime(t, sizeof("yyyy-mm-dd hh:mm:ss"), "%Y-%m-%d %H:%M:%S", &now); + } + + switch (evt) { + case CLINITLOG_SR: + fprintf(file, "<?xml version=\"1.0\"?>\n"); + fprintf(file, "<testsuites xmlns=\"http://check.sourceforge.net/ns\">\n"); + fprintf(file, " <datetime>%s</datetime>\n", t); + break; + case CLENDLOG_SR: + gettimeofday(&endtv, NULL); + fprintf(file, " <duration>%f</duration>\n", + (endtv.tv_sec + (float)(endtv.tv_usec)/1000000) - \ + (inittv.tv_sec + (float)(inittv.tv_usec/1000000))); + fprintf(file, "</testsuites>\n"); + break; + case CLSTART_SR: + break; + case CLSTART_S: + s = obj; + fprintf(file, " <suite>\n"); + fprintf(file, " <title>%s</title>\n", s->name); + break; + case CLEND_SR: + break; + case CLEND_S: + fprintf(file, " </suite>\n"); + s = obj; + break; + case CLEND_T: + tr = obj; + tr_xmlprint(file, tr, CK_VERBOSE); + break; + default: + eprintf("Bad event type received in xml_lfun", __FILE__, __LINE__); + } + +} + + +FILE *srunner_open_lfile (SRunner *sr) +{ + FILE *f = NULL; + if (srunner_has_log (sr)) { + f = fopen(sr->log_fname, "w"); + if (f == NULL) + eprintf ("Error in call to fopen while opening log file %s:", __FILE__, __LINE__ - 2, + sr->log_fname); + } + return f; +} + +FILE *srunner_open_xmlfile (SRunner *sr) +{ + FILE *f = NULL; + if (srunner_has_xml (sr)) { + f = fopen(sr->xml_fname, "w"); + if (f == NULL) + eprintf ("Error in call to fopen while opening xml file %s:", __FILE__, __LINE__ - 2, + sr->xml_fname); + } + return f; +} + +void srunner_init_logging (SRunner *sr, enum print_output print_mode) +{ + FILE *f; + sr->loglst = check_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); + } + f = srunner_open_xmlfile (sr); + if (f) { + srunner_register_lfun (sr, f, 2, xml_lfun, print_mode); + } + srunner_send_evt (sr, NULL, CLINITLOG_SR); +} + +void srunner_end_logging (SRunner *sr) +{ + List *l; + int rval; + + srunner_send_evt (sr, NULL, CLENDLOG_SR); + + 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 in call to fclose while closing log file:", __FILE__, __LINE__ - 2); + } + free (lg); + } + list_free(l); + sr->loglst = NULL; +} diff --git a/libs/gst/check/libcheck/check_log.h b/libs/gst/check/libcheck/check_log.h new file mode 100644 index 000000000..e0a4b29ee --- /dev/null +++ b/libs/gst/check/libcheck/check_log.h @@ -0,0 +1,47 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#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_output, + void *obj, enum cl_event evt); + +void lfile_lfun (SRunner *sr, FILE *file, enum print_output, + void *obj, enum cl_event evt); + +void xml_lfun (SRunner *sr, FILE *file, enum print_output, + void *obj, enum cl_event evt); + +void srunner_register_lfun (SRunner *sr, FILE *lfile, int close, + LFun lfun, enum print_output); + +FILE *srunner_open_lfile (SRunner *sr); +FILE *srunner_open_xmlfile (SRunner *sr); +void srunner_init_logging (SRunner *sr, enum print_output print_mode); +void srunner_end_logging (SRunner *sr); + +#endif /* CHECK_LOG_H */ diff --git a/libs/gst/check/libcheck/check_msg.c b/libs/gst/check/libcheck/check_msg.c new file mode 100644 index 000000000..1a9db863c --- /dev/null +++ b/libs/gst/check/libcheck/check_msg.c @@ -0,0 +1,201 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <stdio.h> + +#include "check_error.h" +#include "check.h" +#include "check_list.h" +#include "check_impl.h" +#include "check_msg.h" +#include "check_pack.h" + + +/* 'Pipe' is implemented as a temporary file to overcome message + * volume limitations outlined in bug #482012. This scheme works well + * with the existing usage wherein the parent does not begin reading + * until the child has done writing and exited. + * + * Pipe life cycle: + * - The parent creates a tmpfile(). + * - The fork() call has the effect of duplicating the file descriptor + * and copying (on write) the FILE* data structures. + * - The child writes to the file, and its dup'ed file descriptor and + * data structures are cleaned up on child process exit. + * - Before reading, the parent rewind()'s the file to reset both + * FILE* and underlying file descriptor location data. + * - When finished, the parent fclose()'s the FILE*, deleting the + * temporary file, per tmpfile()'s semantics. + * + * This scheme may break down if the usage changes to asynchronous + * reading and writing. + */ + +static FILE *send_file1; +static FILE *send_file2; + +static FILE * get_pipe(void); +static void setup_pipe (void); +static void teardown_pipe (void); +static TestResult *construct_test_result (RcvMsg *rmsg, int waserror); +static void tr_set_loc_by_ctx (TestResult *tr, enum ck_result_ctx ctx, + RcvMsg *rmsg); +static FILE * get_pipe(void) +{ + if (send_file2 != 0) { + return send_file2; + } + + if (send_file1 != 0) { + return send_file1; + } + printf ("send_file1=%p,send_file2=%p", send_file1, send_file2); + eprintf("No messaging setup", __FILE__, __LINE__); + + return NULL; +} + +void send_failure_info(const char *msg) +{ + FailMsg fmsg; + + fmsg.msg = (char *) msg; + ppack(fileno(get_pipe()), CK_MSG_FAIL, (CheckMsg *) &fmsg); +} + +void send_loc_info(const char * file, int line) +{ + LocMsg lmsg; + + lmsg.file = (char *) file; + lmsg.line = line; + ppack(fileno(get_pipe()), CK_MSG_LOC, (CheckMsg *) &lmsg); +} + +void send_ctx_info(enum ck_result_ctx ctx) +{ + CtxMsg cmsg; + + cmsg.ctx = ctx; + ppack(fileno(get_pipe()), CK_MSG_CTX, (CheckMsg *) &cmsg); +} + +TestResult *receive_test_result (int waserror) +{ + FILE *fp; + RcvMsg *rmsg; + TestResult *result; + + fp = get_pipe(); + if (fp == NULL) + eprintf ("Error in call to get_pipe",__FILE__, __LINE__ - 2); + rewind(fp); + rmsg = punpack (fileno(fp)); + teardown_pipe(); + setup_pipe(); + + result = construct_test_result (rmsg, waserror); + rcvmsg_free(rmsg); + return result; +} + +static void tr_set_loc_by_ctx (TestResult *tr, enum ck_result_ctx ctx, + RcvMsg *rmsg) +{ + if (ctx == CK_CTX_TEST) { + tr->file = rmsg->test_file; + tr->line = rmsg->test_line; + rmsg->test_file = NULL; + rmsg->test_line = -1; + } else { + tr->file = rmsg->fixture_file; + tr->line = rmsg->fixture_line; + rmsg->fixture_file = NULL; + rmsg->fixture_line = -1; + } +} + +static TestResult *construct_test_result (RcvMsg *rmsg, int waserror) +{ + TestResult *tr; + + if (rmsg == NULL) + return NULL; + + tr = tr_create(); + + if (rmsg->msg != NULL || waserror) { + tr->ctx = (cur_fork_status () == CK_FORK) ? rmsg->lastctx : rmsg->failctx; + tr->msg = rmsg->msg; + rmsg->msg = NULL; + tr_set_loc_by_ctx (tr, tr->ctx, rmsg); + } else if (rmsg->lastctx == CK_CTX_SETUP) { + tr->ctx = CK_CTX_SETUP; + tr->msg = NULL; + tr_set_loc_by_ctx (tr, CK_CTX_SETUP, rmsg); + } else { + tr->ctx = CK_CTX_TEST; + tr->msg = NULL; + tr_set_loc_by_ctx (tr, CK_CTX_TEST, rmsg); + } + + return tr; +} + +void setup_messaging(void) +{ + setup_pipe(); +} + +void teardown_messaging(void) +{ + teardown_pipe(); +} + +static void setup_pipe(void) +{ + if (send_file1 != 0) { + if (send_file2 != 0) + eprintf("Only one nesting of suite runs supported", __FILE__, __LINE__); + send_file2 = tmpfile(); + } else { + send_file1 = tmpfile(); + } +} + +static void teardown_pipe(void) +{ + if (send_file2 != 0) { + fclose(send_file2); + send_file2 = 0; + } else if (send_file1 != 0) { + fclose(send_file1); + send_file1 = 0; + } else { + eprintf("No messaging setup", __FILE__, __LINE__); + } +} + diff --git a/libs/gst/check/libcheck/check_msg.h b/libs/gst/check/libcheck/check_msg.h new file mode 100644 index 000000000..c4d7f2583 --- /dev/null +++ b/libs/gst/check/libcheck/check_msg.h @@ -0,0 +1,36 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef CHECK_MSG_NEW_H +#define CHECK_MSG_NEW_H + + +/* Functions implementing messaging during test runs */ + +void send_failure_info(const char *msg); +void send_loc_info(const char *file, int line); +void send_ctx_info(enum ck_result_ctx ctx); + +TestResult *receive_test_result(int waserror); + +void setup_messaging(void); +void teardown_messaging(void); + +#endif /*CHECK_MSG_NEW_H */ diff --git a/libs/gst/check/libcheck/check_pack.c b/libs/gst/check/libcheck/check_pack.c new file mode 100644 index 000000000..69e306605 --- /dev/null +++ b/libs/gst/check/libcheck/check_pack.c @@ -0,0 +1,417 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#include "check.h" +#include "check_error.h" +#include "check_list.h" +#include "check_impl.h" +#include "check_pack.h" + +/* typedef an unsigned int that has at least 4 bytes */ +typedef uint32_t ck_uint32; + + +static void pack_int (char **buf, int val); +static int upack_int (char **buf); +static void pack_str (char **buf, const char *str); +static char *upack_str (char **buf); + +static int pack_ctx (char **buf, CtxMsg *cmsg); +static int pack_loc (char **buf, LocMsg *lmsg); +static int pack_fail (char **buf, FailMsg *fmsg); +static void upack_ctx (char **buf, CtxMsg *cmsg); +static void upack_loc (char **buf, LocMsg *lmsg); +static void upack_fail (char **buf, FailMsg *fmsg); + +static void check_type (int type, const char *file, int line); +static enum ck_msg_type upack_type (char **buf); +static void pack_type (char **buf, enum ck_msg_type type); + +static int read_buf (int fdes, char **buf); +static int get_result (char *buf, RcvMsg *rmsg); +static void rcvmsg_update_ctx (RcvMsg *rmsg, enum ck_result_ctx ctx); +static void rcvmsg_update_loc (RcvMsg *rmsg, const char *file, int line); +static RcvMsg *rcvmsg_create (void); +void rcvmsg_free (RcvMsg *rmsg); + +typedef int (*pfun) (char **, CheckMsg *); +typedef void (*upfun) (char **, CheckMsg *); + +static pfun pftab [] = { + (pfun) pack_ctx, + (pfun) pack_fail, + (pfun) pack_loc +}; + +static upfun upftab [] = { + (upfun) upack_ctx, + (upfun) upack_fail, + (upfun) upack_loc +}; + +int pack (enum ck_msg_type type, char **buf, CheckMsg *msg) +{ + if (buf == NULL) + return -1; + if (msg == NULL) + return 0; + + check_type (type, __FILE__, __LINE__); + + return pftab[type] (buf, msg); +} + +int upack (char *buf, CheckMsg *msg, enum ck_msg_type *type) +{ + char *obuf; + int nread; + + if (buf == NULL) + return -1; + + obuf = buf; + + *type = upack_type (&buf); + + check_type (*type, __FILE__, __LINE__); + + upftab[*type] (&buf, msg); + + nread = buf - obuf; + return nread; +} + +static void pack_int (char **buf, int val) +{ + unsigned char *ubuf = (unsigned char *) *buf; + ck_uint32 uval = val; + + ubuf[0] = (uval >> 24) & 0xFF; + ubuf[1] = (uval >> 16) & 0xFF; + ubuf[2] = (uval >> 8) & 0xFF; + ubuf[3] = uval & 0xFF; + + *buf += 4; +} + +static int upack_int (char **buf) +{ + unsigned char *ubuf = (unsigned char *) *buf; + ck_uint32 uval; + + uval = ((ubuf[0] << 24) | (ubuf[1] << 16) | (ubuf[2] << 8) | ubuf[3]); + + *buf += 4; + + return (int) uval; +} + +static void pack_str (char **buf, const char *val) +{ + int strsz; + + if (val == NULL) + strsz = 0; + else + strsz = strlen (val); + + pack_int (buf, strsz); + + if (strsz > 0) { + memcpy (*buf, val, strsz); + *buf += strsz; + } +} + +static char *upack_str (char **buf) +{ + char *val; + int strsz; + + strsz = upack_int (buf); + + if (strsz > 0) { + val = emalloc (strsz + 1); + memcpy (val, *buf, strsz); + val[strsz] = 0; + *buf += strsz; + } else { + val = emalloc (1); + *val = 0; + } + + return val; +} + +static void pack_type (char **buf, enum ck_msg_type type) +{ + pack_int (buf, (int) type); +} + +static enum ck_msg_type upack_type (char **buf) +{ + return (enum ck_msg_type) upack_int (buf); +} + + +static int pack_ctx (char **buf, CtxMsg *cmsg) +{ + char *ptr; + int len; + + len = 4 + 4; + *buf = ptr = emalloc (len); + + pack_type (&ptr, CK_MSG_CTX); + pack_int (&ptr, (int) cmsg->ctx); + + return len; +} + +static void upack_ctx (char **buf, CtxMsg *cmsg) +{ + cmsg->ctx = upack_int (buf); +} + +static int pack_loc (char **buf, LocMsg *lmsg) +{ + char *ptr; + int len; + + len = 4 + 4 + (lmsg->file ? strlen (lmsg->file) : 0) + 4; + *buf = ptr = emalloc (len); + + pack_type (&ptr, CK_MSG_LOC); + pack_str (&ptr, lmsg->file); + pack_int (&ptr, lmsg->line); + + return len; +} + +static void upack_loc (char **buf, LocMsg *lmsg) +{ + lmsg->file = upack_str (buf); + lmsg->line = upack_int (buf); +} + +static int pack_fail (char **buf, FailMsg *fmsg) +{ + char *ptr; + int len; + + len = 4 + 4 + (fmsg->msg ? strlen (fmsg->msg) : 0); + *buf = ptr = emalloc (len); + + pack_type (&ptr, CK_MSG_FAIL); + pack_str (&ptr, fmsg->msg); + + return len; +} + +static void upack_fail (char **buf, FailMsg *fmsg) +{ + fmsg->msg = upack_str (buf); +} + +static void check_type (int type, const char *file, int line) +{ + if (type < 0 || type >= CK_MSG_LAST) + eprintf ("Bad message type arg", file, line); +} + +void ppack (int fdes, enum ck_msg_type type, CheckMsg *msg) +{ + char *buf; + int n; + ssize_t r; + + n = pack (type, &buf, msg); + r = write (fdes, buf, n); + if (r == -1) + eprintf ("Error in call to write:", __FILE__, __LINE__ - 2); + + free (buf); +} + +static int read_buf (int fdes, char **buf) +{ + char *readloc; + int n; + int nread = 0; + int size = 1; + int grow = 2; + + *buf = emalloc(size); + readloc = *buf; + while (1) { + n = read (fdes, readloc, size - nread); + if (n == 0) + break; + if (n == -1) + eprintf ("Error in call to read:", __FILE__, __LINE__ - 4); + + nread += n; + size *= grow; + *buf = erealloc (*buf,size); + readloc = *buf + nread; + } + + return nread; +} + + +static int get_result (char *buf, RcvMsg *rmsg) +{ + enum ck_msg_type type; + CheckMsg msg; + int n; + + n = upack (buf, &msg, &type); + if (n == -1) + eprintf ("Error in call to upack", __FILE__, __LINE__ - 2); + + if (type == CK_MSG_CTX) { + CtxMsg *cmsg = (CtxMsg *) &msg; + rcvmsg_update_ctx (rmsg, cmsg->ctx); + } else if (type == CK_MSG_LOC) { + LocMsg *lmsg = (LocMsg *) &msg; + if (rmsg->failctx == CK_CTX_INVALID) + { + rcvmsg_update_loc (rmsg, lmsg->file, lmsg->line); + } + free (lmsg->file); + } else if (type == CK_MSG_FAIL) { + FailMsg *fmsg = (FailMsg *) &msg; + if (rmsg->msg == NULL) + { + rmsg->msg = emalloc (strlen (fmsg->msg) + 1); + strcpy (rmsg->msg, fmsg->msg); + rmsg->failctx = rmsg->lastctx; + } + else + { + /* Skip subsequent failure messages, only happens for CK_NOFORK */ + } + free (fmsg->msg); + } else + check_type (type, __FILE__, __LINE__); + + return n; +} + +static void reset_rcv_test (RcvMsg *rmsg) +{ + rmsg->test_line = -1; + rmsg->test_file = NULL; +} + +static void reset_rcv_fixture (RcvMsg *rmsg) +{ + rmsg->fixture_line = -1; + rmsg->fixture_file = NULL; +} + +static RcvMsg *rcvmsg_create (void) +{ + RcvMsg *rmsg; + + rmsg = emalloc (sizeof (RcvMsg)); + rmsg->lastctx = CK_CTX_INVALID; + rmsg->failctx = CK_CTX_INVALID; + rmsg->msg = NULL; + reset_rcv_test (rmsg); + reset_rcv_fixture (rmsg); + return rmsg; +} + +void rcvmsg_free (RcvMsg *rmsg) +{ + free(rmsg->fixture_file); + free(rmsg->test_file); + free(rmsg->msg); + free(rmsg); +} + +static void rcvmsg_update_ctx (RcvMsg *rmsg, enum ck_result_ctx ctx) +{ + if (rmsg->lastctx != CK_CTX_INVALID) + { + free(rmsg->fixture_file); + reset_rcv_fixture (rmsg); + } + rmsg->lastctx = ctx; +} + +static void rcvmsg_update_loc (RcvMsg *rmsg, const char *file, int line) +{ + int flen = strlen(file); + + if (rmsg->lastctx == CK_CTX_TEST) { + free(rmsg->test_file); + rmsg->test_line = line; + rmsg->test_file = emalloc (flen + 1); + strcpy (rmsg->test_file, file); + } else { + free(rmsg->fixture_file); + rmsg->fixture_line = line; + rmsg->fixture_file = emalloc (flen + 1); + strcpy (rmsg->fixture_file, file); + } +} + +RcvMsg *punpack (int fdes) +{ + int nread, n; + char *buf; + char *obuf; + RcvMsg *rmsg; + + nread = read_buf (fdes, &buf); + obuf = buf; + rmsg = rcvmsg_create (); + + while (nread > 0) { + n = get_result (buf, rmsg); + nread -= n; + buf += n; + } + + free (obuf); + if (rmsg->lastctx == CK_CTX_INVALID) { + free (rmsg); + rmsg = NULL; + } + + return rmsg; +} diff --git a/libs/gst/check/libcheck/check_pack.h b/libs/gst/check/libcheck/check_pack.h new file mode 100644 index 000000000..0b78695a9 --- /dev/null +++ b/libs/gst/check/libcheck/check_pack.h @@ -0,0 +1,76 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef CHECK_PACK_H +#define CHECK_PACK_H + + +enum ck_msg_type { + CK_MSG_CTX, + CK_MSG_FAIL, + CK_MSG_LOC, + CK_MSG_LAST +}; + +typedef struct CtxMsg +{ + enum ck_result_ctx ctx; +} CtxMsg; + +typedef struct LocMsg +{ + int line; + char *file; +} LocMsg; + +typedef struct FailMsg +{ + char *msg; +} FailMsg; + +typedef union +{ + CtxMsg ctx_msg; + FailMsg fail_msg; + LocMsg loc_msg; +} CheckMsg; + +typedef struct RcvMsg +{ + enum ck_result_ctx lastctx; + enum ck_result_ctx failctx; + char *fixture_file; + int fixture_line; + char *test_file; + int test_line; + char *msg; +} RcvMsg; + +void rcvmsg_free (RcvMsg *rmsg); + + +int pack (enum ck_msg_type type, char **buf, CheckMsg *msg); +int upack (char *buf, CheckMsg *msg, enum ck_msg_type *type); + +void ppack (int fdes, enum ck_msg_type type, CheckMsg *msg); +RcvMsg *punpack (int fdes); + + +#endif /*CHECK_PACK_H */ diff --git a/libs/gst/check/libcheck/check_print.c b/libs/gst/check/libcheck/check_print.c new file mode 100644 index 000000000..5aec2e21f --- /dev/null +++ b/libs/gst/check/libcheck/check_print.c @@ -0,0 +1,156 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include "config.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "check.h" +#include "check_list.h" +#include "check_impl.h" +#include "check_str.h" +#include "check_print.h" + +static void srunner_fprint_summary (FILE *file, SRunner *sr, + enum print_output print_mode); +static void srunner_fprint_results (FILE *file, SRunner *sr, + enum print_output print_mode); + + +void srunner_print (SRunner *sr, enum print_output print_mode) +{ + srunner_fprint (stdout, sr, print_mode); +} + +void srunner_fprint (FILE *file, SRunner *sr, enum print_output print_mode) +{ + if (print_mode == CK_ENV) { + print_mode = get_env_printmode(); + } + + srunner_fprint_summary (file, sr, print_mode); + srunner_fprint_results (file, sr, print_mode); +} + +static void srunner_fprint_summary (FILE *file, SRunner *sr, + enum print_output print_mode) +{ + if (print_mode >= CK_MINIMAL) { + char *str; + + str = sr_stat_str (sr); + fprintf (file, "%s\n", str); + free(str); + } + return; +} + +static void srunner_fprint_results (FILE *file, SRunner *sr, + enum print_output 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, enum print_output print_mode) +{ + if (print_mode == CK_ENV) { + print_mode = get_env_printmode(); + } + + if ((print_mode >= CK_VERBOSE && tr->rtype == CK_PASS) || + (tr->rtype != CK_PASS && print_mode >= CK_NORMAL)) { + char *trstr = tr_str (tr); + fprintf (file,"%s\n", trstr); + free(trstr); + } +} + +void tr_xmlprint (FILE *file, TestResult *tr, enum print_output print_mode) +{ + char result[10]; + char *path_name; + char *file_name; + char *slash; + + switch (tr->rtype) { + case CK_PASS: + strcpy(result, "success"); + break; + case CK_FAILURE: + strcpy(result, "failure"); + break; + case CK_ERROR: + strcpy(result, "error"); + break; + case CK_TEST_RESULT_INVALID: + default: + abort (); + break; + } + + slash = strrchr(tr->file, '/'); + if (slash == NULL) { + path_name = (char*)"."; + file_name = tr->file; + } else { + path_name = strdup(tr->file); + path_name[slash - tr->file] = 0; /* Terminate the temporary string. */ + file_name = slash + 1; + } + + + fprintf(file, " <test result=\"%s\">\n", result); + fprintf(file, " <path>%s</path>\n", path_name); + fprintf(file, " <fn>%s:%d</fn>\n", file_name, tr->line); + fprintf(file, " <id>%s</id>\n", tr->tname); + fprintf(file, " <iteration>%d</iteration>\n", tr->iter); + fprintf(file, " <description>%s</description>\n", tr->tcname); + fprintf(file, " <message>%s</message>\n", tr->msg); + fprintf(file, " </test>\n"); + + if (slash != NULL) { + free(path_name); + } +} + +enum print_output get_env_printmode (void) +{ + char *env = getenv ("CK_VERBOSITY"); + if (env == NULL) + return CK_NORMAL; + if (strcmp (env, "silent") == 0) + return CK_SILENT; + if (strcmp (env, "minimal") == 0) + return CK_MINIMAL; + if (strcmp (env, "verbose") == 0) + return CK_VERBOSE; + return CK_NORMAL; +} diff --git a/libs/gst/check/libcheck/check_print.h b/libs/gst/check/libcheck/check_print.h new file mode 100644 index 000000000..f4b02da32 --- /dev/null +++ b/libs/gst/check/libcheck/check_print.h @@ -0,0 +1,30 @@ +/* + * 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., 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, enum print_output print_mode); +void tr_xmlprint (FILE *file, TestResult *tr, enum print_output print_mode); +void srunner_fprint (FILE *file, SRunner *sr, enum print_output print_mode); +enum print_output get_env_printmode (void); + + +#endif /* CHECK_PRINT_H */ diff --git a/libs/gst/check/libcheck/check_run.c b/libs/gst/check/libcheck/check_run.c new file mode 100644 index 000000000..e8546fda7 --- /dev/null +++ b/libs/gst/check/libcheck/check_run.c @@ -0,0 +1,548 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include "config.h" + +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <signal.h> + +#include "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 +}; + +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, + enum print_output print_mode); +static void srunner_run_tcase (SRunner *sr, TCase *tc); +static int srunner_run_unchecked_setup (SRunner *sr, TCase *tc); +static void srunner_run_unchecked_teardown (SRunner *sr, TCase *tc); +static TestResult * tcase_run_checked_setup (SRunner *sr, TCase *tc); +static void tcase_run_checked_teardown (TCase *tc); +static void srunner_iterate_tcase_tfuns (SRunner *sr, TCase *tc); +static void srunner_add_failure (SRunner *sr, TestResult *tf); +static TestResult *tcase_run_tfun_fork (SRunner *sr, TCase *tc, TF *tf, int i); +static TestResult *tcase_run_tfun_nofork (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); +static TestResult *receive_result_info_nofork (const char *tcname, + const char *tname, + int iter); +static void set_fork_info (TestResult *tr, int status, int expected_signal); +static void set_nofork_info (TestResult *tr); +static char *signal_msg (int sig); +static char *signal_error_msg (int signal_received, int signal_expected); +static char *pass_msg (void); +static char *exit_msg (int exitstatus); +static int waserror (int status, int expected_signal); + +#define MSG_LEN 100 + +static int alarm_received; +static pid_t group_pid; + +static void sig_handler(int sig_nr) +{ + switch (sig_nr) { + case SIGALRM: + alarm_received = 1; + killpg(group_pid, SIGKILL); + break; + default: + eprintf("Unhandled signal: %d", __FILE__, __LINE__, sig_nr); + break; + } +} + +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 print_mode) +{ + log_srunner_end (sr); + srunner_end_logging (sr); + teardown_messaging(); + set_fork_status(CK_FORK); +} + +static void srunner_iterate_suites (SRunner *sr, + enum print_output print_mode) + +{ + List *slst; + List *tcl; + TCase *tc; + + 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_suite_end (sr, s); + } +} + +void srunner_run_all (SRunner *sr, enum print_output print_mode) +{ + struct sigaction old_action; + struct sigaction new_action; + + if (sr == NULL) + return; + if (print_mode >= CK_LAST) + { + eprintf ("Bad print_mode argument to srunner_run_all: %d", + __FILE__, __LINE__, print_mode); + } + memset(&new_action, 0, sizeof new_action); + new_action.sa_handler = sig_handler; + sigaction(SIGALRM, &new_action, &old_action); + srunner_run_init (sr, print_mode); + srunner_iterate_suites (sr, print_mode); + srunner_run_end (sr, print_mode); + sigaction(SIGALRM, &old_action, NULL); +} + +static void srunner_add_failure (SRunner *sr, TestResult *tr) +{ + 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 void srunner_iterate_tcase_tfuns (SRunner *sr, TCase *tc) +{ + List *tfl; + TF *tfun; + TestResult *tr = NULL; + + tfl = tc->tflst; + + for (list_front(tfl); !list_at_end (tfl); list_advance (tfl)) { + int i; + tfun = list_val (tfl); + + for (i = tfun->loop_start; i < tfun->loop_end; i++) + { + switch (srunner_fork_status(sr)) { + case CK_FORK: + tr = tcase_run_tfun_fork (sr, tc, tfun, i); + break; + case CK_NOFORK: + tr = tcase_run_tfun_nofork (sr, tc, tfun, i); + break; + default: + eprintf("Bad fork status in SRunner", __FILE__, __LINE__); + } + srunner_add_failure (sr, tr); + log_test_end(sr, tr); + } + } +} + +static int srunner_run_unchecked_setup (SRunner *sr, TCase *tc) +{ + TestResult *tr; + List *l; + Fixture *f; + int rval = 1; + + set_fork_status(CK_NOFORK); + + l = tc->unch_sflst; + + for (list_front(l); !list_at_end(l); list_advance(l)) { + send_ctx_info(CK_CTX_SETUP); + f = list_val(l); + f->fun(); + + tr = receive_result_info_nofork (tc->name, "unchecked_setup", 0); + + if (tr->rtype != CK_PASS) { + srunner_add_failure(sr, tr); + rval = 0; + break; + } + free(tr->file); + free(tr->msg); + free(tr); + } + + set_fork_status(srunner_fork_status(sr)); + return rval; +} + +static TestResult * tcase_run_checked_setup (SRunner *sr, TCase *tc) +{ + TestResult *tr = NULL; + List *l; + Fixture *f; + enum fork_status fstat = srunner_fork_status(sr); + + l = tc->ch_sflst; + if (fstat == CK_FORK) { + send_ctx_info(CK_CTX_SETUP); + } + + for (list_front(l); !list_at_end(l); list_advance(l)) { + if (fstat == CK_NOFORK) { + send_ctx_info(CK_CTX_SETUP); + } + f = list_val(l); + f->fun(); + + /* Stop the setup and return the failure if nofork mode. */ + if (fstat == CK_NOFORK) { + tr = receive_result_info_nofork (tc->name, "checked_setup", 0); + if (tr->rtype != CK_PASS) { + break; + } + + free(tr->file); + free(tr->msg); + free(tr); + tr = NULL; + } + } + + return tr; +} + +static void tcase_run_checked_teardown (TCase *tc) +{ + List *l; + Fixture *f; + + l = tc->ch_tflst; + + send_ctx_info(CK_CTX_TEARDOWN); + + for (list_front(l); !list_at_end(l); list_advance(l)) { + f = list_val(l); + f->fun(); + } +} + +static void srunner_run_unchecked_teardown (SRunner *sr, TCase *tc) +{ + List *l; + Fixture *f; + + set_fork_status(CK_NOFORK); + l = tc->unch_tflst; + + for (list_front(l); !list_at_end(l); list_advance(l)) { + + f = list_val(l); + send_ctx_info(CK_CTX_TEARDOWN); + f->fun (); + } + set_fork_status(srunner_fork_status(sr)); +} + +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 *receive_result_info_fork (const char *tcname, + const char *tname, + int iter, + int status, int expected_signal) +{ + TestResult *tr; + + tr = receive_test_result(waserror(status, expected_signal)); + if (tr == NULL) + eprintf("Failed to receive test result", __FILE__, __LINE__); + tr->tcname = tcname; + tr->tname = tname; + tr->iter = iter; + set_fork_info(tr, status, expected_signal); + + return tr; +} + +static TestResult *receive_result_info_nofork (const char *tcname, + const char *tname, + int iter) +{ + TestResult *tr; + + tr = receive_test_result(0); + if (tr == NULL) + eprintf("Failed to receive test result", __FILE__, __LINE__); + tr->tcname = tcname; + tr->tname = tname; + tr->iter = iter; + set_nofork_info(tr); + + return tr; +} + +static void set_fork_info (TestResult *tr, 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); + + if (was_sig) { + if (signal_expected == signal_received) { + if (alarm_received) { + /* Got alarm instead of signal */ + tr->rtype = CK_ERROR; + tr->msg = signal_error_msg(signal_received, signal_expected); + } else { + tr->rtype = CK_PASS; + tr->msg = pass_msg(); + } + } else if (signal_expected != 0) { + /* signal received, but not the expected one */ + tr->rtype = CK_ERROR; + tr->msg = signal_error_msg(signal_received, signal_expected); + } else { + /* signal received and none expected */ + tr->rtype = CK_ERROR; + tr->msg = signal_msg(signal_received); + } + } else if (signal_expected == 0) { + if (was_exit && exit_status == 0) { + tr->rtype = CK_PASS; + tr->msg = pass_msg(); + } else if (was_exit && exit_status != 0) { + 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) { + tr->msg = exit_msg(exit_status); + if (exit_status == 0) + tr->rtype = CK_FAILURE; /* normal exit status */ + else + tr->rtype = CK_FAILURE; /* early exit */ + } + } +} + +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 TestResult *tcase_run_tfun_nofork (SRunner *sr, TCase *tc, TF *tfun, int i) +{ + TestResult *tr; + + tr = tcase_run_checked_setup(sr, tc); + if (tr == NULL) { + tfun->fn(i); + tcase_run_checked_teardown(tc); + return receive_result_info_nofork(tc->name, tfun->name, i); + } + + return tr; +} + + +static TestResult *tcase_run_tfun_fork (SRunner *sr, TCase *tc, TF *tfun, int i) +{ + pid_t pid_w; + pid_t pid; + int status = 0; + pid = fork(); + if (pid == -1) + eprintf("Error in call to fork:", __FILE__, __LINE__ - 2); + if (pid == 0) { + setpgid(0, 0); + group_pid = getpgrp(); + tcase_run_checked_setup(sr, tc); + tfun->fn(i); + tcase_run_checked_teardown(tc); + exit(EXIT_SUCCESS); + } else { + group_pid = pid; + } + + alarm_received = 0; + alarm(tc->timeout); + do { + pid_w = waitpid(pid, &status, 0); + } while (pid_w == -1); + + killpg(pid, SIGKILL); /* Kill remaining processes. */ + + return receive_result_info_fork(tc->name, tfun->name, i, status, tfun->signal); +} + +static char *signal_error_msg (int signal_received, int signal_expected) +{ + char *sig_r_str; + char *sig_e_str; + char *msg = 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 *signal_msg (int signal) +{ + char *msg = 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 *exit_msg (int exitval) +{ + char *msg = emalloc(MSG_LEN); /* free'd by caller */ + snprintf (msg, MSG_LEN, + "Early exit with return value %d", exitval); + return msg; +} + +static char *pass_msg (void) +{ + char *msg = emalloc(sizeof("Passed")); + strcpy (msg, "Passed"); + return msg; +} + +enum fork_status srunner_fork_status (SRunner *sr) +{ + if (sr->fstat == CK_FORK_GETENV) { + char *env = getenv ("CK_FORK"); + if (env == NULL) + return CK_FORK; + if (strcmp (env,"no") == 0) + return CK_NOFORK; + else + return CK_FORK; + } else + return sr->fstat; +} + +void srunner_set_fork_status (SRunner *sr, enum fork_status fstat) +{ + sr->fstat = fstat; +} + +pid_t check_fork (void) +{ + pid_t pid = fork(); + /* Set the process to a process group to be able to kill it easily. */ + setpgid(pid, group_pid); + return pid; +} + +void check_waitpid_and_exit (pid_t pid) +{ + 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); +} + +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)); +} diff --git a/libs/gst/check/libcheck/check_str.c b/libs/gst/check/libcheck/check_str.c new file mode 100644 index 000000000..4d3b45648 --- /dev/null +++ b/libs/gst/check/libcheck/check_str.c @@ -0,0 +1,118 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <stdio.h> +#include <stdarg.h> + +#include "check.h" +#include "check_list.h" +#include "check_error.h" +#include "check_impl.h" +#include "check_str.h" + +static const char *tr_type_str (TestResult *tr); +static int percent_passed (TestStats *t); + +char *tr_str (TestResult *tr) +{ + const char *exact_msg; + char *rstr; + + exact_msg = (tr->rtype == CK_ERROR) ? "(after this point) ": ""; + + rstr = ck_strdup_printf ("%s:%d:%s:%s:%s:%d: %s%s", + tr->file, tr->line, + tr_type_str(tr), tr->tcname, tr->tname, tr->iter, + exact_msg, tr->msg); + + return rstr; +} + +char *sr_stat_str (SRunner *sr) +{ + char *str; + TestStats *ts; + + ts = sr->stats; + + str = ck_strdup_printf ("%d%%: Checks: %d, Failures: %d, Errors: %d", + percent_passed (ts), ts->n_checked, ts->n_failed, + ts->n_errors); + + return str; +} + +char *ck_strdup_printf (const char *fmt, ...) +{ + /* Guess we need no more than 100 bytes. */ + int n, size = 100; + char *p; + va_list ap; + + p = emalloc (size); + + while (1) + { + /* Try to print in the allocated space. */ + va_start(ap, fmt); + n = vsnprintf (p, size, fmt, ap); + va_end(ap); + /* If that worked, return the string. */ + if (n > -1 && n < size) + return p; + + /* Else try again with more space. */ + if (n > -1) /* C99 conform vsnprintf() */ + size = n+1; /* precisely what is needed */ + else /* glibc 2.0 */ + size *= 2; /* twice the old size */ + + p = erealloc (p, size); + } +} + +static const char *tr_type_str (TestResult *tr) +{ + const char *str = NULL; + if (tr->ctx == CK_CTX_TEST) { + if (tr->rtype == CK_PASS) + str = "P"; + else if (tr->rtype == CK_FAILURE) + str = "F"; + else if (tr->rtype == CK_ERROR) + str = "E"; + } else + str = "S"; + + return str; +} + +static int percent_passed (TestStats *t) +{ + if (t->n_failed == 0 && t->n_errors == 0) + return 100; + else if (t->n_checked == 0) + return 0; + else + return (int) ( (float) (t->n_checked - (t->n_failed + t->n_errors)) / + (float) t->n_checked * 100); +} diff --git a/libs/gst/check/libcheck/check_str.h b/libs/gst/check/libcheck/check_str.h new file mode 100644 index 000000000..bef230f7a --- /dev/null +++ b/libs/gst/check/libcheck/check_str.h @@ -0,0 +1,36 @@ +/* + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef CHECK_STR_H +#define CHECK_STR_H + +/* Return a string representation of the given TestResult. Return + value has been malloc'd, and must be freed by the caller */ +char *tr_str (TestResult *tr); + +/* Return a string representation of the given SRunner's run + statistics (% passed, num run, passed, errors, failures). Return + value has been malloc'd, and must be freed by the caller +*/ +char *sr_stat_str (SRunner *sr); + +char *ck_strdup_printf (const char *fmt, ...); + +#endif /* CHECK_STR_H */ |