120 lines
3.7 KiB
Python
120 lines
3.7 KiB
Python
"""内联执行器测试。
|
|||
|
|
|
||
|
|
重点是两件事:它**确实**同步跑完(违反 launch 不阻塞的约定),以及它对无法
|
||
|
|
支持的暂停能力**如实返回 False** 而不是假装成功。
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import asyncio
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
from chongming_task.models import FailureKind
|
||
|
|
from chongming_task.runtime.executors.base import ExecutionRequest
|
||
|
|
from chongming_task.runtime.executors.inline import InlineExecutor
|
||
|
|
|
||
|
|
|
||
|
|
def _request(target: Any, *args: Any) -> ExecutionRequest:
|
||
|
|
return ExecutionRequest(task_id="t-inline", name="t-inline", target=target, args=args)
|
||
|
|
|
||
|
|
|
||
|
|
def test_launch_runs_to_completion_before_returning() -> None:
|
||
|
|
"""这是它与 ``Executor`` 协议唯一的差异,也是它存在的意义。"""
|
||
|
|
order: list[str] = []
|
||
|
|
|
||
|
|
def work(ctx: Any) -> str:
|
||
|
|
del ctx
|
||
|
|
order.append("task")
|
||
|
|
return "done"
|
||
|
|
|
||
|
|
handle = InlineExecutor().launch(_request(work))
|
||
|
|
order.append("after-launch")
|
||
|
|
|
||
|
|
assert order == ["task", "after-launch"], "内联执行必须在 launch 返回前跑完"
|
||
|
|
assert handle.running is False
|
||
|
|
outcome = handle.wait()
|
||
|
|
assert outcome is not None
|
||
|
|
assert outcome.value == "done"
|
||
|
|
|
||
|
|
|
||
|
|
def test_arguments_are_forwarded_after_the_context() -> None:
|
||
|
|
def work(ctx: Any, left: int, right: int, *, scale: int = 1) -> int:
|
||
|
|
del ctx
|
||
|
|
return (left + right) * scale
|
||
|
|
|
||
|
|
handle = InlineExecutor().launch(
|
||
|
|
ExecutionRequest(
|
||
|
|
task_id="t-inline-args",
|
||
|
|
name="t-inline-args",
|
||
|
|
target=work,
|
||
|
|
args=(2, 3),
|
||
|
|
kwargs={"scale": 10},
|
||
|
|
)
|
||
|
|
)
|
||
|
|
outcome = handle.wait()
|
||
|
|
assert outcome is not None
|
||
|
|
assert outcome.value == 50
|
||
|
|
|
||
|
|
|
||
|
|
def test_failure_is_reported_like_any_other_executor() -> None:
|
||
|
|
def boom(ctx: Any) -> None:
|
||
|
|
del ctx
|
||
|
|
raise KeyError("missing")
|
||
|
|
|
||
|
|
outcome = InlineExecutor().launch(_request(boom)).wait()
|
||
|
|
assert outcome is not None
|
||
|
|
assert not outcome.ok
|
||
|
|
assert outcome.failure is not None
|
||
|
|
assert outcome.failure.kind is FailureKind.EXCEPTION
|
||
|
|
assert "KeyError" in outcome.failure.message
|
||
|
|
assert outcome.failure.traceback is not None
|
||
|
|
|
||
|
|
|
||
|
|
def test_pause_reports_false_because_it_cannot_be_honoured() -> None:
|
||
|
|
"""如实返回 False,调用方据此回滚为 RUNNING,而不是以为暂停成功了。"""
|
||
|
|
handle = InlineExecutor().launch(_request(lambda ctx: "ok"))
|
||
|
|
assert handle.pause(timeout=0.01) is False
|
||
|
|
|
||
|
|
|
||
|
|
def test_resume_is_harmless_after_the_fact() -> None:
|
||
|
|
handle = InlineExecutor().launch(_request(lambda ctx: "ok"))
|
||
|
|
handle.resume()
|
||
|
|
assert handle.running is False
|
||
|
|
|
||
|
|
|
||
|
|
def test_stop_on_a_finished_inline_task_returns_its_real_outcome() -> None:
|
||
|
|
"""已结束的任务应当返回真实结果,而不是落进"兜底放弃"分支。"""
|
||
|
|
handle = InlineExecutor().launch(_request(lambda ctx: 7))
|
||
|
|
outcome = handle.stop(graceful_timeout=1.0)
|
||
|
|
assert outcome.ok
|
||
|
|
assert outcome.value == 7
|
||
|
|
assert outcome.abandoned is False
|
||
|
|
|
||
|
|
|
||
|
|
def test_context_is_injected() -> None:
|
||
|
|
seen: dict[str, Any] = {}
|
||
|
|
|
||
|
|
def work(ctx: Any) -> str:
|
||
|
|
seen["task_id"] = ctx.task_id
|
||
|
|
seen["token"] = ctx.token
|
||
|
|
return "ok"
|
||
|
|
|
||
|
|
handle = InlineExecutor().launch(_request(work))
|
||
|
|
assert seen["task_id"] == "t-inline"
|
||
|
|
assert seen["token"] is handle.token
|
||
|
|
|
||
|
|
|
||
|
|
def test_async_target_runs_to_completion_before_launch_returns() -> None:
|
||
|
|
"""``async def`` 任务在内联执行器下同样是同步跑完的(本线程起一个事件循环)。"""
|
||
|
|
|
||
|
|
async def work(ctx: Any) -> int:
|
||
|
|
await asyncio.sleep(0)
|
||
|
|
return 42
|
||
|
|
|
||
|
|
handle = InlineExecutor().launch(_request(work))
|
||
|
|
assert handle.running is False
|
||
|
|
outcome = handle.wait()
|
||
|
|
assert outcome is not None
|
||
|
|
assert outcome.ok
|
||
|
|
assert outcome.value == 42
|