Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Context - resolves #1 #4

Merged
merged 18 commits into from
Aug 4, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ use std::{

use crate::plan::{Id, Queue};

/// The common callback used by multiple `Context` methods for future events
type Callback = dyn FnOnce(&mut Context);

/// A manager for the state of a discrete-event simulation
///
/// Provides core simulation services including
Expand All @@ -27,8 +30,10 @@ use crate::plan::{Id, Queue};
/// in time and execute the logic in the associated `FnOnce(&mut Context)`
/// closure. Modules can add plans to this queue through the `Context`.
///
/// The simulation also has an immediate callback queue to allow for the
/// accumulation of side effects of mutations by the current controlling module.
/// The simulation also has a separate callback mechanism. Callbacks
/// fire before the next timed event (even if it is scheduled for the
/// current time. This allows modules to schedule actions for immediate
/// execution but outside of the current iteration of the event loop.
///
pub struct Context {
plan_queue: Queue<Box<Callback>>,
Expand Down Expand Up @@ -143,8 +148,6 @@ impl Default for Context {
}
}

type Callback = dyn FnOnce(&mut Context);

/// A trait for objects that can provide data containers to be held by `Context`
pub trait DataPlugin: Any {
type DataContainer;
Expand Down Expand Up @@ -174,6 +177,29 @@ mod tests {

define_data_plugin!(ComponentA, Vec<u32>, vec![]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are really thorough tests. This is approximately how I would do it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I worked hard to get those right.


#[test]
fn empty_context() {
let mut context = Context::new();
context.execute();
assert_eq!(context.get_current_time(), 0.0);
}

#[test]
fn get_data_container() {
let mut context = Context::new();
context.get_data_container_mut::<ComponentA>().push(1);
assert_eq!(
*context.get_data_container::<ComponentA>().unwrap(),
vec![1],
);
}

#[test]
fn get_uninitialized_data_container() {
let context = Context::new();
assert!(context.get_data_container::<ComponentA>().is_none());
}

fn add_plan(context: &mut Context, time: f64, value: u32) -> Id {
context.add_plan(time, move |context| {
context.get_data_container_mut::<ComponentA>().push(value);
Expand Down Expand Up @@ -201,13 +227,6 @@ mod tests {
add_plan(&mut context, f64::NAN, 0);
}

#[test]
fn empty_context() {
let mut context = Context::new();
context.execute();
assert_eq!(context.get_current_time(), 0.0);
}

#[test]
fn timed_plan_only() {
let mut context = Context::new();
Expand Down
27 changes: 25 additions & 2 deletions src/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ use std::{

/// A priority queue that stores arbitrary data sorted by time
///
/// Items of type T are stored in order by `f64` time and called `Plan<T>`.
/// Items of type `T` are stored in order by `f64` time and called `Plan<T>`.
/// When plans are created they are sequentially assigned an `Id` that is a
/// wrapped `u64`. If two plans are scheduled for the same time the plan that is
/// scheduled first (i.e. that has the lowest id) is placed earlier.
/// scheduled first (i.e., that has the lowest id) is placed earlier.
///
/// The pair of time and plan id are stored in a binary heap of `Entry` objects.
/// The data payload of the event is stored in a hash map by plan id.
Expand Down Expand Up @@ -204,6 +204,29 @@ mod tests {
assert!(plan_queue.get_next_plan().is_none());
}

#[test]
fn add_and_get_plans() {
let mut plan_queue = Queue::new();
plan_queue.add_plan(1.0, 1);
plan_queue.add_plan(2.0, 2);

let next_plan = plan_queue.get_next_plan().unwrap();
assert_eq!(next_plan.time, 1.0);
assert_eq!(next_plan.data, 1);

plan_queue.add_plan(3.0, 3);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider adding it with 1.5 to demonstrate that sort works properly.


let next_plan = plan_queue.get_next_plan().unwrap();
assert_eq!(next_plan.time, 2.0);
assert_eq!(next_plan.data, 2);

let next_plan = plan_queue.get_next_plan().unwrap();
assert_eq!(next_plan.time, 3.0);
assert_eq!(next_plan.data, 3);

assert!(plan_queue.get_next_plan().is_none());
}

#[test]
#[should_panic]
fn cancel_invalid_plan() {
Expand Down
Loading