Skip to content

Commit 06d55c5

Browse files
ostwilkensmdickopp
andcommitted
Make Time::update_with_instant public for use in tests (#4469)
# Objective To test systems that implement frame rate-independent update logic, one needs to be able to mock `Time`. By mocking time, it's possible to write tests that confirm systems are frame rate-independent. This is a follow-up PR to #2549 by @ostwilkens and based on his work. ## Solution To mock `Time`, one needs to be able to manually update the Time resource with an `Instant` defined by the developer. This can be achieved by making the existing `Time::update_with_instant` method public for use in tests. ## Changelog - Make `Time::update_with_instant` public - Add doc to `Time::update_with_instant` clarifying that the method should not be called outside of tests. - Add doc test to `Time` demonstrating how to use `update_with_instant` in tests. Co-authored-by: Martin Dickopp <[email protected]>
1 parent 46acb77 commit 06d55c5

File tree

1 file changed

+56
-1
lines changed

1 file changed

+56
-1
lines changed

crates/bevy_core/src/time/time.rs

+56-1
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,66 @@ impl Default for Time {
2929

3030
impl Time {
3131
/// Updates the internal time measurements.
32+
///
33+
/// Calling this method on the [`Time`] resource as part of your app will most likely result in
34+
/// inaccurate timekeeping, as the resource is ordinarily managed by the
35+
/// [`CorePlugin`](crate::CorePlugin).
3236
pub fn update(&mut self) {
3337
self.update_with_instant(Instant::now());
3438
}
3539

36-
pub(crate) fn update_with_instant(&mut self, instant: Instant) {
40+
/// Update time with a specified [`Instant`]
41+
///
42+
/// This method is provided for use in tests. Calling this method on the [`Time`] resource as
43+
/// part of your app will most likely result in inaccurate timekeeping, as the resource is
44+
/// ordinarily managed by the [`CorePlugin`](crate::CorePlugin).
45+
///
46+
/// # Examples
47+
///
48+
/// ```
49+
/// # use bevy_core::prelude::*;
50+
/// # use bevy_ecs::prelude::*;
51+
/// # use bevy_utils::Duration;
52+
/// # fn main () {
53+
/// # test_health_system();
54+
/// # }
55+
/// struct Health {
56+
/// // Health value between 0.0 and 1.0
57+
/// health_value: f32,
58+
/// }
59+
///
60+
/// fn health_system(time: Res<Time>, mut health: ResMut<Health>) {
61+
/// // Increase health value by 0.1 per second, independent of frame rate,
62+
/// // but not beyond 1.0
63+
/// health.health_value = (health.health_value + 0.1 * time.delta_seconds()).min(1.0);
64+
/// }
65+
///
66+
/// // Mock time in tests
67+
/// fn test_health_system() {
68+
/// let mut world = World::default();
69+
/// let mut time = Time::default();
70+
/// time.update();
71+
/// world.insert_resource(time);
72+
/// world.insert_resource(Health { health_value: 0.2 });
73+
///
74+
/// let mut update_stage = SystemStage::single_threaded();
75+
/// update_stage.add_system(health_system);
76+
///
77+
/// // Simulate that 30 ms have passed
78+
/// let mut time = world.resource_mut::<Time>();
79+
/// let last_update = time.last_update().unwrap();
80+
/// time.update_with_instant(last_update + Duration::from_millis(30));
81+
///
82+
/// // Run system
83+
/// update_stage.run(&mut world);
84+
///
85+
/// // Check that 0.003 has been added to the health value
86+
/// let expected_health_value = 0.2 + 0.1 * 0.03;
87+
/// let actual_health_value = world.resource::<Health>().health_value;
88+
/// assert_eq!(expected_health_value, actual_health_value);
89+
/// }
90+
/// ```
91+
pub fn update_with_instant(&mut self, instant: Instant) {
3792
if let Some(last_update) = self.last_update {
3893
self.delta = instant - last_update;
3994
self.delta_seconds_f64 = self.delta.as_secs_f64();

0 commit comments

Comments
 (0)