From 307a7257e57306b6bbe638c98165cdb4d81dcfdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Sat, 20 Mar 2021 19:40:35 +0100 Subject: [PATCH 01/13] example tests for systems --- examples/README.md | 9 +++ tests/how_to_test_systems.rs | 119 +++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 tests/how_to_test_systems.rs diff --git a/examples/README.md b/examples/README.md index 15175765937f4..0565b18f0397b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -47,6 +47,7 @@ git checkout v0.4.0 - [Reflection](#reflection) - [Scene](#scene) - [Shaders](#shaders) + - [Tests](#tests) - [Tools](#tools) - [UI (User Interface)](#ui-user-interface) - [Window](#window) @@ -197,6 +198,14 @@ Example | File | Description `shader_custom_material` | [`shader/shader_custom_material.rs`](./shader/shader_custom_material.rs) | Illustrates creating a custom material and a shader that uses it `shader_defs` | [`shader/shader_defs.rs`](./shader/shader_defs.rs) | Demonstrates creating a custom material that uses "shaders defs" (a tool to selectively toggle parts of a shader) +## Tests + +Those are not in examples, but in the tests folder. + +Example | File | Description +--- | --- | --- +`how_to_test_systems` | [`../tests/how_to_test_systems.rs`](../tests/how_to_test_systems.rs) | How to tests systems with commands, queries or resources + ## Tools Example | File | Description diff --git a/tests/how_to_test_systems.rs b/tests/how_to_test_systems.rs new file mode 100644 index 0000000000000..f51a1817255ad --- /dev/null +++ b/tests/how_to_test_systems.rs @@ -0,0 +1,119 @@ +use std::collections::HashMap; + +use bevy::{ecs::system::CommandQueue, prelude::*}; + +struct Enemy { + hit_points: u32, +} + +struct CharacterTemplate { + hit_points: HashMap<&'static str, u32>, +} + +fn despawn_dead_enemies(mut commands: Commands, enemies: Query<(Entity, &Enemy)>) { + for (entity, enemy) in enemies.iter() { + if enemy.hit_points == 0 { + commands.despawn_recursive(entity); + } + } +} + +fn hurt_enemies(mut enemies: Query<&mut Enemy>) { + for mut enemy in enemies.iter_mut() { + enemy.hit_points -= 1; + } +} + +fn spawn_enemy(mut commands: Commands, character_template: Res) { + commands.spawn((Enemy { + hit_points: *character_template.hit_points.get("enemy").unwrap(), + },)); +} + +#[test] +fn did_hurt_ennemy() { + // Setup world and commands + let mut world = World::default(); + let mut queue = CommandQueue::default(); + let mut commands = Commands::new(&mut queue, &world); + + // Setup stage with our two systems + let mut update_stage = SystemStage::parallel(); + update_stage.add_system(hurt_enemies.system().before("death")); + update_stage.add_system(despawn_dead_enemies.system().label("death")); + let mut schedule = Schedule::default(); + schedule.add_stage("update", update_stage); + + // Setup test entities + let ennemy_id = commands + .spawn((Enemy { hit_points: 5 },)) + .current_entity() + .unwrap(); + queue.apply(&mut world); + + // Run systems + schedule.run(&mut world); + + // Check resulting changes + assert!(world.get::(ennemy_id).is_some()); + assert_eq!(world.get::(ennemy_id).unwrap().hit_points, 4); +} + +#[test] +fn did_despawn_ennemy() { + // Setup world and commands + let mut world = World::default(); + let mut queue = CommandQueue::default(); + let mut commands = Commands::new(&mut queue, &world); + + // Setup stage with our two systems + let mut update_stage = SystemStage::parallel(); + update_stage.add_system(hurt_enemies.system().before("death")); + update_stage.add_system(despawn_dead_enemies.system().label("death")); + let mut schedule = Schedule::default(); + schedule.add_stage("update", update_stage); + + // Setup test entities + let ennemy_id = commands + .spawn((Enemy { hit_points: 1 },)) + .current_entity() + .unwrap(); + queue.apply(&mut world); + + // Run systems + schedule.run(&mut world); + + // Check resulting changes + assert!(world.get::(ennemy_id).is_none()); +} + +#[test] +fn spawned_from_resource() { + // Setup world and commands + let mut world = World::default(); + let mut queue = CommandQueue::default(); + let mut commands = Commands::new(&mut queue, &world); + + // Setup stage with a system + let mut update_stage = SystemStage::parallel(); + update_stage.add_system(spawn_enemy.system()); + let mut schedule = Schedule::default(); + schedule.add_stage("update", update_stage); + + // Setup test resource + let mut hit_points = HashMap::new(); + hit_points.insert("enemy", 25); + commands.insert_resource(CharacterTemplate { hit_points }); + queue.apply(&mut world); + + // Run systems + schedule.run(&mut world); + + // Check resulting changes + let mut query = world.query::<&Enemy>(); + let results = query + .iter(&world) + .map(|enemy| enemy.hit_points) + .collect::>(); + assert_eq!(results, vec![25]); +} From e89ec7003bc043d786355d47bf7c1f29fea701d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Sun, 21 Mar 2021 17:36:19 +0100 Subject: [PATCH 02/13] test name Co-authored-by: Alice Cecile --- tests/how_to_test_systems.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/how_to_test_systems.rs b/tests/how_to_test_systems.rs index f51a1817255ad..6c29629c58e00 100644 --- a/tests/how_to_test_systems.rs +++ b/tests/how_to_test_systems.rs @@ -31,7 +31,7 @@ fn spawn_enemy(mut commands: Commands, character_template: Res Date: Mon, 22 Mar 2021 01:32:20 +0100 Subject: [PATCH 03/13] Update tests/how_to_test_systems.rs Co-authored-by: Alice Cecile --- tests/how_to_test_systems.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/how_to_test_systems.rs b/tests/how_to_test_systems.rs index 6c29629c58e00..90ba85ba0a5aa 100644 --- a/tests/how_to_test_systems.rs +++ b/tests/how_to_test_systems.rs @@ -60,7 +60,7 @@ fn did_hurt_enemy() { } #[test] -fn did_despawn_ennemy() { +fn did_despawn_enemy() { // Setup world and commands let mut world = World::default(); let mut queue = CommandQueue::default(); From 79094ce2bb42d64e4a572d551802fce921a64c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Tue, 23 Mar 2021 19:30:58 +0100 Subject: [PATCH 04/13] update for new api --- tests/how_to_test_systems.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/tests/how_to_test_systems.rs b/tests/how_to_test_systems.rs index 90ba85ba0a5aa..c0ff738faa45a 100644 --- a/tests/how_to_test_systems.rs +++ b/tests/how_to_test_systems.rs @@ -13,7 +13,7 @@ struct CharacterTemplate { fn despawn_dead_enemies(mut commands: Commands, enemies: Query<(Entity, &Enemy)>) { for (entity, enemy) in enemies.iter() { if enemy.hit_points == 0 { - commands.despawn_recursive(entity); + commands.entity(entity).despawn_recursive(); } } } @@ -25,9 +25,9 @@ fn hurt_enemies(mut enemies: Query<&mut Enemy>) { } fn spawn_enemy(mut commands: Commands, character_template: Res) { - commands.spawn((Enemy { + commands.spawn().insert(Enemy { hit_points: *character_template.hit_points.get("enemy").unwrap(), - },)); + }); } #[test] @@ -45,10 +45,7 @@ fn did_hurt_enemy() { schedule.add_stage("update", update_stage); // Setup test entities - let ennemy_id = commands - .spawn((Enemy { hit_points: 5 },)) - .current_entity() - .unwrap(); + let ennemy_id = commands.spawn().insert(Enemy { hit_points: 5 }).id(); queue.apply(&mut world); // Run systems @@ -74,10 +71,7 @@ fn did_despawn_enemy() { schedule.add_stage("update", update_stage); // Setup test entities - let ennemy_id = commands - .spawn((Enemy { hit_points: 1 },)) - .current_entity() - .unwrap(); + let ennemy_id = commands.spawn().insert(Enemy { hit_points: 1 }).id(); queue.apply(&mut world); // Run systems From 95946b01b3f1f7d20978d4f06142afab763cdafa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 12 Apr 2021 21:35:50 +0200 Subject: [PATCH 05/13] merge test and diagnostics in a development category --- examples/README.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/examples/README.md b/examples/README.md index 0565b18f0397b..ee7979942a0cc 100644 --- a/examples/README.md +++ b/examples/README.md @@ -40,7 +40,7 @@ git checkout v0.4.0 - [Application](#application) - [Assets](#assets) - [Audio](#audio) - - [Diagnostics](#diagnostics) + - [Development](#development) - [ECS (Entity Component System)](#ecs-entity-component-system) - [Games](#games) - [Input](#input) @@ -127,12 +127,13 @@ Example | File | Description --- | --- | --- `audio` | [`audio/audio.rs`](./audio/audio.rs) | Shows how to load and play an audio file -## Diagnostics +## Development Example | File | Description --- | --- | --- -`log_diagnostics` | [`diagnostics/log_diagnostics.rs`](./diagnostics/log_diagnostics.rs) | Add a plugin that logs diagnostics to the console `custom_diagnostic` | [`diagnostics/custom_diagnostic.rs`](./diagnostics/custom_diagnostic.rs) | Shows how to create a custom diagnostic +`how_to_test_systems` | [`../tests/how_to_test_systems.rs`](../tests/how_to_test_systems.rs) | How to tests systems with commands, queries or resources +`log_diagnostics` | [`diagnostics/log_diagnostics.rs`](./diagnostics/log_diagnostics.rs) | Add a plugin that logs diagnostics to the console ## ECS (Entity Component System) @@ -198,14 +199,6 @@ Example | File | Description `shader_custom_material` | [`shader/shader_custom_material.rs`](./shader/shader_custom_material.rs) | Illustrates creating a custom material and a shader that uses it `shader_defs` | [`shader/shader_defs.rs`](./shader/shader_defs.rs) | Demonstrates creating a custom material that uses "shaders defs" (a tool to selectively toggle parts of a shader) -## Tests - -Those are not in examples, but in the tests folder. - -Example | File | Description ---- | --- | --- -`how_to_test_systems` | [`../tests/how_to_test_systems.rs`](../tests/how_to_test_systems.rs) | How to tests systems with commands, queries or resources - ## Tools Example | File | Description From c606a4fe9e8c5d32b801d9ac3b9d8035a8a69229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 12 Apr 2021 21:55:30 +0200 Subject: [PATCH 06/13] no need for schedule --- tests/how_to_test_systems.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/how_to_test_systems.rs b/tests/how_to_test_systems.rs index c0ff738faa45a..57762491ef0b5 100644 --- a/tests/how_to_test_systems.rs +++ b/tests/how_to_test_systems.rs @@ -41,15 +41,13 @@ fn did_hurt_enemy() { let mut update_stage = SystemStage::parallel(); update_stage.add_system(hurt_enemies.system().before("death")); update_stage.add_system(despawn_dead_enemies.system().label("death")); - let mut schedule = Schedule::default(); - schedule.add_stage("update", update_stage); // Setup test entities let ennemy_id = commands.spawn().insert(Enemy { hit_points: 5 }).id(); queue.apply(&mut world); // Run systems - schedule.run(&mut world); + update_stage.run(&mut world); // Check resulting changes assert!(world.get::(ennemy_id).is_some()); @@ -67,15 +65,13 @@ fn did_despawn_enemy() { let mut update_stage = SystemStage::parallel(); update_stage.add_system(hurt_enemies.system().before("death")); update_stage.add_system(despawn_dead_enemies.system().label("death")); - let mut schedule = Schedule::default(); - schedule.add_stage("update", update_stage); // Setup test entities let ennemy_id = commands.spawn().insert(Enemy { hit_points: 1 }).id(); queue.apply(&mut world); // Run systems - schedule.run(&mut world); + update_stage.run(&mut world); // Check resulting changes assert!(world.get::(ennemy_id).is_none()); @@ -91,8 +87,6 @@ fn spawned_from_resource() { // Setup stage with a system let mut update_stage = SystemStage::parallel(); update_stage.add_system(spawn_enemy.system()); - let mut schedule = Schedule::default(); - schedule.add_stage("update", update_stage); // Setup test resource let mut hit_points = HashMap::new(); @@ -101,7 +95,7 @@ fn spawned_from_resource() { queue.apply(&mut world); // Run systems - schedule.run(&mut world); + update_stage.run(&mut world); // Check resulting changes let mut query = world.query::<&Enemy>(); From caf497a2c9d94a29ae794cf6fa93c485a85abf72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 12 Apr 2021 21:55:38 +0200 Subject: [PATCH 07/13] typos --- tests/how_to_test_systems.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/how_to_test_systems.rs b/tests/how_to_test_systems.rs index 57762491ef0b5..ed0b5b0cb82af 100644 --- a/tests/how_to_test_systems.rs +++ b/tests/how_to_test_systems.rs @@ -43,15 +43,15 @@ fn did_hurt_enemy() { update_stage.add_system(despawn_dead_enemies.system().label("death")); // Setup test entities - let ennemy_id = commands.spawn().insert(Enemy { hit_points: 5 }).id(); + let enemy_id = commands.spawn().insert(Enemy { hit_points: 5 }).id(); queue.apply(&mut world); // Run systems update_stage.run(&mut world); // Check resulting changes - assert!(world.get::(ennemy_id).is_some()); - assert_eq!(world.get::(ennemy_id).unwrap().hit_points, 4); + assert!(world.get::(enemy_id).is_some()); + assert_eq!(world.get::(enemy_id).unwrap().hit_points, 4); } #[test] @@ -67,14 +67,14 @@ fn did_despawn_enemy() { update_stage.add_system(despawn_dead_enemies.system().label("death")); // Setup test entities - let ennemy_id = commands.spawn().insert(Enemy { hit_points: 1 }).id(); + let enemy_id = commands.spawn().insert(Enemy { hit_points: 1 }).id(); queue.apply(&mut world); // Run systems update_stage.run(&mut world); // Check resulting changes - assert!(world.get::(ennemy_id).is_none()); + assert!(world.get::(enemy_id).is_none()); } #[test] From 328769718ac0ace539dfe70a0c07dc8d79d947f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 12 Apr 2021 21:56:30 +0200 Subject: [PATCH 08/13] typo --- examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index ee7979942a0cc..27fabfeea3ea0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -132,7 +132,7 @@ Example | File | Description Example | File | Description --- | --- | --- `custom_diagnostic` | [`diagnostics/custom_diagnostic.rs`](./diagnostics/custom_diagnostic.rs) | Shows how to create a custom diagnostic -`how_to_test_systems` | [`../tests/how_to_test_systems.rs`](../tests/how_to_test_systems.rs) | How to tests systems with commands, queries or resources +`how_to_test_systems` | [`../tests/how_to_test_systems.rs`](../tests/how_to_test_systems.rs) | How to test systems with commands, queries or resources `log_diagnostics` | [`diagnostics/log_diagnostics.rs`](./diagnostics/log_diagnostics.rs) | Add a plugin that logs diagnostics to the console ## ECS (Entity Component System) From a201c5029050af012384875ca308540df54b04e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Tue, 13 Apr 2021 03:11:02 +0200 Subject: [PATCH 09/13] back to tests & diagnostics --- examples/README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/examples/README.md b/examples/README.md index 27fabfeea3ea0..231451ce4b809 100644 --- a/examples/README.md +++ b/examples/README.md @@ -40,7 +40,7 @@ git checkout v0.4.0 - [Application](#application) - [Assets](#assets) - [Audio](#audio) - - [Development](#development) + - [Diagnostics](#diagnostics) - [ECS (Entity Component System)](#ecs-entity-component-system) - [Games](#games) - [Input](#input) @@ -127,12 +127,11 @@ Example | File | Description --- | --- | --- `audio` | [`audio/audio.rs`](./audio/audio.rs) | Shows how to load and play an audio file -## Development +## Diagnostics Example | File | Description --- | --- | --- `custom_diagnostic` | [`diagnostics/custom_diagnostic.rs`](./diagnostics/custom_diagnostic.rs) | Shows how to create a custom diagnostic -`how_to_test_systems` | [`../tests/how_to_test_systems.rs`](../tests/how_to_test_systems.rs) | How to test systems with commands, queries or resources `log_diagnostics` | [`diagnostics/log_diagnostics.rs`](./diagnostics/log_diagnostics.rs) | Add a plugin that logs diagnostics to the console ## ECS (Entity Component System) @@ -199,6 +198,12 @@ Example | File | Description `shader_custom_material` | [`shader/shader_custom_material.rs`](./shader/shader_custom_material.rs) | Illustrates creating a custom material and a shader that uses it `shader_defs` | [`shader/shader_defs.rs`](./shader/shader_defs.rs) | Demonstrates creating a custom material that uses "shaders defs" (a tool to selectively toggle parts of a shader) +## Tests + +Example | File | Description +--- | --- | --- +`how_to_test_systems` | [`../tests/how_to_test_systems.rs`](../tests/how_to_test_systems.rs) | How to test systems with commands, queries or resources + ## Tools Example | File | Description From 6f2ce5f97624e3e607a5c974327d92af961d85d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Tue, 13 Apr 2021 03:16:26 +0200 Subject: [PATCH 10/13] use world directly for test setup --- tests/how_to_test_systems.rs | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/tests/how_to_test_systems.rs b/tests/how_to_test_systems.rs index ed0b5b0cb82af..cc13cadbf8810 100644 --- a/tests/how_to_test_systems.rs +++ b/tests/how_to_test_systems.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use bevy::{ecs::system::CommandQueue, prelude::*}; +use bevy::prelude::*; struct Enemy { hit_points: u32, @@ -32,10 +32,8 @@ fn spawn_enemy(mut commands: Commands, character_template: Res Date: Tue, 13 Apr 2021 04:08:09 +0200 Subject: [PATCH 11/13] using resource `Input` for example of test is more interesting --- tests/how_to_test_systems.rs | 40 +++++++++++++++++------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/tests/how_to_test_systems.rs b/tests/how_to_test_systems.rs index cc13cadbf8810..320e864182490 100644 --- a/tests/how_to_test_systems.rs +++ b/tests/how_to_test_systems.rs @@ -1,15 +1,10 @@ -use std::collections::HashMap; - use bevy::prelude::*; +#[derive(Default)] struct Enemy { hit_points: u32, } -struct CharacterTemplate { - hit_points: HashMap<&'static str, u32>, -} - fn despawn_dead_enemies(mut commands: Commands, enemies: Query<(Entity, &Enemy)>) { for (entity, enemy) in enemies.iter() { if enemy.hit_points == 0 { @@ -24,10 +19,10 @@ fn hurt_enemies(mut enemies: Query<&mut Enemy>) { } } -fn spawn_enemy(mut commands: Commands, character_template: Res) { - commands.spawn().insert(Enemy { - hit_points: *character_template.hit_points.get("enemy").unwrap(), - }); +fn spawn_enemy(mut commands: Commands, keyboard_input: Res>) { + if keyboard_input.just_pressed(KeyCode::Space) { + commands.spawn().insert(Enemy { hit_points: 5 }); + } } #[test] @@ -72,7 +67,7 @@ fn did_despawn_enemy() { } #[test] -fn spawned_from_resource() { +fn spawn_enemy_using_input_resource() { // Setup world let mut world = World::default(); @@ -81,18 +76,21 @@ fn spawned_from_resource() { update_stage.add_system(spawn_enemy.system()); // Setup test resource - let mut hit_points = HashMap::new(); - hit_points.insert("enemy", 25); - world.insert_resource(CharacterTemplate { hit_points }); + let mut input = Input::::default(); + input.press(KeyCode::Space); + world.insert_resource(input); // Run systems update_stage.run(&mut world); - // Check resulting changes - let mut query = world.query::<&Enemy>(); - let results = query - .iter(&world) - .map(|enemy| enemy.hit_points) - .collect::>(); - assert_eq!(results, vec![25]); + // Check resulting changes, one entity has been spawned with `Enemy` component + assert_eq!(world.query::<&Enemy>().iter(&world).len(), 1); + + world.get_resource_mut::>().unwrap().update(); + + // Run systems + update_stage.run(&mut world); + + // Check resulting changes, no new entity has been spawned + assert_eq!(world.query::<&Enemy>().iter(&world).len(), 1); } From fa68c2979ae32a3d87e6d772f50b6996310b540c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Tue, 13 Apr 2021 04:14:43 +0200 Subject: [PATCH 12/13] with a comment on the resource update --- tests/how_to_test_systems.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/how_to_test_systems.rs b/tests/how_to_test_systems.rs index 320e864182490..be965225476a1 100644 --- a/tests/how_to_test_systems.rs +++ b/tests/how_to_test_systems.rs @@ -86,6 +86,7 @@ fn spawn_enemy_using_input_resource() { // Check resulting changes, one entity has been spawned with `Enemy` component assert_eq!(world.query::<&Enemy>().iter(&world).len(), 1); + // Clear the `just_pressed` status for all `KeyCode`s world.get_resource_mut::>().unwrap().update(); // Run systems From a11f3429ed3cd6dfd53e2f636f8c1641a385790a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Thu, 15 Apr 2021 02:45:50 +0200 Subject: [PATCH 13/13] update for main changes --- tests/how_to_test_systems.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/how_to_test_systems.rs b/tests/how_to_test_systems.rs index be965225476a1..b70807aafa964 100644 --- a/tests/how_to_test_systems.rs +++ b/tests/how_to_test_systems.rs @@ -87,7 +87,7 @@ fn spawn_enemy_using_input_resource() { assert_eq!(world.query::<&Enemy>().iter(&world).len(), 1); // Clear the `just_pressed` status for all `KeyCode`s - world.get_resource_mut::>().unwrap().update(); + world.get_resource_mut::>().unwrap().clear(); // Run systems update_stage.run(&mut world);