Create MockStarter in aos/starter
Change-Id: I58b36a8c0cfa9c6262ce9e1222cd6456dfb5ca1a
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/starter/BUILD b/aos/starter/BUILD
index 8a30408..9068caa 100644
--- a/aos/starter/BUILD
+++ b/aos/starter/BUILD
@@ -247,3 +247,16 @@
"//aos/testing:googletest",
],
)
+
+cc_library(
+ name = "mock_starter",
+ srcs = ["mock_starter.cc"],
+ hdrs = ["mock_starter.h"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//aos/events:simulated_event_loop",
+ "//aos/starter:starter_fbs",
+ "//aos/starter:starter_rpc_fbs",
+ "//aos/starter:starter_rpc_lib",
+ ],
+)
diff --git a/aos/starter/mock_starter.cc b/aos/starter/mock_starter.cc
new file mode 100644
index 0000000..5908abe
--- /dev/null
+++ b/aos/starter/mock_starter.cc
@@ -0,0 +1,115 @@
+#include "aos/starter/mock_starter.h"
+
+namespace aos {
+namespace starter {
+
+MockStarter::MockStarter(aos::EventLoop *event_loop)
+ : event_loop_(event_loop),
+ status_sender_(event_loop_->MakeSender<aos::starter::Status>("/aos")) {
+ aos::TimerHandler *send_timer =
+ event_loop_->AddTimer([this]() { SendStatus(); });
+
+ CHECK(aos::configuration::MultiNode(event_loop_->configuration()));
+
+ for (const aos::Node *node :
+ aos::configuration::GetNodes(event_loop_->configuration())) {
+ const aos::Channel *channel = aos::starter::StarterRpcChannelForNode(
+ event_loop_->configuration(), node);
+ if (aos::configuration::ChannelIsReadableOnNode(channel,
+ event_loop_->node())) {
+ std::string_view channel_name = channel->name()->string_view();
+ event_loop_->MakeWatcher(
+ channel_name, [this](const aos::starter::StarterRpc &command) {
+ for (const flatbuffers::String *node : *command.nodes()) {
+ if (node->string_view() ==
+ event_loop_->node()->name()->string_view()) {
+ CHECK(statuses_.count(command.name()->str()) > 0)
+ << "Unable to find " << command.name()->string_view()
+ << " in our list of applications.";
+ ApplicationStatus &status = statuses_[command.name()->str()];
+ switch (command.command()) {
+ case aos::starter::Command::START:
+ if (!status.running) {
+ status.running = true;
+ status.start_time = event_loop_->monotonic_now();
+ status.id = next_id_++;
+ }
+ break;
+ case aos::starter::Command::STOP:
+ status.running = false;
+ break;
+ case aos::starter::Command::RESTART:
+ status.running = true;
+ status.start_time = event_loop_->monotonic_now();
+ status.id = next_id_++;
+ }
+ SendStatus();
+ }
+ }
+ });
+ }
+ }
+
+ event_loop_->OnRun([this, send_timer]() {
+ send_timer->Setup(event_loop_->monotonic_now(), std::chrono::seconds(1));
+
+ for (const aos::Application *application :
+ *event_loop_->configuration()->applications()) {
+ if (aos::configuration::ApplicationShouldStart(
+ event_loop_->configuration(), event_loop_->node(), application)) {
+ statuses_[application->name()->str()] = ApplicationStatus{
+ next_id_++, application->autostart(), event_loop_->monotonic_now()};
+ }
+ }
+ });
+}
+
+void MockStarter::SendStatus() {
+ aos::Sender<aos::starter::Status>::Builder builder =
+ status_sender_.MakeBuilder();
+ std::vector<flatbuffers::Offset<aos::starter::ApplicationStatus>>
+ status_offsets;
+ for (const std::pair<const std::string, ApplicationStatus> &pair :
+ statuses_) {
+ const flatbuffers::Offset<flatbuffers::String> name_offset =
+ builder.fbb()->CreateString(pair.first);
+ aos::starter::ApplicationStatus::Builder status_builder =
+ builder.MakeBuilder<aos::starter::ApplicationStatus>();
+ status_builder.add_name(name_offset);
+ status_builder.add_state(pair.second.running
+ ? aos::starter::State::RUNNING
+ : aos::starter::State::STOPPED);
+ status_builder.add_last_exit_code(0);
+ status_builder.add_id(pair.second.id);
+ status_builder.add_last_stop_reason(
+ aos::starter::LastStopReason::STOP_REQUESTED);
+ status_builder.add_last_start_time(
+ pair.second.start_time.time_since_epoch().count());
+ if (pair.second.running) {
+ status_builder.add_pid(pair.second.id);
+ }
+ status_offsets.push_back(status_builder.Finish());
+ }
+ const flatbuffers::Offset<
+ flatbuffers::Vector<flatbuffers::Offset<aos::starter::ApplicationStatus>>>
+ statuses_offset = builder.fbb()->CreateVector(status_offsets);
+ aos::starter::Status::Builder status_builder =
+ builder.MakeBuilder<aos::starter::Status>();
+ status_builder.add_statuses(statuses_offset);
+ builder.CheckOk(builder.Send(status_builder.Finish()));
+}
+
+MockStarters::MockStarters(aos::SimulatedEventLoopFactory *event_loop_factory) {
+ CHECK(aos::configuration::MultiNode(event_loop_factory->configuration()));
+ for (const aos::Node *node :
+ aos::configuration::GetNodes(event_loop_factory->configuration())) {
+ event_loops_.emplace_back(
+ event_loop_factory->GetNodeEventLoopFactory(node)->MakeEventLoop(
+ "starterd"));
+ mock_starters_.emplace_back(
+ std::make_unique<MockStarter>(event_loops_.back().get()));
+ }
+}
+
+} // namespace starter
+} // namespace aos
diff --git a/aos/starter/mock_starter.h b/aos/starter/mock_starter.h
new file mode 100644
index 0000000..a0c1b76
--- /dev/null
+++ b/aos/starter/mock_starter.h
@@ -0,0 +1,54 @@
+#include <map>
+
+#include "aos/events/event_loop.h"
+#include "aos/events/simulated_event_loop.h"
+#include "aos/starter/starter_generated.h"
+#include "aos/starter/starter_rpc_generated.h"
+#include "aos/starter/starterd_lib.h"
+
+namespace aos {
+namespace starter {
+
+// Simple mock of starterd that updates the starter status message to act as
+// though applications are started and stopped when requested.
+// TODO(james.kuszmaul): Consider integrating with SimulatedEventLoopFactory.
+class MockStarter {
+ public:
+ struct ApplicationStatus {
+ int id;
+ bool running;
+ aos::monotonic_clock::time_point start_time;
+ };
+
+ MockStarter(aos::EventLoop *event_loop);
+
+ const aos::Node *node() const { return event_loop_->node(); }
+
+ const std::map<std::string, ApplicationStatus> &statuses() const {
+ return statuses_;
+ }
+
+ private:
+ void SendStatus();
+
+ aos::EventLoop *event_loop_;
+ aos::Sender<aos::starter::Status> status_sender_;
+ std::map<std::string, ApplicationStatus> statuses_;
+ int next_id_ = 0;
+};
+
+// Spins up MockStarter's for each node.
+class MockStarters {
+ public:
+ MockStarters(aos::SimulatedEventLoopFactory *event_loop_factory);
+ const std::vector<std::unique_ptr<MockStarter>> &starters() const {
+ return mock_starters_;
+ }
+
+ private:
+ std::vector<std::unique_ptr<aos::EventLoop>> event_loops_;
+ std::vector<std::unique_ptr<MockStarter>> mock_starters_;
+};
+
+} // namespace starter
+} // namespace aos