Files
c3c/test/unit/stdlib/libc/libc.c3
limit-ordinal 85166bc706 Add NetBSD Support (#2661)
* Add NetBSD Support

Includes:
- Hints to find non-compatibility libc functions
- Struct and constant definitions for sockets, polling, etc.
- Changes to the linker code to work around some quirks in the NetBSD dynamic linker
- A target triple for netbsd aarch64 so llvm builds/links the compiler properly on this platform

* Updated releasenotes and some compacting

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-19 19:23:06 +01:00

569 lines
14 KiB
Plaintext

module libc_tests;
import libc;
// Functions from libc.c3 which do not have a test are still present in
// comments here in the same order that they occur within libc.c3
fn void abort() @test
{
// Cannot actually test this, so just checking if it's defined.
assert($defined(libc::abort));
}
fn void abs() @test
{
CInt x = 21;
assert(libc::abs(x) == 21);
assert(libc::abs(-x) == 21);
$assert $typeof(libc::abs(x)) == CInt;
}
fn void asctime() @test
{
Tm time = {
.tm_sec = 13,
.tm_min = 42,
.tm_hour = 3,
.tm_mday = 5,
.tm_mon = 8,
.tm_year = 11,
.tm_wday = 1,
.tm_yday = 41,
.tm_isdst = 0
};
ZString formatted_time = libc::asctime(&time);
assert(libc::strcmp(formatted_time, "Mon Sep 5 03:42:13 1911\n") == 0);
}
fn void asctime_r() @test @if(!env::WIN32)
{
Tm time = {
.tm_sec = 13,
.tm_min = 42,
.tm_hour = 3,
.tm_mday = 5,
.tm_mon = 8,
.tm_year = 11,
.tm_wday = 1,
.tm_yday = 41,
.tm_isdst = 0
};
char[26] formatted_time;
libc::asctime_r(&time, &formatted_time);
assert(libc::strcmp((ZString) &formatted_time, "Mon Sep 5 03:42:13 1911\n") == 0);
}
fn void test_atexit_function()
{
assert(5 == 5);
// This correctly asserts, but does not fail the tests.
// char c = 2;
// assert(c == 5);
}
fn void atexit() @test
{
// No idea how to test this, but we can at least check if this compiles
libc::atexit(&test_atexit_function);
libc::atexit(fn void() => 4);
}
fn void atof() @test
{
assert(libc::atof("123.45") == 123.45);
$assert $typeof(libc::atof("123.45")) == double;
assert(libc::atof("-3.14") == -3.14);
assert(libc::atof("x") == 0.0);
assert(libc::atof("") == 0.0);
}
fn void atoi() @test
{
assert(libc::atoi("123") == 123);
$assert $typeof(libc::atoi("123")) == CInt;
assert(libc::atoi("-3.14") == -3);
assert(libc::atoi("x") == 0);
assert(libc::atoi("") == 0);
}
fn void atoll() @test
{
assert(libc::atoll("123") == 123);
assert(libc::atoll("-3.14") == -3);
$assert $typeof(libc::atoll("-3.14")) == CLongLong;
assert(libc::atoll("x") == 0);
assert(libc::atoll("") == 0);
}
// Compare function, struct and function for libc::bsearch test
fn int compare_cint(void* a, void* b)
{
CInt a_i = *((CInt*) a);
CInt b_i = *((CInt*) b);
return a_i - b_i;
}
struct Event
{
CInt day;
CInt month;
String desc;
}
fn int compare_event(void* a, void* b)
{
Event* a_e = a; // Avoid casts if possible
Event* b_e = b;
return (a_e.month - b_e.month) * 100 + a_e.day - b_e.day;
}
fn void bsearch() @test
{
CInt[] int_ar = { 0, 1, 2, 3, 4, 5, 6 };
CInt key = 0;
CInt* found = (CInt*) libc::bsearch(&key, int_ar, 7, CInt.sizeof, &compare_cint);
assert(*found == 0);
key = 1;
found = (CInt*) libc::bsearch(&key, int_ar, 7, CInt.sizeof, &compare_cint);
assert(*found == 1);
key = 2;
found = (CInt*) libc::bsearch(&key, int_ar, 7, CInt.sizeof, &compare_cint);
assert(*found == 2);
key = 5;
found = (CInt*) libc::bsearch(&key, int_ar, 7, CInt.sizeof, &compare_cint);
assert(*found == 5);
key = 6;
found = (CInt*) libc::bsearch(&key, int_ar, 7, CInt.sizeof, &compare_cint);
assert(*found == 6);
CInt non_existent_key = 12;
found = (CInt*) libc::bsearch(&non_existent_key, int_ar, 7, CInt.sizeof, &compare_cint);
assert(found == null);
Event[] events = {
{ 2, 1, "2nd of February" },
{ 5, 1, "Some day" },
{ 1, 5, "Begin of May" },
{ 1, 7, "Summer holiday start" },
{ 21, 7, "A fine warm day" },
{ 11, 11, "Peace" },
{ 25, 12, "Family and sharing" },
{ 31, 12, "End of the year" }
};
Event searching = { .day = 1, .month = 5 };
Event* found1 = (Event*) libc::bsearch(&searching, events, 7, Event.sizeof, &compare_event);
assert(found1.desc == "Begin of May");
searching = { .day = 4, .month = 4 };
found1 = (Event*) libc::bsearch(&searching, events, 7, Event.sizeof, &compare_event);
assert(found1 == null);
}
fn void calloc() @test
{
usz amount = 10;
CInt* int_ar = libc::calloc(amount, CInt.sizeof);
for (usz i = 0; i < amount; i++)
{
assert(int_ar[i] == 0);
}
libc::free(int_ar);
}
fn void clearerr_feof_ferror() @test
{
CFile* tmpf = libc::tmpfile();
// Example from cppreference.com
ZString test_string = (ZString) "cppreference.com\n" +++ '\0';
libc::fputs(test_string, tmpf);
libc::rewind(tmpf);
for (int i = 0; i < test_string.len(); i++)
{
int c = libc::fgetc(tmpf);
assert(c != libc::EOF);
assert(c == test_string[i]);
}
assert(libc::fgetc(tmpf) == libc::EOF);
assert(libc::feof(tmpf) != 0);
// there should be no errors (EOF is not an error)
assert(libc::ferror(tmpf) == 0);
libc::clearerr(tmpf);
assert(libc::feof(tmpf) == 0);
}
fn void clock() @test
{
Clock_t time1 = libc::clock();
if (time1 < 0)
{
// If getting processor-time is not available, do nothing
return;
}
// Waist bit of time
int i = 0;
for (int j = 0; j <= 1000; j++)
{
i += j;
}
assert(i == (1000 / 2) * (1000 + 1)); // Gauss, prevent compiler-optimisation
Clock_t time2 = libc::clock();
assert(time2 >= time1);
}
fn void close() @test @if(!env::WIN32)
{
assert(libc::close(1313) != 0);
// NOTE: errno is part of libc, and is thus recursively imported
assert(libc::errno() == errno::EBADF);
}
fn void difftime() @test
{
Time_t t1 = 1293;
Time_t t2 = 919919;
assert(libc::difftime(t2, t1) > 0);
assert(libc::difftime(t1, t2) < 0);
}
fn void div() @test
{
DivResult res = libc::div(13, 2);
assert(res.quot == 6);
assert(res.rem == 1);
res = libc::div(-5, 3);
assert(res.quot == -1);
assert(res.rem == -2);
}
fn void exit() @test
{
// Cannot actually test this, so just checking if it's defined.
assert($defined(libc::exit));
}
fn void fclose() @test
{
CFile random_file = libc::tmpfile();
assert(libc::fclose(random_file) == 0);
}
fn void fdopen() @test @if(!env::WIN32)
{
CFile stdin = libc::fdopen(-1, "w");
assert(libc::errno() == errno::EBADF);
}
fn void fflush() @test
{
CFile stdout = libc::stdout();
assert(libc::fflush(stdout) == 0);
}
fn void fgets_fget() @test
{
CFile* tmpf = libc::tmpfile();
// Example from cppreference.com
ZString test_string = (ZString) "cppreference.com\n" +++ '\0';
libc::rewind(tmpf);
libc::fclose(tmpf);
}
import std::io;
fn void fseek_ftell_fgetpos_fsetpos_rewind() @test
{
CFile* tmpf = libc::tmpfile();
// Example from cppreference.com
ZString test_string = (ZString) "cppreference.com\n" +++ '\0';
libc::fputs(test_string, tmpf);
libc::rewind(tmpf);
Fpos_t pos;
libc::fgetpos(tmpf, &pos);
assert(libc::fgetc(tmpf) == 'c');
assert(libc::fgetc(tmpf) == 'p');
libc::fsetpos(tmpf, &pos);
assert(libc::fgetc(tmpf) == 'c');
assert(libc::fgetc(tmpf) == 'p');
assert(libc::ftell(tmpf) == 2);
libc::fseek(tmpf, -2, libc::SEEK_END);
assert(libc::fgetc(tmpf) == 'm');
assert(libc::fgetc(tmpf) == '\n');
libc::fclose(tmpf);
}
// fn void fileno() @test {}
// fn void fopen() @test {}
// fn void fprintf() @test {}
// fn void fputc() @test {}
// fn void fputs() @test {}
// fn void fread() @test {}
// fn void freopen() @test {}
// fn void fscanf() @test {}
// fn void fseek() @test {}
// fn void ftell() @test {}
// fn void fwrite() @test {}
// fn void getc() @test {}
// fn void getchar() @test {}
// fn void getenv() @test {}
// fn void gets() @test {}
// fn void gmtime() @test {}
// fn void gmtime_r() @test {}
// fn void isatty() @test {}
// fn void labs() @test {}
// fn void ldiv() @test {}
// fn void localtime() @test {}
// fn void localtime_r() @test {}
// fn void longjmp() @test {}
fn void malloc_free() @test
{
CInt* ar = libc::malloc(3 * CInt.sizeof);
ar[0] = 10;
ar[1] = -10;
ar[2] = 4;
assert(ar[0] == 10);
assert(ar[1] == -10);
assert(ar[2] == 4);
libc::free(ar);
}
// fn void memchr() @test {}
// fn void memcmp() @test {}
// fn void memcpy() @test {}
// fn void memmove() @test {}
// fn void memset() @test {}
// fn void mktime() @test {}
// fn void perror() @test {}
// fn void printf() @test {}
// fn void putc() @test {}
// fn void putchar() @test {}
// fn void puts() @test {}
// fn void qsort() @test {}
// fn void raise() @test {}
// fn void rand() @test {}
// fn void read() @test {}
// fn void realloc() @test {}
// fn void remove() @test {}
// fn void rename() @test {}
// fn void scanf() @test {}
// fn void setbuf() @test {}
// fn void setenv() @test {}
// fn void setjmp() @test {}
// fn void setvbuf() @test {}
// fn void signal() @test {}
// fn void snprintf() @test {}
// fn void sprintf() @test {}
// fn void srand() @test {}
// fn void sscanf() @test {}
// fn void strcat() @test {}
// fn void strchr() @test {}
// fn void strcmp() @test {}
// fn void strcoll() @test {}
// fn void strcspn() @test {}
// fn void strcpy() @test {}
// fn void strdup() @test {}
// fn void strerror() @test {}
// fn void strftime() @test {}
// fn void strlen() @test {}
// fn void strncat() @test {}
// fn void strncmp() @test {}
// fn void strncpy() @test {}
// fn void stroul() @test {}
// fn void strpbrk() @test {}
// fn void strspn() @test {}
// fn void strptime() @test {}
// fn void strrchr() @test {}
// fn void strstr() @test {}
// fn void strtod() @test {}
// fn void strtof() @test {}
// fn void strtok() @test {}
// fn void strtol() @test {}
// fn void strtoul() @test {}
// fn void strxfrm() @test {}
// fn void system() @test {}
// fn void timegm() @test {}
// fn void tmpnam() @test {}
// fn void tmpfile() @test {}
// fn void ungetc() @test {}
// fn void unsetenv() @test {}
// fn void write() @test {}
//
// extern fn CFile fmemopen(void* ptr, usz size, ZString mode);
// extern fn isz getline(char** linep, usz* linecapp, CFile stream);
// extern fn CInt timespec_get(TimeSpec* ts, CInt base);
// extern fn CInt nanosleep(TimeSpec* req, TimeSpec* remaining);
// extern fn ZString ctime(Time_t *timer);
// extern fn Time_t time(Time_t *timer);
//
// const CInt STDIN_FD = 0;
// const CInt STDOUT_FD = 1;
// const CInt STDERR_FD = 2;
//
// module libc @if(env::LINUX);
// extern CFile __stdin @cname("stdin");
// extern CFile __stdout @cname("stdout");
// extern CFile __stderr @cname("stderr");
// extern fn usz malloc_usable_size(void* ptr);
// macro usz malloc_size(void* ptr) => malloc_usable_size(ptr);
// extern fn void* aligned_alloc(usz align, usz size);
// macro CFile stdin() => __stdin;
// macro CFile stdout() => __stdout;
// macro CFile stderr() => __stderr;
//
// module libc @if(env::NETBSD || env::OPENBSD);
// extern fn int fcntl(CInt socket, int cmd, ...);
// extern fn int _setjmp(void*);
// macro int setjmp(void* ptr) => _setjmp(ptr);
// extern fn int _longjmp(void*, int);
// macro usz longjmp(void* ptr, CInt i) => _longjmp(ptr, i);
// extern fn usz malloc_size(void* ptr);
// extern fn void* aligned_alloc(usz align, usz size);
// macro CFile stdin() { return fdopen(0, "r"); }
// macro CFile stdout() { return fdopen(1, "w"); }
// macro CFile stderr() { return fdopen(2, "w"); }
//
// module libc @if(env::DARWIN || env::FREEBSD);
// extern CFile __stdinp;
// extern CFile __stdoutp;
// extern CFile __stderrp;
// extern fn usz malloc_size(void* ptr) @if(!env::FREEBSD);
// extern fn void* aligned_alloc(usz align, usz size);
// macro CFile stdin() => __stdinp;
// macro CFile stdout() => __stdoutp;
// macro CFile stderr() => __stderrp;
//
// module libc @if(env::FREEBSD);
// extern fn usz malloc_usable_size(void* ptr);
// macro usz malloc_size(void* ptr) => malloc_usable_size(ptr);
//
// module libc @if(env::WIN32);
// macro usz malloc_size(void* ptr) => _msize(ptr);
// macro CFile stdin() => __acrt_iob_func(STDIN_FD);
// macro CFile stdout() => __acrt_iob_func(STDOUT_FD);
// macro CFile stderr() => __acrt_iob_func(STDERR_FD);
//
// module libc @if(env::LIBC && !env::WIN32 && !env::LINUX && !env::DARWIN && !env::BSD_FAMILY);
// macro CFile stdin() { return (CFile*)(uptr)STDIN_FD; }
// macro CFile stdout() { return (CFile*)(uptr)STDOUT_FD; }
// macro CFile stderr() { return (CFile*)(uptr)STDERR_FD; }
//
// module libc @if(!env::LIBC);
//
// fn void longjmp(JmpBuf* buffer, CInt value) @weak @cname("longjmp") @nostrip
// {
// unreachable("longjmp unavailable");
// }
//
// fn CInt setjmp(JmpBuf* buffer) @weak @cname("setjmp") @nostrip
// {
// unreachable("setjmp unavailable");
// }
//
// fn void* malloc(usz size) @weak @cname("malloc") @nostrip
// {
// unreachable("malloc unavailable");
// }
// fn void* calloc(usz count, usz size) @weak @cname("calloc") @nostrip
// {
// unreachable("calloc unavailable");
// }
// fn void* free(void*) @weak @cname("free")
// {
// unreachable("free unavailable");
// }
//
// fn void* realloc(void* ptr, usz size) @weak @cname("realloc") @nostrip
// {
// unreachable("realloc unavailable");
// }
//
// fn void* memcpy(void* dest, void* src, usz n) @weak @cname("memcpy") @nostrip
// {
// for (usz i = 0; i < n; i++) ((char*)dest)[i] = ((char*)src)[i];
// return dest;
// }
//
// fn void* memmove(void* dest, void* src, usz n) @weak @cname("memmove") @nostrip
// {
// return memcpy(dest, src, n) @inline;
// }
//
// fn void* memset(void* dest, CInt value, usz n) @weak @cname("memset") @nostrip
// {
// for (usz i = 0; i < n; i++) ((char*)dest)[i] = (char)value;
// return dest;
// }
//
// fn int fseek(CFile stream, SeekIndex offset, int whence) @weak @cname("fseek") @nostrip
// {
// unreachable("'fseek' not available.");
// }
// fn CFile fopen(ZString filename, ZString mode) @weak @cname("fopen") @nostrip
// {
// unreachable("'fopen' not available.");
// }
//
// fn CFile freopen(ZString filename, ZString mode, CFile stream) @weak @cname("fopen") @nostrip
// {
// unreachable("'freopen' not available.");
// }
//
// fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream) @weak @cname("fwrite") @nostrip
// {
// unreachable("'fwrite' not available.");
// }
//
// fn usz fread(void* ptr, usz size, usz nmemb, CFile stream) @weak @cname("fread") @nostrip
// {
// unreachable("'fread' not available.");
// }
//
// fn CFile fclose(CFile) @weak @cname("fclose") @nostrip
// {
// unreachable("'fclose' not available.");
// }
//
// fn int fflush(CFile stream) @weak @cname("fflush") @nostrip
// {
// unreachable("'fflush' not available.");
// }
//
// fn int fputc(int c, CFile stream) @weak @cname("fputc") @nostrip
// {
// unreachable("'fputc' not available.");
// }
//
// fn char* fgets(ZString str, int n, CFile stream) @weak @cname("fgets") @nostrip
// {
// unreachable("'fgets' not available.");
// }
//
// fn int fgetc(CFile stream) @weak @cname("fgetc") @nostrip
// {
// unreachable("'fgetc' not available.");
// }
//
// fn int feof(CFile stream) @weak @cname("feof") @nostrip
// {
// unreachable("'feof' not available.");
// }
//
// fn int putc(int c, CFile stream) @weak @cname("putc") @nostrip
// {
// unreachable("'putc' not available.");
// }
// fn int putchar(int c) @weak @cname("putchar") @nostrip
// {
// unreachable("'putchar' not available.");
// }
// fn int puts(ZString str) @weak @cname("puts") @nostrip
// {
// unreachable("'puts' not available.");
// }
//
// module libc;