Files
mtgodot-poc/extension/tests/net_text_codec_test.cpp
T
shenandshen c93894313a fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
2026-09-21 16:38:59 -07:00

131 lines
4.8 KiB
C++

// net_text_codec_test — §1.10 fixed-length wire text: the client keeps UTF-8
// internally, while the Chinese 40250 wire uses GB2312-compatible bytes.
#include "../src/net/text_codec.h"
#include <cstdio>
#include <cstring>
#include <string>
static int g_fail = 0;
#define CHECK(c, msg) \
do { \
if (!(c)) { \
std::fprintf(stderr, "FAIL: %s\n", msg); \
++g_fail; \
} \
} while (0)
// U+6C49 "汉": 3 bytes in UTF-8, 2 bytes in GB2312 (BA BA).
static std::string cjk(int n) {
std::string s;
for (int i = 0; i < n; ++i) {
s += "\xE6\xB1\x89";
}
return s;
}
int main() {
using mtnet::from_wire_str;
using mtnet::to_wire;
// CHARACTER_NAME_MAX_LEN is a 24-byte wire capacity. Twelve Chinese
// characters fit; measuring the UTF-8 input would incorrectly allow eight.
{
const std::string in = cjk(12);
CHECK(in.size() == 36, "12 CJK == 36 UTF-8 bytes");
const std::string w = to_wire(in, 24);
CHECK(w.size() == 24, "12 CJK == 24 GB2312 bytes");
CHECK(from_wire_str(w.data(), w.size()) == in, "12 CJK round-trip through GB2312");
CHECK(mtnet::wire_text_fits(in, 24), "12 CJK fits 24-byte name field");
}
// Thirteen characters need 26 GB2312 bytes. Conversion truncates only at a
// legacy character boundary, while input validation rejects the overlong name.
{
const std::string in = cjk(13);
CHECK(in.size() == 39, "13 CJK == 39 UTF-8 bytes");
const std::string w = to_wire(in, 24);
CHECK(w.size() == 24, "13 CJK is capped at 24 wire bytes");
CHECK(from_wire_str(w.data(), w.size()) == cjk(12), "cap keeps 12 whole CJK chars");
CHECK(!mtnet::wire_text_fits(in, 24), "13 CJK rejected by 24-byte name field");
}
// The preflight used by M2Client must use the selected protocol's rule.
// A 32-byte 40250 shop-sign field therefore accepts sixteen GB2312 CJK
// characters even though the UTF-8 input occupies 48 bytes. The generic
// transport intentionally keeps its own byte contract.
{
CHECK(mtnet::protocol_text_fits(cjk(16), 32, true),
"classic preflight measures the encoded 32-byte sign");
CHECK(!mtnet::protocol_text_fits(cjk(17), 32, true),
"classic preflight rejects a 34-byte sign");
CHECK(!mtnet::protocol_text_fits(cjk(16), 32, false),
"generic preflight keeps its UTF-8 byte contract");
}
// Invalid UTF-8 is not leaked to the legacy wire and is rejected by the
// strict validation helper.
{
std::string in = cjk(12);
in += '\xE6'; // dangling UTF-8 lead byte
CHECK(to_wire(in, 24).size() == 24, "invalid suffix cannot overflow cap");
CHECK(!mtnet::wire_text_fits(in, 24), "invalid UTF-8 rejected");
}
// ASCII remains one byte per character and round-trips unchanged.
{
const std::string in = "Warrior";
CHECK(to_wire(in, 24) == in, "ascii under cap unchanged");
CHECK(to_wire(std::string(40, 'x'), 24).size() == 24, "ascii over cap -> 24");
CHECK(from_wire_str(in.data(), in.size()) == in, "ascii round-trip");
}
// The user-visible UTF-8 text is not limited to the test character 汉. A
// real Chinese name must round-trip through the 40250 GB2312 boundary too.
{
const std::string in = "龙驹简体中文";
const std::string wire = to_wire(in, 24);
CHECK(wire.size() == 12, "Chinese name uses GB2312 bytes, not UTF-8 bytes");
CHECK(from_wire_str(wire.data(), wire.size()) == in,
"Chinese name round-trips through the 40250 wire code page");
}
{
const std::string in = "龙驹简体中文免费版";
const std::string wire = to_wire(in, 24);
CHECK(in.size() == 27, "target Chinese name is 27 UTF-8 bytes");
CHECK(wire.size() == 18 && mtnet::wire_text_fits(in, 24),
"target Chinese name fits the 24-byte GB2312 field");
CHECK(from_wire_str(wire.data(), wire.size()) == in,
"target Chinese name round-trips through the 40250 wire code page");
}
// Decode legacy bytes up to NUL.
{
char buf[25] = {};
const std::string legacy = "\xBA\xBA\xBA\xBA\xBA\xBA";
std::memcpy(buf, legacy.data(), legacy.size());
CHECK(from_wire_str(buf, sizeof(buf) - 1) == cjk(3), "decodes GB2312 up to NUL");
}
{
char buf[25] = {};
const std::string legacy = to_wire(cjk(12), 24);
std::memcpy(buf, legacy.data(), legacy.size());
buf[24] = '\xBA'; // dangling lead byte at the field boundary
CHECK(from_wire_str(buf, 25) == cjk(12), "dangling legacy lead byte is ignored");
}
{
// Full 24-byte field, no NUL terminator: read all twelve characters.
char buf[24];
const std::string legacy = to_wire(cjk(12), 24);
std::memcpy(buf, legacy.data(), legacy.size());
CHECK(from_wire_str(buf, 24) == cjk(12), "unterminated full field read whole");
}
if (g_fail) {
std::fprintf(stderr, "%d checks FAILED\n", g_fail);
return 1;
}
std::puts("net_text_codec_test: OK");
return 0;
}