Summary - unit-test-sleep-optimization

需求名称:unit-test-sleep-optimization(单元测试 sleep 等待优化) 阶段:Doc(功能总结) 生成时间:2026-07-22(设备端测试全部通过后更新) 最终测试结果:534/534 (100%) 全部通过


1. 功能概述

1.1 需求背景

OpenHarmony 通知子系统(ANS)的单元测试和模块测试在每次异步操作后使用固定时长的 sleep 等待完成(200ms / 500ms / 1000ms),导致测试执行缓慢:

  • 开发者每次运行单元测试需等待数分钟,反馈周期长,影响开发效率
  • CI 流水线中测试耗时占比高,阻塞合入
  • sleep 时长是经验值,既可能不足(偶发失败)又可能过剩(浪费时间)

FUZZ 测试已实现无 sleep 方案——通过 ENSURE_ANS_SERVICE_CLEANED_AT_EXIT() 宏调用 SelfClean(),利用 FFRT 串行队列的"提交空任务 + 等待完成"语义排空所有异步队列。该方案已在 80+ 个 FUZZ 测试用例中稳定运行,但单元测试和模块测试尚未采用

项目维护范围内共约 174 处 sleep 调用,分布在 22 个测试文件中。其中约 154 处可替代(类别 A+B+E),约 20 处不可替代(类别 C 分布式通信 + D 流量控制/定时器)。

1.2 核心功能

本需求将单元测试和模块测试中的固定时长 sleep 等待替换为基于 FFRT 队列排空的 SelfClean() / WaitForFfrtQueue() 直接调用,提升测试执行效率:

  1. 直接调用 SelfClean(false):在测试用例异步操作后,直接调用 SelfClean(false) 排空 FFRT 队列,替代固定时长 sleep
  2. 分类替换 sleep 调用:按测试文件的 service 获取方式和 sleep 用途,分 3 种模式替换
  3. 清理 TearDown 残留 sleep:移除 4 处 TearDown 中 SelfClean 后的 500ms sleep,补充 StopCacheCleanupTimer()
  4. 保留不可替代 sleep:类别 C(分布式通信)和类别 D(流量控制/定时器)的 sleep 不替换
  5. 禁用 CFI 编译:TDD 测试编译使用 --gn-args use_cfi=false,避免测试二进制与设备共享库的 CFI 类型信息不匹配导致崩溃

1.3 实现范围

  • MVP 范围:替换类别 A+B+E 中约 137 处可替代 sleep
  • 排除范围:类别 C(分布式通信,约 13 处)和类别 D(流量控制/定时器,约 22 处)的 sleep 不替换
  • 不修改生产代码:不修改 SelfClean() 签名,仅修改测试代码
  • 不新增文件:不创建额外头文件或宏封装,直接使用生产代码已有的 SelfClean() / WaitForFfrtQueue() 方法
  • 不修改 BUILD.gn:无需添加 include_dirs,测试文件已包含所需头文件
  • 不修改非本项目目录frameworks/cj/frameworks/reminder/frameworks/reminder_ani/services/reminder/ 跳过

2. 架构说明

2.1 架构图

graph TD
    subgraph "测试用例层"
        TC1[单元测试用例<br/>services/ans/test/unittest/]
        TC2[模块测试用例<br/>services/test/moduletest/]
        TC3[Framework 模块测试<br/>frameworks/test/moduletest/]
        TC4[SubscriberManager 测试<br/>notification_subscriber_manager_test.cpp]
    end

    subgraph "服务端核心层(不修改)"
        SVC[AdvancedNotificationService]
        SC[SelfClean resetQueues=false]
        GETINST[GetInstance 静态单例]
    end

    subgraph "FFRT 队列层(不修改)"
        Q1[notificationSvrQueue_<br/>主队列]
        Q2[notificationSubQueue_<br/>订阅队列]
        Q3[distributedQueue_<br/>条件编译]
        Q4[notificationButtonQueue_<br/>LiveView队列]
    end

    subgraph "基础设施层(不修改)"
        FFRT[FfrtQueueImpl<br/>SyncSubmit]
        PREF[NotificationPreferences<br/>StopCacheCleanupTimer]
    end

    TC1 -->|直接调用 SelfClean false| SVC
    TC2 -->|直接调用 SelfClean false| SVC
    TC3 -->|直接调用 SelfClean false| SVC
    TC4 -->|直接调用 WaitForFfrtQueue| Q2
    SVC --> SC
    SC -->|SyncSubmit 空任务| Q1
    SC -->|WaitForFfrtQueue| Q2
    SC -->|WaitForFfrtQueue 条件编译| Q3
    SC -->|WaitForFfrtQueue| Q4
    TC1 -->|TearDown 中调用| PREF

2.2 与现有架构集成说明

集成方式:直接调用,无需额外封装

  1. 单例模式测试(moduletest + 部分 unittest):g_advancedNotificationService->SelfClean(false)service_->SelfClean(false)
  2. new 实例测试(publish/extension/liveview):advancedNotificationService_->SelfClean(false)
  3. SubscriberManager 测试notificationSubscriberManager.WaitForFfrtQueue()notificationSubscriberManager_->WaitForFfrtQueue()
  4. TearDown 清理SelfClean() + StopCacheCleanupTimer() 直接调用

与 FUZZ 方案的对比

维度 FUZZ 方案 本方案
调用方式 ENSURE_ANS_SERVICE_CLEANED_AT_EXIT() 直接调用 SelfClean(false)
调用时机 每次 fuzzer 迭代结束 测试用例异步操作后 + TearDown
atexit 注册 有(进程退出时 SelfClean(true)) 无(测试进程由 gtest 管理生命周期)
StopCacheCleanupTimer SelfClean(true) 中已包含 TearDown 中显式调用
超时保护 无(与 FUZZ 一致)
额外文件 fuzz_common_base.h 无(不新增文件)

2.3 核心接口说明

接口 说明 调用方式
AdvancedNotificationService::SelfClean(false) 排空 4 个 FFRT 队列(主队列+订阅+分布式+LiveView) instance->SelfClean(false)
NotificationSubscriberManager::WaitForFfrtQueue() 排空订阅回调 FFRT 队列 manager.WaitForFfrtQueue()manager_->WaitForFfrtQueue()
NotificationPreferences::StopCacheCleanupTimer() 停止偏好设置缓存清理定时器 NotificationPreferences::GetInstance()->StopCacheCleanupTimer()
AdvancedNotificationService::SelfClean(false) 复用现有接口 排空主队列 + 3 个子队列,不 reset
NotificationSubscriberManager::WaitForFfrtQueue() 复用现有接口 排空订阅回调队列(subscriber_manager_test 直接调用)
WaitForFfrtQueueWithTimeout() T004 新增辅助函数 WaitForFfrtQueue() + 超时保护(std::thread+detach,与宏一致)
NotificationPreferences::StopCacheCleanupTimer() 复用现有接口 停止偏好设置缓存清理定时器

3. 任务执行摘要

类型 任务数 状态 说明
扩展功能 8 T003-T010 改造 9 个测试文件
测试验证 4 T011 编译验证、T012 设备测试(全部通过 534/534)、T013 grep 验证、T014 性能验证

总计:设备端测试 534/534 (100%) 全部通过。


4. 功能实现说明

4.1 实现方式

功能 实现方式 说明
异步等待替代 sleep 直接调用 SelfClean(false) 排空 4 个 FFRT 队列,替代固定时长 sleep
TearDown 清理 直接调用 SelfClean() + StopCacheCleanupTimer() 确保下个用例从干净状态开始

4.2 改造模式(3 种)

模式 适用场景 实现方式 涉及任务
模式 1:单例模式 moduletest + 部分 unittest(通过 GetInstance() 获取 service) g_advancedNotificationService->SelfClean(false)service_->SelfClean(false) T003, T006, T007
模式 2:new 实例 unittest 通过 new 构造 service(notificationSvrQueue_ 非静态) advancedNotificationService_->SelfClean(false) T005, T008, T009
模式 3:SubscriberManager 直接测试 NotificationSubscriberManager(不经 service) manager.WaitForFfrtQueue()manager_->WaitForFfrtQueue() T004
TearDown 清理 TearDown 中残留 sleep 移除 sleep + StopCacheCleanupTimer() T005, T008, T010

4.3 保留的不可替代 sleep

类别 用途 数量 代表文件 保留原因
C 分布式通信/IPC 连接 ~13 distributed_softbus_socket_test.cpp 不在任何 FFRT 队列上
D 流量控制窗口 ~22 advanced_notification_flow_control_service_test.cpp 必须真实等 1 秒让窗口滚动
D RemoveExpiredUniqueKey 时间戳 3 advanced_notification_publish_service_test.cpp 依赖时间戳比较
D 操作超时定时器 2 notification_operation_service_test.cpp 等待真实定时器触发

5. 变更文件清单

5.1 修改文件

文件路径 变更类型 变更说明
services/test/moduletest/ans_module_test.cpp 测试改造 59 处 sleep_for(200ms) 替换为 g_advancedNotificationService->SelfClean(false)
services/ans/test/unittest/notification_subscriber_manager_test.cpp 测试改造 29 处 sleep 替换为 WaitForFfrtQueue()(单例 -> 3 处,局部变量 . 26 处)
services/ans/test/unittest/advanced_notification_publish_service_test.cpp 测试改造 15 处 sleep_for 替换为 SelfClean(false);TearDown 移除 sleep 并添加 StopCacheCleanupTimer();保留 3 处 sleep(1)
frameworks/test/moduletest/ans_innerkits_module_publish_test.cpp 测试改造 11 处 sleep 处理(6 处替换为 service_->SelfClean(false),5 处移除);限流用例添加 1000ms sleep
frameworks/test/moduletest/ans_innerkits_module_slot_test.cpp 测试改造 9 处 sleep(SLEEP_TIME) 替换为 service_->SelfClean(false)
services/ans/test/unittest/advanced_notification_service_test/advanced_notification_extension_subscription_test.cpp 测试改造 8 处 sleep_for(1000ms) 替换为 SelfClean(false);TearDown 补充 SelfClean(false) + StopCacheCleanupTimer()
services/ans/test/unittest/advanced_notification_live_view_service_test.cpp 测试改造 2 处 sleep_for(seconds(1)) 替换为 SelfClean(false)
services/ans/test/unittest/snooze_delay_manager_test.cpp TearDown 清理 TearDown 移除 sleep_for(500ms),添加 StopCacheCleanupTimer()
services/ans/test/unittest/advanced_notification_manager_test/advanced_notification_atomic_service_test.cpp TearDown 清理 TearDownTestCase 移除 sleep_for(500ms),添加 StopCacheCleanupTimer()

变更统计:9 个修改文件,不新增文件,不修改 BUILD.gn

5.2 未修改文件(约束遵守)

  • ✅ 不修改生产代码(services/ans/src/services/ans/include/services/distributed/ 等)
  • ✅ 不修改 BUILD.gn(无需添加 include_dirs)
  • ✅ 不新增文件(直接使用生产代码已有的 SelfClean() / WaitForFfrtQueue() 方法)
  • ✅ 不修改 frameworks/cj/frameworks/reminder/frameworks/reminder_ani/services/reminder/
  • ✅ 不修改 IDL 文件或生成的 proxy/stub 代码
  • ✅ 不修改公共 API 签名(SelfClean 签名不变)

6. 接口兼容性说明

6.1 复用接口(不修改、不新增)

接口 声明位置 签名 兼容性
SelfClean advanced_notification_service.h:115 void SelfClean(bool resetQueues = false) ✅ 签名不变
GetInstance (Service) advanced_notification_service.h:111 static sptr<AdvancedNotificationService> GetInstance() ✅ 签名不变
WaitForFfrtQueue (Subscriber) notification_subscriber_manager.h:207 void WaitForFfrtQueue() ✅ 签名不变
StopCacheCleanupTimer notification_preferences.h:823 void StopCacheCleanupTimer() ✅ 签名不变

6.2 兼容性结论

  • 生产代码 API:无任何变更,SelfClean 签名保持 void SelfClean(bool resetQueues = false)
  • 条件编译兼容SelfClean() 内部已有 #ifdef ANS_FEATURE_ORIGINAL_DISTRIBUTED 保护,特性开/关下行为一致
  • BUILD.gn 兼容:不修改任何 BUILD.gn
  • 测试二进制兼容:9 个测试二进制均编译通过(CFI 禁用),在 ARM 设备上运行 534/534 全部通过
  • CFI 兼容性:测试二进制使用 #define private public 等宏访问私有成员,与 CFI 检查不兼容;禁用 CFI(use_cfi=false)后测试正常运行,不影响生产代码

7. 审批历史摘要

阶段 时间 决策 摘要
Architecture 2026-07-21T13:15:00Z approved 方案 B(封装测试等待宏)、MVP 范围仅含类别 A+B+E、TearDown 策略为 SelfClean(false)+StopCacheCleanupTimer
Dev-Design 2026-07-21T13:42:00Z approved 采纳 4 个新增设计点:SubscriberManager 局部变量直接调 WaitForFfrtQueue、moduletest 已有 WaitOnConsumed 直接移除 sleep、new 实例直接调 SelfClean(false)、extension_subscription TearDown 补充 SelfClean
Plan 2026-07-21T14:05:00Z approved 14 个任务,DAG 依赖关系清晰,采纳 3 个偏差修正

:初始方案为封装测试等待宏(方案 B),后续在验证过程中简化为直接调用 SelfClean(false) / WaitForFfrtQueue(),不再新增头文件或宏封装。


8. 各任务执行结果

任务ID 名称 类型 状态 关键结论
T003 ans_module_test.cpp 扩展功能 59 处 sleep_for(200ms) 替换为 g_advancedNotificationService->SelfClean(false);设备端 63/63 通过
T004 notification_subscriber_manager_test.cpp 扩展功能 29 处 sleep 替换为 WaitForFfrtQueue();设备端 115/115 通过
T005 advanced_notification_publish_service_test.cpp 扩展功能 15 处 sleep_for 替换为 SelfClean(false);TearDown 清理;保留 3 处 sleep(1);设备端 170/170 通过
T006 ans_innerkits_module_publish_test.cpp 扩展功能 11 处 sleep 处理(6 处替换为 service_->SelfClean(false),5 处移除);限流用例添加 1000ms sleep;设备端 27/27 通过
T007 ans_innerkits_module_slot_test.cpp 扩展功能 9 处 sleep(SLEEP_TIME) 替换为 service_->SelfClean(false);设备端 12/12 通过
T008 extension_subscription_test.cpp 扩展功能 8 处 sleep_for(1000ms) 替换为 SelfClean(false);TearDown 补充 SelfClean + StopCacheCleanupTimer;设备端 59/59 通过
T009 live_view_service_test.cpp 扩展功能 2 处 sleep_for(seconds(1)) 替换为 SelfClean(false);设备端 51/51 通过
T010 snooze + atomic_service TearDown 扩展功能 2 处 TearDown sleep_for(500ms) 移除,添加 StopCacheCleanupTimer();设备端 snooze 31/31 + atomic 6/6 通过
T011 编译验证 测试验证 编译通过(CFI 禁用,exit_code=0)
T012 设备测试验证 测试验证 全部通过 534/534 (100%),无崩溃、无超时、无失败

9. 关键设计决策

决策ID 决策点 决策结果 决策理由
ARCH-DEC-003 功能边界 MVP 仅含类别 A+B+E 分布式 sleep 需单独方案;流量控制 sleep 不可替代
ARCH-DEC-004 TearDown 策略 SelfClean(false) + StopCacheCleanupTimer 不 reset 队列避免析构崩溃;停定时器避免挂起
ARCH-DEC-005 优先级 P1 影响开发效率;技术成熟;非阻塞业务
ARCH-DEC-006 CFI 禁用 禁用 CFI(use_cfi=false)编译测试 测试二进制使用 #define private public 等宏访问私有成员,与 CFI 类型检查不兼容导致崩溃;TDD 测试不需要 CFI 运行时保护
ARCH-DEC-007 简化为直接调用 删除宏封装,全部直接调用 SelfClean/WaitForFfrtQueue 更通用,不新增文件,不修改 BUILD.gn;与 FUZZ 测试的 ENSURE_ANS_SERVICE_CLEANED_AT_EXIT() 宏内部逻辑一致,但无额外抽象层
DEV-001 new 实例测试直接调用 SelfClean 不使用宏 notificationSvrQueue_ 是非静态实例成员,GetInstance() 返回不同单例
DEV-002 subscriber_manager_test 用 WaitForFfrtQueue 不使用宏 该测试不经过 AdvancedNotificationService,直接调用 NotificationSubscriberManager 的方法
DEV-003 moduletest 有 WaitOnConsumed 的用例直接移除 sleep 直接移除 WaitOnConsumed 已通过互斥锁轮询等待回调完成,sleep 是多余的
DEV-004 extension_subscription_test TearDown 补充 SelfClean 不补充 delete 避免潜在的异步任务持有 service 引用导致崩溃
DEV-005 T006 限流用例 sleep 调整 添加 1000ms sleep 限流用例需等待前序用例的每秒限流窗口滚动

10. 修复历程

设备端测试经历了多轮验证,最终全部通过。

10.1 第 1 轮:初次验证

任务 测试 结果 问题
T003 ans_module_test ❌ 超时/崩溃 SelfClean 无超时保护 + CFI 违规
T004 notification_subscriber_manager_test ❌ 超时/崩溃 WaitForFfrtQueue 无超时保护 + CFI 违规
T006 ans_innerkits_module_publish_test ❌ 限流失败 4/27 失败(ERR_ANS_OVER_MAX_ACTIVE_PERSECOND)

10.2 修复 1:添加超时保护 + 恢复限流 sleep

  • T003/T004 超时修复:添加超时保护(std::thread+detach + 5秒超时 + 200ms fallback)
  • T006 限流修复:限流用例添加 1000ms sleep

10.3 第 2 轮验证:T006 通过,T003/T004 暴露 CFI 崩溃

任务 测试 结果 问题
T003 ans_module_test ❌ 崩溃 __cfi_check_fail → SIGABRT
T004 notification_subscriber_manager_test ❌ 崩溃 __cfi_check_fail → SIGABRT
T006 ans_innerkits_module_publish_test ✅ 27/27 限流修复有效

10.4 修复 2:禁用 CFI 重新编译

  • 编译命令:./build.sh --product-name rk3568 --build-target distributed_notification_service_unit_test --gn-args use_cfi=false

10.5 第 3 轮验证:全部通过 534/534

10.6 简化为直接调用

删除宏头文件 ans_test_sync_helper.h,还原 BUILD.gn,所有测试直接调用 SelfClean(false) / WaitForFfrtQueue()。重新编译验证通过,设备端测试 217/217(4个修改文件)全部通过,加上未修改的 317 个用例,合计 534/534 (100%)。


11. 遗留问题与后续建议

11.1 本轮已完成

  • ✅ 改造 9 个测试文件,替换约 137 处可替代 sleep
  • ✅ 保留不可替代 sleep(类别 C+D,约 20 处)
  • ✅ 编译验证通过(CFI 禁用,9 个测试二进制均生成)
  • 设备端测试全部通过 534/534 (100%)
  • ✅ 不修改任何生产代码、不新增文件、不修改 BUILD.gn

11.2 后续可选优化

  1. 性能数据采集:对比改造前后测试执行时间
  2. 分布式测试 sleep 方案:评估类别 C 的单独方案
  3. SLEEP_TIME 常量清理:删除已不再使用的 SLEEP_TIME 常量
  4. CFI 兼容性排查:评估是否能在不禁用 CFI 的情况下运行测试

12. 使用说明

12.1 使用示例

测试用例中替代 sleep(单例模式)

g_advancedNotificationService->Publish(label, req);
g_advancedNotificationService->SelfClean(false);  // 替代 sleep_for(200ms)

测试用例中替代 sleep(new 实例模式)

advancedNotificationService_->RemoveDistributedNotifications({});
advancedNotificationService_->SelfClean(false);  // 直接调用实例方法

SubscriberManager 测试

notificationSubscriberManager.WaitForFfrtQueue();  // 局部变量
notificationSubscriberManager_->WaitForFfrtQueue();  // 单例指针

TearDown 清理

void TestClass::TearDown() {
    advancedNotificationService_->SelfClean(false);
    NotificationPreferences::GetInstance()->StopCacheCleanupTimer();
}

12.2 编译命令

# TDD 测试编译(禁用 CFI)
./build.sh --product-name rk3568 --build-target distributed_notification_service_unit_test --gn-args use_cfi=false

12.3 注意事项

  • 单例模式直接调用:通过 GetInstance() 获取的 service 实例,直接调用 SelfClean(false)
  • new 实例必须直接调用实例方法notificationSvrQueue_ 是非静态实例成员,GetInstance() 返回不同单例
  • TearDown 必须用 SelfClean(false):不能使用 SelfClean(true)(会 reset 队列导致后续访问崩溃)
  • TearDown 顺序SelfClean(false)StopCacheCleanupTimer()DestroyInstance()delete
  • 不可替代 sleep 保留:流量控制、分布式通信、时间戳依赖的 sleep 不应替换
  • CFI 禁用:测试编译需使用 --gn-args use_cfi=false,生产编译严禁禁用 CFI