a43ce66a89
NetLink merkt sich die Verbindungen, die es gerade schließt, und schickt ihnen nichts mehr. Pakete an alle oder an alle außer einem gehen einzeln an die offenen Verbindungen, statt Godot ein Ziel zu nennen, das es nicht mehr gibt. Beides löste sonst Fehler in Godot aus, etwa wenn der Host Figuren weiterreicht, während er einen abgelehnten Gast hinauswirft. Ein neuer Test mit zwei Gästen deckt das ab. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
161 lines
5.9 KiB
GDScript
161 lines
5.9 KiB
GDScript
extends TestCase
|
|
## Two NetLinks in one process, connected over the loopback interface.
|
|
|
|
## Events per link, collected while frames pass.
|
|
var _events: Dictionary[NetLink, Array] = {}
|
|
|
|
|
|
## Polls `links` for `frames` frames and collects their events.
|
|
func _pump(links: Array[NetLink], frames: int) -> void:
|
|
for frame in frames:
|
|
for link in links:
|
|
if not _events.has(link):
|
|
_events[link] = []
|
|
_events[link].append_array(link.poll(1.0 / 60.0))
|
|
await tree.process_frame
|
|
|
|
|
|
func _types(link: NetLink) -> Array:
|
|
return _events.get(link, []).map(func(event: Dictionary) -> String: return event["type"])
|
|
|
|
|
|
func _first(link: NetLink, type: String) -> Dictionary:
|
|
for event: Dictionary in _events.get(link, []):
|
|
if event["type"] == type:
|
|
return event
|
|
return {}
|
|
|
|
|
|
## A host and a guest that have connected.
|
|
func _connected_pair() -> Array[NetLink]:
|
|
var host := NetLink.new()
|
|
check_eq(host.host(0, "127.0.0.1"), OK, "the host listens")
|
|
check(host.local_port() > 0, "on a port the system chose")
|
|
var guest := NetLink.new()
|
|
check_eq(guest.join("127.0.0.1", host.local_port()), OK, "the guest starts connecting")
|
|
await _pump([host, guest], 10)
|
|
return [host, guest]
|
|
|
|
|
|
func test_a_guest_connects_and_both_sides_hear_of_it() -> void:
|
|
var links := await _connected_pair()
|
|
var host := links[0]
|
|
var guest := links[1]
|
|
check(guest.is_connected_to_peers(), "the guest is connected")
|
|
check_eq(_first(guest, "connected").get("peer"), GwSession.HOST_PEER, "the guest sees the host as peer 1")
|
|
check(_first(host, "connected").get("peer", 0) > 1, "the host sees the guest")
|
|
host.close()
|
|
guest.close()
|
|
|
|
|
|
func test_messages_arrive_in_order_on_both_channels() -> void:
|
|
var links := await _connected_pair()
|
|
var host := links[0]
|
|
var guest := links[1]
|
|
var guest_peer: int = _first(host, "connected")["peer"]
|
|
_events.clear()
|
|
var big := PackedByteArray()
|
|
big.resize(50000)
|
|
big.fill(7)
|
|
host.send(guest_peer, big)
|
|
host.send(guest_peer, PackedByteArray([1]))
|
|
guest.send(GwSession.HOST_PEER, PackedByteArray([2, 3]))
|
|
guest.send_figure(GwSession.HOST_PEER, PackedByteArray([4]))
|
|
await _pump([host, guest], 20)
|
|
|
|
var received := _events[guest].filter(func(event: Dictionary) -> bool: return event["type"] == "message")
|
|
check_eq(received.size(), 2, "two messages for the guest")
|
|
if received.size() == 2:
|
|
check_eq(received[0]["bytes"].size(), 50000, "the big one first")
|
|
check_eq(received[1]["bytes"], PackedByteArray([1]), "then the small one")
|
|
check_eq(_first(host, "message").get("bytes"), PackedByteArray([2, 3]), "the host got the guest's message")
|
|
check_eq(_first(host, "figure").get("bytes"), PackedByteArray([4]), "and the position on the other channel")
|
|
host.close()
|
|
guest.close()
|
|
|
|
|
|
func test_closing_a_guest_delivers_what_was_sent_before() -> void:
|
|
var links := await _connected_pair()
|
|
var host := links[0]
|
|
var guest := links[1]
|
|
var guest_peer: int = _first(host, "connected")["peer"]
|
|
_events.clear()
|
|
host.send(guest_peer, PackedByteArray([9, 9]))
|
|
host.close_peer(guest_peer)
|
|
await _pump([host, guest], 30)
|
|
check_eq(_types(guest), ["message", "disconnected"], "the message comes before the end")
|
|
check_eq(_types(host), ["disconnected"], "the host sees the guest go")
|
|
check(not guest.is_open(), "the guest's connection is closed")
|
|
host.close()
|
|
|
|
|
|
func test_a_guest_that_quits_is_seen_leaving() -> void:
|
|
var links := await _connected_pair()
|
|
var host := links[0]
|
|
var guest := links[1]
|
|
_events.clear()
|
|
guest.close()
|
|
await _pump([host, guest], 10)
|
|
check_eq(_types(host), ["disconnected"], "the host sees the guest go")
|
|
host.close()
|
|
|
|
|
|
## Godot reports an error for every packet to a connection that is gone or
|
|
## being closed; the test runner turns each such error into a failure.
|
|
func test_nothing_is_sent_to_connections_that_are_gone_or_closing() -> void:
|
|
var host := NetLink.new()
|
|
check_eq(host.host(0, "127.0.0.1"), OK, "the host listens")
|
|
var first := NetLink.new()
|
|
var second := NetLink.new()
|
|
first.join("127.0.0.1", host.local_port())
|
|
second.join("127.0.0.1", host.local_port())
|
|
await _pump([host, first, second], 10)
|
|
var peers: Array = _events[host].map(func(event: Dictionary) -> int: return event["peer"])
|
|
check_eq(peers.size(), 2, "two guests connected")
|
|
var leaving: int = peers[0]
|
|
var staying: int = peers[1]
|
|
|
|
# Connections that never existed.
|
|
host.send(12345, PackedByteArray([1]))
|
|
host.send_figure(12345, PackedByteArray([1]))
|
|
host.send_figure(-12345, PackedByteArray([1]))
|
|
host.close_peer(12345)
|
|
# A connection that is being closed, and everybody while it closes.
|
|
host.close_peer(leaving)
|
|
for frame in 10:
|
|
host.send(leaving, PackedByteArray([2]))
|
|
host.send_figure(leaving, PackedByteArray([2]))
|
|
host.send_figure(-staying, PackedByteArray([2]))
|
|
host.send_figure(0, PackedByteArray([2]))
|
|
await _pump([host, first, second], 1)
|
|
# The connection is gone now.
|
|
host.send(leaving, PackedByteArray([3]))
|
|
host.send_figure(-leaving, PackedByteArray([3]))
|
|
await _pump([host, first, second], 5)
|
|
|
|
check(_types(first).has("disconnected"), "the first guest was closed")
|
|
check(not _types(second).has("disconnected"), "the second guest stays")
|
|
for link in [host, first, second]:
|
|
link.close()
|
|
|
|
|
|
func test_a_guest_gives_up_when_nobody_answers() -> void:
|
|
# A port that was free a moment ago and has nobody listening now.
|
|
var probe := NetLink.new()
|
|
probe.host(0, "127.0.0.1")
|
|
var port := probe.local_port()
|
|
probe.close()
|
|
var guest := NetLink.new()
|
|
guest.connect_seconds = 0.2
|
|
check_eq(guest.join("127.0.0.1", port), OK, "connecting starts")
|
|
await _pump([guest], 30)
|
|
check_eq(_types(guest), ["failed"], "the guest gives up")
|
|
check(not guest.is_open(), "and closes")
|
|
|
|
|
|
func test_addresses_are_split_into_host_and_port() -> void:
|
|
check_eq(NetLink.split_address("192.168.1.20"), ["192.168.1.20", NetLink.DEFAULT_PORT], "default port")
|
|
check_eq(NetLink.split_address(" example.net:25000 "), ["example.net", 25000], "with port")
|
|
for text in ["", ":25000", "host:", "host:0", "host:70000", "host:port", "a:1:2"]:
|
|
check_eq(NetLink.split_address(text), [], "refused: '%s'" % text)
|