# net_test.ludic - the codec, the codes, a host taking a stranger's hello, reliable delivery in # order, a guest's hello and its timeout, a silent guest and a slot let go - over a fake socket import "ludic.net" import "ludic.base" program NetTest { numbers float property Pkt { ip: int = 0, port: int = 0, len: int = 0, data: []byte } state NetTestState { sent: []Pkt = null inbound: []Pkt = null from_ip: int = 0 from_port: int = 0 quiet: bool = false # sends only counted, not kept: a test of what the package holds sends: int = 0 } function fake_open(port: int) -> int { return 5 } function fake_close(h: int) -> void { } function fake_port(h: int) -> int { return 40000 } function fake_local() -> int { return Udp.ip("10.0.0.5") } function fake_from_ip(net_test_st: NetTestState, h: int) -> int { return net_test_st.from_ip } function fake_from_port(net_test_st: NetTestState, h: int) -> int { return net_test_st.from_port } function fake_send(net_test_st: mut NetTestState, h: int, ip: int, port: int, b: []byte, n: int) -> int { net_test_st.sends += 1 if net_test_st.quiet { return n } let p = new Pkt p.ip = ip p.port = port p.len = n p.data = buffer(n) nb_copy(p.data, 0, b, 0, n) push(net_test_st.sent, p) return n } function fake_recv(net_test_st: mut NetTestState, h: int, b: []byte, cap: int) -> int { if len(net_test_st.inbound) == 0 { return 0 } let p = net_test_st.inbound[0] let rest = new []Pkt for i in 1 .. len(net_test_st.inbound) { push(rest, net_test_st.inbound[i]) } net_test_st.inbound = rest nb_copy(b, 0, p.data, 0, p.len) net_test_st.from_ip = p.ip net_test_st.from_port = p.port return p.len } bind NetSocket { open: fn fake_open, close: fn fake_close, send: fn fake_send, recv: fn fake_recv, from_ip: fn fake_from_ip, from_port: fn fake_from_port, port: fn fake_port, local_ip: fn fake_local } function fresh(net_st: mut NetState, net_test_st: mut NetTestState) -> void { net_test_st.sent = new []Pkt net_test_st.inbound = new []Pkt net_reset(net_st) q_clear(net_inbox(net_st)) q_clear(net_facts(net_st)) } # a packet from ip:port: token, pid, ack, then one message of `kind` (reliable with `id` > 0) function arrive(net_test_st: mut NetTestState, ip: int, port: int, token: int, pid: int, ack: int, kind: int, id: int, payload: int) -> void { let b = buffer(40) nb_put8(b, 0, 'M') nb_put8(b, 1, 'L') nb_put8(b, 2, 1) nb_put8(b, 3, 1) nb_put32(b, 4, token) nb_put32(b, 8, pid) nb_put32(b, 12, ack) nb_put32(b, 16, 1) var o = 20 nb_put8(b, o, kind) nb_put8(b, o + 1, 0) o += 2 if id > 0 { nb_put8(b, o - 1, 1) nb_put32(b, o, id) o += 4 } nb_put8(b, o, 4) nb_put8(b, o + 1, 0) nb_put32(b, o + 2, payload) let p = new Pkt p.ip = ip p.port = port p.len = o + 6 p.data = b push(net_test_st.inbound, p) } function frame(net_st: mut NetState, dt: float) -> void { net_begin(net_st, dt) net_update(net_st, dt) net_end(net_st) } # the host with a guest let in at 10.0.0.9:5000 as pid 2 function host_with_guest(net_st: mut NetState, net_test_st: mut NetTestState) -> int { fresh(net_st, net_test_st) expect(net_host(net_st, 27415, 77)) arrive(net_test_st, Udp.ip("10.0.0.9"), 5000, 0, 0, 0, NET_HELLO, 1, 42) net_begin(net_st, 0.01) let got = q_drain(net_inbox(net_st)) expect_eq(len(got), 1) return net_accept(net_st, got[0].slot) } function facts_of(net_st: NetState, what: int) -> []NetFact { let all = q_drain(net_facts(net_st)) let out = new []NetFact for i in 0 .. len(all) { if all[i].what == what { push(out, all[i]) } } return out } test "the codec carries ints, bools, floats and text, and a short message reads bad" (net_st: mut NetState, net_test_st: mut NetTestState) { fresh(net_st, net_test_st) nw_begin(net_st) nw_i(net_st, -7) nw_b(net_st, true) nw_f(net_st, 2.5) nw_s(net_st, "maroon") net_read(net_st, net_written(net_st)) expect_eq(nr_i(net_st), -7) expect(nr_b(net_st)) expect_near(nr_f(net_st), 2.5, 0.0001) expect(nr_s(net_st) == "maroon") expect(not nr_bad(net_st)) expect_eq(nr_i(net_st), 0) expect(nr_bad(net_st)) } test "a join code decodes to its address and port; an address and a room code are taken too" (net_st: mut NetState, net_test_st: mut NetTestState) { fresh(net_st, net_test_st) let ip = Udp.ip("192.168.1.20") let code = net_code_of(ip, 27415) expect_eq(len(code), 11) expect(net_parse_code(net_st, code)) expect_eq(net_code_ip(net_st), ip) expect_eq(net_code_port(net_st), 27415) expect(net_parse_code(net_st, Text.lower(code))) expect(net_parse_code(net_st, "10.1.2.3:4000")) expect_eq(net_code_port(net_st), 4000) expect(net_parse_code(net_st, "10.1.2.3")) expect_eq(net_code_port(net_st), 27415) expect(not net_parse_code(net_st, "HELLO")) expect(net_is_room("abcdef")) expect(not net_is_room("ABCDE1")) } test "a host takes a stranger only with a hello, and hands out pids from 2" (net_st: mut NetState, net_test_st: mut NetTestState) { let pid = host_with_guest(net_st, net_test_st) expect_eq(pid, 2) expect(net_hosting(net_st)) expect_eq(net_my_pid(net_st), 1) arrive(net_test_st, Udp.ip("10.0.0.10"), 6000, 0, 0, 0, 9, 1, 1) net_begin(net_st, 0.01) expect_eq(q_len(net_inbox(net_st)), 0) arrive(net_test_st, Udp.ip("10.0.0.11"), 6000, 0, 0, 0, NET_HELLO, 1, 1) net_begin(net_st, 0.01) let got = q_drain(net_inbox(net_st)) expect_eq(net_accept(net_st, got[0].slot), 3) } test "a message's payload reads back as it was written" (net_st: mut NetState, net_test_st: mut NetTestState) { host_with_guest(net_st, net_test_st) arrive(net_test_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 2, 1234) net_begin(net_st, 0.01) let got = q_drain(net_inbox(net_st)) expect_eq(len(got), 1) expect_eq(got[0].kind, 9) expect_eq(got[0].from, 2) net_read(net_st, got[0]) expect_eq(nr_i(net_st), 1234) expect(not nr_bad(net_st)) nr_i(net_st) expect(nr_bad(net_st)) } test "a reliable message that arrives early is dropped and taken in order" (net_st: mut NetState, net_test_st: mut NetTestState) { let slot = np_of_pid(net_st, host_with_guest(net_st, net_test_st)) arrive(net_test_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 3, 30) net_begin(net_st, 0.01) expect_eq(q_len(net_inbox(net_st)), 0) arrive(net_test_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 2, 20) arrive(net_test_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 3, 30) net_begin(net_st, 0.01) let got = q_drain(net_inbox(net_st)) expect_eq(len(got), 2) net_read(net_st, got[1]) expect_eq(nr_i(net_st), 30) expect_eq(got[1].slot, slot) } test "a reliable message goes again until it is taken" (net_st: mut NetState, net_test_st: mut NetTestState) { let slot = np_of_pid(net_st, host_with_guest(net_st, net_test_st)) net_end(net_st) net_test_st.sent = new []Pkt nw_begin(net_st) nw_i(net_st, 5) np_queue(net_st, slot, 9, true) frame(net_st, 0.01) expect_eq(len(net_test_st.sent), 1) frame(net_st, 0.1) expect_eq(len(net_test_st.sent), 1) frame(net_st, 0.2) expect_eq(len(net_test_st.sent), 2) arrive(net_test_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 1, 9, 0, 0) frame(net_st, 0.3) let n = len(net_test_st.sent) frame(net_st, 0.3) expect_eq(len(net_test_st.sent), n) } test "an unreliable message half a second old is dropped" (net_st: mut NetState, net_test_st: mut NetTestState) { let slot = np_of_pid(net_st, host_with_guest(net_st, net_test_st)) net_end(net_st) net_test_st.sent = new []Pkt nw_begin(net_st) np_queue(net_st, slot, 9, false) net_begin(net_st, 0.6) net_end(net_st) expect_eq(len(net_test_st.sent), 0) } test "a guest sends its hello until nobody has answered for too long" (net_st: mut NetState, net_test_st: mut NetTestState) { fresh(net_st, net_test_st) nw_begin(net_st) nw_s(net_st, "v1") net_hello_set(net_st) expect(net_join(net_st, Udp.ip("10.0.0.1"), 27415)) expect(net_joining(net_st)) frame(net_st, 0.1) expect_eq(len(net_test_st.sent), 1) expect_eq(nb_get8(net_test_st.sent[0].data, 20), NET_HELLO) for i in 0 .. 110 { frame(net_st, 0.1) } expect_eq(net_state(net_st), NET_LOST) let down = facts_of(net_st, NET_DOWN) expect_eq(len(down), 1) expect_eq(down[0].why, NET_WHY_NO_ANSWER) net_fresh(net_st) expect_eq(net_state(net_st), NET_IDLE) } test "a welcome makes the guest that pid in that session" (net_st: mut NetState, net_test_st: mut NetTestState) { fresh(net_st, net_test_st) nw_begin(net_st) net_hello_set(net_st) net_join(net_st, Udp.ip("10.0.0.1"), 27415) net_joined_as(net_st, 99, 4) expect(net_live(net_st)) expect_eq(net_my_pid(net_st), 4) expect_eq(net_token(net_st), 99) } test "a guest silent for the timeout is gone" (net_st: mut NetState, net_test_st: mut NetTestState) { let pid = host_with_guest(net_st, net_test_st) let slot = np_of_pid(net_st, pid) for i in 0 .. 105 { frame(net_st, 0.1) } let gone = facts_of(net_st, NET_GONE) expect_eq(len(gone), 1) expect_eq(gone[0].pid, pid) expect_eq(gone[0].slot, slot) expect(not net_peer_on(net_st, slot)) } test "a slot is let go once it has taken its last message" (net_st: mut NetState, net_test_st: mut NetTestState) { let slot = np_of_pid(net_st, host_with_guest(net_st, net_test_st)) nw_begin(net_st) np_queue(net_st, slot, 9, true) net_close_when_sent(net_st, slot, 20.0) frame(net_st, 0.01) expect(net_peer_on(net_st, slot)) arrive(net_test_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 1, 9, 0, 0) frame(net_st, 0.01) expect(not net_peer_on(net_st, slot)) expect_eq(len(facts_of(net_st, NET_CLOSED)), 1) } test "with no matchmaker, a room code is refused" (net_st: mut NetState, net_test_st: mut NetTestState) { fresh(net_st, net_test_st) expect(not net_room_on(net_st)) expect(not net_room_join(net_st, "ABCDEF")) expect_eq(net_why(net_st), NET_WHY_NO_MATCHMAKER) } test "sending holds its bytes flat: a message's record and bytes come back once it is sent (6 s of a guest, 6000 messages)" (net_st: mut NetState, net_test_st: mut NetTestState) { let slot = np_of_pid(net_st, host_with_guest(net_st, net_test_st)) net_test_st.quiet = true for r in 0 .. 3000 { if r == 1000 { net_test_st.from_ip = Os.heap_bytes() } nw_begin(net_st) nw_i(net_st, r) np_queue(net_st, slot, 9, false) nw_begin(net_st) nw_i(net_st, r + 1) np_queue(net_st, slot, 9, false) frame(net_st, 0.002) } let after = Os.heap_bytes() expect(net_test_st.sends >= 3000) if Os.heap_bytes() > 0 { expect(after <= net_test_st.from_ip) } expect(net_spare_count(net_st) >= 1) } test "an inbox message's record is kept: a drained list holds still while more come, and comes back two drains on" (net_st: mut NetState, net_test_st: mut NetTestState) { host_with_guest(net_st, net_test_st) q_clear(net_inbox(net_st)) arrive(net_test_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 2, 111) net_begin(net_st, 0.01) let first = q_drain(net_inbox(net_st)) expect_eq(len(first), 1) let r0: pointer = first[0] arrive(net_test_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 3, 222) net_begin(net_st, 0.01) net_read(net_st, first[0]) expect_eq(nr_i(net_st), 111) let second = q_drain(net_inbox(net_st)) net_read(net_st, second[0]) expect_eq(nr_i(net_st), 222) arrive(net_test_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 4, 333) net_begin(net_st, 0.01) let third = q_drain(net_inbox(net_st)) let r2: pointer = third[0] expect(r2 == r0) net_read(net_st, third[0]) expect_eq(nr_i(net_st), 333) } }