229 lines
6.9 KiB
Python
229 lines
6.9 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""Unit tests for TileLayer HeightMap methods (#200)
|
||
|
|
|
||
|
|
Tests TileLayer.apply_threshold() and TileLayer.apply_ranges() methods.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import sys
|
||
|
|
import mcrfpy
|
||
|
|
|
||
|
|
|
||
|
|
def test_apply_threshold_basic():
|
||
|
|
"""apply_threshold sets tiles in range"""
|
||
|
|
hmap = mcrfpy.HeightMap((10, 10), fill=0.5)
|
||
|
|
|
||
|
|
# Create a grid and get a tile layer
|
||
|
|
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||
|
|
layer = grid.add_layer('tile', z_index=0)
|
||
|
|
layer.fill(-1) # Clear all tiles
|
||
|
|
|
||
|
|
# Apply threshold - all cells should get tile 5
|
||
|
|
result = layer.apply_threshold(hmap, (0.4, 0.6), 5)
|
||
|
|
|
||
|
|
# Verify result is the layer (chaining)
|
||
|
|
assert result is layer, "apply_threshold should return self"
|
||
|
|
|
||
|
|
# Verify tiles were set
|
||
|
|
assert layer.at(0, 0) == 5, f"Expected tile 5, got {layer.at(0, 0)}"
|
||
|
|
assert layer.at(5, 5) == 5, f"Expected tile 5, got {layer.at(5, 5)}"
|
||
|
|
print("PASS: test_apply_threshold_basic")
|
||
|
|
|
||
|
|
|
||
|
|
def test_apply_threshold_partial():
|
||
|
|
"""apply_threshold only affects cells in range"""
|
||
|
|
hmap = mcrfpy.HeightMap((10, 10))
|
||
|
|
# Fill with different values in different areas
|
||
|
|
hmap.fill(0.0) # Start with 0
|
||
|
|
|
||
|
|
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||
|
|
layer = grid.add_layer('tile', z_index=0)
|
||
|
|
layer.fill(-1)
|
||
|
|
|
||
|
|
# Apply threshold for range that doesn't match (0.5-1.0 when values are 0.0)
|
||
|
|
layer.apply_threshold(hmap, (0.5, 1.0), 10)
|
||
|
|
|
||
|
|
# Should still be -1 since 0.0 is not in [0.5, 1.0]
|
||
|
|
assert layer.at(0, 0) == -1, f"Expected -1, got {layer.at(0, 0)}"
|
||
|
|
print("PASS: test_apply_threshold_partial")
|
||
|
|
|
||
|
|
|
||
|
|
def test_apply_threshold_preserves_outside():
|
||
|
|
"""apply_threshold doesn't modify cells outside range"""
|
||
|
|
hmap = mcrfpy.HeightMap((10, 10), fill=0.5)
|
||
|
|
|
||
|
|
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||
|
|
layer = grid.add_layer('tile', z_index=0)
|
||
|
|
layer.fill(99) # Fill with marker value
|
||
|
|
|
||
|
|
# Apply threshold for range that doesn't include 0.5
|
||
|
|
layer.apply_threshold(hmap, (0.6, 1.0), 10)
|
||
|
|
|
||
|
|
# Should still be 99
|
||
|
|
assert layer.at(0, 0) == 99, f"Expected 99, got {layer.at(0, 0)}"
|
||
|
|
print("PASS: test_apply_threshold_preserves_outside")
|
||
|
|
|
||
|
|
|
||
|
|
def test_apply_threshold_invalid_range():
|
||
|
|
"""apply_threshold rejects min > max"""
|
||
|
|
hmap = mcrfpy.HeightMap((10, 10))
|
||
|
|
|
||
|
|
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||
|
|
layer = grid.add_layer('tile', z_index=0)
|
||
|
|
|
||
|
|
try:
|
||
|
|
layer.apply_threshold(hmap, (1.0, 0.0), 5) # min > max
|
||
|
|
print("FAIL: test_apply_threshold_invalid_range - should have raised ValueError")
|
||
|
|
sys.exit(1)
|
||
|
|
except ValueError as e:
|
||
|
|
assert "min" in str(e).lower()
|
||
|
|
|
||
|
|
print("PASS: test_apply_threshold_invalid_range")
|
||
|
|
|
||
|
|
|
||
|
|
def test_apply_threshold_size_mismatch():
|
||
|
|
"""apply_threshold rejects mismatched HeightMap size"""
|
||
|
|
hmap = mcrfpy.HeightMap((5, 5)) # Different size
|
||
|
|
|
||
|
|
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||
|
|
layer = grid.add_layer('tile', z_index=0)
|
||
|
|
|
||
|
|
try:
|
||
|
|
layer.apply_threshold(hmap, (0.0, 1.0), 5)
|
||
|
|
print("FAIL: test_apply_threshold_size_mismatch - should have raised ValueError")
|
||
|
|
sys.exit(1)
|
||
|
|
except ValueError as e:
|
||
|
|
assert "size" in str(e).lower()
|
||
|
|
|
||
|
|
print("PASS: test_apply_threshold_size_mismatch")
|
||
|
|
|
||
|
|
|
||
|
|
def test_apply_ranges_basic():
|
||
|
|
"""apply_ranges sets multiple tile ranges"""
|
||
|
|
hmap = mcrfpy.HeightMap((10, 10), fill=0.5)
|
||
|
|
|
||
|
|
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||
|
|
layer = grid.add_layer('tile', z_index=0)
|
||
|
|
layer.fill(-1)
|
||
|
|
|
||
|
|
# Apply ranges - 0.5 falls in the second range
|
||
|
|
result = layer.apply_ranges(hmap, [
|
||
|
|
((0.0, 0.3), 1), # Won't match
|
||
|
|
((0.3, 0.7), 2), # Will match (0.5 is in here)
|
||
|
|
((0.7, 1.0), 3), # Won't match
|
||
|
|
])
|
||
|
|
|
||
|
|
assert result is layer, "apply_ranges should return self"
|
||
|
|
assert layer.at(0, 0) == 2, f"Expected tile 2, got {layer.at(0, 0)}"
|
||
|
|
print("PASS: test_apply_ranges_basic")
|
||
|
|
|
||
|
|
|
||
|
|
def test_apply_ranges_later_wins():
|
||
|
|
"""apply_ranges: later ranges override earlier ones"""
|
||
|
|
hmap = mcrfpy.HeightMap((10, 10), fill=0.5)
|
||
|
|
|
||
|
|
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||
|
|
layer = grid.add_layer('tile', z_index=0)
|
||
|
|
layer.fill(-1)
|
||
|
|
|
||
|
|
# Apply overlapping ranges - later should win
|
||
|
|
layer.apply_ranges(hmap, [
|
||
|
|
((0.0, 1.0), 10), # Matches everything
|
||
|
|
((0.4, 0.6), 20), # Also matches 0.5, comes later
|
||
|
|
])
|
||
|
|
|
||
|
|
# Later range (20) should win
|
||
|
|
assert layer.at(0, 0) == 20, f"Expected tile 20, got {layer.at(0, 0)}"
|
||
|
|
print("PASS: test_apply_ranges_later_wins")
|
||
|
|
|
||
|
|
|
||
|
|
def test_apply_ranges_no_match_unchanged():
|
||
|
|
"""apply_ranges leaves unmatched cells unchanged"""
|
||
|
|
hmap = mcrfpy.HeightMap((10, 10), fill=0.5)
|
||
|
|
|
||
|
|
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||
|
|
layer = grid.add_layer('tile', z_index=0)
|
||
|
|
layer.fill(99)
|
||
|
|
|
||
|
|
# Apply ranges that don't match 0.5
|
||
|
|
layer.apply_ranges(hmap, [
|
||
|
|
((0.0, 0.2), 1),
|
||
|
|
((0.8, 1.0), 2),
|
||
|
|
])
|
||
|
|
|
||
|
|
# Should still be 99
|
||
|
|
assert layer.at(0, 0) == 99, f"Expected 99, got {layer.at(0, 0)}"
|
||
|
|
print("PASS: test_apply_ranges_no_match_unchanged")
|
||
|
|
|
||
|
|
|
||
|
|
def test_apply_ranges_invalid_format():
|
||
|
|
"""apply_ranges rejects invalid range format"""
|
||
|
|
hmap = mcrfpy.HeightMap((10, 10))
|
||
|
|
|
||
|
|
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||
|
|
layer = grid.add_layer('tile', z_index=0)
|
||
|
|
|
||
|
|
# Missing tile index
|
||
|
|
try:
|
||
|
|
layer.apply_ranges(hmap, [((0.0, 1.0),)]) # Tuple with only one element
|
||
|
|
print("FAIL: test_apply_ranges_invalid_format - should have raised TypeError")
|
||
|
|
sys.exit(1)
|
||
|
|
except TypeError:
|
||
|
|
pass
|
||
|
|
|
||
|
|
print("PASS: test_apply_ranges_invalid_format")
|
||
|
|
|
||
|
|
|
||
|
|
def test_apply_threshold_boundary():
|
||
|
|
"""apply_threshold includes boundary values"""
|
||
|
|
hmap = mcrfpy.HeightMap((10, 10), fill=0.5)
|
||
|
|
|
||
|
|
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||
|
|
layer = grid.add_layer('tile', z_index=0)
|
||
|
|
layer.fill(-1)
|
||
|
|
|
||
|
|
# Range includes 0.5 exactly
|
||
|
|
layer.apply_threshold(hmap, (0.5, 0.5), 7)
|
||
|
|
|
||
|
|
assert layer.at(0, 0) == 7, f"Expected 7, got {layer.at(0, 0)}"
|
||
|
|
print("PASS: test_apply_threshold_boundary")
|
||
|
|
|
||
|
|
|
||
|
|
def test_apply_threshold_accepts_list():
|
||
|
|
"""apply_threshold accepts list as range"""
|
||
|
|
hmap = mcrfpy.HeightMap((10, 10), fill=0.5)
|
||
|
|
|
||
|
|
grid = mcrfpy.Grid(grid_size=(10, 10))
|
||
|
|
layer = grid.add_layer('tile', z_index=0)
|
||
|
|
layer.fill(-1)
|
||
|
|
|
||
|
|
# Use list instead of tuple
|
||
|
|
layer.apply_threshold(hmap, [0.4, 0.6], 5)
|
||
|
|
|
||
|
|
assert layer.at(0, 0) == 5
|
||
|
|
print("PASS: test_apply_threshold_accepts_list")
|
||
|
|
|
||
|
|
|
||
|
|
def run_all_tests():
|
||
|
|
"""Run all tests"""
|
||
|
|
print("Running TileLayer HeightMap method tests (#200)...")
|
||
|
|
print()
|
||
|
|
|
||
|
|
test_apply_threshold_basic()
|
||
|
|
test_apply_threshold_partial()
|
||
|
|
test_apply_threshold_preserves_outside()
|
||
|
|
test_apply_threshold_invalid_range()
|
||
|
|
test_apply_threshold_size_mismatch()
|
||
|
|
test_apply_ranges_basic()
|
||
|
|
test_apply_ranges_later_wins()
|
||
|
|
test_apply_ranges_no_match_unchanged()
|
||
|
|
test_apply_ranges_invalid_format()
|
||
|
|
test_apply_threshold_boundary()
|
||
|
|
test_apply_threshold_accepts_list()
|
||
|
|
|
||
|
|
print()
|
||
|
|
print("All TileLayer HeightMap method tests PASSED!")
|
||
|
|
|
||
|
|
|
||
|
|
# Run tests directly
|
||
|
|
run_all_tests()
|
||
|
|
sys.exit(0)
|