Spaces:
Sleeping
Sleeping
File size: 5,394 Bytes
9f084b2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | """Tests for geometry/bbox.py — pure bbox operations."""
from __future__ import annotations
import pytest
from src.app.geometry import bbox
class TestBasicProperties:
def test_x2(self) -> None:
assert bbox.x2((10, 20, 100, 50)) == 110
def test_y2(self) -> None:
assert bbox.y2((10, 20, 100, 50)) == 70
def test_area(self) -> None:
assert bbox.area((0, 0, 10, 20)) == 200
def test_center(self) -> None:
cx, cy = bbox.center((100, 200, 300, 50))
assert cx == 250.0
assert cy == 225.0
class TestContains:
def test_fully_inside(self) -> None:
outer = (0, 0, 100, 100)
inner = (10, 10, 20, 20)
assert bbox.contains(outer, inner) is True
def test_same_box(self) -> None:
b = (10, 20, 30, 40)
assert bbox.contains(b, b) is True
def test_exceeds_right(self) -> None:
outer = (0, 0, 100, 100)
inner = (80, 10, 30, 20) # x2 = 110 > 100
assert bbox.contains(outer, inner) is False
def test_exceeds_bottom(self) -> None:
outer = (0, 0, 100, 100)
inner = (10, 85, 20, 20) # y2 = 105 > 100
assert bbox.contains(outer, inner) is False
def test_tolerance_allows_small_overflow(self) -> None:
outer = (0, 0, 100, 100)
inner = (0, 0, 103, 100) # 3px overflow on right
assert bbox.contains(outer, inner, tolerance=5) is True
assert bbox.contains(outer, inner, tolerance=2) is False
def test_tolerance_on_all_sides(self) -> None:
outer = (10, 10, 80, 80)
inner = (7, 7, 86, 86) # 3px overflow on each side
assert bbox.contains(outer, inner, tolerance=3) is True
assert bbox.contains(outer, inner, tolerance=2) is False
class TestIntersects:
def test_overlapping(self) -> None:
a = (0, 0, 50, 50)
b = (25, 25, 50, 50)
assert bbox.intersects(a, b) is True
def test_not_overlapping(self) -> None:
a = (0, 0, 10, 10)
b = (20, 20, 10, 10)
assert bbox.intersects(a, b) is False
def test_touching_edge_not_overlapping(self) -> None:
a = (0, 0, 10, 10)
b = (10, 0, 10, 10) # touching at x=10
assert bbox.intersects(a, b) is False
def test_contained(self) -> None:
outer = (0, 0, 100, 100)
inner = (10, 10, 20, 20)
assert bbox.intersects(outer, inner) is True
class TestIntersection:
def test_overlap(self) -> None:
a = (0, 0, 50, 50)
b = (25, 25, 50, 50)
result = bbox.intersection(a, b)
assert result == (25, 25, 25, 25)
def test_no_overlap(self) -> None:
a = (0, 0, 10, 10)
b = (20, 20, 10, 10)
assert bbox.intersection(a, b) is None
def test_contained(self) -> None:
outer = (0, 0, 100, 100)
inner = (10, 10, 20, 20)
result = bbox.intersection(outer, inner)
assert result == inner
class TestUnion:
def test_adjacent(self) -> None:
a = (0, 0, 10, 10)
b = (10, 0, 10, 10)
result = bbox.union(a, b)
assert result == (0, 0, 20, 10)
def test_overlapping(self) -> None:
a = (0, 0, 50, 50)
b = (25, 25, 50, 50)
result = bbox.union(a, b)
assert result == (0, 0, 75, 75)
def test_contained(self) -> None:
outer = (0, 0, 100, 100)
inner = (10, 10, 20, 20)
assert bbox.union(outer, inner) == outer
class TestUnionAll:
def test_multiple(self) -> None:
bboxes = [(0, 0, 10, 10), (50, 50, 10, 10), (25, 25, 10, 10)]
result = bbox.union_all(bboxes)
assert result == (0, 0, 60, 60)
def test_single(self) -> None:
assert bbox.union_all([(5, 5, 10, 10)]) == (5, 5, 10, 10)
def test_empty_raises(self) -> None:
with pytest.raises(ValueError, match="empty"):
bbox.union_all([])
class TestExpand:
def test_normal(self) -> None:
result = bbox.expand((10, 20, 100, 50), 5)
assert result == (5, 15, 110, 60)
def test_clamp_to_zero(self) -> None:
result = bbox.expand((2, 3, 10, 10), 10)
assert result[0] == 0
assert result[1] == 0
def test_min_dimension(self) -> None:
result = bbox.expand((10, 10, 10, 10), -100)
assert result[2] >= 1
assert result[3] >= 1
class TestIou:
def test_perfect_overlap(self) -> None:
b = (0, 0, 10, 10)
assert bbox.iou(b, b) == pytest.approx(1.0)
def test_no_overlap(self) -> None:
a = (0, 0, 10, 10)
b = (20, 20, 10, 10)
assert bbox.iou(a, b) == 0.0
def test_partial_overlap(self) -> None:
a = (0, 0, 10, 10)
b = (5, 5, 10, 10)
# intersection = (5,5,5,5) area=25, union = 100+100-25=175
assert bbox.iou(a, b) == pytest.approx(25 / 175)
class TestOverlapRatio:
def test_fully_inside(self) -> None:
inner = (10, 10, 20, 20)
outer = (0, 0, 100, 100)
assert bbox.overlap_ratio(inner, outer) == pytest.approx(1.0)
def test_no_overlap(self) -> None:
a = (0, 0, 10, 10)
b = (20, 20, 10, 10)
assert bbox.overlap_ratio(a, b) == 0.0
def test_half_overlap(self) -> None:
a = (0, 0, 10, 10)
b = (5, 0, 10, 10)
# a ∩ b = (5,0,5,10) area=50, a area=100
assert bbox.overlap_ratio(a, b) == pytest.approx(0.5)
|