Add a simulated message bridge

This gives us multi-node simulations!

Change-Id: I10fa955653766a26e4d11471a6dae5b47ea5cd1c
diff --git a/aos/events/simulated_event_loop_test.cc b/aos/events/simulated_event_loop_test.cc
index b373aa6..de5cbae 100644
--- a/aos/events/simulated_event_loop_test.cc
+++ b/aos/events/simulated_event_loop_test.cc
@@ -3,6 +3,8 @@
 #include <string_view>
 
 #include "aos/events/event_loop_param_test.h"
+#include "aos/events/ping_lib.h"
+#include "aos/events/pong_lib.h"
 #include "aos/events/test_message_generated.h"
 #include "gtest/gtest.h"
 
@@ -230,5 +232,45 @@
             0.0);
 }
 
+// Tests that ping and pong work when on 2 different nodes.
+TEST(SimulatedEventLoopTest, MultinodePingPong) {
+  aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+      aos::configuration::ReadConfig(
+          "aos/events/multinode_pingpong_config.json");
+  const Node *pi1 = configuration::GetNode(&config.message(), "pi1");
+  const Node *pi2 = configuration::GetNode(&config.message(), "pi2");
+
+  SimulatedEventLoopFactory simulated_event_loop_factory(&config.message());
+
+  std::unique_ptr<EventLoop> ping_event_loop =
+      simulated_event_loop_factory.MakeEventLoop("ping", pi1);
+  Ping ping(ping_event_loop.get());
+
+  std::unique_ptr<EventLoop> pong_event_loop =
+      simulated_event_loop_factory.MakeEventLoop("pong", pi2);
+  Pong pong(pong_event_loop.get());
+
+  std::unique_ptr<EventLoop> pi2_pong_counter_event_loop =
+      simulated_event_loop_factory.MakeEventLoop("pi2_pong_counter", pi2);
+
+  int pi2_pong_count = 0;
+  pi2_pong_counter_event_loop->MakeWatcher(
+      "/test",
+      [&pi2_pong_count](const examples::Pong & /*pong*/) { ++pi2_pong_count; });
+
+  std::unique_ptr<EventLoop> pi1_pong_counter_event_loop =
+      simulated_event_loop_factory.MakeEventLoop("pi1_pong_counter", pi1);
+  int pi1_pong_count = 0;
+  pi1_pong_counter_event_loop->MakeWatcher(
+      "/test",
+      [&pi1_pong_count](const examples::Pong & /*pong*/) { ++pi1_pong_count; });
+
+  simulated_event_loop_factory.RunFor(chrono::seconds(10) +
+                                      chrono::milliseconds(5));
+
+  EXPECT_EQ(pi1_pong_count, 1001);
+  EXPECT_EQ(pi2_pong_count, 1001);
+}
+
 }  // namespace testing
 }  // namespace aos