Files
shturman/crates/core/shturman-power/tests/integration.rs
T
kk0t9 32ba1136c7 test(v0.4): integration — thermal-trip/abort + MCU fail-safe-cut (session-шина)
+ фикс B09-таймера: last_heartbeat Option<u64> вместо сентинела 0 (monotonic_secs() стартует
с 0 → первый heartbeat попадал на 0, guard !=0 ложно трактовал как «не было» → cut не срабатывал).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Alexander <akotenev2003@gmail.com>
2026-06-25 15:42:20 +03:00

205 lines
7.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Интеграция `Power1` + dev-mock на session-шине. `#[ignore]` — запуск: `just test-integration`.
//! Проверяет state по шине и fake-ACC: `dev.PowerMock1.SetAcc` → сигнал `Power1.AccChanged`.
use futures_util::StreamExt;
use shturman_ipc::{names, types::PowerState};
use shturman_power::coprocessor::MockCoprocessor;
use shturman_power::thermal::MockTempSource;
use shturman_power::PowerService;
use shturman_sdk::PowerClient;
use std::sync::Arc;
#[tokio::test]
#[ignore = "нужна session-шина: just test-integration"]
async fn power_state_and_fake_acc() {
let svc = PowerService::new();
let mock = svc.mock(Arc::new(MockTempSource::new(20)), Arc::new(MockCoprocessor::new()));
// сервер: Power1 + dev.PowerMock1 на одном пути (владеет ru.shturman.Power)
let server = zbus::Connection::session().await.unwrap();
server
.object_server()
.at(names::power::PATH, svc)
.await
.unwrap();
server
.object_server()
.at(names::power::PATH, mock)
.await
.unwrap();
server.request_name(names::power::NAME).await.unwrap();
let client_conn = zbus::Connection::session().await.unwrap();
let power = PowerClient::new(&client_conn).await.unwrap();
assert_eq!(power.power_state().await.unwrap(), PowerState::Running);
// подписка на AccChanged
let mut acc = power.proxy().receive_acc_changed().await.unwrap();
// dev.PowerMock1.SetAcc(false) сырым D-Bus-вызовом
client_conn
.call_method(
Some(names::power::NAME),
names::power::PATH,
Some(names::power::MOCK_IFACE),
"SetAcc",
&(false,),
)
.await
.unwrap();
let sig = acc.next().await.unwrap();
assert!(!sig.args().unwrap().on());
}
#[tokio::test]
#[ignore = "нужна session-шина: just test-integration"]
async fn shutdown_imminent_then_abort() {
let svc = PowerService::new();
let mock = svc.mock(Arc::new(MockTempSource::new(20)), Arc::new(MockCoprocessor::new()));
let server = zbus::Connection::session().await.unwrap();
server
.object_server()
.at(names::power::PATH, svc)
.await
.unwrap();
server
.object_server()
.at(names::power::PATH, mock)
.await
.unwrap();
server.request_name(names::power::NAME).await.unwrap();
let client = zbus::Connection::session().await.unwrap();
let power = PowerClient::new(&client).await.unwrap();
let mut imminent = power.proxy().receive_shutdown_imminent().await.unwrap();
let mut aborted = power.proxy().receive_shutdown_aborted().await.unwrap();
// ACC-off → ShutdownImminent(acc_off), состояние shutting_down
client
.call_method(
Some(names::power::NAME),
names::power::PATH,
Some(names::power::MOCK_IFACE),
"SetAcc",
&(false,),
)
.await
.unwrap();
let sig = imminent.next().await.unwrap();
assert_eq!(sig.args().unwrap().reason(), "acc_off");
assert_eq!(power.power_state().await.unwrap(), PowerState::ShuttingDown);
// re-power до grace → ShutdownAborted + running
client
.call_method(
Some(names::power::NAME),
names::power::PATH,
Some(names::power::MOCK_IFACE),
"SetAcc",
&(true,),
)
.await
.unwrap();
aborted.next().await.unwrap();
assert_eq!(power.power_state().await.unwrap(), PowerState::Running);
}
#[tokio::test]
#[ignore = "нужна session-шина: just test-integration"]
async fn thermal_trip_then_clear() {
let svc = PowerService::new();
let fsm = svc.fsm_handle();
let thermal_state = svc.thermal_state_handle();
let temp = Arc::new(MockTempSource::new(20));
let copro = Arc::new(MockCoprocessor::new());
let server = zbus::Connection::session().await.unwrap();
server
.object_server()
.at(names::power::PATH, svc)
.await
.unwrap();
server.request_name(names::power::NAME).await.unwrap();
let iface = server
.object_server()
.interface::<_, PowerService>(names::power::PATH)
.await
.unwrap();
let ctx = iface.signal_context().to_owned();
shturman_power::service::spawn_loops(
fsm,
thermal_state,
temp.clone() as Arc<dyn shturman_power::thermal::TempSource>,
Arc::new(shturman_power::thermal::NoopThrottler::default())
as Arc<dyn shturman_power::thermal::Throttler>,
copro as Arc<dyn shturman_power::coprocessor::Coprocessor>,
ctx,
);
let client = zbus::Connection::session().await.unwrap();
let power = PowerClient::new(&client).await.unwrap();
let mut imminent = power.proxy().receive_shutdown_imminent().await.unwrap();
let mut aborted = power.proxy().receive_shutdown_aborted().await.unwrap();
// перегрев → ShutdownImminent(thermal)
temp.set(99);
let sig = imminent.next().await.unwrap();
assert_eq!(sig.args().unwrap().reason(), "thermal");
assert_eq!(power.power_state().await.unwrap(), PowerState::ShuttingDown);
// остыло до PONR → ShutdownAborted + running
temp.set(20);
aborted.next().await.unwrap();
assert_eq!(power.power_state().await.unwrap(), PowerState::Running);
}
#[tokio::test]
#[ignore = "нужна session-шина: just test-integration"]
async fn mcu_failsafe_cuts_on_hang() {
let svc = PowerService::new();
let fsm = svc.fsm_handle();
let thermal_state = svc.thermal_state_handle();
let temp = Arc::new(MockTempSource::new(20));
let copro = Arc::new(MockCoprocessor::new());
let server = zbus::Connection::session().await.unwrap();
server
.object_server()
.at(names::power::PATH, svc)
.await
.unwrap();
server.request_name(names::power::NAME).await.unwrap();
let iface = server
.object_server()
.interface::<_, PowerService>(names::power::PATH)
.await
.unwrap();
let ctx = iface.signal_context().to_owned();
shturman_power::service::spawn_loops(
fsm,
thermal_state,
temp as Arc<dyn shturman_power::thermal::TempSource>,
Arc::new(shturman_power::thermal::NoopThrottler::default())
as Arc<dyn shturman_power::thermal::Throttler>,
copro.clone() as Arc<dyn shturman_power::coprocessor::Coprocessor>,
ctx,
);
let client = zbus::Connection::session().await.unwrap();
let power = PowerClient::new(&client).await.unwrap();
assert_eq!(power.power_state().await.unwrap(), PowerState::Running);
// дать coproc-циклу послать ≥1 heartbeat (иначе last_heartbeat=0 и guard не даст cut)
tokio::time::sleep(std::time::Duration::from_millis(1300)).await;
copro.hang(); // SoC завис → heartbeat не освежает таймер
// ждём, пока coproc-цикл (HEARTBEAT=1с) накопит > FAILSAFE_MISS окон и сделает FailsafeCut
for _ in 0..10 {
tokio::time::sleep(std::time::Duration::from_millis(700)).await;
if power.power_state().await.unwrap() == PowerState::Off {
break;
}
}
assert_eq!(power.power_state().await.unwrap(), PowerState::Off);
}