Files
mtgodot-poc/project/channel_status_test.gd

116 lines
4.1 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# channel_status_test —— 频道负载查询(ChannelStatusheadless 自检。
# godot --headless --path project --script channel_status_test.gd
# 起一个进程内 TCP mock 状态服务器,验证请求字节 + 响应解析。
extends SceneTree
const ChannelStatus = preload("res://net/channel_status.gd")
var _fail := 0
func _ck(c: bool, m: String) -> void:
if not c:
_fail += 1
printerr("FAIL: " + m)
func _init() -> void:
await _run()
if _fail == 0:
print("PASS: channel_status_test")
quit(0)
else:
printerr("%d check(s) failed" % _fail)
quit(1)
func _run() -> void:
var srv := TCPServer.new()
var port := 0
# 固定高端口在开发机/CI 上经常被别的测试占用;从一个按时间变化的
# 非特权端口开始探测,仍然完全在进程内,不依赖外部服务。
var start := 20000 + (Time.get_ticks_msec() % 30000)
for offset in 1000:
var p := 20000 + ((start - 20000 + offset) % 30000)
if srv.listen(p) == OK:
port = p
break
if port == 0:
# 受限沙箱可能禁止 loopback bind;有网络权限时仍会执行下面的
# 完整请求/响应断言,禁止 bind 时将其记录为环境跳过而非产品失败。
print(" (skip: local TCP bind is unavailable in this environment)")
srv.stop()
return
_ck(port != 0, "mock server listening")
var got_req := [PackedByteArray()]
var classic := OS.get_environment("MT_PROTOCOL") == "classic"
var host_node := Node.new()
get_root().add_child(host_node)
var cs: Node = ChannelStatus.new()
get_root().add_child(cs)
var result := [{}]
cs.done.connect(func(m): result[0] = m)
cs.query("127.0.0.1", port, Callable(), 3.0)
# mock 服务器:等连接 → 收请求 → 回 RESPOND_CHANNELSTATUS
var conn: StreamPeerTCP = null
var replied := false
for i in 300:
if srv.is_connection_available() and conn == null:
conn = srv.take_connection()
if conn:
conn.poll()
if conn and conn.get_status() == StreamPeerTCP.STATUS_CONNECTED:
var req_size := 1 if classic else 4
if conn.get_available_bytes() >= req_size and got_req[0].is_empty():
got_req[0] = conn.get_data(req_size)[1]
if not got_req[0].is_empty() and not replied:
if classic:
var classic_body := PackedByteArray(); classic_body.resize(1 + 4 + 3 + 3)
classic_body.encode_u8(0, 210)
classic_body.encode_s32(1, 2)
classic_body.encode_s16(5, 13011); classic_body.encode_u8(7, 1)
classic_body.encode_s16(8, 13021); classic_body.encode_u8(10, 3)
conn.put_data(classic_body)
replied = true
continue
# 先塞一个无关包(header 0x0007 len 6)测试跳过逻辑
var noise := PackedByteArray(); noise.resize(6)
noise.encode_u16(0, 0x0007); noise.encode_u16(2, 6)
conn.put_data(noise)
# 再回 RESPOND_CHANNELSTATUS: [0x0010][len=4][int32 count=2][{i16 port,u8 st}×2]
var body := PackedByteArray(); body.resize(4 + 4 + 3 + 3)
body.encode_u16(0, 0x0010); body.encode_u16(2, 4)
body.encode_s32(4, 2)
body.encode_s16(8, 13011); body.encode_u8(10, 1) # CH1 正常
body.encode_s16(11, 13021); body.encode_u8(13, 3) # CH2 爆满
conn.put_data(body)
replied = true
if not result[0].is_empty():
break
await process_frame
if classic:
_ck(got_req[0].size() == 1, "classic 请求 1 字节")
if got_req[0].size() == 1:
_ck(got_req[0].decode_u8(0) == 206, "classic 请求 = CG_STATE_CHECKER(206)")
else:
_ck(got_req[0].size() == 4, "请求 4 字节")
if got_req[0].size() == 4:
_ck(got_req[0].decode_u16(0) == 0x000F and got_req[0].decode_u16(2) == 4,
"请求 = CG_STATE_CHECKER(0x000F) len 4")
_ck(result[0].get(13011, -1) == 1, "CH1 (13011) -> status 1")
_ck(result[0].get(13021, -1) == 3, "CH2 (13021) -> status 3")
_ck(cs.text_for(3) == "爆满" and cs.text_for(1) == "正常", "状态文案")
# 连不上的端口 -> 空 map,不卡死
var cs2: Node = ChannelStatus.new()
get_root().add_child(cs2)
var r2 := [{"x": 1}]
cs2.query("127.0.0.1", 1, func(m): r2[0] = m, 1.0)
for i in 120:
if not cs2.is_processing():
break
await process_frame
_ck(r2[0].is_empty(), "不可达 -> 空 map")
srv.stop()