diff --git a/wpilibc/src/test/native/cpp/event/BooleanEventTest.cpp b/wpilibc/src/test/native/cpp/event/BooleanEventTest.cpp index 3d875dbb50f..6d44b552b8d 100644 --- a/wpilibc/src/test/native/cpp/event/BooleanEventTest.cpp +++ b/wpilibc/src/test/native/cpp/event/BooleanEventTest.cpp @@ -31,6 +31,78 @@ TEST(BooleanEventTest, BinaryCompositions) { EXPECT_EQ(1, orCounter); } +TEST(BooleanEventTest, BinaryCompositionLoopSemantics) { + EventLoop loop1; + EventLoop loop2; + bool boolean1 = true; + bool boolean2 = true; + int counter1 = 0; + int counter2 = 0; + + (BooleanEvent(&loop1, [&] { return boolean1; }) && BooleanEvent(&loop2, [&] { + return boolean2; + })).IfHigh([&] { ++counter1; }); + (BooleanEvent(&loop2, [&] { return boolean1; }) && BooleanEvent(&loop1, [&] { + return boolean2; + })).IfHigh([&] { ++counter2; }); + + EXPECT_EQ(0, counter1); + EXPECT_EQ(0, counter2); + + loop1.Poll(); + + EXPECT_EQ(1, counter1); + EXPECT_EQ(0, counter2); + + loop2.Poll(); + + EXPECT_EQ(1, counter1); + EXPECT_EQ(1, counter2); + + boolean2 = false; + loop1.Poll(); + + EXPECT_EQ(2, counter1); + EXPECT_EQ(1, counter2); + + loop2.Poll(); + + EXPECT_EQ(2, counter1); + EXPECT_EQ(1, counter2); + + loop1.Poll(); + + EXPECT_EQ(2, counter1); + EXPECT_EQ(1, counter2); + + boolean2 = true; + loop2.Poll(); + + EXPECT_EQ(2, counter1); + EXPECT_EQ(1, counter2); + + loop1.Poll(); + + EXPECT_EQ(3, counter1); + EXPECT_EQ(1, counter2); + + boolean1 = false; + loop2.Poll(); + + EXPECT_EQ(3, counter1); + EXPECT_EQ(1, counter2); + + loop1.Poll(); + + EXPECT_EQ(3, counter1); + EXPECT_EQ(1, counter2); + + loop2.Poll(); + + EXPECT_EQ(3, counter1); + EXPECT_EQ(1, counter2); +} + /** * When a BooleanEvent is constructed, an action is bound to the event loop to * update an internal state variable. This state variable is checked during loop diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/BooleanEventTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/BooleanEventTest.java index 90f94051578..e8388c329ce 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/BooleanEventTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/BooleanEventTest.java @@ -30,6 +30,80 @@ void testBinaryCompositions() { assertEquals(1, orCounter.get()); } + @Test + void testBinaryCompositionLoopSemantics() { + var loop1 = new EventLoop(); + var loop2 = new EventLoop(); + var bool1 = new AtomicBoolean(true); + var bool2 = new AtomicBoolean(true); + var counter1 = new AtomicInteger(0); + var counter2 = new AtomicInteger(0); + + new BooleanEvent(loop1, bool1::get) + .and(new BooleanEvent(loop2, bool2::get)) + .ifHigh(counter1::incrementAndGet); + + new BooleanEvent(loop2, bool1::get) + .and(new BooleanEvent(loop1, bool2::get)) + .ifHigh(counter2::incrementAndGet); + + assertEquals(0, counter1.get()); + assertEquals(0, counter2.get()); + + loop1.poll(); + + assertEquals(1, counter1.get()); + assertEquals(0, counter2.get()); + + loop2.poll(); + + assertEquals(1, counter1.get()); + assertEquals(1, counter2.get()); + + bool2.set(false); + loop1.poll(); + + assertEquals(2, counter1.get()); + assertEquals(1, counter2.get()); + + loop2.poll(); + + assertEquals(2, counter1.get()); + assertEquals(1, counter2.get()); + + loop1.poll(); + + assertEquals(2, counter1.get()); + assertEquals(1, counter2.get()); + + bool2.set(true); + loop2.poll(); + + assertEquals(2, counter1.get()); + assertEquals(1, counter2.get()); + + loop1.poll(); + + assertEquals(3, counter1.get()); + assertEquals(1, counter2.get()); + + bool1.set(false); + loop2.poll(); + + assertEquals(3, counter1.get()); + assertEquals(1, counter2.get()); + + loop1.poll(); + + assertEquals(3, counter1.get()); + assertEquals(1, counter2.get()); + + loop2.poll(); + + assertEquals(3, counter1.get()); + assertEquals(1, counter2.get()); + } + /** * When a BooleanEvent is constructed, an action is bound to the event loop to update an internal * state variable. This state variable is checked during loop polls to determine whether or not to