@@ -716,4 +716,334 @@ mod tests {
716
716
assert ! ( matches!( result, Err ( ScheduleBuildError :: Ambiguity ( _) ) ) ) ;
717
717
}
718
718
}
719
+
720
+ mod system_ambiguity {
721
+ // Required to make the derive macro behave
722
+ use crate as bevy_ecs;
723
+ use crate :: event:: Events ;
724
+ use crate :: prelude:: * ;
725
+
726
+ #[ derive( Resource ) ]
727
+ struct R ;
728
+
729
+ #[ derive( Component ) ]
730
+ struct A ;
731
+
732
+ #[ derive( Component ) ]
733
+ struct B ;
734
+
735
+ // An event type
736
+ #[ derive( Event ) ]
737
+ struct E ;
738
+
739
+ fn empty_system ( ) { }
740
+ fn res_system ( _res : Res < R > ) { }
741
+ fn resmut_system ( _res : ResMut < R > ) { }
742
+ fn nonsend_system ( _ns : NonSend < R > ) { }
743
+ fn nonsendmut_system ( _ns : NonSendMut < R > ) { }
744
+ fn read_component_system ( _query : Query < & A > ) { }
745
+ fn write_component_system ( _query : Query < & mut A > ) { }
746
+ fn with_filtered_component_system ( _query : Query < & mut A , With < B > > ) { }
747
+ fn without_filtered_component_system ( _query : Query < & mut A , Without < B > > ) { }
748
+ fn event_reader_system ( _reader : EventReader < E > ) { }
749
+ fn event_writer_system ( _writer : EventWriter < E > ) { }
750
+ fn event_resource_system ( _events : ResMut < Events < E > > ) { }
751
+ fn read_world_system ( _world : & World ) { }
752
+ fn write_world_system ( _world : & mut World ) { }
753
+
754
+ // Tests for conflict detection
755
+
756
+ #[ test]
757
+ fn one_of_everything ( ) {
758
+ let mut world = World :: new ( ) ;
759
+ world. insert_resource ( R ) ;
760
+ world. spawn ( A ) ;
761
+ world. init_resource :: < Events < E > > ( ) ;
762
+
763
+ let mut schedule = Schedule :: default ( ) ;
764
+ schedule
765
+ // nonsendmut system deliberately conflicts with resmut system
766
+ . add_systems ( ( resmut_system, write_component_system, event_writer_system) ) ;
767
+
768
+ let _ = schedule. initialize ( & mut world) ;
769
+
770
+ assert_eq ! ( schedule. graph( ) . conflicting_systems( ) . len( ) , 0 ) ;
771
+ }
772
+
773
+ #[ test]
774
+ fn read_only ( ) {
775
+ let mut world = World :: new ( ) ;
776
+ world. insert_resource ( R ) ;
777
+ world. spawn ( A ) ;
778
+ world. init_resource :: < Events < E > > ( ) ;
779
+
780
+ let mut schedule = Schedule :: default ( ) ;
781
+ schedule. add_systems ( (
782
+ empty_system,
783
+ empty_system,
784
+ res_system,
785
+ res_system,
786
+ nonsend_system,
787
+ nonsend_system,
788
+ read_component_system,
789
+ read_component_system,
790
+ event_reader_system,
791
+ event_reader_system,
792
+ read_world_system,
793
+ read_world_system,
794
+ ) ) ;
795
+
796
+ let _ = schedule. initialize ( & mut world) ;
797
+
798
+ assert_eq ! ( schedule. graph( ) . conflicting_systems( ) . len( ) , 0 ) ;
799
+ }
800
+
801
+ #[ test]
802
+ fn read_world ( ) {
803
+ let mut world = World :: new ( ) ;
804
+ world. insert_resource ( R ) ;
805
+ world. spawn ( A ) ;
806
+ world. init_resource :: < Events < E > > ( ) ;
807
+
808
+ let mut schedule = Schedule :: default ( ) ;
809
+ schedule. add_systems ( (
810
+ resmut_system,
811
+ write_component_system,
812
+ event_writer_system,
813
+ read_world_system,
814
+ ) ) ;
815
+
816
+ let _ = schedule. initialize ( & mut world) ;
817
+
818
+ assert_eq ! ( schedule. graph( ) . conflicting_systems( ) . len( ) , 3 ) ;
819
+ }
820
+
821
+ #[ test]
822
+ fn resources ( ) {
823
+ let mut world = World :: new ( ) ;
824
+ world. insert_resource ( R ) ;
825
+
826
+ let mut schedule = Schedule :: default ( ) ;
827
+ schedule. add_systems ( ( resmut_system, res_system) ) ;
828
+
829
+ let _ = schedule. initialize ( & mut world) ;
830
+
831
+ assert_eq ! ( schedule. graph( ) . conflicting_systems( ) . len( ) , 1 ) ;
832
+ }
833
+
834
+ #[ test]
835
+ fn nonsend ( ) {
836
+ let mut world = World :: new ( ) ;
837
+ world. insert_resource ( R ) ;
838
+
839
+ let mut schedule = Schedule :: default ( ) ;
840
+ schedule. add_systems ( ( nonsendmut_system, nonsend_system) ) ;
841
+
842
+ let _ = schedule. initialize ( & mut world) ;
843
+
844
+ assert_eq ! ( schedule. graph( ) . conflicting_systems( ) . len( ) , 1 ) ;
845
+ }
846
+
847
+ #[ test]
848
+ fn components ( ) {
849
+ let mut world = World :: new ( ) ;
850
+ world. spawn ( A ) ;
851
+
852
+ let mut schedule = Schedule :: default ( ) ;
853
+ schedule. add_systems ( ( read_component_system, write_component_system) ) ;
854
+
855
+ let _ = schedule. initialize ( & mut world) ;
856
+
857
+ assert_eq ! ( schedule. graph( ) . conflicting_systems( ) . len( ) , 1 ) ;
858
+ }
859
+
860
+ #[ test]
861
+ #[ ignore = "Known failing but fix is non-trivial: https://github.com/bevyengine/bevy/issues/4381" ]
862
+ fn filtered_components ( ) {
863
+ let mut world = World :: new ( ) ;
864
+ world. spawn ( A ) ;
865
+
866
+ let mut schedule = Schedule :: default ( ) ;
867
+ schedule. add_systems ( (
868
+ with_filtered_component_system,
869
+ without_filtered_component_system,
870
+ ) ) ;
871
+
872
+ let _ = schedule. initialize ( & mut world) ;
873
+
874
+ assert_eq ! ( schedule. graph( ) . conflicting_systems( ) . len( ) , 0 ) ;
875
+ }
876
+
877
+ #[ test]
878
+ fn events ( ) {
879
+ let mut world = World :: new ( ) ;
880
+ world. init_resource :: < Events < E > > ( ) ;
881
+
882
+ let mut schedule = Schedule :: default ( ) ;
883
+ schedule. add_systems ( (
884
+ // All of these systems clash
885
+ event_reader_system,
886
+ event_writer_system,
887
+ event_resource_system,
888
+ ) ) ;
889
+
890
+ let _ = schedule. initialize ( & mut world) ;
891
+
892
+ assert_eq ! ( schedule. graph( ) . conflicting_systems( ) . len( ) , 3 ) ;
893
+ }
894
+
895
+ #[ test]
896
+ fn exclusive ( ) {
897
+ let mut world = World :: new ( ) ;
898
+ world. insert_resource ( R ) ;
899
+ world. spawn ( A ) ;
900
+ world. init_resource :: < Events < E > > ( ) ;
901
+
902
+ let mut schedule = Schedule :: default ( ) ;
903
+ schedule. add_systems ( (
904
+ // All 3 of these conflict with each other
905
+ write_world_system,
906
+ write_world_system,
907
+ res_system,
908
+ ) ) ;
909
+
910
+ let _ = schedule. initialize ( & mut world) ;
911
+
912
+ assert_eq ! ( schedule. graph( ) . conflicting_systems( ) . len( ) , 3 ) ;
913
+ }
914
+
915
+ // Tests for silencing and resolving ambiguities
916
+ #[ test]
917
+ fn before_and_after ( ) {
918
+ let mut world = World :: new ( ) ;
919
+ world. init_resource :: < Events < E > > ( ) ;
920
+
921
+ let mut schedule = Schedule :: default ( ) ;
922
+ schedule. add_systems ( (
923
+ event_reader_system. before ( event_writer_system) ,
924
+ event_writer_system,
925
+ event_resource_system. after ( event_writer_system) ,
926
+ ) ) ;
927
+
928
+ let _ = schedule. initialize ( & mut world) ;
929
+
930
+ assert_eq ! ( schedule. graph( ) . conflicting_systems( ) . len( ) , 0 ) ;
931
+ }
932
+
933
+ #[ test]
934
+ fn ignore_all_ambiguities ( ) {
935
+ let mut world = World :: new ( ) ;
936
+ world. insert_resource ( R ) ;
937
+
938
+ let mut schedule = Schedule :: default ( ) ;
939
+ schedule. add_systems ( (
940
+ resmut_system. ambiguous_with_all ( ) ,
941
+ res_system,
942
+ nonsend_system,
943
+ ) ) ;
944
+
945
+ let _ = schedule. initialize ( & mut world) ;
946
+
947
+ assert_eq ! ( schedule. graph( ) . conflicting_systems( ) . len( ) , 0 ) ;
948
+ }
949
+
950
+ #[ test]
951
+ fn ambiguous_with_label ( ) {
952
+ let mut world = World :: new ( ) ;
953
+ world. insert_resource ( R ) ;
954
+
955
+ #[ derive( SystemSet , Hash , PartialEq , Eq , Debug , Clone ) ]
956
+ struct IgnoreMe ;
957
+
958
+ let mut schedule = Schedule :: default ( ) ;
959
+ schedule. add_systems ( (
960
+ resmut_system. ambiguous_with ( IgnoreMe ) ,
961
+ res_system. in_set ( IgnoreMe ) ,
962
+ nonsend_system. in_set ( IgnoreMe ) ,
963
+ ) ) ;
964
+
965
+ let _ = schedule. initialize ( & mut world) ;
966
+
967
+ assert_eq ! ( schedule. graph( ) . conflicting_systems( ) . len( ) , 0 ) ;
968
+ }
969
+
970
+ #[ test]
971
+ fn ambiguous_with_system ( ) {
972
+ let mut world = World :: new ( ) ;
973
+
974
+ let mut schedule = Schedule :: default ( ) ;
975
+ schedule. add_systems ( (
976
+ write_component_system. ambiguous_with ( read_component_system) ,
977
+ read_component_system,
978
+ ) ) ;
979
+ let _ = schedule. initialize ( & mut world) ;
980
+
981
+ assert_eq ! ( schedule. graph( ) . conflicting_systems( ) . len( ) , 0 ) ;
982
+ }
983
+
984
+ // Tests that the correct ambiguities were reported in the correct order.
985
+ #[ test]
986
+ fn correct_ambiguities ( ) {
987
+ use super :: * ;
988
+
989
+ #[ derive( ScheduleLabel , Hash , PartialEq , Eq , Debug , Clone ) ]
990
+ struct TestSchedule ;
991
+
992
+ fn system_a ( _res : ResMut < R > ) { }
993
+ fn system_b ( _res : ResMut < R > ) { }
994
+ fn system_c ( _res : ResMut < R > ) { }
995
+ fn system_d ( _res : ResMut < R > ) { }
996
+ fn system_e ( _res : ResMut < R > ) { }
997
+
998
+ let mut world = World :: new ( ) ;
999
+ world. insert_resource ( R ) ;
1000
+
1001
+ let mut schedule = Schedule :: new ( TestSchedule ) ;
1002
+ schedule. add_systems ( (
1003
+ system_a,
1004
+ system_b,
1005
+ system_c. ambiguous_with_all ( ) ,
1006
+ system_d. ambiguous_with ( system_b) ,
1007
+ system_e. after ( system_a) ,
1008
+ ) ) ;
1009
+
1010
+ schedule. graph_mut ( ) . initialize ( & mut world) ;
1011
+ let _ = schedule
1012
+ . graph_mut ( )
1013
+ . build_schedule ( world. components ( ) , & TestSchedule . dyn_clone ( ) ) ;
1014
+
1015
+ let ambiguities: Vec < _ > = schedule
1016
+ . graph ( )
1017
+ . conflicts_to_string ( world. components ( ) )
1018
+ . collect ( ) ;
1019
+
1020
+ let expected = & [
1021
+ (
1022
+ "system_d" . to_string ( ) ,
1023
+ "system_a" . to_string ( ) ,
1024
+ vec ! [ "bevy_ecs::schedule::tests::system_ambiguity::R" ] ,
1025
+ ) ,
1026
+ (
1027
+ "system_d" . to_string ( ) ,
1028
+ "system_e" . to_string ( ) ,
1029
+ vec ! [ "bevy_ecs::schedule::tests::system_ambiguity::R" ] ,
1030
+ ) ,
1031
+ (
1032
+ "system_b" . to_string ( ) ,
1033
+ "system_a" . to_string ( ) ,
1034
+ vec ! [ "bevy_ecs::schedule::tests::system_ambiguity::R" ] ,
1035
+ ) ,
1036
+ (
1037
+ "system_b" . to_string ( ) ,
1038
+ "system_e" . to_string ( ) ,
1039
+ vec ! [ "bevy_ecs::schedule::tests::system_ambiguity::R" ] ,
1040
+ ) ,
1041
+ ] ;
1042
+
1043
+ // ordering isn't stable so do this
1044
+ for entry in expected {
1045
+ assert ! ( ambiguities. contains( entry) ) ;
1046
+ }
1047
+ }
1048
+ }
719
1049
}
0 commit comments