test: 添加插件依赖机制自动化回归测试
- 新增 7 个测试用例固化插件依赖关系 - 验证 http 插件依赖 video - 验证 ble/wifi/video/screen 插件无依赖 - 验证所有插件 ID 唯一性 - 验证拓扑排序正确性(依赖项先初始化) - 所有 31 个测试通过 防止依赖关系漂移,确保架构稳定性。
This commit is contained in:
55
TEAM_CHAT.md
55
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!` 进行断言
|
||||
- 拓扑排序测试通过检查事件日志中的初始化顺序验证
|
||||
|
||||
### 价值
|
||||
这些测试固化了当前的插件依赖关系,任何未经审查的依赖变更都会导致测试失败,防止依赖漂移。
|
||||
|
||||
|
||||
@@ -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<Box<dyn Plugin>> = 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
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user