170 lines
5.5 KiB
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;
|
|
}
|