Files
vlibc/tests/test_strerror.c

170 lines
5.5 KiB
C

/*
* 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.
*
* All diagnostics go through raw SYS_write (no stdio): under -Iinclude the
* vlibc public headers shadow GCC's internal ones, so mixing in a host
* <stdio.h> breaks compilation (glibc's stdio.h pulls in our minimal
* <stdarg.h>, which lacks __gnuc_va_list). <string.h> below is vlibc's own
* header, which is safe.
*
* 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 <string.h>
#include "../src/internal/syscall.h"
static int failures;
/* Write a NUL-terminated string to fd via the raw syscall layer. */
static void
say(int fd, const char *s)
{
long n = 0;
while (s[n] != '\0')
{
n++;
}
__syscall3(SYS_write, fd, (long)s, n);
}
static void
check(int cond, const char *what)
{
if (!cond)
{
say(2, "FAIL: ");
say(2, what);
say(2, "\n");
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)
{
say(1, "strerror(9999)=NULL\n");
return 1;
}
say(1, "strerror(9999)=");
say(1, s);
say(1, "\n");
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)
{
say(2, "FAILED\n");
}
else
{
say(1, "strerror ok\n");
}
return failures == 0 ? 0 : 1;
}