50fdaab25b
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Alexander <akotenev2003@gmail.com>
211 lines
7.4 KiB
Rust
211 lines
7.4 KiB
Rust
//! Интеграция `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);
|
||
}
|