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. Each4
// writes a tiny .purr that calls a single socket function, compiles it with5
// 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 the7
// per-module system-level test (StdlibE2E.Socket).8
//9
// Real networking has no fixed output, so each program asserts DETERMINISTIC10
// PROPERTIES as booleans (io.print emits "True"/"False"): bind to 127.0.0.111
// 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 a13
// bad fd (-1) for the error paths. Functions that block waiting on a peer are14
// 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.18
TEST(SocketCompileRun, TcpListen) {19
e2e::expect_e2e("socket_tcp_listen", R"PURR(import io20
import socket21
let fd = socket.tcp_listen("127.0.0.1", 0, 1)22
io.print(fd >= 0)23
socket.close(fd)24
)PURR", "True\n");25
}27
// local_port: after binding to port 0 the OS assigns a real (> 0) port.28
TEST(SocketCompileRun, LocalPort) {29
e2e::expect_e2e("socket_local_port", R"PURR(import io30
import socket31
let fd = socket.tcp_listen("127.0.0.1", 0, 1)32
io.print(socket.local_port(fd) > 0)33
socket.close(fd)34
)PURR", "True\n");35
}37
// tcp_connect: connecting to port 1 (nothing listening) is refused -> < 0.38
TEST(SocketCompileRun, TcpConnect) {39
e2e::expect_e2e("socket_tcp_connect", R"PURR(import io40
import socket41
io.print(socket.tcp_connect("127.0.0.1", 1) < 0)42
)PURR", "True\n");43
}45
// close: closing a live fd returns 0; closing a bad fd returns < 0.46
TEST(SocketCompileRun, Close) {47
e2e::expect_e2e("socket_close", R"PURR(import io48
import socket49
let fd = socket.tcp_listen("127.0.0.1", 0, 1)50
io.print(socket.close(fd) == 0, socket.close(-1) < 0)51
)PURR", "True True\n");52
}54
// socket: creating a raw IPv4 TCP socket yields a valid (>= 0) fd.55
TEST(SocketCompileRun, Socket) {56
e2e::expect_e2e("socket_socket", R"PURR(import io57
import socket58
let fd = socket.socket()59
io.print(fd >= 0)60
socket.close(fd)61
)PURR", "True\n");62
}64
// set_reuseaddr: enabling SO_REUSEADDR on a fresh socket succeeds (returns 0).65
// udp_socket / sendto / recvfrom: a datagram round trip from purr, the sender's address reported.66
TEST(SocketCompileRun, UdpLoopback) {67
e2e::expect_e2e("socket_udp_loopback", R"PURR(import io68
import socket69
let rx = socket.udp_socket()70
socket.bind(rx, "127.0.0.1", 0)71
socket.set_timeout(rx, 500)72
let port = socket.local_port(rx)73
let tx = socket.udp_socket()74
let sent = socket.sendto(tx, "127.0.0.1", port, "pose 7")75
let host = ""76
let from_port = 077
let got = socket.recvfrom(rx, 64, host, from_port)78
io.print(sent == 6 and got == "pose 7" and host == "127.0.0.1" and from_port > 0)79
socket.close(tx)80
socket.close(rx)81
)PURR", "True\n");82
}84
TEST(SocketCompileRun, SetReuseaddr) {85
e2e::expect_e2e("socket_set_reuseaddr", R"PURR(import io86
import socket87
let fd = socket.socket()88
io.print(socket.set_reuseaddr(fd) == 0)89
socket.close(fd)90
)PURR", "True\n");91
}93
// bind: binding a socket to 127.0.0.1:0 succeeds (returns 0).94
TEST(SocketCompileRun, Bind) {95
e2e::expect_e2e("socket_bind", R"PURR(import io96
import socket97
let fd = socket.socket()98
io.print(socket.bind(fd, "127.0.0.1", 0) == 0)99
socket.close(fd)100
)PURR", "True\n");101
}103
// listen: marking a bound socket passive returns 0; listen on a bad fd < 0.104
TEST(SocketCompileRun, Listen) {105
e2e::expect_e2e("socket_listen", R"PURR(import io106
import socket107
let fd = socket.socket()108
socket.bind(fd, "127.0.0.1", 0)109
io.print(socket.listen(fd, 1) == 0, socket.listen(-1, 1) < 0)110
socket.close(fd)111
)PURR", "True True\n");112
}114
// connect: connecting to port 1 (nothing listening) is refused -> < 0.115
TEST(SocketCompileRun, Connect) {116
e2e::expect_e2e("socket_connect", R"PURR(import io117
import socket118
let fd = socket.socket()119
io.print(socket.connect(fd, "127.0.0.1", 1) < 0)120
socket.close(fd)121
)PURR", "True\n");122
}124
// last_error: after a failed call, last_error() reports a non-empty message.125
TEST(SocketCompileRun, LastError) {126
e2e::expect_e2e("socket_last_error", R"PURR(import io127
import socket128
let r = socket.connect(-1, "127.0.0.1", 1)129
io.print(r < 0, socket.last_error() != "")130
)PURR", "True True\n");131
}133
// send: a single send() on a bad fd returns < 0 (error path).134
TEST(SocketCompileRun, Send) {135
e2e::expect_e2e("socket_send", R"PURR(import io136
import socket137
io.print(socket.send(-1, "x") < 0)138
)PURR", "True\n");139
}141
// sendall: looping send on a bad fd returns < 0 (error path).142
TEST(SocketCompileRun, Sendall) {143
e2e::expect_e2e("socket_sendall", R"PURR(import io144
import socket145
io.print(socket.sendall(-1, "x") < 0)146
)PURR", "True\n");147
}149
// recv: a real recv() blocks until a peer sends, so it can't be made150
// deterministic standalone. Exercise only the non-blocking error paths: a bad151
// fd and a non-positive bufsize both return "" without touching the socket.152
TEST(SocketCompileRun, Recv) {153
e2e::expect_e2e("socket_recv", R"PURR(import io154
import socket155
io.print(socket.recv(-1, 64) == "", socket.recv(5, 0) == "")156
)PURR", "True True\n");157
}159
// accept: a real accept() blocks until a client connects, so it can't be made160
// deterministic standalone. Exercise only the non-blocking error path: accept161
// on a bad fd returns < 0 without blocking.162
TEST(SocketCompileRun, Accept) {163
e2e::expect_e2e("socket_accept", R"PURR(import io164
import socket165
io.print(socket.accept(-1) < 0)166
)PURR", "True\n");167
}