Skip to content

test: add unit tests for deviceinfo ThreadPoolTask/ThreadPool/MainJob/DeviceInterface - #930

Merged
deepin-bot[bot] merged 2 commits into
linuxdeepin:masterfrom
add-uos:ut-t29
Oct 27, 2026
Merged

deepin-bot[bot] merged 2 commits into
linuxdeepin:masterfrom
add-uos:ut-t29

Conversation

@add-uos

@add-uos add-uos commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Add/rewrite Google Test suites for the server device-info module (deepin-devicemanager-server/deepin-deviceinfo/src), covering ThreadPoolTask / ThreadPool / MainJob / DeviceInterface, plus full coverage instrumentation and tiered coverage reporting.

Test scope and counts

File Action Classes Test cases
tests/src/LoadInfo/ut_ThreadPoolTask.cpp rewritten ThreadPoolTask (15 methods) 59
tests/src/LoadInfo/ut_ThreadPool.cpp rewritten ThreadPool 26
tests/src/ut_mainjob.cpp rewritten + extended MainJob (17 methods) 27
tests/src/LoadInfo/ut_DeviceInterface.cpp new DeviceInterface (7 methods) 21

Total in-scope: 4 classes / 133 test cases (pre-existing suites untouched; binary total 334 cases / 20 suites).

Build & run results

  • CMake (Qt 6.8.0, Debug, coverage instrumentation) + full binary build: 0 errors
  • Test run: 333 passed / 1 failed (324 → 334 cases across two commits)
  • The sole failure CoreCpu_UT.LogicalIsExisted_NegativeCoreId_ReturnsFalse is pre-existing on base 6ccddaf (verified via git stash; out of this task's scope)
  • self-check-structural: 0 errors across all 4 files (1 benign SOLE_BOOL warning on the DISABLE_POLKIT one-liner test)

Coverage summary (gcov tracefile; lcov unavailable in env)

Tiered gates (line / function) all pass:

Level Methods Function cov Line cov Gate
high 15 100% 91.6% ≥90% line, 100% fn ✅
mid 18 100% 92.5% ≥60% line, 100% fn ✅
low 6 100% 94.7% ≥60% line ✅
total 39 100% matched 92.0% ✅

Uncovered functions: none (39/39 inventory methods matched and called; remaining lines are timer-deferred lambdas and root-owned-path branches exercised via stubs).

Defect report (Mode 5)

  • 0 suspected source defects in the 4 target files
  • 1 pre-existing test defect (low): CoreCpu_UT.LogicalIsExisted_NegativeCoreId_ReturnsFalse — the case inserts id 0 then asserts logicalIsExisted(0) is false, contradicting the plain map-find implementation at corecpu.cpp:40. Details + suggested fix in tests/.ut/defects.json / defects-summary.md (uploaded to the issue).

Stubbing strategy

External dependencies (DBus, QFile, QProcess, QThread, timers, sysfs) are fully stubbed — tests never touch the real system. Virtual member functions (QFile::open, QIODevice::readAll) are stubbed via dlsym-resolved pmfs (prologue patching with &Class::method addresses is unsafe for virtuals — vtable-index-encoded, not code addresses).

Commits

  • 0f04cce6 test(LoadInfo): rewrite ThreadPoolTask/ThreadPool/MainJob and add DeviceInterface GTest suites
  • 6036734a test(LoadInfo): cover remaining MainJob/DeviceInterface branches, lift tiered coverage to gates

Summary by Sourcery

Strengthen server device-info regression protection by adding isolated unit tests for the core device-loading, monitoring, caching, and interface workflows.

New Features:

  • Add comprehensive Google Test coverage for DeviceInterface, ThreadPool, ThreadPoolTask, and MainJob in the server device-info module.

Enhancements:

  • Expand unit tests to cover command execution, device-info caching, authorization, hardware monitoring, CPU/GPU information, driver repository handling, and deferred update paths using isolated dependency stubs.

Tests:

  • Rewrite the ThreadPoolTask, ThreadPool, and MainJob test suites and add a new DeviceInterface suite, covering 133 in-scope test cases with tiered line and function coverage gates.

@sourcery-ai

sourcery-ai Bot commented Oct 26, 2026

Copy link
Copy Markdown

Sorry @add-uos, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 2 days and 15 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@github-actions

Copy link
Copy Markdown
  • 检测到敏感词lshw, dmidecode变动
详情
    {
    "lshw": {
        "deepin-devicemanager-server/tests/src/LoadInfo/ut_ThreadPool.cpp": [
            " *   B1: lshw appended to both lists, canNotReplace=false",
            "    // Act \u2014 find lshw entry in both lists",
            "        if (c.file == \"lshw.txt\")",
            "    EXPECT_EQ(std::count_if(pool->m_ListCmd.cbegin(), pool->m_ListCmd.cend(), [](const Cmd &c) { return c.file == \"lshw.txt\"; }), 1);",
            "    // Act \u2014 lshw cmd redirects to PATH/lshw.txt",
            "    // Act \u2014 m_ListCmd: lshw + 9 dmidecode + upower + lscpu + lsblk + ls_sg + lspci +",
            "    // Act \u2014 m_ListUpdate: lshw + upower + lscpu + lsblk + ls_sg + lspci + lpstat +"
        ],
        "deepin-devicemanager-server/tests/src/LoadInfo/ut_ThreadPoolTask.cpp": [
            "    ThreadPoolTask task(\"lshw > f\", \"ut_r2f_cnr.txt\", true, -1);",
            "    task.runCmdToFile(\"lshw > f\");   // branch: CanNotReplace && exists -> return",
            "    ThreadPoolTask task(\"lshw > f\", \"ut_r2f_rep.txt\", false, -1);",
            "    task.runCmdToFile(\"lshw > f\");   // branch: replace allowed -> runCmd(cmd)",
            "    EXPECT_EQ(ut_tt_runCmd1Calls, QStringList() << QString(\"lshw > f\"));"
        ]
    },
    "dmidecode": {
        "deepin-devicemanager-server/tests/src/LoadInfo/ut_ThreadPool.cpp": [
            " *   B2: dmidecode_spn appended to m_ListCmd only, canNotReplace=true",
            "    // Act \u2014 m_ListCmd contains dmidecode-only entries not in m_ListUpdate",
            "        if (c.file == \"dmidecode_spn.txt\")",
            "    EXPECT_EQ(std::count_if(pool->m_ListCmd.cbegin(), pool->m_ListCmd.cend(), [](const Cmd &c) { return c.file == \"dmidecode_spn.txt\"; }), 1);",
            "    EXPECT_EQ(std::count_if(pool->m_ListUpdate.cbegin(), pool->m_ListUpdate.cend(), [](const Cmd &c) { return c.file == \"dmidecode_spn.txt\"; }), 0);",
            "    // Act \u2014 m_ListCmd: lshw + 9 dmidecode + upower + lscpu + lsblk + ls_sg + lspci +"
        ]
    }
}

@sourcery-ai

sourcery-ai Bot commented Oct 26, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds or rewrites four Google Test suites for ThreadPoolTask, ThreadPool, MainJob, and DeviceInterface, using deterministic stubs for system and D-Bus dependencies to provide 133 focused tests and 100% function coverage with 92.0% line coverage; the reported single failing test is documented as pre-existing outside this PR.

File-Level Changes

Change Details Files
Rewrote and expanded ThreadPoolTask unit coverage with isolated Google Test fixtures and dependency stubs.
  • Covers command execution, wildcard handling, cache replacement, CPU loading, display-width detection, smartctl/lspci enrichment, and file-output paths.
  • Stubs QProcess, CpuInfo, QFile, and virtual Qt methods while observing real in-memory DeviceInfoManager state.
  • Adds explicit branch-oriented tests for success, failure, timeout, malformed input, and early-return behavior.
deepin-devicemanager-server/tests/src/LoadInfo/ut_ThreadPoolTask.cpp
Reworked ThreadPool tests to validate command initialization and task scheduling without launching real worker processes.
  • Verifies command/update list contents, flags, waiting times, directory setup, and inheritance.
  • Stubs QThreadPool and QProcess to inspect queued task fields and cached command output.
  • Adds coverage for empty commands, argument parsing, and normal cache updates.
deepin-devicemanager-server/tests/src/LoadInfo/ut_ThreadPool.cpp
Rewrote MainJob tests around deterministic state transitions and external-interaction stubs.
  • Covers construction, first versus subsequent device updates, working flags, USB/driver events, instruction handling, and running-state flags.
  • Exercises version parsing, driver repository decisions, deferred wakeup behavior, and hardware-info persistence.
  • Stubs thread pools, timers, D-Bus-facing collaborators, QProcess, QFile, and filesystem probes.
deepin-devicemanager-server/tests/src/ut_mainjob.cpp
Added a dedicated DeviceInterface suite covering authorization, manager lookup, signal emission, monitoring delegation, and GPU memory handling.
  • Tests parent/bus construction and authorization-dependent API behavior.
  • Verifies MainJob delegation and GPU result caching within the TTL.
  • Exercises QFile-backed GPU parsing for failure cases and MB/GB/KB/B unit conversion using virtual-method stubs.
deepin-devicemanager-server/tests/src/LoadInfo/ut_DeviceInterface.cpp

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

@github-actions

Copy link
Copy Markdown
  • 检测到敏感词lshw, dmidecode变动
详情
    {
    "lshw": {
        "deepin-devicemanager-server/tests/src/LoadInfo/ut_ThreadPool.cpp": [
            " *   B1: lshw appended to both lists, canNotReplace=false",
            "    // Act \u2014 find lshw entry in both lists",
            "        if (c.file == \"lshw.txt\")",
            "    EXPECT_EQ(std::count_if(pool->m_ListCmd.cbegin(), pool->m_ListCmd.cend(), [](const Cmd &c) { return c.file == \"lshw.txt\"; }), 1);",
            "    // Act \u2014 lshw cmd redirects to PATH/lshw.txt",
            "    // Act \u2014 m_ListCmd: lshw + 9 dmidecode + upower + lscpu + lsblk + ls_sg + lspci +",
            "    // Act \u2014 m_ListUpdate: lshw + upower + lscpu + lsblk + ls_sg + lspci + lpstat +"
        ],
        "deepin-devicemanager-server/tests/src/LoadInfo/ut_ThreadPoolTask.cpp": [
            "    ThreadPoolTask task(\"lshw > f\", \"ut_r2f_cnr.txt\", true, -1);",
            "    task.runCmdToFile(\"lshw > f\");   // branch: CanNotReplace && exists -> return",
            "    ThreadPoolTask task(\"lshw > f\", \"ut_r2f_rep.txt\", false, -1);",
            "    task.runCmdToFile(\"lshw > f\");   // branch: replace allowed -> runCmd(cmd)",
            "    EXPECT_EQ(ut_tt_runCmd1Calls, QStringList() << QString(\"lshw > f\"));"
        ]
    },
    "dmidecode": {
        "deepin-devicemanager-server/tests/src/LoadInfo/ut_ThreadPool.cpp": [
            " *   B2: dmidecode_spn appended to m_ListCmd only, canNotReplace=true",
            "    // Act \u2014 m_ListCmd contains dmidecode-only entries not in m_ListUpdate",
            "        if (c.file == \"dmidecode_spn.txt\")",
            "    EXPECT_EQ(std::count_if(pool->m_ListCmd.cbegin(), pool->m_ListCmd.cend(), [](const Cmd &c) { return c.file == \"dmidecode_spn.txt\"; }), 1);",
            "    EXPECT_EQ(std::count_if(pool->m_ListUpdate.cbegin(), pool->m_ListUpdate.cend(), [](const Cmd &c) { return c.file == \"dmidecode_spn.txt\"; }), 0);",
            "    // Act \u2014 m_ListCmd: lshw + 9 dmidecode + upower + lscpu + lsblk + ls_sg + lspci +"
        ]
    }
}

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

🤖 AI 代码审查报告

总体评分: 92 分 (通过阈值: 70分)

Pass


📊 总体评价

项目 结果
审查结论 代码审查通过
评分详情 本次提交为 deviceinfo 模块(ThreadPoolTask/ThreadPool/MainJob/DeviceInterface)新增/重写单元测试,commit 目的明确。测试代码结构清晰、分支覆盖文档完善、采用 AAA 编排并合理 stub 外部依赖(QProcess/QFile/D-Bus/线程),无安全漏洞。启发式扫描满分,OCR 提出 16 条均为测试质量/可维护性建议(无安全漏洞)。综合四维度评估:语法逻辑与代码质量存在少量测试健壮性与可维护性改进点(函数局部静态量导致的测试顺序依赖、DeviceInfoManager 单例状态在部分文件未在 TearDown 清理、dlsym 失败时 abort 直接终止测试进程、个别同义重言测试与残留标记),性能与安全维度无问题。建议合入并按建议优化测试隔离性。

🔍 详细分析

1. 语法逻辑 ✅

评价: 语法正确,逻辑清晰 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: ['Constructor_AfterInit_CreatesDeviceInfoDirectory:Arrange 阶段先删除 PATH 目录、Act 段不再调用 mkpath,仅依赖构造函数的 mkdir,再断言 exists,使测试真正验证副作用', 'ut_mainjob.cpp 中 ut_mj_msleepMs 比较统一使用 1000ul,避免 unsigned int/unsigned long 宽度差异告警']


2. 代码质量 ✅

评价: 代码结构清晰,注释完整 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: ['统一在所有测试 fixture 的 TearDown 中清理 DeviceInfoManager 单例状态(或在 DeviceInfoManager 增加 public clear()/reset() 方法供测试使用),保证测试隔离', 'dlsym 失败时改用 ADD_FAILURE()/GTEST_FAIL 并返回空 PMF 或抛异常,避免 abort 终止整个二进制;调用方校验 PMF 有效性', 'ut_ThreadPool.cpp 显式 #include ', "删除或解释 '// DBG_STUB_INVOKE' 标记注释", '移除未使用的死变量,或补全对应断言', '为 pmfFromSymbol 的 memcpy 增加 static_assert(sizeof(T) >= sizeof(void*)) 编译期守护']


3. 代码性能 ✅

评价: 性能良好,资源使用合理 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: ['ut_ThreadPoolTask.cpp 可在 TearDown 增加 QDir(PATH).removeRecursively() 清理测试产生的临时文件,避免反复运行后 /tmp/device-info 残留文件累积']


4. 代码安全 🔒

评价: 存在0个安全漏洞 ✅ 通过

🔐 发现 0 个安全漏洞

安全漏洞详情:
✅ 未发现安全漏洞

建议: ['无安全相关改进建议']


💡 改进建议代码示例

// ut_ThreadPool.cpp — 显式包含 <algorithm> 并清理单例状态
#include <gtest/gtest.h>
#include <QProcess>
#include <QDir>
#include <algorithm>          // 显式包含,避免传递依赖
#include "threadpool.h"
#include "threadpooltask.h"
#include "deviceinfomanager.h"
#include "DDLog.h"
#include "../stub.h"

#define private public
#include "deviceinfomanager.h"
#undef private

class ThreadPool_UT : public ::testing::Test
{
public:
    Stub stub;
    ThreadPool *pool = nullptr;
    void SetUp() override
    {
        ut_tp_resetState();
        DeviceInfoManager::getInstance()->m_MapInfo.clear();   // 统一清理单例
        stub.set(ut_tp_startFn, ut_tp_poolStart);
        stub.set(ut_tp_procStartFn, ut_tp_procStart);
        stub.set(ADDR(QProcess, waitForFinished), ut_tp_waitFinished);
        stub.set(ADDR(QProcess, readAllStandardOutput), ut_tp_readOut);
        QDir().mkpath(PATH);
        pool = new ThreadPool();
    }
    void TearDown() override
    {
        delete pool;
        pool = nullptr;
        DeviceInfoManager::getInstance()->m_MapInfo.clear();   // 统一清理单例
        stub.reset(ADDR(QProcess, readAllStandardOutput));
        stub.reset(ADDR(QProcess, waitForFinished));
        stub.reset(ut_tp_procStartFn);
        stub.reset(ut_tp_startFn);
    }
};

// ut_mainjob.cpp — dlsym 失败时优雅失败而非 abort
template <typename T>
static T ut_mj_pmfFromSymbol(const char *mangled)
{
    void *addr = dlsym(RTLD_DEFAULT, mangled);
    if (!addr) {
        ADD_FAILURE() << "dlsym failed for " << mangled;
        return T{};   // 返回空 PMF,调用方应校验
    }
    static_assert(sizeof(T) >= sizeof(void *), "PMF layout assumption violated");
    T pmf;
    std::memset(&pmf, 0, sizeof(pmf));
    std::memcpy(&pmf, &addr, sizeof(void *));
    return pmf;
}

本报告由 AI 代码审查工具自动生成

…iceInterface GTest suites

重写 server 端 loadinfo 四个类的单元测试并补齐 DeviceInterface:

- ut_ThreadPoolTask.cpp:重写 59 用例,覆盖 15 方法(ctor/run/runCmd/
  runCmdToCache/runCmdToFile/loadCpuInfo 及全部 load*ToCache/ToFile 解析器),
  重定向/管道/通配符分支与智能谓词解析全覆盖
- ut_ThreadPool.cpp:重写 26 用例,覆盖 initCmd 全部分支(15 段命令模板)
- ut_mainjob.cpp:重写 18 用例,覆盖 ctor/updateAllDevice/slotUsbChanged/
  executeClientInstruction/setWorkingFlag,单例计数器跨用例隔离
- ut_DeviceInterface.cpp:新增 20 用例,覆盖 getInfo/refreshInfo/
  getGpuInfoForFTDTM/getGpuMemInfoForFTDTM 单位换算/setMonitorDeviceFlag 授权分支
- stub 要点:QThreadPool::start 按自动删除语义释放;QFile::open/exists 与
  QIODevice::readAll 为虚函数,取址得到 vtable 索引编码的 pmf,改用 dlsym
  解析真实代码地址构造 pmf 后再 prologue 打桩
- 外部依赖(QProcess/DBus/文件系统/sysfs)全部 stub,不依赖真实设备环境;
  DeviceInfoManager 单例在 SetUp/TearDown 用 m_MapInfo.clear() 隔离
- 合跑 323/324 PASSED(唯一失败 CoreCpu_UT.LogicalIsExisted_NegativeCoreId
  为基线 6ccddaf 既有失败,经 git stash 验证与本批改动无关,用例前提
  与 logicalIsExisted 语义不符,留待后续批次修正)
…t tiered coverage to gates

Mode 2/3 coverage completion for deepin-deviceinfo (ut-t29 worktree):

- ut_mainjob.cpp (+9 cases): getVersion (file unopenable / Major-Minor
  parsing), initDriverRepoSource (pro-driver early return / device repo
  exists / open fail), slotDriverControl (success triggers DETECT /
  failure no-op), slotWakeupHandle (singleShot deferral), sqlCopytoKernel
  (disableInDevice + hwinfo probe persisted via stubbed QProcess and
  ControlInterface)
- ut_DeviceInterface.cpp (+1 case): getUserAuthorPasswd real body under
  DISABLE_POLKIT (stub released locally, re-armed after)
- QFile::open / QIODevice::readAll / static QFile::exists / QProcess
  stubbed via dlsym-resolved pmfs; no real file or subprocess touched
- self-check structural: 0 errors (ut_mainjob 0/0, DeviceInterface 0
  errors 1 benign SOLE_BOOL warning)
- coverage (gcov tracefile, lcov unavailable in env): high 91.6% line /
  100% fn, mid 92.5% / 100%, low 94.7% / 100%, total 92.0% line - all
  tiered gates pass
- test results: 334 cases / 20 suites / 333 passed; sole failure
  CoreCpu_UT.LogicalIsExisted_NegativeCoreId_ReturnsFalse is pre-existing
  on base 6ccddaf (out of scope, documented in tests/.ut/defects.json)
- mode 5 defect export: tests/.ut/defects.json + defects-summary.md
  (1 test defect, 0 source defects)
@github-actions

Copy link
Copy Markdown
  • 检测到敏感词lshw, dmidecode变动
详情
    {
    "lshw": {
        "deepin-devicemanager-server/tests/src/LoadInfo/ut_ThreadPool.cpp": [
            " *   B1: lshw appended to both lists, canNotReplace=false",
            "    // Act \u2014 find lshw entry in both lists",
            "        if (c.file == \"lshw.txt\")",
            "    EXPECT_EQ(std::count_if(pool->m_ListCmd.cbegin(), pool->m_ListCmd.cend(), [](const Cmd &c) { return c.file == \"lshw.txt\"; }), 1);",
            "    // Act \u2014 lshw cmd redirects to PATH/lshw.txt",
            "    // Act \u2014 m_ListCmd: lshw + 9 dmidecode + upower + lscpu + lsblk + ls_sg + lspci +",
            "    // Act \u2014 m_ListUpdate: lshw + upower + lscpu + lsblk + ls_sg + lspci + lpstat +"
        ],
        "deepin-devicemanager-server/tests/src/LoadInfo/ut_ThreadPoolTask.cpp": [
            "    ThreadPoolTask task(\"lshw > f\", \"ut_r2f_cnr.txt\", true, -1);",
            "    task.runCmdToFile(\"lshw > f\");   // branch: CanNotReplace && exists -> return",
            "    ThreadPoolTask task(\"lshw > f\", \"ut_r2f_rep.txt\", false, -1);",
            "    task.runCmdToFile(\"lshw > f\");   // branch: replace allowed -> runCmd(cmd)",
            "    EXPECT_EQ(ut_tt_runCmd1Calls, QStringList() << QString(\"lshw > f\"));"
        ]
    },
    "dmidecode": {
        "deepin-devicemanager-server/tests/src/LoadInfo/ut_ThreadPool.cpp": [
            " *   B2: dmidecode_spn appended to m_ListCmd only, canNotReplace=true",
            "    // Act \u2014 m_ListCmd contains dmidecode-only entries not in m_ListUpdate",
            "        if (c.file == \"dmidecode_spn.txt\")",
            "    EXPECT_EQ(std::count_if(pool->m_ListCmd.cbegin(), pool->m_ListCmd.cend(), [](const Cmd &c) { return c.file == \"dmidecode_spn.txt\"; }), 1);",
            "    EXPECT_EQ(std::count_if(pool->m_ListUpdate.cbegin(), pool->m_ListUpdate.cend(), [](const Cmd &c) { return c.file == \"dmidecode_spn.txt\"; }), 0);",
            "    // Act \u2014 m_ListCmd: lshw + 9 dmidecode + upower + lscpu + lsblk + ls_sg + lspci +"
        ]
    }
}

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: add-uos, lzwind

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@add-uos

add-uos commented Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

/forcemerge

@deepin-bot

deepin-bot Bot commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

This pr force merged! (status: unstable)

@deepin-bot
deepin-bot Bot merged commit 8f27c63 into linuxdeepin:master Oct 27, 2026
14 of 17 checks passed
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.

3 participants