aboutsummaryrefslogtreecommitdiff
path: root/tests/core/sys/posix/structs.odin
blob: 66b7cb0e12582e212046f8046f13985ea87a1faa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#+build linux, darwin, freebsd, openbsd, netbsd, haiku
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 wpid == -1 && posix.errno() == .EINTR {
				continue
			}
			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[:])
		}
	}
}