Skip to content

Commit

Permalink
Complete tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lycantropos committed Dec 17, 2020
1 parent e0ba964 commit 2b1f5bb
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 115 deletions.
10 changes: 10 additions & 0 deletions tests/hints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from typing import Tuple

from ground.core.hints import (QuaternaryPointFunction,
TernaryPointFunction)
from ground.hints import Point
from ground.linear import SegmentsRelationship

SegmentContainmentChecker = TernaryPointFunction[bool]
SegmentsIntersector = QuaternaryPointFunction[Tuple[Point, ...]]
SegmentsRelater = QuaternaryPointFunction[SegmentsRelationship]
42 changes: 25 additions & 17 deletions tests/planar_tests/strategies.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,43 @@
from functools import partial
from itertools import (combinations,
repeat)
from typing import (List,
Tuple)
repeat,
starmap)
from typing import List

from hypothesis import strategies

from bentley_ottmann.hints import (Point,
Segment)
from tests.strategies import (points_strategies,
rational_points_strategies,
rational_segments_strategies,
segments_strategies)
from tests.utils import (Strategy,
from tests.utils import (Contour,
Point,
Segment,
Strategy,
pack,
scale_segment)

contours = points_strategies.flatmap(partial(strategies.lists,
min_size=3))
non_triangular_contours = (points_strategies
.flatmap(partial(strategies.lists,
min_size=4)))
contours = (points_strategies.flatmap(partial(strategies.lists,
min_size=3))
.map(Contour))
non_triangular_rational_contours = (rational_points_strategies
.flatmap(partial(strategies.lists,
min_size=4))
.map(Contour))
triangular_contours = (points_strategies
.flatmap(partial(strategies.lists,
min_size=3,
max_size=3)))
max_size=3))
.map(Contour))
degenerate_contours = (points_strategies
.flatmap(partial(strategies.lists,
max_size=2)))
max_size=2))
.map(Contour))


def points_to_nets(points: Strategy[Point]) -> Strategy[List[Segment]]:
def to_net(points_list: List[Point]) -> List[Segment]:
return list(combinations(points_list, 2))
return list(starmap(Segment, combinations(points_list, 2)))

return (strategies.lists(points,
min_size=2,
Expand All @@ -53,17 +61,17 @@ def to_overlapped_segments(segments: List[Segment],
segments_lists |= strategies.builds(to_overlapped_segments, segments_lists,
strategies.integers(1, 100))
empty_segments_lists = strategies.builds(list)
non_empty_segments_lists = ((segments_strategies
non_empty_segments_lists = ((rational_segments_strategies
.flatmap(partial(strategies.lists,
min_size=1)))
| nets)


def points_to_degenerate_segments(points: Strategy[Point]
) -> Strategy[Tuple[Point, Point]]:
) -> Strategy[Segment]:
return (points.map(partial(repeat,
times=2))
.map(tuple))
.map(pack(Segment)))


degenerate_segments = points_strategies.flatmap(points_to_degenerate_segments)
Expand Down
81 changes: 48 additions & 33 deletions tests/planar_tests/test_edges_intersect.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
combinations)

import pytest
from ground.linear import SegmentsRelationship
from hypothesis import given
from robust.linear import segment_contains

from bentley_ottmann.core.linear import (SegmentsRelationship,
segments_intersections,
segments_relationship)
from bentley_ottmann.hints import Contour
from bentley_ottmann.planar import edges_intersect
from tests.utils import (contour_to_segments,
reverse_point_coordinates)
from tests.hints import (SegmentContainmentChecker,
SegmentsIntersector,
SegmentsRelater)
from tests.utils import (Contour,
Segment,
contour_to_edges,
reverse_contour,
reverse_contour_coordinates)
from . import strategies


Expand All @@ -23,62 +25,75 @@ def test_basic(contour: Contour) -> None:


@given(strategies.triangular_contours)
def test_base_case(contour: Contour) -> None:
def test_base_case(segment_containment_checker: SegmentContainmentChecker,
contour: Contour) -> None:
result = edges_intersect(contour)

left_vertex, mid_vertex, right_vertex = sorted(contour)
assert result is segment_contains((left_vertex, right_vertex), mid_vertex)
left_vertex, mid_vertex, right_vertex = sorted(contour.vertices)
assert result is segment_containment_checker(left_vertex, right_vertex,
mid_vertex)


@given(strategies.non_triangular_contours)
def test_step(contour: Contour) -> None:
first_vertex, *rest_vertices = contour
@given(strategies.non_triangular_rational_contours)
def test_step(segments_intersector: SegmentsIntersector,
segments_relater: SegmentsRelater,
contour: Contour) -> None:
first_vertex, *rest_vertices = contour.vertices
rest_contour = Contour(rest_vertices)

result = edges_intersect(rest_vertices)
result = edges_intersect(rest_contour)
next_result = edges_intersect(contour)

first_edge, last_edge = ((first_vertex, rest_vertices[0]),
(rest_vertices[-1], first_vertex))
rest_edges = contour_to_segments(rest_vertices)
first_edge, last_edge = (Segment(first_vertex, rest_vertices[0]),
Segment(rest_vertices[-1], first_vertex))
rest_edges = contour_to_edges(rest_contour)
assert (next_result
is (result
and len(rest_vertices) > 2
and (any(segments_intersections(rest_edges[index],
rest_edges[other_index])
and (any(segments_intersector(rest_edges[index].start,
rest_edges[index].end,
rest_edges[other_index].start,
rest_edges[other_index].end)
for index in range(len(rest_edges) - 1)
for other_index in chain(
range(index - 1),
range(index + 2, len(rest_edges) - 1)))
or any(segments_relationship(edge, other_edge)
for other_index
in chain(range(index - 1),
range(index + 2, len(rest_edges) - 1)))
or any(segments_relater(edge.start, edge.end,
other_edge.start, other_edge.end)
is SegmentsRelationship.OVERLAP
for edge, other_edge in combinations(
rest_edges[:-1], 2)))
or any(segments_intersections(first_edge, edge)
for edge, other_edge
in combinations(rest_edges[:-1], 2)))
or any(segments_intersector(first_edge.start, first_edge.end,
edge.start, edge.end)
for edge in rest_edges[1:-1])
or any(segments_intersections(last_edge, edge)
or any(segments_intersector(last_edge.start, last_edge.end,
edge.start, edge.end)
for edge in rest_edges[:-2])
or len(rest_vertices) > 1
and (segments_relationship(first_edge, rest_edges[0])
and (segments_relater(first_edge.start, first_edge.end,
rest_edges[0].start, rest_edges[0].end)
is SegmentsRelationship.OVERLAP
or segments_relationship(first_edge, last_edge)
or segments_relater(first_edge.start, first_edge.end,
last_edge.start, last_edge.end)
is SegmentsRelationship.OVERLAP
or segments_relationship(last_edge, rest_edges[0])
or segments_relater(last_edge.start, last_edge.end,
rest_edges[0].start,
rest_edges[0].end)
is SegmentsRelationship.OVERLAP)))


@given(strategies.contours)
def test_reversed(contour: Contour) -> None:
result = edges_intersect(contour)

assert result is edges_intersect(contour[::-1])
assert result is edges_intersect(reverse_contour(contour))


@given(strategies.contours)
def test_reversed_coordinates(contour: Contour) -> None:
result = edges_intersect(contour)

assert result is edges_intersect([reverse_point_coordinates(vertex)
for vertex in contour])
assert result is edges_intersect(reverse_contour_coordinates(contour))


@given(strategies.degenerate_contours)
Expand Down
20 changes: 11 additions & 9 deletions tests/planar_tests/test_segments_cross_or_overlap.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from typing import List

import pytest
from ground.hints import Segment
from ground.linear import SegmentsRelationship
from hypothesis import given

from bentley_ottmann.core.linear import (SegmentsRelationship,
segments_relationship)
from bentley_ottmann.hints import Segment
from bentley_ottmann.planar import segments_cross_or_overlap
from tests.hints import SegmentsRelater
from tests.utils import (reverse_segment,
reverse_segment_coordinates)
from . import strategies
Expand All @@ -27,18 +27,20 @@ def test_base_case(segments: List[Segment]) -> None:


@given(strategies.non_empty_segments_lists)
def test_step(segments: List[Segment]) -> None:
def test_step(segments_relater: SegmentsRelater,
segments: List[Segment]) -> None:
first_segment, *rest_segments = segments

result = segments_cross_or_overlap(rest_segments)
next_result = segments_cross_or_overlap(segments)

assert (next_result
is (result
or any(segments_relationship(first_segment, segment)
in (SegmentsRelationship.CROSS,
SegmentsRelationship.OVERLAP)
for segment in rest_segments)))
is (result or any(segments_relater(first_segment.start,
first_segment.end,
segment.start, segment.end)
in (SegmentsRelationship.CROSS,
SegmentsRelationship.OVERLAP)
for segment in rest_segments)))


@given(strategies.segments_lists)
Expand Down
13 changes: 8 additions & 5 deletions tests/planar_tests/test_segments_intersect.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from typing import List

import pytest
from ground.hints import Segment
from hypothesis import given

from bentley_ottmann.core.linear import segments_intersections
from bentley_ottmann.hints import Segment
from bentley_ottmann.planar import segments_intersect
from tests.utils import (reverse_segment,
reverse_segment_coordinates)
from . import strategies
from ..hints import SegmentsIntersector


@given(strategies.segments_lists)
Expand All @@ -26,15 +26,18 @@ def test_base_case(segments: List[Segment]) -> None:


@given(strategies.non_empty_segments_lists)
def test_step(segments: List[Segment]) -> None:
def test_step(segments_intersector: SegmentsIntersector,
segments: List[Segment]) -> None:
first_segment, *rest_segments = segments

result = segments_intersect(rest_segments)
next_result = segments_intersect(segments)

assert next_result is (result
or any(segments_intersections(first_segment,
segment)
or any(segments_intersector(first_segment.start,
first_segment.end,
segment.start,
segment.end)
for segment in rest_segments))


Expand Down
24 changes: 14 additions & 10 deletions tests/planar_tests/test_segments_intersections.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
from typing import List

import pytest
from ground.hints import Segment
from hypothesis import given

from bentley_ottmann.core.linear import (segments_intersections
as segments_pair_intersections)
from bentley_ottmann.hints import Segment
from bentley_ottmann.planar import segments_intersections
from tests.hints import SegmentsIntersector
from tests.utils import is_point
from . import strategies

Expand Down Expand Up @@ -38,23 +37,28 @@ def test_base_case(segments: List[Segment]) -> None:


@given(strategies.non_empty_segments_lists)
def test_step(segments: List[Segment]) -> None:
def test_step(segments_intersector: SegmentsIntersector,
segments: List[Segment]) -> None:
*rest_segments, last_segment = segments

result = segments_intersections(rest_segments)
next_result = segments_intersections(segments)

assert (next_result.keys() ==
(result.keys()
| set(chain.from_iterable(
segments_pair_intersections(last_segment, segment)
assert (next_result.keys()
== (result.keys()
| set(chain.from_iterable(
segments_intersector(last_segment.start,
last_segment.end,
segment.start, segment.end)
for segment in rest_segments))))
assert all(segment_id < next_segment_id == len(segments) - 1
for point, intersections in next_result.items()
for segment_id, next_segment_id in (intersections
- result.get(point, set())))
assert all(point in segments_pair_intersections(segments[segment_id],
segments[next_segment_id])
assert all(point in segments_intersector(segments[segment_id].start,
segments[segment_id].end,
segments[next_segment_id].start,
segments[next_segment_id].end)
for point, intersections in next_result.items()
for segment_id, next_segment_id in (intersections
- result.get(point, set())))
Expand Down
2 changes: 2 additions & 0 deletions tests/strategies/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
from .base import (points_strategies,
rational_points_strategies,
rational_segments_strategies,
segments_strategies)

0 comments on commit 2b1f5bb

Please sign in to comment.