user: implement mlibc as the libc, finally.

It's finally done..

Signed-off-by: kaguya <vpshinomiya@protonmail.com>
This commit is contained in:
kaguya
2026-05-02 03:31:49 -04:00
parent 2fa39ad85a
commit 9a9b91c940
2387 changed files with 152741 additions and 315 deletions
+21
View File
@@ -0,0 +1,21 @@
#include <signal.h>
#include <assert.h>
#include <stdlib.h>
void handler(int sig, siginfo_t *info, void *ctx) {
(void)sig;
(void)info;
(void)ctx;
}
int main() {
struct sigaction sa;
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
int ret = sigaction(SIGABRT, &sa, NULL);
assert(!ret);
abort();
}
+125
View File
@@ -0,0 +1,125 @@
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#ifdef USE_HOST_LIBC
#define TEST_PORT 31337
#else
#define TEST_PORT 42069
#endif
static struct sockaddr_in connect_addr, accept_addr;
static int listen_fd;
int permutations[] = {
0,
SOCK_CLOEXEC,
SOCK_NONBLOCK,
SOCK_CLOEXEC | SOCK_NONBLOCK,
};
static bool run_test(int flags)
{
int connect_fd;
int fd_flags, access;
socklen_t addrlen;
connect_fd = socket(AF_INET, SOCK_STREAM, 0);
connect(connect_fd, (struct sockaddr *)&connect_addr, sizeof(connect_addr));
addrlen = sizeof(accept_addr);
int accept_fd = accept4(listen_fd, (struct sockaddr *)&accept_addr, &addrlen, flags);
if(accept_fd == -1) {
fprintf(stderr, "accept4 failed: %s\n", strerror(errno));
goto cleanup;
}
fd_flags = fcntl(accept_fd, F_GETFD);
if(!(!!(fd_flags & FD_CLOEXEC) == !!(flags & SOCK_CLOEXEC))) {
fprintf(stderr, "CLOEXEC mismatch, got %d instead of %d\n", !!(fd_flags & FD_CLOEXEC), !!(flags & SOCK_CLOEXEC));
goto cleanup;
}
access = fcntl(accept_fd, F_GETFL);
if(!(!!(access & O_NONBLOCK) == !!(flags & SOCK_NONBLOCK))) {
fprintf(stderr, "NONBLOCK flag mismatch, %d vs %d\n", !!(access & O_NONBLOCK), !!(flags & SOCK_NONBLOCK));
goto cleanup;
}
close(accept_fd);
close(connect_fd);
fprintf(stderr, "tested CLOEXEC %d, NONBLOCK %d\n", !!(flags & SOCK_CLOEXEC), !!(flags & SOCK_NONBLOCK));
return true;
cleanup:
close(accept_fd);
close(connect_fd);
return false;
}
static int socket_setup(void)
{
struct sockaddr_in addr;
int reuseaddr;
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(TEST_PORT);
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if(socket_fd == -1) {
fprintf(stderr, "socket failed: %s\n", strerror(errno));
exit(1);
}
reuseaddr = 1;
int ret = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr));
if(ret == -1) {
fprintf(stderr, "setsockopt failed: %s\n", strerror(errno));
exit(1);
}
ret = bind(socket_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
if(ret == -1) {
fprintf(stderr, "bind failed: %s\n", strerror(errno));
exit(1);
}
ret = listen(socket_fd, 5);
if(ret == -1) {
fprintf(stderr, "listen failed: %s\n", strerror(errno));
exit(1);
}
return socket_fd;
}
int main() {
memset(&connect_addr, 0, sizeof(connect_addr));
connect_addr.sin_family = AF_INET;
connect_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
connect_addr.sin_port = htons(TEST_PORT);
for(size_t i = 0; i < 4; i++) {
listen_fd = socket_setup();
if(!run_test(permutations[i])) {
exit(1);
}
close(listen_fd);
}
return 0;
}
+32
View File
@@ -0,0 +1,32 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <fcntl.h>
#ifdef USE_HOST_LIBC
#define TEST_FILE "access-host-libc.tmp"
#else
#define TEST_FILE "access.tmp"
#endif
int main() {
// Make sure that it wasn't created by a previous test run
unlink(TEST_FILE);
assert(access(TEST_FILE, F_OK) == -1);
assert(access(TEST_FILE, W_OK) == -1);
assert(access(TEST_FILE, R_OK) == -1);
assert(access(TEST_FILE, X_OK) == -1);
close(open(TEST_FILE, O_CREAT | O_RDWR, 0666));
assert(access(TEST_FILE, F_OK) == 0);
assert(access(TEST_FILE, W_OK) == 0);
assert(access(TEST_FILE, R_OK) == 0);
assert(access(TEST_FILE, X_OK) == -1);
unlink(TEST_FILE);
return 0;
}
+36
View File
@@ -0,0 +1,36 @@
#include <signal.h>
#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static volatile int alarms_fired = 0;
static void sigalrm_handler(int signal) {
if(signal == SIGALRM)
alarms_fired++;
}
int main() {
signal(SIGALRM, sigalrm_handler);
alarms_fired = 0;
unsigned int ret = alarm(10);
assert(!ret);
sleep(1);
ret = alarm(1);
assert(ret == 9);
sleep(2);
if(alarms_fired != 1) {
fprintf(stderr, "alarm handler fired %u times instead of 1\n", alarms_fired);
exit(1);
}
return 0;
}
+19
View File
@@ -0,0 +1,19 @@
#include <assert.h>
#include <libgen.h>
#include <string.h>
int main() {
/* intentionally a macro: basename modifies its argument */
#define test_string(x, expectval) do { \
char str[] = x; \
assert(strcmp(basename(str), expectval) == 0); \
} while (0)
test_string("/usr/lib", "lib");
test_string("/usr/", "usr");
test_string("/", "/");
test_string(".", ".");
test_string("..", "..");
return 0;
}
+39
View File
@@ -0,0 +1,39 @@
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#ifdef USE_HOST_LIBC
#define TEST_FILE "dprintf-host-libc.tmp"
#else
#define TEST_FILE "dprintf.tmp"
#endif
int main() {
int fd = open(TEST_FILE, O_RDWR | O_CREAT, 0666);
assert(fd > -1);
int ret = dprintf(fd, "aaa");
assert(ret == 3);
// Check if we can read back the same things.
// Also checks to see if the fd is still open,
// which is issue #199.
off_t seek = lseek(fd, 0, SEEK_SET);
assert(seek == 0);
char buf[3];
ssize_t num_read = read(fd, buf, 3);
assert(num_read == 3);
assert(buf[0] == 'a'
&& buf[1] == 'a'
&& buf[2] == 'a');
// All the tests pass, now we can unlink the file.
ret = unlink(TEST_FILE);
assert(ret == 0);
return 0;
}
+39
View File
@@ -0,0 +1,39 @@
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define TEST_FILE "fdopen.tmp"
int main() {
int fd = open(TEST_FILE, O_CREAT | O_RDWR, 0666);
assert(fd >= 0);
char *str = "mlibc fdopen test";
assert(write(fd, str, strlen(str)));
// Seek to the beginning, then reopen with fdopen in append mode.
lseek(fd, 0, SEEK_SET);
FILE *file = fdopen(fd, "a");
assert(file);
// Append and close.
str = " appended";
fwrite(str, strlen(str), 1, file);
fflush(file);
fclose(file);
// Open it again and check that the append succeeded.
fd = open(TEST_FILE, O_RDONLY);
assert(fd >= 0);
file = fdopen(fd, "r");
assert(file);
str = "mlibc fdopen test appended";
char buf[100] = {0};
assert(fread(buf, 1, strlen(str), file));
assert(!strcmp(buf, str));
fclose(file);
return 0;
}
+18
View File
@@ -0,0 +1,18 @@
#include <assert.h>
#include <strings.h>
#include <limits.h>
int main(void){
assert(ffs(0x8000) == 16);
assert(ffs(0) == 0);
assert(ffs(INT_MAX - 1) == 2);
assert(ffs(INT_MAX) == 1);
assert(ffs(INT_MIN) == (int)(sizeof(int) * CHAR_BIT));
assert(ffs(INT_MIN + 1) == 1);
for (int i = 1; i < 0x1000; i++) {
assert(ffs(i) - 1 == __builtin_ctz(i));
}
return 0;
}
@@ -0,0 +1,35 @@
#include <assert.h>
#include <time.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
static void *worker(void *arg) {
(void)arg;
flockfile(stdout);
fputs_unlocked("hello from worker", stdout);
funlockfile(stdout);
return NULL;
}
int main() {
// Check that recursive locking works.
assert(!ftrylockfile(stdout));
flockfile(stdout);
flockfile(stdout);
funlockfile(stdout);
funlockfile(stdout);
funlockfile(stdout);
assert(!ftrylockfile(stdout));
pthread_t thread;
int ret = pthread_create(&thread, NULL, &worker, NULL);
assert(!ret);
sleep(1);
funlockfile(stdout);
assert(!pthread_join(thread, NULL));
return 0;
}
+150
View File
@@ -0,0 +1,150 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER0_SIZE 0x1000
#define BUFFER1 "Hello world"
#define BUFFER1_SIZE (sizeof(BUFFER1))
int main() {
// test seek with mode "r"
FILE *f = fmemopen(NULL, BUFFER0_SIZE, "r");
assert(f);
int ret = fseek(f, 0, SEEK_END);
assert(!ret);
long pos = ftell(f);
fprintf(stderr, "pos = %ld\n", pos);
// Despite the fact that this is correct behavior (see below),
// this sometimes fails on glibc; newlib seems to get this wrong, too.
// to quote what posix says about it:
// > The stream shall also maintain the size of the current buffer contents;
// > use of fseek() or fseeko() on the stream with SEEK_END shall seek relative to this size.
// > For modes r and r+ the size shall be set to the value given by the size argument.
#if !defined(__GLIBC__)
assert(pos == BUFFER0_SIZE);
#endif
fclose(f);
// test seek with mode "w"
f = fmemopen(NULL, BUFFER0_SIZE, "w");
assert(f);
ret = fseek(f, 0, SEEK_END);
assert(!ret);
pos = ftell(f);
assert(pos == 0);
fclose(f);
// test seek with mode "a" and a NULL buffer
f = fmemopen(NULL, BUFFER0_SIZE, "a");
assert(f);
ret = fseek(f, 0, SEEK_END);
assert(!ret);
pos = ftell(f);
assert(pos == 0);
fclose(f);
// test seek with mode "a" and a buffer containing a '\0'
f = fmemopen(BUFFER1, BUFFER1_SIZE + 2, "a");
assert(f);
pos = ftell(f);
assert(pos == (long) (BUFFER1_SIZE - 1));
ret = fseek(f, 0, SEEK_SET);
assert(!ret);
pos = ftell(f);
assert(!pos);
ret = fseek(f, 0, SEEK_END);
assert(!ret);
pos = ftell(f);
assert(pos == (long) (BUFFER1_SIZE - 1));
fclose(f);
// test seek with mode "a" and a buffer not containing a '\0'
f = fmemopen(BUFFER1, BUFFER1_SIZE - 2, "a");
assert(f);
ret = fseek(f, 0, SEEK_END);
assert(!ret);
pos = ftell(f);
assert(pos == (long) (BUFFER1_SIZE - 2));
fclose(f);
f = fmemopen(BUFFER1, BUFFER1_SIZE, "r");
assert(f);
ret = fseek(f, 0, SEEK_SET);
assert(!ret);
char buf[BUFFER1_SIZE];
int read = fread(buf, 1, BUFFER1_SIZE - 2, f);
assert(read == BUFFER1_SIZE - 2);
assert(!strncmp(BUFFER1, buf, BUFFER1_SIZE - 2));
fseek(f, 0, SEEK_END);
read = fread(buf, 1, 2, f);
assert(read == 0);
assert(feof(f));
fclose(f);
// Open a buffer for read+write
char *buf1 = strdup(BUFFER1);
f = fmemopen(buf1, BUFFER1_SIZE, "w+");
assert(f);
assert(strlen(BUFFER1) == BUFFER1_SIZE - 1);
// seek to somewhere in the middle of the buffer
fseek(f, BUFFER1_SIZE - 5, SEEK_SET);
// write as much data to it as possible
read = fwrite(BUFFER1, 1, 9, f);
rewind(f);
// seek the the same position in the middle of the buffer
ret = fseek(f, BUFFER1_SIZE - 5, SEEK_SET);
assert(!ret);
memset(buf, 0, BUFFER1_SIZE);
// read what we just wrote
read = fread(buf, 1, 5, f);
// check that the write got correctly truncated
fprintf(stderr, "buf '%s' (%zu)\n", buf, strlen(buf));
assert(!strncmp(BUFFER1, buf, 4) && strlen(buf) == 4);
fclose(f);
free(buf1);
char *buf2 = strdup(BUFFER1);
f = fmemopen(buf2, 0, "r");
assert(f || errno == EINVAL);
if(f) {
memset(buf, 0, BUFFER1_SIZE);
read = fread(buf, 10, 1, f);
assert(!read);
rewind(f);
read = fwrite(BUFFER1, 1, 12, f);
assert(read == 0);
fclose(f);
}
free(buf2);
return 0;
}
@@ -0,0 +1,71 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
struct testcookie {
bool read;
bool write;
bool seek;
bool close;
};
ssize_t cookie_read(void *c, char *buf, size_t size) {
(void) buf;
struct testcookie *cookie = c;
cookie->read = true;
return size;
}
ssize_t cookie_write(void *c, const char *buf, size_t size) {
(void) buf;
struct testcookie *cookie = c;
cookie->write = true;
return size;
}
int cookie_seek(void *c, off64_t *offset, int whence) {
(void) offset;
(void) whence;
struct testcookie *cookie = c;
cookie->seek = true;
return 0;
}
int cookie_close(void *c) {
struct testcookie *cookie = c;
cookie->close = true;
return 0;
}
int main() {
struct testcookie cookie = { false, false, false, false };
cookie_io_functions_t funcs = {
.read = cookie_read,
.write = cookie_write,
.seek = cookie_seek,
.close = cookie_close,
};
FILE *stream = fopencookie(&cookie, "w+", funcs);
assert(stream);
unsigned char buf[1];
int ret = fread(buf, 1, 1, stream);
assert(ret == 1);
ret = fwrite(buf, 1, 1, stream);
assert(ret == 1);
ret = fseek(stream, 0, SEEK_SET);
assert(!ret);
ret = fclose(stream);
assert(!ret);
assert(cookie.read && cookie.write && cookie.seek && cookie.close);
return 0;
}
@@ -0,0 +1,235 @@
#if !defined(USE_HOST_LIBC)
#include <mlibc-config.h>
#endif
#include <netdb.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#if __MLIBC_LINUX_OPTION || defined(USE_HOST_LIBC)
#include <ifaddrs.h>
static bool has_ipv4_addr(void) {
struct ifaddrs *addrs;
if (getifaddrs(&addrs)) {
return false;
}
int found = 0;
for (struct ifaddrs *cur = addrs; cur; cur = cur->ifa_next) {
if (cur->ifa_addr && (cur->ifa_flags & IFF_UP) && cur->ifa_addr->sa_family == AF_INET && strncmp(cur->ifa_name, "lo", IF_NAMESIZE)) {
found = 1;
break;
}
}
freeifaddrs(addrs);
return found;
}
static bool has_ipv6_addr(void) {
struct ifaddrs *addrs;
if (getifaddrs(&addrs)) {
return false;
}
int found = 0;
for (struct ifaddrs *cur = addrs; cur; cur = cur->ifa_next) {
if (cur->ifa_addr && (cur->ifa_flags & IFF_UP) && cur->ifa_addr->sa_family == AF_INET6 && strncmp(cur->ifa_name, "lo", IF_NAMESIZE)) {
found = 1;
break;
}
}
freeifaddrs(addrs);
return found;
}
#endif // __MLIBC_LINUX_OPTION || defined(USE_HOST_LIBC)
int main() {
struct addrinfo *res = NULL;
struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int ret = getaddrinfo(NULL, "443", &hints, &res);
assert(ret == 0);
struct sockaddr_in *addr = (struct sockaddr_in*)(res[0].ai_addr);
assert(addr->sin_port == htons(443));
assert(res[0].ai_socktype == SOCK_STREAM);
assert(res[0].ai_protocol == IPPROTO_TCP);
freeaddrinfo(res);
res = NULL;
/* check we can resolve any domain */
ret = getaddrinfo("example.net", NULL, &hints, &res);
assert(ret == 0);
freeaddrinfo(res);
res = NULL;
hints.ai_flags = AI_NUMERICHOST;
ret = getaddrinfo("10.10.10.10", NULL, &hints, &res);
assert(ret == 0);
addr = (struct sockaddr_in*)res[0].ai_addr;
assert((addr->sin_addr.s_addr & 0xFF) == 10);
assert(((addr->sin_addr.s_addr >> 8) & 0xFF) == 10);
assert(((addr->sin_addr.s_addr >> 16) & 0xFF) == 10);
assert(((addr->sin_addr.s_addr >> 24) & 0xFF) == 10);
freeaddrinfo(res);
res = NULL;
ret = getaddrinfo("example.net", NULL, &hints, &res);
assert(ret == EAI_NONAME);
freeaddrinfo(res);
res = NULL;
hints.ai_family = AF_INET6;
hints.ai_flags = AI_NUMERICHOST;
ret = getaddrinfo("::1", "1234", &hints, &res);
assert(ret == 0);
assert(res[0].ai_family == AF_INET6);
struct sockaddr_in6 *addr6 = (struct sockaddr_in6*) res[0].ai_addr;
assert(addr6->sin6_port == htons(1234));
assert(addr6->sin6_addr.s6_addr[15] == 1);
for (int i = 0; i < 15; i++) {
assert(addr6->sin6_addr.s6_addr[i] == 0);
}
freeaddrinfo(res);
res = NULL;
hints.ai_family = AF_INET6;
hints.ai_flags = 0;
ret = getaddrinfo("example.net", "443", &hints, &res);
assert(ret == 0);
assert(res[0].ai_family == AF_INET6);
addr6 = (struct sockaddr_in6*)res[0].ai_addr;
assert(addr6->sin6_port == htons(443));
freeaddrinfo(res);
res = NULL;
#if __MLIBC_LINUX_OPTION || defined(USE_HOST_LIBC)
// Test with AF_INET
hints.ai_family = AF_INET;
ret = getaddrinfo("localhost", NULL, &hints, &res);
if (has_ipv4_addr()) {
assert(ret == 0);
for(struct addrinfo *p = res; p != NULL; p = p->ai_next) {
assert(p->ai_family == AF_INET);
}
} else {
assert(ret != 0);
}
freeaddrinfo(res);
res = NULL;
// Test with AF_INET6
hints.ai_family = AF_INET6;
ret = getaddrinfo("localhost", NULL, &hints, &res);
assert(ret == 0);
for(struct addrinfo *p = res; p != NULL; p = p->ai_next) {
assert(p->ai_family == AF_INET6);
}
freeaddrinfo(res);
res = NULL;
// Test AI_ADDRCONFIG
hints = (struct addrinfo){0};
hints.ai_flags = AI_ADDRCONFIG;
hints.ai_family = AF_UNSPEC;
ret = getaddrinfo("localhost", NULL, &hints, &res);
if (ret == 0) {
int found_ipv4 = 0;
int found_ipv6 = 0;
for(struct addrinfo *p = res; p != NULL; p = p->ai_next) {
if (p->ai_family == AF_INET)
found_ipv4 = 1;
else if (p->ai_family == AF_INET6)
found_ipv6 = 1;
}
if (has_ipv4_addr())
assert(found_ipv4);
else
assert(!found_ipv4);
if (has_ipv6_addr())
assert(found_ipv6);
else
assert(!found_ipv6);
} else {
// If getaddrinfo fails, it should be because no addresses are configured.
assert(!has_ipv4_addr() && !has_ipv6_addr());
}
freeaddrinfo(res);
res = NULL;
#endif // __MLIBC_LINUX_OPTION || defined(USE_HOST_LIBC)
// Test AI_NUMERICSERV.
hints = (struct addrinfo){0};
hints.ai_flags = AI_NUMERICSERV;
hints.ai_family = AF_INET;
ret = getaddrinfo(NULL, "8080", &hints, &res);
assert(ret == 0);
assert(res->ai_family == AF_INET);
struct sockaddr_in *addr_in = (struct sockaddr_in *)res->ai_addr;
assert(addr_in->sin_port == htons(8080));
freeaddrinfo(res);
res = NULL;
ret = getaddrinfo(NULL, "http", &hints, &res);
assert(ret == EAI_NONAME);
assert(res == NULL);
hints.ai_family = AF_INET6;
ret = getaddrinfo(NULL, "9090", &hints, &res);
assert(ret == 0);
assert(res->ai_family == AF_INET6);
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)res->ai_addr;
assert(addr_in6->sin6_port == htons(9090));
freeaddrinfo(res);
res = NULL;
ret = getaddrinfo(NULL, "https", &hints, &res);
assert(ret == EAI_NONAME);
assert(res == NULL);
// Test for NXDOMAIN
hints = (struct addrinfo){0};
ret = getaddrinfo("this-domain-should-not-exist.nxdomain", NULL, &hints, &res);
assert(ret == EAI_NONAME);
assert(res == NULL);
// Test AI_CANONNAME.
hints = (struct addrinfo){0};
hints.ai_flags = AI_CANONNAME;
ret = getaddrinfo("localhost.localdomain", NULL, &hints, &res);
assert(ret == 0);
assert(res->ai_canonname);
freeaddrinfo(res);
res = NULL;
return 0;
}
+22
View File
@@ -0,0 +1,22 @@
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
char buf[PATH_MAX];
char *ret = getcwd(buf, PATH_MAX);
assert(ret);
assert((strlen(ret) == strlen(buf)) && strlen(ret));
assert(!strcmp(ret, buf));
char *ret2 = getcwd(NULL, 0);
assert(ret2);
assert(strlen(ret2));
assert(!strcmp(ret, ret2));
free(ret2);
return 0;
}
+92
View File
@@ -0,0 +1,92 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#ifdef USE_HOST_LIBC
#define TEST_FILE "getdelim-host-libc.tmp"
#else
#define TEST_FILE "getdelim.tmp"
#endif
int main(void) {
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
// We have to open the file for writing and then reading separately,
// as mlibc doesn't allow us to do both at the same time (yet).
fp = fopen(TEST_FILE, "w");
assert(fp);
fputs("foo\nbar\nbaz\nquux\n", fp);
fclose(fp);
fp = fopen(TEST_FILE, "r");
assert(fp);
while ((read = getline(&line, &len, fp)) != -1) {
printf("read line of length %zd, capacity %zu\n", read, len);
}
assert(!ferror(fp));
free(line);
fclose(fp);
size_t nchars = 10000;
fp = fopen(TEST_FILE, "w");
assert(fp);
for (size_t i = 0; i < nchars; i++)
fputc('a', fp);
fputc('b', fp);
fclose(fp);
line = NULL;
len = 0;
fp = fopen(TEST_FILE, "r");
assert(fp);
while ((read = getdelim(&line, &len, 'b', fp)) != -1) {
printf("read line of length %zd, capacity %zu\n", read, len);
assert((size_t)read == nchars + 1);
}
assert(len > nchars + 1);
assert(!ferror(fp));
assert(feof(fp));
assert(getdelim(&line, &len, 'b', fp) == -1);
assert(feof(fp));
free(line);
fclose(fp);
fp = fopen(TEST_FILE, "w");
assert(fp);
assert(fwrite("1234ef\0f", 1, 8, fp) == 8);
fclose(fp);
fp = fopen(TEST_FILE, "r");
assert(fp);
/* test with line = NULL and large len */
line = NULL;
len = (size_t) ~0;
assert(getdelim(&line, &len, 'e', fp) == 5);
assert(!memcmp(line, "1234e", 6));
assert(5 < len);
/* test handling of internal nulls */
assert(getdelim(&line, &len, 'e', fp) == 3);
assert(!memcmp(line, "f\0f", 4));
assert(3 < len);
/* test handling of EOF */
assert(getdelim(&line, &len, 'e', fp) == -1);
free(line);
fclose(fp);
// Delete the file
unlink(TEST_FILE);
return 0;
}
@@ -0,0 +1,22 @@
#include <netdb.h>
#include <assert.h>
#include <arpa/inet.h>
#include <stddef.h>
#include <string.h>
int main() {
struct sockaddr_in addr;
addr.sin_family = AF_INET;
assert(inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr));
char host[64];
assert(!getnameinfo((struct sockaddr*)&addr, sizeof(addr), host,
sizeof(host), NULL, 0, 0));
assert(inet_pton(AF_INET, "8.8.8.8", &addr.sin_addr));
assert(!getnameinfo((struct sockaddr*)&addr, sizeof(addr), host,
sizeof(host), NULL, 0, 0));
assert(!strcmp(host, "dns.google"));
return 0;
}
+23
View File
@@ -0,0 +1,23 @@
#include <assert.h>
#include <errno.h>
#include <sched.h>
#include <unistd.h>
int main() {
struct sched_param param = {
.sched_priority = 100,
};
int ret = sched_getparam(getpid(), &param);
assert(!ret);
ret = sched_setparam(getpid(), &param);
assert(ret == 0);
param.sched_priority = 0xD00DFEED;
ret = sched_setparam(getpid(), &param);
assert(ret == -1 && errno == EINVAL);
return 0;
}
@@ -0,0 +1,27 @@
#include <assert.h>
#include <netdb.h>
#include <string.h>
#include <arpa/inet.h>
int main() {
struct servent *ret = getservbyname("http", "tcp");
assert(ret);
assert(!strcmp("http", ret->s_name));
assert(ret->s_port == htons(80));
assert(!strcmp("tcp", ret->s_proto));
ret = getservbyname("http", NULL);
assert(ret);
assert(!strcmp("http", ret->s_name));
assert(ret->s_port == htons(80));
assert(!strcmp("tcp", ret->s_proto));
ret = getservbyname("babel", "udp");
assert(ret);
ret = getservbyname("babel", "tcp");
assert(!ret);
ret = getservbyname("", NULL);
assert(!ret);
return 0;
}
@@ -0,0 +1,28 @@
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
int main() {
struct servent *ret = getservbyport(htons(80), "tcp");
assert(ret);
assert(!strcmp("http", ret->s_name));
assert(ret->s_port == htons(80));
assert(!strcmp("tcp", ret->s_proto));
ret = getservbyport(htons(80), NULL);
assert(ret);
assert(!strcmp("http", ret->s_name));
assert(ret->s_port == htons(80));
assert(!strcmp("tcp", ret->s_proto));
ret = getservbyport(htons(6696), "udp");
assert(ret);
ret = getservbyport(htons(6696), "tcp");
assert(!ret);
ret = getservbyport(htons(0), NULL);
assert(!ret);
return 0;
}
+155
View File
@@ -0,0 +1,155 @@
#include <stdlib.h>
#include <stdio.h>
#include <grp.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
void check_root_group(struct group *grp) {
assert(grp);
printf("group name: %s\n", grp->gr_name);
fflush(stdout);
assert(grp->gr_gid == 0);
assert(!strcmp(grp->gr_name, "root"));
// Depending on your system, the root group may or may not contain the root user.
if (grp->gr_mem[0] != NULL) {
bool found = false;
for (size_t i = 0; grp->gr_mem[i] != NULL; i++) {
printf("group member: %s\n", grp->gr_mem[i]);
fflush(stdout);
if (!strcmp(grp->gr_mem[i], "root")) {
found = true;
break;
}
}
assert(found);
}
}
int main()
{
struct group grp, *result = NULL;
char *buf;
size_t bufsize;
int s;
bool password = false;
char *members[3];
char filename[] = "grpXXXXXX";
int tmpfd;
FILE *tmp;
char *expected = "managarm:passwordhash:12345:managarm,mlibc\n";
size_t readsize;
bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
assert(bufsize > 0 && bufsize < 0x100000);
buf = malloc(bufsize);
assert(buf);
s = getgrnam_r("root", &grp, buf, bufsize, &result);
assert(!s);
check_root_group(result);
s = getgrgid_r(0, &grp, buf, bufsize, &result);
assert(!s);
check_root_group(result);
result = getgrnam("root");
check_root_group(result);
result = getgrgid(0);
check_root_group(result);
result = getgrnam("this_group_doesnt_exist");
assert(!result);
#ifndef USE_HOST_LIBC
// This is not guaranteed.
assert(errno == ESRCH);
#endif
s = getgrnam_r("this_group_doesnt_exist", &grp, buf, bufsize, &result);
assert(!result);
#ifndef USE_HOST_LIBC
// This is not guaranteed.
assert(s == ESRCH);
#endif
errno = 0;
setgrent();
assert(errno == 0);
result = getgrent();
assert(result);
grp.gr_name = strdup(result->gr_name);
if(result->gr_passwd) {
password = true;
}
grp.gr_passwd = strdup(result->gr_passwd);
grp.gr_gid = result->gr_gid;
assert(grp.gr_name);
if(password)
assert(grp.gr_passwd);
assert(grp.gr_name);
if(password)
assert(grp.gr_passwd);
endgrent();
result = getgrent();
assert(result);
assert(strcmp(result->gr_name, grp.gr_name) == 0);
if(password)
assert(strcmp(result->gr_passwd, grp.gr_passwd) == 0);
assert(result->gr_gid == grp.gr_gid);
free(grp.gr_name);
if(password)
free(grp.gr_passwd);
grp.gr_name = "managarm";
grp.gr_passwd = "passwordhash";
grp.gr_gid = 12345;
members[0] = "managarm";
members[1] = "mli:bc";
members[2] = NULL;
grp.gr_mem = members;
// tmpfile() cannot be used because its unimplemented in mlibc.
tmpfd = mkstemp(filename);
assert(tmpfd);
tmp = fdopen(tmpfd, "w+");
assert(tmp);
assert(putgrent(NULL, tmp) < 0);
assert(putgrent(&grp, tmp) < 0);
members[1] = "mlibc";
grp.gr_name = "mana:garm";
assert(putgrent(&grp, tmp) < 0);
grp.gr_name = "managarm";
grp.gr_passwd = "passwordhash\n";
assert(putgrent(&grp, tmp) < 0);
grp.gr_passwd = "passwordhash";
assert(putgrent(&grp, tmp) == 0);
rewind(tmp);
readsize = fread(buf, 1, bufsize, tmp);
assert(readsize == strlen(expected));
assert(strncmp(expected, buf, strlen(expected)) == 0);
fclose(tmp);
unlink(filename);
free(buf);
return 0;
}
@@ -0,0 +1,13 @@
#include <assert.h>
#include <net/if.h>
#include <stdio.h>
int main() {
char name[IF_NAMESIZE];
assert(name == if_indextoname(1, name));
printf("test: name '%s'\n", name);
assert(1 == if_nametoindex(name));
return 0;
}
+20
View File
@@ -0,0 +1,20 @@
#include <assert.h>
#include <strings.h>
#include <stddef.h>
int main() {
char str[] = "This is a sample string";
char *pch;
// The character 's' is at position 4, 7, 11 and 18
pch = index(str, 's');
assert(pch - str + 1 == 4);
pch = index(pch + 1, 's');
assert(pch - str + 1 == 7);
pch = index(pch + 1, 's');
assert(pch - str + 1 == 11);
pch = index(pch + 1, 's');
assert(pch - str + 1 == 18);
pch = index(pch + 1, 's');
assert(pch == NULL);
return 0;
}
@@ -0,0 +1,30 @@
#include <arpa/inet.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
int main() {
struct in_addr addr;
addr.s_addr = (1 << 24) |
(1 << 16) | (1 << 8) | 1;
char buf[INET_ADDRSTRLEN];
assert(inet_ntop(AF_INET, &addr, buf, INET_ADDRSTRLEN) != NULL);
assert(strncmp("1.1.1.1", buf, INET_ADDRSTRLEN) == 0);
struct in6_addr addr2 = { .s6_addr = {0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1} };
char buf2[INET6_ADDRSTRLEN];
assert(inet_ntop(AF_INET6, &addr2, buf2, INET6_ADDRSTRLEN) != NULL);
assert(strncmp("2001:db8::1:0:0:1", buf2, INET6_ADDRSTRLEN) == 0);
struct in6_addr addr3 = { .s6_addr = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} };
char buf3[INET6_ADDRSTRLEN];
assert(inet_ntop(AF_INET6, &addr3, buf3, INET6_ADDRSTRLEN) != NULL);
assert(strncmp("::1", buf3, INET6_ADDRSTRLEN) == 0);
struct in6_addr addr4 = { .s6_addr = {0x20, 0x1, 0xd, 0xb8, 00, 00, 00, 0x1, 00, 0x1, 00, 0x1, 00, 0x1, 00, 0x1} };
char buf4[INET6_ADDRSTRLEN];
assert(inet_ntop(AF_INET6, &addr4, buf4, INET6_ADDRSTRLEN) != NULL);
assert(strncmp("2001:db8:0:1:1:1:1:1", buf4, INET6_ADDRSTRLEN) == 0);
return 0;
}
+136
View File
@@ -0,0 +1,136 @@
#include <arpa/inet.h>
#include <assert.h>
#include <string.h>
int main() {
struct in_addr addr;
assert(inet_pton(AF_INET, "1.1.1.1", &addr));
assert((addr.s_addr & 0xFF) == 1);
assert(((addr.s_addr >> 8) & 0xFF) == 1);
assert(((addr.s_addr >> 16) & 0xFF) == 1);
assert(((addr.s_addr >> 24) & 0xFF) == 1);
assert(!inet_pton(AF_INET, "256.999.1234.555", &addr));
assert(!inet_pton(AF_INET, "a.b.c.d", &addr));
struct in6_addr test6;
{
struct in6_addr addr6 = {
.s6_addr = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
},
};
int ret = inet_pton(AF_INET6, "::1", &test6);
assert(ret == 1);
assert(!memcmp(&addr6, &test6, sizeof(addr6)));
}
memset(&test6, 0, sizeof(test6));
{
struct in6_addr addr6 = {
.s6_addr16 = {
htons(0x2606), htons(0x4700), htons(0x4700), htons(0x00), htons(0x00), htons(0x00), htons(0x00), htons(0x1111)
},
};
int ret = inet_pton(AF_INET6, "2606:4700:4700::1111", &test6);
assert(ret == 1);
assert(!memcmp(&addr6, &test6, sizeof(addr6)));
}
memset(&test6, 0, sizeof(test6));
{
struct in6_addr addr6 = {
.s6_addr16 = {
htons(0x2606), htons(0x4700), htons(0x4700), htons(0x00), htons(0x00), htons(0x00), htons(0x00), htons(0x1111)
},
};
int ret = inet_pton(AF_INET6, "2606:4700:4700:0000:0000:0000:0000:1111", &test6);
assert(ret == 1);
assert(!memcmp(&addr6, &test6, sizeof(addr6)));
}
memset(&test6, 0, sizeof(test6));
{
struct in6_addr addr6 = {
.s6_addr16 = {
0, 0, 0, 0, 0, 0, 0, 0
},
};
int ret = inet_pton(AF_INET6, "::", &test6);
assert(ret == 1);
assert(!memcmp(&addr6, &test6, sizeof(addr6)));
}
memset(&test6, 0, sizeof(test6));
{
int ret = inet_pton(AF_INET6, "mlibc::", &test6);
assert(!ret);
}
memset(&test6, 0, sizeof(test6));
{
struct in6_addr addr6 = {
.s6_addr = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0xFF, 0xFF, 204, 152, 189, 116,
},
};
int ret = inet_pton(AF_INET6, "::FFFF:204.152.189.116", &test6);
assert(ret == 1);
assert(!memcmp(&addr6, &test6, sizeof(addr6)));
}
memset(&test6, 0, sizeof(test6));
{
int ret = inet_pton(AF_INET6, "::FFFF:204.152.189.", &test6);
assert(!ret);
}
memset(&test6, 0, sizeof(test6));
{
int ret = inet_pton(AF_INET6, "::FFFF:204.152.189.420", &test6);
assert(!ret);
}
memset(&test6, 0, sizeof(test6));
{
int ret = inet_pton(AF_INET6, "::FFFF:204.152.420.69", &test6);
assert(!ret);
}
memset(&test6, 0, sizeof(test6));
{
int ret = inet_pton(AF_INET6, "1100::FFFF:204.152.189.116", &test6);
(void) ret;
#if !defined(USE_HOST_LIBC)
assert(!ret);
#endif
}
memset(&test6, 0, sizeof(test6));
{
struct in6_addr addr6 = {
.s6_addr = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0xFF, 0xFF, 204, 152, 189, 116,
},
};
int ret = inet_pton(AF_INET6, "0000:0000:0000:0000:0000:FFFF:204.152.189.116", &test6);
assert(ret == 1);
assert(!memcmp(&addr6, &test6, sizeof(addr6)));
}
return 0;
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main () {
char str[] = "The Last Supper by Leonardo da Vinci";
char *str_temp;
// Test strstr
str_temp = strstr(str, "Supper"); /* Find a substring in the string */
assert(!strcmp(str_temp, "Supper by Leonardo da Vinci"));
/* Following calls use memory APIs for the above tasks */
// Test memchr
str_temp = (char *)memchr((void *)str, 'L', strlen(str));
assert(!strcmp(str_temp, "Last Supper by Leonardo da Vinci"));
// Test memrchr
str_temp = (char *)memrchr((void *)str, 'L', strlen(str));
assert(!strcmp(str_temp, "Leonardo da Vinci"));
return 0;
}
+72
View File
@@ -0,0 +1,72 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
void validate_pattern(char *p) {
assert(memcmp(p, "XXXXXX", 6));
assert(memchr(p, 0, 6) == NULL);
for (int i = 0; i < 6; i++) {
assert(isalnum(p[i]));
}
}
int main() {
int ret;
// Make sure the patterns themselves cannot be chosen. This
// *could* happen on glibc, or if we widen the character set
// used for generating random names. Odds are 1 in 60 billion,
// but I'd rather not worry about this.
ret = open("prefixXXXXXX", O_RDWR | O_CREAT | O_EXCL, 0600);
assert(ret >= 0 || (ret == -1 && errno == EEXIST));
ret = open("longprefixXXXXXXlongsuffix", O_RDWR | O_CREAT | O_EXCL, 0600);
assert(ret >= 0 || (ret == -1 && errno == EEXIST));
ret = mkstemp("short");
assert(ret == -1);
assert(errno == EINVAL);
ret = mkstemp("lessthan6XXX");
assert(ret == -1);
assert(errno == EINVAL);
ret = mkstemps("lessthan6XXXswithsuffix", 11);
assert(ret == -1);
assert(errno == EINVAL);
char *p = strdup("prefixXXXXXX");
ret = mkstemp(p);
// We can't really protect against EEXIST...
assert(ret >= 0 || (ret == -1 && errno == EEXIST));
assert(!memcmp(p, "prefix", 6));
assert(p[12] == 0);
validate_pattern(p + 6);
if (ret >= 0) {
ret = close(ret);
assert(!ret);
}
free(p);
p = strdup("longprefixXXXXXXlongsuffix");
ret = mkstemps(p, 10);
// We can't really protect against EEXIST...
assert(ret >= 0 || (ret == -1 && errno == EEXIST));
assert(!memcmp(p, "longprefix", 10));
assert(!memcmp(p + 16, "longsuffix", 10));
assert(p[26] == 0);
validate_pattern(p + 10);
if (ret >= 0) {
ret = close(ret);
assert(!ret);
}
free(p);
return 0;
}
+37
View File
@@ -0,0 +1,37 @@
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/resource.h>
#include <unistd.h>
#include <limits.h>
int main() {
errno = 0;
int original_priority = nice(0);
assert(original_priority != -1 || !errno);
errno = 0;
int ret = nice(1);
if(ret == -1 && errno) {
fprintf(stderr, "%s", strerror(errno));
exit(1);
}
errno = 0;
int new_priority = nice(0);
assert(new_priority != -1 || !errno);
assert(new_priority == original_priority + 1);
errno = 0;
new_priority = nice(INT_MAX - new_priority);
assert(new_priority != -1 || !errno);
assert(new_priority != INT_MAX);
return 0;
}
@@ -0,0 +1,65 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define WRITE_NO 1024
int main() {
size_t size;
char *buf;
FILE *fp = open_memstream(&buf, &size);
assert(fp);
char c = 'A';
for (size_t i = 0; i < WRITE_NO; i++)
assert(fwrite(&c, sizeof(char), 1, fp) == 1);
// Flush the file to update the pointers.
assert(!fflush(fp));
assert(size == WRITE_NO);
for (size_t i = 0; i < size; i++)
assert(buf[i] == c);
// Check if the stream maintains a null-bye at the end.
assert(buf[size] == '\0');
// Stream should be expanded, size should be == 2*WRITE_NO.
assert(!fseek(fp, WRITE_NO, SEEK_END));
assert(!fflush(fp));
assert(size == 2*WRITE_NO);
assert(buf[size] == '\0');
// Check if it's filled with zero's.
for (size_t i = WRITE_NO; i < size; i++)
assert(buf[i] == '\0');
// Go back and overwrite the 0's with 'B'.
assert(!fseek(fp, -WRITE_NO, SEEK_CUR));
c = 'B';
for (size_t i = 0; i < WRITE_NO; i++)
assert(fwrite(&c, sizeof(char), 1, fp) == 1);
// Check if that happened.
assert(size == 2*WRITE_NO);
for (size_t i = WRITE_NO; i < size; i++)
assert(buf[i] == c);
assert(buf[size] == '\0');
// Go to the front and write 'B'.
assert(!fseek(fp, 0, SEEK_SET));
for (size_t i = 0; i < WRITE_NO; i++)
assert(fwrite(&c, sizeof(char), 1, fp) == 1);
// Check if that happened.
assert(size == 2*WRITE_NO);
for (size_t i = 0; i < size; i++)
assert(buf[i] == c);
assert(buf[size] == '\0');
// Close the file, we have tested everything.
assert(!fclose(fp));
free(buf);
return 0;
}
+49
View File
@@ -0,0 +1,49 @@
#include <assert.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/wait.h>
static void noop(int x) {
(void)x;
}
int main() {
signal(SIGUSR1, &noop);
int pid;
switch(pid = fork()) {
case -1:
perror("fork");
abort();
case 0:
pause();
assert(errno == EINTR);
return 0;
default:
while (1) {
usleep(100);
kill(pid, SIGUSR1);
usleep(100);
int status;
errno = 0;
if (waitpid(-1, &status, WNOHANG) <= 0) {
if (errno && errno != EAGAIN) {
perror("wait");
kill(pid, SIGKILL);
}
continue;
}
if (!WIFEXITED(status)) {
printf("wait returned %x\n", status);
abort();
}
return WEXITSTATUS(status);
}
}
}
+42
View File
@@ -0,0 +1,42 @@
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#ifdef USE_HOST_LIBC
#define TEST_FILE "popen-host-libc.tmp"
#else
#define TEST_FILE "popen.tmp"
#endif
#define TEST_STRING1 "the quick brown fox jumps over the lazy dog"
#define TEST_STRING1_LEN 43
#define TEST_STRING2 "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
#define TEST_STRING2_LEN 55
int main() {
// Test reading
FILE *f1 = popen("echo -n '" TEST_STRING1 "'", "r");
assert(f1);
char buf1[TEST_STRING1_LEN];
assert(fread(buf1, 1, TEST_STRING1_LEN, f1) == TEST_STRING1_LEN);
assert(memcmp(buf1, TEST_STRING1, TEST_STRING1_LEN) == 0);
pclose(f1);
// Test writing
FILE *f2 = popen("cat - > " TEST_FILE, "w");
assert(f2);
assert(fwrite(TEST_STRING2, 1, TEST_STRING2_LEN, f2) == TEST_STRING2_LEN);
pclose(f2);
// Read test file back
FILE *test_file = fopen(TEST_FILE, "r");
assert(test_file);
char buf2[TEST_STRING2_LEN];
assert(fread(buf2, 1, TEST_STRING2_LEN, test_file) == TEST_STRING2_LEN);
assert(memcmp(buf2, TEST_STRING2, TEST_STRING2_LEN) == 0);
fclose(test_file);
assert(unlink(TEST_FILE) == 0);
return 0;
}
@@ -0,0 +1,119 @@
#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#if defined(__linux__)
#include <pthread.h>
size_t COUNTER_EXPIRATIONS = 3;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
size_t count = 0;
void timer_handler(union sigval val) {
(void) val;
pthread_mutex_lock(&mutex);
count++;
fprintf(stderr, "new count %zu\n", count);
if(count >= COUNTER_EXPIRATIONS) {
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mutex);
}
static void test_thread_notify() {
timer_t timer;
struct sigevent sev = {
.sigev_value = {
.sival_ptr = &timer,
},
.sigev_notify = SIGEV_THREAD,
};
sev.sigev_notify_function = timer_handler;
sev.sigev_notify_attributes = NULL;
int ret = timer_create(CLOCK_MONOTONIC, &sev, &timer);
assert(ret == 0);
struct itimerspec its = {
.it_interval = {
.tv_sec = 1,
.tv_nsec = 0,
},
.it_value = {
.tv_sec = 1,
.tv_nsec = 0,
},
};
ret = timer_settime(timer, 0, &its, NULL);
assert(ret == 0);
pthread_mutex_lock(&mutex);
while(count < COUNTER_EXPIRATIONS)
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
ret = timer_delete(timer);
assert(ret == 0);
}
#endif
int main() {
struct sigevent evp;
memset(&evp, 0, sizeof(evp));
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigprocmask(SIG_BLOCK, &set, 0);
evp.sigev_notify = SIGEV_SIGNAL;
evp.sigev_signo = SIGUSR1;
struct timeval start;
gettimeofday(&start, NULL);
timer_t timer;
if (timer_create(CLOCK_MONOTONIC, &evp, &timer)) {
perror("timer_create");
exit(1);
}
struct itimerspec spec;
memset(&spec, 0, sizeof(spec));
spec.it_value.tv_sec = 1;
spec.it_value.tv_nsec = 0;
int sig;
timer_settime(timer, 0, &spec, NULL);
struct itimerspec its = {};
int ret = timer_gettime(timer, &its);
assert(ret == 0);
assert(its.it_value.tv_sec || its.it_value.tv_nsec);
sigwait(&set, &sig);
struct timeval end;
gettimeofday(&end, NULL);
double diff = end.tv_sec - start.tv_sec;
diff += (end.tv_usec - start.tv_usec) / 1000000.0;
assert(diff >= 1.0);
#if defined(__linux__)
test_thread_notify();
#endif
timer_delete(timer);
return 0;
}
@@ -0,0 +1,23 @@
#include <stdlib.h>
#include <assert.h>
#include <stdint.h>
#include <errno.h>
int main() {
void *p = NULL;
// align must be a power of two
assert(posix_memalign(&p, 3, 1) == EINVAL && p == NULL);
// align must be a multiple of sizeof(void *)
assert(posix_memalign(&p, sizeof(void *) / 2, 8) == EINVAL && p == NULL);
assert(posix_memalign(&p, sizeof(void *), sizeof(void *)) == 0 && p != NULL && (uintptr_t)p % sizeof(void *) == 0);
free(p);
assert(posix_memalign(&p, 256, 1) == 0 && p != NULL && (uintptr_t)p % 256 == 0);
free(p);
assert(posix_memalign(&p, 256, 256) == 0 && p != NULL && (uintptr_t)p % 256 == 0);
free(p);
return 0;
}
@@ -0,0 +1,38 @@
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <spawn.h>
#include <sys/wait.h>
extern char **environ;
void run_cmd(char *cmd)
{
pid_t pid;
char *argv[] = {"sh", "-c", cmd, NULL};
int status;
printf("Run command: %s\n", cmd);
status = posix_spawn(&pid, "/bin/sh", NULL, NULL, argv, environ);
if(status == 0) {
printf("Child pid: %i\n", pid);
if(waitpid(pid, &status, 0) != -1) {
printf("Child exited with status %i\n", status);
printf("Child exit status: %i\n", WEXITSTATUS(status));
assert(WEXITSTATUS(status) == 0);
} else {
perror("waitpid");
assert(0 == 1);
}
} else {
printf("posix_spawn: %s\n", strerror(status));
assert(0 == 1);
}
}
int main() {
run_cmd(":");
return 0;
}
@@ -0,0 +1,54 @@
#include <assert.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
_Atomic int prepare_order = 0;
_Atomic int parent_order = 0;
_Atomic int child_order = 0;
static void prepare1() { prepare_order = 1; }
static void prepare2() { prepare_order = 2; }
static void parent1() { parent_order = 1; }
static void parent2() { parent_order = 2; }
static void child1() { child_order = 1; }
static void child2() { child_order = 2; }
int main() {
assert(!pthread_atfork(prepare1, parent1, child1));
assert(!pthread_atfork(prepare2, parent2, child2));
pid_t pid = fork();
assert(pid >= 0);
if (!pid) {
assert(child_order == 2);
exit(0);
} else {
assert(prepare_order == 1);
assert(parent_order == 2);
while (1) {
int status = 0;
int ret = waitpid(pid, &status, 0);
if (ret == -1 && errno == EINTR)
continue;
assert(ret > 0);
if (WIFSIGNALED(status) && WTERMSIG(status) == SIGABRT)
return 1;
return WEXITSTATUS(status);
}
}
return 0;
}
@@ -0,0 +1,166 @@
#include <pthread.h>
#include <assert.h>
#include <errno.h>
#include <alloca.h>
#include <string.h>
#include <sys/mman.h>
#include <signal.h>
static void test_detachstate() {
pthread_attr_t attr;
assert(!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
int detachstate;
assert(!pthread_attr_getdetachstate(&attr, &detachstate));
assert(detachstate == PTHREAD_CREATE_DETACHED);
assert(pthread_attr_setdetachstate(&attr, 2* (PTHREAD_CREATE_DETACHED +
PTHREAD_CREATE_JOINABLE)) == EINVAL);
}
static void *stacksize_worker(void *arg) {
size_t default_stacksize = (*(size_t*)arg);
size_t alloc_size = default_stacksize + default_stacksize/2;
void *area = alloca(alloc_size);
// If the allocated stack was not enough this will crash.
// Trample both the start and end of the area so it works on both upwards-
// and downwards-growing stacks.
*(volatile char*)area = 1;
*(volatile char*)(area + alloc_size - 1) = 1;
return NULL;
}
static void test_stacksize() {
pthread_attr_t attr;
assert(!pthread_attr_init(&attr));
size_t stacksize;
assert(!pthread_attr_getstacksize(&attr, &stacksize));
assert(!pthread_attr_setstacksize(&attr, stacksize * 2));
pthread_t thread;
assert(!pthread_create(&thread, &attr, stacksize_worker, &stacksize));
assert(!pthread_join(thread, NULL));
}
static void test_guardsize() {
pthread_attr_t attr;
assert(!pthread_attr_init(&attr));
assert(!pthread_attr_setguardsize(&attr, 0));
size_t guardsize;
assert(!pthread_attr_getguardsize(&attr, &guardsize));
assert(!guardsize);
}
static void test_scope() {
pthread_attr_t attr;
assert(!pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM));
int scope;
assert(!pthread_attr_getscope(&attr, &scope));
assert(scope == PTHREAD_SCOPE_SYSTEM);
assert(pthread_attr_setscope(&attr, 2* (PTHREAD_SCOPE_SYSTEM +
PTHREAD_SCOPE_PROCESS)) == EINVAL);
}
static void test_inheritsched() {
pthread_attr_t attr;
assert(!pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED));
int inheritsched;
assert(!pthread_attr_getinheritsched(&attr, &inheritsched));
assert(inheritsched == PTHREAD_INHERIT_SCHED);
assert(pthread_attr_setinheritsched(&attr, 2* (PTHREAD_INHERIT_SCHED +
PTHREAD_EXPLICIT_SCHED)) == EINVAL);
}
static void test_schedparam() {
pthread_attr_t attr;
struct sched_param init_param = {0};
assert(!pthread_attr_setschedparam(&attr, &init_param));
struct sched_param param = {1};
assert(!pthread_attr_getschedparam(&attr, &param));
assert(param.sched_priority == init_param.sched_priority);
}
static void test_schedpolicy() {
pthread_attr_t attr;
assert(!pthread_attr_setschedpolicy(&attr, SCHED_FIFO));
int policy;
assert(!pthread_attr_getschedpolicy(&attr, &policy));
assert(policy == SCHED_FIFO);
assert(pthread_attr_setinheritsched(&attr, 2* (SCHED_FIFO + SCHED_RR +
SCHED_OTHER)) == EINVAL);
}
static void *stackaddr_worker(void *arg) {
void *addr = *(void**)arg;
void *sp;
#if defined(__x86_64__)
asm volatile ("mov %%rsp, %0" : "=r"(sp));
#elif defined(__i386__)
asm volatile ("mov %%esp, %0" : "=r"(sp));
#elif defined(__aarch64__)
asm volatile ("mov %0, sp" : "=r"(sp));
#elif defined (__riscv)
asm volatile ("mv %0, sp" : "=r"(sp));
#elif defined(__m68k__)
asm volatile ("move.l %%sp, %0" : "=r"(sp));
#elif defined (__loongarch64)
asm volatile ("move %0, $sp" : "=r"(sp));
#else
# error Unknown architecture
#endif
// Check if our stack pointer is in a sane range.
assert(sp > addr);
return NULL;
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
static void test_stackaddr() {
pthread_attr_t attr;
assert(!pthread_attr_init(&attr));
size_t size;
assert(!pthread_attr_getstacksize(&attr, &size));
void *addr = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
assert(!pthread_attr_setstack(&attr, addr, size));
assert(!pthread_attr_setguardsize(&attr, 0));
void *new_addr;
size_t new_size;
assert(!pthread_attr_getstack(&attr, &new_addr, &new_size));
assert(new_addr == addr);
assert(new_size == size);
pthread_t thread;
assert(!pthread_create(&thread, &attr, stackaddr_worker, &addr));
assert(!pthread_join(thread, NULL));
}
#pragma GCC diagnostic pop
#if !defined(USE_HOST_LIBC) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32)
static void test_stack() {
pthread_attr_t attr;
void *stackaddr = (void*)1;
size_t stacksize = PTHREAD_STACK_MIN;
assert(!pthread_attr_setstack(&attr, stackaddr, stacksize));
void *new_addr;
size_t new_size;
assert(!pthread_attr_getstack(&attr, &new_addr, &new_size));
assert(new_addr == stackaddr);
assert(new_size == stacksize);
}
#endif
int main() {
test_detachstate();
test_stacksize();
test_guardsize();
test_scope();
test_inheritsched();
test_schedparam();
test_schedpolicy();
test_stackaddr();
#if !defined(USE_HOST_LIBC) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32)
test_stack();
#endif
return 0;
}
@@ -0,0 +1,58 @@
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
pthread_barrier_t barrier;
_Atomic int hitBarrierCount, pastBarrierCount;
static void *worker(void *arg) {
(void)arg;
hitBarrierCount++;
pthread_barrier_wait(&barrier);
pastBarrierCount++;
return NULL;
}
int main() {
// pthread_barrierattr_t
pthread_barrierattr_t attr;
pthread_barrierattr_init(&attr);
int pshared;
pthread_barrierattr_getpshared(&attr, &pshared);
assert(pshared == PTHREAD_PROCESS_PRIVATE);
pthread_barrierattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_barrierattr_getpshared(&attr, &pshared);
assert(pshared == PTHREAD_PROCESS_SHARED);
pthread_barrierattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
pthread_barrierattr_getpshared(&attr, &pshared);
assert(pshared == PTHREAD_PROCESS_PRIVATE);
// pthread_barrier_t
pthread_barrier_init(&barrier, &attr, 3);
pthread_barrierattr_destroy(&attr);
pthread_t thread1;
int ret = pthread_create(&thread1, NULL, &worker, NULL);
assert(!ret);
pthread_t thread2;
ret = pthread_create(&thread2, NULL, &worker, NULL);
assert(!ret);
sleep(1);
// Make sure the barrier actually stops threads from proceeding.
assert(pastBarrierCount == 0);
assert(hitBarrierCount <= 2);
hitBarrierCount++;
pthread_barrier_wait(&barrier);
assert(hitBarrierCount == 3);
pthread_barrier_destroy(&barrier);
return 0;
}
@@ -0,0 +1,150 @@
#include <assert.h>
#include <pthread.h>
#include <unistd.h>
_Atomic int worker_ready = 0;
_Atomic int main_ready = 0;
static void *worker1(void *arg) {
(void)arg;
assert(!pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
assert(!pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL));
worker_ready = 1;
while (1) sleep(1);
return NULL;
}
static void *worker2(void *arg) {
(void) arg;
assert(!pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
worker_ready = 1;
while(!main_ready);
// Cancellation point - we should cancel right now
sleep(1);
assert(!"Expected to be cancelled!");
__builtin_unreachable();
}
static void *worker3(void *arg) {
(void) arg;
assert(!pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
worker_ready = 1;
while(!main_ready);
// Cancellation point - we should cancel right now
pthread_testcancel();
assert(!"Expected to be cancelled!");
__builtin_unreachable();
}
static void *worker4(void *arg) {
(void) arg;
assert(!pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
worker_ready = 1;
sleep(1);
// We expect to be canceled during the sleep
assert(!"Expected to be cancelled!");
__builtin_unreachable();
}
static void *worker5(void *arg) {
assert(!pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
worker_ready = 1;
while(!main_ready);
// Cancellation point - we should NOT cancel right now
pthread_testcancel();
int *arg_int = (int*)arg;
*arg_int = 1;
return NULL;
}
static void check_result(pthread_t thread) {
void *ret_val = NULL;
int ret = pthread_join(thread, &ret_val);
assert(!ret);
assert(ret_val == PTHREAD_CANCELED);
}
int main() {
pthread_t thread;
int ret = pthread_create(&thread, NULL, &worker1, NULL);
assert(!ret);
while (!worker_ready);
ret = pthread_cancel(thread);
assert(!ret);
check_result(thread);
main_ready = 0;
worker_ready = 0;
main_ready = 0;
ret = pthread_create(&thread, NULL, &worker2, NULL);
assert(!ret);
while(!worker_ready);
ret = pthread_cancel(thread);
assert(!ret);
main_ready = 1;
check_result(thread);
main_ready = 0;
worker_ready = 0;
main_ready = 0;
ret = pthread_create(&thread, NULL, &worker3, NULL);
assert(!ret);
while(!worker_ready);
ret = pthread_cancel(thread);
assert(!ret);
main_ready = 1;
check_result(thread);
worker_ready = 0;
main_ready = 0;
ret = pthread_create(&thread, NULL, &worker4, NULL);
assert(!ret);
while(!worker_ready);
ret = pthread_cancel(thread);
assert(!ret);
main_ready = 1;
check_result(thread);
// Test for bug where pthread_testcancel() was not checking if
// cancellation was triggered properly.
worker_ready = 0;
int pthread_arg = 0;
main_ready = 0;
ret = pthread_create(&thread, NULL, &worker5, &pthread_arg);
assert(!ret);
while(!worker_ready);
main_ready = 1;
void *ret_val = NULL;
ret = pthread_join(thread, &ret_val);
assert(!ret);
assert(!ret_val);
assert(pthread_arg == 1);
return 0;
}
@@ -0,0 +1,42 @@
#include <stdint.h>
#include <assert.h>
#include <pthread.h>
_Atomic uintptr_t cleanup_val = 0;
static void cleanup(void *arg) {
cleanup_val = (uintptr_t)arg;
}
static void *worker(void *arg) {
(void)arg;
pthread_cleanup_push(cleanup, (void *)1);
pthread_cleanup_push(cleanup, (void *)2);
pthread_cleanup_push(cleanup, (void *)3);
pthread_cleanup_push(cleanup, (void *)4);
pthread_cleanup_pop(1);
assert(cleanup_val == 4);
pthread_cleanup_pop(0);
assert(cleanup_val == 4);
pthread_exit(NULL);
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
return NULL;
}
int main() {
pthread_t thread;
assert(!pthread_create(&thread, NULL, &worker, NULL));
assert(!pthread_join(thread, NULL));
assert(cleanup_val == 1);
return 0;
}
@@ -0,0 +1,108 @@
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
_Atomic int waiting, should_exit;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static void *worker(void *arg) {
(void)arg;
pthread_mutex_lock(&mtx);
++waiting;
while (!should_exit)
pthread_cond_wait(&cond, &mtx);
pthread_mutex_unlock(&mtx);
return NULL;
}
static void test_broadcast_wakes_all() {
pthread_t t1, t2;
pthread_create(&t1, NULL, &worker, NULL);
pthread_create(&t2, NULL, &worker, NULL);
// Wait until the workers have actually entered the cond_wait
// before doing a broadcast.
while (waiting != 2 || pthread_mutex_trylock(&mtx) == EBUSY)
usleep(150000); // 150ms
should_exit = 1;
assert(!pthread_cond_broadcast(&cond));
pthread_mutex_unlock(&mtx);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
}
static void test_timedwait_timedout() {
// Use CLOCK_MONOTONIC.
pthread_condattr_t attr;
pthread_condattr_init(&attr);
assert(!pthread_condattr_setclock(&attr, CLOCK_MONOTONIC));
struct timespec before_now;
assert(!clock_gettime(CLOCK_MONOTONIC, &before_now));
before_now.tv_nsec -= 10000;
pthread_mutex_lock(&mtx);
int e = pthread_cond_timedwait(&cond, &mtx, &before_now);
assert(e == ETIMEDOUT);
pthread_mutex_unlock(&mtx);
long nanos_per_second = 1000000000;
struct timespec after_now;
assert(!clock_gettime(CLOCK_MONOTONIC, &after_now));
after_now.tv_nsec += nanos_per_second / 10; // 100ms
if (after_now.tv_nsec >= nanos_per_second) {
after_now.tv_nsec -= nanos_per_second;
after_now.tv_sec++;
}
pthread_mutex_lock(&mtx);
e = pthread_cond_timedwait(&cond, &mtx, &after_now);
assert(e == ETIMEDOUT);
pthread_mutex_unlock(&mtx);
after_now.tv_nsec += nanos_per_second;
pthread_mutex_lock(&mtx);
e = pthread_cond_timedwait(&cond, &mtx, &after_now);
assert(e == EINVAL);
pthread_mutex_unlock(&mtx);
}
static void test_attr() {
pthread_condattr_t attr;
pthread_condattr_init(&attr);
clockid_t clock;
assert(!pthread_condattr_getclock(&attr, &clock));
assert(clock == CLOCK_REALTIME);
assert(!pthread_condattr_setclock(&attr, CLOCK_MONOTONIC));
assert(!pthread_condattr_getclock(&attr, &clock));
assert(clock == CLOCK_MONOTONIC);
int pshared;
assert(!pthread_condattr_getpshared(&attr, &pshared));
assert(pshared == PTHREAD_PROCESS_PRIVATE);
assert(!pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED));
assert(!pthread_condattr_getpshared(&attr, &pshared));
assert(pshared == PTHREAD_PROCESS_SHARED);
pthread_condattr_destroy(&attr);
pthread_condattr_init(&attr);
// Make sure that we can create a pthread_cond_t with an attr.
pthread_cond_t cond;
pthread_cond_init(&cond, &attr);
pthread_cond_destroy(&cond);
pthread_condattr_destroy(&attr);
}
int main() {
test_attr();
test_broadcast_wakes_all();
test_timedwait_timedout();
return 0;
}
@@ -0,0 +1,22 @@
#include <assert.h>
#include <time.h>
#include <pthread.h>
int variable = 0;
static void *worker(void *arg) {
(void) arg;
variable = 1;
return NULL;
}
int main() {
pthread_t thread;
int ret = pthread_create(&thread, NULL, &worker, NULL);
assert(!ret);
ret = pthread_join(thread, NULL);
assert(!ret);
assert(variable == 1);
return 0;
}
@@ -0,0 +1,95 @@
#include <assert.h>
#include <pthread.h>
#include <errno.h>
#include <limits.h>
_Atomic int dtors_entered = 0;
pthread_key_t key3;
static void dtor1(void *arg) {
(void)arg;
dtors_entered++;
// Set the key during destruction to trigger dtor2 (as it only runs if
// the key value is non-NULL).
assert(!pthread_setspecific(key3, &key3));
assert(pthread_getspecific(key3) == &key3);
}
static void dtor2(void *arg) {
(void)arg;
dtors_entered++;
}
static void *worker1(void *arg) {
(void)arg;
pthread_key_t key1, key2;
assert(!pthread_key_create(&key1, NULL));
assert(!pthread_key_create(&key2, dtor1));
assert(!pthread_key_create(&key3, dtor2));
assert(!pthread_setspecific(key1, &key1));
assert(pthread_getspecific(key1) == &key1);
assert(!pthread_setspecific(key2, &key2));
assert(pthread_getspecific(key2) == &key2);
pthread_exit(0);
return NULL;
}
static void dtor3(void *arg) {
(void)arg;
// Make sure that we can create and destroy keys inside the dtor.
pthread_key_t dtorKey;
assert(!pthread_key_create(&dtorKey, NULL));
assert(!pthread_setspecific(dtorKey, &dtorKey));
assert(pthread_getspecific(dtorKey) == &dtorKey);
assert(!pthread_key_delete(dtorKey));
}
static void *worker2(void *arg) {
(void)arg;
pthread_key_t key;
assert(!pthread_key_create(&key, dtor3));
assert(!pthread_setspecific(key, &key));
assert(pthread_getspecific(key) == &key);
pthread_exit(0);
return NULL;
}
int main() {
// NOTE that the EINVAL return from pthread_setspecific is mlibc-specific,
// POSIX specifies that accessing an invalid key is undefined behavior.
assert(pthread_getspecific(PTHREAD_KEYS_MAX) == NULL);
assert(pthread_setspecific(PTHREAD_KEYS_MAX, NULL) == EINVAL);
pthread_key_t key;
assert(!pthread_key_create(&key, NULL));
assert(!pthread_setspecific(key, &key));
assert(pthread_getspecific(key) == &key);
assert(!pthread_key_delete(key));
pthread_t thread;
assert(!pthread_create(&thread, NULL, &worker1, NULL));
assert(!pthread_join(thread, NULL));
assert(pthread_getspecific(key) == NULL);
assert(!pthread_setspecific(key, &key));
assert(pthread_getspecific(key) == &key);
assert(dtors_entered == 2);
assert(!pthread_create(&thread, NULL, &worker2, NULL));
assert(!pthread_join(thread, NULL));
return 0;
}
@@ -0,0 +1,46 @@
#include <assert.h>
#include <pthread.h>
#include <signal.h>
_Atomic int handler_ready = 0;
_Atomic int thread_signal_ran = 0;
static void sig_handler(int sig, siginfo_t *info, void *ctx) {
(void)sig;
(void)info;
(void)ctx;
thread_signal_ran = 1;
}
static void *worker(void *arg) {
(void)arg;
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = sig_handler;
sa.sa_flags = SA_SIGINFO;
assert(!sigaction(SIGUSR1, &sa, NULL));
handler_ready = 1;
while (!thread_signal_ran)
;
return NULL;
}
int main() {
pthread_t thread;
assert(!pthread_create(&thread, NULL, &worker, NULL));
while (!handler_ready)
;
assert(!pthread_kill(thread, SIGUSR1));
assert(!pthread_join(thread, NULL));
assert(thread_signal_ran);
return 0;
}
@@ -0,0 +1,103 @@
#include <pthread.h>
#include <assert.h>
#include <errno.h>
#define TEST_ATTR(attr, field, value) ({ \
int x; \
assert(!pthread_mutexattr_set ## field (&(attr), (value))); \
assert(!pthread_mutexattr_get ## field (&(attr), &x)); \
assert(x == (value)); \
})
int variable;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static void *worker(void *arg) {
(void)arg;
pthread_mutex_lock(&mutex);
variable = 1;
pthread_mutex_unlock(&mutex);
return NULL;
}
static void testAttr() {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
TEST_ATTR(attr, type, PTHREAD_MUTEX_DEFAULT);
TEST_ATTR(attr, type, PTHREAD_MUTEX_NORMAL);
TEST_ATTR(attr, type, PTHREAD_MUTEX_ERRORCHECK);
TEST_ATTR(attr, type, PTHREAD_MUTEX_RECURSIVE);
TEST_ATTR(attr, robust, PTHREAD_MUTEX_STALLED);
TEST_ATTR(attr, robust, PTHREAD_MUTEX_ROBUST);
TEST_ATTR(attr, protocol, PTHREAD_PRIO_NONE);
TEST_ATTR(attr, protocol, PTHREAD_PRIO_INHERIT);
TEST_ATTR(attr, protocol, PTHREAD_PRIO_PROTECT);
TEST_ATTR(attr, pshared, PTHREAD_PROCESS_PRIVATE);
TEST_ATTR(attr, pshared, PTHREAD_PROCESS_SHARED);
// TODO: sched_get_priority* is unimplemented.
// int prio = sched_get_priority_max(SCHED_FIFO);
// TEST_ATTR(attr, prioceiling, prio);
pthread_mutexattr_destroy(&attr);
}
static void testNormal() {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutex_init(&mutex, &attr);
pthread_mutexattr_destroy(&attr);
pthread_mutex_lock(&mutex);
variable = 0;
pthread_t thread;
int ret = pthread_create(&thread, NULL, &worker, NULL);
assert(!ret);
assert(pthread_mutex_trylock(&mutex) == EBUSY);
pthread_mutex_unlock(&mutex);
ret = pthread_join(thread, NULL);
assert(!ret);
assert(variable == 1);
pthread_mutex_destroy(&mutex);
}
static void testRecursive() {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex, &attr);
pthread_mutexattr_destroy(&attr);
pthread_mutex_lock(&mutex);
variable = 0;
pthread_t thread;
int ret = pthread_create(&thread, NULL, &worker, NULL);
assert(!ret);
assert(pthread_mutex_trylock(&mutex) == 0);
pthread_mutex_unlock(&mutex);
pthread_mutex_unlock(&mutex);
ret = pthread_join(thread, NULL);
assert(!ret);
assert(variable == 1);
pthread_mutex_destroy(&mutex);
}
int main() {
testAttr();
testNormal();
testRecursive();
return 0;
}
@@ -0,0 +1,116 @@
#include <assert.h>
#include <errno.h>
#include <pthread.h>
static void test_write_lock_unlock() {
int res;
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
res = pthread_rwlock_wrlock(&rw);
assert(!res);
res = pthread_rwlock_unlock(&rw);
assert(!res);
}
static void test_read_lock_unlock() {
int res;
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
res = pthread_rwlock_rdlock(&rw);
assert(!res);
res = pthread_rwlock_unlock(&rw);
assert(!res);
}
static void test_write_trylock_unlock() {
int res;
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
res = pthread_rwlock_trywrlock(&rw);
assert(!res);
res = pthread_rwlock_unlock(&rw);
assert(!res);
}
static void test_read_trylock_unlock() {
int res;
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
res = pthread_rwlock_tryrdlock(&rw);
assert(!res);
res = pthread_rwlock_unlock(&rw);
assert(!res);
}
static void test_write_prevents_read() {
int res;
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
res = pthread_rwlock_wrlock(&rw);
assert(!res);
res = pthread_rwlock_tryrdlock(&rw);
assert(res == EBUSY);
res = pthread_rwlock_unlock(&rw);
assert(!res);
}
static void test_write_prevents_write() {
int res;
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
res = pthread_rwlock_wrlock(&rw);
assert(!res);
res = pthread_rwlock_trywrlock(&rw);
assert(res == EBUSY);
res = pthread_rwlock_unlock(&rw);
assert(!res);
}
static void test_read_prevents_write() {
int res;
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
res = pthread_rwlock_rdlock(&rw);
assert(!res);
res = pthread_rwlock_trywrlock(&rw);
assert(res == EBUSY);
res = pthread_rwlock_unlock(&rw);
assert(!res);
}
static void test_read_allows_read() {
int res;
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;
res = pthread_rwlock_rdlock(&rw);
assert(!res);
res = pthread_rwlock_tryrdlock(&rw);
assert(!res);
res = pthread_rwlock_unlock(&rw);
assert(!res);
}
static void test_attr() {
pthread_rwlockattr_t attr;
pthread_rwlockattr_init(&attr);
int pshared;
pthread_rwlockattr_getpshared(&attr, &pshared);
assert(pshared == PTHREAD_PROCESS_PRIVATE);
pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_rwlockattr_getpshared(&attr, &pshared);
assert(pshared == PTHREAD_PROCESS_SHARED);
pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
pthread_rwlockattr_getpshared(&attr, &pshared);
assert(pshared == PTHREAD_PROCESS_PRIVATE);
pthread_rwlockattr_destroy(&attr);
}
int main() {
test_write_lock_unlock();
test_read_lock_unlock();
test_write_trylock_unlock();
test_read_trylock_unlock();
test_write_prevents_read();
test_write_prevents_write();
test_read_prevents_write();
test_read_allows_read();
test_attr();
return 0;
}
@@ -0,0 +1,34 @@
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
int main() {
struct sched_param param = {
.sched_priority = 100,
};
int policy = 0xDEADBEEF;
int ret = pthread_getschedparam(pthread_self(), &policy, &param);
assert(policy == SCHED_OTHER);
assert(!ret);
param.sched_priority = 10;
ret = pthread_setschedparam(pthread_self(), SCHED_FIFO, &param);
assert(!ret || ret == EPERM);
if(ret == EPERM) {
exit(0);
}
param.sched_priority = 0xDEADBEEF;
ret = pthread_getschedparam(pthread_self(), &policy, &param);
assert(policy == SCHED_FIFO);
assert(param.sched_priority == 10);
assert(!ret);
return 0;
}
@@ -0,0 +1,55 @@
#include <stdio.h>
#include <pthread.h>
#include <assert.h>
_Thread_local unsigned int counter = 9999;
_Thread_local unsigned int uninitialized;
void *check_counter(void *arg)
{
(void)arg;
fprintf(stderr, "counter for worker thread: %d, at %p\n", counter, &counter);
fflush(stderr);
assert(counter == 9999);
fprintf(stderr, "uninitialized data for worker thread: %d, at %p\n", uninitialized, &uninitialized);
fflush(stderr);
assert(uninitialized == 0);
++counter;
++uninitialized;
fprintf(stderr, "counter for worker thread: %d\n", counter);
fflush(stderr);
assert(counter == 10000);
assert(uninitialized == 1);
return NULL;
}
int main()
{
fprintf(stderr, "counter for main thread: %d, at %p\n", counter, &counter);
fflush(stderr);
assert(counter == 9999);
fprintf(stderr, "uninitialized data for main thread: %d, at %p\n", uninitialized, &uninitialized);
fflush(stderr);
assert(uninitialized == 0);
++counter;
++uninitialized;
fprintf(stderr, "counter for main: %d\n", counter);
fflush(stderr);
assert(counter == 10000);
assert(uninitialized == 1);
pthread_t thd;
pthread_create(&thd, NULL, check_counter, NULL);
pthread_join(thd, NULL);
fprintf(stderr, "counter for main: %d\n", counter);
fflush(stderr);
assert(counter == 10000);
assert(uninitialized == 1);
return 0;
}
+128
View File
@@ -0,0 +1,128 @@
#include <stdlib.h>
#include <stdio.h>
#include <pwd.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
int main()
{
struct passwd pwd, *result = NULL;
char *buf;
size_t bufsize;
int s;
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
assert(bufsize > 0 && bufsize < 0x100000);
buf = malloc(bufsize);
assert(buf);
s = getpwnam_r("root", &pwd, buf, bufsize, &result);
assert(!s);
assert(pwd.pw_uid == 0);
assert(!strcmp(pwd.pw_name, "root"));
assert(strlen(pwd.pw_passwd) <= 1000);
s = getpwuid_r(0, &pwd, buf, bufsize, &result);
assert(!s);
assert(pwd.pw_uid == 0);
assert(!strcmp(pwd.pw_name, "root"));
assert(strlen(pwd.pw_passwd) <= 1000);
result = getpwnam("root");
assert(result);
assert(result->pw_uid == 0);
assert(!strcmp(result->pw_name, "root"));
assert(strlen(result->pw_passwd) <= 1000);
result = getpwuid(0);
assert(result);
assert(result->pw_uid == 0);
assert(!strcmp(result->pw_name, "root"));
assert(strlen(result->pw_passwd) <= 1000);
errno = 0;
setpwent();
assert(errno == 0);
errno = 0;
result = getpwent();
assert(result);
pwd = *result;
pwd.pw_name = strdup(result->pw_name);
pwd.pw_passwd = strdup(result->pw_passwd);
pwd.pw_gecos = strdup(result->pw_gecos);
pwd.pw_dir = strdup(result->pw_dir);
pwd.pw_shell = strdup(result->pw_shell);
assert(pwd.pw_name);
assert(pwd.pw_passwd);
assert(pwd.pw_gecos);
assert(pwd.pw_dir);
assert(pwd.pw_shell);
errno = 0;
setpwent();
assert(errno == 0);
errno = 0;
result = getpwent();
assert(result);
assert(!strcmp(pwd.pw_name, result->pw_name));
assert(!strcmp(pwd.pw_passwd, result->pw_passwd));
assert(!strcmp(pwd.pw_gecos, result->pw_gecos));
assert(!strcmp(pwd.pw_dir, result->pw_dir));
assert(!strcmp(pwd.pw_shell, result->pw_shell));
assert(pwd.pw_uid == result->pw_uid);
assert(pwd.pw_gid == result->pw_gid);
free(pwd.pw_name);
free(pwd.pw_passwd);
free(pwd.pw_gecos);
free(pwd.pw_dir);
free(pwd.pw_shell);
pwd.pw_name = "managarm";
pwd.pw_passwd = "passwordhash";
pwd.pw_uid = 1234;
pwd.pw_gid = 12345;
pwd.pw_gecos = "/etc/password test";
pwd.pw_dir = "/home/managarm\n";
pwd.pw_shell = "/usr/bin/bash";
// tmpfile() cannot be used because its unimplemented in mlibc.
char filename[] = "pwdXXXXXX";
int tmpfd = mkstemp(filename);
assert(tmpfd);
FILE *tmp = fdopen(tmpfd, "w+");
assert(tmp);
assert(putpwent(NULL, tmp) < 0);
assert(putpwent(&pwd, tmp) < 0);
pwd.pw_dir = "/home/managarm:";
assert(putpwent(&pwd, tmp) < 0);
pwd.pw_dir = "/home/:managarm\n";
assert(putpwent(&pwd, tmp) < 0);
pwd.pw_dir = "/home/managarm";
assert(putpwent(&pwd, tmp) == 0);
rewind(tmp);
char *expected = "managarm:passwordhash:1234:12345:/etc/password test:/home/managarm:/usr/bin/bash\n";
size_t expectedlen = strlen(expected);
size_t readsize = fread(buf, 1, bufsize, tmp);
assert(readsize == expectedlen);
assert(strncmp(expected, buf, expectedlen) == 0);
fclose(tmp);
unlink(filename);
free(buf);
return 0;
}
@@ -0,0 +1,53 @@
#include <assert.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/uio.h>
#ifdef USE_HOST_LIBC
#define TEST_FILE "readv-writev-host-libc.tmp"
#else
#define TEST_FILE "readv-writev.tmp"
#endif
int main() {
// Make sure that it wasn't created by a previous test run
unlink(TEST_FILE);
int fd = open(TEST_FILE, O_RDWR | O_CREAT, 0644);
assert(fd != -1);
char str0[] = "hello ";
char str1[] = "world!";
struct iovec bufs[2] = {
{
.iov_base = &str0,
.iov_len = strlen(str0),
},
{
.iov_base = &str1,
.iov_len = strlen(str1),
},
};
ssize_t written = writev(fd, bufs, 2);
assert(written == 12);
memset(&str0, 0, strlen(str0));
memset(&str1, 0, strlen(str1));
assert(!lseek(fd, 0, SEEK_SET));
ssize_t read = readv(fd, bufs, 2);
assert(read == 12);
assert(!strncmp(str0, "hello ", 7));
assert(!strncmp(str1, "world!", 7));
assert(!close(fd));
unlink(TEST_FILE);
return 0;
}
+100
View File
@@ -0,0 +1,100 @@
#include <unistd.h>
#include <sys/stat.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#ifdef USE_HOST_LIBC
#define TEST_BASE "/tmp/mlibc-realpath-host-libc"
#else
#define TEST_BASE "/tmp/mlibc-realpath"
#endif
void prepare() {
assert(!mkdir(TEST_BASE "/", S_IRWXU));
assert(!mkdir(TEST_BASE "/dir1", S_IRWXU));
assert(!mkdir(TEST_BASE "/dir2", S_IRWXU));
assert(!symlink(TEST_BASE "/dir2/", TEST_BASE "/dir1/abs-link"));
assert(!symlink(TEST_BASE "/dir2///", TEST_BASE "/dir1/abs-link-trail"));
assert(!symlink(TEST_BASE "/dir2", TEST_BASE "/dir1/abs-link-no-trail"));
assert(!chdir(TEST_BASE "/dir1"));
assert(!symlink("../dir2/", TEST_BASE "/dir1/rel-link"));
}
void cleanup(int do_assert) {
if (do_assert) {
assert(!unlink(TEST_BASE "/dir1/rel-link"));
assert(!unlink(TEST_BASE "/dir1/abs-link"));
assert(!unlink(TEST_BASE "/dir1/abs-link-trail"));
assert(!unlink(TEST_BASE "/dir1/abs-link-no-trail"));
assert(!rmdir(TEST_BASE "/dir2"));
assert(!rmdir(TEST_BASE "/dir1"));
assert(!rmdir(TEST_BASE "/"));
} else {
unlink(TEST_BASE "/dir1/rel-link");
unlink(TEST_BASE "/dir1/abs-link");
unlink(TEST_BASE "/dir1/abs-link-trail");
unlink(TEST_BASE "/dir1/abs-link-no-trail");
rmdir(TEST_BASE "/dir2");
rmdir(TEST_BASE "/dir1");
rmdir(TEST_BASE "/");
}
}
void signal_handler(int sig, siginfo_t *info, void *ctx) {
(void)sig;
(void)info;
(void)ctx;
cleanup(0);
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_handler = SIG_DFL;
sa.sa_flags = 0;
sigaction(SIGABRT, &sa, NULL);
abort();
}
#define TEST_PATH(rpath, expected) \
path = realpath(rpath, NULL); \
assert(path); \
assert(!strcmp(path, expected)); \
free(path);
int main() {
char *path;
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = signal_handler;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGABRT, &sa, NULL);
prepare();
TEST_PATH(TEST_BASE "/dir1/", TEST_BASE "/dir1");
TEST_PATH(TEST_BASE "/dir1/../dir2", TEST_BASE "/dir2");
TEST_PATH(TEST_BASE "/dir1/abs-link/", TEST_BASE "/dir2");
TEST_PATH(TEST_BASE "/dir1/rel-link/", TEST_BASE "/dir2");
TEST_PATH(TEST_BASE "/dir1/abs-link/../", TEST_BASE "");
TEST_PATH(TEST_BASE "/dir1/rel-link/../", TEST_BASE "");
TEST_PATH(TEST_BASE "/dir1/abs-link/../dir1/abs-link/", TEST_BASE "/dir2");
TEST_PATH(TEST_BASE "/dir1/rel-link/../dir1/rel-link/", TEST_BASE "/dir2");
TEST_PATH(TEST_BASE "/dir1/abs-link/../dir1/rel-link/", TEST_BASE "/dir2");
TEST_PATH(TEST_BASE "/dir1/rel-link/../dir1/abs-link/", TEST_BASE "/dir2");
TEST_PATH(TEST_BASE "/dir1/abs-link-no-trail/", TEST_BASE "/dir2");
TEST_PATH(TEST_BASE "/dir1/abs-link-trail/", TEST_BASE "/dir2");
TEST_PATH("/tmp", "/tmp");
TEST_PATH("/", "/");
TEST_PATH("//", "/");
cleanup(1);
return 0;
}
+30
View File
@@ -0,0 +1,30 @@
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main(void) {
char *testString = "mlibc is the best best best libc";
char *pattern = "\\(be[a-z]t\\) \\1";
regex_t reg;
int rc = regcomp(&reg, pattern, 0);
assert(!rc);
regmatch_t matches[2];
rc = regexec(&reg, testString, 2, matches, 0);
assert(!rc);
printf("Whole pattern: \"%.*s\" at %zd-%zd.\n",
(int)(matches[0].rm_eo - matches[0].rm_so), &testString[matches[0].rm_so],
(ssize_t)matches[0].rm_so, (ssize_t)(matches[0].rm_eo - 1));
assert(matches[0].rm_so == 13 && matches[0].rm_eo == 22);
printf("Substring: \"%.*s\" at %zd-%zd.\n",
(int)(matches[1].rm_eo - matches[1].rm_so), &testString[matches[1].rm_so],
(ssize_t)matches[1].rm_so, (ssize_t)matches[1].rm_eo - 1);
assert(matches[1].rm_so == 13 && matches[1].rm_eo == 17);
regfree(&reg);
return 0;
}
+11
View File
@@ -0,0 +1,11 @@
#include <strings.h>
#include <assert.h>
int main() {
char str[] = "This is a sample string";
char *pch;
pch = rindex(str, 's');
// The last occurence of 's' is at position 18
assert(pch - str + 1 == 18);
return 0;
}
+26
View File
@@ -0,0 +1,26 @@
#include <sys/resource.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
int main() {
struct rlimit getlim, setlim;
setlim.rlim_cur = 16;
setlim.rlim_max = 4096;
int ret = setrlimit(RLIMIT_NOFILE, &setlim);
if(ret == -1) {
fprintf(stderr, "%s\n", strerror(errno));
assert(!ret);
}
assert(!getrlimit(RLIMIT_NOFILE, &getlim));
assert(setlim.rlim_cur == getlim.rlim_cur);
assert(setlim.rlim_max == getlim.rlim_max);
return 0;
}
+96
View File
@@ -0,0 +1,96 @@
#include <search.h>
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <errno.h>
static int compare(const void *pa, const void *pb) {
if (*(int*)pa < *(int*) pb)
return -1;
if (*(int*)pa > *(int*) pb)
return 1;
return 0;
}
static void check_key(int key, void *root) {
int keyp = key;
void *ret = tfind((void*) &keyp, &root, compare);
assert(ret);
assert(**((int **) ret) == key);
}
static int freed = 0;
static void free_key(void *key) {
freed++;
free(key);
}
int main() {
void *root = NULL;
for (int i = 0; i < 12; i++) {
int *ptr = malloc(sizeof(int));
assert(ptr);
*ptr = i;
void *ret = tsearch((void*) ptr, &root, compare);
assert(ret);
assert(**((int **) ret) == i);
}
// Test a couple of keys
check_key(1, root);
check_key(5, root);
check_key(10, root);
// Verify NULL on non-existent key
int key = -1;
void *ret = tfind((void*) &key, &root, compare);
assert(ret == NULL);
tdestroy(root, free_key);
assert(freed == 12);
assert(hcreate(3));
// Search for a non-existent entry
ENTRY entry = {
.key = (char *)"foo",
.data = (void *)0x12345678
};
ENTRY *result = hsearch(entry, FIND);
assert(!result);
assert(errno == ESRCH);
// Add a couple keys
assert(hsearch(entry, ENTER));
entry.key = (char *)"bar";
entry.data = (void *)0x87654321;
assert(hsearch(entry, ENTER));
entry.key = (char *)"baz";
entry.data = (void *)0x12344321;
assert(hsearch(entry, ENTER));
// Make sure that we can't add more keys
entry.key = (char *)"not existing";
assert(!hsearch(entry, ENTER));
assert(errno == ENOMEM);
// Check that the entries are in the hash table
entry.key = (char *)"baz";
result = hsearch(entry, FIND);
assert(result);
assert(result->data == (void *)0x12344321);
entry.key = (char *)"foo";
result = hsearch(entry, FIND);
assert(result);
assert(result->data == (void *)0x12345678);
entry.key = (char *)"bar";
result = hsearch(entry, FIND);
assert(result);
assert(result->data == (void *)0x87654321);
hdestroy();
return 0;
}
@@ -0,0 +1,28 @@
#include <semaphore.h>
#include <assert.h>
#include <errno.h>
int main() {
sem_t sem;
assert(sem_init(&sem, 0, 1) == 0);
assert(sem_destroy(&sem) == 0);
assert(sem_init(&sem, 0, -1) == -1);
assert(errno == EINVAL);
assert(sem_init(&sem, 0, 3) == 0);
assert(sem_trywait(&sem) == 0);
assert(sem_trywait(&sem) == 0);
assert(sem_trywait(&sem) == 0);
assert(sem_trywait(&sem) == -1);
assert(errno == EAGAIN);
assert(sem_destroy(&sem) == 0);
sem_init(&sem, 0, 1);
assert(sem_wait(&sem) == 0);
assert(sem_trywait(&sem) == -1);
assert(sem_post(&sem) == 0);
assert(sem_wait(&sem) == 0);
assert(sem_destroy(&sem) == 0);
return 0;
}
@@ -0,0 +1,29 @@
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/resource.h>
#include <unistd.h>
int main() {
errno = 0;
int original_priority = getpriority(PRIO_PROCESS, getpid());
assert(original_priority != -1 || !errno);
int ret = setpriority(PRIO_PROCESS, getpid(), original_priority + 1);
if(ret) {
fprintf(stderr, "%s", strerror(errno));
exit(1);
}
errno = 0;
int new_priority = getpriority(PRIO_PROCESS, getpid());
assert(new_priority != -1 || !errno);
assert(new_priority == original_priority + 1);
return 0;
}
+35
View File
@@ -0,0 +1,35 @@
#include <sys/shm.h>
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
int main(void) {
size_t page_size = getpagesize();
srand(time(NULL));
int shmid = -1;
for (int i = 0; i < 10; i++) {
shmid = shmget((key_t)rand(), page_size, 0644 | IPC_CREAT | IPC_EXCL);
if (shmid != -1) {
break;
}
}
assert(shmid != -1);
struct shmid_ds buf;
assert(shmctl(shmid, IPC_STAT, &buf) >= 0);
assert((size_t)buf.shm_segsz == page_size);
assert(buf.shm_cpid == getpid());
void *attach_addr = shmat(shmid, NULL, SHM_RDONLY);
assert(attach_addr != NULL);
assert(shmctl(shmid, IPC_RMID, &buf) >= 0);
assert(shmdt(attach_addr) == 0);
return 0;
}
@@ -0,0 +1,63 @@
#include <assert.h>
#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>
static jmp_buf env;
static char *sigStack;
static void sig_handler(int sig, siginfo_t *info, void *ctx) {
(void)sig;
(void)info;
(void)ctx;
longjmp(env, 1);
}
int main() {
if (setjmp(env)) {
free(sigStack);
return 0;
}
sigStack = malloc(SIGSTKSZ);
stack_t ss;
ss.ss_sp = sigStack;
ss.ss_size = SIGSTKSZ;
ss.ss_flags = 0;
assert(!sigaltstack(&ss, NULL));
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
sa.sa_sigaction = sig_handler;
assert(!sigaction(SIGSEGV, &sa, NULL));
// This is used to trash the stack to ensure sigaltstack actually switched stacks.
#if defined(__x86_64__)
asm volatile ("mov $0, %rsp\n"
"\t" "push $0");
#elif defined(__i386__)
asm volatile ("mov $0, %esp\n"
"\t" "push $0");
#elif defined(__aarch64__)
asm volatile ("mov sp, %0\n"
"\t" "stp x0, x1, [sp, #-16]!" :: "r"((uint64_t)0));
#elif defined(__riscv) && __riscv_xlen == 64
asm volatile ("li sp, 0\n"
"\t" "sd zero, 0(sp)");
#elif defined (__m68k__)
asm volatile ("move.l #0, %sp\n"
"\t" "move.l #0, -(%sp)");
#elif defined(__loongarch64)
asm volatile ("addi.d $sp, $r0, 0\n"
"\t" "st.d $r0, $sp, 0");
#else
# error Unknown architecture
#endif
return 0;
}
@@ -0,0 +1,53 @@
#include <assert.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>
_Atomic int handler_ready = 0;
_Atomic int thread_signal_ran = 0;
static void sig_handler(int sig, siginfo_t *info, void *ctx) {
(void)sig;
(void)info;
(void)ctx;
thread_signal_ran = 1;
}
static void *worker(void *arg) {
(void)arg;
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = sig_handler;
sa.sa_flags = SA_SIGINFO;
assert(!sigaction(SIGUSR1, &sa, NULL));
handler_ready = 1;
sigset_t set;
sigfillset(&set);
sigdelset(&set, SIGUSR1);
assert(sigsuspend(&set));
assert(thread_signal_ran);
assert(errno == EINTR);
return NULL;
}
int main() {
pthread_t thread;
assert(!pthread_create(&thread, NULL, &worker, NULL));
while (!handler_ready)
;
sleep(1);
assert(!pthread_kill(thread, SIGUSR1));
assert(!pthread_join(thread, NULL));
return 0;
}
@@ -0,0 +1,93 @@
#include <stdio.h>
#include <assert.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
pid_t parent;
pid_t child;
int fds[2];
void parent_fn() {
int res;
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGUSR2);
sigprocmask(SIG_BLOCK, &set, NULL);
res = write(fds[1], "!", 1);
assert(res == 1);
siginfo_t info;
res = sigwaitinfo(&set, &info);
assert(res == SIGUSR2);
assert(info.si_signo == SIGUSR2);
res = write(fds[1], "!", 1);
assert(res == 1);
// XXX: This may not be long enough to get scheduled
struct timespec tout = {1, 0};
res = sigtimedwait(&set, &info, &tout);
assert(res == SIGUSR2);
assert(info.si_signo == SIGUSR2);
res = write(fds[1], "!", 1);
assert(res == 1);
int sig;
res = sigwait(&set, &sig);
assert(res == 0);
assert(sig == SIGUSR2);
res = write(fds[1], "!", 1);
assert(res == 1);
res = sigtimedwait(&set, &info, &tout);
assert(res < 0);
assert(errno == EAGAIN);
int wsts;
res = waitpid(child, &wsts, 0);
assert(res >= 0);
}
void child_fn() {
int res;
char c;
res = read(fds[0], &c, 1);
assert(res == 1);
kill(parent, SIGUSR2);
res = read(fds[0], &c, 1);
assert(res == 1);
kill(parent, SIGUSR2);
res = read(fds[0], &c, 1);
assert(res == 1);
kill(parent, SIGUSR2);
res = read(fds[0], &c, 1);
assert(res == 1);
}
int main() {
int res;
parent = getpid();
assert(parent > 0);
res = pipe(fds);
assert(res == 0);
child = fork();
assert(child >= 0);
if (child)
parent_fn();
else
child_fn();
return 0;
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <assert.h>
#include <string.h>
int main() {
char test[19] = "Hello mlibc World!";
char *alloca_ed = strdupa(test);
assert(!strcmp(test, alloca_ed));
char *trimmed = strndupa(test, 5);
assert(!strcmp("Hello", trimmed));
return 0;
}
+29
View File
@@ -0,0 +1,29 @@
#include <string.h>
#include <assert.h>
int main() {
char buf[4];
// stpncpy
assert(stpncpy(buf, "", 4) == buf);
assert(!strcmp(buf, ""));
assert(stpncpy(buf, "123", 4) == buf + 3);
assert(!strcmp(buf, "123"));
assert(stpncpy(buf, "12", 4) == buf + 2);
assert(buf[0] == '1' && buf[1] == '2' && buf[2] == '\0' && buf[3] == '\0');
assert(stpncpy(buf, "123456", 4) == buf + 4);
assert(buf[0] == '1' && buf[1] == '2' && buf[2] == '3' && buf[3] == '4');
// stpcpy
assert(stpcpy(buf, "") == buf);
assert(!strcmp(buf, ""));
assert(stpcpy(buf, "12") == buf + 2);
assert(!strcmp(buf, "12"));
assert(stpcpy(buf, "123") == buf + 3);
assert(!strcmp(buf, "123"));
}
+66
View File
@@ -0,0 +1,66 @@
#include <assert.h>
#include <stdint.h>
#include <unistd.h>
void test_swab_even_bytes() {
uint8_t input[] = {0x01, 0x02, 0x03, 0x04};
uint8_t output[4];
swab(input, output, sizeof(input));
assert(output[0] == 0x02);
assert(output[1] == 0x01);
assert(output[2] == 0x04);
assert(output[3] == 0x03);
}
void test_swab_odd_bytes() {
uint8_t input[] = {0x01, 0x02, 0x03, 0x04, 0x05};
uint8_t output[5] = {0, 0, 0, 0, 0};
swab(input, output, sizeof(input));
assert(output[0] == 0x02);
assert(output[1] == 0x01);
assert(output[2] == 0x04);
assert(output[3] == 0x03);
// Last byte is UB, assume unchanged?
assert(output[4] == 0);
}
void test_swab_negative_bytes() {
uint8_t input[] = {0x01, 0x02, 0x03, 0x04};
uint8_t output[4] = {0, 0, 0, 0};
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overflow"
swab(input, output, -1); // Should change nothing
#pragma GCC diagnostic pop
assert(output[0] == 0);
assert(output[1] == 0);
assert(output[2] == 0);
assert(output[3] == 0);
}
void test_swab_zero_bytes() {
uint8_t input[] = {0x01, 0x02, 0x03, 0x04};
uint8_t output[4] = {0, 0, 0, 0};
swab(input, output, 0); // Should change nothing
assert(output[0] == 0);
assert(output[1] == 0);
assert(output[2] == 0);
assert(output[3] == 0);
}
int main() {
test_swab_even_bytes();
test_swab_odd_bytes();
test_swab_negative_bytes();
test_swab_zero_bytes();
return 0;
}
+20
View File
@@ -0,0 +1,20 @@
#include <assert.h>
#include <stdlib.h>
#include <sys/wait.h>
int main() {
int res;
res = system("true");
assert(WIFEXITED(res));
assert(WEXITSTATUS(res) == 0);
res = system("false");
assert(WIFEXITED(res));
assert(WEXITSTATUS(res) == 1);
res = system(NULL);
assert(res == 1);
return 0;
}
+193
View File
@@ -0,0 +1,193 @@
#include <assert.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define BUF_SIZE 1024
int main() {
struct tm tm = {0};
char buf[BUF_SIZE];
char *a = strptime("%", "%%", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(strftime(buf, BUF_SIZE, "%%", &tm) == 1);
assert(!strcmp(buf, "%"));
memset(&tm, 0, sizeof(tm));
a = strptime("1991-11-21", "%F", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_mday == 21);
assert(tm.tm_mon == 10);
assert(tm.tm_wday == 4);
assert(tm.tm_yday == 324);
assert(strftime(buf, BUF_SIZE, "%F", &tm) == 10);
assert(!strcmp(buf, "1991-11-21"));
memset(&tm, 0, sizeof(tm));
a = strptime("10/19/91", "%D", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_mday == 19);
assert(tm.tm_mon == 9);
assert(tm.tm_year == 91);
assert(tm.tm_wday == 6);
assert(tm.tm_yday == 291);
assert(strftime(buf, BUF_SIZE, "%D", &tm) == 8);
assert(!strcmp(buf, "10/19/91"));
memset(&tm, 0, sizeof(tm));
a = strptime("15:23", "%R", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_min == 23);
assert(tm.tm_hour == 15);
assert(strftime(buf, BUF_SIZE, "%R", &tm) == 5);
assert(!strcmp(buf, "15:23"));
memset(&tm, 0, sizeof(tm));
a = strptime("17:12:56", "%T", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_sec == 56);
assert(tm.tm_min == 12);
assert(tm.tm_hour == 17);
assert(strftime(buf, BUF_SIZE, "%T", &tm) == 8);
assert(!strcmp(buf, "17:12:56"));
memset(&tm, 0, sizeof(tm));
a = strptime("10", "%m", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_yday == 272);
assert(strftime(buf, BUF_SIZE, "%m", &tm) == 2);
assert(!strcmp(buf, "10"));
memset(&tm, 0, sizeof(tm));
a = strptime("14 83", "%C %y", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_year == -417);
assert(strftime(buf, BUF_SIZE, "%C %y", &tm) == 5);
assert(!strcmp(buf, "14 83"));
memset(&tm, 0, sizeof(tm));
a = strptime("32 16", "%y %C", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_year == -268);
assert(tm.tm_wday == 3);
assert(strftime(buf, BUF_SIZE, "%y %C", &tm) == 5);
assert(!strcmp(buf, "32 16"));
memset(&tm, 0, sizeof(tm));
a = strptime("12", "%C", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_year == -700);
assert(tm.tm_wday == 5);
assert(strftime(buf, BUF_SIZE, "%C", &tm) == 2);
assert(!strcmp(buf, "12"));
memset(&tm, 0, sizeof(tm));
a = strptime("1683-9-23", "%F", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_mday == 23);
assert(tm.tm_mon == 8);
assert(tm.tm_year == -217);
assert(tm.tm_wday == 4);
assert(tm.tm_yday == 265);
assert(strftime(buf, BUF_SIZE, "%F", &tm) == 10);
assert(!strcmp(buf, "1683-09-23"));
memset(&tm, 0, sizeof(tm));
a = strptime("14 53", "%H%t%S", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_sec == 53);
assert(tm.tm_hour == 14);
assert(strftime(buf, BUF_SIZE, "%H%t%S", &tm) == 5);
assert(!strcmp(buf, "14 53"));
memset(&tm, 0, sizeof(tm));
a = strptime("24", "%H", &tm);
assert(a == NULL);
memset(&tm, 0, sizeof(tm));
a = strptime("0", "%I", &tm);
assert(a == NULL);
memset(&tm, 0, sizeof(tm));
setlocale(LC_TIME, "en_US.UTF-8");
a = strptime("10 21 PM", "%I %M %p", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_hour == 22);
assert(tm.tm_min == 21);
assert(strftime(buf, BUF_SIZE, "%I %M %p", &tm) == 8);
assert(!strcmp(buf, "10 21 PM"));
memset(&tm, 0, sizeof(tm));
tm.tm_min = 23;
assert(strftime(buf, BUF_SIZE, "%I %M", &tm) == 5);
assert(!strcmp(buf, "12 23"));
memset(&tm, 0, sizeof(tm));
a = strptime("January", "%h", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_mon == 0);
assert(strftime(buf, BUF_SIZE, "%h %b", &tm) == 7);
assert(!strcmp(buf, "Jan Jan"));
assert(strftime(buf, BUF_SIZE, "%B", &tm) == 7);
assert(!strcmp(buf, "January"));
memset(&tm, 0, sizeof(tm));
a = strptime("2", "%j", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_yday == 1);
assert(strftime(buf, BUF_SIZE, "%j", &tm) == 3);
assert(!strcmp(buf, "002"));
memset(&tm, 0, sizeof(tm));
a = strptime("Wednesday", "%A", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_wday == 3);
assert(strftime(buf, BUF_SIZE, "%A", &tm) == 9);
assert(!strcmp(buf, "Wednesday"));
memset(&tm, 0, sizeof(tm));
a = strptime("11:51:13 PM", "%r", &tm);
assert(a != NULL);
assert(*a == '\0');
assert(tm.tm_hour == 23);
assert(tm.tm_min == 51);
assert(tm.tm_sec == 13);
assert(strftime(buf, BUF_SIZE, "%r", &tm) == 11);
assert(!strcmp(buf, "11:51:13 PM"));
memset(&tm, 0, sizeof(tm));
tm.tm_hour = 0;
tm.tm_min = 51;
tm.tm_sec = 13;
assert(strftime(buf, BUF_SIZE, "%r", &tm) == 11);
assert(!strcmp(buf, "12:51:13 AM"));
memset(&tm, 0, sizeof(tm));
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
assert(strftime(buf, BUF_SIZE, "%", &tm) == 1);
fprintf(stderr, "%s\n", buf);
assert(!strcmp(buf, "%"));
memset(&tm, 0, sizeof(tm));
#pragma GCC diagnostic pop
return 0;
}
+25
View File
@@ -0,0 +1,25 @@
#include <stdio.h>
#include <sys/time.h>
#include <assert.h>
int main() {
struct timeval a = {0, 0};
struct timeval b = {0, 0};
struct timeval res = {0, 0};
a.tv_sec = 10;
assert(timerisset(&a) == 1);
timerclear(&a);
assert(timerisset(&a) == 0);
a.tv_sec = 40;
a.tv_usec = 500;
b.tv_sec = 10;
b.tv_usec = 20;
timeradd(&a, &b, &res);
assert(res.tv_sec == 50);
assert(res.tv_usec == 520);
timerclear(&res);
timersub(&a, &b, &res);
assert(res.tv_sec == 30);
assert(res.tv_usec == 480);
return 0;
}
@@ -0,0 +1,63 @@
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
static void testfor(const char *expected, char *shell, int *count) {
if (strcmp(shell, expected) == 0)
*count += 1;
}
#define BUFFER_SIZE (1024 * 512)
int main(void) {
setusershell();
FILE *f = fopen("/etc/shells", "r");
if (f == NULL) {
/* /etc/shells file is unreadable, test for the existance of /bin/sh and /bin/csh */
char *shell = getusershell();
int csh = 0;
int sh = 0;
testfor("/bin/sh", shell, &sh);
testfor("/bin/csh", shell, &csh);
shell = getusershell();
assert(shell);
testfor("/bin/sh", shell, &sh);
testfor("/bin/csh", shell, &csh);
assert(sh == 1 && csh == 1);
assert(getusershell() == NULL);
} else {
/* /etc/shells is there, read the whole file into a buffer and change all '\n's to '\0'. */
char buffer[BUFFER_SIZE + 1];
int nbytes = fread(buffer, 1, BUFFER_SIZE, f);
buffer[nbytes] = '\0';
for (int i = 0; i < nbytes; ++i) {
if (buffer[i] == '\n')
buffer[i] = '\0';
}
/* loop through each shell and make sure the result is right */
char *shell = buffer;
while (shell < &buffer[nbytes]) {
char *usershell = getusershell();
while (usershell && *usershell == '#')
usershell = getusershell();
while (*shell == '#')
shell += strlen(shell) + 1;
assert(strcmp(shell, usershell) == 0);
shell += strlen(shell) + 1;
}
}
endusershell();
return 0;
}
+30
View File
@@ -0,0 +1,30 @@
#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
void prevent_atforks(void){
exit(1);
}
int main() {
pid_t pid;
int status;
assert(!pthread_atfork(prevent_atforks, NULL, NULL));
switch (pid = vfork()) {
case -1:
perror("vfork");
abort();
case 0:
_exit(12);
default: break;
}
assert(wait(&status) == pid);
assert(WIFEXITED(status) && WEXITSTATUS(status) == 12);
return 0;
}
+22
View File
@@ -0,0 +1,22 @@
#include <assert.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
siginfo_t si;
int main() {
int pid = fork();
if(!pid)
exit(69);
int ret = waitid(P_ALL, 0, &si, WEXITED);
assert(ret == 0);
assert(si.si_pid == pid);
assert(si.si_signo == SIGCHLD);
assert(si.si_code == CLD_EXITED);
return 0;
}
+67
View File
@@ -0,0 +1,67 @@
#include <assert.h>
#include <locale.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#if UINTPTR_MAX == UINT64_MAX
#define WCHAR_SPEC ""
#else
#define WCHAR_SPEC "l"
#endif
/*
* The code in this test is taken from https://github.com/termux/wcwidth/,
* under the following license:
*
* Copyright (C) Fredrik Fornwall 2016.
* Distributed under the MIT License.
*
* Implementation of wcwidth(3) as a C port of:
* https://github.com/jquast/wcwidth
*
* Report issues at:
* https://github.com/termux/wcwidth
*/
static int tests_run;
static int test_failures;
void assertWidthIs(int expected_width, wchar_t c) {
tests_run++;
int actual_width = wcwidth(c);
if (actual_width != expected_width) {
fprintf(stderr, "ERROR: wcwidth(U+%04" WCHAR_SPEC "x, '%lc') returned %d, expected %d\n", c, (wint_t) c, actual_width, expected_width);
test_failures++;
}
}
int main() {
setlocale(LC_CTYPE, "C.UTF-8");
assertWidthIs(1, 'a');
assertWidthIs(1, L'ö');
// Some wide:
assertWidthIs(2, L'');
assertWidthIs(2, L'');
assertWidthIs(2, L'');
assertWidthIs(2, L'');
assertWidthIs(2, L'');
assertWidthIs(2, 0x679C);
assertWidthIs(2, 0x679D);
assertWidthIs(2, 0x2070E);
assertWidthIs(2, 0x20731);
#ifndef USE_HOST_LIBC
assertWidthIs(1, 0x11A3);
#endif
assertWidthIs(2, 0x1F428); // Koala emoji.
assertWidthIs(2, 0x231a); // Watch emoji.
if (test_failures > 0) printf("%d tests FAILED, ", test_failures);
printf("%d tests OK\n", tests_run - test_failures);
return (test_failures == 0) ? 0 : 1;
}
+140
View File
@@ -0,0 +1,140 @@
#include <assert.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <unistd.h>
#include <wordexp.h>
static const char *wordexp_return_stringify(int val, int *should_free);
struct we_test {
int expected_return;
const char *words;
int flags;
const char *wordv[16];
size_t wordc;
} test_cases[] = {
/* (unbalanced) pairs */
{WRDE_SYNTAX, "\\", 0, {NULL}, 0},
{WRDE_SYNTAX, "\'", 0, {NULL}, 0},
{0, "\'\'", 0, {""}, 1},
{0, "\'\"\'", 0, {"\""}, 1},
{WRDE_SYNTAX, "\"\'", 0, {NULL}, 0},
/* numbers & calculations */
{0, "$((5+5))", 0, {"10"}, 1},
{0, "$((-5+5))", 0, {"0"}, 1},
{0, "$((010))", 0, {"8"}, 1},
{0, "$((0x10))", 0, {"16"}, 1},
/* errant symbols */
{WRDE_BADCHAR, "(string", WRDE_NOCMD, {NULL}, 0},
{WRDE_BADCHAR, "string)", WRDE_NOCMD, {NULL}, 0},
{WRDE_BADCHAR, "{string", WRDE_NOCMD, {NULL}, 0},
{WRDE_BADCHAR, "string}", WRDE_NOCMD, {NULL}, 0},
{WRDE_BADCHAR, "<string", WRDE_NOCMD, {NULL}, 0},
{WRDE_BADCHAR, "string>", WRDE_NOCMD, {NULL}, 0},
{WRDE_BADCHAR, "string|", WRDE_NOCMD, {NULL}, 0},
{WRDE_BADCHAR, "string;", WRDE_NOCMD, {NULL}, 0},
{WRDE_BADCHAR, "string&", WRDE_NOCMD, {NULL}, 0},
/* command substitution with WRDE_NOCMD */
{WRDE_CMDSUB, "$(pwd)", WRDE_NOCMD, {NULL}, 0},
/* env vars */
{WRDE_BADVAL, "$DOES_NOT_EXIST", WRDE_UNDEF, {NULL}, 0},
/* normal arguments */
{0, "arg1 arg2", 0, {"arg1", "arg2"}, 2},
{-1, NULL, 0, {NULL}, 0},
};
int main() {
wordexp_t we;
wordexp("$(pwd)", &we, 0);
char *cwd = getcwd(NULL, 0);
assert(!strcmp(cwd, we.we_wordv[0]));
wordfree(&we);
free(cwd);
char *home;
assert(asprintf(&home, "%s/.config", getenv("HOME")) != -1);
wordexp("$HOME ~ ~/.config", &we, WRDE_REUSE);
assert(!strcmp(getenv("HOME"), we.we_wordv[0]));
assert(!strcmp(getenv("HOME"), we.we_wordv[1]));
assert(!strcmp(home, we.we_wordv[2]));
free(home);
wordfree(&we);
struct passwd *pw = getpwnam("root");
assert(asprintf(&home, "%s/.config", pw->pw_dir) != -1);
wordexp("~root ~root/.config", &we, WRDE_REUSE);
assert(!strcmp(pw->pw_dir, we.we_wordv[0]));
assert(!strcmp(home, we.we_wordv[1]));
free(home);
wordfree(&we);
size_t i = 0;
for(struct we_test *test = &test_cases[i]; test->expected_return != -1; test = &test_cases[++i]) {
wordexp_t test_we;
int should_free;
const char *expected = wordexp_return_stringify(test->expected_return, &should_free);
fprintf(stderr, "TESTCASE %zu: '%s' with flags %d expected to return %s\n", i, test->words, test->flags, expected);
int test_ret = wordexp(test->words, &test_we, test->flags);
if(test_ret != test->expected_return) {
fprintf(stderr, "\twordexp() returned %s, but we expect %s\n", wordexp_return_stringify(test_ret, &should_free), wordexp_return_stringify(test->expected_return, &should_free));
}
assert(test_ret == test->expected_return);
if(test_ret != 0)
continue;
assert(test_we.we_wordc == test->wordc);
for(size_t j = 0; j < 16 && j < test->wordc; j++) {
assert(test->wordv[j] && test_we.we_wordv[j]);
assert(!strcmp(test->wordv[j], test_we.we_wordv[j]));
}
wordfree(&test_we);
if (should_free)
free((void *)expected);
}
exit(EXIT_SUCCESS);
}
struct wordexp_return_val {
int val;
const char *string;
} wordexp_return_vals[] = {
{0, "WRDE_SUCCESS"},
{WRDE_BADCHAR, "WRDE_BADCHAR"},
{WRDE_BADVAL, "WRDE_BADVAL"},
{WRDE_CMDSUB, "WRDE_CMDSUB"},
{WRDE_SYNTAX, "WRDE_SYNTAX"},
{-1, NULL},
};
static const char *wordexp_return_stringify(int val, int *should_free) {
for(size_t i = 0; wordexp_return_vals[i].val != -1 || wordexp_return_vals[i].string; i++) {
if(wordexp_return_vals[i].val == val) {
*should_free = 0;
return wordexp_return_vals[i].string;
}
}
char *unknown;
assert(asprintf(&unknown, "unknown return value %d", val) != -1);
*should_free = 1;
return unknown;
}