strategy_sums проверяет Deadline каждые DEADLINE_CHECK_EVERY = 500 шагов внутреннего перебора поддеревьев (худший случай O(n²) завершается TIMEOUT, а не висит); линейные стратегии — каждые 500 строк (D-8). Strategy — Protocol (rows, deadline=None); resolve_levels передаёт дедлайн. Тесты: истечение внутри sums, проверка перед каждой стратегией, пакинг линейных стратегий, часы по умолчанию — time.monotonic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
62 lines
1.8 KiB
Python
62 lines
1.8 KiB
Python
"""Кооперативный дедлайн разбора (design D-8, ruling R-3): проверка между шагами, часы подменяются в тестах."""
|
||
|
||
import inspect
|
||
import time
|
||
|
||
import pytest
|
||
|
||
from src.parser.deadline import Deadline
|
||
from src.parser.errors import ParseTimeoutError
|
||
|
||
|
||
class FakeClock:
|
||
"""Часы-заглушка: время двигается только вручную."""
|
||
|
||
def __init__(self) -> None:
|
||
self.now = 1000.0
|
||
|
||
def __call__(self) -> float:
|
||
return self.now
|
||
|
||
|
||
def test_check_passes_within_limit() -> None:
|
||
clock = FakeClock()
|
||
deadline = Deadline(10, clock=clock)
|
||
|
||
clock.now += 10
|
||
|
||
deadline.check("иерархия") # ровно на границе лимита — ещё не превышен
|
||
|
||
|
||
def test_check_raises_after_limit_with_step_name() -> None:
|
||
clock = FakeClock()
|
||
deadline = Deadline(120, clock=clock)
|
||
|
||
clock.now += 120.5
|
||
|
||
with pytest.raises(ParseTimeoutError) as raised:
|
||
deadline.check("иерархия (outline)")
|
||
|
||
assert raised.value.message == "превышен лимит 120 с на шаге иерархия (outline)"
|
||
|
||
|
||
def test_limit_is_counted_from_creation() -> None:
|
||
clock = FakeClock()
|
||
clock.now += 500
|
||
deadline = Deadline(1, clock=clock)
|
||
|
||
deadline.check("старт")
|
||
|
||
|
||
def test_default_clock_is_monotonic() -> None:
|
||
default = inspect.signature(Deadline).parameters["clock"].default
|
||
|
||
assert default is time.monotonic
|
||
|
||
|
||
def test_default_clock_measures_real_time() -> None:
|
||
Deadline(60).check("сразу") # монотонные часы: свежий лимит не превышен
|
||
|
||
with pytest.raises(ParseTimeoutError):
|
||
Deadline(-1).check("уже в прошлом") # граница раньше «сейчас» по тем же часам
|