use crate::common::CountingDbConnection;
use crate::common::*;
use bevy::prelude::IntoScheduleConfigs;
use bevy::prelude::*;
use bevy_persistence_database::bevy::components::Guid;
use bevy_persistence_database::bevy::params::query::PersistentQuery;
use bevy_persistence_database::bevy::plugins::persistence_plugin::PersistenceSystemSet;
use bevy_persistence_database::core::session::commit_sync;
use bevy_persistence_database_derive::db_matrix_test;
use std::sync::{
Arc,
atomic::{AtomicUsize, Ordering},
};
#[db_matrix_test]
fn test_load_then_pass_through() {
let (real_db, _container) = setup();
let mut app_seed = setup_test_app(real_db.clone(), None);
app_seed
.world_mut()
.spawn((Health { value: 150 }, Position { x: 10.0, y: 20.0 }));
app_seed.update();
commit_sync(&mut app_seed, real_db.clone(), TEST_STORE).expect("seed commit failed");
let counter = Arc::new(AtomicUsize::new(0));
let db = Arc::new(CountingDbConnection::new(real_db.clone(), counter.clone()));
let mut app = setup_test_app(db.clone(), None);
fn sys_load(mut pq: PersistentQuery<(&Health, &Position), (With<Health>, With<Position>)>) {
let _ = pq.load().load();
}
app.add_systems(Update, sys_load);
app.update();
fn sys_verify(pq: PersistentQuery<(&Health, &Position), (With<Health>, With<Position>)>) {
let all: Vec<_> = pq.iter().collect();
assert_eq!(all.len(), 1);
let (_e, (h, p)) = pq.single().unwrap();
assert_eq!(h.value, 150);
assert_eq!(p.x, 10.0);
assert_eq!(p.y, 20.0);
let combos = pq.iter_combinations::<1>().count();
assert_eq!(combos, 1);
}
app.add_systems(Update, sys_verify);
app.update();
assert_eq!(
counter.load(Ordering::SeqCst),
1,
"expected exactly one execute_documents call"
);
}
#[db_matrix_test]
fn test_cache_prevents_duplicate_loads_in_same_frame() {
let (real_db, _container) = setup();
let mut app_seed = setup_test_app(real_db.clone(), None);
app_seed.world_mut().spawn(Health { value: 42 });
app_seed.update();
commit_sync(&mut app_seed, real_db.clone(), TEST_STORE).unwrap();
let counter = Arc::new(AtomicUsize::new(0));
let db = Arc::new(CountingDbConnection::new(real_db.clone(), counter.clone()));
let mut app = setup_test_app(db.clone(), None);
fn sys_twice(mut pq: PersistentQuery<&Health, With<Health>>) {
pq.load();
pq.load(); }
app.add_systems(Update, sys_twice);
app.update();
app.update();
assert_eq!(
counter.load(Ordering::SeqCst),
1,
"cache should coalesce identical loads"
);
}
#[db_matrix_test]
fn test_deref_forwards_bevy_query_methods() {
let (db_real, _container) = setup();
let mut app_seed = setup_test_app(db_real.clone(), None);
let e = app_seed
.world_mut()
.spawn((Health { value: 5 }, Position { x: 1.0, y: 2.0 }))
.id();
app_seed.update();
commit_sync(&mut app_seed, db_real.clone(), TEST_STORE).unwrap();
let mut app = setup_test_app(db_real.clone(), None);
fn load(mut pq: PersistentQuery<(&Health, &Position)>) {
let _ = pq.load();
}
app.add_systems(Update, load);
app.update();
#[derive(Resource)]
struct Ent(Entity);
app.insert_resource(Ent(e));
fn verify(pq: PersistentQuery<(&Health, &Position)>, ent: Res<Ent>) {
assert!(pq.contains(ent.0));
let (_e, (h, p)) = pq.get(ent.0).unwrap();
assert_eq!(h.value, 5);
assert_eq!(p.x, 1.0);
let v: Vec<_> = pq.iter().collect();
assert_eq!(v.len(), 1);
assert_eq!(pq.iter_combinations::<1>().count(), 1);
let _ = pq.get_many([ent.0]).unwrap();
}
app.add_systems(Update, verify);
app.update();
}
#[db_matrix_test]
fn test_immediate_pass_through() {
let (db, _container) = setup();
let mut app_seed = setup_test_app(db.clone(), None);
app_seed.world_mut().spawn(Health { value: 10 });
app_seed.world_mut().spawn(Health { value: 20 });
app_seed.update();
commit_sync(&mut app_seed, db.clone(), TEST_STORE).expect("seed commit failed");
let mut app = setup_test_app(db.clone(), None);
#[derive(Resource, Default)]
struct TestResults {
immediate_iter_count: usize,
immediate_get_success: bool,
immediate_contains: bool,
first_entity: Option<Entity>,
}
app.insert_resource(TestResults::default());
fn test_immediate_system(mut pq: PersistentQuery<&Health>, mut results: ResMut<TestResults>) {
pq.load();
let entities: Vec<Entity> = pq.iter().map(|(e, _)| e).collect();
results.immediate_iter_count = entities.len();
if let Some(&first) = entities.first() {
results.first_entity = Some(first);
results.immediate_get_success = pq.get(first).is_ok();
results.immediate_contains = pq.contains(first);
}
}
app.add_systems(
PostUpdate,
test_immediate_system.after(PersistenceSystemSet::PreCommit),
);
app.update();
let result = app.world().resource::<TestResults>();
assert_eq!(
result.immediate_iter_count, 2,
"iter should return all loaded entities immediately"
);
assert!(
result.immediate_get_success,
"get should work immediately after load"
);
assert!(
result.immediate_contains,
"contains should work immediately after load"
);
if let Some(entity) = result.first_entity {
let health = app.world().get::<Health>(entity);
assert!(health.is_some(), "Entity should exist in the world");
} else {
panic!("Failed to capture entity during immediate apply");
}
}
#[db_matrix_test]
fn test_query_contains_method() {
let (db, _container) = setup();
let mut app_seed = setup_test_app(db.clone(), None);
let entity = app_seed.world_mut().spawn(Health { value: 42 }).id();
app_seed.update();
commit_sync(&mut app_seed, db.clone(), TEST_STORE).expect("seed commit failed");
let _guid = app_seed
.world()
.get::<Guid>(entity)
.unwrap()
.id()
.to_string();
let mut app = setup_test_app(db.clone(), None);
#[derive(Resource, Default)]
struct LoadedEntity(Option<Entity>);
app.insert_resource(LoadedEntity::default());
fn load_health(mut pq: PersistentQuery<&Health>, mut res: ResMut<LoadedEntity>) {
pq.load();
if let Some((e, _)) = pq.iter().next() {
res.0 = Some(e);
}
}
#[derive(Resource, Default)]
struct TestResult(bool);
app.insert_resource(TestResult::default());
fn check_contains(
pq: PersistentQuery<&Health>,
entity: Res<LoadedEntity>,
mut result: ResMut<TestResult>,
) {
if let Some(e) = entity.0 {
result.0 = pq.contains(e);
}
}
app.add_systems(Update, load_health.after(PersistenceSystemSet::PreCommit));
app.update();
app.add_systems(Update, check_contains);
app.update();
let contains_result = app.world().resource::<TestResult>().0;
assert!(
contains_result,
"contains() should return true for a loaded entity"
);
}
#[db_matrix_test]
fn test_query_get_method() {
let (db, _container) = setup();
let mut app_seed = setup_test_app(db.clone(), None);
let _entity = app_seed.world_mut().spawn(Health { value: 42 }).id();
app_seed.update();
commit_sync(&mut app_seed, db.clone(), TEST_STORE).expect("seed commit failed");
let mut app = setup_test_app(db.clone(), None);
#[derive(Resource, Default)]
struct LoadedEntity(Option<Entity>);
app.insert_resource(LoadedEntity::default());
fn load_health(mut pq: PersistentQuery<&Health>, mut res: ResMut<LoadedEntity>) {
pq.load();
if let Some((e, _)) = pq.iter().next() {
res.0 = Some(e);
}
}
#[derive(Resource, Default)]
struct TestResult(bool);
app.insert_resource(TestResult::default());
fn check_get(
pq: PersistentQuery<&Health>,
entity: Res<LoadedEntity>,
mut result: ResMut<TestResult>,
) {
if let Some(e) = entity.0 {
result.0 = pq.get(e).is_ok();
}
}
app.add_systems(Update, load_health.after(PersistenceSystemSet::PreCommit));
app.update();
app.add_systems(Update, check_get);
app.update();
let get_result = app.world().resource::<TestResult>().0;
assert!(get_result, "get() should succeed for a loaded entity");
}
#[db_matrix_test]
fn test_query_get_mut_method() {
let (db, _container) = setup();
let mut app_seed = setup_test_app(db.clone(), None);
let _entity = app_seed.world_mut().spawn(Health { value: 42 }).id();
app_seed.update();
commit_sync(&mut app_seed, db.clone(), TEST_STORE).expect("seed commit failed");
let mut app = setup_test_app(db.clone(), None);
#[derive(Resource, Default)]
struct LoadedEntity(Option<Entity>);
app.insert_resource(LoadedEntity::default());
fn load_health(mut pq: PersistentQuery<&Health>, mut res: ResMut<LoadedEntity>) {
pq.load();
if let Some((e, _)) = pq.iter().next() {
res.0 = Some(e);
}
}
#[derive(Resource, Default)]
struct TestResult(bool);
app.insert_resource(TestResult::default());
fn check_get_mut(
mut pq: PersistentQuery<&mut Health>,
entity: Res<LoadedEntity>,
mut result: ResMut<TestResult>,
) {
if let Some(e) = entity.0 {
result.0 = pq.get_mut(e).is_ok();
}
}
app.add_systems(Update, load_health.after(PersistenceSystemSet::PreCommit));
app.update();
app.add_systems(Update, check_get_mut);
app.update();
let get_mut_result = app.world().resource::<TestResult>().0;
assert!(
get_mut_result,
"get_mut() should succeed for a loaded entity"
);
}
#[db_matrix_test]
fn test_query_get_many_method() {
let (db, _container) = setup();
let mut app_seed = setup_test_app(db.clone(), None);
app_seed.world_mut().spawn(Health { value: 10 });
app_seed.world_mut().spawn(Health { value: 20 });
app_seed.update();
commit_sync(&mut app_seed, db.clone(), TEST_STORE).expect("seed commit failed");
let mut app = setup_test_app(db.clone(), None);
#[derive(Resource, Default)]
struct LoadedEntities(Vec<Entity>);
app.insert_resource(LoadedEntities::default());
fn load_health(mut pq: PersistentQuery<&Health>, mut res: ResMut<LoadedEntities>) {
pq.load();
for (e, _) in pq.iter() {
res.0.push(e);
}
}
#[derive(Resource, Default)]
struct TestResult(bool);
app.insert_resource(TestResult::default());
fn check_get_many(
pq: PersistentQuery<&Health>,
entities: Res<LoadedEntities>,
mut result: ResMut<TestResult>,
) {
if entities.0.len() < 2 {
return;
}
let get_result = pq.get_many([entities.0[0], entities.0[1]]);
result.0 = get_result.is_ok();
}
app.add_systems(Update, load_health.after(PersistenceSystemSet::PreCommit));
app.update();
app.add_systems(Update, check_get_many);
app.update();
let get_many_result = app.world().resource::<TestResult>().0;
assert!(
get_many_result,
"get_many() should succeed for loaded entities"
);
}
#[db_matrix_test]
fn test_query_single_method() {
let (db, _container) = setup();
let mut app_seed = setup_test_app(db.clone(), None);
app_seed.world_mut().spawn((
Health { value: 50 },
PlayerName {
name: "player".into(),
},
));
app_seed.update();
commit_sync(&mut app_seed, db.clone(), TEST_STORE).expect("seed commit failed");
let mut app = setup_test_app(db.clone(), None);
#[derive(Resource, Default)]
struct SingleResult {
health_value: Option<i32>,
success: bool,
}
app.insert_resource(SingleResult::default());
fn test_single(
mut pq: PersistentQuery<&Health, With<PlayerName>>,
mut result: ResMut<SingleResult>,
) {
pq.load();
match pq.single() {
Ok((_e, health)) => {
result.health_value = Some(health.value);
result.success = true;
}
Err(_) => {
result.success = false;
}
}
}
app.add_systems(Update, test_single.after(PersistenceSystemSet::PreCommit));
app.update();
let result = app.world().resource::<SingleResult>();
assert!(
result.success,
"single() should succeed for a unique entity"
);
assert_eq!(
result.health_value,
Some(50),
"single() should return the correct health value"
);
}
#[db_matrix_test]
fn test_query_iter_combinations_method() {
let (db, _container) = setup();
let mut app_seed = setup_test_app(db.clone(), None);
app_seed.world_mut().spawn(Health { value: 10 });
app_seed.world_mut().spawn(Health { value: 20 });
app_seed.world_mut().spawn(Health { value: 30 });
app_seed.update();
commit_sync(&mut app_seed, db.clone(), TEST_STORE).expect("seed commit failed");
let mut app = setup_test_app(db.clone(), None);
#[derive(Resource, Default)]
struct CombinationsResult {
count: usize,
}
app.insert_resource(CombinationsResult::default());
fn test_combinations(mut pq: PersistentQuery<&Health>, mut result: ResMut<CombinationsResult>) {
pq.load();
result.count = pq.iter_combinations::<2>().count();
bevy::log::info!("Found {} combinations of 2 entities", result.count);
}
app.add_systems(
Update,
test_combinations.after(PersistenceSystemSet::PreCommit),
);
app.update();
let result = app.world().resource::<CombinationsResult>();
assert_eq!(
result.count, 3,
"iter_combinations should return the correct number of combinations"
);
}