mod components;
mod cons;
mod manager;
mod runner;
mod user;
mod writer;
pub use components::{
FinalTag, MultiEdge, OnCompletion, SingleEdge, TaskComponent, TaskProgress,
};
pub use cons::{Cons, TaskFactory, TaskGraph};
pub use user::TaskUser;
pub use manager::TaskManagerSystem;
pub use runner::TaskRunnerSystem;
pub use writer::TaskWriter;
use specs::prelude::*;
#[derive(SystemData)]
pub struct TaskData<'a, P, S, M, F> {
entities: Entities<'a>,
lazy: Read<'a, LazyUpdate>,
progress: P,
single_edges: S,
multi_edges: M,
final_tags: F,
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct AlreadyComplete {
was_run: bool,
}
impl Component for AlreadyComplete {
type Storage = VecStorage<Self>;
}
impl<'a> TaskComponent<'a> for AlreadyComplete {
type Data = ();
fn run(&mut self, _data: &mut Self::Data) -> bool {
self.was_run = true;
true
}
}
#[derive(Clone)]
struct WriteValue {
value: usize,
}
impl Component for WriteValue {
type Storage = VecStorage<Self>;
}
impl<'a> TaskComponent<'a> for WriteValue {
type Data = Write<'a, usize>;
fn run(&mut self, data: &mut Self::Data) -> bool {
**data = self.value;
true
}
}
fn set_up<'a, 'b>() -> (World, Dispatcher<'a, 'b>) {
let mut world = World::new();
let mut dispatcher = DispatcherBuilder::new()
.with(
TaskRunnerSystem::<AlreadyComplete>::default(),
"already_complete",
&[],
)
.with(
TaskRunnerSystem::<WriteValue>::default(),
"write_value",
&[],
)
.with(
TaskManagerSystem,
"task_manager",
&["already_complete", "write_value"],
)
.build();
dispatcher.setup(&mut world);
(world, dispatcher)
}
enum MakeSingleTask {
Finalize(OnCompletion),
DontFinalize,
}
fn make_single_task<'a, T: TaskComponent<'a> + Send + Sync>(
world: &mut World,
task: T,
option: MakeSingleTask,
) -> Entity {
let entity = world.exec(|user: TaskUser| {
let task = user.make_task_lazy(task);
if let MakeSingleTask::Finalize(on_completion) = option {
user.finalize_lazy(task, on_completion);
}
task
});
world.maintain();
entity
}
fn entity_is_complete(world: &mut World, entity: Entity) -> bool {
world.exec(|user: TaskUser| user.entity_is_complete(entity))
}
#[test]
fn single_task_not_run_until_finalized() {
let (mut world, mut dispatcher) = set_up();
let task = make_single_task(
&mut world,
AlreadyComplete::default(),
MakeSingleTask::DontFinalize,
);
dispatcher.dispatch(&world);
dispatcher.dispatch(&world);
assert_eq!(
world.read_storage::<AlreadyComplete>().get(task),
Some(&AlreadyComplete { was_run: false })
);
world.exec(|user: TaskUser| user.finalize_lazy(task, OnCompletion::None));
world.maintain();
dispatcher.dispatch(&world);
dispatcher.dispatch(&world);
world.maintain();
assert!(entity_is_complete(&mut world, task));
assert_eq!(
world.read_storage::<AlreadyComplete>().get(task),
Some(&AlreadyComplete { was_run: true }),
);
}
#[test]
fn single_task_deleted_on_completion() {
let (mut world, mut dispatcher) = set_up();
let task = make_single_task(
&mut world,
AlreadyComplete::default(),
MakeSingleTask::Finalize(OnCompletion::Delete),
);
dispatcher.dispatch(&world);
dispatcher.dispatch(&world);
world.maintain();
assert_eq!(world.entities().is_alive(task), false);
}
#[test]
fn joined_tasks_run_in_order_and_deleted_on_completion() {
let (mut world, mut dispatcher) = set_up();
let root = world.exec(|user: TaskUser| {
let task_graph: TaskGraph = seq!(
@WriteValue { value: 1 },
@WriteValue { value: 2 },
@WriteValue { value: 3 }
);
task_graph.assemble(OnCompletion::Delete, &user)
});
world.maintain();
dispatcher.dispatch(&world);
dispatcher.dispatch(&world);
assert_eq!(*world.fetch::<usize>(), 1);
dispatcher.dispatch(&world);
assert_eq!(*world.fetch::<usize>(), 2);
dispatcher.dispatch(&world);
assert_eq!(*world.fetch::<usize>(), 3);
world.maintain();
assert_eq!(world.entities().is_alive(root.unwrap()), false);
}
#[test]
fn all_prongs_of_fork_run_before_join_and_deleted_on_completion() {
let (mut world, mut dispatcher) = set_up();
let root = world.exec(|user: TaskUser| {
let task_graph: TaskGraph = seq!(
@WriteValue { value: 1 },
fork!(
@WriteValue { value: 2 },
@WriteValue { value: 3 }
),
@WriteValue { value: 4 }
);
task_graph.assemble(OnCompletion::Delete, &user)
});
world.maintain();
dispatcher.dispatch(&world);
dispatcher.dispatch(&world);
assert_eq!(*world.fetch::<usize>(), 1);
dispatcher.dispatch(&world);
let cur_value = *world.fetch::<usize>();
assert!(cur_value == 2 || cur_value == 3);
dispatcher.dispatch(&world);
assert_eq!(*world.fetch::<usize>(), 4);
world.maintain();
assert_eq!(world.entities().is_alive(root.unwrap()), false);
}
}