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(13) == -1); // 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 stdin = libc::stdin(); assert(libc::fflush(stdin) == 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;