# in case and if your file system supports case sensitive file names. Windows
# and Mac users are advised to set this option to NO.
-CASE_SENSE_NAMES = YES
+CASE_SENSE_NAMES = NO
# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
# will show members with their full class and namespace scopes in the
# will put a list of the files that are included by a file in the documentation
# of that file.
-SHOW_INCLUDE_FILES = YES
+SHOW_INCLUDE_FILES = NO
# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
# is inserted in the documentation for inline members.
# Note: To get rid of all source code in the generated output, make sure also
# VERBATIM_HEADERS is set to NO.
-SOURCE_BROWSER = YES
+SOURCE_BROWSER = NO
# Setting the INLINE_SOURCES tag to YES will include the body
# of functions and classes directly in the documentation.
#include "version.h"
#include <getopt.h>
-long long tests, errors;
+/** @brief Count of tests */
+long long tests;
+
+/** @brief Count of errors */
+long long errors;
+
+/** @brief If set, first error will fail whole test */
int fail_first;
+
+/** @brief Verbose mode */
int verbose;
+
+/** @brief If set, test will return 'skipped' indicator */
int skipped;
+/** @brief Count up an error
+ *
+ * If @ref fail_first is set then the test run is aborted.
+ */
void count_error(void) {
++errors;
if(fail_first)
abort();
}
+/** @brief Render a string into printable ASCII
+ * @param s String to format
+ * @return Allocated copy of formatted string
+ *
+ * Replaces any non-ASCII characters with a hex escape.
+ */
const char *format(const char *s) {
struct dynstr d;
int c;
return d.vec;
}
+/** @brief Format a UTF-32 string into hex
+ * @param s String to format
+ * @return Allocated copy of formatted string
+ *
+ * Returns the hex codes of @p s separated by spaces.
+ */
const char *format_utf32(const uint32_t *s) {
struct dynstr d;
uint32_t c;
return d.vec;
}
+/** @brief Convert a string of hex codes to a UTF-32 string
+ * @param s String of hex codes, separated by spaces
+ * @return Allocated string, 0-terminated
+ */
uint32_t *ucs4parse(const char *s) {
struct dynstr_ucs4 d;
char *e;
return d.vec;
}
+/** @brief Format a string like asprintf()
+ * @param fmt Format string, per printf(3)
+ * @param ... Arguments
+ * @return Formatted string or null pointer on error
+ */
const char *do_printf(const char *fmt, ...) {
va_list ap;
char *s;
return s;
}
+/** @brief Jump buffer for exitfn() testing */
jmp_buf fatal_env;
+/** @brief exitfn() callback for testing
+ * @param rc Value to return from setjmp()
+ * Jumps to @ref fatal_env
+ */
void test_exitfn(int rc) {
assert(rc != 0);
longjmp(fatal_env, rc);
exit(0);
}
+/** @brief Standard test program initialization
+ * @param argc Argument count
+ * @param argv Arguments
+ */
void test_init(int argc, char **argv) {
int n;
extern int verbose;
extern int skipped;
-/** @brief Checks that @p expr is nonzero */
+/** @brief Checks that @p expr is nonzero
+ * @param expr Expression to check
+ *
+ * If @p expr is nonzero then logs an error (and continues).
+ */
#define insist(expr) do { \
if(!(expr)) { \
count_error(); \
++tests; \
} while(0)
+/** @brief Check that a pair of strings match
+ * @param GOT What we actually got
+ * @param WANT What we wanted
+ *
+ * If @p GOT and @p WANT differ then logs an error (and continues).
+ *
+ * @p WANT is allowed to evaluate to a null pointer (but if it comes to
+ * anything else then must be safe to strcmp).
+ */
#define check_string(GOT, WANT) do { \
const char *got = GOT; \
const char *want = WANT; \
++tests; \
} while(0)
+/** @brief Check that a string prefix matches
+ * @param GOT What we actually got
+ * @param WANT What we wanted
+ *
+ * If @p WANT is not a prefix of @p GOT then logs an error (and continues).
+ *
+ * @p WANT is allowed to evaluate to a null pointer (but if it comes to
+ * anything else then must be safe to strcmp).
+ */
#define check_string_prefix(GOT, WANT) do { \
const char *got = GOT; \
const char *want = WANT; \
++tests; \
} while(0)
+/** @brief Check that a pair of integers match.
+ * @param GOT What we actually got
+ * @param WANT What we wanted
+ *
+ * If @p GOT and @p WANT differ then logs an error (and continues).
+ */
#define check_integer(GOT, WANT) do { \
const intmax_t got = GOT, want = WANT; \
if(got != want) { \
++tests; \
} while(0)
+/** @brief Check that a function calls fatal()
+ * @param WHAT Expression to evaluate
+ *
+ * Evaluates WHAT and if it does not call fatal(), logs an error. In any case,
+ * continues. Modifies exitfn() so that fatal() isn't actually fatal.
+ */
#define check_fatal(WHAT) do { \
void (*const save_exitfn)(int) attribute((noreturn)) = exitfn; \
\
extern jmp_buf fatal_env;
void test_exitfn(int) attribute((noreturn));
+/** @brief Common code for each test source file
+ * @param name Name of test
+ *
+ * Expands to a @c main function which:
+ * - calls test_init()
+ * - calls test_NAME()
+ * - reports a count of errors
+ * - returns the right exit status
+ */
#define TEST(name) \
int main(int argc, char **argv) { \
test_init(argc, argv); \