Convert control loop tests over to simulated event loop
This makes it so that we properly only use ShmEventLoop for running in
realtime on a robot. Very nice.
Change-Id: I46b770b336f59e08cfaf28511b3bd5689f72fff1
diff --git a/frc971/codelab/basic_test.cc b/frc971/codelab/basic_test.cc
index 2dc8dea..91059f6 100644
--- a/frc971/codelab/basic_test.cc
+++ b/frc971/codelab/basic_test.cc
@@ -16,21 +16,36 @@
namespace codelab {
namespace testing {
+namespace chrono = ::std::chrono;
+using aos::monotonic_clock;
+
// Class which simulates stuff and sends out queue messages with the
// position.
class BasicSimulation {
public:
- BasicSimulation()
- : basic_queue_(".frc971.codelab.basic_queue",
- ".frc971.codelab.basic_queue.goal",
- ".frc971.codelab.basic_queue.position",
- ".frc971.codelab.basic_queue.output",
- ".frc971.codelab.basic_queue.status") {}
+ BasicSimulation(::aos::EventLoop *event_loop, chrono::nanoseconds dt)
+ : event_loop_(event_loop),
+ position_sender_(event_loop_->MakeSender<BasicQueue::Position>(
+ ".frc971.codelab.basic_queue.position")),
+ status_fetcher_(event_loop_->MakeFetcher<BasicQueue::Status>(
+ ".frc971.codelab.basic_queue.status")),
+ output_fetcher_(event_loop_->MakeFetcher<BasicQueue::Output>(
+ ".frc971.codelab.basic_queue.output")) {
+ event_loop_->AddPhasedLoop(
+ [this](int) {
+ // Skip this the first time.
+ if (!first_) {
+ Simulate();
+ }
+ first_ = false;
+ SendPositionMessage();
+ },
+ dt);
+ }
// Sends a queue message with the position data.
void SendPositionMessage() {
- ::aos::ScopedMessagePtr<BasicQueue::Position> position =
- basic_queue_.position.MakeMessage();
+ auto position = position_sender_.MakeMessage();
position->limit_sensor = limit_sensor_;
@@ -38,68 +53,81 @@
}
void VerifyResults(double voltage, bool status) {
- basic_queue_.output.FetchLatest();
- basic_queue_.status.FetchLatest();
+ output_fetcher_.Fetch();
+ status_fetcher_.Fetch();
- ASSERT_TRUE(basic_queue_.output.get() != nullptr);
- ASSERT_TRUE(basic_queue_.status.get() != nullptr);
+ ASSERT_TRUE(output_fetcher_.get() != nullptr);
+ ASSERT_TRUE(status_fetcher_.get() != nullptr);
- EXPECT_EQ(basic_queue_.output->intake_voltage, voltage);
- EXPECT_EQ(basic_queue_.status->intake_complete, status);
+ EXPECT_EQ(output_fetcher_->intake_voltage, voltage);
+ EXPECT_EQ(status_fetcher_->intake_complete, status);
}
void set_limit_sensor(bool value) { limit_sensor_ = value; }
// Simulates basic control loop for a single timestep.
- void Simulate() { EXPECT_TRUE(basic_queue_.output.FetchLatest()); }
+ void Simulate() { EXPECT_TRUE(output_fetcher_.Fetch()); }
private:
- BasicQueue basic_queue_;
+ ::aos::EventLoop *event_loop_;
+
+ ::aos::Sender<BasicQueue::Position> position_sender_;
+ ::aos::Fetcher<BasicQueue::Status> status_fetcher_;
+ ::aos::Fetcher<BasicQueue::Output> output_fetcher_;
+
bool limit_sensor_ = false;
+
+ bool first_ = true;
};
class BasicControlLoopTest : public ::aos::testing::ControlLoopTest {
public:
BasicControlLoopTest()
- : basic_queue_(".frc971.codelab.basic_queue",
- ".frc971.codelab.basic_queue.goal",
- ".frc971.codelab.basic_queue.position",
- ".frc971.codelab.basic_queue.output",
- ".frc971.codelab.basic_queue.status"),
- basic_loop_(&event_loop_, ".frc971.codelab.basic_queue") {
+ : ::aos::testing::ControlLoopTest(chrono::microseconds(5050)),
+ test_event_loop_(MakeEventLoop()),
+ goal_sender_(test_event_loop_->MakeSender<BasicQueue::Goal>(
+ ".frc971.codelab.basic_queue.goal")),
+
+ basic_event_loop_(MakeEventLoop()),
+ basic_(basic_event_loop_.get(), ".frc971.codelab.basic_queue"),
+
+ basic_simulation_event_loop_(MakeEventLoop()),
+ basic_simulation_(basic_simulation_event_loop_.get(), dt()) {
set_team_id(control_loops::testing::kTeamNumber);
}
- // Runs one iteration of the whole simulation.
- void RunIteration(bool enabled = true) {
- SendMessages(enabled);
+ ::std::unique_ptr<::aos::EventLoop> test_event_loop_;
+ ::aos::Sender<BasicQueue::Goal> goal_sender_;
- basic_simulation_.SendPositionMessage();
- basic_loop_.Iterate();
- basic_simulation_.Simulate();
+ ::std::unique_ptr<::aos::EventLoop> basic_event_loop_;
+ Basic basic_;
- TickTime();
- }
-
- BasicQueue basic_queue_;
- ::aos::ShmEventLoop event_loop_;
- Basic basic_loop_;
+ ::std::unique_ptr<::aos::EventLoop> basic_simulation_event_loop_;
BasicSimulation basic_simulation_;
};
// Tests that when the motor has finished intaking it stops.
TEST_F(BasicControlLoopTest, IntakeLimitTransitionsToTrue) {
- ASSERT_TRUE(basic_queue_.goal.MakeWithBuilder().intake(true).Send());
+ {
+ auto message = goal_sender_.MakeMessage();
+ message->intake = true;
+ ASSERT_TRUE(message.Send());
+ }
+
basic_simulation_.set_limit_sensor(false);
- RunIteration();
+ RunFor(dt() * 2);
basic_simulation_.VerifyResults(12.0, false);
- ASSERT_TRUE(basic_queue_.goal.MakeWithBuilder().intake(true).Send());
+ {
+ auto message = goal_sender_.MakeMessage();
+ message->intake = true;
+ ASSERT_TRUE(message.Send());
+ }
basic_simulation_.set_limit_sensor(true);
- RunIteration();
+ RunFor(dt() * 2);
basic_simulation_.VerifyResults(0.0, true);
}
@@ -107,10 +135,14 @@
// Tests that the intake goes on if the sensor is not pressed
// and intake is requested.
TEST_F(BasicControlLoopTest, IntakeLimitNotSet) {
- ASSERT_TRUE(basic_queue_.goal.MakeWithBuilder().intake(true).Send());
+ {
+ auto message = goal_sender_.MakeMessage();
+ message->intake = true;
+ ASSERT_TRUE(message.Send());
+ }
basic_simulation_.set_limit_sensor(false);
- RunIteration();
+ RunFor(dt() * 2);
basic_simulation_.VerifyResults(12.0, false);
}
@@ -118,10 +150,14 @@
// Tests that the intake is off if no intake is requested,
// even if the limit sensor is off.
TEST_F(BasicControlLoopTest, NoIntakeLimitNotSet) {
- ASSERT_TRUE(basic_queue_.goal.MakeWithBuilder().intake(false).Send());
+ {
+ auto message = goal_sender_.MakeMessage();
+ message->intake = false;
+ ASSERT_TRUE(message.Send());
+ }
basic_simulation_.set_limit_sensor(false);
- RunIteration();
+ RunFor(dt() * 2);
basic_simulation_.VerifyResults(0.0, false);
}
@@ -129,20 +165,28 @@
// Tests that the intake is off even if the limit sensor
// is pressed and intake is requested.
TEST_F(BasicControlLoopTest, IntakeLimitSet) {
- ASSERT_TRUE(basic_queue_.goal.MakeWithBuilder().intake(true).Send());
+ {
+ auto message = goal_sender_.MakeMessage();
+ message->intake = true;
+ ASSERT_TRUE(message.Send());
+ }
basic_simulation_.set_limit_sensor(true);
- RunIteration();
+ RunFor(dt() * 2);
basic_simulation_.VerifyResults(0.0, true);
}
// Tests that the intake is off if no intake is requested,
TEST_F(BasicControlLoopTest, NoIntakeLimitSet) {
- ASSERT_TRUE(basic_queue_.goal.MakeWithBuilder().intake(false).Send());
+ {
+ auto message = goal_sender_.MakeMessage();
+ message->intake = false;
+ ASSERT_TRUE(message.Send());
+ }
basic_simulation_.set_limit_sensor(true);
- RunIteration();
+ RunFor(dt() * 2);
basic_simulation_.VerifyResults(0.0, false);
}
@@ -151,7 +195,7 @@
TEST_F(BasicControlLoopTest, NoGoalSet) {
basic_simulation_.set_limit_sensor(true);
- RunIteration();
+ RunFor(dt() * 2);
basic_simulation_.VerifyResults(0.0, false);
}
@@ -160,9 +204,14 @@
TEST_F(BasicControlLoopTest, Disabled) {
basic_simulation_.set_limit_sensor(true);
- ASSERT_TRUE(basic_queue_.goal.MakeWithBuilder().intake(true).Send());
+ {
+ auto message = goal_sender_.MakeMessage();
+ message->intake = true;
+ ASSERT_TRUE(message.Send());
+ }
- RunIteration(false);
+ SetEnabled(false);
+ RunFor(dt() * 2);
basic_simulation_.VerifyResults(0.0, false);
}