1
0
Protocol/structs/parser_test.go

417 lines
11 KiB
Go

package structs
import (
"os"
"path/filepath"
"testing"
)
// TestParseLoginRequest tests parsing the LoginRequest XML
func TestParseLoginRequest(t *testing.T) {
// Create a temporary directory with test XML
tmpDir := t.TempDir()
xmlDir := filepath.Join(tmpDir, "xml", "login")
os.MkdirAll(xmlDir, 0755)
// Write test LoginRequest XML
loginXML := `<packet name="LoginRequest">
<version number="1">
<str16 name="sessionID"/>
<str16 name="sessionRecycleToken"/>
<str16 name="username"/>
<str16 name="password"/>
<u32 name="acctNum"/>
<u32 name="passCode"/>
<u16 name="version"/>
</version>
<version number="562">
<str16 name="accesscode"/>
<str16 name="unknown1"/>
<str16 name="username"/>
<str16 name="password"/>
<u8 name="unknown2" size="8"/>
<u8 name="unknown3" size="2"/>
<u32 name="version"/>
<u16 name="unknown3b"/>
<u32 name="unknown4"/>
</version>
</packet>`
xmlFile := filepath.Join(xmlDir, "LoginRequest.xml")
if err := os.WriteFile(xmlFile, []byte(loginXML), 0644); err != nil {
t.Fatalf("Failed to write test XML: %v", err)
}
// Create parser and load file
parser := NewParser(tmpDir)
if err := parser.LoadFile(xmlFile); err != nil {
t.Fatalf("Failed to load XML: %v", err)
}
// Check packet was loaded
def, exists := parser.GetPacket("LoginRequest")
if !exists {
t.Fatal("LoginRequest packet not found")
}
if def.IsSubstruct {
t.Error("LoginRequest should not be a substruct")
}
// Check versions
if len(def.Versions) != 2 {
t.Errorf("Expected 2 versions, got %d", len(def.Versions))
}
// Check version 1 fields
if def.Versions[0].Version != 1 {
t.Errorf("Expected version 1, got %d", def.Versions[0].Version)
}
if len(def.Versions[0].Fields) != 7 {
t.Errorf("Expected 7 fields in version 1, got %d", len(def.Versions[0].Fields))
}
// Check specific fields
field := def.Versions[0].Fields[2]
if field.Name != "username" {
t.Errorf("Expected field name 'username', got '%s'", field.Name)
}
if field.Type != FieldTypeString16 {
t.Errorf("Expected field type String16, got %v", field.Type)
}
// Check version 562
if def.Versions[1].Version != 562 {
t.Errorf("Expected version 562, got %d", def.Versions[1].Version)
}
// Check array field
arrayField := def.Versions[1].Fields[4]
if arrayField.Name != "unknown2" {
t.Errorf("Expected field name 'unknown2', got '%s'", arrayField.Name)
}
if arrayField.Size != 8 {
t.Errorf("Expected size 8, got %d", arrayField.Size)
}
}
// TestPacketStruct tests packet struct serialization/deserialization
func TestPacketStruct(t *testing.T) {
// Create a simple packet version for testing
version := &PacketVersion{
Version: 1,
Fields: []Field{
{Name: "id", Type: FieldTypeUInt32},
{Name: "name", Type: FieldTypeString16},
{Name: "level", Type: FieldTypeUInt8},
{Name: "x", Type: FieldTypeFloat},
{Name: "y", Type: FieldTypeFloat},
{Name: "z", Type: FieldTypeFloat},
},
}
// Create packet struct
ps := NewPacketStruct(version)
// Set values
ps.Set("id", uint32(12345))
ps.Set("name", "TestPlayer")
ps.Set("level", uint8(50))
ps.Set("x", float32(100.5))
ps.Set("y", float32(200.5))
ps.Set("z", float32(300.5))
// Serialize
data, err := ps.Serialize()
if err != nil {
t.Fatalf("Failed to serialize: %v", err)
}
// Create new packet struct and deserialize
ps2 := NewPacketStruct(version)
if err := ps2.Deserialize(data); err != nil {
t.Fatalf("Failed to deserialize: %v", err)
}
// Check values
if val, _ := ps2.Get("id"); val.(uint32) != 12345 {
t.Errorf("Expected id 12345, got %v", val)
}
if val, _ := ps2.Get("name"); val.(string) != "TestPlayer" {
t.Errorf("Expected name 'TestPlayer', got %v", val)
}
if val, _ := ps2.Get("level"); val.(uint8) != 50 {
t.Errorf("Expected level 50, got %v", val)
}
if val, _ := ps2.Get("x"); val.(float32) != 100.5 {
t.Errorf("Expected x 100.5, got %v", val)
}
}
// TestConfigReader tests the ConfigReader interface
func TestConfigReader(t *testing.T) {
// Create temporary directory with test XML
tmpDir := t.TempDir()
structsDir := filepath.Join(tmpDir, "structs", "xml", "test")
os.MkdirAll(structsDir, 0755)
// Write test packet XML
testXML := `<packet name="TestPacket">
<version number="1">
<u32 name="id"/>
<str16 name="name"/>
</version>
<version number="100">
<u32 name="id"/>
<str16 name="name"/>
<u8 name="level"/>
</version>
<version number="200">
<u32 name="id"/>
<str16 name="name"/>
<u8 name="level"/>
<u16 name="flags"/>
</version>
</packet>`
xmlFile := filepath.Join(structsDir, "TestPacket.xml")
if err := os.WriteFile(xmlFile, []byte(testXML), 0644); err != nil {
t.Fatalf("Failed to write test XML: %v", err)
}
// Create config reader
cr := NewConfigReader(tmpDir)
if err := cr.LoadFiles([]string{xmlFile}); err != nil {
t.Fatalf("Failed to load structs: %v", err)
}
// Test GetStruct with version selection
tests := []struct {
version uint16
expectedFields int
}{
{1, 2}, // Exact match
{50, 2}, // Should use version 1
{100, 3}, // Exact match
{150, 3}, // Should use version 100
{200, 4}, // Exact match
{300, 4}, // Should use version 200
}
for _, test := range tests {
ps, err := cr.GetStruct("TestPacket", test.version)
if err != nil {
t.Errorf("Failed to get struct for version %d: %v", test.version, err)
continue
}
if len(ps.definition.Fields) != test.expectedFields {
t.Errorf("Version %d: expected %d fields, got %d",
test.version, test.expectedFields, len(ps.definition.Fields))
}
}
// Test GetStructByVersion (exact match only)
ps, err := cr.GetStructByVersion("TestPacket", 100)
if err != nil {
t.Errorf("Failed to get struct by exact version: %v", err)
}
if ps != nil && len(ps.definition.Fields) != 3 {
t.Errorf("Expected 3 fields for version 100, got %d", len(ps.definition.Fields))
}
// This should fail (no exact match)
_, err = cr.GetStructByVersion("TestPacket", 150)
if err == nil {
t.Error("Expected error for non-existent exact version 150")
}
// Test GetStructVersion
version, err := cr.GetStructVersion("TestPacket", 150)
if err != nil {
t.Errorf("Failed to get struct version: %v", err)
}
if version != 100 {
t.Errorf("Expected version 100 for client version 150, got %d", version)
}
}
// TestStringFields tests various string field types
func TestStringFields(t *testing.T) {
version := &PacketVersion{
Version: 1,
Fields: []Field{
{Name: "str8", Type: FieldTypeString8},
{Name: "str16", Type: FieldTypeString16},
{Name: "str32", Type: FieldTypeString32},
{Name: "char_fixed", Type: FieldTypeChar, Size: 10},
{Name: "char_null", Type: FieldTypeChar, Size: 0},
},
}
ps := NewPacketStruct(version)
// Set string values
ps.Set("str8", "Short")
ps.Set("str16", "Medium length string")
ps.Set("str32", "This is a much longer string that might be used for descriptions")
ps.Set("char_fixed", "Fixed")
ps.Set("char_null", "Null-term")
// Serialize
data, err := ps.Serialize()
if err != nil {
t.Fatalf("Failed to serialize: %v", err)
}
// Deserialize
ps2 := NewPacketStruct(version)
if err := ps2.Deserialize(data); err != nil {
t.Fatalf("Failed to deserialize: %v", err)
}
// Verify strings
if val, _ := ps2.Get("str8"); val.(string) != "Short" {
t.Errorf("str8 mismatch: got %v", val)
}
if val, _ := ps2.Get("str16"); val.(string) != "Medium length string" {
t.Errorf("str16 mismatch: got %v", val)
}
if val, _ := ps2.Get("str32"); val.(string) != "This is a much longer string that might be used for descriptions" {
t.Errorf("str32 mismatch: got %v", val)
}
if val, _ := ps2.Get("char_fixed"); val.(string) != "Fixed" {
t.Errorf("char_fixed mismatch: got %v", val)
}
if val, _ := ps2.Get("char_null"); val.(string) != "Null-term" {
t.Errorf("char_null mismatch: got %v", val)
}
}
// TestConditionalFields tests fields with if-set/if-not-set conditions
func TestConditionalFields(t *testing.T) {
version := &PacketVersion{
Version: 1,
Fields: []Field{
{Name: "hasData", Type: FieldTypeUInt8},
{Name: "data", Type: FieldTypeUInt32, IfSet: "hasData"},
{Name: "noData", Type: FieldTypeUInt16, IfNotSet: "hasData"},
},
}
// Test with hasData set
ps1 := NewPacketStruct(version)
ps1.Set("hasData", uint8(1))
ps1.Set("data", uint32(999))
ps1.Set("noData", uint16(111))
data1, err := ps1.Serialize()
if err != nil {
t.Fatalf("Failed to serialize: %v", err)
}
// Should contain: uint8(1), uint32(999)
// Should NOT contain: uint16(111)
expectedSize := 1 + 4
if len(data1) != expectedSize {
t.Errorf("Expected serialized size %d, got %d", expectedSize, len(data1))
}
// Test with hasData not set
ps2 := NewPacketStruct(version)
ps2.Set("data", uint32(999))
ps2.Set("noData", uint16(111))
data2, err := ps2.Serialize()
if err != nil {
t.Fatalf("Failed to serialize: %v", err)
}
// Should contain: uint8(0), uint16(111)
// Should NOT contain: uint32(999)
expectedSize = 1 + 2
if len(data2) != expectedSize {
t.Errorf("Expected serialized size %d, got %d", expectedSize, len(data2))
}
}
// BenchmarkSerialization benchmarks packet serialization
func BenchmarkSerialization(b *testing.B) {
version := &PacketVersion{
Version: 1,
Fields: []Field{
{Name: "id", Type: FieldTypeUInt32},
{Name: "name", Type: FieldTypeString16},
{Name: "level", Type: FieldTypeUInt8},
{Name: "x", Type: FieldTypeFloat},
{Name: "y", Type: FieldTypeFloat},
{Name: "z", Type: FieldTypeFloat},
{Name: "flags", Type: FieldTypeUInt32},
{Name: "timestamp", Type: FieldTypeUInt64},
},
}
ps := NewPacketStruct(version)
ps.Set("id", uint32(12345))
ps.Set("name", "BenchmarkPlayer")
ps.Set("level", uint8(50))
ps.Set("x", float32(100.5))
ps.Set("y", float32(200.5))
ps.Set("z", float32(300.5))
ps.Set("flags", uint32(0xFF00FF00))
ps.Set("timestamp", uint64(1234567890))
b.ResetTimer()
for i := 0; i < b.N; i++ {
data, err := ps.Serialize()
if err != nil {
b.Fatal(err)
}
_ = data
}
}
// BenchmarkDeserialization benchmarks packet deserialization
func BenchmarkDeserialization(b *testing.B) {
version := &PacketVersion{
Version: 1,
Fields: []Field{
{Name: "id", Type: FieldTypeUInt32},
{Name: "name", Type: FieldTypeString16},
{Name: "level", Type: FieldTypeUInt8},
{Name: "x", Type: FieldTypeFloat},
{Name: "y", Type: FieldTypeFloat},
{Name: "z", Type: FieldTypeFloat},
},
}
// Create test data
ps := NewPacketStruct(version)
ps.Set("id", uint32(12345))
ps.Set("name", "TestPlayer")
ps.Set("level", uint8(50))
ps.Set("x", float32(100.5))
ps.Set("y", float32(200.5))
ps.Set("z", float32(300.5))
data, _ := ps.Serialize()
b.ResetTimer()
for i := 0; i < b.N; i++ {
ps2 := NewPacketStruct(version)
err := ps2.Deserialize(data)
if err != nil {
b.Fatal(err)
}
}
}