Path: blob/main/crates/bevy_dev_tools/src/schedule_data/serde.rs
30636 views
//! Utilities for serializing schedule data for an [`App`](bevy_app::App).1//!2//! These are mostly around providing types implementing [`Serialize`]/[`Deserialize`] that3//! represent schedule data. In addition, there are tools for extracting this data from the4//! [`World`](bevy_ecs::world::World).56use bevy_ecs::{7component::{ComponentId, Components},8schedule::{9ApplyDeferred, ConditionWithAccess, InternedScheduleLabel, NodeId, Schedule,10ScheduleBuildMetadata, Schedules,11},12system::SystemStateFlags,13};14use bevy_platform::collections::{hash_map::Entry, HashMap};15use serde::{Deserialize, Serialize};16use thiserror::Error;1718/// The data for the entire app's schedule.19#[derive(Serialize, Deserialize, Debug, Clone)]20pub struct AppData {21/// A list of all schedules in the app.22pub schedules: Vec<ScheduleData>,23}2425impl AppData {26/// Creates the data from the underlying [`Schedules`].27///28/// Note: we assume all schedules in `schedules` have been initialized through29/// [`Schedule::initialize`].30pub fn from_schedules(31schedules: &Schedules,32world_components: &Components,33label_to_build_metadata: &HashMap<InternedScheduleLabel, ScheduleBuildMetadata>,34) -> Result<Self, ExtractAppDataError> {35Ok(Self {36schedules: schedules37.iter()38.map(|(_, schedule)| {39ScheduleData::from_schedule(40schedule,41world_components,42label_to_build_metadata.get(&schedule.label()),43)44})45.collect::<Result<_, ExtractAppDataError>>()?,46})47}48}4950/// Data about a particular schedule.51#[derive(Serialize, Deserialize, Debug, Clone)]52pub struct ScheduleData {53/// The name of the schedule.54pub name: String,55/// The systems in this schedule.56pub systems: Vec<SystemData>,57/// The system sets in this schedule.58pub system_sets: Vec<SystemSetData>,59/// A list of relationships indicating that a system/system set is contained in a system set.60///61/// The order is (parent, child).62pub hierarchy: Vec<(SystemSetIndex, ScheduleIndex)>,63/// A list of ordering constraints, ensuring that one system/system set runs before another.64///65/// The order is (first, second).66pub dependency: Vec<(ScheduleIndex, ScheduleIndex)>,67/// The components that these systems access.68pub components: Vec<ComponentData>,69/// A list of conflicts between systems.70pub conflicts: Vec<SystemConflict>,71}7273/// Data about a component type.74#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]75pub struct ComponentData {76/// The name of the component.77pub name: String,78}7980/// Data about a particular system.81#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]82pub struct SystemData {83/// The name of the system.84pub name: String,85/// Whether this system is a sync point (aka [`ApplyDeferred`]).86pub apply_deferred: bool,87/// Whether this system is exclusive.88pub exclusive: bool,89/// Whether this system has deferred buffers to apply.90pub deferred: bool,91// TODO: Store the conditions specific to this system.92}9394/// Data about a particular system set.95#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]96pub struct SystemSetData {97/// The name of the system set.98pub name: String,99/// The conditions applied to this system.100pub conditions: Vec<ConditionData>,101}102103/// Data about a run condition for a system.104#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]105pub struct ConditionData {106/// The name of the condition.107pub name: String,108}109110/// An index of an element in a schedule.111#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]112pub enum ScheduleIndex {113/// The index of a system.114System(u32),115/// The index of a system set.116SystemSet(u32),117}118119/// Data about an access conflict between two systems.120#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]121pub struct SystemConflict {122/// The first system index.123pub system_1: u32,124/// The second system index.125pub system_2: u32,126/// The kind of conflict between these systems.127pub conflicting_access: AccessConflict,128}129130/// Data for describing the kind of access conflict.131#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]132pub enum AccessConflict {133/// There is a conflict on the **whole world**, since one of the systems requires world access134/// and the other needs mutable access to (some of) the world.135World,136/// There is incompatible accesses to the listed components.137Components(Vec<u32>),138}139140/// A newtype for the index of a system set.141///142/// This is the same kind of index as [`ScheduleIndex::SystemSet`], but for cases where we know we143/// can't have a system.144#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]145pub struct SystemSetIndex(pub u32);146147impl ScheduleData {148/// Creates the data from the underlying [`Schedule`].149///150/// Note: we assume `schedule` has already been initialized.151pub fn from_schedule(152schedule: &Schedule,153world_components: &Components,154build_metadata: Option<&ScheduleBuildMetadata>,155) -> Result<Self, ExtractAppDataError> {156let graph = schedule.graph();157158let mut system_key_to_index = HashMap::new();159let mut system_set_key_to_index = HashMap::new();160161fn extract_condition_data(conditions: &[ConditionWithAccess]) -> Vec<ConditionData> {162conditions163.iter()164.map(|condition| ConditionData {165name: format!("{}", condition.condition.name()),166})167.collect()168}169170let systems = schedule171.systems()172.map_err(|_| {173ExtractAppDataError::ScheduleNotInitialized(format!("{:?}", schedule.label()))174})?175.enumerate()176.map(|(index, (key, system))| {177system_key_to_index.insert(key, index);178179let flags = system.flags();180181SystemData {182name: format!("{}", system.name()),183apply_deferred: system.system_type()184== core::any::TypeId::of::<ApplyDeferred>(),185exclusive: flags.contains(SystemStateFlags::EXCLUSIVE),186deferred: flags.contains(SystemStateFlags::DEFERRED),187}188})189.collect();190191let system_sets = graph192.system_sets193.iter()194.enumerate()195.map(|(index, (key, system_set, conditions))| {196system_set_key_to_index.insert(key, index);197198SystemSetData {199name: format!("{:?}", system_set),200conditions: extract_condition_data(conditions),201}202})203.collect();204205let node_id_to_schedule_index = |node_id: NodeId| match node_id {206NodeId::System(key) => ScheduleIndex::System(207*system_key_to_index208.get(&key)209.expect("the system this key refers to should have already been seen")210as _,211),212NodeId::Set(key) => ScheduleIndex::SystemSet(213*system_set_key_to_index214.get(&key)215.expect("the system set this key refers to should have already been seen")216as _,217),218};219220let hierarchy = graph221.hierarchy()222.graph()223.all_edges()224.map(|(parent, child)| {225let parent = system_set_key_to_index226.get(227&parent228.as_set()229.expect("the parent of a system/set is always a set"),230)231.expect("the system set this key refers to should have already been seen");232let child = node_id_to_schedule_index(child);233234(SystemSetIndex(*parent as _), child)235})236.collect();237238let mut dependency = graph239.dependency()240.graph()241.all_edges()242.map(|(a, b)| (node_id_to_schedule_index(a), node_id_to_schedule_index(b)))243.collect::<Vec<_>>();244245if let Some(build_metadata) = build_metadata {246// Add in all the edges that were created by build passes.247dependency.extend(248build_metadata249.edges_added_by_build_passes250.iter()251.map(|(a, b)| {252(253node_id_to_schedule_index(NodeId::System(*a)),254node_id_to_schedule_index(NodeId::System(*b)),255)256}),257);258}259260let mut component_id_to_index = HashMap::<ComponentId, usize>::new();261let mut components = vec![];262263let conflicts = graph264.conflicting_systems()265.iter()266.map(|(system_1, system_2, conflicts)| {267let system_1 = system_key_to_index268.get(system_1)269.expect("the system this key refers to should have already been seen");270let system_2 = system_key_to_index271.get(system_2)272.expect("the system this key refers to should have already been seen");273274SystemConflict {275system_1: *system_1 as _,276system_2: *system_2 as _,277conflicting_access: if conflicts.is_empty() {278// The systems conflict on the world if there's no particular component IDs.279AccessConflict::World280} else {281AccessConflict::Components(282conflicts283.iter()284.map(|id| match component_id_to_index.entry(*id) {285Entry::Occupied(entry) => *entry.get() as _,286Entry::Vacant(entry) => {287let component = world_components.get_info(*id).expect(288"the component has already been registered by the system",289);290291components.push(ComponentData {292name: format!("{}", component.name()),293});294*entry.insert(components.len() - 1) as _295}296})297.collect(),298)299},300}301})302.collect();303304Ok(Self {305name: format!("{:?}", schedule.label()),306components,307systems,308system_sets,309hierarchy,310dependency,311conflicts,312})313}314}315316/// An error occurring while attempting to extract schedule data from an app.317#[derive(Error, Debug)]318pub enum ExtractAppDataError {319/// A schedule has not been initialized through [`Schedule::initialize`].320#[error("executable schedule has not been created for label \"{0}\"")]321ScheduleNotInitialized(String),322}323324#[cfg(test)]325/// Tests for extracted schedule data.326///327/// This is public to allow other test modules in this crate to use its utilities.328pub mod tests {329use bevy_app::{App, Update};330use bevy_ecs::{331component::Component,332query::{With, Without},333schedule::{IntoScheduleConfigs, Schedules, SystemSet},334system::{Commands, Query},335};336use bevy_platform::collections::HashMap;337338use crate::schedule_data::serde::{339AccessConflict, AppData, ComponentData, ExtractAppDataError, ScheduleData, ScheduleIndex,340SystemConflict, SystemData, SystemSetData, SystemSetIndex,341};342343fn app_data_from_app(app: &mut App) -> Result<AppData, ExtractAppDataError> {344let schedules = app.world_mut().resource::<Schedules>();345// TODO: This is a pain. It would be nice to be able to just hokey-pokey the whole346// `Schedules` resource, but initializing a schedule writes to `Schedules`. Also we need to347// use interned labels since `Box<dyn ScheduleLabel>` doesn't impl `ScheduleLabel`!348let interned_labels = schedules349.iter()350.map(|(_, schedule)| schedule.label())351.collect::<Vec<_>>();352353let mut label_to_build_metadata = HashMap::new();354355for label in interned_labels {356let build_metadata = app357.world_mut()358.schedule_scope(label, |world, schedule| schedule.initialize(world))359.unwrap()360.unwrap();361label_to_build_metadata.insert(label, build_metadata);362}363364let mut app_data = AppData::from_schedules(365app.world().resource::<Schedules>(),366app.world().components(),367&label_to_build_metadata,368)?;369370remove_module_paths(&mut app_data);371sort_app_data(&mut app_data);372Ok(app_data)373}374375/// Removes the module paths from all items in the [`AppData`], so that moving tests around376/// doesn't change the output.377pub fn remove_module_paths(app_data: &mut AppData) {378for schedule in app_data.schedules.iter_mut() {379for system in schedule.systems.iter_mut() {380system.name = system.name.rsplit_once(":").unwrap().1.to_string();381}382for set in schedule.system_sets.iter_mut() {383let name_modless = set384.name385.rsplit_once(":")386.map(|(_, suffix)| suffix)387.unwrap_or(set.name.as_str())388.to_string();389if set.name.starts_with("SystemTypeSet") {390// This is a set corresponding to a system. Make sure to keep the391// `SystemTypeSet` prefix.392set.name = format!("SystemTypeSet:{name_modless}");393} else {394set.name = name_modless;395}396}397for component in schedule.components.iter_mut() {398component.name = component.name.rsplit_once(":").unwrap().1.to_string();399}400}401}402403/// Sorts the [`AppData`] so we have a deterministic order when asserting.404// Note: we could do this when extracting unconditionally (even in prod), but there's not much405// point since schedule order is not guaranteed to be deterministic anyway. So relying on the406// same order seems weird.407pub fn sort_app_data(app_data: &mut AppData) {408// Sort schedules by name.409app_data410.schedules411.sort_by_key(|schedule| schedule.name.clone());412// Sort each schedule.413app_data.schedules.iter_mut().for_each(sort_schedule);414415/// Sorts a schedule so that systems, system sets, conditions, and components are in name416/// order, and other structures are in index order.417fn sort_schedule(schedule: &mut ScheduleData) {418/// Sorts the slice using `key_fn` and returns a mapping, which maps the original index419/// to the new index.420fn reorder_slice<T, K: Ord>(421slice: &mut [T],422key_fn: impl Fn(&T) -> K,423) -> HashMap<usize, usize> {424let mut mapping = (0..slice.len()).collect::<Vec<_>>();425// We assume the two sorts produce the same thing which should be true since we are426// using a stable sort.427mapping.sort_by_key(|index| key_fn(&slice[*index]));428slice.sort_by_key(key_fn);429430mapping431.into_iter()432// Enumerating produces the new indices.433.enumerate()434// Flip the order of indices so that we go from old to new.435.map(|(new, old)| (old, new))436.collect()437}438439let system_old_index_to_new_index =440reorder_slice(&mut schedule.systems, |system| system.name.clone());441let system_set_old_index_to_new_index =442reorder_slice(&mut schedule.system_sets, |set| set.name.clone());443let component_old_index_to_new_index =444reorder_slice(&mut schedule.components, |component| component.name.clone());445446let reindex_system = |index: &mut u32| {447*index = *system_old_index_to_new_index448.get(&(*index as usize))449.unwrap() as u32;450};451let reindex_system_set = |index: &mut u32| {452*index = *system_set_old_index_to_new_index453.get(&(*index as usize))454.unwrap() as u32;455};456let reindex_schedule_index = |index: &mut ScheduleIndex| match index {457ScheduleIndex::System(system) => reindex_system(system),458ScheduleIndex::SystemSet(set) => reindex_system_set(set),459};460461let reindex_component = |index: &mut u32| {462*index = *component_old_index_to_new_index463.get(&(*index as usize))464.unwrap() as u32;465};466467// Sort the conditions in a system set.468for set in schedule.system_sets.iter_mut() {469set.conditions470.sort_by_key(|condition| condition.name.clone());471}472473// Reindex the hierarchy, and sort it.474for (parent, child) in schedule.hierarchy.iter_mut() {475reindex_system_set(&mut parent.0);476reindex_schedule_index(child);477}478schedule.hierarchy.sort();479480// Reindex the dependencies, and sort it.481for (parent, child) in schedule.dependency.iter_mut() {482reindex_schedule_index(parent);483reindex_schedule_index(child);484}485schedule.dependency.sort();486487// Reindex the conflicts.488for conflict in schedule.conflicts.iter_mut() {489reindex_system(&mut conflict.system_1);490reindex_system(&mut conflict.system_2);491492// The order of the indices don't matter, so pick the ordering such that `system_1 <493// system_2`.494if conflict.system_1 > conflict.system_2 {495core::mem::swap(&mut conflict.system_1, &mut conflict.system_2);496}497498match &mut conflict.conflicting_access {499AccessConflict::World => {}500AccessConflict::Components(components) => {501components.iter_mut().for_each(reindex_component);502components.sort();503}504};505}506schedule507.conflicts508.sort_by_key(|conflict| (conflict.system_1, conflict.system_2));509}510}511512/// Convenience to create a [`SystemData`] for the common case of no flags set.513pub fn simple_system(name: &str) -> SystemData {514SystemData {515name: name.into(),516apply_deferred: false,517exclusive: false,518deferred: false,519}520}521522/// Convenience to create a [`SystemSetData`] for the common case of being empty.523pub fn simple_system_set(name: &str) -> SystemSetData {524SystemSetData {525name: name.into(),526conditions: vec![],527}528}529530/// Convenience to create a [`ComponentData`] to make test cases shorter.531pub fn simple_component(name: &str) -> ComponentData {532ComponentData { name: name.into() }533}534535/// Convenience to create a [`SystemConflict`] to make test cases shorter.536pub fn conflict(537system_1: u32,538system_2: u32,539conflicting_access: AccessConflict,540) -> SystemConflict {541SystemConflict {542system_1,543system_2,544conflicting_access,545}546}547548/// A convenience system set that is generic allowing us to make many of these quickly.549#[derive(SystemSet, Hash, PartialEq, Eq, Clone)]550struct MySet<const NUM: u32>;551552impl<const NUM: u32> core::fmt::Debug for MySet<NUM> {553fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {554write!(f, "MySet<{NUM}>")555}556}557558/// A convenience component that is generic allowing us to make many of these quickly.559#[derive(Component)]560struct MyComponent<const NUM: u32>;561562#[test]563fn linear() {564let mut app = App::empty();565566fn a() {}567fn b() {}568fn c() {}569570app.add_systems(Update, (a, b, c).chain());571572let data = app_data_from_app(&mut app).unwrap();573assert_eq!(data.schedules.len(), 1);574let schedule = &data.schedules[0];575assert_eq!(schedule.name, "Update");576assert_eq!(577schedule.systems,578[simple_system("a"), simple_system("b"), simple_system("c"),]579);580// Each system is also a system set.581assert_eq!(582schedule.system_sets,583[584simple_system_set("SystemTypeSet:a"),585simple_system_set("SystemTypeSet:b"),586simple_system_set("SystemTypeSet:c"),587]588);589// Every system is in its own system set.590assert_eq!(591schedule.hierarchy,592[593(SystemSetIndex(0), ScheduleIndex::System(0)),594(SystemSetIndex(1), ScheduleIndex::System(1)),595(SystemSetIndex(2), ScheduleIndex::System(2)),596]597);598// There are 2 dependency edges to connect a-b and b-c.599assert_eq!(600schedule.dependency,601[602(ScheduleIndex::System(0), ScheduleIndex::System(1)),603(ScheduleIndex::System(1), ScheduleIndex::System(2)),604]605);606assert_eq!(schedule.components.len(), 0);607assert_eq!(schedule.conflicts.len(), 0);608}609610#[test]611fn linear_with_system_sets() {612let mut app = App::empty();613614app.configure_sets(Update, (MySet::<0>, MySet::<1>, MySet::<2>).chain());615616let data = app_data_from_app(&mut app).unwrap();617assert_eq!(data.schedules.len(), 1);618let schedule = &data.schedules[0];619assert_eq!(schedule.name, "Update");620assert_eq!(schedule.systems, []);621assert_eq!(622schedule.system_sets,623[624simple_system_set("MySet<0>"),625simple_system_set("MySet<1>"),626simple_system_set("MySet<2>"),627]628);629assert_eq!(schedule.hierarchy, []);630// There are 2 dependency edges to connect 0-1 and 1-2.631assert_eq!(632schedule.dependency,633[634(ScheduleIndex::SystemSet(0), ScheduleIndex::SystemSet(1)),635(ScheduleIndex::SystemSet(1), ScheduleIndex::SystemSet(2)),636]637);638assert_eq!(schedule.components.len(), 0);639assert_eq!(schedule.conflicts.len(), 0);640}641642#[test]643fn stack_of_system_sets() {644let mut app = App::empty();645646fn a() {}647648app.add_systems(Update, a.in_set(MySet::<0>))649.configure_sets(Update, MySet::<0>.in_set(MySet::<1>))650.configure_sets(Update, MySet::<1>.in_set(MySet::<2>));651652let data = app_data_from_app(&mut app).unwrap();653assert_eq!(data.schedules.len(), 1);654let schedule = &data.schedules[0];655assert_eq!(schedule.name, "Update");656assert_eq!(schedule.systems, [simple_system("a")]);657assert_eq!(658schedule.system_sets,659[660simple_system_set("MySet<0>"),661simple_system_set("MySet<1>"),662simple_system_set("MySet<2>"),663simple_system_set("SystemTypeSet:a"),664]665);666assert_eq!(667schedule.hierarchy,668[669(SystemSetIndex(0), ScheduleIndex::System(0)),670(SystemSetIndex(1), ScheduleIndex::SystemSet(0)),671(SystemSetIndex(2), ScheduleIndex::SystemSet(1)),672(SystemSetIndex(3), ScheduleIndex::System(0)),673]674);675assert_eq!(schedule.dependency, []);676assert_eq!(schedule.components.len(), 0);677assert_eq!(schedule.conflicts.len(), 0);678}679680#[test]681fn records_system_kind_flags() {682let mut app = App::empty();683684fn a0(_commands: Commands) {}685fn a1(_commands: Commands) {}686fn b0() {}687fn b1() {}688689fn c0() {}690fn c1() {}691692app.add_systems(Update, (((a0, a1), (b0, b1)).chain(), (c0, c1).chain()));693694let data = app_data_from_app(&mut app).unwrap();695assert_eq!(data.schedules.len(), 1);696let schedule = &data.schedules[0];697assert_eq!(schedule.name, "Update");698assert_eq!(699schedule.systems,700[701SystemData {702name: "a0".into(),703apply_deferred: false,704exclusive: false,705deferred: true,706},707SystemData {708name: "a1".into(),709apply_deferred: false,710exclusive: false,711deferred: true,712},713SystemData {714name: "apply_deferred".into(),715apply_deferred: true,716exclusive: true,717deferred: false,718},719simple_system("b0"),720simple_system("b1"),721simple_system("c0"),722simple_system("c1"),723]724);725assert_eq!(726schedule.system_sets,727[728simple_system_set("SystemTypeSet:a0"),729simple_system_set("SystemTypeSet:a1"),730simple_system_set("SystemTypeSet:b0"),731simple_system_set("SystemTypeSet:b1"),732simple_system_set("SystemTypeSet:c0"),733simple_system_set("SystemTypeSet:c1"),734]735);736assert_eq!(737schedule.hierarchy,738[739(SystemSetIndex(0), ScheduleIndex::System(0)),740(SystemSetIndex(1), ScheduleIndex::System(1)),741(SystemSetIndex(2), ScheduleIndex::System(3)),742(SystemSetIndex(3), ScheduleIndex::System(4)),743(SystemSetIndex(4), ScheduleIndex::System(5)),744(SystemSetIndex(5), ScheduleIndex::System(6)),745]746);747assert_eq!(748schedule.dependency,749[750// a->sync and a->b751(ScheduleIndex::System(0), ScheduleIndex::System(2)),752(ScheduleIndex::System(0), ScheduleIndex::System(3)),753(ScheduleIndex::System(0), ScheduleIndex::System(4)),754(ScheduleIndex::System(1), ScheduleIndex::System(2)),755(ScheduleIndex::System(1), ScheduleIndex::System(3)),756(ScheduleIndex::System(1), ScheduleIndex::System(4)),757// sync->b758(ScheduleIndex::System(2), ScheduleIndex::System(3)),759(ScheduleIndex::System(2), ScheduleIndex::System(4)),760// c0->c1761(ScheduleIndex::System(5), ScheduleIndex::System(6)),762]763);764assert_eq!(schedule.components.len(), 0);765assert_eq!(schedule.conflicts.len(), 0);766}767768#[test]769fn records_conflicts() {770let mut app = App::empty();771772// These two systems don't conflict.773fn a0(_: Query<&MyComponent<0>>) {}774fn a1(_: Query<&MyComponent<0>>) {}775776// These two systems conflict on one component.777fn b0(_: Query<&MyComponent<1>>) {}778fn b1(_: Query<&mut MyComponent<1>>) {}779780// These two systems conflict on two components.781fn c0(782_: Query<(783&MyComponent<2>,784&mut MyComponent<3>,785&MyComponent<4>,786&MyComponent<5>,787)>,788) {789}790fn c1(791_: Query<(792&mut MyComponent<2>,793&MyComponent<3>,794&MyComponent<4>,795&MyComponent<6>,796)>,797) {798}799800// These two systems use With/Without to avoid a conflict.801fn d0(_: Query<&mut MyComponent<7>, With<MyComponent<8>>>) {}802fn d1(_: Query<&mut MyComponent<7>, Without<MyComponent<8>>>) {}803804// These two systems use an ordering to avoid a conflict.805fn e0(_: Query<&mut MyComponent<9>>) {}806fn e1(_: Query<&mut MyComponent<9>>) {}807808app.add_systems(Update, (a0, a1, b0, b1, c0, c1, d0, d1, (e0, e1).chain()));809810let data = app_data_from_app(&mut app).unwrap();811assert_eq!(data.schedules.len(), 1);812let schedule = &data.schedules[0];813assert_eq!(schedule.name, "Update");814assert_eq!(815schedule.systems,816[817simple_system("a0"),818simple_system("a1"),819simple_system("b0"),820simple_system("b1"),821simple_system("c0"),822simple_system("c1"),823simple_system("d0"),824simple_system("d1"),825simple_system("e0"),826simple_system("e1"),827]828);829assert_eq!(830schedule.system_sets,831[832simple_system_set("SystemTypeSet:a0"),833simple_system_set("SystemTypeSet:a1"),834simple_system_set("SystemTypeSet:b0"),835simple_system_set("SystemTypeSet:b1"),836simple_system_set("SystemTypeSet:c0"),837simple_system_set("SystemTypeSet:c1"),838simple_system_set("SystemTypeSet:d0"),839simple_system_set("SystemTypeSet:d1"),840simple_system_set("SystemTypeSet:e0"),841simple_system_set("SystemTypeSet:e1"),842]843);844assert_eq!(845schedule.hierarchy,846[847(SystemSetIndex(0), ScheduleIndex::System(0)),848(SystemSetIndex(1), ScheduleIndex::System(1)),849(SystemSetIndex(2), ScheduleIndex::System(2)),850(SystemSetIndex(3), ScheduleIndex::System(3)),851(SystemSetIndex(4), ScheduleIndex::System(4)),852(SystemSetIndex(5), ScheduleIndex::System(5)),853(SystemSetIndex(6), ScheduleIndex::System(6)),854(SystemSetIndex(7), ScheduleIndex::System(7)),855(SystemSetIndex(8), ScheduleIndex::System(8)),856(SystemSetIndex(9), ScheduleIndex::System(9)),857]858);859assert_eq!(860schedule.dependency,861[862// e0 -> e1863(ScheduleIndex::System(8), ScheduleIndex::System(9)),864]865);866assert_eq!(867schedule.components,868[869simple_component("MyComponent<1>"),870simple_component("MyComponent<2>"),871simple_component("MyComponent<3>"),872]873);874assert_eq!(875schedule.conflicts,876[877conflict(2, 3, AccessConflict::Components(vec![0])),878conflict(4, 5, AccessConflict::Components(vec![1, 2]))879]880);881}882}883884885