AtlatestRepositorysigil-socket
sigil-socket / tree / testtest-socket.sgl
1
(import (sigil test)2
(sigil socket)3
(sigil fs))5
;; ============================================================6
;; TCP echo7
;; ============================================================9
(test-group "TCP communication"10
(test "tcp echo on localhost"11
(let ((server (tcp-listen 0)))12
(assert-true (socket? server))13
(let* ((addr (socket-local-address server))14
(port (cadr addr))15
(client (tcp-connect "127.0.0.1" port)))16
(assert-true (socket? client))17
(let ((peer (tcp-accept server)))18
(assert-true (socket? peer))19
(socket-write-line client "hello")20
(let ((received (socket-read-line peer)))21
(assert-equal "hello" received))22
(socket-close peer))23
(socket-close client))24
(socket-close server))))26
;; ============================================================27
;; UDP28
;; ============================================================30
(test-group "UDP communication"31
(test "udp send and receive on localhost"32
(let ((sock (udp-socket)))33
(assert-true (socket? sock))34
(udp-bind sock 0)35
(let* ((addr (socket-local-address sock))36
(port (cadr addr)))37
(udp-send sock "ping" "127.0.0.1" port)38
(let ((result (udp-receive sock)))39
(assert-true (pair? result))40
(assert-equal "ping" (car result))))41
(socket-close sock))))43
;; ============================================================44
;; Address info45
;; ============================================================47
(test-group "address info"48
(test "gethostname returns non-empty string"49
(let ((name (gethostname)))50
(assert-true (string? name))51
(assert-true (> (string-length name) 0))))53
(test "resolve-hostname localhost"54
(let ((result (resolve-hostname "localhost")))55
(assert-true (pair? result))56
(assert-true (string? (car result))))))58
;; ============================================================59
;; Socket state60
;; ============================================================62
(test-group "socket state"63
(test "socket-closed? is #f for open socket"64
(let ((server (tcp-listen 0)))65
(assert-false (socket-closed? server))66
(socket-close server)))68
(test "socket-closed? is #t after close"69
(let ((server (tcp-listen 0)))70
(socket-close server)71
(assert-true (socket-closed? server))))73
(test "socket-close on already-closed doesn't crash"74
(let ((server (tcp-listen 0)))75
(socket-close server)76
(socket-close server)77
(assert-true #t))))79
;; ============================================================80
;; Address introspection81
;; ============================================================83
(test-group "address introspection"84
(test "socket-local-address on bound TCP socket"85
(let ((server (tcp-listen 0)))86
(let ((addr (socket-local-address server)))87
(assert-true (pair? addr))88
(assert-true (string? (car addr)))89
(assert-true (number? (cadr addr)))90
(assert-true (> (cadr addr) 0)))91
(socket-close server)))93
(test "socket-remote-address on connected TCP socket"94
(let ((server (tcp-listen 0)))95
(let* ((port (cadr (socket-local-address server)))96
(client (tcp-connect "127.0.0.1" port)))97
(let ((addr (socket-remote-address client)))98
(assert-true (pair? addr))99
(assert-true (string? (car addr)))100
(assert-equal port (cadr addr)))101
(socket-close client))102
(socket-close server))))104
;; ============================================================105
;; Non-blocking I/O106
;; ============================================================108
(test-group "non-blocking I/O"109
(test "socket-set-non-blocking! and socket-non-blocking?"110
(let ((server (tcp-listen 0)))111
(assert-false (socket-non-blocking? server))112
(socket-set-non-blocking! server #t)113
(assert-true (socket-non-blocking? server))114
(socket-set-non-blocking! server #f)115
(assert-false (socket-non-blocking? server))116
(socket-close server)))118
(test "socket-ready? returns #f when no data"119
(let ((server (tcp-listen 0)))120
(let* ((port (cadr (socket-local-address server)))121
(client (tcp-connect "127.0.0.1" port))122
(peer (tcp-accept server)))123
;; No data written yet, peer should not be ready124
(assert-false (socket-ready? peer))125
(socket-close peer)126
(socket-close client))127
(socket-close server)))129
(test "socket-ready? returns #t after write"130
(let ((server (tcp-listen 0)))131
(let* ((port (cadr (socket-local-address server)))132
(client (tcp-connect "127.0.0.1" port))133
(peer (tcp-accept server)))134
(socket-write-line client "data")135
;; Give data time to arrive136
(assert-true (socket-ready? peer))137
(socket-close peer)138
(socket-close client))139
(socket-close server)))141
(test "socket-select returns ready sockets"142
(let ((server (tcp-listen 0)))143
(let* ((port (cadr (socket-local-address server)))144
(client (tcp-connect "127.0.0.1" port))145
(peer (tcp-accept server)))146
(socket-write-line client "hello")147
(let* ((result (socket-select (list peer) '() 1000))148
(readable (car result)))149
(assert-true (pair? readable))150
(assert-equal 1 (length readable)))151
(socket-close peer)152
(socket-close client))153
(socket-close server))))155
;; ============================================================156
;; TCP options157
;; ============================================================159
(test-group "TCP options"160
(test "socket-set-tcp-nodelay! doesn't crash"161
(let ((server (tcp-listen 0)))162
(let* ((port (cadr (socket-local-address server)))163
(client (tcp-connect "127.0.0.1" port)))164
(socket-set-tcp-nodelay! client #t)165
(assert-true #t)166
(socket-close client))167
(socket-close server))))169
;; ============================================================170
;; Higher-level utilities171
;; ============================================================173
(test-group "higher-level utilities"174
(test "call-with-tcp-server creates and closes server"175
(let ((port-num #f))176
(call-with-tcp-server 0177
(lambda (server)178
(set! port-num (cadr (socket-local-address server)))179
(assert-true (socket? server))180
(assert-true (> port-num 0))))181
;; Server should be closed after proc returns182
(assert-true (number? port-num))))184
(test "call-with-tcp-connection connects and closes"185
(let ((server (tcp-listen 0)))186
(let ((port (cadr (socket-local-address server))))187
(call-with-tcp-connection "127.0.0.1" port188
(lambda (sock)189
(assert-true (socket? sock))190
(let ((peer (tcp-accept server)))191
(socket-write-line peer "hello from server")192
(socket-close peer))193
(let ((line (socket-read-line sock)))194
(assert-equal "hello from server" line)))))195
(socket-close server)))197
(test "socket-send-all sends complete data"198
(let ((server (tcp-listen 0)))199
(let* ((port (cadr (socket-local-address server)))200
(client (tcp-connect "127.0.0.1" port))201
(peer (tcp-accept server)))202
(assert-true (socket-send-all client "hello world"))203
(socket-close client)204
(let ((received (socket-read-all peer)))205
(assert-equal "hello world" received))206
(socket-close peer))207
(socket-close server)))209
(test "socket-read-all reads until EOF"210
(let ((server (tcp-listen 0)))211
(let* ((port (cadr (socket-local-address server)))212
(client (tcp-connect "127.0.0.1" port))213
(peer (tcp-accept server)))214
(socket-write peer "part1")215
(socket-write peer "part2")216
(socket-close peer)217
(let ((all (socket-read-all client)))218
(assert-equal "part1part2" all))219
(socket-close client))220
(socket-close server)))222
(test "make-line-reader returns complete lines"223
(let ((server (tcp-listen 0)))224
(let* ((port (cadr (socket-local-address server)))225
(client (tcp-connect "127.0.0.1" port))226
(peer (tcp-accept server))227
(reader (make-line-reader peer)))228
(socket-write client "line1\nline2\n")229
(assert-equal "line1" (reader))230
(assert-equal "line2" (reader))231
(socket-close client)232
(socket-close peer))233
(socket-close server)))235
(test "make-line-reader handles CRLF"236
(let ((server (tcp-listen 0)))237
(let* ((port (cadr (socket-local-address server)))238
(client (tcp-connect "127.0.0.1" port))239
(peer (tcp-accept server))240
(reader (make-line-reader peer)))241
(socket-write client "hello\r\nworld\r\n")242
(assert-equal "hello" (reader))243
(assert-equal "world" (reader))244
(socket-close client)245
(socket-close peer))246
(socket-close server))))248
;; ============================================================249
;; Error handling250
;; ============================================================252
;; ============================================================253
;; socket-select stress test254
;; ============================================================256
(test-group "socket-select stress"257
(test "socket-select with many connections"258
;; Create 100 TCP connections, write to all, verify socket-select259
;; finds all readable.260
(let ((server (tcp-listen 0)))261
(let ((port (cadr (socket-local-address server))))262
(let loop ((i 0) (clients '()) (peers '()))263
(if (< i 100)264
(let* ((client (tcp-connect "127.0.0.1" port))265
(peer (tcp-accept server)))266
(socket-write client "x")267
(loop (+ i 1) (cons client clients) (cons peer peers)))268
;; All connections established and data written269
(let* ((result (socket-select peers '() 5000))270
(readable (car result)))271
(assert-equal (length peers) (length readable))272
;; Clean up273
(for-each socket-close clients)274
(for-each socket-close peers)))))275
(socket-close server))))277
;; ============================================================278
;; Unix domain sockets279
;; ============================================================281
(test-group "Unix domain sockets"282
(test "unix-listen creates a socket"283
(let ((server (unix-listen "/tmp/sigil-test-unix.sock")))284
(assert-true (socket? server))285
(socket-close server)286
(delete-file "/tmp/sigil-test-unix.sock")))288
(test "unix-connect to unix-listen socket"289
(let ((server (unix-listen "/tmp/sigil-test-unix2.sock")))290
(let ((client (unix-connect "/tmp/sigil-test-unix2.sock")))291
(assert-true (socket? client))292
(socket-close client))293
(socket-close server)294
(delete-file "/tmp/sigil-test-unix2.sock")))296
(test "unix echo via accept"297
(let ((server (unix-listen "/tmp/sigil-test-unix3.sock")))298
(let* ((client (unix-connect "/tmp/sigil-test-unix3.sock"))299
(peer (tcp-accept server)))300
(assert-true (socket? peer))301
(socket-write-line client "hello unix")302
(let ((received (socket-read-line peer)))303
(assert-equal "hello unix" received))304
(socket-write-line peer "reply")305
(let ((reply (socket-read-line client)))306
(assert-equal "reply" reply))307
(socket-close peer)308
(socket-close client))309
(socket-close server)310
(delete-file "/tmp/sigil-test-unix3.sock")))312
(test "unix-listen removes stale socket file"313
(let ((path "/tmp/sigil-test-unix4.sock"))314
;; Create a socket, close it (leaves stale file)315
(let ((s1 (unix-listen path)))316
(socket-close s1))317
;; Should succeed even though file exists318
(let ((s2 (unix-listen path)))319
(assert-true (socket? s2))320
(socket-close s2)321
(delete-file path))))323
(test "unix-connect to nonexistent path returns #f"324
(let ((result (unix-connect "/tmp/sigil-test-nonexistent.sock")))325
(assert-false result))))327
(test-group "error handling"328
(test "tcp-connect to closed port returns #f"329
;; Bind a server to get a port, close it, then try to connect330
(let ((server (tcp-listen 0)))331
(let ((port (cadr (socket-local-address server))))332
(socket-close server)333
(let ((result (tcp-connect "127.0.0.1" port)))334
(assert-false result))))))336
(run-tests)