package parser import ( "eq2emu/internal/common" "testing" ) func TestBasicParsing(t *testing.T) { pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["Test"] if packet == nil { t.Fatal("Test packet not found") } // Check fields if len(packet.Fields) != 3 { t.Errorf("Expected 3 fields, got %d", len(packet.Fields)) } if packet.Fields["player_id"].Type != common.TypeSInt32 { t.Error("player_id should be TypeSInt32") } if packet.Fields["player_name"].Type != common.TypeString16 { t.Error("player_name should be TypeString16") } if packet.Fields["skin_color"].Type != common.TypeColor { t.Error("skin_color should be TypeColor") } // Check order order := packet.Orders[1] expected := []string{"player_id", "player_name", "skin_color"} if !equalSlices(order, expected) { t.Errorf("Expected order %v, got %v", expected, order) } } func TestFloat64Support(t *testing.T) { pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["FloatTest"] if packet.Fields["position_x"].Type != common.TypeFloat { t.Error("position_x should be TypeFloat") } if packet.Fields["precise_value"].Type != common.TypeDouble { t.Error("precise_value should be TypeDouble") } if packet.Fields["legacy_double"].Type != common.TypeDouble { t.Error("legacy_double should be TypeDouble") } } func TestOversizedFields(t *testing.T) { pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["OversizedTest"] if packet.Fields["small_count"].Oversized != 0 { t.Error("small_count should not be oversized") } if packet.Fields["num_words"].Oversized != 255 { t.Errorf("num_words oversized should be 255, got %d", packet.Fields["num_words"].Oversized) } if packet.Fields["large_value"].Oversized != 65535 { t.Errorf("large_value oversized should be 65535, got %d", packet.Fields["large_value"].Oversized) } } func TestType2Support(t *testing.T) { pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["Type2Test"] statValue := packet.Fields["stat_value"] if statValue.Type != common.TypeSInt32 { t.Error("stat_value primary type should be TypeSInt32") } if statValue.Type2 != common.TypeFloat { t.Error("stat_value type2 should be TypeFloat") } if statValue.Type2Cond != "stat_type!=6" { t.Errorf("Expected type2_if 'stat_type!=6', got '%s'", statValue.Type2Cond) } anotherField := packet.Fields["another_field"] if anotherField.Type2 != common.TypeSInt32 { t.Error("another_field type2 should be TypeSInt32") } if anotherField.Type2Cond != "" { t.Error("another_field should have empty type2_if") } } func TestAdvancedFieldAttributes(t *testing.T) { pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["AttributeTest"] dataArray := packet.Fields["data_array"] if dataArray.Length != 10 { t.Errorf("Expected size 10, got %d", dataArray.Length) } if dataArray.DefaultValue != 5 { t.Errorf("Expected default 5, got %d", dataArray.DefaultValue) } optionalText := packet.Fields["optional_text"] if optionalText.Condition != "var:has_text" { t.Errorf("Expected condition 'var:has_text', got '%s'", optionalText.Condition) } if !optionalText.Optional { t.Error("optional_text should be optional") } hiddenField := packet.Fields["hidden_field"] if hiddenField.AddToStruct { t.Error("hidden_field should not be added to struct") } if hiddenField.AddType != common.TypeSInt16 { t.Error("hidden_field add_type should be TypeSInt16") } } func TestArrayMaxSize(t *testing.T) { pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["ArrayMaxTest"] itemsField := packet.Fields["items"] if itemsField.MaxArraySize != 100 { t.Errorf("Expected max_size 100, got %d", itemsField.MaxArraySize) } } func TestArrayOptionalAttributes(t *testing.T) { pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["ArrayOptionalTest"] itemsField := packet.Fields["optional_items"] if !itemsField.Optional { t.Error("optional_items should be optional") } if itemsField.AddToStruct { t.Error("optional_items should not be added to struct") } } func TestMultipleVersions(t *testing.T) { pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["MultiVersion"] if packet == nil { t.Fatal("MultiVersion packet not found") } // Check both versions exist if len(packet.Orders) != 2 { t.Errorf("Expected 2 versions, got %d", len(packet.Orders)) } v1Order := packet.Orders[1] v562Order := packet.Orders[562] if len(v1Order) != 2 { t.Errorf("Version 1 should have 2 fields, got %d", len(v1Order)) } if len(v562Order) != 3 { t.Errorf("Version 562 should have 3 fields, got %d", len(v562Order)) } } func TestArrayParsing(t *testing.T) { pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["ArrayTest"] itemsField := packet.Fields["items"] if itemsField.Type != common.TypeArray { t.Error("items should be TypeArray") } if itemsField.Condition != "var:item_count" { t.Errorf("Expected condition 'var:item_count', got '%s'", itemsField.Condition) } if itemsField.SubDef == nil { t.Fatal("SubDef should not be nil") } // Check substruct fields if len(itemsField.SubDef.Fields) != 2 { t.Errorf("Expected 2 substruct fields, got %d", len(itemsField.SubDef.Fields)) } if itemsField.SubDef.Fields["item_id"].Type != common.TypeSInt32 { t.Error("item_id should be TypeSInt32") } } func TestConditionalParsing(t *testing.T) { pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["ConditionalTest"] if packet.Fields["guild_name"].Condition != "flag:has_guild" { t.Errorf("guild_name condition wrong: %s", packet.Fields["guild_name"].Condition) } if packet.Fields["enhancement"].Condition != "item_type!=0" { t.Errorf("enhancement condition wrong: %s", packet.Fields["enhancement"].Condition) } if packet.Fields["aura"].Condition != "special_flags&0x01" { t.Errorf("aura condition wrong: %s", packet.Fields["aura"].Condition) } } func TestCommaFieldNames(t *testing.T) { pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["CommaTest"] expectedFields := []string{"player_id", "account_id", "pos_x", "pos_y", "pos_z"} if len(packet.Fields) != len(expectedFields) { t.Errorf("Expected %d fields, got %d", len(expectedFields), len(packet.Fields)) } for _, field := range expectedFields { if _, exists := packet.Fields[field]; !exists { t.Errorf("Field %s not found", field) } } } func TestSubstructReference(t *testing.T) { pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["SubstructTest"] itemsField := packet.Fields["items"] if itemsField.SubDef == nil { t.Fatal("SubDef should not be nil for referenced substruct") } if len(itemsField.SubDef.Fields) != 2 { t.Errorf("Expected 2 substruct fields, got %d", len(itemsField.SubDef.Fields)) } } func TestFieldAttributes(t *testing.T) { pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["AttributeTest"] if packet.Fields["data_array"].Length != 10 { t.Errorf("Expected size 10, got %d", packet.Fields["data_array"].Length) } if packet.Fields["optional_text"].Condition != "var:has_text" { t.Errorf("Expected condition 'var:has_text', got '%s'", packet.Fields["optional_text"].Condition) } } func TestComments(t *testing.T) { pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["CommentTest"] if len(packet.Fields) != 2 { t.Errorf("Comments should not affect parsing, expected 2 fields, got %d", len(packet.Fields)) } } func TestBinaryParsingFloat64(t *testing.T) { // Test binary parsing with float64 pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } // Create test data: 8 bytes representing float64 value 123.456 testData := []byte{0x77, 0xbe, 0x9f, 0x1a, 0x2f, 0xdd, 0x5e, 0x40} // 123.456 in little-endian result, err := packets["BinaryFloat64"].Parse(testData, 1, 0) if err != nil { t.Fatalf("Binary parse failed: %v", err) } if val, ok := result["precise_value"].(float64); !ok { t.Error("precise_value should be float64") } else if val < 123.0 || val > 124.0 { // Rough check t.Errorf("Expected value around 123.456, got %f", val) } } func TestBinaryParsingOversized(t *testing.T) { // Test oversized field parsing pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } // Test data: normal 16-bit value (100), then oversized marker (255) + 16-bit value (1000) testData := []byte{0x64, 0x00, 0xff, 0xe8, 0x03} // 100, 255, 1000 result, err := packets["BinaryOversized"].Parse(testData, 1, 0) if err != nil { t.Fatalf("Binary parse failed: %v", err) } if val := result["normal_value"].(int16); val != 100 { t.Errorf("Expected normal_value 100, got %d", val) } if val := result["oversized_value"].(int16); val != 1000 { t.Errorf("Expected oversized_value 1000, got %d", val) } } func TestBinaryParsingType2(t *testing.T) { // Test type2 switching in binary parsing pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } // Test with stat_type = 6 (should use float) testData1 := []byte{0x06, 0x00, 0x00, 0x20, 0x41} // stat_type=6, float 10.0 result1, err := packets["BinaryType2"].Parse(testData1, 1, 0) if err != nil { t.Fatalf("Binary parse failed: %v", err) } if statType := result1["stat_type"].(int8); statType != 6 { t.Errorf("Expected stat_type 6, got %d", statType) } // Note: The actual type switching logic depends on conditions.go implementation // This test verifies the parsing structure is correct } func TestBinaryParsingArrayMaxSize(t *testing.T) { // Test array max size limit pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } // Test data: count=5, but max_size=2 should limit to 2 items testData := []byte{0x05, 0x01, 0x00, 0x02, 0x00} // count=5, item1=1, item2=2 result, err := packets["BinaryArrayMax"].Parse(testData, 1, 0) if err != nil { t.Fatalf("Binary parse failed: %v", err) } items := result["items"].([]map[string]any) if len(items) != 2 { t.Errorf("Expected 2 items due to max_size, got %d", len(items)) } } func TestArrayIndexConditions(t *testing.T) { // Test array index substitution in conditions pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["ArrayConditionTest"] // Verify fields were parsed correctly statsField := packet.Fields["stats"] if statsField.Type != common.TypeArray { t.Error("stats should be TypeArray") } if statsField.SubDef == nil { t.Fatal("SubDef should not be nil") } // Check that conditions were preserved modifiedValue := statsField.SubDef.Fields["modified_value"] if modifiedValue.Condition != "stat_type_%i>=1&stat_type_%i<=5" { t.Errorf("Expected 'stat_type_%%i>=1&stat_type_%%i<=5', got '%s'", modifiedValue.Condition) } percentage := statsField.SubDef.Fields["percentage"] if percentage.Condition != "stat_type_%i==6" { t.Errorf("Expected 'stat_type_%%i==6', got '%s'", percentage.Condition) } description := statsField.SubDef.Fields["description"] if description.Condition != "!var:stat_type_%i" { t.Errorf("Expected '!var:stat_type_%%i', got '%s'", description.Condition) } } func TestArrayIndexBinaryParsing(t *testing.T) { // Test that array index conditions work during binary parsing pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } // Test data: 2 stats, first with type=5, second with type=6 // stat_count=2, stat1: type=5, base=100, stat2: type=6, base=200, percentage=1.5 testData := []byte{ 0x02, // stat_count = 2 0x05, 0x64, 0x00, 0x00, 0x00, // stat 1: type=5, base=100 (no percentage) 0x06, 0xC8, 0x00, 0x00, 0x00, // stat 2: type=6, base=200 0x00, 0x00, 0xC0, 0x3F, // percentage=1.5 (float32) } result, err := packets["ArrayConditionBinary"].Parse(testData, 1, 0) if err != nil { t.Fatalf("Binary parse failed: %v", err) } stats := result["stats"].([]map[string]any) if len(stats) != 2 { t.Fatalf("Expected 2 stats, got %d", len(stats)) } // First stat (type=5) should not have percentage field stat1 := stats[0] if stat1["stat_type"].(int8) != 5 { t.Errorf("Expected stat_type 5, got %d", stat1["stat_type"]) } if _, hasPercentage := stat1["percentage"]; hasPercentage { t.Error("Stat type 5 should not have percentage field") } // Second stat (type=6) should have percentage field stat2 := stats[1] if stat2["stat_type"].(int8) != 6 { t.Errorf("Expected stat_type 6, got %d", stat2["stat_type"]) } if percentage, hasPercentage := stat2["percentage"]; !hasPercentage { t.Error("Stat type 6 should have percentage field") } else if percentage.(float32) < 1.4 || percentage.(float32) > 1.6 { t.Errorf("Expected percentage around 1.5, got %f", percentage) } } func TestComplexArrayConditions(t *testing.T) { // Test complex conditions with array indices pml := ` ` packets, err := Parse(pml) if err != nil { t.Fatalf("Parse failed: %v", err) } packet := packets["ComplexArrayTest"] itemsField := packet.Fields["items"] enhancement := itemsField.SubDef.Fields["enhancement"] if enhancement.Condition != "item_type_%i!=0&item_flags_%i&0x01" { t.Errorf("Enhancement condition wrong: %s", enhancement.Condition) } specialColor := itemsField.SubDef.Fields["special_color"] if specialColor.Condition != "item_type_%i>=100,item_flags_%i&0x02" { t.Errorf("Special color condition wrong: %s", specialColor.Condition) } } func TestErrorHandling(t *testing.T) { testCases := []struct { name string pml string }{ {"Unclosed tag", ""}, {"Invalid XML", ""}, {"Missing quotes", ""}, {"Invalid oversized", ""}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { _, err := Parse(tc.pml) if err == nil { t.Error("Expected error but got none") } }) } } func TestTemplateDefinitionAndUsage(t *testing.T) { pml := `