224 lines
6.5 KiB
Python
224 lines
6.5 KiB
Python
|
|
"""
|
||
|
|
Test the improved anchor matching logic.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import sys
|
||
|
|
import pathlib
|
||
|
|
import importlib.util
|
||
|
|
import types
|
||
|
|
|
||
|
|
# add server src to path and load modules
|
||
|
|
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
||
|
|
SRC = ROOT / "UnityMcpBridge" / "UnityMcpServer~" / "src"
|
||
|
|
sys.path.insert(0, str(SRC))
|
||
|
|
|
||
|
|
# stub mcp.server.fastmcp
|
||
|
|
mcp_pkg = types.ModuleType("mcp")
|
||
|
|
server_pkg = types.ModuleType("mcp.server")
|
||
|
|
fastmcp_pkg = types.ModuleType("mcp.server.fastmcp")
|
||
|
|
|
||
|
|
class _Dummy:
|
||
|
|
pass
|
||
|
|
|
||
|
|
fastmcp_pkg.FastMCP = _Dummy
|
||
|
|
fastmcp_pkg.Context = _Dummy
|
||
|
|
server_pkg.fastmcp = fastmcp_pkg
|
||
|
|
mcp_pkg.server = server_pkg
|
||
|
|
sys.modules.setdefault("mcp", mcp_pkg)
|
||
|
|
sys.modules.setdefault("mcp.server", server_pkg)
|
||
|
|
sys.modules.setdefault("mcp.server.fastmcp", fastmcp_pkg)
|
||
|
|
|
||
|
|
def load_module(path, name):
|
||
|
|
spec = importlib.util.spec_from_file_location(name, path)
|
||
|
|
module = importlib.util.module_from_spec(spec)
|
||
|
|
spec.loader.exec_module(module)
|
||
|
|
return module
|
||
|
|
|
||
|
|
manage_script_edits_module = load_module(SRC / "tools" / "manage_script_edits.py", "manage_script_edits_module")
|
||
|
|
|
||
|
|
def test_improved_anchor_matching():
|
||
|
|
"""Test that our improved anchor matching finds the right closing brace."""
|
||
|
|
|
||
|
|
test_code = '''using UnityEngine;
|
||
|
|
|
||
|
|
public class TestClass : MonoBehaviour
|
||
|
|
{
|
||
|
|
void Start()
|
||
|
|
{
|
||
|
|
Debug.Log("test");
|
||
|
|
}
|
||
|
|
|
||
|
|
void Update()
|
||
|
|
{
|
||
|
|
// Update logic
|
||
|
|
}
|
||
|
|
}'''
|
||
|
|
|
||
|
|
import re
|
||
|
|
|
||
|
|
# Test the problematic anchor pattern
|
||
|
|
anchor_pattern = r"\s*}\s*$"
|
||
|
|
flags = re.MULTILINE
|
||
|
|
|
||
|
|
# Test our improved function
|
||
|
|
best_match = manage_script_edits_module._find_best_anchor_match(
|
||
|
|
anchor_pattern, test_code, flags, prefer_last=True
|
||
|
|
)
|
||
|
|
|
||
|
|
if best_match:
|
||
|
|
match_pos = best_match.start()
|
||
|
|
|
||
|
|
# Get line number
|
||
|
|
lines_before = test_code[:match_pos].count('\n')
|
||
|
|
line_num = lines_before + 1
|
||
|
|
|
||
|
|
print(f"Improved matching chose position {match_pos} on line {line_num}")
|
||
|
|
|
||
|
|
# Show context
|
||
|
|
before_context = test_code[max(0, match_pos-50):match_pos]
|
||
|
|
after_context = test_code[match_pos:match_pos+20]
|
||
|
|
print(f"Context: ...{before_context}|MATCH|{after_context}...")
|
||
|
|
|
||
|
|
# Check if this is closer to the end (should be line 13 or 14, not line 7)
|
||
|
|
total_lines = test_code.count('\n') + 1
|
||
|
|
print(f"Total lines: {total_lines}")
|
||
|
|
|
||
|
|
if line_num >= total_lines - 2: # Within last 2 lines
|
||
|
|
print("✅ SUCCESS: Improved matching found class-ending brace!")
|
||
|
|
return True
|
||
|
|
else:
|
||
|
|
print("❌ FAIL: Still matching early in file")
|
||
|
|
return False
|
||
|
|
else:
|
||
|
|
print("❌ FAIL: No match found")
|
||
|
|
return False
|
||
|
|
|
||
|
|
def test_old_vs_new_matching():
|
||
|
|
"""Compare old vs new matching behavior."""
|
||
|
|
|
||
|
|
test_code = '''using UnityEngine;
|
||
|
|
|
||
|
|
public class TestClass : MonoBehaviour
|
||
|
|
{
|
||
|
|
void Start()
|
||
|
|
{
|
||
|
|
Debug.Log("test");
|
||
|
|
}
|
||
|
|
|
||
|
|
void Update()
|
||
|
|
{
|
||
|
|
if (condition)
|
||
|
|
{
|
||
|
|
DoSomething();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void LateUpdate()
|
||
|
|
{
|
||
|
|
// More logic
|
||
|
|
}
|
||
|
|
}'''
|
||
|
|
|
||
|
|
import re
|
||
|
|
|
||
|
|
anchor_pattern = r"\s*}\s*$"
|
||
|
|
flags = re.MULTILINE
|
||
|
|
|
||
|
|
# Old behavior (first match)
|
||
|
|
old_match = re.search(anchor_pattern, test_code, flags)
|
||
|
|
old_line = test_code[:old_match.start()].count('\n') + 1 if old_match else None
|
||
|
|
|
||
|
|
# New behavior (improved matching)
|
||
|
|
new_match = manage_script_edits_module._find_best_anchor_match(
|
||
|
|
anchor_pattern, test_code, flags, prefer_last=True
|
||
|
|
)
|
||
|
|
new_line = test_code[:new_match.start()].count('\n') + 1 if new_match else None
|
||
|
|
|
||
|
|
print(f"Old matching (first): Line {old_line}")
|
||
|
|
print(f"New matching (improved): Line {new_line}")
|
||
|
|
|
||
|
|
total_lines = test_code.count('\n') + 1
|
||
|
|
print(f"Total lines: {total_lines}")
|
||
|
|
|
||
|
|
# The new approach should choose a line much closer to the end
|
||
|
|
if new_line and old_line and new_line > old_line:
|
||
|
|
print("✅ SUCCESS: New matching chooses a later line!")
|
||
|
|
|
||
|
|
# Verify it's actually the class end, not just a later method
|
||
|
|
if new_line >= total_lines - 2:
|
||
|
|
print("✅ EXCELLENT: New matching found the actual class end!")
|
||
|
|
return True
|
||
|
|
else:
|
||
|
|
print("⚠️ PARTIAL: Better than before, but might still be a method end")
|
||
|
|
return True
|
||
|
|
else:
|
||
|
|
print("❌ FAIL: New matching didn't improve")
|
||
|
|
return False
|
||
|
|
|
||
|
|
def test_apply_edits_with_improved_matching():
|
||
|
|
"""Test that _apply_edits_locally uses improved matching."""
|
||
|
|
|
||
|
|
original_code = '''using UnityEngine;
|
||
|
|
|
||
|
|
public class TestClass : MonoBehaviour
|
||
|
|
{
|
||
|
|
public string message = "Hello World";
|
||
|
|
|
||
|
|
void Start()
|
||
|
|
{
|
||
|
|
Debug.Log(message);
|
||
|
|
}
|
||
|
|
}'''
|
||
|
|
|
||
|
|
# Test anchor_insert with the problematic pattern
|
||
|
|
edits = [{
|
||
|
|
"op": "anchor_insert",
|
||
|
|
"anchor": r"\s*}\s*$", # This should now find the class end
|
||
|
|
"position": "before",
|
||
|
|
"text": "\n public void NewMethod() { Debug.Log(\"Added at class end\"); }\n"
|
||
|
|
}]
|
||
|
|
|
||
|
|
try:
|
||
|
|
result = manage_script_edits_module._apply_edits_locally(original_code, edits)
|
||
|
|
|
||
|
|
# Check where the new method was inserted
|
||
|
|
lines = result.split('\n')
|
||
|
|
for i, line in enumerate(lines):
|
||
|
|
if "NewMethod" in line:
|
||
|
|
print(f"NewMethod inserted at line {i+1}: {line.strip()}")
|
||
|
|
|
||
|
|
# Verify it's near the end, not in the middle
|
||
|
|
total_lines = len(lines)
|
||
|
|
if i >= total_lines - 5: # Within last 5 lines
|
||
|
|
print("✅ SUCCESS: Method inserted near class end!")
|
||
|
|
return True
|
||
|
|
else:
|
||
|
|
print("❌ FAIL: Method inserted too early in file")
|
||
|
|
return False
|
||
|
|
|
||
|
|
print("❌ FAIL: NewMethod not found in result")
|
||
|
|
return False
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
print(f"❌ ERROR: {e}")
|
||
|
|
return False
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
print("Testing improved anchor matching...")
|
||
|
|
print("="*60)
|
||
|
|
|
||
|
|
success1 = test_improved_anchor_matching()
|
||
|
|
|
||
|
|
print("\n" + "="*60)
|
||
|
|
print("Comparing old vs new behavior...")
|
||
|
|
success2 = test_old_vs_new_matching()
|
||
|
|
|
||
|
|
print("\n" + "="*60)
|
||
|
|
print("Testing _apply_edits_locally with improved matching...")
|
||
|
|
success3 = test_apply_edits_with_improved_matching()
|
||
|
|
|
||
|
|
print("\n" + "="*60)
|
||
|
|
if success1 and success2 and success3:
|
||
|
|
print("🎉 ALL TESTS PASSED! Improved anchor matching is working!")
|
||
|
|
else:
|
||
|
|
print("💥 Some tests failed. Need more work on anchor matching.")
|