use std::{
collections::HashSet,
fmt::Debug,
hash::Hash,
ops::{Deref, DerefMut},
thread::sleep,
time::Duration,
};
use bevy::{ecs::query::QueryFilter, prelude::*};
#[derive(Default)]
pub struct AppTest {
app: App,
retry_count: usize,
retry_wait: Duration,
}
impl AppTest {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn assert_r(condition: bool, message: String) -> Result<(), String> {
if !condition {
return Err(message);
}
Ok(())
}
pub fn assert_state<S: States>(&self, s: &S) {
self.assert_state_r(s).unwrap();
}
pub fn assert_state_r<S: States + Debug>(&self, s: &S) -> Result<(), String> {
let app_state = self
.app
.world()
.get_resource::<State<S>>()
.ok_or("State not found".to_string())?
.get();
if app_state != s {
return Err(format!("State was not equal: {app_state:?} != {s:?}"));
}
Ok(())
}
pub fn count_all(&mut self) -> usize {
self.count::<()>()
}
pub fn count<F: QueryFilter>(&mut self) -> usize {
let w = self.app.world_mut();
w.query_filtered::<(), F>().iter(w).count()
}
pub fn all_entities(&mut self) -> HashSet<Entity> {
self.entities::<()>()
}
pub fn entities<F: QueryFilter>(&mut self) -> HashSet<Entity> {
let w = self.app.world_mut();
w.query_filtered::<Entity, F>()
.iter(w)
.collect::<HashSet<_>>()
}
pub fn click<K: Clone + Eq + Hash + Send + Sync + 'static>(&mut self, k: K) {
let mut input = self.app.world_mut().resource_mut::<ButtonInput<K>>();
input.press(k.clone());
input.release(k);
self.app.update();
}
pub fn assert_clicked<K: Clone + Eq + Hash + Send + Sync + 'static>(&mut self, k: K) {
self.assert_clicked_r(k).unwrap();
}
pub fn assert_clicked_r<K: Clone + Eq + Hash + Send + Sync + 'static>(
&mut self,
k: K,
) -> Result<(), String> {
let input = self.app.world().resource::<ButtonInput<K>>();
if !input.just_pressed(k) {
return Err("No key was pressed".to_string());
}
Ok(())
}
pub fn send_message<M: Message>(&mut self, m: M) {
self.world_mut().write_message(m);
}
pub fn assert_message<M: Message + PartialEq>(&mut self, m: &M) {
self.assert_message_r(m).unwrap();
}
pub fn assert_message_r<M: Message + PartialEq>(&mut self, m: &M) -> Result<(), String> {
let msgs = self.world().resource::<Messages<M>>();
let mut cursor = msgs.get_cursor();
for msg in cursor.read(msgs) {
if m == msg {
return Ok(());
}
}
Err("Message not found".to_string())
}
pub fn assert_any_message<M: Message>(&mut self) {
self.assert_any_message_r::<M>().unwrap();
}
pub fn assert_any_message_r<M: Message>(&mut self) -> Result<(), String> {
let msgs = self.world().resource::<Messages<M>>();
if msgs.is_empty() {
return Err("Message not found".to_string());
}
Ok(())
}
pub fn get<C: Component>(&mut self, id: Entity) -> &C {
self.get_r::<C>(id).unwrap()
}
pub fn get_r<C: Component>(&mut self, id: Entity) -> Result<&C, String> {
self.world()
.entity(id)
.get::<C>()
.ok_or("Component or entity not found".to_string())
}
pub fn retry<T, E: Debug, F: FnMut(&mut AppTest) -> Result<T, E>>(&mut self, f: F) -> T {
self.retry_r(f).unwrap()
}
pub fn retry_r<T, E, F: FnMut(&mut AppTest) -> Result<T, E>>(
&mut self,
mut f: F,
) -> Result<T, E> {
let mut ct = 0;
loop {
let res = f(self);
match res {
Ok(t) => return Ok(t),
Err(e) => {
if ct >= self.retry_count {
return Err(e);
}
}
}
sleep(self.retry_wait);
self.update();
ct += 1;
}
}
pub fn setup_retry(&mut self, count: usize, wait: Duration) {
self.retry_count = count;
self.retry_wait = wait;
}
}
impl From<App> for AppTest {
fn from(value: App) -> Self {
Self {
app: value,
retry_count: 0,
retry_wait: Duration::from_millis(100),
}
}
}
impl Deref for AppTest {
type Target = App;
fn deref(&self) -> &Self::Target {
&self.app
}
}
impl DerefMut for AppTest {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.app
}
}