#include "akerror.h" #include "err_capture.h" #include /* * AKERR_MAX_ERR_VALUE sizes the __AKERR_ERROR_NAMES table and is the upper bound * akerr_name_for_status() accepts. If it is smaller than the highest AKERR_* * code, those codes silently lose their names (this was a real bug: the max was * +15 while AKERR_BADEXC is +17). * * Rather than hardcode the list of codes (which rots the moment someone adds a * code), this test parses the generated akerror.h, discovers every * #define AKERR_ (AKERR_LAST_ERRNO_VALUE + ) * finds the highest offset actually defined, and verifies AKERR_MAX_ERR_VALUE * covers it. The path to the header the library was built from is injected by * CMake as AKERR_GENERATED_HEADER. */ #ifndef AKERR_GENERATED_HEADER #error "AKERR_GENERATED_HEADER (path to generated akerror.h) must be defined" #endif int main(void) { FILE *fh = fopen(AKERR_GENERATED_HEADER, "r"); AKERR_CHECK(fh != NULL); /* #define AKERR_NAME (AKERR_LAST_ERRNO_VALUE + N) */ regex_t re; const char *pattern = "^[[:space:]]*#define[[:space:]]+(AKERR_[A-Za-z0-9_]+)[[:space:]]+" "\\(AKERR_LAST_ERRNO_VALUE[[:space:]]*\\+[[:space:]]*([0-9]+)\\)"; AKERR_CHECK(regcomp(&re, pattern, REG_EXTENDED) == 0); int highest_code = -1; /* highest offset among real error codes */ char highest_name[64] = ""; int max_err_value = -1; /* offset parsed from AKERR_MAX_ERR_VALUE */ int code_count = 0; char line[4096]; regmatch_t m[3]; while ( fgets(line, sizeof(line), fh) != NULL ) { if ( regexec(&re, line, 3, m, 0) != 0 ) { continue; } char name[64]; int nlen = (int)(m[1].rm_eo - m[1].rm_so); if ( nlen >= (int)sizeof(name) ) { nlen = (int)sizeof(name) - 1; } memcpy(name, line + m[1].rm_so, nlen); name[nlen] = '\0'; char num[16]; int vlen = (int)(m[2].rm_eo - m[2].rm_so); if ( vlen >= (int)sizeof(num) ) { vlen = (int)sizeof(num) - 1; } memcpy(num, line + m[2].rm_so, vlen); num[vlen] = '\0'; int offset = atoi(num); if ( strcmp(name, "AKERR_MAX_ERR_VALUE") == 0 ) { max_err_value = offset; } else { code_count++; if ( offset > highest_code ) { highest_code = offset; snprintf(highest_name, sizeof(highest_name), "%s", name); } } } regfree(&re); fclose(fh); /* We must have actually parsed both the codes and the ceiling. */ AKERR_CHECK(code_count > 0); AKERR_CHECK(highest_code > 0); AKERR_CHECK(max_err_value > 0); /* Guard against parsing a different header than the one compiled in. */ AKERR_CHECK(max_err_value == (AKERR_MAX_ERR_VALUE - AKERR_LAST_ERRNO_VALUE)); /* The actual invariant: every defined code is indexable in the names table. */ AKERR_CHECK(max_err_value >= highest_code); fprintf(stderr, "err_maxval ok (%d codes parsed, highest %s at +%d, max +%d)\n", code_count, highest_name, highest_code, max_err_value); return 0; }