From c48340dece8cbdd69a57905399984faaa20511d0 Mon Sep 17 00:00:00 2001 From: showen Date: Thu, 12 Mar 2026 13:11:47 +0800 Subject: [PATCH] =?UTF-8?q?test:=20=E6=B7=BB=E5=8A=A0=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E6=9C=BA=E5=88=B6=E8=87=AA=E5=8A=A8=E5=8C=96?= =?UTF-8?q?=E5=9B=9E=E5=BD=92=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 7 个测试用例固化插件依赖关系 - 验证 http 插件依赖 video - 验证 ble/wifi/video/screen 插件无依赖 - 验证所有插件 ID 唯一性 - 验证拓扑排序正确性(依赖项先初始化) - 所有 31 个测试通过 防止依赖关系漂移,确保架构稳定性。 --- TEAM_CHAT.md | 55 ++++++++++++++++++++++++++ src/core/tests.rs | 98 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) diff --git a/TEAM_CHAT.md b/TEAM_CHAT.md index 8667e06..f1e403c 100644 --- a/TEAM_CHAT.md +++ b/TEAM_CHAT.md @@ -2008,3 +2008,58 @@ cargo check ## 结论 BLE GATT notify 功能已完整实现,符合 BlueZ GATT 规范。WiFi 状态变更时会主动推送 PropertiesChanged 信号给已订阅的 BLE 客户端。无需修改代码。 + +--- + +## 周雅婷 (测试工程师) - 2026-03-12 + +### 任务:插件依赖机制自动化回归测试 + +根据架构师王思远的要求,为插件依赖声明机制编写自动化回归测试,防止依赖关系漂移。 + +### 测试实现 + +在 `src/core/tests.rs` 中新增 7 个测试用例: + +#### 1. 依赖关系固化测试 +- `http_plugin_must_depend_on_video`: 验证 http 插件必须依赖 video (line 324-329) +- `ble_plugin_must_have_no_dependencies`: 验证 ble 插件无依赖 (line 331-336) +- `wifi_plugin_must_have_no_dependencies`: 验证 wifi 插件无依赖 (line 338-343) +- `video_plugin_must_have_no_dependencies`: 验证 video 插件无依赖 (line 345-350) +- `screen_plugin_must_have_no_dependencies`: 验证 screen 插件无依赖 (line 352-357) + +#### 2. 插件 ID 唯一性测试 +- `all_plugin_ids_must_be_unique`: 验证所有插件 ID 不重复 (line 359-385) + - 测试所有 5 个插件:ble, http, screen, video, wifi + - 使用 HashSet 检测重复 ID + +#### 3. 拓扑排序验证测试 +- `topological_sort_places_http_after_video`: 验证依赖拓扑排序正确 (line 387-413) + - 使用 TestPlugin 模拟 http 依赖 video + - 验证 video 在 http 之前初始化 + +### 测试结果 +```bash +export PATH=/home/showen/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/bin:$PATH +cargo test --lib +``` + +✅ **所有 31 个测试通过**(包括新增的 7 个测试) +- 编译时间:46.75s +- 测试执行时间:0.17s +- 0 个失败,0 个忽略 + +### 测试覆盖 +- ✅ http 插件依赖 video +- ✅ ble/wifi/video/screen 插件无依赖 +- ✅ 所有插件 ID 唯一 +- ✅ 拓扑排序正确(依赖项先初始化) + +### 技术细节 +- 直接调用各插件的 `dependencies()` 方法验证返回值 +- 使用 `assert_eq!` 和 `assert!` 进行断言 +- 拓扑排序测试通过检查事件日志中的初始化顺序验证 + +### 价值 +这些测试固化了当前的插件依赖关系,任何未经审查的依赖变更都会导致测试失败,防止依赖漂移。 + diff --git a/src/core/tests.rs b/src/core/tests.rs index 0e966c6..5dc6139 100644 --- a/src/core/tests.rs +++ b/src/core/tests.rs @@ -319,3 +319,101 @@ fn wifi_result_sent_to_manager_is_broadcast_to_plugins() { assert!(has_event(&events, "msg:alpha:wifi_result:connected")); assert!(has_event(&events, "msg:beta:wifi_result:connected")); } + +#[test] +fn http_plugin_must_depend_on_video() { + use crate::plugins::http::HttpPlugin; + let plugin = HttpPlugin::new(); + let deps = plugin.dependencies(); + assert_eq!(deps, vec!["video"], "http plugin must depend on video"); +} + +#[test] +fn ble_plugin_must_have_no_dependencies() { + use crate::plugins::ble::BlePlugin; + let plugin = BlePlugin::new(); + let deps = plugin.dependencies(); + assert!(deps.is_empty(), "ble plugin must have no dependencies"); +} + +#[test] +fn wifi_plugin_must_have_no_dependencies() { + use crate::plugins::wifi::WifiPlugin; + let plugin = WifiPlugin::new(); + let deps = plugin.dependencies(); + assert!(deps.is_empty(), "wifi plugin must have no dependencies"); +} + +#[test] +fn video_plugin_must_have_no_dependencies() { + use crate::plugins::video::VideoPlugin; + let plugin = VideoPlugin::new(); + let deps = plugin.dependencies(); + assert!(deps.is_empty(), "video plugin must have no dependencies"); +} + +#[test] +fn screen_plugin_must_have_no_dependencies() { + use crate::plugins::screen::ScreenPlugin; + let plugin = ScreenPlugin::new(); + let deps = plugin.dependencies(); + assert!(deps.is_empty(), "screen plugin must have no dependencies"); +} + +#[test] +fn all_plugin_ids_must_be_unique() { + use crate::plugins::ble::BlePlugin; + use crate::plugins::http::HttpPlugin; + use crate::plugins::screen::ScreenPlugin; + use crate::plugins::video::VideoPlugin; + use crate::plugins::wifi::WifiPlugin; + use std::collections::HashSet; + + let plugins: Vec> = vec![ + Box::new(BlePlugin::new()), + Box::new(HttpPlugin::new()), + Box::new(ScreenPlugin::new()), + Box::new(VideoPlugin::new()), + Box::new(WifiPlugin::new()), + ]; + + let mut ids = HashSet::new(); + for plugin in plugins { + let id = plugin.id(); + assert!(ids.insert(id), "duplicate plugin id detected: '{}'", id); + } +} + +#[test] +fn topological_sort_places_http_after_video() { + let events = Arc::new(Mutex::new(Vec::new())); + let mut manager = ServiceManager::new(test_config()); + + manager.register(Box::new(TestPlugin::new( + "http", + vec!["video"], + events.clone(), + ))); + manager.register(Box::new(TestPlugin::new("video", vec![], events.clone()))); + + manager + .start_all() + .expect("start_all should succeed with http depending on video"); + + let event_log = lock_events(&events).clone(); + let http_init_pos = event_log + .iter() + .position(|e| e == "init:http") + .expect("http should be initialized"); + let video_init_pos = event_log + .iter() + .position(|e| e == "init:video") + .expect("video should be initialized"); + + assert!( + video_init_pos < http_init_pos, + "video must be initialized before http (video at {}, http at {})", + video_init_pos, + http_init_pos + ); +}