Files
enginex-ascend-910-vllm/tests/ut/patch/platform/test_patch_balance_schedule.py
Sun Ruoxi 7f8a1b1f7a init v0.23.0
Signed-off-by: Sun Ruoxi <sunruoxi@4paradigm.com>
2026-08-27 15:11:51 +08:00

202 lines
6.5 KiB
Python

# SPDX-License-Identifier: Apache-2.0
from types import SimpleNamespace
from unittest.mock import MagicMock, patch
import pytest
from vllm.v1.core.sched.interface import PauseState
from vllm.v1.core.sched.request_queue import SchedulingPolicy
from vllm_ascend.patch.platform.patch_balance_schedule import (
_ORIGINAL_SCHEDULER,
BalanceScheduler,
_disable_preemption_on_prefill_node,
)
@pytest.mark.parametrize(
("kv_role", "expected"),
[
("kv_producer", True),
("kv_consumer", False),
("kv_both", False),
(None, False),
],
)
def test_disable_preemption_only_on_v023_prefill_nodes(kv_role, expected):
kv_transfer_config = None if kv_role is None else SimpleNamespace(kv_role=kv_role)
vllm_config = SimpleNamespace(kv_transfer_config=kv_transfer_config)
with patch(
"vllm_ascend.patch.platform.patch_balance_schedule.vllm_version_is",
return_value=True,
):
assert _disable_preemption_on_prefill_node(vllm_config) is expected
def test_disable_preemption_is_limited_to_v023():
vllm_config = SimpleNamespace(kv_transfer_config=SimpleNamespace(kv_role="kv_producer"))
with patch(
"vllm_ascend.patch.platform.patch_balance_schedule.vllm_version_is",
return_value=False,
):
assert not _disable_preemption_on_prefill_node(vllm_config)
@pytest.mark.parametrize("scheduler_name", ["default", "profiling_chunk"])
def test_prefill_node_keeps_running_request_when_allocation_fails(
scheduler_name,
):
request = SimpleNamespace(
request_id="prefill-request",
num_output_placeholders=0,
num_tokens_with_spec=2,
num_computed_tokens=1,
num_prompt_tokens=2,
has_encoder_inputs=False,
spec_token_ids=[],
)
if scheduler_name == "default":
scheduler = BalanceScheduler.__new__(BalanceScheduler)
scheduler._balance_enabled = False
else:
from vllm_ascend.core.scheduler_profiling_chunk import (
ProfilingChunkScheduler,
)
scheduler = ProfilingChunkScheduler.__new__(ProfilingChunkScheduler)
scheduler.profiling_chunk_manager = SimpleNamespace(
predictor=SimpleNamespace(target_latency=None),
is_ready=False,
)
scheduler.needs_kv_cache_zeroing = False
scheduler._disable_preemption = True
scheduler._pause_state = PauseState.UNPAUSED
scheduler.running = [request]
scheduler.waiting = []
scheduler.skipped_waiting = []
scheduler.policy = SchedulingPolicy.FCFS
scheduler.max_num_scheduled_tokens = 1
scheduler.max_num_encoder_input_tokens = 0
scheduler.max_num_running_reqs = 1
scheduler.max_model_len = 16
scheduler.num_lookahead_tokens = 0
scheduler.need_mamba_block_aligned_split = False
scheduler.scheduler_config = SimpleNamespace(long_prefill_token_threshold=0)
scheduler.kv_cache_config = SimpleNamespace(kv_cache_groups=[object()])
scheduler.kv_cache_manager = MagicMock()
scheduler.kv_cache_manager.allocate_slots.return_value = None
scheduler.kv_cache_manager.get_num_common_prefix_blocks.return_value = [0]
scheduler.encoder_cache_manager = MagicMock()
scheduler.encoder_cache_manager.get_freed_mm_hashes.return_value = []
scheduler.connector = None
scheduler.ec_connector = None
scheduler.connector_prefix_cache_stats = None
scheduler.lora_config = None
scheduler.is_encoder_decoder = False
scheduler.log_stats = False
scheduler.use_eagle = False
scheduler.use_v2_model_runner = False
scheduler.finished_req_ids = set()
scheduler.prev_step_scheduled_req_ids = set()
scheduler._preempt_request = MagicMock()
scheduler._make_cached_request_data = MagicMock()
scheduler._update_after_schedule = MagicMock()
output = scheduler.schedule()
scheduler.kv_cache_manager.allocate_slots.assert_called_once_with(
request,
1,
num_lookahead_tokens=0,
)
scheduler._preempt_request.assert_not_called()
assert scheduler.running == [request]
assert output.total_num_scheduled_tokens == 0
assert output.preempted_req_ids == set()
def test_non_prefill_node_uses_upstream_scheduler():
scheduler = BalanceScheduler.__new__(BalanceScheduler)
scheduler._balance_enabled = False
scheduler._disable_preemption = False
expected = object()
with (
patch(
"vllm_ascend.patch.platform.patch_balance_schedule.vllm_version_is",
return_value=True,
),
patch.object(_ORIGINAL_SCHEDULER, "schedule", return_value=expected) as schedule,
):
assert scheduler.schedule() is expected
schedule.assert_called_once_with()
def test_async_scheduler_inherits_prefill_preemption_guard():
from vllm.v1.core.sched.async_scheduler import AsyncScheduler
assert BalanceScheduler in AsyncScheduler.__mro__
def test_profiling_chunk_scheduler_inherits_prefill_preemption_guard():
from vllm_ascend.core.scheduler_profiling_chunk import (
ProfilingChunkScheduler,
)
assert BalanceScheduler in ProfilingChunkScheduler.__mro__
def test_prefill_node_rejects_forced_prefix_cache_reset_while_running():
scheduler = BalanceScheduler.__new__(BalanceScheduler)
scheduler._disable_preemption = True
scheduler.running = [object()]
scheduler._preempt_request = MagicMock()
with pytest.raises(RuntimeError, match="drain or abort"):
scheduler.reset_prefix_cache(reset_running_requests=True)
scheduler._preempt_request.assert_not_called()
@pytest.mark.parametrize(
("disable_preemption", "running"),
[
(True, []),
(False, [object()]),
],
)
def test_forced_prefix_cache_reset_delegates_when_safe(
disable_preemption,
running,
):
scheduler = BalanceScheduler.__new__(BalanceScheduler)
scheduler._disable_preemption = disable_preemption
scheduler.running = running
with patch.object(
_ORIGINAL_SCHEDULER,
"reset_prefix_cache",
return_value=True,
) as reset_prefix_cache:
assert scheduler.reset_prefix_cache(True, True)
reset_prefix_cache.assert_called_once_with(True, True)
def test_non_forced_prefix_cache_reset_keeps_upstream_behavior():
scheduler = BalanceScheduler.__new__(BalanceScheduler)
scheduler._disable_preemption = True
scheduler.running = [object()]
with patch.object(
_ORIGINAL_SCHEDULER,
"reset_prefix_cache",
return_value=False,
) as reset_prefix_cache:
assert not scheduler.reset_prefix_cache()
reset_prefix_cache.assert_called_once_with(False, False)