feat(errno): public errno.h with strerror/strerror_r
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* vlibc — strerror/strerror_r test (todo 2).
|
||||
*
|
||||
* Exercises the public <errno.h> end to end:
|
||||
*
|
||||
* 1. strerror(0) is "Success"; known values return the exact message and
|
||||
* are non-NULL and distinct; aliases (EWOULDBLOCK/EDEADLOCK) resolve to
|
||||
* their canonical entries; the last table value (EHWPOISON) is covered.
|
||||
* 2. Unknown errno values never return NULL and never crash: 9999 and the
|
||||
* ABI-gap value 41 format as "Unknown error <N>", negatives too.
|
||||
* 3. strerror_r (XSI): returns 0 and fills buf with the right text; an
|
||||
* unknown value still returns 0 with "Unknown error <N>"; truncation
|
||||
* into a tiny buf stays NUL-terminated; buflen 0 writes nothing; a NULL
|
||||
* buf with buflen > 0 returns EINVAL.
|
||||
*
|
||||
* The test deliberately never touches errno itself: under the host libc the
|
||||
* TCB slot our errno macro addresses is glibc's private TLS state, and
|
||||
* writing it would corrupt the host (see tests/syscall_test.c). All coverage
|
||||
* here is through the errnum parameters.
|
||||
*
|
||||
* Not part of the library proper; compiled manually for this todo (the
|
||||
* tests/ + make check wiring is owned by a later todo).
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static int failures;
|
||||
|
||||
static void
|
||||
check(int cond, const char *what)
|
||||
{
|
||||
if (!cond)
|
||||
{
|
||||
printf("FAIL: %s\n", what);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Failure scenario (-f): an unknown errno must yield "Unknown error 9999",
|
||||
* non-NULL, without crashing. Prints the observed string; exits 0 only when
|
||||
* the failure behaved exactly as specified.
|
||||
*/
|
||||
static int
|
||||
failure_scenario(void)
|
||||
{
|
||||
const char *s = strerror(9999);
|
||||
|
||||
if (s == NULL)
|
||||
{
|
||||
printf("strerror(9999)=NULL\n");
|
||||
return 1;
|
||||
}
|
||||
printf("strerror(9999)=%s\n", s);
|
||||
return strcmp(s, "Unknown error 9999") == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
const char *a;
|
||||
const char *b;
|
||||
char buf[64];
|
||||
char tiny[8];
|
||||
char untouched[4];
|
||||
int r;
|
||||
|
||||
if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'f')
|
||||
{
|
||||
return failure_scenario();
|
||||
}
|
||||
|
||||
/* 1. Known values: exact text, non-NULL, distinct. */
|
||||
check(strcmp(strerror(0), "Success") == 0, "strerror(0) == \"Success\"");
|
||||
check(strcmp(strerror(2), "No such file or directory") == 0,
|
||||
"strerror(2) == \"No such file or directory\"");
|
||||
check(strcmp(strerror(EINVAL), "Invalid argument") == 0,
|
||||
"strerror(EINVAL) == \"Invalid argument\"");
|
||||
check(strcmp(strerror(EPERM), "Operation not permitted") == 0,
|
||||
"strerror(EPERM) == \"Operation not permitted\"");
|
||||
|
||||
a = strerror(EINVAL);
|
||||
b = strerror(EPERM);
|
||||
check(a != NULL && b != NULL, "known values non-NULL");
|
||||
check(a != b, "known values distinct");
|
||||
|
||||
/* Aliases resolve to their canonical entries. */
|
||||
check(strerror(EWOULDBLOCK) == strerror(EAGAIN), "EWOULDBLOCK == EAGAIN");
|
||||
check(strerror(EDEADLOCK) == strerror(EDEADLK), "EDEADLOCK == EDEADLK");
|
||||
|
||||
/* Table ends: last defined value and ABI gap values. */
|
||||
check(strerror(EHWPOISON) != NULL, "EHWPOISON (last value) non-NULL");
|
||||
check(strcmp(strerror(41), "Unknown error 41") == 0, "ABI gap 41 unknown");
|
||||
check(strcmp(strerror(58), "Unknown error 58") == 0, "ABI gap 58 unknown");
|
||||
|
||||
/* 2. Unknown values: never NULL, never crash, formatted. */
|
||||
a = strerror(9999);
|
||||
check(a != NULL, "strerror(9999) non-NULL");
|
||||
check(strcmp(a, "Unknown error 9999") == 0, "strerror(9999) text");
|
||||
|
||||
a = strerror(-7);
|
||||
check(a != NULL, "strerror(-7) non-NULL");
|
||||
check(strcmp(a, "Unknown error -7") == 0, "strerror(-7) text");
|
||||
|
||||
/* 3. strerror_r: XSI int return, exact fill. */
|
||||
r = strerror_r(EDOM, buf, sizeof buf);
|
||||
check(r == 0, "strerror_r(EDOM) returns 0");
|
||||
check(strcmp(buf, "Numerical argument out of domain") == 0, "strerror_r(EDOM) fills buf");
|
||||
|
||||
/* Unknown errno still succeeds and formats into buf (XSI). */
|
||||
r = strerror_r(9999, buf, sizeof buf);
|
||||
check(r == 0, "strerror_r(9999) returns 0 (XSI)");
|
||||
check(strcmp(buf, "Unknown error 9999") == 0, "strerror_r(9999) text");
|
||||
|
||||
/* Truncation into a tiny buf stays NUL-terminated. */
|
||||
r = strerror_r(ENAMETOOLONG, tiny, sizeof tiny);
|
||||
check(r == 0, "strerror_r(ENAMETOOLONG, tiny) returns 0");
|
||||
check(tiny[sizeof tiny - 1] == '\0', "tiny buf NUL-terminated");
|
||||
check(strcmp(tiny, "File na") == 0, "tiny buf truncated prefix");
|
||||
|
||||
/* buflen 0: nothing written, still success. */
|
||||
untouched[0] = 'X';
|
||||
untouched[1] = 'X';
|
||||
untouched[2] = 'X';
|
||||
untouched[3] = '\0';
|
||||
r = strerror_r(EDOM, untouched, 0);
|
||||
check(r == 0, "strerror_r(EDOM, buf, 0) returns 0");
|
||||
check(untouched[0] == 'X', "buflen 0 writes nothing");
|
||||
|
||||
/* NULL buf with buflen > 0: EINVAL. */
|
||||
r = strerror_r(EDOM, NULL, 16);
|
||||
check(r == EINVAL, "strerror_r(NULL buf) returns EINVAL");
|
||||
|
||||
if (failures > 0)
|
||||
{
|
||||
printf("FAILED\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("strerror ok\n");
|
||||
}
|
||||
return failures == 0 ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user