Add a simple multi-node test as an example

Someone asked if there was a good multi-node example.  I couldn't find
one, so I made one.

Change-Id: Ieb40c8d835e3e3873c5224f10cf2231738e20b64
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
diff --git a/aos/events/pingpong_test.cc b/aos/events/pingpong_test.cc
index 07340f7..9c874ef 100644
--- a/aos/events/pingpong_test.cc
+++ b/aos/events/pingpong_test.cc
@@ -79,5 +79,93 @@
   EXPECT_EQ(ping_count, 1001);
 }
 
+// Multi-node ping pong test.  This test carefully mirrors the structure of the
+// single node test above to help highlight the similarities and differences.
+class MultiNodePingPongTest : public ::testing::Test {
+ public:
+  MultiNodePingPongTest()
+      : config_(aos::configuration::ReadConfig(ArtifactPath(
+            "aos/events/multinode_pingpong_test_split_config.json"))),
+        event_loop_factory_(&config_.message()),
+        pi1_(
+            configuration::GetNode(event_loop_factory_.configuration(), "pi1")),
+        pi2_(
+            configuration::GetNode(event_loop_factory_.configuration(), "pi2")),
+        ping_event_loop_(event_loop_factory_.MakeEventLoop("ping", pi1_)),
+        ping_(ping_event_loop_.get()),
+        pong_event_loop_(event_loop_factory_.MakeEventLoop("pong", pi2_)),
+        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_;
+
+  // Convenience pointers for each Node.
+  const Node *pi1_;
+  const Node *pi2_;
+
+  // 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 the number of pong messages matches the number of ping messages
+// (on both nodes this time)
+TEST_F(MultiNodePingPongTest, AlwaysReplies) {
+  // For grins, test that ping and pong appear on both nodes and match.
+  std::unique_ptr<EventLoop> pi1_test_event_loop =
+      event_loop_factory_.MakeEventLoop("test", pi1_);
+  std::unique_ptr<EventLoop> pi2_test_event_loop =
+      event_loop_factory_.MakeEventLoop("test", pi2_);
+
+  int pi1_ping_count = 0;
+  int pi2_ping_count = 0;
+  int pi1_pong_count = 0;
+  int pi2_pong_count = 0;
+
+  // Confirm that the ping value matches on both nodes.
+  pi1_test_event_loop->MakeWatcher(
+      "/test", [&pi1_ping_count](const examples::Ping &ping) {
+        EXPECT_EQ(ping.value(), pi1_ping_count + 1);
+        ++pi1_ping_count;
+      });
+  pi2_test_event_loop->MakeWatcher(
+      "/test", [&pi2_ping_count](const examples::Ping &ping) {
+        EXPECT_EQ(ping.value(), pi2_ping_count + 1);
+        ++pi2_ping_count;
+      });
+
+  // Confirm that the ping and pong counts both match, and the value also
+  // matches.
+  pi2_test_event_loop->MakeWatcher(
+      "/test", [&pi2_pong_count, &pi2_ping_count](const examples::Pong &pong) {
+        EXPECT_EQ(pong.value(), pi2_pong_count + 1);
+        ++pi2_pong_count;
+        EXPECT_EQ(pi2_ping_count, pi2_pong_count);
+      });
+  pi1_test_event_loop->MakeWatcher(
+      "/test", [&pi1_pong_count, &pi1_ping_count](const examples::Pong &pong) {
+        EXPECT_EQ(pong.value(), pi1_pong_count + 1);
+        ++pi1_pong_count;
+        EXPECT_EQ(pi1_ping_count, pi1_pong_count);
+      });
+
+  // Since forwarding takes "time", we need to run a bit longer to let pong
+  // finish the last cycle.
+  event_loop_factory_.RunFor(chrono::seconds(10) + chrono::milliseconds(5));
+
+  // We run at t=0 and t=10 seconds, which means we run 1 extra time.
+  EXPECT_EQ(pi1_ping_count, 1001);
+  EXPECT_EQ(pi2_ping_count, 1001);
+}
+
 }  // namespace testing
 }  // namespace aos