[Pather/Planner] Doc cleanup and more informative error enums
This commit is contained in:
parent
3a3cd854a5
commit
49713896c3
13 changed files with 370 additions and 95 deletions
|
|
@ -5,8 +5,9 @@ import pytest
|
|||
import numpy
|
||||
from numpy import pi
|
||||
|
||||
from masque import Pather, Library, Port, RouteError
|
||||
from masque import MinimumStatus, Pather, Library, Port, RouteError, RouteFailurePolicy
|
||||
from masque.builder.planner import RoutePortContext, RoutingPlanner
|
||||
from masque.builder.planner.planner import NoLegalRouteError
|
||||
from masque.builder.tools import BendOffer, PathTool, RenderStep, StraightOffer, Tool, UOffer
|
||||
from masque.error import BuildError
|
||||
from masque.library import ILibrary
|
||||
|
|
@ -216,8 +217,8 @@ def test_route_error_reports_preferred_minimum_and_request_details() -> None:
|
|||
assert details.resolved_jog is None
|
||||
# The length-2 route exists, but normal cost ranking prefers length 5.
|
||||
assert details.minimum_length == 5
|
||||
assert details.minimum_attempted
|
||||
assert not details.minimum_exhausted
|
||||
assert details.minimum_status is MinimumStatus.FOUND
|
||||
assert exc_info.value.policy is RouteFailurePolicy.RECOVERABLE
|
||||
assert details.minimum_cause is None
|
||||
assert 'preferred_minimum_length: 5' in str(exc_info.value)
|
||||
assert tool.commit_calls == 0
|
||||
|
|
@ -260,13 +261,53 @@ def test_route_error_reports_no_route_at_any_length() -> None:
|
|||
p.ccw('A', 10, out_ptype='optical')
|
||||
|
||||
details = exc_info.value.details
|
||||
assert details.minimum_attempted
|
||||
assert details.minimum_status is MinimumStatus.NO_ROUTE
|
||||
assert details.minimum_length is None
|
||||
assert details.minimum_exhausted
|
||||
assert details.minimum_cause is not None
|
||||
assert 'no legal route exists at any length' in str(exc_info.value)
|
||||
|
||||
|
||||
def test_route_error_reports_failed_minimum_diagnosis(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
planner = RoutingPlanner()
|
||||
context = RoutePortContext(
|
||||
'A',
|
||||
Port((0, 0), rotation=0, ptype='wire'),
|
||||
PlanningOnlyTool(),
|
||||
)
|
||||
solve_count = 0
|
||||
|
||||
def solver_for_request(_request: Any) -> Any:
|
||||
nonlocal solve_count
|
||||
solve_count += 1
|
||||
current_solve = solve_count
|
||||
|
||||
class FailingSolver:
|
||||
def solve(self) -> Never:
|
||||
if current_solve == 1:
|
||||
raise NoLegalRouteError('requested length has no route')
|
||||
raise RuntimeError('minimum diagnosis failed')
|
||||
|
||||
return FailingSolver()
|
||||
|
||||
monkeypatch.setattr(planner, 'solver_for_request', solver_for_request)
|
||||
|
||||
with pytest.raises(RouteError) as exc_info:
|
||||
planner.plan_leg(
|
||||
'bend',
|
||||
context,
|
||||
'trace_to',
|
||||
{'ccw': True, 'length': 1},
|
||||
length=1,
|
||||
ccw=True,
|
||||
)
|
||||
|
||||
details = exc_info.value.details
|
||||
assert details.minimum_status is MinimumStatus.FAILED
|
||||
assert details.minimum_length is None
|
||||
assert details.minimum_cause == 'minimum diagnosis failed'
|
||||
assert 'minimum-length calculation failed' in str(exc_info.value)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('length', [-1, numpy.nan, numpy.inf])
|
||||
def test_invalid_route_length_fails_before_offer_query_or_dead_fallback(length: float) -> None:
|
||||
tool = RequestCountingTool()
|
||||
|
|
@ -281,7 +322,8 @@ def test_invalid_route_length_fails_before_offer_query_or_dead_fallback(length:
|
|||
p.ccw('A', length)
|
||||
|
||||
details = exc_info.value.details
|
||||
assert not details.minimum_attempted
|
||||
assert details.minimum_status is MinimumStatus.NOT_EVALUATED
|
||||
assert exc_info.value.policy is RouteFailurePolicy.FATAL
|
||||
assert details.minimum_length is None
|
||||
assert tool.offer_calls == 0
|
||||
assert numpy.allclose(p.ports['A'].offset, (0, 0))
|
||||
|
|
@ -302,7 +344,7 @@ def test_negative_position_length_reports_bound_without_offer_query() -> None:
|
|||
details = exc_info.value.details
|
||||
assert details.request == {'ccw': True, 'x': 1}
|
||||
assert details.resolved_length == -1
|
||||
assert not details.minimum_attempted
|
||||
assert details.minimum_status is MinimumStatus.NOT_EVALUATED
|
||||
assert tool.offer_calls == 0
|
||||
|
||||
|
||||
|
|
@ -325,7 +367,7 @@ def test_negative_bundle_length_reports_failing_port_and_bound() -> None:
|
|||
assert details.portspec == 'A'
|
||||
assert details.request == {'ccw': True, 'emax': 0, 'spacing': 2}
|
||||
assert details.resolved_length == -2
|
||||
assert not details.minimum_attempted
|
||||
assert details.minimum_status is MinimumStatus.NOT_EVALUATED
|
||||
assert tool.offer_calls == 0
|
||||
|
||||
|
||||
|
|
@ -348,7 +390,7 @@ def test_negative_each_length_reports_failing_port_without_offer_query() -> None
|
|||
assert details.portspec == 'A'
|
||||
assert details.request == {'ccw': None, 'each': -2}
|
||||
assert details.resolved_length == -2
|
||||
assert not details.minimum_attempted
|
||||
assert details.minimum_status is MinimumStatus.NOT_EVALUATED
|
||||
assert tool.offer_calls == 0
|
||||
|
||||
|
||||
|
|
@ -887,7 +929,7 @@ def test_pather_uturn_failed_two_bend_route_is_atomic() -> None:
|
|||
with pytest.raises(RouteError, match='U-turn') as exc_info:
|
||||
p.uturn('A', 1.5, length=0)
|
||||
|
||||
assert exc_info.value.details.minimum_attempted
|
||||
assert exc_info.value.details.minimum_status is MinimumStatus.NO_ROUTE
|
||||
assert exc_info.value.details.minimum_length is None
|
||||
|
||||
assert numpy.allclose(p.pattern.ports['A'].offset, (0, 0))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue