Andrey Epifancev 68c5e2cce0 fix(parser): дедлайн внутри стратегий иерархии, тесты дедлайна (T6.1, T6.5)
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
2026-10-05 13:40:20 +04:00

62 lines
1.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Кооперативный дедлайн разбора (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("уже в прошлом") # граница раньше «сейчас» по тем же часам