Port tests\core\net

This commit is contained in:
Jeroen van Rijn
2024-05-31 16:32:12 +02:00
committed by Feoramund
parent dacb0f7786
commit 62b7d8de97
3 changed files with 85 additions and 195 deletions

View File

@@ -81,7 +81,7 @@ noise_test:
$(ODIN) test math/noise $(COMMON) -out:test_noise
net_test:
$(ODIN) run net $(COMMON) -out:test_core_net
$(ODIN) test net $(COMMON) -out:test_core_net
os_exit_test:
$(ODIN) run os/test_core_os_exit.odin -file -out:test_core_os_exit && exit 1 || exit 0

View File

@@ -81,7 +81,7 @@ echo ---
echo ---
echo Running core:net
echo ---
%PATH_TO_ODIN% run net %COMMON% -out:test_core_net.exe || exit /b
%PATH_TO_ODIN% test net %COMMON% -out:test_core_net.exe || exit /b
echo ---
echo Running core:odin tests

View File

@@ -11,71 +11,12 @@
package test_core_net
import "core:testing"
import "core:mem"
import "core:fmt"
import "core:net"
import "core:strconv"
import "core:sync"
import "core:time"
import "core:thread"
import "core:os"
_, _ :: time, thread
TEST_count := 0
TEST_fail := 0
t := &testing.T{}
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
_tracking_allocator := mem.Tracking_Allocator{}
print_tracking_allocator_report :: proc() {
for _, leak in _tracking_allocator.allocation_map {
fmt.printf("%v leaked %v bytes\n", leak.location, leak.size)
}
for bf in _tracking_allocator.bad_free_array {
fmt.printf("%v allocation %p was freed badly\n", bf.location, bf.memory)
}
}
main :: proc() {
mem.tracking_allocator_init(&_tracking_allocator, context.allocator)
context.allocator = mem.tracking_allocator(&_tracking_allocator)
address_parsing_test(t)
tcp_tests(t)
split_url_test(t)
join_url_test(t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
print_tracking_allocator_report()
if TEST_fail > 0 {
os.exit(1)
}
}
import "core:fmt"
@test
address_parsing_test :: proc(t: ^testing.T) {
@@ -89,127 +30,66 @@ address_parsing_test :: proc(t: ^testing.T) {
}
valid := len(vector.binstr) > 0
fmt.printf("%v %v\n", kind, vector.input)
msg := "-set a proper message-"
switch vector.family {
case .IP4, .IP4_Alt:
/*
Does `net.parse_ip4_address` think we parsed the address properly?
*/
// Does `net.parse_ip4_address` think we parsed the address properly?
non_decimal := vector.family == .IP4_Alt
any_addr := net.parse_address(vector.input, non_decimal)
parsed_ok := any_addr != nil
parsed: net.IP4_Address
any_addr := net.parse_address(vector.input, non_decimal)
parsed_ok := any_addr != nil
parsed: net.IP4_Address
/*
Ensure that `parse_address` doesn't parse IPv4 addresses into IPv6 addreses by mistake.
*/
// Ensure that `parse_address` doesn't parse IPv4 addresses into IPv6 addreses by mistake.
switch addr in any_addr {
case net.IP4_Address:
parsed = addr
case net.IP6_Address:
parsed_ok = false
msg = fmt.tprintf("parse_address mistook %v as IPv6 address %04x", vector.input, addr)
expect(t, false, msg)
testing.expectf(t, false, "parse_address mistook %v as IPv6 address %04x", vector.input, addr)
}
if !parsed_ok && valid {
msg = fmt.tprintf("parse_ip4_address failed to parse %v, expected %v", vector.input, binstr_to_address(vector.binstr))
testing.expectf(t, parsed_ok == valid, "parse_ip4_address failed to parse %v, expected %v", vector.input, binstr_to_address(t, vector.binstr))
} else if parsed_ok && !valid {
msg = fmt.tprintf("parse_ip4_address parsed %v into %v, expected failure", vector.input, parsed)
testing.expectf(t, parsed_ok == valid, "parse_ip4_address parsed %v into %v, expected failure", vector.input, parsed)
}
expect(t, parsed_ok == valid, msg)
if valid && parsed_ok {
actual_binary := address_to_binstr(parsed)
msg = fmt.tprintf("parse_ip4_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr)
expect(t, actual_binary == vector.binstr, msg)
testing.expectf(t, actual_binary == vector.binstr, "parse_ip4_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr)
/*
Do we turn an address back into the same string properly?
No point in testing the roundtrip if the first part failed.
*/
// Do we turn an address back into the same string properly? No point in testing the roundtrip if the first part failed.
if len(vector.output) > 0 && actual_binary == vector.binstr {
stringified := net.address_to_string(parsed)
msg = fmt.tprintf("address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output)
expect(t, stringified == vector.output, msg)
testing.expectf(t, stringified == vector.output, "address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output)
}
}
case .IP6:
/*
Do we parse the address properly?
*/
// Do we parse the address properly?
parsed, parsed_ok := net.parse_ip6_address(vector.input)
if !parsed_ok && valid {
msg = fmt.tprintf("parse_ip6_address failed to parse %v, expected %04x", vector.input, binstr_to_address(vector.binstr))
testing.expectf(t, parsed_ok == valid, "parse_ip6_address failed to parse %v, expected %04x", vector.input, binstr_to_address(t, vector.binstr))
} else if parsed_ok && !valid {
msg = fmt.tprintf("parse_ip6_address parsed %v into %04x, expected failure", vector.input, parsed)
testing.expectf(t, parsed_ok == valid, "parse_ip6_address parsed %v into %04x, expected failure", vector.input, parsed)
}
expect(t, parsed_ok == valid, msg)
if valid && parsed_ok {
actual_binary := address_to_binstr(parsed)
msg = fmt.tprintf("parse_ip6_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr)
expect(t, actual_binary == vector.binstr, msg)
testing.expectf(t, actual_binary == vector.binstr, "parse_ip6_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr)
/*
Do we turn an address back into the same string properly?
No point in testing the roundtrip if the first part failed.
*/
// Do we turn an address back into the same string properly? No point in testing the roundtrip if the first part failed.
if len(vector.output) > 0 && actual_binary == vector.binstr {
stringified := net.address_to_string(parsed)
msg = fmt.tprintf("address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output)
expect(t, stringified == vector.output, msg)
testing.expectf(t, stringified == vector.output, "address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output)
}
}
}
}
}
address_to_binstr :: proc(address: net.Address) -> (binstr: string) {
switch t in address {
case net.IP4_Address:
b := transmute(u32be)t
return fmt.tprintf("%08x", b)
case net.IP6_Address:
b := transmute(u128be)t
return fmt.tprintf("%32x", b)
case:
return ""
}
unreachable()
}
binstr_to_address :: proc(binstr: string) -> (address: net.Address) {
switch len(binstr) {
case 8: // IPv4
a, ok := strconv.parse_u64_of_base(binstr, 16)
expect(t, ok, "failed to parse test case bin string")
ipv4 := u32be(a)
return net.IP4_Address(transmute([4]u8)ipv4)
case 32: // IPv6
a, ok := strconv.parse_u128_of_base(binstr, 16)
expect(t, ok, "failed to parse test case bin string")
ipv4 := u128be(a)
return net.IP6_Address(transmute([8]u16be)ipv4)
case 0:
return nil
}
panic("Invalid test case")
}
Kind :: enum {
IP4, // Decimal IPv4
IP4_Alt, // Non-decimal address
@@ -223,10 +103,7 @@ IP_Address_Parsing_Test_Vector :: struct {
// Input address to try and parse.
input: string,
/*
Hexadecimal representation of the expected numeric value of the address.
Zero length means input is invalid and the parser should report failure.
*/
// Hexadecimal representation of the expected numeric value of the address. Zero length means input is invalid and the parser should report failure.
binstr: string,
// Expected `address_to_string` output, if a valid input and this string is non-empty.
@@ -335,38 +212,30 @@ IP_Address_Parsing_Test_Vectors :: []IP_Address_Parsing_Test_Vector{
{ .IP6, "c0a8", "", ""},
}
tcp_tests :: proc(t: ^testing.T) {
fmt.println("Testing two servers trying to bind to the same endpoint...")
two_servers_binding_same_endpoint(t)
fmt.println("Testing client connecting to a closed port...")
client_connects_to_closed_port(t)
fmt.println("Testing client sending server data...")
client_sends_server_data(t)
}
ENDPOINT := net.Endpoint{
net.IP4_Address{127, 0, 0, 1},
9999,
}
ENDPOINT_TWO_SERVERS := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 9991}
ENDPOINT_CLOSED_PORT := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 9992}
ENDPOINT_SERVER_SENDS := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 9993}
@(test)
two_servers_binding_same_endpoint :: proc(t: ^testing.T) {
skt1, err1 := net.listen_tcp(ENDPOINT)
skt1, err1 := net.listen_tcp(ENDPOINT_TWO_SERVERS)
defer net.close(skt1)
skt2, err2 := net.listen_tcp(ENDPOINT)
skt2, err2 := net.listen_tcp(ENDPOINT_TWO_SERVERS)
defer net.close(skt2)
expect(t, err1 == nil, "expected first server binding to endpoint to do so without error")
expect(t, err2 == net.Bind_Error.Address_In_Use, "expected second server to bind to an endpoint to return .Address_In_Use")
testing.expect(t, err1 == nil, "expected first server binding to endpoint to do so without error")
testing.expect(t, err2 == net.Bind_Error.Address_In_Use, "expected second server to bind to an endpoint to return .Address_In_Use")
}
@(test)
client_connects_to_closed_port :: proc(t: ^testing.T) {
skt, err := net.dial_tcp(ENDPOINT)
skt, err := net.dial_tcp(ENDPOINT_CLOSED_PORT)
defer net.close(skt)
expect(t, err == net.Dial_Error.Refused, "expected dial of a closed endpoint to return .Refused")
testing.expect(t, err == net.Dial_Error.Refused, "expected dial of a closed endpoint to return .Refused")
}
@(test)
client_sends_server_data :: proc(t: ^testing.T) {
CONTENT: string: "Hellope!"
@@ -390,8 +259,8 @@ client_sends_server_data :: proc(t: ^testing.T) {
defer sync.wait_group_done(r.wg)
if r.skt, r.err = net.dial_tcp(ENDPOINT); r.err != nil {
log(r.t, r.err)
if r.skt, r.err = net.dial_tcp(ENDPOINT_SERVER_SENDS); r.err != nil {
testing.expectf(r.t, false, "[tcp_client:dial_tcp] %v", r.err)
return
}
@@ -405,19 +274,17 @@ client_sends_server_data :: proc(t: ^testing.T) {
defer sync.wait_group_done(r.wg)
log(r.t, "tcp_server listen")
if r.skt, r.err = net.listen_tcp(ENDPOINT); r.err != nil {
if r.skt, r.err = net.listen_tcp(ENDPOINT_SERVER_SENDS); r.err != nil {
sync.wait_group_done(r.wg)
log(r.t, r.err)
testing.expectf(r.t, false, "[tcp_server:listen_tcp] %v", r.err)
return
}
sync.wait_group_done(r.wg)
log(r.t, "tcp_server accept")
client: net.TCP_Socket
if client, _, r.err = net.accept_tcp(r.skt.(net.TCP_Socket)); r.err != nil {
log(r.t, r.err)
testing.expectf(r.t, false, "[tcp_server:accept_tcp] %v", r.err)
return
}
defer net.close(client)
@@ -437,10 +304,7 @@ client_sends_server_data :: proc(t: ^testing.T) {
thread_data[0].wg = &wg
thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_server, context)
log(t, "waiting for server to start listening")
sync.wait_group_wait(&wg)
log(t, "starting up client")
sync.wait_group_add(&wg, 2)
thread_data[1].t = t
@@ -454,20 +318,15 @@ client_sends_server_data :: proc(t: ^testing.T) {
net.close(thread_data[1].skt)
thread.destroy(thread_data[1].tid)
}
log(t, "waiting for threads to finish")
sync.wait_group_wait(&wg)
log(t, "threads finished")
okay := thread_data[0].err == nil && thread_data[1].err == nil
msg := fmt.tprintf("Expected client and server to return `nil`, got %v and %v", thread_data[0].err, thread_data[1].err)
expect(t, okay, msg)
testing.expectf(t, okay, "Expected client and server to return `nil`, got %v and %v", thread_data[0].err, thread_data[1].err)
received := string(thread_data[0].data[:thread_data[0].length])
okay = received == CONTENT
msg = fmt.tprintf("Expected client to send \"{}\", got \"{}\"", CONTENT, received)
expect(t, okay, msg)
testing.expectf(t, okay, "Expected client to send \"{}\", got \"{}\"", CONTENT, received)
}
URL_Test :: struct {
@@ -559,22 +418,15 @@ split_url_test :: proc(t: ^testing.T) {
delete(test.queries)
}
msg := fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.scheme, scheme)
expect(t, scheme == test.scheme, msg)
msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.host, host)
expect(t, host == test.host, msg)
msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.path, path)
expect(t, path == test.path, msg)
msg = fmt.tprintf("Expected `net.split_url` to return %d queries, got %d queries", len(test.queries), len(queries))
expect(t, len(queries) == len(test.queries), msg)
testing.expectf(t, scheme == test.scheme, "Expected `net.split_url` to return %s, got %s", test.scheme, scheme)
testing.expectf(t, host == test.host, "Expected `net.split_url` to return %s, got %s", test.host, host)
testing.expectf(t, path == test.path, "Expected `net.split_url` to return %s, got %s", test.path, path)
testing.expectf(t, len(queries) == len(test.queries), "Expected `net.split_url` to return %d queries, got %d queries", len(test.queries), len(queries))
for k, v in queries {
expected := test.queries[k]
msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", expected, v)
expect(t, v == expected, msg)
testing.expectf(t, v == expected, "Expected `net.split_url` to return %s, got %s", expected, v)
}
msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.fragment, fragment)
expect(t, fragment == test.fragment, msg)
testing.expectf(t, fragment == test.fragment, "Expected `net.split_url` to return %s, got %s", test.fragment, fragment)
}
}
@@ -659,7 +511,45 @@ join_url_test :: proc(t: ^testing.T) {
for test_url in test.url {
pass |= url == test_url
}
msg := fmt.tprintf("Expected `net.join_url` to return one of %s, got %s", test.url, url)
expect(t, pass, msg)
testing.expectf(t, pass, "Expected `net.join_url` to return one of %s, got %s", test.url, url)
}
}
@(private)
address_to_binstr :: proc(address: net.Address) -> (binstr: string) {
switch t in address {
case net.IP4_Address:
b := transmute(u32be)t
return fmt.tprintf("%08x", b)
case net.IP6_Address:
b := transmute(u128be)t
return fmt.tprintf("%32x", b)
case:
return ""
}
unreachable()
}
@(private)
binstr_to_address :: proc(t: ^testing.T, binstr: string) -> (address: net.Address) {
switch len(binstr) {
case 8: // IPv4
a, ok := strconv.parse_u64_of_base(binstr, 16)
testing.expect(t, ok, "failed to parse test case bin string")
ipv4 := u32be(a)
return net.IP4_Address(transmute([4]u8)ipv4)
case 32: // IPv6
a, ok := strconv.parse_u128_of_base(binstr, 16)
testing.expect(t, ok, "failed to parse test case bin string")
ipv4 := u128be(a)
return net.IP6_Address(transmute([8]u16be)ipv4)
case 0:
return nil
}
panic("Invalid test case")
}