Convert aos over to flatbuffers

Everything builds, and all the tests pass.  I suspect that some entries
are missing from the config files, but those will be found pretty
quickly on startup.

There is no logging or live introspection of queue messages.

Change-Id: I496ee01ed68f202c7851bed7e8786cee30df29f5
diff --git a/aos/events/event_loop_param_test.cc b/aos/events/event_loop_param_test.cc
new file mode 100644
index 0000000..8a0f9b6
--- /dev/null
+++ b/aos/events/event_loop_param_test.cc
@@ -0,0 +1,664 @@
+#include "aos/events/event_loop_param_test.h"
+
+#include <chrono>
+
+#include "glog/logging.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "glog/logging.h"
+#include "aos/events/test_message_generated.h"
+
+namespace aos {
+namespace testing {
+namespace {
+namespace chrono = ::std::chrono;
+}  // namespace
+
+// Tests that watcher can receive messages from a sender.
+// Also tests that OnRun() works.
+TEST_P(AbstractEventLoopTest, Basic) {
+  auto loop1 = Make();
+  auto loop2 = MakePrimary();
+
+  aos::Sender<TestMessage> sender = loop1->MakeSender<TestMessage>("/test");
+
+  bool happened = false;
+
+  loop2->OnRun([&]() {
+    happened = true;
+
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(200);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  });
+
+  loop2->MakeWatcher("/test", [&](const TestMessage &message) {
+    EXPECT_EQ(message.value(), 200);
+    this->Exit();
+  });
+
+  EXPECT_FALSE(happened);
+  Run();
+  EXPECT_TRUE(happened);
+}
+
+// Tests that a fetcher can fetch from a sender.
+// Also tests that OnRun() works.
+TEST_P(AbstractEventLoopTest, FetchWithoutRun) {
+  auto loop1 = Make();
+  auto loop2 = Make();
+  auto loop3 = MakePrimary();
+
+  auto sender = loop1->MakeSender<TestMessage>("/test");
+
+  auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
+
+  EXPECT_FALSE(fetcher.Fetch());
+
+  aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+  TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+  builder.add_value(200);
+  ASSERT_TRUE(msg.Send(builder.Finish()));
+
+  EXPECT_TRUE(fetcher.Fetch());
+  ASSERT_FALSE(fetcher.get() == nullptr);
+  EXPECT_EQ(fetcher.get()->value(), 200);
+}
+
+// Tests that watcher will receive all messages sent if they are sent after
+// initialization and before running.
+TEST_P(AbstractEventLoopTest, DoubleSendAtStartup) {
+  auto loop1 = Make();
+  auto loop2 = MakePrimary();
+
+  auto sender = loop1->MakeSender<TestMessage>("/test");
+
+  ::std::vector<int> values;
+
+  loop2->MakeWatcher("/test", [&](const TestMessage &message) {
+    values.push_back(message.value());
+    if (values.size() == 2) {
+      this->Exit();
+    }
+  });
+
+  // Before Run, should be ignored.
+  {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(199);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  }
+
+  loop2->OnRun([&]() {
+    {
+      aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+      TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+      builder.add_value(200);
+      ASSERT_TRUE(msg.Send(builder.Finish()));
+    }
+    {
+      aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+      TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+      builder.add_value(201);
+      ASSERT_TRUE(msg.Send(builder.Finish()));
+    }
+  });
+
+  Run();
+
+  EXPECT_THAT(values, ::testing::ElementsAreArray({200, 201}));
+}
+
+// Tests that watcher will not receive messages sent before the watcher is
+// created.
+TEST_P(AbstractEventLoopTest, DoubleSendAfterStartup) {
+  auto loop1 = Make();
+  auto loop2 = MakePrimary();
+
+  auto sender = loop1->MakeSender<TestMessage>("/test");
+
+  ::std::vector<int> values;
+
+  {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(200);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  }
+  {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(201);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  }
+
+  loop2->MakeWatcher("/test", [&](const TestMessage &message) {
+    values.push_back(message.value());
+  });
+
+  // Add a timer to actually quit.
+  auto test_timer = loop2->AddTimer([this]() { this->Exit(); });
+  loop2->OnRun([&test_timer, &loop2]() {
+    test_timer->Setup(loop2->monotonic_now(), ::std::chrono::milliseconds(100));
+  });
+
+  Run();
+  EXPECT_EQ(0, values.size());
+}
+
+// Tests that FetchNext gets all the messages sent after it is constructed.
+TEST_P(AbstractEventLoopTest, FetchNext) {
+  auto loop1 = Make();
+  auto loop2 = MakePrimary();
+
+  auto sender = loop1->MakeSender<TestMessage>("/test");
+  auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
+
+  ::std::vector<int> values;
+
+  {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(200);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  }
+  {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(201);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  }
+
+  // Add a timer to actually quit.
+  auto test_timer = loop2->AddTimer([&fetcher, &values, this]() {
+    while (fetcher.FetchNext()) {
+      values.push_back(fetcher.get()->value());
+    }
+    this->Exit();
+  });
+
+  loop2->OnRun([&test_timer, &loop2]() {
+    test_timer->Setup(loop2->monotonic_now(), ::std::chrono::milliseconds(100));
+  });
+
+  Run();
+  EXPECT_THAT(values, ::testing::ElementsAreArray({200, 201}));
+}
+
+// Tests that FetchNext gets no messages sent before it is constructed.
+TEST_P(AbstractEventLoopTest, FetchNextAfterSend) {
+  auto loop1 = Make();
+  auto loop2 = MakePrimary();
+
+  auto sender = loop1->MakeSender<TestMessage>("/test");
+
+  ::std::vector<int> values;
+
+  {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(200);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  }
+  {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(201);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  }
+
+  auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
+
+  // Add a timer to actually quit.
+  auto test_timer = loop2->AddTimer([&fetcher, &values, this]() {
+    while (fetcher.FetchNext()) {
+      values.push_back(fetcher.get()->value());
+    }
+    this->Exit();
+  });
+
+  loop2->OnRun([&test_timer, &loop2]() {
+    test_timer->Setup(loop2->monotonic_now(), ::std::chrono::milliseconds(100));
+  });
+
+  Run();
+  EXPECT_THAT(0, values.size());
+}
+
+// Tests that Fetch returns the last message created before the loop was
+// started.
+TEST_P(AbstractEventLoopTest, FetchDataFromBeforeCreation) {
+  auto loop1 = Make();
+  auto loop2 = MakePrimary();
+
+  auto sender = loop1->MakeSender<TestMessage>("/test");
+
+  ::std::vector<int> values;
+
+  {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(200);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  }
+  {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(201);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  }
+
+  auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
+
+  // Add a timer to actually quit.
+  auto test_timer = loop2->AddTimer([&fetcher, &values, this]() {
+    if (fetcher.Fetch()) {
+      values.push_back(fetcher.get()->value());
+    }
+    // Do it again to make sure we don't double fetch.
+    if (fetcher.Fetch()) {
+      values.push_back(fetcher.get()->value());
+    }
+    this->Exit();
+  });
+
+  loop2->OnRun([&test_timer, &loop2]() {
+    test_timer->Setup(loop2->monotonic_now(), ::std::chrono::milliseconds(100));
+  });
+
+  Run();
+  EXPECT_THAT(values, ::testing::ElementsAreArray({201}));
+}
+
+// Tests that Fetch and FetchNext interleave as expected.
+TEST_P(AbstractEventLoopTest, FetchAndFetchNextTogether) {
+  auto loop1 = Make();
+  auto loop2 = MakePrimary();
+
+  auto sender = loop1->MakeSender<TestMessage>("/test");
+
+  ::std::vector<int> values;
+
+  {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(200);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  }
+  {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(201);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  }
+
+  auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
+
+  // Add a timer to actually quit.
+  auto test_timer = loop2->AddTimer([&fetcher, &values, &sender, this]() {
+    if (fetcher.Fetch()) {
+      values.push_back(fetcher.get()->value());
+    }
+
+    {
+      aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+      TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+      builder.add_value(202);
+      ASSERT_TRUE(msg.Send(builder.Finish()));
+    }
+    {
+      aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+      TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+      builder.add_value(203);
+      ASSERT_TRUE(msg.Send(builder.Finish()));
+    }
+    {
+      aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+      TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+      builder.add_value(204);
+      ASSERT_TRUE(msg.Send(builder.Finish()));
+    }
+
+    if (fetcher.FetchNext()) {
+      values.push_back(fetcher.get()->value());
+    }
+
+    if (fetcher.Fetch()) {
+      values.push_back(fetcher.get()->value());
+    }
+
+    this->Exit();
+  });
+
+  loop2->OnRun([&test_timer, &loop2]() {
+    test_timer->Setup(loop2->monotonic_now(), ::std::chrono::milliseconds(100));
+  });
+
+  Run();
+  EXPECT_THAT(values, ::testing::ElementsAreArray({201, 202, 204}));
+}
+
+
+// Tests that FetchNext behaves correctly when we get two messages in the queue
+// but don't consume the first until after the second has been sent.
+TEST_P(AbstractEventLoopTest, FetchNextTest) {
+
+  auto send_loop = Make();
+  auto fetch_loop = Make();
+  auto sender = send_loop->MakeSender<TestMessage>("/test");
+  Fetcher<TestMessage> fetcher = fetch_loop->MakeFetcher<TestMessage>("/test");
+
+  {
+      aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+      TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+      builder.add_value(100);
+      ASSERT_TRUE(msg.Send(builder.Finish()));
+  }
+
+  {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(200);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  }
+
+  ASSERT_TRUE(fetcher.FetchNext());
+  ASSERT_NE(nullptr, fetcher.get());
+  EXPECT_EQ(100, fetcher.get()->value());
+
+  ASSERT_TRUE(fetcher.FetchNext());
+  ASSERT_NE(nullptr, fetcher.get());
+  EXPECT_EQ(200, fetcher.get()->value());
+
+  // When we run off the end of the queue, expect to still have the old message:
+  ASSERT_FALSE(fetcher.FetchNext());
+  ASSERT_NE(nullptr, fetcher.get());
+  EXPECT_EQ(200, fetcher.get()->value());
+}
+
+// Verify that making a fetcher and watcher for "/test" succeeds.
+TEST_P(AbstractEventLoopTest, FetcherAndWatcher) {
+  auto loop = Make();
+  auto fetcher = loop->MakeFetcher<TestMessage>("/test");
+  loop->MakeWatcher("/test", [&](const TestMessage &) {});
+}
+
+// Verify that making 2 fetchers for "/test" succeeds.
+TEST_P(AbstractEventLoopTest, TwoFetcher) {
+  auto loop = Make();
+  auto fetcher = loop->MakeFetcher<TestMessage>("/test");
+  auto fetcher2 = loop->MakeFetcher<TestMessage>("/test");
+}
+
+// Verify that registering a watcher for an invalid channel name dies.
+TEST_P(AbstractEventLoopDeathTest, InvalidChannelName) {
+  auto loop = Make();
+  EXPECT_DEATH(
+      { loop->MakeWatcher("/test/invalid", [&](const TestMessage &) {}); },
+      "/test/invalid");
+}
+
+// Verify that registering a watcher twice for "/test" fails.
+TEST_P(AbstractEventLoopDeathTest, TwoWatcher) {
+  auto loop = Make();
+  loop->MakeWatcher("/test", [&](const TestMessage &) {});
+  EXPECT_DEATH(loop->MakeWatcher("/test", [&](const TestMessage &) {}),
+               "/test");
+}
+
+// Verify that SetRuntimeRealtimePriority fails while running.
+TEST_P(AbstractEventLoopDeathTest, SetRuntimeRealtimePriority) {
+  auto loop = MakePrimary();
+  // Confirm that runtime priority calls work when not realtime.
+  loop->SetRuntimeRealtimePriority(5);
+
+  loop->OnRun([&]() { loop->SetRuntimeRealtimePriority(5); });
+
+  EXPECT_DEATH(Run(), "realtime");
+}
+
+// Verify that registering a watcher and a sender for "/test" fails.
+TEST_P(AbstractEventLoopDeathTest, WatcherAndSender) {
+  auto loop = Make();
+  auto sender = loop->MakeSender<TestMessage>("/test");
+  EXPECT_DEATH(loop->MakeWatcher("/test", [&](const TestMessage &) {}),
+               "/test");
+}
+
+// Verify that we can't create a sender inside OnRun.
+TEST_P(AbstractEventLoopDeathTest, SenderInOnRun) {
+  auto loop1 = MakePrimary();
+
+  loop1->OnRun(
+      [&]() { auto sender = loop1->MakeSender<TestMessage>("/test2"); });
+
+  EXPECT_DEATH(Run(), "running");
+}
+
+// Verify that we can't create a watcher inside OnRun.
+TEST_P(AbstractEventLoopDeathTest, WatcherInOnRun) {
+  auto loop1 = MakePrimary();
+
+  loop1->OnRun(
+      [&]() { loop1->MakeWatcher("/test", [&](const TestMessage &) {}); });
+
+  EXPECT_DEATH(Run(), "running");
+}
+
+// Verify that Quit() works when there are multiple watchers.
+TEST_P(AbstractEventLoopTest, MultipleWatcherQuit) {
+  auto loop1 = Make();
+  auto loop2 = MakePrimary();
+
+  loop2->MakeWatcher("/test1", [&](const TestMessage &) {});
+  loop2->MakeWatcher("/test2", [&](const TestMessage &message) {
+    EXPECT_EQ(message.value(), 200);
+    this->Exit();
+  });
+
+  auto sender = loop1->MakeSender<TestMessage>("/test2");
+
+  loop2->OnRun([&]() {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(200);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  });
+
+  Run();
+}
+
+// Verify that timer intervals and duration function properly.
+TEST_P(AbstractEventLoopTest, TimerIntervalAndDuration) {
+  auto loop = MakePrimary();
+  ::std::vector<::aos::monotonic_clock::time_point> iteration_list;
+
+  auto test_timer = loop->AddTimer([&iteration_list, &loop]() {
+    iteration_list.push_back(loop->monotonic_now());
+  });
+
+  // TODO(austin): This should be an error...  Should be done in OnRun only.
+  test_timer->Setup(loop->monotonic_now(), ::std::chrono::milliseconds(20));
+  EndEventLoop(loop.get(), ::std::chrono::milliseconds(150));
+  // Testing that the timer thread waits for the event loop to start before
+  // running
+  ::std::this_thread::sleep_for(std::chrono::milliseconds(2));
+  Run();
+
+  EXPECT_EQ(iteration_list.size(), 8);
+}
+
+// Verify that we can change a timer's parameters during execution.
+TEST_P(AbstractEventLoopTest, TimerChangeParameters) {
+  auto loop = MakePrimary();
+  ::std::vector<::aos::monotonic_clock::time_point> iteration_list;
+
+  auto test_timer = loop->AddTimer([&iteration_list, &loop]() {
+    iteration_list.push_back(loop->monotonic_now());
+  });
+
+  auto modifier_timer = loop->AddTimer([&loop, &test_timer]() {
+    test_timer->Setup(loop->monotonic_now(), ::std::chrono::milliseconds(30));
+  });
+
+  test_timer->Setup(loop->monotonic_now(), ::std::chrono::milliseconds(20));
+  modifier_timer->Setup(loop->monotonic_now() +
+                        ::std::chrono::milliseconds(45));
+  EndEventLoop(loop.get(), ::std::chrono::milliseconds(150));
+  Run();
+
+  EXPECT_EQ(iteration_list.size(), 7);
+}
+
+// Verify that we can disable a timer during execution.
+TEST_P(AbstractEventLoopTest, TimerDisable) {
+  auto loop = MakePrimary();
+  ::std::vector<::aos::monotonic_clock::time_point> iteration_list;
+
+  auto test_timer = loop->AddTimer([&iteration_list, &loop]() {
+    iteration_list.push_back(loop->monotonic_now());
+  });
+
+  auto ender_timer = loop->AddTimer([&test_timer]() {
+    test_timer->Disable();
+  });
+
+  test_timer->Setup(loop->monotonic_now(), ::std::chrono::milliseconds(20));
+  ender_timer->Setup(loop->monotonic_now() +
+                        ::std::chrono::milliseconds(45));
+  EndEventLoop(loop.get(), ::std::chrono::milliseconds(150));
+  Run();
+
+  EXPECT_EQ(iteration_list.size(), 3);
+}
+
+// Verify that the send time on a message is roughly right.
+TEST_P(AbstractEventLoopTest, MessageSendTime) {
+  auto loop1 = MakePrimary();
+  auto loop2 = Make();
+  auto sender = loop1->MakeSender<TestMessage>("/test");
+  auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
+
+  auto test_timer = loop1->AddTimer([&sender]() {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(200);
+    ASSERT_TRUE(msg.Send(builder.Finish()));
+  });
+
+  loop2->MakeWatcher("/test", [&loop2](const TestMessage &msg) {
+    // Confirm that the data pointer makes sense from a watcher.
+    EXPECT_GT(&msg, loop2->context().data);
+    EXPECT_LT(&msg, reinterpret_cast<void *>(
+                        reinterpret_cast<char *>(loop2->context().data) +
+                        loop2->context().size));
+  });
+
+  test_timer->Setup(loop1->monotonic_now() + ::std::chrono::seconds(1));
+
+  EndEventLoop(loop1.get(), ::std::chrono::seconds(2));
+  Run();
+
+  EXPECT_TRUE(fetcher.Fetch());
+
+  monotonic_clock::duration monotonic_time_offset =
+      fetcher.context().monotonic_sent_time -
+      (loop1->monotonic_now() - ::std::chrono::seconds(1));
+  realtime_clock::duration realtime_time_offset =
+      fetcher.context().realtime_sent_time -
+      (loop1->realtime_now() - ::std::chrono::seconds(1));
+
+  EXPECT_TRUE(monotonic_time_offset > ::std::chrono::milliseconds(-500))
+      << ": Got "
+      << fetcher.context().monotonic_sent_time.time_since_epoch().count()
+      << " expected " << loop1->monotonic_now().time_since_epoch().count();
+  // Confirm that the data pointer makes sense.
+  EXPECT_GT(fetcher.get(), fetcher.context().data);
+  EXPECT_LT(fetcher.get(),
+            reinterpret_cast<void *>(
+                reinterpret_cast<char *>(fetcher.context().data) +
+                fetcher.context().size));
+  EXPECT_TRUE(monotonic_time_offset < ::std::chrono::milliseconds(500))
+      << ": Got "
+      << fetcher.context().monotonic_sent_time.time_since_epoch().count()
+      << " expected " << loop1->monotonic_now().time_since_epoch().count();
+
+  EXPECT_TRUE(realtime_time_offset > ::std::chrono::milliseconds(-500))
+      << ": Got "
+      << fetcher.context().realtime_sent_time.time_since_epoch().count()
+      << " expected " << loop1->realtime_now().time_since_epoch().count();
+  EXPECT_TRUE(realtime_time_offset < ::std::chrono::milliseconds(500))
+      << ": Got "
+      << fetcher.context().realtime_sent_time.time_since_epoch().count()
+      << " expected " << loop1->realtime_now().time_since_epoch().count();
+}
+
+// Tests that a couple phased loops run in a row result in the correct offset
+// and period.
+TEST_P(AbstractEventLoopTest, PhasedLoopTest) {
+  const chrono::milliseconds kOffset = chrono::milliseconds(400);
+  const int kCount = 5;
+
+  auto loop1 = MakePrimary();
+
+  // Collect up a couple of samples.
+  ::std::vector<::aos::monotonic_clock::time_point> times;
+
+  // Run kCount iterations.
+  loop1->AddPhasedLoop(
+      [&times, &loop1, this](int count) {
+        EXPECT_EQ(count, 1);
+        times.push_back(loop1->monotonic_now());
+        LOG(INFO) << times.size();
+        if (times.size() == kCount) {
+          this->Exit();
+        }
+      },
+      chrono::seconds(1), kOffset);
+
+  // Add a delay to make sure that delay during startup doesn't result in a
+  // "missed cycle".
+  SleepFor(chrono::seconds(2));
+
+  Run();
+
+  // Confirm that we got both the right number of samples, and it's odd.
+  EXPECT_EQ(times.size(), static_cast<size_t>(kCount));
+  EXPECT_EQ((times.size() % 2), 1);
+
+  // Grab the middle sample.
+  ::aos::monotonic_clock::time_point middle_time = times[times.size() / 2 + 1];
+
+  // Add up all the delays of all the times.
+  ::aos::monotonic_clock::duration sum = chrono::seconds(0);
+  for (const ::aos::monotonic_clock::time_point time : times) {
+    sum += time - middle_time;
+  }
+
+  // Average and add to the middle to find the average time.
+  sum /= times.size();
+  middle_time += sum;
+
+  // Compute the offset from the start of the second of the average time.  This
+  // should be pretty close to the offset.
+  const ::aos::monotonic_clock::duration remainder =
+      middle_time.time_since_epoch() -
+      chrono::duration_cast<chrono::seconds>(middle_time.time_since_epoch());
+
+  const chrono::milliseconds kEpsilon(100);
+  EXPECT_LT(remainder, kOffset + kEpsilon);
+  EXPECT_GT(remainder, kOffset - kEpsilon);
+
+  // Make sure that the average duration is close to 1 second.
+  EXPECT_NEAR(chrono::duration_cast<chrono::duration<double>>(times.back() -
+                                                              times.front())
+                      .count() /
+                  static_cast<double>(times.size() - 1),
+              1.0, 0.1);
+}
+
+}  // namespace testing
+}  // namespace aos