Skip to content

fix(system): 修复队列完成后电源操作因先请求前端关闭而永远无法执行的问题 - #612

Open
1w1w11w1 wants to merge 1 commit into
devfrom
fix/queue-power-operation
Open

fix(system): 修复队列完成后电源操作因先请求前端关闭而永远无法执行的问题#612
1w1w11w1 wants to merge 1 commit into
devfrom
fix/queue-power-operation

Conversation

@1w1w11w1

@1w1w11w1 1w1w11w1 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Closes #611

摘要

  • 队列完成后的系统电源操作(关机 / 强制关机 / 重启 / 休眠 / 睡眠 / 注销)改为直接执行,不再先请求前端关闭并等待其退出:前端退出会让 Electron 退出并连带结束后端进程,导致关机、重启命令永远没有机会执行(v5.5.0-beta.2 起回归,表现为倒计时结束后只有软件退出)
  • KillSelf 退出路径保留先请求前端关闭的既有行为,该 helper 现仅服务此场景
  • 新增回归测试钉住边界:系统电源动作不依赖前端关闭,KillSelf 行为不变

验证

  • python -m pytest tests/services/test_power_countdown.py tests/services/test_set_power_execution.py -q:5 passed
  • python -m pytest tests --collect-only -q:626 collected,exit 0
  • ruff check / ruff format --check:通过

@sourcery-ai

sourcery-ai Bot commented Sep 8, 2026

Copy link
Copy Markdown

审查者指南

修复队列完成后的系统电源操作流程:关机、强制关机、重启、休眠、睡眠和注销现在直接执行,不再等待前端退出;仅 KillSelf 保留原有的前端关闭流程,并通过回归测试锁定两类行为边界。

队列完成后直接执行系统电源操作的时序图

sequenceDiagram
    participant Queue as QueueCompletion
    participant System as SystemService
    participant Power as PowerExecutor
    participant Frontend as Frontend

    Queue->>System: set_power(mode)
    System->>Power: execute(mode)
    Power-->>System: power action started
    Note over Frontend,System: Frontend close is not requested
Loading

文件级变更

变更 详情 文件
执行系统电源动作时绕过前端关闭流程,避免 Electron 退出连带终止后端而阻止关机、重启等命令执行。
  • 移除系统电源模式执行前对前端关闭的等待
  • 保留 KillSelf 场景请求前端关闭并设置服务退出标志的行为
  • 更新关闭 helper 文档和日志以限定其适用场景
app/services/system.py
增加回归测试,验证系统电源动作与 KillSelf 的前端关闭依赖边界。
  • 断言 ShutdownForce 直接执行且不请求前端关闭
  • 断言 KillSelf 仍请求前端关闭并触发服务退出
tests/services/test_set_power_execution.py
更新应用版本资源。
  • 调整版本信息以反映修复版本
res/version.json

可能关联的问题


提示和命令

与 Sourcery 交互

  • 触发新的审查: 在拉取请求中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 根据审查评论创建 GitHub issue: 回复审查评论,请 Sourcery 根据该评论创建 issue。你也可以回复审查评论并使用 @sourcery-ai issue,以根据该评论创建 issue。
  • 生成拉取请求标题: 在拉取请求标题的任意位置写入 @sourcery-ai,即可随时生成标题。你也可以在拉取请求中评论 @sourcery-ai title,以随时重新生成标题。
  • 生成拉取请求摘要: 在拉取请求正文的任意位置写入 @sourcery-ai summary,即可在指定位置随时生成 PR 摘要。你也可以在拉取请求中评论 @sourcery-ai summary,以随时重新生成摘要。
  • 生成审查者指南: 在拉取请求中评论 @sourcery-ai guide,即可随时重新生成审查者指南。
  • 解决所有 Sourcery 评论: 在拉取请求中评论 @sourcery-ai resolve,即可解决所有 Sourcery 评论。如果你已经处理完所有评论并且不想再看到它们,这个功能会很有用。
  • 忽略所有 Sourcery 审查: 在拉取请求中评论 @sourcery-ai dismiss,即可忽略所有现有的 Sourcery 审查。如果你想从新的审查开始,这个功能尤其有用——别忘了评论 @sourcery-ai review 以触发新的审查!

自定义你的使用体验

访问你的控制面板以:

  • 启用或禁用审查功能,例如 Sourcery 生成的拉取请求摘要、审查者指南等。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查说明。
  • 调整其他审查设置。

获取帮助

Original review guide in English

Reviewer's Guide

修复队列完成后的系统电源操作流程:关机、强制关机、重启、休眠、睡眠和注销现在直接执行,不再等待前端退出;仅 KillSelf 保留原有的前端关闭流程,并通过回归测试锁定两类行为边界。

Sequence diagram for direct system power execution after queue completion

sequenceDiagram
    participant Queue as QueueCompletion
    participant System as SystemService
    participant Power as PowerExecutor
    participant Frontend as Frontend

    Queue->>System: set_power(mode)
    System->>Power: execute(mode)
    Power-->>System: power action started
    Note over Frontend,System: Frontend close is not requested
Loading

File-Level Changes

Change Details Files
执行系统电源动作时绕过前端关闭流程,避免 Electron 退出连带终止后端而阻止关机、重启等命令执行。
  • 移除系统电源模式执行前对前端关闭的等待
  • 保留 KillSelf 场景请求前端关闭并设置服务退出标志的行为
  • 更新关闭 helper 文档和日志以限定其适用场景
app/services/system.py
增加回归测试,验证系统电源动作与 KillSelf 的前端关闭依赖边界。
  • 断言 ShutdownForce 直接执行且不请求前端关闭
  • 断言 KillSelf 仍请求前端关闭并触发服务退出
tests/services/test_set_power_execution.py
更新应用版本资源。
  • 调整版本信息以反映修复版本
res/version.json

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

嘿——我发现了 1 个问题

给 AI 代理的提示
请处理此次代码审查中的评论:

## 单独评论

### 评论 1
<location path="tests/services/test_set_power_execution.py" line_range="14-18" />
<code_context>
+                "app.services.system.power.execute", new_callable=AsyncMock
+            ) as execute,
+        ):
+            await System.set_power("ShutdownForce")
+
+        execute.assert_awaited_once_with("ShutdownForce")
</code_context>
<issue_to_address>
**issue (testing):** 回归测试在未模拟 `kill_emulator_processes` 的情况下调用 `System.set_power("ShutdownForce")`,因此会扫描主机进程表,并终止名称包含模拟器相关关键词的所有进程。在存在匹配的模拟器进程的机器上运行该测试,可能会终止该进程,使测试具有破坏性或不稳定性。

**触发条件:** 当测试在开发者机器或 CI 机器上运行,且其中存在名称包含 `Nemu``emulator``MuMu` 的进程时。

**建议修复:** 在调用 `set_power` 之前,在测试中使用 `AsyncMock``System.kill_emulator_processes` 进行 patch。

```suggestion
            patch.object(
                System, "kill_emulator_processes", new_callable=AsyncMock
            ),
            patch(
                "app.services.system.power.execute", new_callable=AsyncMock
            ) as execute,
        ):
            await System.set_power("ShutdownForce")
```
</issue_to_address>

Sourcery 评估

需要人工审查。 首先需要处理 1 个发现的问题;如果执行顺序的变更不正确,排队中的关机、重启或注销操作可能会在不应执行时执行,从而立即导致服务中断或用户会话丢失,而这类后果无法通过回滚撤销。受影响的操作范围有限,未来的行为可以通过回滚恢复,但任何已经触发的电源操作都已生效。

阻塞性发现:tests/services/test_set_power_execution.py:18


Sourcery 对开源项目免费——如果您喜欢我们的审查结果,请考虑分享 ✨
Original comment in English

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="tests/services/test_set_power_execution.py" line_range="14-18" />
<code_context>
+                "app.services.system.power.execute", new_callable=AsyncMock
+            ) as execute,
+        ):
+            await System.set_power("ShutdownForce")
+
+        execute.assert_awaited_once_with("ShutdownForce")
</code_context>
<issue_to_address>
**issue (testing):** The regression test calls `System.set_power("ShutdownForce")` without mocking `kill_emulator_processes`, so it scans the host process table and kills every process whose name contains emulator-related keywords. Running the test on a machine with a matching emulator process can terminate that process and make the test destructive or flaky.

**Triggers:** When the test runs on a developer or CI machine with a process whose name contains `Nemu`, `emulator`, or `MuMu`.

**Suggested fix:** Patch `System.kill_emulator_processes` with an `AsyncMock` in the test before invoking `set_power`.

```suggestion
            patch.object(
                System, "kill_emulator_processes", new_callable=AsyncMock
            ),
            patch(
                "app.services.system.power.execute", new_callable=AsyncMock
            ) as execute,
        ):
            await System.set_power("ShutdownForce")
```
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 1 finding to address first, and if the sequencing change is wrong, a queued Shutdown, Reboot, or Logoff can execute when it should not, causing an immediate outage or user session loss that reverting cannot undo. The affected operation is bounded and future behavior can be restored by reverting, but any power action already triggered has already taken effect.

Blocking findings: tests/services/test_set_power_execution.py:18


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

Comment on lines +14 to +18
patch(
"app.services.system.power.execute", new_callable=AsyncMock
) as execute,
):
await System.set_power("ShutdownForce")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (testing): 回归测试在未模拟 kill_emulator_processes 的情况下调用 System.set_power("ShutdownForce"),因此会扫描主机进程表,并终止名称包含模拟器相关关键词的所有进程。在存在匹配的模拟器进程的机器上运行该测试,可能会终止该进程,使测试具有破坏性或不稳定性。

触发条件: 当测试在开发者机器或 CI 机器上运行,且其中存在名称包含 NemuemulatorMuMu 的进程时。

建议修复: 在调用 set_power 之前,在测试中使用 AsyncMockSystem.kill_emulator_processes 进行 patch。

Suggested change
patch(
"app.services.system.power.execute", new_callable=AsyncMock
) as execute,
):
await System.set_power("ShutdownForce")
patch.object(
System, "kill_emulator_processes", new_callable=AsyncMock
),
patch(
"app.services.system.power.execute", new_callable=AsyncMock
) as execute,
):
await System.set_power("ShutdownForce")
Original comment in English

issue (testing): The regression test calls System.set_power("ShutdownForce") without mocking kill_emulator_processes, so it scans the host process table and kills every process whose name contains emulator-related keywords. Running the test on a machine with a matching emulator process can terminate that process and make the test destructive or flaky.

Triggers: When the test runs on a developer or CI machine with a process whose name contains Nemu, emulator, or MuMu.

Suggested fix: Patch System.kill_emulator_processes with an AsyncMock in the test before invoking set_power.

Suggested change
patch(
"app.services.system.power.execute", new_callable=AsyncMock
) as execute,
):
await System.set_power("ShutdownForce")
patch.object(
System, "kill_emulator_processes", new_callable=AsyncMock
),
patch(
"app.services.system.power.execute", new_callable=AsyncMock
) as execute,
):
await System.set_power("ShutdownForce")

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