@@ -1365,73 +1365,246 @@ mod tests {
13651365 }
13661366
13671367 #[ test]
1368- fn run_fallible_condition ( ) {
1368+ fn combinators_with_maybe_failing_condition ( ) {
1369+ use crate :: system:: RunSystemOnce ;
13691370 use alloc:: sync:: Arc ;
13701371 use core:: sync:: atomic:: { AtomicUsize , Ordering } ;
13711372
1373+ // Things that should be tested:
1374+ // - the final result of the combinator is correct
1375+ // - the systems that are expected to run do run
1376+ // - the systems that are expected to not run do not run
1377+
13721378 #[ derive( Component ) ]
13731379 struct Vacant ;
13741380
1381+ // SystemConditions don't have mutable access to the world, so we use a
1382+ // `Res<AtomicCounter>` to count invocations.
13751383 #[ derive( Resource , Default ) ]
13761384 struct AtomicCounter ( Arc < AtomicUsize > ) ;
13771385
1378- fn is_true ( ) -> bool {
1379- true
1380- }
1386+ // The following constants are used to represent a system having run.
1387+ // both are prime so that when multiplied they give a unique value for any TRUE^n*FALSE^m
1388+ const FALSE : usize = 2 ;
1389+ const TRUE : usize = 3 ;
13811390
1391+ // this is a system, but has the same side effect as `test_true`
13821392 fn is_true_inc ( counter : Res < AtomicCounter > ) -> bool {
1383- counter. 0 . fetch_add ( 1 , Ordering :: SeqCst ) ;
1384- true
1393+ test_true ( & counter)
13851394 }
13861395
1387- fn noop ( ) { }
1396+ // this is a system, but has the same side effect as `test_false`
1397+ fn is_false_inc ( counter : Res < AtomicCounter > ) -> bool {
1398+ test_false ( & counter)
1399+ }
13881400
1389- // This condition will always return true false, because `Vacant` is never present.
1401+ // This condition will always yield ` false` , because `Vacant` is never present.
13901402 fn vacant ( _: crate :: system:: Single < & Vacant > ) -> bool {
13911403 true
13921404 }
13931405
1394- let mut world = World :: new ( ) ;
1395- world. init_resource :: < Counter > ( ) ;
1396- world. init_resource :: < AtomicCounter > ( ) ;
1397- let mut schedule = Schedule :: default ( ) ;
1406+ fn test_true ( counter : & AtomicCounter ) -> bool {
1407+ _ = counter
1408+ . 0
1409+ . fetch_update ( Ordering :: SeqCst , Ordering :: SeqCst , |v| Some ( v * TRUE ) ) ;
1410+ true
1411+ }
13981412
1399- // This system should fail
1400- assert ! ( crate :: system:: RunSystemOnce :: run_system_once( & mut world, vacant) . is_err( ) ) ;
1413+ fn test_false ( counter : & AtomicCounter ) -> bool {
1414+ _ = counter
1415+ . 0
1416+ . fetch_update ( Ordering :: SeqCst , Ordering :: SeqCst , |v| Some ( v * FALSE ) ) ;
1417+ false
1418+ }
14011419
1402- schedule. add_systems (
1403- (
1404- increment_counter. run_if ( is_true. or ( vacant) ) ,
1405- increment_counter. run_if ( is_true. xor ( vacant) ) ,
1406- increment_counter. run_if ( is_true. and ( vacant) ) ,
1407- // vacant first
1408- increment_counter. run_if ( vacant. or ( is_true) ) ,
1409- increment_counter. run_if ( vacant. xor ( is_true) ) ,
1410- increment_counter. run_if ( vacant. and ( is_true) ) ,
1411- )
1412- . chain ( ) ,
1413- ) ;
1414- schedule. add_systems (
1415- (
1416- increment_counter. run_if ( is_true_inc. and ( vacant) ) ,
1417- increment_counter. run_if ( is_true_inc. xor ( vacant) ) ,
1418- increment_counter. run_if ( is_true_inc. or ( vacant) ) ,
1419- noop. run_if ( is_true_inc. or ( vacant) ) ,
1420- // vacant first
1421- increment_counter. run_if ( vacant. and ( is_true_inc) ) ,
1422- increment_counter. run_if ( vacant. xor ( is_true_inc) ) ,
1423- increment_counter. run_if ( vacant. or ( is_true_inc) ) ,
1424- noop. run_if ( vacant. or ( is_true_inc) ) ,
1425- )
1426- . chain ( ) ,
1427- ) ;
1420+ // Helper function that runs a logic call and returns the result, as
1421+ // well as the prime factorization of the calls.
1422+ fn logic_call_result ( f : impl FnOnce ( & AtomicCounter ) -> bool ) -> ( usize , bool ) {
1423+ let counter = AtomicCounter ( Arc :: new ( AtomicUsize :: new ( 1 ) ) ) ;
1424+ let result = f ( & counter) ;
1425+ ( counter. 0 . load ( Ordering :: SeqCst ) , result)
1426+ }
14281427
1429- schedule. run ( & mut world) ;
1430- assert_eq ! ( world. resource:: <Counter >( ) . 0 , 8 ) ;
1428+ // we expect `true() || false()` to yield `true`, and short circuit after `true()`
14311429 assert_eq ! (
1432- world . resource :: < AtomicCounter > ( ) . 0 . load ( Ordering :: SeqCst ) ,
1433- 7
1430+ logic_call_result ( |c| test_true ( c ) || test_false ( c ) ) ,
1431+ ( TRUE . pow ( 1 ) * FALSE . pow ( 0 ) , true )
14341432 ) ;
1433+
1434+ let mut world = World :: new ( ) ;
1435+ world. init_resource :: < AtomicCounter > ( ) ;
1436+
1437+ // ensure there are no `Vacant` entities
1438+ assert ! ( world. query:: <& Vacant >( ) . iter( & world) . next( ) . is_none( ) ) ;
1439+ assert ! ( matches!(
1440+ world. run_system_once( ( || true ) . or( vacant) ) ,
1441+ Ok ( true )
1442+ ) ) ;
1443+
1444+ // This system should fail
1445+ assert ! ( RunSystemOnce :: run_system_once( & mut world, vacant) . is_err( ) ) ;
1446+
1447+ #[ track_caller]
1448+ fn assert_system < Marker > (
1449+ world : & mut World ,
1450+ system : impl crate :: system:: IntoSystem < ( ) , bool , Marker > ,
1451+ equivalent_to : impl FnOnce ( & AtomicCounter ) -> bool ,
1452+ ) {
1453+ use crate :: system:: System ;
1454+
1455+ world
1456+ . resource :: < AtomicCounter > ( )
1457+ . 0
1458+ . store ( 1 , Ordering :: SeqCst ) ;
1459+
1460+ let system = crate :: system:: IntoSystem :: into_system ( system) ;
1461+ let name = system. name ( ) ;
1462+
1463+ let out = RunSystemOnce :: run_system_once ( & mut * world, system) . unwrap_or ( false ) ;
1464+
1465+ let ( expected_counter, expected) = logic_call_result ( equivalent_to) ;
1466+ let caller = std:: panic:: Location :: caller ( ) ;
1467+ let counter = world
1468+ . resource :: < AtomicCounter > ( )
1469+ . 0
1470+ . swap ( 1 , Ordering :: SeqCst ) ;
1471+
1472+ assert_eq ! (
1473+ out,
1474+ expected,
1475+ "At {}:{} System `{name}` yielded unexpected value `{out}`, expected `{expected}`" ,
1476+ caller. file( ) ,
1477+ caller. line( ) ,
1478+ ) ;
1479+
1480+ assert_eq ! (
1481+ counter, expected_counter,
1482+ "At {}:{} System `{name}` did not increment counter as expected: expected `{expected_counter}`, got `{counter}`" ,
1483+ caller. file( ) ,
1484+ caller. line( ) ,
1485+ ) ;
1486+ }
1487+
1488+ assert_system ( & mut world, is_true_inc. or ( vacant) , |c| {
1489+ test_true ( c) || false
1490+ } ) ;
1491+ assert_system ( & mut world, is_true_inc. nor ( vacant) , |c| {
1492+ !( test_true ( c) || false )
1493+ } ) ;
1494+ assert_system ( & mut world, is_true_inc. xor ( vacant) , |c| {
1495+ test_true ( c) ^ false
1496+ } ) ;
1497+ assert_system ( & mut world, is_true_inc. xnor ( vacant) , |c| {
1498+ !( test_true ( c) ^ false )
1499+ } ) ;
1500+ assert_system ( & mut world, is_true_inc. and ( vacant) , |c| {
1501+ test_true ( c) && false
1502+ } ) ;
1503+ assert_system ( & mut world, is_true_inc. nand ( vacant) , |c| {
1504+ !( test_true ( c) && false )
1505+ } ) ;
1506+
1507+ // even if `vacant` fails as the first condition, where applicable (or,
1508+ // xor), `is_true_inc` should still be called. `and` and `nand` short
1509+ // circuit on an initial `false`.
1510+ assert_system ( & mut world, vacant. or ( is_true_inc) , |c| {
1511+ false || test_true ( c)
1512+ } ) ;
1513+ assert_system ( & mut world, vacant. nor ( is_true_inc) , |c| {
1514+ !( false || test_true ( c) )
1515+ } ) ;
1516+ assert_system ( & mut world, vacant. xor ( is_true_inc) , |c| {
1517+ false ^ test_true ( c)
1518+ } ) ;
1519+ assert_system ( & mut world, vacant. xnor ( is_true_inc) , |c| {
1520+ !( false ^ test_true ( c) )
1521+ } ) ;
1522+ assert_system ( & mut world, vacant. and ( is_true_inc) , |c| {
1523+ false && test_true ( c)
1524+ } ) ;
1525+ assert_system ( & mut world, vacant. nand ( is_true_inc) , |c| {
1526+ !( false && test_true ( c) )
1527+ } ) ;
1528+
1529+ // the same logic ought to be the case with a condition that runs, but yields `false`:
1530+ assert_system ( & mut world, is_true_inc. or ( is_false_inc) , |c| {
1531+ test_true ( c) || test_false ( c)
1532+ } ) ;
1533+ assert_system ( & mut world, is_true_inc. nor ( is_false_inc) , |c| {
1534+ !( test_true ( c) || test_false ( c) )
1535+ } ) ;
1536+ assert_system ( & mut world, is_true_inc. xor ( is_false_inc) , |c| {
1537+ test_true ( c) ^ test_false ( c)
1538+ } ) ;
1539+ assert_system ( & mut world, is_true_inc. xnor ( is_false_inc) , |c| {
1540+ !( test_true ( c) ^ test_false ( c) )
1541+ } ) ;
1542+ assert_system ( & mut world, is_true_inc. and ( is_false_inc) , |c| {
1543+ test_true ( c) && test_false ( c)
1544+ } ) ;
1545+ assert_system ( & mut world, is_true_inc. nand ( is_false_inc) , |c| {
1546+ !( test_true ( c) && test_false ( c) )
1547+ } ) ;
1548+
1549+ // and where one condition yields `false` and the other fails:
1550+ assert_system ( & mut world, is_false_inc. or ( vacant) , |c| {
1551+ test_false ( c) || false
1552+ } ) ;
1553+ assert_system ( & mut world, is_false_inc. nor ( vacant) , |c| {
1554+ !( test_false ( c) || false )
1555+ } ) ;
1556+ assert_system ( & mut world, is_false_inc. xor ( vacant) , |c| {
1557+ test_false ( c) ^ false
1558+ } ) ;
1559+ assert_system ( & mut world, is_false_inc. xnor ( vacant) , |c| {
1560+ !( test_false ( c) ^ false )
1561+ } ) ;
1562+ assert_system ( & mut world, is_false_inc. and ( vacant) , |c| {
1563+ test_false ( c) && false
1564+ } ) ;
1565+ assert_system ( & mut world, is_false_inc. nand ( vacant) , |c| {
1566+ !( test_false ( c) && false )
1567+ } ) ;
1568+
1569+ // and where both conditions yield `true`:
1570+ assert_system ( & mut world, is_true_inc. or ( is_true_inc) , |c| {
1571+ test_true ( c) || test_true ( c)
1572+ } ) ;
1573+ assert_system ( & mut world, is_true_inc. nor ( is_true_inc) , |c| {
1574+ !( test_true ( c) || test_true ( c) )
1575+ } ) ;
1576+ assert_system ( & mut world, is_true_inc. xor ( is_true_inc) , |c| {
1577+ test_true ( c) ^ test_true ( c)
1578+ } ) ;
1579+ assert_system ( & mut world, is_true_inc. xnor ( is_true_inc) , |c| {
1580+ !( test_true ( c) ^ test_true ( c) )
1581+ } ) ;
1582+ assert_system ( & mut world, is_true_inc. and ( is_true_inc) , |c| {
1583+ test_true ( c) && test_true ( c)
1584+ } ) ;
1585+ assert_system ( & mut world, is_true_inc. nand ( is_true_inc) , |c| {
1586+ !( test_true ( c) && test_true ( c) )
1587+ } ) ;
1588+
1589+ // and where both conditions yield `false`:
1590+ assert_system ( & mut world, is_false_inc. or ( is_false_inc) , |c| {
1591+ test_false ( c) || test_false ( c)
1592+ } ) ;
1593+ assert_system ( & mut world, is_false_inc. nor ( is_false_inc) , |c| {
1594+ !( test_false ( c) || test_false ( c) )
1595+ } ) ;
1596+ assert_system ( & mut world, is_false_inc. xor ( is_false_inc) , |c| {
1597+ test_false ( c) ^ test_false ( c)
1598+ } ) ;
1599+ assert_system ( & mut world, is_false_inc. xnor ( is_false_inc) , |c| {
1600+ !( test_false ( c) ^ test_false ( c) )
1601+ } ) ;
1602+ assert_system ( & mut world, is_false_inc. and ( is_false_inc) , |c| {
1603+ test_false ( c) && test_false ( c)
1604+ } ) ;
1605+ assert_system ( & mut world, is_false_inc. nand ( is_false_inc) , |c| {
1606+ !( test_false ( c) && test_false ( c) )
1607+ } ) ;
14351608 }
14361609
14371610 #[ test]
0 commit comments