evalstone/evalscope/tests/service/test_process_stop.py
sora 13274243a0 Bump vendored EvalScope and add K3-ready DPV4 configs.
Keep K3 suite selection and report-schema scoring in bash, merge K3/vision dataset_args into dpv4 yamls, and pin EvalScope at 735d920ee911 with local patches.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 07:30:48 +00:00

85 lines
2.7 KiB
Python

# Copyright (c) Alibaba, Inc. and its affiliates.
"""Tests for user-initiated stop handling in service subprocess utilities.
Covers the interaction between ``run_in_subprocess`` and ``stop_process``:
a user-initiated stop must surface as :class:`TaskStoppedError` (graceful),
while a genuine child crash must still raise ``RuntimeError``.
"""
import multiprocessing
import threading
import time
import unittest
from evalscope.service.utils.process import (
TaskStoppedError,
_user_stopped_tasks,
register_process,
run_in_subprocess,
stop_process,
)
def _sleep_forever() -> str:
time.sleep(60)
return 'done'
def _quick_success() -> str:
return 'ok'
def _crash() -> None:
raise ValueError('boom')
class TestProcessStop(unittest.TestCase):
def test_user_stop_raises_task_stopped_error(self):
"""Stopping a running task raises TaskStoppedError, not RuntimeError."""
task_id = 'test-stop-graceful'
outcome = {}
def run() -> None:
try:
outcome['result'] = run_in_subprocess(_sleep_forever, task_id=task_id)
except Exception as e: # noqa: BLE001 - capture for assertion
outcome['exc'] = e
t = threading.Thread(target=run)
t.start()
time.sleep(3) # let the spawned child start sleeping
self.assertTrue(stop_process(task_id))
t.join(timeout=30)
self.assertIsInstance(outcome.get('exc'), TaskStoppedError)
def test_success_returns_result(self):
"""A normal run returns the child's result unchanged."""
self.assertEqual(run_in_subprocess(_quick_success, task_id='test-stop-success'), 'ok')
def test_child_crash_still_raises_runtime_error(self):
"""A genuine child error is not misreported as a user stop."""
with self.assertRaises(RuntimeError):
run_in_subprocess(_crash, task_id='test-stop-crash')
def test_stop_unknown_task_returns_false(self):
"""Stopping a non-existent task is a no-op returning False."""
self.assertFalse(stop_process('test-stop-nonexistent'))
def test_stop_after_natural_exit_is_noop(self):
"""Stopping an already-exited (but still registered) process must not
set the user-stop marker, so a pending success result is not masked."""
task_id = 'test-stop-already-dead'
proc = multiprocessing.get_context('spawn').Process(target=_quick_success)
proc.start()
proc.join() # let it exit naturally before the stop request
register_process(task_id, proc)
self.assertFalse(stop_process(task_id))
self.assertNotIn(task_id, _user_stopped_tasks)
if __name__ == '__main__':
unittest.main()