posix: add package

This commit is contained in:
Laytan Laats
2024-08-14 01:44:35 +02:00
parent ac68a9d52c
commit efe68c2e24
83 changed files with 12665 additions and 328 deletions
+1
View File
@@ -38,6 +38,7 @@ download_assets :: proc() {
@(require) import "slice"
@(require) import "strconv"
@(require) import "strings"
@(require) import "sys/posix"
@(require) import "sys/windows"
@(require) import "text/i18n"
@(require) import "text/match"
+211
View File
@@ -0,0 +1,211 @@
//+build darwin, freebsd, openbsd, netbsd
package tests_core_posix
import "core:sys/posix"
import "core:testing"
import "core:log"
import "core:strings"
import "core:path/filepath"
@(test)
test_arpa_inet :: proc(t: ^testing.T) {
check :: proc(t: ^testing.T, $af: posix.AF, src: cstring, expect: posix.pton_result, loc := #caller_location) {
when af == .INET {
addr: posix.in_addr
dst: [posix.INET_ADDRSTRLEN]byte
} else {
addr: posix.in6_addr
dst: [posix.INET6_ADDRSTRLEN]byte
}
res := posix.inet_pton(af, src, &addr, size_of(addr))
testing.expect_value(t, res, expect, loc)
if expect == .SUCCESS {
back := posix.inet_ntop(af, &addr, raw_data(dst[:]), len(dst))
testing.expect_value(t, back, src, loc)
when af == .INET {
back = posix.inet_ntoa(addr)
testing.expect_value(t, back, src, loc)
}
}
}
check(t, .INET, "127.0.0.1", .SUCCESS)
check(t, .INET, "blah", .INVALID)
check(t, .INET6, "::1", .SUCCESS)
check(t, .INET6, "L", .INVALID)
check(t, .UNIX, "127.0.0.1", .AFNOSUPPORT)
}
@(test)
test_dirent :: proc(t: ^testing.T) {
test := #load_directory(#directory)
test_map: map[string]struct{}
defer delete(test_map)
test_map[".."] = {}
test_map["."] = {}
for file in test {
test_map[filepath.base(file.name)] = {}
}
{
list: [^]^posix.dirent
ret := posix.scandir(#directory, &list)
testing.expectf(t, ret >= 0, "%v >= 0: %v", ret, posix.strerror(posix.errno()))
defer posix.free(list)
entries := list[:ret]
for entry in entries {
defer posix.free(entry)
if entry.d_type != .REG {
continue
}
name := string(cstring(raw_data(entry.d_name[:])))
testing.expectf(t, name in test_map, "%v in %v", name, test_map)
}
}
{
dir := posix.opendir(#directory)
defer posix.closedir(dir)
for {
posix.set_errno(.NONE)
entry := posix.readdir(dir)
if entry == nil {
testing.expect_value(t, posix.errno(), posix.Errno.NONE)
break
}
if entry.d_type != .REG {
continue
}
name := string(cstring(raw_data(entry.d_name[:])))
testing.expectf(t, name in test_map, "%v in %v", name, test_map)
}
}
}
@(test)
test_errno :: proc(t: ^testing.T) {
posix.errno(posix.Errno.ENOMEM)
testing.expect_value(t, posix.errno(), posix.Errno.ENOMEM)
res := posix.open("", {})
testing.expect_value(t, res, -1)
testing.expect_value(t, posix.errno(), posix.Errno.ENOENT)
}
@(test)
test_fcntl :: proc(t: ^testing.T) {
res := posix.open(#file, { .WRONLY, .CREAT, .EXCL })
testing.expect_value(t, res, -1)
testing.expect_value(t, posix.errno(), posix.Errno.EEXIST)
}
@(test)
test_fnmatch :: proc(t: ^testing.T) {
testing.expect_value(t, posix.fnmatch("*.odin", #file, {}), 0)
testing.expect_value(t, posix.fnmatch("*.txt", #file, {}), posix.FNM_NOMATCH)
testing.expect_value(t, posix.fnmatch("**/*.odin", #file, {}), 0)
}
@(test)
test_glob :: proc(t: ^testing.T) {
glob: posix.glob_t
res := posix.glob(#directory + ":)))))))", {}, nil, &glob)
testing.expect_value(t, res, posix.Glob_Result.NOMATCH)
posix.globfree(&glob)
}
@(test)
test_langinfo :: proc(t: ^testing.T) {
locale := posix.setlocale(.TIME, nil)
testing.expectf(t, locale == "POSIX" || locale == "C", "invalid locale for test: %v", locale)
day1 := posix.nl_langinfo(.DAY_1)
testing.expect_value(t, day1, "Sunday")
}
@(test)
test_libgen :: proc(t: ^testing.T) {
tests := [][3]cstring{
{ "usr", ".", "usr" },
{ "usr/", ".", "usr" },
{ "", ".", "." },
{ "/", "/", "/" },
{ "//", "/", "/" },
{ "///", "/", "/" },
{ "/usr/", "/", "usr" },
{ "/usr/lib", "/usr", "lib" },
{ "//usr//lib//", "//usr", "lib" },
{ "/home//dwc//test", "/home//dwc", "test" },
}
for test in tests {
// NOTE: dir/basename can change their input so they can't be literals.
dinput := strings.clone_to_cstring(string(test[0]))
defer delete(dinput)
dir := posix.dirname(dinput)
testing.expectf(t, dir == test[1], "dirname(%q) == %q, expected %q", test[0], dir, test[1])
binput := strings.clone_to_cstring(string(test[0]))
defer delete(binput)
base := posix.basename(binput)
testing.expectf(t, base == test[2], "basename(%q) == %q, expected %q", test[0], base, test[2])
}
}
@(test)
test_locale :: proc(t: ^testing.T) {
lconv := posix.localeconv()
testing.expect(t, lconv != nil)
locale := posix.setlocale(.ALL, nil)
testing.expectf(t, locale == "POSIX" || locale == "C", "%q is not POSIX or C", locale)
}
@(test)
test_monetary :: proc(t: ^testing.T) {
when ODIN_OS == .Darwin && .Address in ODIN_SANITIZER_FLAGS {
log.warn("skipping on darwin with -sanitize:address, this fails inside macOS (also from C/clang)")
return
}
value := 123456.789
buf: [128]byte
size := posix.strfmon(raw_data(buf[:]), len(buf), "%n", value)
testing.expectf(t, int(size) != -1, "strfmon failure: %v", posix.strerror(posix.errno()))
log.debug(string(buf[:size]))
}
@(test)
test_stat :: proc(t: ^testing.T) {
testing.expect_value(t, posix.S_IRWXU, transmute(posix.mode_t)posix._mode_t(posix._S_IRWXU))
testing.expect_value(t, posix.S_IRWXG, transmute(posix.mode_t)posix._mode_t(posix._S_IRWXG))
testing.expect_value(t, posix.S_IRWXO, transmute(posix.mode_t)posix._mode_t(posix._S_IRWXO))
testing.expect_value(t, posix._S_IFMT, transmute(posix.mode_t)posix._mode_t(posix.__S_IFMT))
}
@(test)
test_termios :: proc(t: ^testing.T) {
testing.expect_value(t, transmute(posix.CControl_Flags)posix.tcflag_t(posix._CSIZE), posix.CSIZE)
testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._NLDLY), posix.NLDLY)
testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._CRDLY), posix.CRDLY)
testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._TABDLY), posix.TABDLY)
testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._BSDLY), posix.BSDLY)
testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._VTDLY), posix.VTDLY)
testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._FFDLY), posix.FFDLY)
}
+127
View File
@@ -0,0 +1,127 @@
//+build darwin, freebsd, openbsd, netbsd
package tests_core_posix
import "core:log"
import "core:testing"
import "core:sys/posix"
// This test tests some of the process APIs of posix while also double checking size and alignment
// of the structs we bound.
@(test)
execute_struct_checks :: proc(t: ^testing.T) {
log.debug("compiling C project")
{
switch pid := posix.fork(); pid {
case -1:
log.errorf("fork() failed: %s", posix.strerror())
case 0:
c_compiler := posix.getenv("CC")
if c_compiler == nil {
c_compiler = "clang"
}
posix.execlp(c_compiler,
c_compiler, #directory + "/structs/structs.c", "-o", #directory + "/structs/c_structs", nil)
posix.exit(69)
case:
if !wait_for(t, pid) { return }
log.debug("C code has been compiled!")
}
}
log.debug("compiling Odin project")
{
switch pid := posix.fork(); pid {
case -1:
log.errorf("fork() failed: %s", posix.strerror())
case 0:
posix.execlp(ODIN_ROOT + "/odin",
ODIN_ROOT + "/odin", "build", #directory + "/structs/structs.odin", "-out:" + #directory + "/structs/odin_structs", "-file", nil)
posix.exit(69)
case:
if !wait_for(t, pid) { return }
log.debug("Odin code has been compiled!")
}
}
c_buf: [dynamic]byte
defer delete(c_buf)
c_out := get_output(t, &c_buf, #directory + "/structs/c_structs", nil)
odin_buf: [dynamic]byte
defer delete(odin_buf)
odin_out := get_output(t, &odin_buf, #directory + "/structs/odin_structs", nil)
testing.expectf(t, c_out == odin_out, "The C output and Odin output differ!\nC output:\n%s\n\n\n\nOdin Output:\n%s", c_out, odin_out)
/* ----------- HELPERS ----------- */
wait_for :: proc(t: ^testing.T, pid: posix.pid_t) -> (ok: bool) {
log.debugf("waiting on pid %v", pid)
waiting: for {
status: i32
wpid := posix.waitpid(pid, &status, {})
if !testing.expectf(t, wpid != -1, "waitpid() failure: %v", posix.strerror()) {
return false
}
switch {
case posix.WIFEXITED(status):
ok = testing.expect_value(t, posix.WEXITSTATUS(status), 0)
break waiting
case posix.WIFSIGNALED(status):
log.errorf("child process raised: %v", posix.strsignal(posix.WTERMSIG(status)))
ok = false
break waiting
case:
log.errorf("unexpected status (this should never happen): %v", status)
ok = false
break waiting
}
}
return
}
get_output :: proc(t: ^testing.T, output: ^[dynamic]byte, cmd: ..cstring) -> (out_str: string) {
log.debugf("capturing output of: %v", cmd)
pipe: [2]posix.FD
if !testing.expect_value(t, posix.pipe(&pipe), posix.result.OK) {
return
}
switch pid := posix.fork(); pid {
case -1:
log.errorf("fork() failed: %s", posix.strerror())
return
case 0:
posix.close(pipe[0])
posix.dup2(pipe[1], 1)
posix.execv(cmd[0], raw_data(cmd[:]))
panic(string(posix.strerror()))
case:
posix.close(pipe[1])
log.debugf("waiting on pid %v", pid)
reader: for {
buf: [256]byte
switch read := posix.read(pipe[0], &buf[0], 256); {
case read < 0:
log.errorf("read output failed: %v", posix.strerror())
return
case read == 0:
break reader
case:
append(output, ..buf[:read])
}
}
wait_for(t, pid)
return string(output[:])
}
}
}
+2
View File
@@ -0,0 +1,2 @@
c_structs
odin_structs
+103
View File
@@ -0,0 +1,103 @@
#include <dirent.h>
#include <fcntl.h>
#include <glob.h>
#include <grp.h>
#include <locale.h>
#include <pthread.h>
#include <stdio.h>
#include <termios.h>
#include <netinet/in.h>
#include <netdb.h>
#include <poll.h>
#include <pwd.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/un.h>
#include <stddef.h>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <sys/times.h>
#include <signal.h>
#include <sys/select.h>
#include <sys/uio.h>
#include <sys/sem.h>
#include <sys/statvfs.h>
#include <sys/time.h>
#include <utime.h>
#include <wordexp.h>
#include <sys/socket.h>
int main(int argc, char *argv[])
{
printf("dirent %zu %zu\n", sizeof(struct dirent), _Alignof(struct dirent));
printf("flock %zu %zu\n", sizeof(struct flock), _Alignof(struct flock));
printf("glob_t %zu %zu\n", sizeof(glob_t), _Alignof(glob_t));
printf("group %zu %zu\n", sizeof(struct group), _Alignof(struct group));
printf("lconv %zu %zu\n", sizeof(struct lconv), _Alignof(struct lconv));
printf("pthread_t %zu %zu\n", sizeof(pthread_t), _Alignof(pthread_t));
printf("pthread_attr_t %zu %zu\n", sizeof(pthread_attr_t), _Alignof(pthread_attr_t));
printf("pthread_key_t %zu %zu\n", sizeof(pthread_key_t), _Alignof(pthread_key_t));
printf("sched_param %zu %zu\n", sizeof(struct sched_param), _Alignof(struct sched_param));
printf("termios %zu %zu\n", sizeof(struct termios), _Alignof(struct termios));
printf("in_addr %zu %zu\n", sizeof(struct in_addr), _Alignof(struct in_addr));
printf("in6_addr %zu %zu\n", sizeof(struct in6_addr), _Alignof(struct in6_addr));
printf("sockaddr_in %zu %zu\n", sizeof(struct sockaddr_in), _Alignof(struct sockaddr_in));
printf("sockaddr_in6 %zu %zu\n", sizeof(struct sockaddr_in6), _Alignof(struct sockaddr_in6));
printf("ipv6_mreq %zu %zu\n", sizeof(struct ipv6_mreq), _Alignof(struct ipv6_mreq));
printf("sockaddr_storage %zu %zu\n", sizeof(struct sockaddr_storage), _Alignof(struct sockaddr_storage));
printf("msghdr %zu %zu\n", sizeof(struct msghdr), _Alignof(struct msghdr));
printf("cmsghdr %zu %zu\n", sizeof(struct cmsghdr), _Alignof(struct cmsghdr));
printf("linger %zu %zu\n", sizeof(struct linger), _Alignof(struct linger));
printf("hostent %zu %zu\n", sizeof(struct hostent), _Alignof(struct hostent));
printf("netent %zu %zu\n", sizeof(struct netent), _Alignof(struct netent));
printf("protoent %zu %zu\n", sizeof(struct protoent), _Alignof(struct protoent));
printf("servent %zu %zu\n", sizeof(struct servent), _Alignof(struct servent));
printf("addrinfo %zu %zu\n", sizeof(struct addrinfo), _Alignof(struct addrinfo));
printf("pollfd %zu %zu\n", sizeof(struct pollfd), _Alignof(struct pollfd));
printf("passwd %zu %zu\n", sizeof(struct passwd), _Alignof(struct passwd));
printf("shmid_ds %zu %zu\n", sizeof(struct shmid_ds), _Alignof(struct shmid_ds));
printf("ipc_perm %zu %zu\n", sizeof(struct ipc_perm), _Alignof(struct ipc_perm));
printf("msqid_ds %zu %zu\n", sizeof(struct msqid_ds), _Alignof(struct msqid_ds));
printf("rlimit %zu %zu\n", sizeof(struct rlimit), _Alignof(struct rlimit));
printf("rusage %zu %zu\n", sizeof(struct rusage), _Alignof(struct rusage));
printf("sockaddr_un %zu %zu\n", sizeof(struct sockaddr_un), _Alignof(struct sockaddr_un));
printf("utsname %zu %zu\n", sizeof(struct utsname), _Alignof(struct utsname));
printf("tms %zu %zu\n", sizeof(struct tms), _Alignof(struct tms));
printf("sigaction %zu %zu\n", sizeof(struct sigaction), _Alignof(struct sigaction));
printf("stack_t %zu %zu\n", sizeof(stack_t), _Alignof(stack_t));
printf("siginfo_t %zu %zu\n", sizeof(siginfo_t), _Alignof(siginfo_t));
printf("fd_set %zu %zu\n", sizeof(fd_set), _Alignof(fd_set));
printf("iovec %zu %zu\n", sizeof(struct iovec), _Alignof(struct iovec));
printf("semid_ds %zu %zu\n", sizeof(struct semid_ds), _Alignof(struct semid_ds));
printf("sembuf %zu %zu\n", sizeof(struct sembuf), _Alignof(struct sembuf));
printf("itimerval %zu %zu\n", sizeof(struct itimerval), _Alignof(struct itimerval));
printf("utimbuf %zu %zu\n", sizeof(struct utimbuf), _Alignof(struct utimbuf));
printf("wordexp_t %zu %zu\n", sizeof(wordexp_t), _Alignof(wordexp_t));
printf("time_t %zu %zu\n", sizeof(time_t), _Alignof(time_t));
printf("timespec %zu %zu\n", sizeof(struct timespec), _Alignof(struct timespec));
printf("clock_t %zu %zu\n", sizeof(clock_t), _Alignof(clock_t));
return 0;
}
+74
View File
@@ -0,0 +1,74 @@
package main
import "core:fmt"
import "core:sys/posix"
main :: proc() {
fmt.println("dirent", size_of(posix.dirent), align_of(posix.dirent))
fmt.println("flock", size_of(posix.flock), align_of(posix.flock))
fmt.println("glob_t", size_of(posix.glob_t), align_of(posix.glob_t))
fmt.println("group", size_of(posix.group), align_of(posix.group))
fmt.println("lconv", size_of(posix.lconv), align_of(posix.lconv))
fmt.println("pthread_t", size_of(posix.pthread_t), align_of(posix.pthread_t))
fmt.println("pthread_attr_t", size_of(posix.pthread_attr_t), align_of(posix.pthread_attr_t))
fmt.println("pthread_key_t", size_of(posix.pthread_key_t), align_of(posix.pthread_key_t))
fmt.println("sched_param", size_of(posix.sched_param), align_of(posix.sched_param))
fmt.println("termios", size_of(posix.termios), align_of(posix.termios))
fmt.println("in_addr", size_of(posix.in_addr), align_of(posix.in_addr))
fmt.println("in6_addr", size_of(posix.in6_addr), align_of(posix.in6_addr))
fmt.println("sockaddr_in", size_of(posix.sockaddr_in), align_of(posix.sockaddr_in))
fmt.println("sockaddr_in6", size_of(posix.sockaddr_in6), align_of(posix.sockaddr_in6))
fmt.println("ipv6_mreq", size_of(posix.ipv6_mreq), align_of(posix.ipv6_mreq))
fmt.println("sockaddr_storage", size_of(posix.sockaddr_storage), align_of(posix.sockaddr_storage))
fmt.println("msghdr", size_of(posix.msghdr), align_of(posix.msghdr))
fmt.println("cmsghdr", size_of(posix.cmsghdr), align_of(posix.cmsghdr))
fmt.println("linger", size_of(posix.linger), align_of(posix.linger))
fmt.println("hostent", size_of(posix.hostent), align_of(posix.hostent))
fmt.println("netent", size_of(posix.netent), align_of(posix.netent))
fmt.println("protoent", size_of(posix.protoent), align_of(posix.protoent))
fmt.println("servent", size_of(posix.servent), align_of(posix.servent))
fmt.println("addrinfo", size_of(posix.addrinfo), align_of(posix.addrinfo))
fmt.println("pollfd", size_of(posix.pollfd), align_of(posix.pollfd))
fmt.println("passwd", size_of(posix.passwd), align_of(posix.passwd))
fmt.println("shmid_ds", size_of(posix.shmid_ds), align_of(posix.shmid_ds))
fmt.println("ipc_perm", size_of(posix.ipc_perm), align_of(posix.ipc_perm))
fmt.println("msqid_ds", size_of(posix.msqid_ds), align_of(posix.msqid_ds))
fmt.println("rlimit", size_of(posix.rlimit), align_of(posix.rlimit))
fmt.println("rusage", size_of(posix.rusage), align_of(posix.rusage))
fmt.println("sockaddr_un", size_of(posix.sockaddr_un), align_of(posix.sockaddr_un))
fmt.println("utsname", size_of(posix.utsname), align_of(posix.utsname))
fmt.println("tms", size_of(posix.tms), align_of(posix.tms))
fmt.println("sigaction", size_of(posix.sigaction_t), align_of(posix.sigaction_t))
fmt.println("stack_t", size_of(posix.stack_t), align_of(posix.stack_t))
fmt.println("siginfo_t", size_of(posix.siginfo_t), align_of(posix.siginfo_t))
fmt.println("fd_set", size_of(posix.fd_set), align_of(posix.fd_set))
fmt.println("iovec", size_of(posix.iovec), align_of(posix.iovec))
fmt.println("semid_ds", size_of(posix.semid_ds), align_of(posix.semid_ds))
fmt.println("sembuf", size_of(posix.sembuf), align_of(posix.sembuf))
fmt.println("itimerval", size_of(posix.itimerval), align_of(posix.itimerval))
fmt.println("utimbuf", size_of(posix.utimbuf), align_of(posix.utimbuf))
fmt.println("wordexp_t", size_of(posix.wordexp_t), align_of(posix.wordexp_t))
fmt.println("time_t", size_of(posix.time_t), align_of(posix.time_t))
fmt.println("timespec", size_of(posix.timespec), align_of(posix.timespec))
fmt.println("clock_t", size_of(posix.clock_t), align_of(posix.clock_t))
}