use egui_kittest::Harness;
use egui_kittest::kittest::Queryable;
use ek_core::main_engine::MainEngine;
use ek_core::plugin::Plugin;
use ek_ui::plugin::UI_NAME;
use ek_ui::runtime::{Stage, StageApp, ui_kinds};
use ek_ui::state::UiShared;
use std::sync::Arc;
fn temp_store(tag: &str) -> ek_cfg::Store {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("target")
.join("tmp")
.join(format!("s-{}", tag));
let _ = std::fs::remove_dir_all(&root);
ek_cfg::Store::with_root(root)
}
struct CountPlugin {
name: &'static str,
deps: Vec<&'static str>,
starts: std::sync::atomic::AtomicUsize,
stops: std::sync::atomic::AtomicUsize,
}
impl CountPlugin {
fn new(name: &'static str, deps: Vec<&'static str>) -> Self {
Self {
name,
deps,
starts: std::sync::atomic::AtomicUsize::new(0),
stops: std::sync::atomic::AtomicUsize::new(0),
}
}
}
impl Plugin for CountPlugin {
fn name(&self) -> &str {
self.name
}
fn deps(&self) -> &[&str] {
&self.deps
}
fn on_start(&self) {
self.starts
.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
fn on_stop(&self) {
self.stops.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
fn on_mail(&self, _event: &ek_event::Event) {}
}
fn stage_setup(tag: &str) -> (StageApp, Arc<MainEngine>, Arc<UiShared>) {
let engine = Arc::new(MainEngine::new(None));
let shared = Arc::new(UiShared::new());
let mut app = StageApp::new_setup(
Arc::clone(&engine),
Arc::clone(&shared),
ui_kinds(&shared, true),
Some(temp_store(tag)),
None,
);
app.set_selected("tq", false);
(app, engine, shared)
}
#[test]
fn u_s1a_setup_has_no_main_window() {
ek_cfg::set_default_root_for_tests(std::path::PathBuf::from("C:/dev/fuke/ek-ui/target/cfg-test-root"));
let (mut app, _e, _s) = stage_setup("s1a");
let mut harness = Harness::new_ui_state(
move |ui, ()| {
app.render_setup_panel(ui);
},
(),
);
let _ = harness.run();
assert!(
harness.query_by_label_contains("运维").is_none(),
"配置相不许画主界面菜单"
);
assert!(
harness.query_by_label("启动运行").is_some(),
"配置面控件要在场(0.9.10 一钮:装配并入启动运行;精确名 = 避开上方说明文字含钮名的多命中)"
);
}
#[test]
fn u_s1b_running_shows_main_after_start() {
ek_cfg::set_default_root_for_tests(std::path::PathBuf::from("C:/dev/fuke/ek-ui/target/cfg-test-root"));
let (mut app, _engine, shared) = stage_setup("s1b");
let msg = app.do_start();
assert!(app.assembled(), "启动钮要先完成装配:{msg}");
assert!(msg.contains("已启动"), "{msg}");
assert_eq!(app.stage(), Stage::Running);
let mut harness = Harness::new_ui_state(
{
let shared = Arc::clone(&shared);
move |ui, ()| {
ek_ui::render_body(ui.ctx(), &shared);
}
},
(),
);
let _ = harness.run();
assert!(
harness.query_by_label_contains("运维").is_some(),
"运行相主界面要在场"
);
}
#[test]
fn u_s2a_lock_after_full_start() {
ek_cfg::set_default_root_for_tests(std::path::PathBuf::from("C:/dev/fuke/ek-ui/target/cfg-test-root"));
let (mut app, engine, shared) = stage_setup("s2a");
app.do_start();
assert!(
shared.assembly_locked(),
"全壳 Running 要置锁:{:?}",
engine.plugin_names()
);
shared.set_ops_window_open(true);
let mut harness = Harness::new_ui_state(
{
let shared = Arc::clone(&shared);
move |ui, ()| {
ek_ui::render_body(ui.ctx(), &shared);
}
},
(),
);
let _ = harness.run();
harness.get_by_label("运维").click_accesskit();
let _ = harness.run();
assert!(
harness.query_by_label_contains("已锁定(运行中)").is_some(),
"水印行要在场"
);
let mut hit = None;
for probe in ["移除", "移除 插件"] {
if harness.query_by_label_contains(probe).is_some() {
hit = Some(probe);
break;
}
}
let probe = match hit {
Some(p) => p,
None => {
for _ in 0..3 {
let _ = harness.run();
}
assert!(
shared.assembly_locked() && engine.plugin_names().iter().any(|n| n == "dm"),
"锁定态与装配态必须扛住帧推进(钮级 disable 由 ui_kittest S2 同路覆盖)"
);
return;
}
};
for _ in 0..2 {
harness.get_by_label(probe).click_accesskit();
let _ = harness.run();
}
assert!(
engine.plugin_names().iter().any(|n| n == "dm"),
"锁定下点摘除不能真摘"
);
}
#[test]
fn u_s3a_failed_start_does_not_lock() {
ek_cfg::set_default_root_for_tests(std::path::PathBuf::from("C:/dev/fuke/ek-ui/target/cfg-test-root"));
let engine = Arc::new(MainEngine::new(None));
let shared = Arc::new(UiShared::new());
let spy = Arc::new(CountPlugin::new("lonely", vec!["nope"]));
engine.add_plugin(Arc::clone(&spy)).expect("add spy");
let mut app = StageApp::new_setup(
Arc::clone(&engine),
Arc::clone(&shared),
ui_kinds(&shared, true),
Some(temp_store("s3a")),
None,
);
app.set_selected("tq", false);
let msg = app.do_start();
assert!(
app.assembled() && (msg.contains("启动失败") || msg.contains("未就绪")),
"装配要成、失败要显形:{msg}"
);
assert!(!shared.assembly_locked(), "启动失败不得锁(留修路)");
assert_eq!(app.stage(), Stage::Running, "失败也要进运行相(主界面出)");
}
#[test]
fn u_s3b_setup_close_never_starts() {
ek_cfg::set_default_root_for_tests(std::path::PathBuf::from("C:/dev/fuke/ek-ui/target/cfg-test-root"));
let engine = Arc::new(MainEngine::new(None));
let shared = Arc::new(UiShared::new());
let spy = Arc::new(CountPlugin::new("counted", vec![]));
engine.add_plugin(Arc::clone(&spy)).expect("add counted");
let app = StageApp::new_setup(
Arc::clone(&engine),
Arc::clone(&shared),
ui_kinds(&shared, true),
Some(temp_store("s3b")),
None,
);
assert_eq!(app.stage(), Stage::Setup);
engine.close_all(std::time::Duration::from_secs(2));
assert_eq!(spy.starts.load(std::sync::atomic::Ordering::SeqCst), 0);
}
#[test]
fn u_s4b_ui_row_always_checked() {
ek_cfg::set_default_root_for_tests(std::path::PathBuf::from("C:/dev/fuke/ek-ui/target/cfg-test-root"));
let (mut app, _e, _s) = stage_setup("s4b");
app.set_selected(UI_NAME, false);
assert!(
app.picked().iter().any(|n| n == UI_NAME),
"ui 行必须恒勾(数据口)"
);
let mut harness = Harness::new_ui_state(
move |ui, ()| {
app.render_setup_panel(ui);
},
(),
);
let _ = harness.run();
assert!(
harness.query_by_label("启动运行").is_some(),
"配置表要在场"
);
let _ = std::fs::remove_dir_all(
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("target")
.join("tmp"),
);
}