use std::time::Duration;
use bevy::prelude::*;
use crate::{
app::AppSet,
backend::log::{LogResponseEvent, RefreshLogEvent},
screens::Screen,
utils::AppExt,
};
use super::config::Config;
#[mutants::skip]
#[tracing::instrument(skip_all)]
pub fn plugin(app: &mut App) {
trace!("Initializing plugin...");
app.register_scoped_type::<PollTimer>(Screen::Interface);
app.add_systems(
Update,
(
PollTimer::tick.in_set(AppSet::TickTimers),
(PollTimer::debounce, PollTimer::check)
.chain()
.in_set(AppSet::Update),
)
.run_if(in_state(Screen::Interface)),
);
trace!("Plugin initialized.");
}
#[derive(Deref, DerefMut, Reflect, Resource)]
struct PollTimer(Timer);
impl FromWorld for PollTimer {
fn from_world(world: &mut World) -> Self {
let config = world.resource::<Config>();
let duration = Duration::from_millis(config.log.poll_interval_ms);
Self(Timer::new(duration, TimerMode::Repeating))
}
}
impl PollTimer {
fn tick(mut poll_timer: ResMut<Self>, time: Res<Time>) {
poll_timer.tick(time.delta());
}
#[tracing::instrument(skip_all)]
fn check(poll_timer: Res<Self>, mut ev_refresh_log: EventWriter<RefreshLogEvent>) {
if poll_timer.just_finished() {
ev_refresh_log.send(default());
}
}
fn debounce(mut poll_timer: ResMut<Self>, mut ev_log_response: EventReader<LogResponseEvent>) {
for _ in ev_log_response.read() {
poll_timer.reset();
break;
}
}
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use bevy::ecs::system::RunSystemOnce;
use crate::{backend::config::LogConfig, events};
use super::*;
fn plugin(app: &mut App) {
app.add_plugins(events::plugin);
app.insert_resource(Config {
log: LogConfig {
poll_interval_ms: 50,
},
..Config::default()
});
app.init_resource::<Time>();
app.init_resource::<PollTimer>();
}
#[test]
fn uses_config_interval() -> Result<()> {
let mut app = App::new();
app.add_plugins(plugin);
(app.world_mut()).run_system_once(|timer: Res<PollTimer>| {
assert_eq!(timer.duration(), Duration::from_millis(50));
})?;
Ok(())
}
#[test]
fn sends_refresh_events() -> Result<()> {
let mut app = App::new();
app.add_plugins(plugin);
app.add_systems(Update, (PollTimer::tick, PollTimer::check).chain());
app.update();
(app.world_mut()).run_system_once(
|ev_refresh_log: EventReader<RefreshLogEvent>, mut time: ResMut<Time>| {
assert!(ev_refresh_log.is_empty());
time.advance_by(Duration::from_millis(26));
},
)?;
app.update();
(app.world_mut()).run_system_once(
|ev_refresh_log: EventReader<RefreshLogEvent>, mut time: ResMut<Time>| {
assert!(ev_refresh_log.is_empty());
time.advance_by(Duration::from_millis(26));
},
)?;
app.update();
(app.world_mut()).run_system_once(
|ev_refresh_log: EventReader<RefreshLogEvent>, mut time: ResMut<Time>| {
assert_eq!(ev_refresh_log.len(), 1);
time.advance_by(Duration::from_millis(50));
},
)?;
app.update();
(app.world_mut()).run_system_once(|ev_refresh_log: EventReader<RefreshLogEvent>| {
assert_eq!(ev_refresh_log.len(), 2);
})?;
Ok(())
}
#[test]
fn debounces() -> Result<()> {
let mut app = App::new();
app.add_plugins(plugin);
app.add_systems(
Update,
(PollTimer::tick, PollTimer::debounce, PollTimer::check).chain(),
);
(app.world_mut()).run_system_once(|mut time: ResMut<Time>| {
time.advance_by(Duration::from_millis(49));
})?;
app.update();
(app.world_mut()).run_system_once(
|mut ev_log_response: EventWriter<LogResponseEvent>,
ev_refresh_log: EventReader<RefreshLogEvent>,
mut time: ResMut<Time>| {
assert!(ev_refresh_log.is_empty());
time.advance_by(Duration::from_millis(49));
ev_log_response.send(LogResponseEvent(vec![]));
},
)?;
app.update();
(app.world_mut()).run_system_once(
|ev_refresh_log: EventReader<RefreshLogEvent>, mut time: ResMut<Time>| {
assert!(ev_refresh_log.is_empty());
time.advance_by(Duration::from_millis(51));
},
)?;
app.update();
(app.world_mut()).run_system_once(|ev_refresh_log: EventReader<RefreshLogEvent>| {
assert_eq!(ev_refresh_log.len(), 1);
})?;
Ok(())
}
}