mod query;
use std::{any::type_name, fmt::Debug};
use bevy::{
ecs::{
query::{QueryFilter, ReadOnlyQueryData},
world::SpawnBatchIter,
},
prelude::*,
};
use colored::Colorize;
use query::AssertQuery;
use sealed::sealed;
#[sealed]
pub trait TestApp {
fn spawn_empty(&mut self) -> EntityWorldMut;
fn spawn<B: Bundle>(&mut self, bundle: B) -> EntityWorldMut;
fn spawn_batch<I>(&mut self, iter: I) -> SpawnBatchIter<'_, I::IntoIter>
where
I: IntoIterator,
I::Item: Bundle;
fn entity(&self, entity: Entity) -> EntityRef;
fn entity_mut(&mut self, entity: Entity) -> EntityWorldMut;
fn get_entity(&self, entity: Entity) -> Option<EntityRef>;
fn get_entity_mut(&mut self, entity: Entity) -> Option<EntityWorldMut>;
fn component<T: Component>(&self, entity: Entity) -> &T;
fn get_component<T: Component>(&self, entity: Entity) -> Option<&T>;
fn query<'w, D: ReadOnlyQueryData>(&'w mut self) -> AssertQuery<'w, D>
where
D::Item<'w>: PartialEq + Debug;
fn query_filtered<'w, D: ReadOnlyQueryData, F: QueryFilter>(&'w mut self) -> AssertQuery<'w, D>
where
D::Item<'w>: PartialEq + Debug;
fn update_once(&mut self);
fn update_n_times(&mut self, amount: u32);
}
#[sealed]
impl TestApp for App {
fn spawn_empty(&mut self) -> EntityWorldMut {
self.world_mut().spawn_empty()
}
fn spawn<B: Bundle>(&mut self, bundle: B) -> EntityWorldMut {
self.world_mut().spawn(bundle)
}
fn spawn_batch<I>(&mut self, iter: I) -> SpawnBatchIter<'_, I::IntoIter>
where
I: IntoIterator,
I::Item: Bundle,
{
self.world_mut().spawn_batch(iter)
}
fn entity(&self, entity: Entity) -> EntityRef {
self.world().entity(entity)
}
fn entity_mut(&mut self, entity: Entity) -> EntityWorldMut {
self.world_mut().entity_mut(entity)
}
fn get_entity(&self, entity: Entity) -> Option<EntityRef> {
self.world().get_entity(entity)
}
fn get_entity_mut(&mut self, entity: Entity) -> Option<EntityWorldMut> {
self.world_mut().get_entity_mut(entity)
}
fn component<T: Component>(&self, entity: Entity) -> &T {
self.get_component(entity).unwrap_or_else(|| {
panic!(
"component \"{}\" is not part of the entity",
type_name::<T>()
)
})
}
fn get_component<T: Component>(&self, entity: Entity) -> Option<&T> {
self.world().entity(entity).get::<T>()
}
fn query<'w, D: ReadOnlyQueryData>(&'w mut self) -> AssertQuery<'w, D>
where
D::Item<'w>: PartialEq + Debug,
{
let mut query = self.world_mut().query::<D>();
let collected = query.iter(self.world()).collect::<Vec<_>>();
AssertQuery {
query: collected,
invert: false,
}
}
fn query_filtered<'w, D: ReadOnlyQueryData, F: QueryFilter>(&'w mut self) -> AssertQuery<'w, D>
where
D::Item<'w>: PartialEq + Debug,
{
let mut query = self.world_mut().query_filtered::<D, F>();
let collected = query.iter(self.world()).collect::<Vec<_>>();
AssertQuery {
query: collected,
invert: false,
}
}
fn update_once(&mut self) {
self.update();
}
fn update_n_times(&mut self, amount: u32) {
for _ in 0..amount {
self.update_once();
}
}
}
const MAX_DEBUG_LEN: usize = 300;
fn mismatch(message: &str, given: impl Debug, found: impl Debug) -> ! {
let mut given = format!("{:#?}", given);
if given.len() > MAX_DEBUG_LEN {
given = given[0..MAX_DEBUG_LEN].to_owned() + &" ...".bright_black();
}
let mut found = format!("{:#?}", found);
if found.len() > MAX_DEBUG_LEN {
found = found[0..MAX_DEBUG_LEN].to_owned() + &" ...".bright_black();
}
eprintln!("{}", message.red());
if given.contains('\n') {
eprintln!("{}", "Given:".bright_black());
eprintln!("{}", given);
} else {
eprintln!("{} {}", "Given:".bright_black(), given);
}
eprintln!();
if found.contains('\n') {
eprintln!("{}", "Found:".bright_black());
eprintln!("{}", found);
} else {
eprintln!("{} {}", "Found:".bright_black(), found);
}
panic!("assertion failed");
}
fn unexpected_match(message: &str, matches: impl Debug) -> ! {
let mut given = format!("{:#?}", matches);
if given.len() > MAX_DEBUG_LEN {
given = given[0..MAX_DEBUG_LEN].to_owned() + &" ...".bright_black();
}
eprintln!("{}", message.red());
if given.contains('\n') {
eprintln!("{}", "Match:".bright_black());
eprintln!("{}", given);
} else {
eprintln!("{} {}", "Match:".bright_black(), given);
}
panic!("assertion failed");
}
pub mod p {
pub use crate::TestApp;
pub use bevy::prelude::*;
}
mod my_lib {
use crate::p::*;
#[derive(Component, Debug, PartialEq)]
pub struct Countdown(pub u32);
pub struct CountdownPlugin;
impl Plugin for CountdownPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, countdown_sys);
}
}
fn countdown_sys(mut query: Query<&mut Countdown>) {
for mut countdown in &mut query {
countdown.0 -= 1;
}
}
}