[wpilib] Make BooleanEvent more consistent (#5436)

Co-authored-by: Tyler Veness <calcmogul@gmail.com>
Co-authored-by: Joseph Eng <91924258+KangarooKoala@users.noreply.github.com>
This commit is contained in:
Gold856
2023-10-05 00:22:57 -04:00
committed by GitHub
parent 6576d9b474
commit 58141d6eb5
5 changed files with 890 additions and 58 deletions

View File

@@ -0,0 +1,487 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <atomic>
#include <gtest/gtest.h>
#include "frc/event/BooleanEvent.h"
#include "frc/event/EventLoop.h"
using namespace frc;
TEST(BooleanEventTest, BinaryCompositions) {
EventLoop loop;
int andCounter = 0;
int orCounter = 0;
EXPECT_EQ(0, andCounter);
EXPECT_EQ(0, orCounter);
(BooleanEvent(&loop, [] { return true; }) && BooleanEvent(&loop, [] {
return false;
})).IfHigh([&] { ++andCounter; });
(BooleanEvent(&loop, [] { return true; }) || BooleanEvent(&loop, [] {
return false;
})).IfHigh([&] { ++orCounter; });
loop.Poll();
EXPECT_EQ(0, andCounter);
EXPECT_EQ(1, orCounter);
}
/**
* Tests that composed edge events only execute on edges (two
* rising edge events composed with and() should only execute when both signals
* are on the rising edge)
*/
TEST(BooleanEventTest, BinaryCompositionsWithEdgeDecorators) {
EventLoop loop;
bool boolean1 = false;
bool boolean2 = false;
bool boolean3 = false;
bool boolean4 = false;
int counter = 0;
auto event1 = BooleanEvent(&loop, [&] { return boolean1; }).Rising();
auto event2 = BooleanEvent(&loop, [&] { return boolean2; }).Rising();
auto event3 = BooleanEvent(&loop, [&] { return boolean3; }).Rising();
auto event4 = BooleanEvent(&loop, [&] { return boolean4; }).Rising();
(event1 && event2).IfHigh([&] { ++counter; });
(event3 || event4).IfHigh([&] { ++counter; });
EXPECT_EQ(0, counter);
boolean1 = true;
boolean2 = true;
boolean3 = true;
boolean4 = true;
loop.Poll(); // Both actions execute
EXPECT_EQ(2, counter);
loop.Poll(); // Nothing should happen since nothing is on rising edge
EXPECT_EQ(2, counter);
boolean1 = false;
boolean2 = false;
boolean3 = false;
boolean4 = false;
loop.Poll(); // Nothing should happen
EXPECT_EQ(2, counter);
boolean1 = true;
loop.Poll(); // Nothing should happen since only Bool 1 is on rising edge
EXPECT_EQ(2, counter);
boolean2 = true;
loop.Poll(); // Bool 2 is on rising edge, but Bool 1 isn't, nothing should
// happen
EXPECT_EQ(2, counter);
boolean1 = false;
boolean2 = false;
loop.Poll(); // Nothing should happen
EXPECT_EQ(2, counter);
boolean1 = true;
boolean2 = true;
loop.Poll(); // Bool 1 and 2 are on rising edge, increments counter once
EXPECT_EQ(3, counter);
boolean3 = true;
loop.Poll(); // Bool 3 is on rising edge, increments counter once
EXPECT_EQ(4, counter);
loop.Poll(); // Nothing should happen, Bool 3 isn't on rising edge
EXPECT_EQ(4, counter);
boolean4 = true;
loop.Poll(); // Bool 4 is on rising edge, increments counter once
EXPECT_EQ(5, counter);
loop.Poll(); // Nothing should happen, Bool 4 isn't on rising edge
EXPECT_EQ(5, counter);
}
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 boolean2; }) && BooleanEvent(&loop1, [&] {
return boolean1;
})).IfHigh([&] { ++counter2; });
EXPECT_EQ(0, counter1);
EXPECT_EQ(0, counter2);
loop1
.Poll(); // 1st event executes, Bool 1 and 2 are true, increments counter
EXPECT_EQ(1, counter1);
EXPECT_EQ(0, counter2);
loop2
.Poll(); // 2nd event executes, Bool 1 and 2 are true, increments counter
EXPECT_EQ(1, counter1);
EXPECT_EQ(1, counter2);
boolean2 = false;
loop1.Poll(); // 1st event executes, Bool 2 is still true because loop 2
// hasn't updated it, increments counter
EXPECT_EQ(2, counter1);
EXPECT_EQ(1, counter2);
loop2.Poll(); // 2nd event executes, Bool 2 is now false because this loop
// updated it, does nothing
EXPECT_EQ(2, counter1);
EXPECT_EQ(1, counter2);
loop1.Poll(); // All bools are updated at this point, nothing should happen
EXPECT_EQ(2, counter1);
EXPECT_EQ(1, counter2);
boolean2 = true;
loop2.Poll(); // 2nd event executes, Bool 2 is true because this loop updated
// it, increments counter
EXPECT_EQ(2, counter1);
EXPECT_EQ(2, counter2);
loop1.Poll(); // 1st event executes, Bool 2 is true because loop 2 updated
// it, increments counter
EXPECT_EQ(3, counter1);
EXPECT_EQ(2, counter2);
boolean1 = false;
loop2.Poll(); // 2nd event executes, Bool 1 is still true because loop 1
// hasn't updated it, increments counter
EXPECT_EQ(3, counter1);
EXPECT_EQ(3, counter2);
loop1.Poll(); // 1st event executes, Bool 1 is false because this loop
// updated it, does nothing
EXPECT_EQ(3, counter1);
EXPECT_EQ(3, counter2);
loop2.Poll(); // All bools are updated at this point, nothing should happen
EXPECT_EQ(3, counter1);
EXPECT_EQ(3, counter2);
}
/** Tests the order of actions bound to an event loop. */
TEST(BooleanEventTest, PollOrdering) {
EventLoop loop;
bool boolean1 = true;
bool boolean2 = true;
bool enableAssert = false;
int counter = 0;
(BooleanEvent( // This event binds an action to the event loop first
&loop,
[&] {
if (enableAssert) {
++counter;
EXPECT_EQ(1, counter % 3);
}
return boolean1;
}) && // The composed event binds an action to the event loop third
// This event binds an action to the event loop second
BooleanEvent(&loop, [&] {
if (enableAssert) {
++counter;
EXPECT_EQ(2, counter % 3);
}
return boolean2;
// This binds an action to the event loop fourth
})).IfHigh([&] {
if (enableAssert) {
++counter;
EXPECT_EQ(0, counter % 3);
}
});
enableAssert = true;
loop.Poll();
loop.Poll();
loop.Poll();
loop.Poll();
}
TEST(BooleanEventTest, EdgeDecorators) {
EventLoop loop;
bool boolean = false;
int counter = 0;
BooleanEvent(&loop, [&] { return boolean; }).Falling().IfHigh([&] {
--counter;
});
BooleanEvent(&loop, [&] { return boolean; }).Rising().IfHigh([&] {
++counter;
});
EXPECT_EQ(0, counter);
boolean = false;
loop.Poll();
EXPECT_EQ(0, counter);
boolean = true;
loop.Poll();
EXPECT_EQ(1, counter);
boolean = true;
loop.Poll();
EXPECT_EQ(1, counter);
boolean = false;
loop.Poll();
EXPECT_EQ(0, counter);
}
/**
* Tests that binding actions to the same edge event will result in all actions
* executing.
*/
TEST(BooleanEventTest, EdgeReuse) {
EventLoop loop;
bool boolean = false;
int counter = 0;
auto event = BooleanEvent(&loop, [&] { return boolean; }).Rising();
event.IfHigh([&] { ++counter; });
event.IfHigh([&] { ++counter; });
EXPECT_EQ(0, counter);
loop.Poll();
EXPECT_EQ(0, counter);
boolean = true;
loop.Poll();
EXPECT_EQ(2, counter);
loop.Poll();
EXPECT_EQ(2, counter);
boolean = false;
loop.Poll();
EXPECT_EQ(2, counter);
boolean = true;
loop.Poll();
EXPECT_EQ(4, counter);
}
/**
* Tests that all actions execute on separate edge events constructed from the
* original event.
*/
TEST(BooleanEventTest, EdgeReconstruct) {
EventLoop loop;
bool boolean = false;
int counter = 0;
auto event = BooleanEvent(&loop, [&] { return boolean; });
event.Rising().IfHigh([&] { ++counter; });
event.Rising().IfHigh([&] { ++counter; });
EXPECT_EQ(0, counter);
loop.Poll();
EXPECT_EQ(0, counter);
boolean = true;
loop.Poll();
EXPECT_EQ(2, counter);
loop.Poll();
EXPECT_EQ(2, counter);
boolean = false;
loop.Poll();
EXPECT_EQ(2, counter);
boolean = true;
loop.Poll();
EXPECT_EQ(4, counter);
}
/** Tests that all actions bound to an event will still execute even if the
* signal is changed during the loop poll */
TEST(BooleanEventTest, MidLoopBooleanChange) {
EventLoop loop;
bool boolean = false;
int counter = 0;
auto event = BooleanEvent(&loop, [&] { return boolean; }).Rising();
event.IfHigh([&] {
boolean = false;
++counter;
});
event.IfHigh([&] { ++counter; });
EXPECT_EQ(0, counter);
loop.Poll();
EXPECT_EQ(0, counter);
boolean = true;
loop.Poll();
EXPECT_EQ(2, counter);
loop.Poll();
EXPECT_EQ(2, counter);
boolean = false;
loop.Poll();
EXPECT_EQ(2, counter);
boolean = true;
loop.Poll();
EXPECT_EQ(4, counter);
}
/**
* Tests that all actions bound to composed events will still execute even if
* the composed signal changes during the loop poll.
*/
TEST(BooleanEventTest, MidLoopBooleanChangeWithComposedEvents) {
EventLoop loop;
bool boolean1 = false;
bool boolean2 = false;
bool boolean3 = false;
bool boolean4 = false;
int counter = 0;
auto event1 = BooleanEvent(&loop, [&] { return boolean1; });
auto event2 = BooleanEvent(&loop, [&] { return boolean2; });
auto event3 = BooleanEvent(&loop, [&] { return boolean3; });
auto event4 = BooleanEvent(&loop, [&] { return boolean4; });
event1.IfHigh([&] {
boolean2 = false;
boolean3 = false;
++counter;
});
(event3 || event4).IfHigh([&] {
boolean1 = false;
++counter;
});
(event1 && event2).IfHigh([&] {
boolean4 = false;
++counter;
});
EXPECT_EQ(0, counter);
boolean1 = true;
boolean2 = true;
boolean3 = true;
boolean4 = true;
loop.Poll(); // All three actions execute, incrementing the counter three
// times and setting all booleans to false
EXPECT_EQ(3, counter);
loop.Poll(); // Nothing should happen since everything was set to false
EXPECT_EQ(3, counter);
boolean1 = true;
boolean2 = true;
loop.Poll(); // Bool 1 and 2 are true, increments counter twice, Bool 2 gets
// set to false
EXPECT_EQ(5, counter);
boolean1 = false;
loop.Poll(); // Nothing should happen
EXPECT_EQ(5, counter);
boolean1 = true;
boolean3 = true;
loop.Poll(); // Bool 1 and 3 are true, increments counter twice, Bool 3 gets
// set to false
EXPECT_EQ(7, counter);
boolean1 = false;
boolean4 = true;
loop.Poll(); // Bool 4 is true, increments counter once
EXPECT_EQ(8, counter);
}
TEST(BooleanEventTest, Negation) {
EventLoop loop;
bool boolean = false;
int counter = 0;
(!BooleanEvent(&loop, [&] { return boolean; })).IfHigh([&] { ++counter; });
EXPECT_EQ(0, counter);
loop.Poll();
EXPECT_EQ(1, counter);
boolean = true;
loop.Poll();
EXPECT_EQ(1, counter);
boolean = false;
loop.Poll();
EXPECT_EQ(2, counter);
boolean = true;
loop.Poll();
EXPECT_EQ(2, counter);
}