cheatah
Source

tests/purrc/socket_cr_test.cpp

1// Copyright (c) 2026 BigBrain LLC. MIT-licensed (see LICENSE).
2// Original work; see ACKNOWLEDGMENTS.md for the open-source ideas we build upon.
3// Compile-run unit tests for the `socket` module: one test per function. Each
4// writes a tiny .purr that calls a single socket function, compiles it with
5// purrc, runs it under the cheatah runtime, and asserts the exact stdout.
6// Complements the in-process unit tests (stdlib/tests/socket_test.cpp) and the
7// per-module system-level test (StdlibE2E.Socket).
8//
9// Real networking has no fixed output, so each program asserts DETERMINISTIC
10// PROPERTIES as booleans (io.print emits "True"/"False"): bind to 127.0.0.1
11// port 0 (OS-assigned) so nothing depends on a fixed port or external network,
12// connect to port 1 (nothing listens there) for the refusal paths, and use a
13// bad fd (-1) for the error paths. Functions that block waiting on a peer are
14// exercised only through their non-blocking bad-fd error path.
15#include "e2e_harness.hpp"
17// tcp_listen: binding a listener on 127.0.0.1:0 yields a valid (>= 0) fd.
18TEST(SocketCompileRun, TcpListen) {
19 e2e::expect_e2e("socket_tcp_listen", R"PURR(import io
20import socket
21let fd = socket.tcp_listen("127.0.0.1", 0, 1)
22io.print(fd >= 0)
23socket.close(fd)
24)PURR", "True\n");
27// local_port: after binding to port 0 the OS assigns a real (> 0) port.
28TEST(SocketCompileRun, LocalPort) {
29 e2e::expect_e2e("socket_local_port", R"PURR(import io
30import socket
31let fd = socket.tcp_listen("127.0.0.1", 0, 1)
32io.print(socket.local_port(fd) > 0)
33socket.close(fd)
34)PURR", "True\n");
37// tcp_connect: connecting to port 1 (nothing listening) is refused -> < 0.
38TEST(SocketCompileRun, TcpConnect) {
39 e2e::expect_e2e("socket_tcp_connect", R"PURR(import io
40import socket
41io.print(socket.tcp_connect("127.0.0.1", 1) < 0)
42)PURR", "True\n");
45// close: closing a live fd returns 0; closing a bad fd returns < 0.
46TEST(SocketCompileRun, Close) {
47 e2e::expect_e2e("socket_close", R"PURR(import io
48import socket
49let fd = socket.tcp_listen("127.0.0.1", 0, 1)
50io.print(socket.close(fd) == 0, socket.close(-1) < 0)
51)PURR", "True True\n");
54// socket: creating a raw IPv4 TCP socket yields a valid (>= 0) fd.
55TEST(SocketCompileRun, Socket) {
56 e2e::expect_e2e("socket_socket", R"PURR(import io
57import socket
58let fd = socket.socket()
59io.print(fd >= 0)
60socket.close(fd)
61)PURR", "True\n");
64// set_reuseaddr: enabling SO_REUSEADDR on a fresh socket succeeds (returns 0).
65TEST(SocketCompileRun, SetReuseaddr) {
66 e2e::expect_e2e("socket_set_reuseaddr", R"PURR(import io
67import socket
68let fd = socket.socket()
69io.print(socket.set_reuseaddr(fd) == 0)
70socket.close(fd)
71)PURR", "True\n");
74// bind: binding a socket to 127.0.0.1:0 succeeds (returns 0).
75TEST(SocketCompileRun, Bind) {
76 e2e::expect_e2e("socket_bind", R"PURR(import io
77import socket
78let fd = socket.socket()
79io.print(socket.bind(fd, "127.0.0.1", 0) == 0)
80socket.close(fd)
81)PURR", "True\n");
84// listen: marking a bound socket passive returns 0; listen on a bad fd < 0.
85TEST(SocketCompileRun, Listen) {
86 e2e::expect_e2e("socket_listen", R"PURR(import io
87import socket
88let fd = socket.socket()
89socket.bind(fd, "127.0.0.1", 0)
90io.print(socket.listen(fd, 1) == 0, socket.listen(-1, 1) < 0)
91socket.close(fd)
92)PURR", "True True\n");
95// connect: connecting to port 1 (nothing listening) is refused -> < 0.
96TEST(SocketCompileRun, Connect) {
97 e2e::expect_e2e("socket_connect", R"PURR(import io
98import socket
99let fd = socket.socket()
100io.print(socket.connect(fd, "127.0.0.1", 1) < 0)
101socket.close(fd)
102)PURR", "True\n");
105// last_error: after a failed call, last_error() reports a non-empty message.
106TEST(SocketCompileRun, LastError) {
107 e2e::expect_e2e("socket_last_error", R"PURR(import io
108import socket
109let r = socket.connect(-1, "127.0.0.1", 1)
110io.print(r < 0, socket.last_error() != "")
111)PURR", "True True\n");
114// send: a single send() on a bad fd returns < 0 (error path).
115TEST(SocketCompileRun, Send) {
116 e2e::expect_e2e("socket_send", R"PURR(import io
117import socket
118io.print(socket.send(-1, "x") < 0)
119)PURR", "True\n");
122// sendall: looping send on a bad fd returns < 0 (error path).
123TEST(SocketCompileRun, Sendall) {
124 e2e::expect_e2e("socket_sendall", R"PURR(import io
125import socket
126io.print(socket.sendall(-1, "x") < 0)
127)PURR", "True\n");
130// recv: a real recv() blocks until a peer sends, so it can't be made
131// deterministic standalone. Exercise only the non-blocking error paths: a bad
132// fd and a non-positive bufsize both return "" without touching the socket.
133TEST(SocketCompileRun, Recv) {
134 e2e::expect_e2e("socket_recv", R"PURR(import io
135import socket
136io.print(socket.recv(-1, 64) == "", socket.recv(5, 0) == "")
137)PURR", "True True\n");
140// accept: a real accept() blocks until a client connects, so it can't be made
141// deterministic standalone. Exercise only the non-blocking error path: accept
142// on a bad fd returns < 0 without blocking.
143TEST(SocketCompileRun, Accept) {
144 e2e::expect_e2e("socket_accept", R"PURR(import io
145import socket
146io.print(socket.accept(-1) < 0)
147)PURR", "True\n");