347 lines
9.5 KiB
Go
347 lines
9.5 KiB
Go
package processor
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"cook/utils"
|
|
)
|
|
|
|
// TestRealWorldGameXML tests with game-like XML structure
|
|
func TestRealWorldGameXML(t *testing.T) {
|
|
original := `<?xml version="1.0" encoding="utf-8"?>
|
|
<Items>
|
|
<Item name="Fiber" identifier="Item_Fiber" category="Resource">
|
|
<Icon texture="Items/Fiber.png" />
|
|
<Weight value="0.01" />
|
|
<MaxStack value="1000" />
|
|
<Description text="Soft plant fibers useful for crafting." />
|
|
</Item>
|
|
<Item name="Wood" identifier="Item_Wood" category="Resource">
|
|
<Icon texture="Items/Wood.png" />
|
|
<Weight value="0.05" />
|
|
<MaxStack value="500" />
|
|
<Description text="Basic building material." />
|
|
</Item>
|
|
</Items>`
|
|
|
|
// Parse
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse: %v", err)
|
|
}
|
|
|
|
// Modify: Double all MaxStack values and change Wood weight
|
|
modElem := deepCopyXMLElement(origElem)
|
|
|
|
// Fiber MaxStack: 1000 → 2000
|
|
fiberItem := modElem.Children[0]
|
|
fiberMaxStack := fiberItem.Children[2]
|
|
valueAttr := fiberMaxStack.Attributes["value"]
|
|
valueAttr.Value = "2000"
|
|
fiberMaxStack.Attributes["value"] = valueAttr
|
|
|
|
// Wood MaxStack: 500 → 1000
|
|
woodItem := modElem.Children[1]
|
|
woodMaxStack := woodItem.Children[2]
|
|
valueAttr2 := woodMaxStack.Attributes["value"]
|
|
valueAttr2.Value = "1000"
|
|
woodMaxStack.Attributes["value"] = valueAttr2
|
|
|
|
// Wood Weight: 0.05 → 0.10
|
|
woodWeight := woodItem.Children[1]
|
|
weightAttr := woodWeight.Attributes["value"]
|
|
weightAttr.Value = "0.10"
|
|
woodWeight.Attributes["value"] = weightAttr
|
|
|
|
// Generate changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
|
|
if len(changes) != 3 {
|
|
t.Fatalf("Expected 3 changes, got %d", len(changes))
|
|
}
|
|
|
|
// Apply
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
// Verify changes
|
|
if !strings.Contains(result, `<MaxStack value="2000"`) {
|
|
t.Errorf("Failed to update Fiber MaxStack")
|
|
}
|
|
if !strings.Contains(result, `<MaxStack value="1000"`) {
|
|
t.Errorf("Failed to update Wood MaxStack")
|
|
}
|
|
if !strings.Contains(result, `<Weight value="0.10"`) {
|
|
t.Errorf("Failed to update Wood Weight")
|
|
}
|
|
|
|
// Verify formatting preserved (check XML declaration and indentation)
|
|
if !strings.HasPrefix(result, `<?xml version="1.0" encoding="utf-8"?>`) {
|
|
t.Errorf("XML declaration not preserved")
|
|
}
|
|
if !strings.Contains(result, "\n <Item") {
|
|
t.Errorf("Indentation not preserved")
|
|
}
|
|
}
|
|
|
|
// TestAddRemoveMultipleChildren tests adding and removing multiple elements
|
|
func TestAddRemoveMultipleChildren(t *testing.T) {
|
|
original := `<inventory>
|
|
<item name="sword" />
|
|
<item name="shield" />
|
|
<item name="potion" />
|
|
<item name="scroll" />
|
|
</inventory>`
|
|
|
|
// Parse
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse: %v", err)
|
|
}
|
|
|
|
// Remove middle two items, add a new one
|
|
modElem := deepCopyXMLElement(origElem)
|
|
|
|
// Remove shield and potion (indices 1 and 2)
|
|
modElem.Children = []*XMLElement{
|
|
modElem.Children[0], // sword
|
|
modElem.Children[3], // scroll
|
|
}
|
|
|
|
// Add a new item
|
|
newItem := &XMLElement{
|
|
Tag: "item",
|
|
Attributes: map[string]XMLAttribute{
|
|
"name": {Value: "helmet"},
|
|
},
|
|
Children: []*XMLElement{},
|
|
}
|
|
modElem.Children = append(modElem.Children, newItem)
|
|
|
|
// Generate changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
|
|
// The algorithm compares by matching indices:
|
|
// orig[0]=sword vs mod[0]=sword (no change)
|
|
// orig[1]=shield vs mod[1]=scroll (treated as replace - shows as attribute changes)
|
|
// orig[2]=potion vs mod[2]=helmet (treated as replace)
|
|
// orig[3]=scroll (removed)
|
|
// This is fine - the actual edits will be correct
|
|
|
|
if len(changes) == 0 {
|
|
t.Fatalf("Expected changes, got none")
|
|
}
|
|
|
|
// Apply
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
// Verify
|
|
if strings.Contains(result, `name="shield"`) {
|
|
t.Errorf("Shield not removed")
|
|
}
|
|
if strings.Contains(result, `name="potion"`) {
|
|
t.Errorf("Potion not removed")
|
|
}
|
|
if !strings.Contains(result, `name="sword"`) {
|
|
t.Errorf("Sword incorrectly removed")
|
|
}
|
|
if !strings.Contains(result, `name="scroll"`) {
|
|
t.Errorf("Scroll incorrectly removed")
|
|
}
|
|
if !strings.Contains(result, `name="helmet"`) {
|
|
t.Errorf("Helmet not added")
|
|
}
|
|
}
|
|
|
|
// TestModifyAttributesAndText tests changing both attributes and text content
|
|
func TestModifyAttributesAndText(t *testing.T) {
|
|
original := `<weapon>
|
|
<item type="sword" damage="10">Iron Sword</item>
|
|
<item type="axe" damage="15">Battle Axe</item>
|
|
</weapon>`
|
|
|
|
// Parse
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse: %v", err)
|
|
}
|
|
|
|
// Modify both items
|
|
modElem := deepCopyXMLElement(origElem)
|
|
|
|
// First item: change damage and text
|
|
item1 := modElem.Children[0]
|
|
dmgAttr := item1.Attributes["damage"]
|
|
dmgAttr.Value = "20"
|
|
item1.Attributes["damage"] = dmgAttr
|
|
item1.Text = "Steel Sword"
|
|
|
|
// Second item: change damage and type
|
|
item2 := modElem.Children[1]
|
|
dmgAttr2 := item2.Attributes["damage"]
|
|
dmgAttr2.Value = "30"
|
|
item2.Attributes["damage"] = dmgAttr2
|
|
typeAttr := item2.Attributes["type"]
|
|
typeAttr.Value = "greataxe"
|
|
item2.Attributes["type"] = typeAttr
|
|
|
|
// Generate and apply changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
// Verify
|
|
if !strings.Contains(result, `damage="20"`) {
|
|
t.Errorf("First item damage not updated")
|
|
}
|
|
if !strings.Contains(result, "Steel Sword") {
|
|
t.Errorf("First item text not updated")
|
|
}
|
|
if !strings.Contains(result, `damage="30"`) {
|
|
t.Errorf("Second item damage not updated")
|
|
}
|
|
if !strings.Contains(result, `type="greataxe"`) {
|
|
t.Errorf("Second item type not updated")
|
|
}
|
|
if strings.Contains(result, "Iron Sword") {
|
|
t.Errorf("Old text still present")
|
|
}
|
|
}
|
|
|
|
// TestSelfClosingTagPreservation tests that self-closing tags work correctly
|
|
func TestSelfClosingTagPreservation(t *testing.T) {
|
|
original := `<root>
|
|
<item name="test" />
|
|
<empty></empty>
|
|
</root>`
|
|
|
|
// Parse
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse: %v", err)
|
|
}
|
|
|
|
// Modify first item's attribute
|
|
modElem := deepCopyXMLElement(origElem)
|
|
item := modElem.Children[0]
|
|
nameAttr := item.Attributes["name"]
|
|
nameAttr.Value = "modified"
|
|
item.Attributes["name"] = nameAttr
|
|
|
|
// Generate and apply changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
// Verify the change was made
|
|
if !strings.Contains(result, `name="modified"`) {
|
|
t.Errorf("Attribute not updated: %s", result)
|
|
}
|
|
}
|
|
|
|
// TestNumericAttributeModification tests numeric attribute changes
|
|
func TestNumericAttributeModification(t *testing.T) {
|
|
original := `<stats health="100" mana="50" stamina="75.5" />`
|
|
|
|
// Parse
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse: %v", err)
|
|
}
|
|
|
|
// Double all numeric values
|
|
modElem := deepCopyXMLElement(origElem)
|
|
|
|
// Helper to modify numeric attributes
|
|
modifyNumericAttr := func(attrName string, multiplier float64) {
|
|
if attr, exists := modElem.Attributes[attrName]; exists {
|
|
if val, ok := parseNumeric(attr.Value); ok {
|
|
attr.Value = formatNumeric(val * multiplier)
|
|
modElem.Attributes[attrName] = attr
|
|
}
|
|
}
|
|
}
|
|
|
|
modifyNumericAttr("health", 2.0)
|
|
modifyNumericAttr("mana", 2.0)
|
|
modifyNumericAttr("stamina", 2.0)
|
|
|
|
// Generate and apply changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
|
|
if len(changes) != 3 {
|
|
t.Fatalf("Expected 3 changes, got %d", len(changes))
|
|
}
|
|
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
// Verify numeric changes
|
|
if !strings.Contains(result, `health="200"`) {
|
|
t.Errorf("Health not doubled: %s", result)
|
|
}
|
|
if !strings.Contains(result, `mana="100"`) {
|
|
t.Errorf("Mana not doubled: %s", result)
|
|
}
|
|
if !strings.Contains(result, `stamina="151"`) {
|
|
t.Errorf("Stamina not doubled: %s", result)
|
|
}
|
|
}
|
|
|
|
// TestMinimalGitDiff verifies that only changed parts are modified
|
|
func TestMinimalGitDiff(t *testing.T) {
|
|
original := `<config>
|
|
<setting name="volume" value="50" />
|
|
<setting name="brightness" value="75" />
|
|
<setting name="contrast" value="100" />
|
|
</config>`
|
|
|
|
// Parse
|
|
origElem, err := parseXMLWithPositions(original)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse: %v", err)
|
|
}
|
|
|
|
// Change only brightness
|
|
modElem := deepCopyXMLElement(origElem)
|
|
brightnessItem := modElem.Children[1]
|
|
valueAttr := brightnessItem.Attributes["value"]
|
|
valueAttr.Value = "90"
|
|
brightnessItem.Attributes["value"] = valueAttr
|
|
|
|
// Generate changes
|
|
changes := findXMLChanges(origElem, modElem, "")
|
|
|
|
// Should be exactly 1 change
|
|
if len(changes) != 1 {
|
|
t.Fatalf("Expected exactly 1 change for minimal diff, got %d", len(changes))
|
|
}
|
|
|
|
if changes[0].OldValue != "75" || changes[0].NewValue != "90" {
|
|
t.Errorf("Wrong change detected: %v", changes[0])
|
|
}
|
|
|
|
// Apply
|
|
commands := applyXMLChanges(changes)
|
|
result, _ := utils.ExecuteModifications(commands, original)
|
|
|
|
// Calculate diff size (rough approximation)
|
|
diffChars := len(changes[0].OldValue) + len(changes[0].NewValue)
|
|
if diffChars > 10 {
|
|
t.Errorf("Diff too large: %d characters changed (expected < 10)", diffChars)
|
|
}
|
|
|
|
// Verify only brightness changed
|
|
if !strings.Contains(result, `value="50"`) {
|
|
t.Errorf("Volume incorrectly modified")
|
|
}
|
|
if !strings.Contains(result, `value="90"`) {
|
|
t.Errorf("Brightness not modified")
|
|
}
|
|
if !strings.Contains(result, `value="100"`) {
|
|
t.Errorf("Contrast incorrectly modified")
|
|
}
|
|
}
|