(root)/
xz-5.4.5/
tests/
tuktest.h
       1  ///////////////////////////////////////////////////////////////////////////////
       2  //
       3  /// \file       tuktest.h
       4  /// \brief      Helper macros for writing simple test programs
       5  /// \version    2023-01-08
       6  ///
       7  /// Some inspiration was taken from STest by Keith Nicholas.
       8  ///
       9  /// This is standard C99/C11 only and thus should be fairly portable
      10  /// outside POSIX systems too.
      11  ///
      12  /// This supports putting multiple tests in a single test program
      13  /// although it is perfectly fine to have only one test per program.
      14  /// Each test can produce one of these results:
      15  ///   - Pass
      16  ///   - Fail
      17  ///   - Skip
      18  ///   - Hard error (the remaining tests, if any, are not run)
      19  ///
      20  /// By default this produces an exit status that is compatible with
      21  /// Automake and Meson, and mostly compatible with CMake.[1]
      22  /// If a test program contains multiple tests, only one exit code can
      23  /// be returned. Of the following, the first match is used:
      24  ///   - 99 if any test returned a hard error
      25  ///   - stdlib.h's EXIT_FAILURE if at least one test failed
      26  ///   - 77 if at least one test was skipped or no tests were run at all
      27  ///   - stdlib.h's EXIT_SUCCESS (0 on POSIX); that is, if none of the above
      28  ///     are true then there was at least one test to run and none of them
      29  ///     failed, was skipped, or returned a hard error.
      30  ///
      31  /// A summary of tests being run and their results are printed to stdout.
      32  /// If you want ANSI coloring for the output, #define TUKTEST_COLOR.
      33  /// If you only want output when something goes wrong, #define TUKTEST_QUIET.
      34  ///
      35  /// The downside of the above mapping is that it cannot indicate if
      36  /// some tests were skipped and some passed. If that is likely to
      37  /// happen it may be better to split into multiple test programs (one
      38  /// test per program) or use the TAP mode described below.
      39  ///
      40  /// By using #define TUKTEST_TAP before #including this file the
      41  /// output will be Test Anything Protocol (TAP) version 12 compatible
      42  /// and the exit status will always be EXIT_SUCCESS. This can be easily
      43  /// used with Automake via its tap-driver.sh. Meson supports TAP natively.
      44  /// TAP's todo-directive isn't supported for now, mostly because it's not
      45  /// trivially convertible to the exit-status reporting method.
      46  ///
      47  /// If TUKTEST_TAP is used, TUKTEST_QUIET and TUKTEST_COLOR are ignored.
      48  ///
      49  /// The main() function may look like this (remember to include config.h
      50  /// or such files too if needed!):
      51  ///
      52  ///     #include "tuktest.h"
      53  ///
      54  ///     int main(int argc, char **argv)
      55  ///     {
      56  ///         tuktest_start(argc, argv);
      57  ///
      58  ///         if (!is_package_foo_available())
      59  ///             tuktest_early_skip("Optional package foo is not available");
      60  ///
      61  ///         if (!do_common_initializations())
      62  ///             tuktest_error("Error during common initializations");
      63  ///
      64  ///         tuktest_run(testfunc1);
      65  ///         tuktest_run(testfunc2);
      66  ///
      67  ///         return tuktest_end();
      68  ///     }
      69  ///
      70  /// Using exit(tuktest_end()) as a pair to tuktest_start() is OK too.
      71  ///
      72  /// Each test function called via tuktest_run() should be of type
      73  /// "void testfunc1(void)". The test functions should use the
      74  /// various assert_CONDITION() macros. The current test stops if
      75  /// an assertion fails (this is implemented with setjmp/longjmp).
      76  /// Execution continues from the next test unless the failure was
      77  /// due to assert_error() (indicating a hard error) which makes
      78  /// the program exit() without running any remaining tests.
      79  ///
      80  /// Search for "define assert" in this file to find the explanations
      81  /// of the available assertion macros.
      82  ///
      83  /// IMPORTANT:
      84  ///
      85  ///   - The assert_CONDITION() macros may only be used by code that is
      86  ///     called via tuktest_run()! This includes the function named in
      87  ///     the tuktest_run() call and functions called further from there.
      88  ///     (The assert_CONDITION() macros depend on setup code in tuktest_run()
      89  ///     and other use results in undefined behavior.)
      90  ///
      91  ///   - tuktest_start(), tuktest_early_skip, tuktest_run(), and tuktest_end()
      92  ///     must not be used in the tests called via tuktest_run()! (tuktest_end()
      93  ///     is called more freely internally by this file but such use isn't part
      94  ///     of the API.)
      95  ///
      96  ///   - tuktest_error(), tuktest_malloc(), tuktest_free(),
      97  ///     tuktest_file_from_srcdir(), and tuktest_file_from_builddir()
      98  ///     can be used everywhere after tuktest_start() has been called.
      99  ///     (In tests running under tuktest_run(), assert_error() can be used
     100  ///     instead of tuktest_error() when a hard error occurs.)
     101  ///
     102  ///   - Everything else is for internal use only.
     103  ///
     104  /// Footnotes:
     105  ///
     106  /// [1] As of 2022-06-02:
     107  ///     See the Automake manual "info (automake)Scripts-based Testsuites" or:
     108  ///     https://www.gnu.org/software/automake/manual/automake.html#Scripts_002dbased-Testsuites
     109  ///
     110  ///     Meson: https://mesonbuild.com/Unit-tests.html
     111  ///
     112  ///     CMake handles passing and failing tests by default but treats hard
     113  ///     errors as regular fails. To CMake support skipped tests correctly,
     114  ///     one has to set the SKIP_RETURN_CODE property for each test:
     115  ///
     116  ///     set_tests_properties(foo_test_name PROPERTIES SKIP_RETURN_CODE 77)
     117  ///
     118  ///     See:
     119  ///     https://cmake.org/cmake/help/latest/command/set_tests_properties.html
     120  ///     https://cmake.org/cmake/help/latest/prop_test/SKIP_RETURN_CODE.html
     121  //
     122  //  Author:    Lasse Collin
     123  //
     124  //  This file has been put into the public domain.
     125  //  You can do whatever you want with this file.
     126  //
     127  ///////////////////////////////////////////////////////////////////////////////
     128  
     129  #ifndef TUKTEST_H
     130  #define TUKTEST_H
     131  
     132  #include <stddef.h>
     133  
     134  // On some (too) old systems inttypes.h doesn't exist or isn't good enough.
     135  // Include it conditionally so that any portability tricks can be done before
     136  // tuktest.h is included. On any modern system inttypes.h is fine as is.
     137  #ifndef PRIu64
     138  #	include <inttypes.h>
     139  #endif
     140  
     141  #include <setjmp.h>
     142  #include <stdlib.h>
     143  #include <string.h>
     144  #include <stdio.h>
     145  
     146  
     147  #if defined(__GNUC__) && defined(__GNUC_MINOR__)
     148  #	define TUKTEST_GNUC_REQ(major, minor) \
     149  		((__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)) \
     150  			|| __GNUC__ > (major))
     151  #else
     152  #	define TUKTEST_GNUC_REQ(major, minor) 0
     153  #endif
     154  
     155  
     156  // This is silencing warnings about unused functions. Not all test programs
     157  // need all functions from this header.
     158  #if TUKTEST_GNUC_REQ(3, 0)
     159  #	define tuktest_maybe_unused __attribute__((__unused__))
     160  #else
     161  #	define tuktest_maybe_unused
     162  #endif
     163  
     164  // We need printf("") so silence the warning about empty format string.
     165  #if TUKTEST_GNUC_REQ(4, 2)
     166  #	pragma GCC diagnostic ignored "-Wformat-zero-length"
     167  #endif
     168  
     169  
     170  // Types and printf format macros to use in integer assertions and also for
     171  // printing size_t values (C99's %zu isn't available on very old systems).
     172  typedef int64_t tuktest_int;
     173  typedef uint64_t tuktest_uint;
     174  #define TUKTEST_PRId PRId64
     175  #define TUKTEST_PRIu PRIu64
     176  #define TUKTEST_PRIX PRIX64
     177  
     178  
     179  // When TAP mode isn't used, Automake-compatible exit statuses are used.
     180  #define TUKTEST_EXIT_PASS EXIT_SUCCESS
     181  #define TUKTEST_EXIT_FAIL EXIT_FAILURE
     182  #define TUKTEST_EXIT_SKIP 77
     183  #define TUKTEST_EXIT_ERROR 99
     184  
     185  
     186  enum tuktest_result {
     187  	TUKTEST_PASS,
     188  	TUKTEST_FAIL,
     189  	TUKTEST_SKIP,
     190  	TUKTEST_ERROR,
     191  };
     192  
     193  
     194  #ifdef TUKTEST_TAP
     195  #	undef TUKTEST_QUIET
     196  #	undef TUKTEST_COLOR
     197  #	undef TUKTEST_TAP
     198  #	define TUKTEST_TAP 1
     199  #	define TUKTEST_STR_PASS  "ok -"
     200  #	define TUKTEST_STR_FAIL  "not ok -"
     201  #	define TUKTEST_STR_SKIP  "ok - # SKIP"
     202  #	define TUKTEST_STR_ERROR "Bail out!"
     203  #else
     204  #	define TUKTEST_TAP 0
     205  #	ifdef TUKTEST_COLOR
     206  #		define TUKTEST_COLOR_PASS  "\x1B[0;32m"
     207  #		define TUKTEST_COLOR_FAIL  "\x1B[0;31m"
     208  #		define TUKTEST_COLOR_SKIP  "\x1B[1;34m"
     209  #		define TUKTEST_COLOR_ERROR "\x1B[0;35m"
     210  #		define TUKTEST_COLOR_TOTAL "\x1B[1m"
     211  #		define TUKTEST_COLOR_OFF   "\x1B[m"
     212  #		define TUKTEST_COLOR_IF(cond, color) ((cond) ? (color) : "" )
     213  #	else
     214  #		define TUKTEST_COLOR_PASS  ""
     215  #		define TUKTEST_COLOR_FAIL  ""
     216  #		define TUKTEST_COLOR_SKIP  ""
     217  #		define TUKTEST_COLOR_ERROR ""
     218  #		define TUKTEST_COLOR_TOTAL ""
     219  #		define TUKTEST_COLOR_OFF   ""
     220  #		define TUKTEST_COLOR_IF(cond, color) ""
     221  #	endif
     222  #	define TUKTEST_COLOR_ADD(str, color) color str TUKTEST_COLOR_OFF
     223  #	define TUKTEST_STR_PASS \
     224  		TUKTEST_COLOR_ADD("PASS:", TUKTEST_COLOR_PASS)
     225  #	define TUKTEST_STR_FAIL \
     226  		TUKTEST_COLOR_ADD("FAIL:", TUKTEST_COLOR_FAIL)
     227  #	define TUKTEST_STR_SKIP \
     228  		TUKTEST_COLOR_ADD("SKIP:", TUKTEST_COLOR_SKIP)
     229  #	define TUKTEST_STR_ERROR \
     230  		TUKTEST_COLOR_ADD("ERROR:", TUKTEST_COLOR_ERROR)
     231  #endif
     232  
     233  // NOTE: If TUKTEST_TAP is defined then TUKTEST_QUIET will get undefined above.
     234  #ifndef TUKTEST_QUIET
     235  #	define TUKTEST_QUIET 0
     236  #else
     237  #	undef TUKTEST_QUIET
     238  #	define TUKTEST_QUIET 1
     239  #endif
     240  
     241  
     242  // Counts of the passed, failed, skipped, and hard-errored tests.
     243  // This is indexed with the enumeration constants from enum tuktest_result.
     244  static unsigned tuktest_stats[4] = { 0, 0, 0, 0 };
     245  
     246  // Copy of argc and argv from main(). These are set by tuktest_start().
     247  static int tuktest_argc = 0;
     248  static char **tuktest_argv = NULL;
     249  
     250  // Name of the currently-running test. This exists because it's nice
     251  // to print the main test function name even if the failing test-assertion
     252  // fails in a function called by the main test function.
     253  static const char *tuktest_name = NULL;
     254  
     255  // longjmp() target for when a test-assertion fails.
     256  static jmp_buf tuktest_jmpenv;
     257  
     258  
     259  // This declaration is needed for tuktest_malloc().
     260  static int tuktest_end(void);
     261  
     262  
     263  // Internal helper for handling hard errors both inside and
     264  // outside tuktest_run().
     265  #define tuktest_error_impl(filename, line, ...) \
     266  do { \
     267  	tuktest_print_result_prefix(TUKTEST_ERROR, filename, line); \
     268  	printf(__VA_ARGS__); \
     269  	printf("\n"); \
     270  	++tuktest_stats[TUKTEST_ERROR]; \
     271  	exit(tuktest_end()); \
     272  } while (0)
     273  
     274  
     275  // printf() is without checking its return value in many places. This function
     276  // is called before exiting to check the status of stdout and catch errors.
     277  static void
     278  tuktest_catch_stdout_errors(void)
     279  {
     280  	if (ferror(stdout) || fclose(stdout)) {
     281  		fputs("Error while writing to stdout\n", stderr);
     282  		exit(TUKTEST_EXIT_ERROR);
     283  	}
     284  }
     285  
     286  
     287  // A simplified basename()-like function that is good enough for
     288  // cleaning up __FILE__. This supports / and \ as path separator.
     289  // If the path separator is wrong then the full path will be printed;
     290  // it's a cosmetic problem only.
     291  static const char *
     292  tuktest_basename(const char *filename)
     293  {
     294  	for (const char *p = filename + strlen(filename); p > filename; --p)
     295  		if (*p == '/' || *p == '\\')
     296  			return p + 1;
     297  
     298  	return filename;
     299  }
     300  
     301  
     302  // Internal helper that prints the prefix of the fail/skip/error message line.
     303  static void
     304  tuktest_print_result_prefix(enum tuktest_result result,
     305  		const char *filename, unsigned line)
     306  {
     307  	// This is never called with TUKTEST_PASS but I kept it here anyway.
     308  	const char *result_str
     309  			= result == TUKTEST_PASS ? TUKTEST_STR_PASS
     310  			: result == TUKTEST_FAIL ? TUKTEST_STR_FAIL
     311  			: result == TUKTEST_SKIP ? TUKTEST_STR_SKIP
     312  			: TUKTEST_STR_ERROR;
     313  
     314  	const char *short_filename = tuktest_basename(filename);
     315  
     316  	if (tuktest_name != NULL)
     317  		printf("%s %s [%s:%u] ", result_str, tuktest_name,
     318  				short_filename, line);
     319  	else
     320  		printf("%s [%s:%u] ", result_str, short_filename, line);
     321  }
     322  
     323  
     324  // An entry for linked list of memory allocations.
     325  struct tuktest_malloc_record {
     326  	struct tuktest_malloc_record *next;
     327  	void *p;
     328  };
     329  
     330  // Linked list of per-test allocations. This is used when under tuktest_run().
     331  // These allocations are freed in tuktest_run() and, in case of a hard error,
     332  // also in tuktest_end().
     333  static struct tuktest_malloc_record *tuktest_malloc_test = NULL;
     334  
     335  // Linked list of global allocations. This is used allocations are made
     336  // outside tuktest_run(). These are freed in tuktest_end().
     337  static struct tuktest_malloc_record *tuktest_malloc_global = NULL;
     338  
     339  
     340  /// A wrapper for malloc() that never return NULL and the allocated memory is
     341  /// automatically freed at the end of tuktest_run() (if allocation was done
     342  /// within a test) or early in tuktest_end() (if allocation was done outside
     343  /// tuktest_run()).
     344  ///
     345  /// If allocation fails, a hard error is reported and this function won't
     346  /// return. Possible other tests won't be run (this will call exit()).
     347  #define tuktest_malloc(size) tuktest_malloc_impl(size, __FILE__, __LINE__)
     348  
     349  static void *
     350  tuktest_malloc_impl(size_t size, const char *filename, unsigned line)
     351  {
     352  	void *p = malloc(size == 0 ? 1 : size);
     353  	struct tuktest_malloc_record *r = malloc(sizeof(*r));
     354  
     355  	if (p == NULL || r == NULL) {
     356  		free(r);
     357  		free(p);
     358  
     359  		// Avoid %zu for portability to very old systems that still
     360  		// can compile C99 code.
     361  		tuktest_error_impl(filename, line,
     362  				"tuktest_malloc(%" TUKTEST_PRIu ") failed",
     363  				(tuktest_uint)size);
     364  	}
     365  
     366  	r->p = p;
     367  
     368  	if (tuktest_name == NULL) {
     369  		// We were called outside tuktest_run().
     370  		r->next = tuktest_malloc_global;
     371  		tuktest_malloc_global = r;
     372  	} else {
     373  		// We were called under tuktest_run().
     374  		r->next = tuktest_malloc_test;
     375  		tuktest_malloc_test = r;
     376  	}
     377  
     378  	return p;
     379  }
     380  
     381  
     382  /// Frees memory allocated using tuktest_malloc(). Usually this isn't needed
     383  /// as the memory is freed automatically.
     384  ///
     385  /// NULL is silently ignored.
     386  ///
     387  /// NOTE: Under tuktest_run() only memory allocated there can be freed.
     388  /// That is,  allocations done outside tuktest_run() can only be freed
     389  /// outside tuktest_run().
     390  #define tuktest_free(ptr) tuktest_free_impl(ptr, __FILE__, __LINE__)
     391  
     392  static void
     393  tuktest_free_impl(void *p, const char *filename, unsigned line)
     394  {
     395  	if (p == NULL)
     396  		return;
     397  
     398  	struct tuktest_malloc_record **r = tuktest_name != NULL
     399  			? &tuktest_malloc_test : &tuktest_malloc_global;
     400  
     401  	while (*r != NULL) {
     402  		struct tuktest_malloc_record *tmp = *r;
     403  
     404  		if (tmp->p == p) {
     405  			*r = tmp->next;
     406  			free(p);
     407  			free(tmp);
     408  			return;
     409  		}
     410  
     411  		r = &tmp->next;
     412  	}
     413  
     414  	tuktest_error_impl(filename, line, "tuktest_free: "
     415  			"Allocation matching the pointer was not found");
     416  }
     417  
     418  
     419  // Frees all allocates in the given record list. The argument must be
     420  // either &tuktest_malloc_test or &tuktest_malloc_global.
     421  static void
     422  tuktest_free_all(struct tuktest_malloc_record **r)
     423  {
     424  	while (*r != NULL) {
     425  		struct tuktest_malloc_record *tmp = *r;
     426  		*r = tmp->next;
     427  		free(tmp->p);
     428  		free(tmp);
     429  	}
     430  }
     431  
     432  
     433  /// Initialize the test framework. No other functions or macros
     434  /// from this file may be called before calling this.
     435  ///
     436  /// If the arguments from main() aren't available, use 0 and NULL.
     437  /// If these are set, then only a subset of tests can be run by
     438  /// specifying their names on the command line.
     439  #define tuktest_start(argc, argv) \
     440  do { \
     441  	tuktest_argc = argc; \
     442  	tuktest_argv = argv; \
     443  	if (!TUKTEST_TAP && !TUKTEST_QUIET) \
     444  		printf("=== %s ===\n", tuktest_basename(__FILE__)); \
     445  } while (0)
     446  
     447  
     448  /// If it can be detected early that no tests can be run, this macro can
     449  /// be called after tuktest_start() but before any tuktest_run() to print
     450  /// a reason why the tests were skipped. Note that this macro calls exit().
     451  ///
     452  /// Using "return tuktest_end();" in main() when no tests were run has
     453  /// the same result as tuktest_early_skip() except that then no reason
     454  /// for the skipping can be printed.
     455  #define tuktest_early_skip(...) \
     456  do { \
     457  	printf("%s [%s:%u] ", \
     458  			TUKTEST_TAP ? "1..0 # SKIP" : TUKTEST_STR_SKIP, \
     459  			tuktest_basename(__FILE__), __LINE__); \
     460  	printf(__VA_ARGS__); \
     461  	printf("\n"); \
     462  	if (!TUKTEST_TAP && !TUKTEST_QUIET) \
     463  		printf("=== END ===\n"); \
     464  	tuktest_catch_stdout_errors(); \
     465  	exit(TUKTEST_TAP ? EXIT_SUCCESS : TUKTEST_EXIT_SKIP); \
     466  } while (0)
     467  
     468  
     469  /// Some test programs need to do initializations before or between
     470  /// calls to tuktest_run(). If such initializations unexpectedly fail,
     471  /// tuktest_error() can be used to report it as a hard error outside
     472  /// test functions, for example, in main(). Then the remaining tests
     473  /// won't be run (this macro calls exit()).
     474  ///
     475  /// Typically tuktest_error() would be used before any tuktest_run()
     476  /// calls but it is also possible to use tuktest_error() after one or
     477  /// more tests have been run with tuktest_run(). This is in contrast to
     478  /// tuktest_early_skip() which must never be called after tuktest_run().
     479  ///
     480  /// NOTE: tuktest_start() must have been called before tuktest_error().
     481  ///
     482  /// NOTE: This macro can be called from test functions running under
     483  /// tuktest_run() but assert_error() is somewhat preferred in that context.
     484  #define tuktest_error(...) tuktest_error_impl(__FILE__, __LINE__, __VA_ARGS__)
     485  
     486  
     487  /// At the end of main() one should have "return tuktest_end();" which
     488  /// prints the stats or the TAP plan, and handles the exit status.
     489  /// Using exit(tuktest_end()) is OK too.
     490  ///
     491  /// If the test program can detect early that all tests must be skipped,
     492  /// then tuktest_early_skip() may be useful so that the reason why the
     493  /// tests were skipped can be printed.
     494  static int
     495  tuktest_end(void)
     496  {
     497  	tuktest_free_all(&tuktest_malloc_test);
     498  	tuktest_free_all(&tuktest_malloc_global);
     499  
     500  	unsigned total_tests = 0;
     501  	for (unsigned i = 0; i <= TUKTEST_ERROR; ++i)
     502  		total_tests += tuktest_stats[i];
     503  
     504  	if (tuktest_stats[TUKTEST_ERROR] == 0 && tuktest_argc > 1
     505  			&& (unsigned)(tuktest_argc - 1) > total_tests) {
     506  		printf(TUKTEST_STR_ERROR " Fewer tests were run than "
     507  				"specified on the command line. "
     508  				"Was a test name mistyped?\n");
     509  		++tuktest_stats[TUKTEST_ERROR];
     510  	}
     511  
     512  #if TUKTEST_TAP
     513  	// Print the plan only if no "Bail out!" has occurred.
     514  	// Print the skip directive if no tests were run.
     515  	// We cannot know the reason for the skip here though
     516  	// (see tuktest_early_skip()).
     517  	if (tuktest_stats[TUKTEST_ERROR] == 0)
     518  		printf("1..%u%s\n", total_tests,
     519  				total_tests == 0 ? " # SKIP" : "");
     520  
     521  	tuktest_catch_stdout_errors();
     522  	return EXIT_SUCCESS;
     523  #else
     524  	if (!TUKTEST_QUIET)
     525  		printf("---\n"
     526  				"%s# TOTAL: %u" TUKTEST_COLOR_OFF "\n"
     527  				"%s# PASS:  %u" TUKTEST_COLOR_OFF "\n"
     528  				"%s# SKIP:  %u" TUKTEST_COLOR_OFF "\n"
     529  				"%s# FAIL:  %u" TUKTEST_COLOR_OFF "\n"
     530  				"%s# ERROR: %u" TUKTEST_COLOR_OFF "\n"
     531  				"=== END ===\n",
     532  				TUKTEST_COLOR_TOTAL,
     533  				total_tests,
     534  				TUKTEST_COLOR_IF(
     535  					tuktest_stats[TUKTEST_PASS] > 0,
     536  					TUKTEST_COLOR_PASS),
     537  				tuktest_stats[TUKTEST_PASS],
     538  				TUKTEST_COLOR_IF(
     539  					tuktest_stats[TUKTEST_SKIP] > 0,
     540  					TUKTEST_COLOR_SKIP),
     541  				tuktest_stats[TUKTEST_SKIP],
     542  				TUKTEST_COLOR_IF(
     543  					tuktest_stats[TUKTEST_FAIL] > 0,
     544  					TUKTEST_COLOR_FAIL),
     545  				tuktest_stats[TUKTEST_FAIL],
     546  				TUKTEST_COLOR_IF(
     547  					tuktest_stats[TUKTEST_ERROR] > 0,
     548  					TUKTEST_COLOR_ERROR),
     549  				tuktest_stats[TUKTEST_ERROR]);
     550  
     551  	tuktest_catch_stdout_errors();
     552  
     553  	if (tuktest_stats[TUKTEST_ERROR] > 0)
     554  		return TUKTEST_EXIT_ERROR;
     555  
     556  	if (tuktest_stats[TUKTEST_FAIL] > 0)
     557  		return TUKTEST_EXIT_FAIL;
     558  
     559  	if (tuktest_stats[TUKTEST_SKIP] > 0 || total_tests == 0)
     560  		return TUKTEST_EXIT_SKIP;
     561  
     562  	return TUKTEST_EXIT_PASS;
     563  #endif
     564  }
     565  
     566  
     567  /// Runs the specified test function. Requires that tuktest_start()
     568  /// has already been called and that tuktest_end() has NOT been called yet.
     569  #define tuktest_run(testfunc) \
     570  	tuktest_run_test(&(testfunc), #testfunc)
     571  
     572  tuktest_maybe_unused
     573  static void
     574  tuktest_run_test(void (*testfunc)(void), const char *testfunc_str)
     575  {
     576  	// If any command line arguments were given, only the test functions
     577  	// named on the command line will be run.
     578  	if (tuktest_argc > 1) {
     579  		int i = 1;
     580  		while (strcmp(tuktest_argv[i], testfunc_str) != 0)
     581  			if (++i == tuktest_argc)
     582  				return;
     583  	}
     584  
     585  	// This is set so that failed assertions can print the correct
     586  	// test name even when the assertion is in a helper function
     587  	// called by the test function.
     588  	tuktest_name = testfunc_str;
     589  
     590  	// The way setjmp() may be called is very restrictive.
     591  	// A switch statement is one of the few conforming ways
     592  	// to get the value passed to longjmp(); doing something
     593  	// like "int x = setjmp(env)" is NOT allowed (undefined behavior).
     594  	switch (setjmp(tuktest_jmpenv)) {
     595  		case 0:
     596  			testfunc();
     597  			++tuktest_stats[TUKTEST_PASS];
     598  			if (!TUKTEST_QUIET)
     599  				printf(TUKTEST_STR_PASS " %s\n", tuktest_name);
     600  			break;
     601  
     602  		case TUKTEST_FAIL:
     603  			++tuktest_stats[TUKTEST_FAIL];
     604  			break;
     605  
     606  		case TUKTEST_SKIP:
     607  			++tuktest_stats[TUKTEST_SKIP];
     608  			break;
     609  
     610  		default:
     611  			++tuktest_stats[TUKTEST_ERROR];
     612  			exit(tuktest_end());
     613  	}
     614  
     615  	tuktest_free_all(&tuktest_malloc_test);
     616  	tuktest_name = NULL;
     617  }
     618  
     619  
     620  // Maximum allowed file size in tuktest_file_from_* macros and functions.
     621  #ifndef TUKTEST_FILE_SIZE_MAX
     622  #	define TUKTEST_FILE_SIZE_MAX (64L << 20)
     623  #endif
     624  
     625  /// Allocates memory and reads the specified file into a buffer.
     626  /// If the environment variable srcdir is set, it will be prefixed
     627  /// to the filename. Otherwise the filename is used as is (and so
     628  /// the behavior is identical to tuktest_file_from_builddir() below).
     629  ///
     630  /// On success the a pointer to malloc'ed memory is returned.
     631  /// The size of the allocation and the file is stored in *size.
     632  ///
     633  /// If anything goes wrong, a hard error is reported and this function
     634  /// won't return. Possible other tests won't be run (this will call exit()).
     635  ///
     636  /// Empty files and files over TUKTEST_FILE_SIZE_MAX are rejected.
     637  /// The assumption is that something is wrong in these cases.
     638  ///
     639  /// This function can be called either from outside the tests (like in main())
     640  /// or from tests run via tuktest_run(). Remember to free() the memory to
     641  /// keep Valgrind happy.
     642  #define tuktest_file_from_srcdir(filename, sizeptr) \
     643  	tuktest_file_from_x(getenv("srcdir"), filename, sizeptr, \
     644  			__FILE__, __LINE__)
     645  
     646  /// Like tuktest_file_from_srcdir except this reads from the current directory.
     647  #define tuktest_file_from_builddir(filename, sizeptr) \
     648  	tuktest_file_from_x(NULL, filename, sizeptr, __FILE__, __LINE__)
     649  
     650  // Internal helper for the macros above.
     651  tuktest_maybe_unused
     652  static void *
     653  tuktest_file_from_x(const char *prefix, const char *filename, size_t *size,
     654  		const char *prog_filename, unsigned prog_line)
     655  {
     656  	// If needed: buffer for holding prefix + '/' + filename + '\0'.
     657  	char *alloc_name = NULL;
     658  
     659  	// Buffer for the data read from the file.
     660  	void *buf = NULL;
     661  
     662  	// File being read
     663  	FILE *f = NULL;
     664  
     665  	// Error message to use under the "error:" label.
     666  	const char *error_msg = NULL;
     667  
     668  	if (filename == NULL) {
     669  		error_msg = "Filename is NULL";
     670  		filename = "(NULL)";
     671  		goto error;
     672  	}
     673  
     674  	if (filename[0] == '\0') {
     675  		error_msg = "Filename is an empty string";
     676  		filename = "(empty string)";
     677  		goto error;
     678  	}
     679  
     680  	if (size == NULL) {
     681  		error_msg = "The size argument is NULL";
     682  		goto error;
     683  	}
     684  
     685  	// If a prefix was given, construct the full filename.
     686  	if (prefix != NULL && prefix[0] != '\0') {
     687  		const size_t prefix_len = strlen(prefix);
     688  		const size_t filename_len = strlen(filename);
     689  
     690  		const size_t alloc_name_size
     691  				= prefix_len + 1 + filename_len + 1;
     692  		alloc_name = tuktest_malloc_impl(alloc_name_size,
     693  				prog_filename, prog_line);
     694  
     695  		memcpy(alloc_name, prefix, prefix_len);
     696  		alloc_name[prefix_len] = '/';
     697  		memcpy(alloc_name + prefix_len + 1, filename, filename_len);
     698  		alloc_name[prefix_len + 1 + filename_len] = '\0';
     699  
     700  		// Set filename to point to the new string. alloc_name
     701  		// can be freed unconditionally as it is NULL if a prefix
     702  		// wasn't specified.
     703  		filename = alloc_name;
     704  	}
     705  
     706  	f = fopen(filename, "rb");
     707  	if (f == NULL) {
     708  		error_msg = "Failed to open the file";
     709  		goto error;
     710  	}
     711  
     712  	// Get the size of the file and store it in *size.
     713  	//
     714  	// We assume that the file isn't big and even reject very big files.
     715  	// There is no need to use fseeko/ftello from POSIX to support
     716  	// large files. Using standard C functions is portable outside POSIX.
     717  	if (fseek(f, 0, SEEK_END) != 0) {
     718  		error_msg = "Seeking failed (fseek end)";
     719  		goto error;
     720  	}
     721  
     722  	const long end = ftell(f);
     723  	if (end < 0) {
     724  		error_msg = "Seeking failed (ftell)";
     725  		goto error;
     726  	}
     727  
     728  	if (end == 0) {
     729  		error_msg = "File is empty";
     730  		goto error;
     731  	}
     732  
     733  	if (end > TUKTEST_FILE_SIZE_MAX) {
     734  		error_msg = "File size exceeds TUKTEST_FILE_SIZE_MAX";
     735  		goto error;
     736  	}
     737  
     738  	*size = (size_t)end;
     739  	rewind(f);
     740  
     741  	buf = tuktest_malloc_impl(*size, prog_filename, prog_line);
     742  
     743  	const size_t amount = fread(buf, 1, *size, f);
     744  	if (ferror(f)) {
     745  		error_msg = "Read error";
     746  		goto error;
     747  	}
     748  
     749  	if (amount != *size) {
     750  		error_msg = "File is smaller than indicated by ftell()";
     751  		goto error;
     752  	}
     753  
     754  	const int fclose_ret = fclose(f);
     755  	f = NULL;
     756  	if (fclose_ret != 0) {
     757  		error_msg = "Error closing the file";
     758  		goto error;
     759  	}
     760  
     761  	tuktest_free(alloc_name);
     762  	return buf;
     763  
     764  error:
     765  	if (f != NULL)
     766  		(void)fclose(f);
     767  
     768  	tuktest_error_impl(prog_filename, prog_line,
     769  			"tuktest_file_from_x: %s: %s\n", filename, error_msg);
     770  }
     771  
     772  
     773  // Internal helper for assert_fail, assert_skip, and assert_error.
     774  #define tuktest_print_and_jump(result, ...) \
     775  do { \
     776  	tuktest_print_result_prefix(result, __FILE__, __LINE__); \
     777  	printf(__VA_ARGS__); \
     778  	printf("\n"); \
     779  	longjmp(tuktest_jmpenv, result); \
     780  } while (0)
     781  
     782  
     783  /// Unconditionally fails the test (non-zero exit status if not using TAP).
     784  /// Execution will continue from the next test.
     785  ///
     786  /// A printf format string is supported.
     787  /// If no extra message is wanted, use "" as the argument.
     788  #define assert_fail(...) tuktest_print_and_jump(TUKTEST_FAIL, __VA_ARGS__)
     789  
     790  
     791  /// Skips the test (exit status 77 if not using TAP).
     792  /// Execution will continue from the next test.
     793  ///
     794  /// If you can detect early that no tests can be run, tuktest_early_skip()
     795  /// might be a better way to skip the test(s). Especially in TAP mode this
     796  /// makes a difference as with assert_skip() it will list a skipped specific
     797  /// test name but with tuktest_early_skip() it will indicate that the whole
     798  /// test program was skipped (with tuktest_early_skip() the TAP plan will
     799  /// indicate zero tests).
     800  ///
     801  /// A printf format string is supported.
     802  /// If no extra message is wanted, use "" as the argument.
     803  #define assert_skip(...) tuktest_print_and_jump(TUKTEST_SKIP, __VA_ARGS__)
     804  
     805  
     806  /// Hard error (exit status 99 if not using TAP).
     807  /// The remaining tests in this program will not be run or reported.
     808  ///
     809  /// A printf format string is supported.
     810  /// If no extra message is wanted, use "" as the argument.
     811  #define assert_error(...) tuktest_print_and_jump(TUKTEST_ERROR, __VA_ARGS__)
     812  
     813  
     814  /// Fails the test if the test expression doesn't evaluate to false.
     815  #define assert_false(test_expr) \
     816  do { \
     817  	if (test_expr) \
     818  		assert_fail("assert_fail: '%s' is true but should be false", \
     819  				#test_expr); \
     820  } while (0)
     821  
     822  
     823  /// Fails the test if the test expression doesn't evaluate to true.
     824  #define assert_true(test_expr) \
     825  do { \
     826  	if (!(test_expr)) \
     827  		assert_fail("assert_true: '%s' is false but should be true", \
     828  				#test_expr); \
     829  } while (0)
     830  
     831  
     832  /// Fails the test if comparing the signed integer expressions using the
     833  /// specified comparison operator evaluates to false. For example,
     834  /// assert_int(foobar(), >=, 0) fails the test if 'foobar() >= 0' isn't true.
     835  /// For good error messages, the first argument should be the test expression
     836  /// and the third argument the reference value (usually a constant).
     837  ///
     838  /// For equality (==) comparison there is a assert_int_eq() which
     839  /// might be more convenient to use.
     840  #define assert_int(test_expr, cmp_op, ref_value) \
     841  do { \
     842  	const tuktest_int v_test_ = (test_expr); \
     843  	const tuktest_int v_ref_ = (ref_value); \
     844  	if (!(v_test_ cmp_op v_ref_)) \
     845  		assert_fail("assert_int: '%s == %" TUKTEST_PRId \
     846  				"' but expected '... %s %" TUKTEST_PRId "'", \
     847  				#test_expr, v_test_, #cmp_op, v_ref_); \
     848  } while (0)
     849  
     850  
     851  /// Like assert_int() but for unsigned integers.
     852  ///
     853  /// For equality (==) comparison there is a assert_uint_eq() which
     854  /// might be more convenient to use.
     855  #define assert_uint(test_expr, cmp_op, ref_value) \
     856  do { \
     857  	const tuktest_uint v_test_ = (test_expr); \
     858  	const tuktest_uint v_ref_ = (ref_value); \
     859  	if (!(v_test_ cmp_op v_ref_)) \
     860  		assert_fail("assert_uint: '%s == %" TUKTEST_PRIu \
     861  				"' but expected '... %s %" TUKTEST_PRIu "'", \
     862  				#test_expr, v_test_, #cmp_op, v_ref_); \
     863  } while (0)
     864  
     865  
     866  /// Fails the test if test expression doesn't equal the expected
     867  /// signed integer value.
     868  #define assert_int_eq(test_expr, ref_value) \
     869  	assert_int(test_expr, ==, ref_value)
     870  
     871  
     872  /// Fails the test if test expression doesn't equal the expected
     873  /// unsigned integer value.
     874  #define assert_uint_eq(test_expr, ref_value) \
     875  	assert_uint(test_expr, ==, ref_value)
     876  
     877  
     878  /// Fails the test if the test expression doesn't equal the expected
     879  /// enumeration value. This is like assert_int_eq() but the error message
     880  /// shows the enumeration constant names instead of their numeric values
     881  /// as long as the values are non-negative and not big.
     882  ///
     883  /// The third argument must be a table of string pointers. A pointer to
     884  /// a pointer doesn't work because this determines the number of elements
     885  /// in the array using sizeof. For example:
     886  ///
     887  ///     const char *my_enum_names[] = { "MY_FOO", "MY_BAR", "MY_BAZ" };
     888  ///     assert_enum_eq(some_func_returning_my_enum(), MY_BAR, my_enum_names);
     889  ///
     890  /// (If the reference value is out of bounds, both values are printed as
     891  /// an integer. If only test expression is out of bounds, it is printed
     892  /// as an integer and the reference as a string. Otherwise both are printed
     893  /// as a string.)
     894  #define assert_enum_eq(test_expr, ref_value, enum_strings) \
     895  do { \
     896  	const tuktest_int v_test_ = (test_expr); \
     897  	const tuktest_int v_ref_ = (ref_value); \
     898  	if (v_test_ != v_ref_) { \
     899  		const int array_len_ = (int)(sizeof(enum_strings) \
     900  				/ sizeof((enum_strings)[0])); \
     901  		if (v_ref_ < 0 || v_ref_ >= array_len_) \
     902  			assert_fail("assert_enum_eq: '%s == %" TUKTEST_PRId \
     903  					"' but expected " \
     904  					"'... == %" TUKTEST_PRId "'", \
     905  					#test_expr, v_test_, v_ref_); \
     906  		else if (v_test_ < 0 || v_test_ >= array_len_) \
     907  			assert_fail("assert_enum_eq: '%s == %" TUKTEST_PRId \
     908  					"' but expected '... == %s'", \
     909  					#test_expr, v_test_, \
     910  					(enum_strings)[v_ref_]); \
     911  		else \
     912  			assert_fail("assert_enum_eq: '%s == %s' " \
     913  					"but expected '... = %s'", \
     914  					#test_expr, (enum_strings)[v_test_], \
     915  					(enum_strings)[v_ref_]); \
     916  	} \
     917  } while (0)
     918  
     919  
     920  /// Fails the test if the specified bit isn't set in the test expression.
     921  #define assert_bit_set(test_expr, bit) \
     922  do { \
     923  	const tuktest_uint v_test_ = (test_expr); \
     924  	const unsigned v_bit_ = (bit); \
     925  	const tuktest_uint v_mask_ = (tuktest_uint)1 << v_bit_; \
     926  	if (!(v_test_ & v_mask_)) \
     927  		assert_fail("assert_bit_set: '%s == 0x%" TUKTEST_PRIX \
     928  				"' but bit %u (0x%" TUKTEST_PRIX ") " \
     929  				"is not set", \
     930  				#test_expr, v_test_, v_bit_, v_mask_); \
     931  } while (0)
     932  
     933  
     934  /// Fails the test if the specified bit is set in the test expression.
     935  #define assert_bit_not_set(test_expr, bit) \
     936  do { \
     937  	const tuktest_uint v_test_ = (test_expr); \
     938  	const unsigned v_bit_ = (bit); \
     939  	const tuktest_uint v_mask_ = (tuktest_uint)1 << v_bit_; \
     940  	if (v_test_ & v_mask_) \
     941  		assert_fail("assert_bit_not_set: '%s == 0x%" TUKTEST_PRIX \
     942  				"' but bit %u (0x%" TUKTEST_PRIX ") is set", \
     943  				#test_expr, v_test_, v_bit_, v_mask_); \
     944  } while (0)
     945  
     946  
     947  /// Fails the test if unless all bits that are set in the bitmask are also
     948  /// set in the test expression.
     949  #define assert_bitmask_set(test_expr, mask) \
     950  do { \
     951  	const tuktest_uint v_mask_ = (mask); \
     952  	const tuktest_uint v_test_ = (test_expr) & v_mask_; \
     953  	if (v_test_ != v_mask_) \
     954  		assert_fail("assert_bitmask_set: " \
     955  				"'((%s) & 0x%" TUKTEST_PRIX ") == " \
     956  				"0x%" TUKTEST_PRIX "' but expected " \
     957  				"'... == 0x%" TUKTEST_PRIX "'", \
     958  				#test_expr, v_mask_, v_test_, v_mask_); \
     959  } while (0)
     960  
     961  
     962  /// Fails the test if any of the bits that are set in the bitmask are also
     963  /// set in the test expression.
     964  #define assert_bitmask_not_set(test_expr, mask) \
     965  do { \
     966  	const tuktest_uint v_mask_ = (mask); \
     967  	const tuktest_uint v_test_ = (test_expr) & v_mask_; \
     968  	if (v_test_ != 0) \
     969  		assert_fail("assert_bitmask_not_set: "\
     970  				"'((%s) & 0x%" TUKTEST_PRIX ") == " \
     971  				"0x%" TUKTEST_PRIX "' but expected " \
     972  				"'... == 0'", \
     973  				#test_expr, v_mask_, v_test_); \
     974  } while (0)
     975  
     976  
     977  // Internal helper to add common code for string assertions.
     978  #define tuktest_str_helper1(macro_name, test_expr, ref_value) \
     979  	const char *v_test_ = (test_expr); \
     980  	const char *v_ref_ = (ref_value); \
     981  	if (v_test_ == NULL) \
     982  		assert_fail(macro_name ": Test expression '%s' is NULL", \
     983  				#test_expr); \
     984  	if (v_ref_ == NULL) \
     985  		assert_fail(macro_name ": Reference value '%s' is NULL", \
     986  				#ref_value)
     987  
     988  
     989  // Internal helper to add common code for string assertions and to check
     990  // that the reference value isn't an empty string.
     991  #define tuktest_str_helper2(macro_name, test_expr, ref_value) \
     992  	tuktest_str_helper1(macro_name, test_expr, ref_value); \
     993  	if (v_ref_[0] == '\0') \
     994  		assert_fail(macro_name ": Reference value is an empty string")
     995  
     996  
     997  /// Fails the test if the test expression evaluates to string that doesn't
     998  /// equal to the expected string.
     999  #define assert_str_eq(test_expr, ref_value) \
    1000  do { \
    1001  	tuktest_str_helper1("assert_str_eq", test_expr, ref_value); \
    1002  	if (strcmp(v_ref_, v_test_) != 0) \
    1003  		assert_fail("assert_str_eq: '%s' evaluated to '%s' " \
    1004  				"but expected '%s'", \
    1005  				#test_expr, v_test_, v_ref_); \
    1006  } while (0)
    1007  
    1008  
    1009  /// Fails the test if the test expression evaluates to a string that doesn't
    1010  /// contain the reference value as a substring. Also fails the test if
    1011  /// the reference value is an empty string.
    1012  #define assert_str_contains(test_expr, ref_value) \
    1013  do { \
    1014  	tuktest_str_helper2("assert_str_contains", test_expr, ref_value); \
    1015  	if (strstr(v_test_, v_ref_) == NULL) \
    1016  		assert_fail("assert_str_contains: '%s' evaluated to '%s' " \
    1017  				"which doesn't contain '%s'", \
    1018  				#test_expr, v_test_, v_ref_); \
    1019  } while (0)
    1020  
    1021  
    1022  /// Fails the test if the test expression evaluates to a string that
    1023  /// contains the reference value as a substring. Also fails the test if
    1024  /// the reference value is an empty string.
    1025  #define assert_str_doesnt_contain(test_expr, ref_value) \
    1026  do { \
    1027  	tuktest_str_helper2("assert_str_doesnt_contain", \
    1028  			test_expr, ref_value); \
    1029  	if (strstr(v_test_, v_ref_) != NULL) \
    1030  		assert_fail("assert_str_doesnt_contain: " \
    1031  				"'%s' evaluated to '%s' which contains '%s'", \
    1032  				#test_expr, v_test_, v_ref_); \
    1033  } while (0)
    1034  
    1035  
    1036  /// Fails the test if the first array_size elements of the test array
    1037  /// don't equal to correct_array.
    1038  ///
    1039  /// NOTE: This avoids %zu for portability to very old systems that still
    1040  /// can compile C99 code.
    1041  #define assert_array_eq(test_array, correct_array, array_size) \
    1042  do { \
    1043  	for (size_t i_ = 0; i_ < (array_size); ++i_) \
    1044  		if ((test_array)[i_] != (correct_array)[i_]) \
    1045  			assert_fail("assert_array_eq: " \
    1046  					"%s[%" TUKTEST_PRIu "] != "\
    1047  					"%s[%" TUKTEST_PRIu "] " \
    1048  					"but should be equal", \
    1049  					#test_array, (tuktest_uint)i_, \
    1050  					#correct_array, (tuktest_uint)i_); \
    1051  } while (0)
    1052  
    1053  #endif