Positions are always mcrfpy.Vector, Vector/tuple/iterables expected as inputs, and for position-only inputs we permit x,y args to prevent requiring double-parens
This commit is contained in:
parent
016ca693b5
commit
d2e4791f5a
25 changed files with 2109 additions and 636 deletions
152
tests/unit/automation_vector_test.py
Normal file
152
tests/unit/automation_vector_test.py
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
"""Test automation module with new position parsing and Vector returns"""
|
||||
import mcrfpy
|
||||
from mcrfpy import automation
|
||||
import sys
|
||||
|
||||
# Track test results
|
||||
passed = 0
|
||||
failed = 0
|
||||
|
||||
def test(name, condition):
|
||||
global passed, failed
|
||||
if condition:
|
||||
print(f" PASS: {name}")
|
||||
passed += 1
|
||||
else:
|
||||
print(f" FAIL: {name}")
|
||||
failed += 1
|
||||
|
||||
print("Testing automation module updates...")
|
||||
print()
|
||||
|
||||
# Test 1: position() returns Vector
|
||||
print("1. Testing position() returns Vector...")
|
||||
pos = automation.position()
|
||||
test("position() returns Vector type", type(pos).__name__ == "Vector")
|
||||
test("position has x attribute", hasattr(pos, 'x'))
|
||||
test("position has y attribute", hasattr(pos, 'y'))
|
||||
print()
|
||||
|
||||
# Test 2: size() returns Vector
|
||||
print("2. Testing size() returns Vector...")
|
||||
sz = automation.size()
|
||||
test("size() returns Vector type", type(sz).__name__ == "Vector")
|
||||
test("size has x attribute", hasattr(sz, 'x'))
|
||||
test("size has y attribute", hasattr(sz, 'y'))
|
||||
test("size.x > 0", sz.x > 0)
|
||||
test("size.y > 0", sz.y > 0)
|
||||
print()
|
||||
|
||||
# Test 3: onScreen() accepts various position formats
|
||||
print("3. Testing onScreen() with various position formats...")
|
||||
# Move mouse to a known position first
|
||||
automation.moveTo((100, 100))
|
||||
test("onScreen((100, 100)) with tuple", automation.onScreen((100, 100)) == True)
|
||||
test("onScreen([50, 50]) with list", automation.onScreen([50, 50]) == True)
|
||||
test("onScreen(mcrfpy.Vector(200, 200)) with Vector", automation.onScreen(mcrfpy.Vector(200, 200)) == True)
|
||||
# Should be off-screen (negative)
|
||||
test("onScreen((-10, -10)) returns False", automation.onScreen((-10, -10)) == False)
|
||||
print()
|
||||
|
||||
# Test 4: moveTo() accepts position as grouped argument
|
||||
print("4. Testing moveTo() with grouped position...")
|
||||
automation.moveTo((150, 150))
|
||||
pos = automation.position()
|
||||
test("moveTo((150, 150)) moves to correct x", int(pos.x) == 150)
|
||||
test("moveTo((150, 150)) moves to correct y", int(pos.y) == 150)
|
||||
|
||||
automation.moveTo([200, 200])
|
||||
pos = automation.position()
|
||||
test("moveTo([200, 200]) with list", int(pos.x) == 200 and int(pos.y) == 200)
|
||||
|
||||
automation.moveTo(mcrfpy.Vector(250, 250))
|
||||
pos = automation.position()
|
||||
test("moveTo(Vector(250, 250)) with Vector", int(pos.x) == 250 and int(pos.y) == 250)
|
||||
print()
|
||||
|
||||
# Test 5: moveRel() accepts offset as grouped argument
|
||||
print("5. Testing moveRel() with grouped offset...")
|
||||
automation.moveTo((100, 100)) # Start position
|
||||
automation.moveRel((50, 50)) # Relative move
|
||||
pos = automation.position()
|
||||
test("moveRel((50, 50)) from (100, 100)", int(pos.x) == 150 and int(pos.y) == 150)
|
||||
print()
|
||||
|
||||
# Test 6: click() accepts optional position as grouped argument
|
||||
print("6. Testing click() with grouped position...")
|
||||
# Click at current position (no args should work)
|
||||
try:
|
||||
automation.click()
|
||||
test("click() with no args (current position)", True)
|
||||
except:
|
||||
test("click() with no args (current position)", False)
|
||||
|
||||
try:
|
||||
automation.click((200, 200))
|
||||
test("click((200, 200)) with tuple", True)
|
||||
except:
|
||||
test("click((200, 200)) with tuple", False)
|
||||
|
||||
try:
|
||||
automation.click([300, 300], clicks=2)
|
||||
test("click([300, 300], clicks=2) with list", True)
|
||||
except:
|
||||
test("click([300, 300], clicks=2) with list", False)
|
||||
print()
|
||||
|
||||
# Test 7: scroll() accepts position as second grouped argument
|
||||
print("7. Testing scroll() with grouped position...")
|
||||
try:
|
||||
automation.scroll(3) # No position - use current
|
||||
test("scroll(3) without position", True)
|
||||
except:
|
||||
test("scroll(3) without position", False)
|
||||
|
||||
try:
|
||||
automation.scroll(3, (100, 100))
|
||||
test("scroll(3, (100, 100)) with tuple", True)
|
||||
except:
|
||||
test("scroll(3, (100, 100)) with tuple", False)
|
||||
print()
|
||||
|
||||
# Test 8: mouseDown/mouseUp with grouped position
|
||||
print("8. Testing mouseDown/mouseUp with grouped position...")
|
||||
try:
|
||||
automation.mouseDown((100, 100))
|
||||
automation.mouseUp((100, 100))
|
||||
test("mouseDown/mouseUp((100, 100)) with tuple", True)
|
||||
except:
|
||||
test("mouseDown/mouseUp((100, 100)) with tuple", False)
|
||||
print()
|
||||
|
||||
# Test 9: dragTo() with grouped position
|
||||
print("9. Testing dragTo() with grouped position...")
|
||||
automation.moveTo((100, 100))
|
||||
try:
|
||||
automation.dragTo((200, 200))
|
||||
test("dragTo((200, 200)) with tuple", True)
|
||||
except Exception as e:
|
||||
print(f" Error: {e}")
|
||||
test("dragTo((200, 200)) with tuple", False)
|
||||
print()
|
||||
|
||||
# Test 10: dragRel() with grouped offset
|
||||
print("10. Testing dragRel() with grouped offset...")
|
||||
automation.moveTo((100, 100))
|
||||
try:
|
||||
automation.dragRel((50, 50))
|
||||
test("dragRel((50, 50)) with tuple", True)
|
||||
except Exception as e:
|
||||
print(f" Error: {e}")
|
||||
test("dragRel((50, 50)) with tuple", False)
|
||||
print()
|
||||
|
||||
# Summary
|
||||
print("=" * 40)
|
||||
print(f"Results: {passed} passed, {failed} failed")
|
||||
if failed == 0:
|
||||
print("All tests passed!")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("Some tests failed")
|
||||
sys.exit(1)
|
||||
129
tests/unit/test_drawable_move_resize_position_parsing.py
Normal file
129
tests/unit/test_drawable_move_resize_position_parsing.py
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
"""Test that Drawable.move() and Drawable.resize() accept flexible position arguments."""
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def run_tests():
|
||||
"""Test the new position parsing for move() and resize()."""
|
||||
errors = []
|
||||
|
||||
# Create a test scene
|
||||
scene = mcrfpy.Scene("test_drawable_methods")
|
||||
|
||||
# Create a Frame to test with (since Drawable is abstract)
|
||||
frame = mcrfpy.Frame(pos=(100, 100), size=(50, 50))
|
||||
scene.children.append(frame)
|
||||
|
||||
# Test 1: move() with two separate arguments (original behavior)
|
||||
try:
|
||||
frame.x = 100
|
||||
frame.y = 100
|
||||
frame.move(10, 20)
|
||||
if not (frame.x == 110 and frame.y == 120):
|
||||
errors.append(f"move(10, 20) failed: got ({frame.x}, {frame.y}), expected (110, 120)")
|
||||
else:
|
||||
print("PASS: move(dx, dy) with two arguments works")
|
||||
except Exception as e:
|
||||
errors.append(f"move(10, 20) raised: {e}")
|
||||
|
||||
# Test 2: move() with a tuple
|
||||
try:
|
||||
frame.x = 100
|
||||
frame.y = 100
|
||||
frame.move((15, 25))
|
||||
if not (frame.x == 115 and frame.y == 125):
|
||||
errors.append(f"move((15, 25)) failed: got ({frame.x}, {frame.y}), expected (115, 125)")
|
||||
else:
|
||||
print("PASS: move((dx, dy)) with tuple works")
|
||||
except Exception as e:
|
||||
errors.append(f"move((15, 25)) raised: {e}")
|
||||
|
||||
# Test 3: move() with a list
|
||||
try:
|
||||
frame.x = 100
|
||||
frame.y = 100
|
||||
frame.move([5, 10])
|
||||
if not (frame.x == 105 and frame.y == 110):
|
||||
errors.append(f"move([5, 10]) failed: got ({frame.x}, {frame.y}), expected (105, 110)")
|
||||
else:
|
||||
print("PASS: move([dx, dy]) with list works")
|
||||
except Exception as e:
|
||||
errors.append(f"move([5, 10]) raised: {e}")
|
||||
|
||||
# Test 4: move() with a Vector
|
||||
try:
|
||||
frame.x = 100
|
||||
frame.y = 100
|
||||
vec = mcrfpy.Vector(12, 18)
|
||||
frame.move(vec)
|
||||
if not (frame.x == 112 and frame.y == 118):
|
||||
errors.append(f"move(Vector(12, 18)) failed: got ({frame.x}, {frame.y}), expected (112, 118)")
|
||||
else:
|
||||
print("PASS: move(Vector) works")
|
||||
except Exception as e:
|
||||
errors.append(f"move(Vector) raised: {e}")
|
||||
|
||||
# Test 5: resize() with two separate arguments (original behavior)
|
||||
try:
|
||||
frame.resize(200, 150)
|
||||
if not (frame.w == 200 and frame.h == 150):
|
||||
errors.append(f"resize(200, 150) failed: got ({frame.w}, {frame.h}), expected (200, 150)")
|
||||
else:
|
||||
print("PASS: resize(w, h) with two arguments works")
|
||||
except Exception as e:
|
||||
errors.append(f"resize(200, 150) raised: {e}")
|
||||
|
||||
# Test 6: resize() with a tuple
|
||||
try:
|
||||
frame.resize((180, 120))
|
||||
if not (frame.w == 180 and frame.h == 120):
|
||||
errors.append(f"resize((180, 120)) failed: got ({frame.w}, {frame.h}), expected (180, 120)")
|
||||
else:
|
||||
print("PASS: resize((w, h)) with tuple works")
|
||||
except Exception as e:
|
||||
errors.append(f"resize((180, 120)) raised: {e}")
|
||||
|
||||
# Test 7: resize() with a list
|
||||
try:
|
||||
frame.resize([100, 80])
|
||||
if not (frame.w == 100 and frame.h == 80):
|
||||
errors.append(f"resize([100, 80]) failed: got ({frame.w}, {frame.h}), expected (100, 80)")
|
||||
else:
|
||||
print("PASS: resize([w, h]) with list works")
|
||||
except Exception as e:
|
||||
errors.append(f"resize([100, 80]) raised: {e}")
|
||||
|
||||
# Test 8: resize() with a Vector
|
||||
try:
|
||||
vec = mcrfpy.Vector(250, 200)
|
||||
frame.resize(vec)
|
||||
if not (frame.w == 250 and frame.h == 200):
|
||||
errors.append(f"resize(Vector(250, 200)) failed: got ({frame.w}, {frame.h}), expected (250, 200)")
|
||||
else:
|
||||
print("PASS: resize(Vector) works")
|
||||
except Exception as e:
|
||||
errors.append(f"resize(Vector) raised: {e}")
|
||||
|
||||
# Test 9: move() with keyword argument pos
|
||||
try:
|
||||
frame.x = 100
|
||||
frame.y = 100
|
||||
frame.move(pos=(7, 13))
|
||||
if not (frame.x == 107 and frame.y == 113):
|
||||
errors.append(f"move(pos=(7, 13)) failed: got ({frame.x}, {frame.y}), expected (107, 113)")
|
||||
else:
|
||||
print("PASS: move(pos=(dx, dy)) with keyword works")
|
||||
except Exception as e:
|
||||
errors.append(f"move(pos=(7, 13)) raised: {e}")
|
||||
|
||||
# Summary
|
||||
if errors:
|
||||
print("\nFAILURES:")
|
||||
for e in errors:
|
||||
print(f" - {e}")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("\nAll tests passed!")
|
||||
sys.exit(0)
|
||||
|
||||
# Run tests
|
||||
run_tests()
|
||||
135
tests/unit/test_entity_position_parsing.py
Normal file
135
tests/unit/test_entity_position_parsing.py
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
"""Test Entity.at() and Entity.path_to() position argument parsing.
|
||||
|
||||
These methods should accept:
|
||||
- Two separate integers: method(x, y)
|
||||
- A tuple: method((x, y))
|
||||
- Keyword arguments: method(x=x, y=y) or method(pos=(x, y))
|
||||
- A Vector: method(Vector(x, y))
|
||||
"""
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def run_tests():
|
||||
# Create a grid with some walkable cells
|
||||
grid = mcrfpy.Grid(grid_size=(10, 10), pos=(0, 0), size=(320, 320))
|
||||
|
||||
# Make the grid walkable
|
||||
for x in range(10):
|
||||
for y in range(10):
|
||||
grid.at(x, y).walkable = True
|
||||
|
||||
# Create an entity at (2, 2)
|
||||
entity = mcrfpy.Entity(grid_pos=(2, 2), grid=grid)
|
||||
|
||||
print("Testing Entity.at() position parsing...")
|
||||
|
||||
# Test 1: Two separate integers
|
||||
try:
|
||||
state1 = entity.at(3, 3)
|
||||
print(" PASS: entity.at(3, 3)")
|
||||
except Exception as e:
|
||||
print(f" FAIL: entity.at(3, 3) - {e}")
|
||||
return False
|
||||
|
||||
# Test 2: Tuple argument
|
||||
try:
|
||||
state2 = entity.at((4, 4))
|
||||
print(" PASS: entity.at((4, 4))")
|
||||
except Exception as e:
|
||||
print(f" FAIL: entity.at((4, 4)) - {e}")
|
||||
return False
|
||||
|
||||
# Test 3: Keyword arguments
|
||||
try:
|
||||
state3 = entity.at(x=5, y=5)
|
||||
print(" PASS: entity.at(x=5, y=5)")
|
||||
except Exception as e:
|
||||
print(f" FAIL: entity.at(x=5, y=5) - {e}")
|
||||
return False
|
||||
|
||||
# Test 4: pos= keyword argument
|
||||
try:
|
||||
state4 = entity.at(pos=(6, 6))
|
||||
print(" PASS: entity.at(pos=(6, 6))")
|
||||
except Exception as e:
|
||||
print(f" FAIL: entity.at(pos=(6, 6)) - {e}")
|
||||
return False
|
||||
|
||||
# Test 5: List argument
|
||||
try:
|
||||
state5 = entity.at([7, 7])
|
||||
print(" PASS: entity.at([7, 7])")
|
||||
except Exception as e:
|
||||
print(f" FAIL: entity.at([7, 7]) - {e}")
|
||||
return False
|
||||
|
||||
# Test 6: Vector argument
|
||||
try:
|
||||
vec = mcrfpy.Vector(8, 8)
|
||||
state6 = entity.at(vec)
|
||||
print(" PASS: entity.at(Vector(8, 8))")
|
||||
except Exception as e:
|
||||
print(f" FAIL: entity.at(Vector(8, 8)) - {e}")
|
||||
return False
|
||||
|
||||
print("\nTesting Entity.path_to() position parsing...")
|
||||
|
||||
# Test 1: Two separate integers
|
||||
try:
|
||||
path1 = entity.path_to(5, 5)
|
||||
print(" PASS: entity.path_to(5, 5)")
|
||||
except Exception as e:
|
||||
print(f" FAIL: entity.path_to(5, 5) - {e}")
|
||||
return False
|
||||
|
||||
# Test 2: Tuple argument
|
||||
try:
|
||||
path2 = entity.path_to((6, 6))
|
||||
print(" PASS: entity.path_to((6, 6))")
|
||||
except Exception as e:
|
||||
print(f" FAIL: entity.path_to((6, 6)) - {e}")
|
||||
return False
|
||||
|
||||
# Test 3: Keyword arguments
|
||||
try:
|
||||
path3 = entity.path_to(x=7, y=7)
|
||||
print(" PASS: entity.path_to(x=7, y=7)")
|
||||
except Exception as e:
|
||||
print(f" FAIL: entity.path_to(x=7, y=7) - {e}")
|
||||
return False
|
||||
|
||||
# Test 4: pos= keyword argument
|
||||
try:
|
||||
path4 = entity.path_to(pos=(8, 8))
|
||||
print(" PASS: entity.path_to(pos=(8, 8))")
|
||||
except Exception as e:
|
||||
print(f" FAIL: entity.path_to(pos=(8, 8)) - {e}")
|
||||
return False
|
||||
|
||||
# Test 5: List argument
|
||||
try:
|
||||
path5 = entity.path_to([9, 9])
|
||||
print(" PASS: entity.path_to([9, 9])")
|
||||
except Exception as e:
|
||||
print(f" FAIL: entity.path_to([9, 9]) - {e}")
|
||||
return False
|
||||
|
||||
# Test 6: Vector argument
|
||||
try:
|
||||
vec = mcrfpy.Vector(4, 4)
|
||||
path6 = entity.path_to(vec)
|
||||
print(" PASS: entity.path_to(Vector(4, 4))")
|
||||
except Exception as e:
|
||||
print(f" FAIL: entity.path_to(Vector(4, 4)) - {e}")
|
||||
return False
|
||||
|
||||
print("\nAll tests passed!")
|
||||
return True
|
||||
|
||||
# Run tests immediately (no game loop needed for these)
|
||||
if run_tests():
|
||||
print("PASS")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("FAIL")
|
||||
sys.exit(1)
|
||||
156
tests/unit/test_grid_pathfinding_positions.py
Normal file
156
tests/unit/test_grid_pathfinding_positions.py
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Test Grid pathfinding methods with new position parsing.
|
||||
|
||||
Tests that Grid.find_path, Grid.compute_fov, etc. accept positions
|
||||
in multiple formats: tuples, lists, Vectors.
|
||||
"""
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def run_tests():
|
||||
"""Run all grid pathfinding position parsing tests."""
|
||||
print("Testing Grid pathfinding position parsing...")
|
||||
|
||||
# Create a test grid
|
||||
texture = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||
grid = mcrfpy.Grid(grid_size=(10, 10), texture=texture, pos=(0, 0), size=(320, 320))
|
||||
|
||||
# Set up walkability: all cells walkable initially
|
||||
for y in range(10):
|
||||
for x in range(10):
|
||||
cell = grid.at((x, y))
|
||||
cell.walkable = True
|
||||
|
||||
# Add a wall in the middle
|
||||
grid.at((5, 5)).walkable = False
|
||||
|
||||
print(" Grid created with walkable cells and one wall at (5,5)")
|
||||
|
||||
# ============ Test find_path ============
|
||||
print("\n Testing find_path...")
|
||||
|
||||
# Test with tuple positions
|
||||
path1 = grid.find_path((0, 0), (3, 3))
|
||||
assert path1 is not None, "find_path with tuples returned None"
|
||||
assert len(path1) > 0, "find_path with tuples returned empty path"
|
||||
print(f" find_path((0,0), (3,3)) -> {len(path1)} steps: PASS")
|
||||
|
||||
# Test with list positions
|
||||
path2 = grid.find_path([0, 0], [3, 3])
|
||||
assert path2 is not None, "find_path with lists returned None"
|
||||
assert len(path2) > 0, "find_path with lists returned empty path"
|
||||
print(f" find_path([0,0], [3,3]) -> {len(path2)} steps: PASS")
|
||||
|
||||
# Test with Vector positions
|
||||
start_vec = mcrfpy.Vector(0, 0)
|
||||
end_vec = mcrfpy.Vector(3, 3)
|
||||
path3 = grid.find_path(start_vec, end_vec)
|
||||
assert path3 is not None, "find_path with Vectors returned None"
|
||||
assert len(path3) > 0, "find_path with Vectors returned empty path"
|
||||
print(f" find_path(Vector(0,0), Vector(3,3)) -> {len(path3)} steps: PASS")
|
||||
|
||||
# Test path with diagonal_cost parameter
|
||||
path4 = grid.find_path((0, 0), (3, 3), diagonal_cost=1.41)
|
||||
assert path4 is not None, "find_path with diagonal_cost returned None"
|
||||
print(f" find_path with diagonal_cost=1.41: PASS")
|
||||
|
||||
# ============ Test compute_fov / is_in_fov ============
|
||||
print("\n Testing compute_fov / is_in_fov...")
|
||||
|
||||
# All cells transparent for FOV testing
|
||||
for y in range(10):
|
||||
for x in range(10):
|
||||
cell = grid.at((x, y))
|
||||
cell.transparent = True
|
||||
|
||||
# Test compute_fov with tuple
|
||||
grid.compute_fov((5, 5), radius=5)
|
||||
print(" compute_fov((5,5), radius=5): PASS")
|
||||
|
||||
# Test is_in_fov with tuple
|
||||
in_fov1 = grid.is_in_fov((5, 5))
|
||||
assert in_fov1 == True, "Center should be in FOV"
|
||||
print(f" is_in_fov((5,5)) = {in_fov1}: PASS")
|
||||
|
||||
# Test is_in_fov with list
|
||||
in_fov2 = grid.is_in_fov([4, 5])
|
||||
assert in_fov2 == True, "Adjacent cell should be in FOV"
|
||||
print(f" is_in_fov([4,5]) = {in_fov2}: PASS")
|
||||
|
||||
# Test is_in_fov with Vector
|
||||
pos_vec = mcrfpy.Vector(6, 5)
|
||||
in_fov3 = grid.is_in_fov(pos_vec)
|
||||
assert in_fov3 == True, "Adjacent cell should be in FOV"
|
||||
print(f" is_in_fov(Vector(6,5)) = {in_fov3}: PASS")
|
||||
|
||||
# Test compute_fov with Vector
|
||||
center_vec = mcrfpy.Vector(3, 3)
|
||||
grid.compute_fov(center_vec, radius=3)
|
||||
print(" compute_fov(Vector(3,3), radius=3): PASS")
|
||||
|
||||
# ============ Test compute_dijkstra / get_dijkstra_* ============
|
||||
print("\n Testing Dijkstra methods...")
|
||||
|
||||
# Test compute_dijkstra with tuple
|
||||
grid.compute_dijkstra((0, 0))
|
||||
print(" compute_dijkstra((0,0)): PASS")
|
||||
|
||||
# Test get_dijkstra_distance with tuple
|
||||
dist1 = grid.get_dijkstra_distance((3, 3))
|
||||
assert dist1 is not None, "Distance should not be None for reachable cell"
|
||||
print(f" get_dijkstra_distance((3,3)) = {dist1:.2f}: PASS")
|
||||
|
||||
# Test get_dijkstra_distance with list
|
||||
dist2 = grid.get_dijkstra_distance([2, 2])
|
||||
assert dist2 is not None, "Distance should not be None for reachable cell"
|
||||
print(f" get_dijkstra_distance([2,2]) = {dist2:.2f}: PASS")
|
||||
|
||||
# Test get_dijkstra_distance with Vector
|
||||
dist3 = grid.get_dijkstra_distance(mcrfpy.Vector(1, 1))
|
||||
assert dist3 is not None, "Distance should not be None for reachable cell"
|
||||
print(f" get_dijkstra_distance(Vector(1,1)) = {dist3:.2f}: PASS")
|
||||
|
||||
# Test get_dijkstra_path with tuple
|
||||
dpath1 = grid.get_dijkstra_path((3, 3))
|
||||
assert dpath1 is not None, "Dijkstra path should not be None"
|
||||
print(f" get_dijkstra_path((3,3)) -> {len(dpath1)} steps: PASS")
|
||||
|
||||
# Test get_dijkstra_path with Vector
|
||||
dpath2 = grid.get_dijkstra_path(mcrfpy.Vector(4, 4))
|
||||
assert dpath2 is not None, "Dijkstra path should not be None"
|
||||
print(f" get_dijkstra_path(Vector(4,4)) -> {len(dpath2)} steps: PASS")
|
||||
|
||||
# ============ Test compute_astar_path ============
|
||||
print("\n Testing compute_astar_path...")
|
||||
|
||||
# Test with tuples
|
||||
apath1 = grid.compute_astar_path((0, 0), (3, 3))
|
||||
assert apath1 is not None, "A* path should not be None"
|
||||
print(f" compute_astar_path((0,0), (3,3)) -> {len(apath1)} steps: PASS")
|
||||
|
||||
# Test with lists
|
||||
apath2 = grid.compute_astar_path([1, 1], [4, 4])
|
||||
assert apath2 is not None, "A* path should not be None"
|
||||
print(f" compute_astar_path([1,1], [4,4]) -> {len(apath2)} steps: PASS")
|
||||
|
||||
# Test with Vectors
|
||||
apath3 = grid.compute_astar_path(mcrfpy.Vector(2, 2), mcrfpy.Vector(7, 7))
|
||||
assert apath3 is not None, "A* path should not be None"
|
||||
print(f" compute_astar_path(Vector(2,2), Vector(7,7)) -> {len(apath3)} steps: PASS")
|
||||
|
||||
print("\n" + "="*50)
|
||||
print("All grid pathfinding position tests PASSED!")
|
||||
print("="*50)
|
||||
return True
|
||||
|
||||
# Run tests
|
||||
try:
|
||||
success = run_tests()
|
||||
if success:
|
||||
print("\nPASS")
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
print(f"\nFAIL: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
149
tests/unit/test_layer_position_parsing.py
Normal file
149
tests/unit/test_layer_position_parsing.py
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Test ColorLayer and TileLayer position parsing with new PyPositionHelper pattern."""
|
||||
import sys
|
||||
import mcrfpy
|
||||
|
||||
def test_colorlayer_at():
|
||||
"""Test ColorLayer.at() with various position formats."""
|
||||
print("Testing ColorLayer.at() position parsing...")
|
||||
|
||||
# Create a grid and color layer
|
||||
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||||
layer = mcrfpy.ColorLayer(z_index=-1, grid_size=(10, 10))
|
||||
grid.layers.append(layer)
|
||||
|
||||
# Set a color at position
|
||||
layer.set((5, 5), mcrfpy.Color(255, 0, 0))
|
||||
|
||||
# Test at() with tuple
|
||||
c1 = layer.at((5, 5))
|
||||
assert c1.r == 255 and c1.g == 0 and c1.b == 0, f"Failed: tuple position - got {c1.r},{c1.g},{c1.b}"
|
||||
print(" - tuple position: PASS")
|
||||
|
||||
# Test at() with two args
|
||||
c2 = layer.at(5, 5)
|
||||
assert c2.r == 255 and c2.g == 0 and c2.b == 0, f"Failed: two args - got {c2.r},{c2.g},{c2.b}"
|
||||
print(" - two args: PASS")
|
||||
|
||||
# Test at() with list (if supported)
|
||||
c3 = layer.at([5, 5])
|
||||
assert c3.r == 255 and c3.g == 0 and c3.b == 0, f"Failed: list position - got {c3.r},{c3.g},{c3.b}"
|
||||
print(" - list position: PASS")
|
||||
|
||||
# Test at() with Vector
|
||||
vec = mcrfpy.Vector(5, 5)
|
||||
c4 = layer.at(vec)
|
||||
assert c4.r == 255 and c4.g == 0 and c4.b == 0, f"Failed: Vector position - got {c4.r},{c4.g},{c4.b}"
|
||||
print(" - Vector position: PASS")
|
||||
|
||||
print("ColorLayer.at(): ALL PASS")
|
||||
|
||||
|
||||
def test_colorlayer_set():
|
||||
"""Test ColorLayer.set() with grouped position."""
|
||||
print("Testing ColorLayer.set() grouped position...")
|
||||
|
||||
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||||
layer = mcrfpy.ColorLayer(z_index=-1, grid_size=(10, 10))
|
||||
grid.layers.append(layer)
|
||||
|
||||
# Test set() with tuple position
|
||||
layer.set((3, 4), mcrfpy.Color(0, 255, 0))
|
||||
c = layer.at((3, 4))
|
||||
assert c.g == 255, f"Failed: tuple position - got g={c.g}"
|
||||
print(" - tuple position: PASS")
|
||||
|
||||
# Test set() with list position
|
||||
layer.set([7, 8], (0, 0, 255)) # Also test tuple color
|
||||
c2 = layer.at((7, 8))
|
||||
assert c2.b == 255, f"Failed: list position - got b={c2.b}"
|
||||
print(" - list position: PASS")
|
||||
|
||||
# Test set() with Vector position
|
||||
layer.set(mcrfpy.Vector(1, 1), mcrfpy.Color(128, 128, 128))
|
||||
c3 = layer.at((1, 1))
|
||||
assert c3.r == 128, f"Failed: Vector position - got r={c3.r}"
|
||||
print(" - Vector position: PASS")
|
||||
|
||||
print("ColorLayer.set(): ALL PASS")
|
||||
|
||||
|
||||
def test_tilelayer_at():
|
||||
"""Test TileLayer.at() with various position formats."""
|
||||
print("Testing TileLayer.at() position parsing...")
|
||||
|
||||
# Create a grid and tile layer
|
||||
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
layer = mcrfpy.TileLayer(z_index=-1, texture=texture, grid_size=(10, 10))
|
||||
grid.layers.append(layer)
|
||||
|
||||
# Set a tile at position
|
||||
layer.set((5, 5), 42)
|
||||
|
||||
# Test at() with tuple
|
||||
t1 = layer.at((5, 5))
|
||||
assert t1 == 42, f"Failed: tuple position - got {t1}"
|
||||
print(" - tuple position: PASS")
|
||||
|
||||
# Test at() with two args
|
||||
t2 = layer.at(5, 5)
|
||||
assert t2 == 42, f"Failed: two args - got {t2}"
|
||||
print(" - two args: PASS")
|
||||
|
||||
# Test at() with list
|
||||
t3 = layer.at([5, 5])
|
||||
assert t3 == 42, f"Failed: list position - got {t3}"
|
||||
print(" - list position: PASS")
|
||||
|
||||
# Test at() with Vector
|
||||
t4 = layer.at(mcrfpy.Vector(5, 5))
|
||||
assert t4 == 42, f"Failed: Vector position - got {t4}"
|
||||
print(" - Vector position: PASS")
|
||||
|
||||
print("TileLayer.at(): ALL PASS")
|
||||
|
||||
|
||||
def test_tilelayer_set():
|
||||
"""Test TileLayer.set() with grouped position."""
|
||||
print("Testing TileLayer.set() grouped position...")
|
||||
|
||||
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
layer = mcrfpy.TileLayer(z_index=-1, texture=texture, grid_size=(10, 10))
|
||||
grid.layers.append(layer)
|
||||
|
||||
# Test set() with tuple position
|
||||
layer.set((3, 4), 10)
|
||||
assert layer.at((3, 4)) == 10, "Failed: tuple position"
|
||||
print(" - tuple position: PASS")
|
||||
|
||||
# Test set() with list position
|
||||
layer.set([7, 8], 20)
|
||||
assert layer.at((7, 8)) == 20, "Failed: list position"
|
||||
print(" - list position: PASS")
|
||||
|
||||
# Test set() with Vector position
|
||||
layer.set(mcrfpy.Vector(1, 1), 30)
|
||||
assert layer.at((1, 1)) == 30, "Failed: Vector position"
|
||||
print(" - Vector position: PASS")
|
||||
|
||||
print("TileLayer.set(): ALL PASS")
|
||||
|
||||
|
||||
# Run all tests
|
||||
try:
|
||||
test_colorlayer_at()
|
||||
test_colorlayer_set()
|
||||
test_tilelayer_at()
|
||||
test_tilelayer_set()
|
||||
print("\n=== ALL TESTS PASSED ===")
|
||||
sys.exit(0)
|
||||
except AssertionError as e:
|
||||
print(f"\nTEST FAILED: {e}")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"\nTEST ERROR: {type(e).__name__}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
158
tests/unit/test_position_helper.py
Normal file
158
tests/unit/test_position_helper.py
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Test script for PyPositionHelper - validates Grid.at() position parsing.
|
||||
|
||||
This tests the standardized position argument parsing that supports:
|
||||
- Two separate args: func(x, y)
|
||||
- A tuple: func((x, y))
|
||||
- A list: func([x, y])
|
||||
- A Vector object: func(Vector(x, y))
|
||||
- Keyword args: func(x=x, y=y) or func(pos=(x,y))
|
||||
"""
|
||||
|
||||
import sys
|
||||
import mcrfpy
|
||||
|
||||
def test_grid_at_position_parsing():
|
||||
"""Test all the different ways to call Grid.at() with positions."""
|
||||
|
||||
# Create a test scene and grid
|
||||
scene = mcrfpy.Scene("test_position")
|
||||
|
||||
# Create a grid with enough cells to test indexing
|
||||
grid = mcrfpy.Grid(grid_x=10, grid_y=10)
|
||||
|
||||
errors = []
|
||||
|
||||
# Test 1: Two separate integer arguments
|
||||
try:
|
||||
point1 = grid.at(3, 4)
|
||||
if point1 is None:
|
||||
errors.append("Test 1 FAIL: grid.at(3, 4) returned None")
|
||||
else:
|
||||
print("Test 1 PASS: grid.at(3, 4) works")
|
||||
except Exception as e:
|
||||
errors.append(f"Test 1 FAIL: grid.at(3, 4) raised {type(e).__name__}: {e}")
|
||||
|
||||
# Test 2: Tuple argument
|
||||
try:
|
||||
point2 = grid.at((5, 6))
|
||||
if point2 is None:
|
||||
errors.append("Test 2 FAIL: grid.at((5, 6)) returned None")
|
||||
else:
|
||||
print("Test 2 PASS: grid.at((5, 6)) works")
|
||||
except Exception as e:
|
||||
errors.append(f"Test 2 FAIL: grid.at((5, 6)) raised {type(e).__name__}: {e}")
|
||||
|
||||
# Test 3: List argument
|
||||
try:
|
||||
point3 = grid.at([7, 8])
|
||||
if point3 is None:
|
||||
errors.append("Test 3 FAIL: grid.at([7, 8]) returned None")
|
||||
else:
|
||||
print("Test 3 PASS: grid.at([7, 8]) works")
|
||||
except Exception as e:
|
||||
errors.append(f"Test 3 FAIL: grid.at([7, 8]) raised {type(e).__name__}: {e}")
|
||||
|
||||
# Test 4: Vector argument
|
||||
try:
|
||||
vec = mcrfpy.Vector(2, 3)
|
||||
point4 = grid.at(vec)
|
||||
if point4 is None:
|
||||
errors.append("Test 4 FAIL: grid.at(Vector(2, 3)) returned None")
|
||||
else:
|
||||
print("Test 4 PASS: grid.at(Vector(2, 3)) works")
|
||||
except Exception as e:
|
||||
errors.append(f"Test 4 FAIL: grid.at(Vector(2, 3)) raised {type(e).__name__}: {e}")
|
||||
|
||||
# Test 5: Keyword arguments x=, y=
|
||||
try:
|
||||
point5 = grid.at(x=1, y=2)
|
||||
if point5 is None:
|
||||
errors.append("Test 5 FAIL: grid.at(x=1, y=2) returned None")
|
||||
else:
|
||||
print("Test 5 PASS: grid.at(x=1, y=2) works")
|
||||
except Exception as e:
|
||||
errors.append(f"Test 5 FAIL: grid.at(x=1, y=2) raised {type(e).__name__}: {e}")
|
||||
|
||||
# Test 6: pos= keyword with tuple
|
||||
try:
|
||||
point6 = grid.at(pos=(4, 5))
|
||||
if point6 is None:
|
||||
errors.append("Test 6 FAIL: grid.at(pos=(4, 5)) returned None")
|
||||
else:
|
||||
print("Test 6 PASS: grid.at(pos=(4, 5)) works")
|
||||
except Exception as e:
|
||||
errors.append(f"Test 6 FAIL: grid.at(pos=(4, 5)) raised {type(e).__name__}: {e}")
|
||||
|
||||
# Test 7: pos= keyword with Vector
|
||||
try:
|
||||
vec2 = mcrfpy.Vector(6, 7)
|
||||
point7 = grid.at(pos=vec2)
|
||||
if point7 is None:
|
||||
errors.append("Test 7 FAIL: grid.at(pos=Vector(6, 7)) returned None")
|
||||
else:
|
||||
print("Test 7 PASS: grid.at(pos=Vector(6, 7)) works")
|
||||
except Exception as e:
|
||||
errors.append(f"Test 7 FAIL: grid.at(pos=Vector(6, 7)) raised {type(e).__name__}: {e}")
|
||||
|
||||
# Test 8: pos= keyword with list
|
||||
try:
|
||||
point8 = grid.at(pos=[8, 9])
|
||||
if point8 is None:
|
||||
errors.append("Test 8 FAIL: grid.at(pos=[8, 9]) returned None")
|
||||
else:
|
||||
print("Test 8 PASS: grid.at(pos=[8, 9]) works")
|
||||
except Exception as e:
|
||||
errors.append(f"Test 8 FAIL: grid.at(pos=[8, 9]) raised {type(e).__name__}: {e}")
|
||||
|
||||
# Test 9: Out of range should raise IndexError (not TypeError)
|
||||
try:
|
||||
grid.at(100, 100)
|
||||
errors.append("Test 9 FAIL: grid.at(100, 100) should have raised IndexError")
|
||||
except IndexError:
|
||||
print("Test 9 PASS: grid.at(100, 100) raises IndexError")
|
||||
except Exception as e:
|
||||
errors.append(f"Test 9 FAIL: grid.at(100, 100) raised {type(e).__name__} instead of IndexError: {e}")
|
||||
|
||||
# Test 10: Invalid type should raise TypeError
|
||||
try:
|
||||
grid.at("invalid")
|
||||
errors.append("Test 10 FAIL: grid.at('invalid') should have raised TypeError")
|
||||
except TypeError:
|
||||
print("Test 10 PASS: grid.at('invalid') raises TypeError")
|
||||
except Exception as e:
|
||||
errors.append(f"Test 10 FAIL: grid.at('invalid') raised {type(e).__name__} instead of TypeError: {e}")
|
||||
|
||||
# Test 11: Float integers should work (e.g., 3.0 is valid as int)
|
||||
try:
|
||||
point11 = grid.at(3.0, 4.0)
|
||||
if point11 is None:
|
||||
errors.append("Test 11 FAIL: grid.at(3.0, 4.0) returned None")
|
||||
else:
|
||||
print("Test 11 PASS: grid.at(3.0, 4.0) works (float integers)")
|
||||
except Exception as e:
|
||||
errors.append(f"Test 11 FAIL: grid.at(3.0, 4.0) raised {type(e).__name__}: {e}")
|
||||
|
||||
# Test 12: Non-integer float should raise TypeError
|
||||
try:
|
||||
grid.at(3.5, 4.5)
|
||||
errors.append("Test 12 FAIL: grid.at(3.5, 4.5) should have raised TypeError")
|
||||
except TypeError:
|
||||
print("Test 12 PASS: grid.at(3.5, 4.5) raises TypeError for non-integer floats")
|
||||
except Exception as e:
|
||||
errors.append(f"Test 12 FAIL: grid.at(3.5, 4.5) raised {type(e).__name__} instead of TypeError: {e}")
|
||||
|
||||
# Summary
|
||||
print()
|
||||
print("=" * 50)
|
||||
if errors:
|
||||
print(f"FAILED: {len(errors)} test(s) failed")
|
||||
for err in errors:
|
||||
print(f" - {err}")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("SUCCESS: All 12 tests passed!")
|
||||
sys.exit(0)
|
||||
|
||||
# Run tests immediately (no game loop needed for this)
|
||||
test_grid_at_position_parsing()
|
||||
Loading…
Add table
Add a link
Reference in a new issue