Fix SimulatedMessageBridge with slopes

We were incorrectly converting to and from the distributed clock.
Thankfully, the CHECKs in place caught it.  Add a test that exercises
them and fix it.

Change-Id: Id77674b296a6f5c32b35abc9561abd123728e271
diff --git a/aos/events/simulated_event_loop_test.cc b/aos/events/simulated_event_loop_test.cc
index cf48c34..375b8fd 100644
--- a/aos/events/simulated_event_loop_test.cc
+++ b/aos/events/simulated_event_loop_test.cc
@@ -779,5 +779,104 @@
   EXPECT_EQ(remote_timestamps_pi1_on_pi2.count(), 1001);
 }
 
+// Tests that the time offset having a slope doesn't break the world.
+// SimulatedMessageBridge has enough self consistency CHECK statements to
+// confirm, and we can can also check a message in each direction to make sure
+// it gets delivered as expected.
+TEST(SimulatedEventLoopTest, MultinodePingPongWithOffsetAndSlope) {
+  aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+      aos::configuration::ReadConfig(ConfigPrefix() +
+                                     "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());
+  NodeEventLoopFactory *pi2_factory =
+      simulated_event_loop_factory.GetNodeEventLoopFactory(pi2);
+
+  // Move the pi far into the future so the slope is significant.  And set it to
+  // something reasonable.
+  constexpr chrono::milliseconds kOffset{150100};
+  pi2_factory->SetDistributedOffset(kOffset, 1.0001);
+
+  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> pi1_counter_event_loop =
+      simulated_event_loop_factory.MakeEventLoop("pi1_counter", pi1);
+  std::unique_ptr<EventLoop> pi2_counter_event_loop =
+      simulated_event_loop_factory.MakeEventLoop("pi2_counter", pi2);
+
+  aos::Fetcher<examples::Ping> ping_on_pi1_fetcher =
+      pi1_counter_event_loop->MakeFetcher<examples::Ping>("/test");
+  aos::Fetcher<examples::Ping> ping_on_pi2_fetcher =
+      pi2_counter_event_loop->MakeFetcher<examples::Ping>("/test");
+
+  aos::Fetcher<examples::Pong> pong_on_pi2_fetcher =
+      pi2_counter_event_loop->MakeFetcher<examples::Pong>("/test");
+  aos::Fetcher<examples::Pong> pong_on_pi1_fetcher =
+      pi1_counter_event_loop->MakeFetcher<examples::Pong>("/test");
+
+  // End after a pong message comes back.  This will leave the latest messages
+  // on all channels so we can look at timestamps easily and check they make
+  // sense.
+  std::unique_ptr<EventLoop> pi1_pong_ender =
+      simulated_event_loop_factory.MakeEventLoop("pi2_counter", pi1);
+  int count = 0;
+  pi1_pong_ender->MakeWatcher(
+      "/test", [&simulated_event_loop_factory, &count](const examples::Pong &) {
+        if (++count == 100) {
+          simulated_event_loop_factory.Exit();
+        }
+      });
+
+  // Run enough that messages should be delivered.
+  simulated_event_loop_factory.Run();
+
+  // Grab the latest messages.
+  EXPECT_TRUE(ping_on_pi1_fetcher.Fetch());
+  EXPECT_TRUE(ping_on_pi2_fetcher.Fetch());
+  EXPECT_TRUE(pong_on_pi1_fetcher.Fetch());
+  EXPECT_TRUE(pong_on_pi2_fetcher.Fetch());
+
+  // Compute their time on the global distributed clock so we can compute
+  // distance betwen them.
+  const distributed_clock::time_point pi1_ping_time =
+      simulated_event_loop_factory.GetNodeEventLoopFactory(pi1)
+          ->ToDistributedClock(
+              ping_on_pi1_fetcher.context().monotonic_event_time);
+  const distributed_clock::time_point pi2_ping_time =
+      simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
+          ->ToDistributedClock(
+              ping_on_pi2_fetcher.context().monotonic_event_time);
+  const distributed_clock::time_point pi1_pong_time =
+      simulated_event_loop_factory.GetNodeEventLoopFactory(pi1)
+          ->ToDistributedClock(
+              pong_on_pi1_fetcher.context().monotonic_event_time);
+  const distributed_clock::time_point pi2_pong_time =
+      simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
+          ->ToDistributedClock(
+              pong_on_pi2_fetcher.context().monotonic_event_time);
+
+  // And confirm the delivery delay is just about exactly 150 uS for both
+  // directions like expected.  There will be a couple ns of rounding errors in
+  // the conversion functions that aren't worth accounting for right now.  This
+  // will either be really close, or really far.
+  EXPECT_GE(pi2_ping_time, chrono::microseconds(150) - chrono::nanoseconds(10) +
+                               pi1_ping_time);
+  EXPECT_LE(pi2_ping_time, chrono::microseconds(150) + chrono::nanoseconds(10) +
+                               pi1_ping_time);
+
+  EXPECT_GE(pi1_pong_time, chrono::microseconds(150) - chrono::nanoseconds(10) +
+                               pi2_pong_time);
+  EXPECT_LE(pi1_pong_time, chrono::microseconds(150) + chrono::nanoseconds(10) +
+                               pi2_pong_time);
+}
+
 }  // namespace testing
 }  // namespace aos