Skip to content

test(core): stop lifecycle restart test from hanging the suite - #10640

Closed
yuexia5 wants to merge 1 commit into
AstrBotDevs:masterfrom
yuexia5:fix/lifecycle-restart-test-hang
Closed

yuexia5 wants to merge 1 commit into
AstrBotDevs:masterfrom
yuexia5:fix/lifecycle-restart-test-hang

Conversation

@yuexia5

@yuexia5 yuexia5 commented Oct 20, 2026 •

Copy link
Copy Markdown

tests/unit/test_core_lifecycle.py::TestAstrBotCoreLifecycleRestart::test_restart_terminates_managers_and_starts_thread hangs forever, so every Unit Tests run now dies on the 30-minute job timeout.

On master all three lanes are cancelled at exactly the timeout:

ubuntu-latest   09:30:54 -> 10:01:08
macos-latest    09:31:40 -> 10:01:58
windows-latest  09:30:46 -> 10:00:59

and the ubuntu log stops at 77%, right before tests/unit/test_core_lifecycle.py:

tests/unit/test_conversation_restart.py ... [ 76%]
............................................................  [ 77%]
<26 minutes of silence, then the job is killed>

Root cause

The test patches astrbot.core.core_lifecycle.threading.Thread:

with patch("astrbot.core.core_lifecycle.threading.Thread") as mock_thread:

astrbot.core.core_lifecycle.threading is the global threading module, so this replaces threading.Thread process-wide.

AstrBotCoreLifecycle.restart() (await added in #10515) then calls:

await asyncio.to_thread(close_trace_store)

asyncio.to_thread() submits to a ThreadPoolExecutor, and its
_adjust_thread_count() spawns workers with threading.Thread. With the mock
in place the "worker" is a MagicMock that never runs, so the callable is never
executed and the awaited future never resolves. The event loop goes idle in
selectors.select and the test hangs forever.

Minimal reproduction, independent of the test suite:

import asyncio
from unittest.mock import patch


def work():
    print("worker ran")


async def main():
    await asyncio.to_thread(work)


with patch("astrbot.core.core_lifecycle.threading.Thread"):
    asyncio.run(asyncio.wait_for(main(), timeout=3))  # never prints "worker ran"

Fix

Patch the threading reference on the lifecycle module instead of
threading.Thread itself, so the real threading module, threading.Thread
and the default executor stay intact. The assertions on the restart thread are
unchanged.

Verification

Before, on master:

$ timeout 60 python -m pytest tests/unit/test_core_lifecycle.py::TestAstrBotCoreLifecycleRestart
<hangs, killed by timeout>

After:

$ python -m pytest tests/unit/test_core_lifecycle.py
26 passed in 2.44s

$ python -m pytest tests
4667 passed, 12 skipped in 163.05s (0:02:43)

The suite previously stalled at 77% for the full 30-minute CI job timeout.

Summary by Sourcery

Fix the lifecycle restart test to isolate its thread mock and prevent executor-backed asynchronous work from hanging the test suite.

Bug Fixes:

  • Prevent the lifecycle restart unit test from hanging by avoiding process-wide mocking of Python’s threading implementation.

Tests:

  • Update lifecycle restart test mocking so the full unit test suite completes successfully.

`test_restart_terminates_managers_and_starts_thread` patched
`astrbot.core.core_lifecycle.threading.Thread`, and that attribute is the
global `threading` module, so the patch replaced `threading.Thread`
process-wide.

`AstrBotCoreLifecycle.restart()` awaits `asyncio.to_thread(close_trace_store)`,
and `ThreadPoolExecutor` creates its workers with `threading.Thread`. With the
process-wide mock the worker was a `MagicMock` that never ran, so the submitted
callable was never executed and the awaited future never completed: the test
hung until the 30-minute CI job timeout.

Patch the `threading` reference on the lifecycle module instead, which leaves
the real `threading.Thread` and the default executor intact. The assertions on
the restart thread are unchanged.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!

Sourcery assessment

Approved.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@yuexia5

yuexia5 commented Oct 20, 2026

Copy link
Copy Markdown
Author

Closing as a duplicate of #10617, which fixes the same restart-test hang (it mocks asyncio.to_thread in addition to the threading.Thread patch, so the executor worker is never created). #10617 also covers the related trace sn ordering and Windows test issues in the same change set, so keeping both would only split the review. Thanks @wcqqq1214 — I missed it when I opened this.

@yuexia5 yuexia5 closed this Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant