Add test case for ping and pong
This provides a simple example for how to test, and also provides a
staring point for the tests for the logger.
Change-Id: Ieac6858156627c0efcdbb739612e6a4efaf19b80
diff --git a/aos/BUILD b/aos/BUILD
index a2c7134..4f87873 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -442,7 +442,8 @@
"testdata/config3.json",
"testdata/expected.json",
"//aos/events:config.fb.json",
- "//aos/events:pingpong.bfbs",
+ "//aos/events:ping.bfbs",
+ "//aos/events:pong.bfbs",
],
deps = [
":configuration",
diff --git a/aos/events/BUILD b/aos/events/BUILD
index 17ed688..3b3fb52 100644
--- a/aos/events/BUILD
+++ b/aos/events/BUILD
@@ -1,4 +1,5 @@
load("@com_github_google_flatbuffers//:build_defs.bzl", "flatbuffer_cc_library")
+load("//aos:config.bzl", "aos_config")
package(default_visibility = ["//visibility:public"])
@@ -9,8 +10,14 @@
)
flatbuffer_cc_library(
- name = "pingpong_fbs",
- srcs = ["pingpong.fbs"],
+ name = "ping_fbs",
+ srcs = ["ping.fbs"],
+ gen_reflections = 1,
+)
+
+flatbuffer_cc_library(
+ name = "pong_fbs",
+ srcs = ["pong.fbs"],
gen_reflections = 1,
)
@@ -41,14 +48,31 @@
],
)
+cc_library(
+ name = "ping_lib",
+ srcs = [
+ "ping_lib.cc",
+ ],
+ hdrs = [
+ "ping_lib.h",
+ ],
+ deps = [
+ ":event_loop",
+ ":ping_fbs",
+ ":pong_fbs",
+ "//aos:json_to_flatbuffer",
+ "@com_github_google_glog//:glog",
+ ],
+)
+
cc_binary(
name = "ping",
srcs = [
"ping.cc",
],
- data = ["config.fb.json"],
+ data = ["pingpong_config.json"],
deps = [
- ":pingpong_fbs",
+ ":ping_lib",
":shm_event_loop",
"//aos:configuration",
"//aos:init",
@@ -57,14 +81,41 @@
],
)
+aos_config(
+ name = "pingpong_config",
+ src = "config.fb.json",
+ flatbuffers = [
+ ":ping_fbs",
+ ":pong_fbs",
+ ],
+)
+
+cc_library(
+ name = "pong_lib",
+ srcs = [
+ "pong_lib.cc",
+ ],
+ hdrs = [
+ "pong_lib.h",
+ ],
+ deps = [
+ ":event_loop",
+ ":ping_fbs",
+ ":pong_fbs",
+ "@com_github_google_glog//:glog",
+ ],
+)
+
cc_binary(
name = "pong",
srcs = [
"pong.cc",
],
- data = ["config.fb.json"],
+ data = ["pingpong_config.json"],
deps = [
- ":pingpong_fbs",
+ ":ping_fbs",
+ ":pong_fbs",
+ ":pong_lib",
":shm_event_loop",
"//aos:configuration",
"//aos:init",
@@ -73,6 +124,20 @@
],
)
+cc_test(
+ name = "pingpong_test",
+ srcs = ["pingpong_test.cc"],
+ data = [":pingpong_config.json"],
+ deps = [
+ ":ping_lib",
+ ":pong_lib",
+ ":simulated_event_loop",
+ "//aos:configuration",
+ "//aos:flatbuffers",
+ "//aos/testing:googletest",
+ ],
+)
+
cc_library(
name = "shm_event_loop",
srcs = ["shm_event_loop.cc"],
diff --git a/aos/events/ping.cc b/aos/events/ping.cc
index b904cb2..66c8f12 100644
--- a/aos/events/ping.cc
+++ b/aos/events/ping.cc
@@ -1,81 +1,18 @@
-#include <chrono>
-
#include "aos/configuration.h"
-#include "aos/events/pingpong_generated.h"
+#include "aos/events/ping_lib.h"
#include "aos/events/shm_event_loop.h"
#include "aos/init.h"
#include "aos/json_to_flatbuffer.h"
#include "gflags/gflags.h"
#include "glog/logging.h"
-DEFINE_int32(sleep_ms, 10, "Time to sleep between pings");
-
-namespace aos {
-
-namespace chrono = std::chrono;
-
-class Ping {
- public:
- Ping(EventLoop *event_loop)
- : event_loop_(event_loop),
- sender_(event_loop_->MakeSender<examples::Ping>("/test")) {
- timer_handle_ = event_loop_->AddTimer([this]() { SendPing(); });
-
- event_loop_->MakeWatcher(
- "/test", [this](const examples::Pong &pong) { HandlePong(pong); });
-
- event_loop_->OnRun([this]() {
- timer_handle_->Setup(event_loop_->monotonic_now(),
- chrono::milliseconds(FLAGS_sleep_ms));
- });
-
- event_loop_->SetRuntimeRealtimePriority(5);
- }
-
- void SendPing() {
- ++count_;
- aos::Sender<examples::Ping>::Builder msg = sender_.MakeBuilder();
- examples::Ping::Builder builder = msg.MakeBuilder<examples::Ping>();
- builder.add_value(count_);
- builder.add_send_time(
- event_loop_->monotonic_now().time_since_epoch().count());
- CHECK(msg.Send(builder.Finish()));
- VLOG(2) << "Sending ping";
- }
-
- void HandlePong(const examples::Pong &pong) {
- const aos::monotonic_clock::time_point monotonic_send_time(
- chrono::nanoseconds(pong.initial_send_time()));
- const aos::monotonic_clock::time_point monotonic_now =
- event_loop_->monotonic_now();
-
- const chrono::nanoseconds round_trip_time =
- monotonic_now - monotonic_send_time;
-
- if (pong.value() == count_) {
- VLOG(1) << "Elapsed time " << round_trip_time.count() << " ns "
- << FlatbufferToJson(&pong);
- } else {
- VLOG(1) << "Missmatched pong message";
- }
- }
-
- private:
- EventLoop *event_loop_;
- aos::Sender<examples::Ping> sender_;
- TimerHandler *timer_handle_;
- int count_ = 0;
-};
-
-} // namespace aos
-
int main(int argc, char **argv) {
FLAGS_logtostderr = true;
google::InitGoogleLogging(argv[0]);
::gflags::ParseCommandLineFlags(&argc, &argv, true);
aos::FlatbufferDetachedBuffer<aos::Configuration> config =
- aos::configuration::ReadConfig("aos/events/config.fb.json");
+ aos::configuration::ReadConfig("aos/events/pingpong_config.json");
::aos::ShmEventLoop event_loop(&config.message());
diff --git a/aos/events/ping.fbs b/aos/events/ping.fbs
new file mode 100644
index 0000000..c41498b
--- /dev/null
+++ b/aos/events/ping.fbs
@@ -0,0 +1,8 @@
+namespace aos.examples;
+
+table Ping {
+ value:int;
+ send_time:long;
+}
+
+root_type Ping;
diff --git a/aos/events/ping_lib.cc b/aos/events/ping_lib.cc
new file mode 100644
index 0000000..31c7b18
--- /dev/null
+++ b/aos/events/ping_lib.cc
@@ -0,0 +1,58 @@
+#include "aos/events/ping_lib.h"
+
+#include "aos/events/pong_generated.h"
+#include "aos/events/ping_generated.h"
+#include "aos/json_to_flatbuffer.h"
+#include "gflags/gflags.h"
+#include "glog/logging.h"
+
+DEFINE_int32(sleep_ms, 10, "Time to sleep between pings");
+
+namespace aos {
+
+namespace chrono = std::chrono;
+
+Ping::Ping(EventLoop *event_loop)
+ : event_loop_(event_loop),
+ sender_(event_loop_->MakeSender<examples::Ping>("/test")) {
+ timer_handle_ = event_loop_->AddTimer([this]() { SendPing(); });
+
+ event_loop_->MakeWatcher(
+ "/test", [this](const examples::Pong &pong) { HandlePong(pong); });
+
+ event_loop_->OnRun([this]() {
+ timer_handle_->Setup(event_loop_->monotonic_now(),
+ chrono::milliseconds(FLAGS_sleep_ms));
+ });
+
+ event_loop_->SetRuntimeRealtimePriority(5);
+}
+
+void Ping::SendPing() {
+ ++count_;
+ aos::Sender<examples::Ping>::Builder msg = sender_.MakeBuilder();
+ examples::Ping::Builder builder = msg.MakeBuilder<examples::Ping>();
+ builder.add_value(count_);
+ builder.add_send_time(
+ event_loop_->monotonic_now().time_since_epoch().count());
+ CHECK(msg.Send(builder.Finish()));
+ VLOG(2) << "Sending ping";
+}
+
+void Ping::HandlePong(const examples::Pong &pong) {
+ const aos::monotonic_clock::time_point monotonic_send_time(
+ chrono::nanoseconds(pong.initial_send_time()));
+ const aos::monotonic_clock::time_point monotonic_now =
+ event_loop_->monotonic_now();
+
+ const chrono::nanoseconds round_trip_time =
+ monotonic_now - monotonic_send_time;
+
+ if (pong.value() == count_) {
+ VLOG(1) << "Elapsed time " << round_trip_time.count() << " ns "
+ << FlatbufferToJson(&pong);
+ } else {
+ VLOG(1) << "Missmatched pong message";
+ }
+}
+} // namespace aos
diff --git a/aos/events/ping_lib.h b/aos/events/ping_lib.h
new file mode 100644
index 0000000..2cc4f5e
--- /dev/null
+++ b/aos/events/ping_lib.h
@@ -0,0 +1,34 @@
+#ifndef AOS_EVENTS_PING_LIB_H_
+#define AOS_EVENTS_PING_LIB_H_
+
+#include <chrono>
+
+#include "aos/events/event_loop.h"
+#include "aos/events/pong_generated.h"
+#include "aos/events/ping_generated.h"
+
+namespace aos {
+
+// Class which sends out a Ping message every X ms, and times the response.
+class Ping {
+ public:
+ Ping(EventLoop *event_loop);
+
+ private:
+ // Sends out the ping message with an incrementing count.
+ void SendPing();
+
+ // Receives the reply and measures the latency.
+ void HandlePong(const examples::Pong &pong);
+
+ aos::EventLoop *event_loop_;
+ aos::Sender<examples::Ping> sender_;
+ // Timer handle which sends the Ping message.
+ aos::TimerHandler *timer_handle_;
+ // Number of pings sent.
+ int count_ = 0;
+};
+
+} // namespace aos
+
+#endif // AOS_EVENTS_PING_LIB_H_
diff --git a/aos/events/pingpong_test.cc b/aos/events/pingpong_test.cc
new file mode 100644
index 0000000..0bc1f88
--- /dev/null
+++ b/aos/events/pingpong_test.cc
@@ -0,0 +1,80 @@
+#include "aos/events/ping_lib.h"
+#include "aos/events/pong_lib.h"
+#include "aos/events/simulated_event_loop.h"
+#include "aos/json_to_flatbuffer.h"
+#include "glog/logging.h"
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace testing {
+
+namespace chrono = std::chrono;
+
+class PingPongTest : public ::testing::Test {
+ public:
+ PingPongTest()
+ : config_(aos::configuration::ReadConfig(
+ "aos/events/pingpong_config.json")),
+ event_loop_factory_(&config_.message()),
+ ping_event_loop_(event_loop_factory_.MakeEventLoop()),
+ ping_(ping_event_loop_.get()),
+ pong_event_loop_(event_loop_factory_.MakeEventLoop()),
+ pong_(pong_event_loop_.get()) {}
+
+ // Config and factory.
+ // The factory is a factory for connected event loops. Each application needs
+ // a separate event loop (because you can't send a message to yourself in a
+ // single event loop). The created event loops can then send messages to each
+ // other and trigger callbacks to be called, or fetchers to receive data.
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
+ SimulatedEventLoopFactory event_loop_factory_;
+
+ // Event loop and app for Ping
+ std::unique_ptr<EventLoop> ping_event_loop_;
+ Ping ping_;
+
+ // Event loop and app for Pong
+ std::unique_ptr<EventLoop> pong_event_loop_;
+ Pong pong_;
+};
+
+// Tests that we can startup at all. This confirms that the channels are all in
+// the config.
+TEST_F(PingPongTest, Starts) {
+ // RunFor lives in the factory because all the loops need to run together, and
+ // the factory is the only object that conceptually knows about all of the
+ // loops at once.
+ event_loop_factory_.RunFor(chrono::seconds(10));
+}
+
+// Tests that the number of pong messages matches the number of ping messages.
+TEST_F(PingPongTest, AlwaysReplies) {
+ std::unique_ptr<EventLoop> test_event_loop =
+ event_loop_factory_.MakeEventLoop();
+
+ int ping_count = 0;
+ int pong_count = 0;
+
+ // Confirm that the ping value matches.
+ test_event_loop->MakeWatcher("/test",
+ [&ping_count](const examples::Ping &ping) {
+ EXPECT_EQ(ping.value(), ping_count + 1);
+ ++ping_count;
+ });
+ // Confirm that the ping and pong counts both match, and the value also
+ // matches.
+ test_event_loop->MakeWatcher(
+ "/test", [&pong_count, &ping_count](const examples::Pong &pong) {
+ EXPECT_EQ(pong.value(), pong_count + 1);
+ ++pong_count;
+ EXPECT_EQ(ping_count, pong_count);
+ });
+
+ event_loop_factory_.RunFor(chrono::seconds(10));
+
+ // We run at t=0 and t=10 seconds, which means we run 1 extra time.
+ EXPECT_EQ(ping_count, 1001);
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/events/pong.cc b/aos/events/pong.cc
index 1504bfb..339d4d7 100644
--- a/aos/events/pong.cc
+++ b/aos/events/pong.cc
@@ -1,47 +1,18 @@
-#include <chrono>
-
#include "aos/configuration.h"
-#include "aos/events/pingpong_generated.h"
+#include "aos/events/ping_generated.h"
+#include "aos/events/pong_generated.h"
+#include "aos/events/pong_lib.h"
#include "aos/events/shm_event_loop.h"
#include "aos/init.h"
-#include "aos/json_to_flatbuffer.h"
-#include "gflags/gflags.h"
#include "glog/logging.h"
-namespace aos {
-
-namespace chrono = std::chrono;
-
-class Pong {
- public:
- Pong(EventLoop *event_loop)
- : event_loop_(event_loop),
- sender_(event_loop_->MakeSender<examples::Pong>("/test")) {
- event_loop_->MakeWatcher("/test", [this](const examples::Ping &ping) {
- aos::Sender<examples::Pong>::Builder msg = sender_.MakeBuilder();
- examples::Pong::Builder builder = msg.MakeBuilder<examples::Pong>();
- builder.add_value(ping.value());
- builder.add_initial_send_time(ping.send_time());
- CHECK(msg.Send(builder.Finish()));
- });
-
- event_loop_->SetRuntimeRealtimePriority(5);
- }
-
- private:
- EventLoop *event_loop_;
- aos::Sender<examples::Pong> sender_;
-};
-
-} // namespace aos
-
int main(int argc, char **argv) {
FLAGS_logtostderr = true;
google::InitGoogleLogging(argv[0]);
::gflags::ParseCommandLineFlags(&argc, &argv, true);
aos::FlatbufferDetachedBuffer<aos::Configuration> config =
- aos::configuration::ReadConfig("aos/events/config.fb.json");
+ aos::configuration::ReadConfig("aos/events/pingpong_config.json");
::aos::ShmEventLoop event_loop(&config.message());
diff --git a/aos/events/pong.fbs b/aos/events/pong.fbs
new file mode 100644
index 0000000..115dbbe
--- /dev/null
+++ b/aos/events/pong.fbs
@@ -0,0 +1,8 @@
+namespace aos.examples;
+
+table Pong {
+ value:int;
+ initial_send_time:long;
+}
+
+root_type Pong;
diff --git a/aos/events/pong_lib.cc b/aos/events/pong_lib.cc
new file mode 100644
index 0000000..5ff38b9
--- /dev/null
+++ b/aos/events/pong_lib.cc
@@ -0,0 +1,24 @@
+#include "aos/events/pong_lib.h"
+
+#include "aos/events/event_loop.h"
+#include "aos/events/pong_generated.h"
+#include "aos/events/ping_generated.h"
+#include "glog/logging.h"
+
+namespace aos {
+
+Pong::Pong(EventLoop *event_loop)
+ : event_loop_(event_loop),
+ sender_(event_loop_->MakeSender<examples::Pong>("/test")) {
+ event_loop_->MakeWatcher("/test", [this](const examples::Ping &ping) {
+ aos::Sender<examples::Pong>::Builder msg = sender_.MakeBuilder();
+ examples::Pong::Builder builder = msg.MakeBuilder<examples::Pong>();
+ builder.add_value(ping.value());
+ builder.add_initial_send_time(ping.send_time());
+ CHECK(msg.Send(builder.Finish()));
+ });
+
+ event_loop_->SetRuntimeRealtimePriority(5);
+}
+
+} // namespace aos
diff --git a/aos/events/pong_lib.h b/aos/events/pong_lib.h
new file mode 100644
index 0000000..17d779c
--- /dev/null
+++ b/aos/events/pong_lib.h
@@ -0,0 +1,22 @@
+#ifndef AOS_EVENTS_PONG_LIB_H_
+#define AOS_EVENTS_PONG_LIB_H_
+
+#include "aos/events/event_loop.h"
+#include "aos/events/pong_generated.h"
+#include "aos/events/ping_generated.h"
+
+namespace aos {
+
+// Class which replies to a Ping message with a Pong message immediately.
+class Pong {
+ public:
+ Pong(EventLoop *event_loop);
+
+ private:
+ EventLoop *event_loop_;
+ aos::Sender<examples::Pong> sender_;
+};
+
+} // namespace aos
+
+#endif // AOS_EVENTS_PONG_LIB_H_