Add RunFor to SimulatedEventLoopFactory

Turns out we really want to be able to run for short periods of time in
simulation. So add support and tests for it.

Change-Id: I9276e1330a0d4eaea717f949516b290b1a01ed89
diff --git a/aos/events/event-loop.h b/aos/events/event-loop.h
index 2a3673e..d1885bb 100644
--- a/aos/events/event-loop.h
+++ b/aos/events/event-loop.h
@@ -127,9 +127,6 @@
   // Use this to run code once the thread goes into "real-time-mode",
   virtual void OnRun(std::function<void()>) = 0;
 
-  // Starts receiving events.
-  virtual void Run() = 0;
-
   // TODO(austin): Sort out how to switch to realtime on run.
   // virtual void RunRealtime() = 0;
 
diff --git a/aos/events/event-loop_param_test.cc b/aos/events/event-loop_param_test.cc
index aa03643..6f1e5be 100644
--- a/aos/events/event-loop_param_test.cc
+++ b/aos/events/event-loop_param_test.cc
@@ -28,7 +28,7 @@
 TEST_P(AbstractEventLoopTest, Basic) {
   auto loop1 = Make();
   auto loop2 = Make();
-  auto loop3 = Make();
+  auto loop3 = MakePrimary();
 
   auto sender = loop1->MakeSender<TestMessage>("/test");
 
@@ -54,7 +54,7 @@
   });
 
   EXPECT_FALSE(happened);
-  loop3->Run();
+  Run();
   EXPECT_TRUE(happened);
 }
 
@@ -91,7 +91,7 @@
 // Verify that Quit() works when there are multiple watchers.
 TEST_P(AbstractEventLoopTest, MultipleWatcherQuit) {
   auto loop1 = Make();
-  auto loop2 = Make();
+  auto loop2 = MakePrimary();
 
   auto sender = loop1->MakeSender<TestMessage>("/test2");
   {
@@ -105,12 +105,12 @@
     EXPECT_EQ(message.msg_value, 200);
     loop2->Exit();
   });
-  loop2->Run();
+  Run();
 }
 
 // Verify that timer intervals and duration function properly.
 TEST_P(AbstractEventLoopTest, TimerIntervalAndDuration) {
-  auto loop = Make();
+  auto loop = MakePrimary();
   ::std::vector<::aos::monotonic_clock::time_point> iteration_list;
 
   auto test_timer = loop->AddTimer([&iteration_list, &loop]() {
@@ -122,14 +122,14 @@
   // Testing that the timer thread waits for the event loop to start before
   // running
   ::std::this_thread::sleep_for(std::chrono::milliseconds(2));
-  loop->Run();
+  Run();
 
   EXPECT_EQ(iteration_list.size(), 8);
 }
 
 // Verify that we can change a timer's parameters during execution.
 TEST_P(AbstractEventLoopTest, TimerChangeParameters) {
-  auto loop = Make();
+  auto loop = MakePrimary();
   ::std::vector<::aos::monotonic_clock::time_point> iteration_list;
 
   auto test_timer = loop->AddTimer([&iteration_list, &loop]() {
@@ -145,14 +145,14 @@
   modifier_timer->Setup(loop->monotonic_now() +
                         ::std::chrono::milliseconds(45));
   EndEventLoop(loop.get(), ::std::chrono::milliseconds(150));
-  loop->Run();
+  Run();
 
   EXPECT_EQ(iteration_list.size(), 7);
 }
 
 // Verify that we can disable a timer during execution.
 TEST_P(AbstractEventLoopTest, TimerDisable) {
-  auto loop = Make();
+  auto loop = MakePrimary();
   ::std::vector<::aos::monotonic_clock::time_point> iteration_list;
 
   auto test_timer = loop->AddTimer([&iteration_list, &loop]() {
@@ -167,14 +167,14 @@
   ender_timer->Setup(loop->monotonic_now() +
                         ::std::chrono::milliseconds(45));
   EndEventLoop(loop.get(), ::std::chrono::milliseconds(150));
-  loop->Run();
+  Run();
 
   EXPECT_EQ(iteration_list.size(), 3);
 }
 
 // Verify that the send time on a message is roughly right.
 TEST_P(AbstractEventLoopTest, MessageSendTime) {
-  auto loop1 = Make();
+  auto loop1 = MakePrimary();
   auto loop2 = Make();
   auto sender = loop1->MakeSender<TestMessage>("/test");
   auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
@@ -188,7 +188,7 @@
   test_timer->Setup(loop1->monotonic_now() + ::std::chrono::seconds(1));
 
   EndEventLoop(loop1.get(), ::std::chrono::seconds(2));
-  loop1->Run();
+  Run();
 
   EXPECT_TRUE(fetcher.Fetch());
 
diff --git a/aos/events/event-loop_param_test.h b/aos/events/event-loop_param_test.h
index a4e4b8e..b5574ba 100644
--- a/aos/events/event-loop_param_test.h
+++ b/aos/events/event-loop_param_test.h
@@ -13,7 +13,14 @@
  public:
   virtual ~EventLoopTestFactory() {}
 
+  // Makes a connected event loop.
   virtual std::unique_ptr<EventLoop> Make() = 0;
+  // Makes a primary event loop.  This is the one the tests will try to use for
+  // anything blocking.
+  virtual std::unique_ptr<EventLoop> MakePrimary() = 0;
+
+  // Runs the loops until they quit.
+  virtual void Run() = 0;
 };
 
 class AbstractEventLoopTest
@@ -21,12 +28,15 @@
  public:
   AbstractEventLoopTest() { factory_.reset(GetParam()()); }
 
-  std::unique_ptr<EventLoop> Make() { return factory_->Make(); }
+  ::std::unique_ptr<EventLoop> Make() { return factory_->Make(); }
+  ::std::unique_ptr<EventLoop> MakePrimary() { return factory_->MakePrimary(); }
+
+  void Run() { return factory_->Run(); }
   // You can implement all the usual fixture class members here.
   // To access the test parameter, call GetParam() from class
   // TestWithParam<T>.
  private:
-  std::unique_ptr<EventLoopTestFactory> factory_;
+  ::std::unique_ptr<EventLoopTestFactory> factory_;
 };
 
 }  // namespace testing
diff --git a/aos/events/raw-event-loop.h b/aos/events/raw-event-loop.h
index d12187a..fd9ea8f 100644
--- a/aos/events/raw-event-loop.h
+++ b/aos/events/raw-event-loop.h
@@ -114,6 +114,8 @@
   virtual void Disable() = 0;
 };
 
+class EventScheduler;
+
 // Virtual base class for all event queue-types.
 class RawEventLoop {
  public:
@@ -124,7 +126,7 @@
 
   // The passed in function will be called when the event loop starts.
   // Use this to run code once the thread goes into "real-time-mode",
-  virtual void OnRun(std::function<void()> on_run) = 0;
+  virtual void OnRun(::std::function<void()> on_run) = 0;
 
   bool is_running() const { return is_running_.load(); }
 
@@ -132,13 +134,14 @@
   // Returns a TimerHandle for configuration of the timer
   virtual TimerHandler *AddTimer(::std::function<void()> callback) = 0;
 
-  // Starts receiving events.
-  virtual void Run() = 0;
-
   // Stops receiving events.
   virtual void Exit() = 0;
 
+  // TODO(austin): This shouldn't belong.
+  virtual void Run() = 0;
+
  protected:
+  friend class EventScheduler;
   void set_is_running(bool value) { is_running_.store(value); }
 
   // Will send new messages from (path, type).
@@ -155,7 +158,7 @@
       std::function<void(const Message *message)> watcher) = 0;
 
  private:
-  std::atomic<bool> is_running_{false};
+  ::std::atomic<bool> is_running_{false};
 };
 
 }  // namespace aos
diff --git a/aos/events/shm-event-loop_test.cc b/aos/events/shm-event-loop_test.cc
index d6f493f..0878e12 100644
--- a/aos/events/shm-event-loop_test.cc
+++ b/aos/events/shm-event-loop_test.cc
@@ -10,11 +10,23 @@
 
 class ShmEventLoopTestFactory : public EventLoopTestFactory {
  public:
-  std::unique_ptr<EventLoop> Make() override {
-    return std::unique_ptr<EventLoop>(new ShmEventLoop());
+  ::std::unique_ptr<EventLoop> Make() override {
+    return ::std::unique_ptr<EventLoop>(new ShmEventLoop());
   }
 
+  ::std::unique_ptr<EventLoop> MakePrimary() override {
+    ::std::unique_ptr<ShmEventLoop> loop =
+        ::std::unique_ptr<ShmEventLoop>(new ShmEventLoop());
+    primary_event_loop_ = loop.get();
+    return ::std::move(loop);
+  }
+
+  void Run() override { CHECK_NOTNULL(primary_event_loop_)->Run(); }
+
+ private:
   ::aos::testing::TestSharedMemory my_shm_;
+
+  ::aos::ShmEventLoop *primary_event_loop_;
 };
 
 INSTANTIATE_TEST_CASE_P(ShmEventLoopTest, AbstractEventLoopTest,
diff --git a/aos/events/simulated-event-loop.cc b/aos/events/simulated-event-loop.cc
index 02d15a6..5a9960e 100644
--- a/aos/events/simulated-event-loop.cc
+++ b/aos/events/simulated-event-loop.cc
@@ -124,8 +124,10 @@
       EventScheduler *scheduler,
       ::std::map<::std::pair<::std::string, QueueTypeInfo>, SimulatedQueue>
           *queues)
-      : scheduler_(scheduler), queues_(queues) {}
-  ~SimulatedEventLoop() override {};
+      : scheduler_(scheduler), queues_(queues) {
+    scheduler_->AddRawEventLoop(this);
+  }
+  ~SimulatedEventLoop() override { scheduler_->RemoveRawEventLoop(this); };
 
   ::aos::monotonic_clock::time_point monotonic_now() override {
     return scheduler_->monotonic_now();
@@ -150,11 +152,10 @@
     scheduler_->Schedule(scheduler_->monotonic_now(), on_run);
   }
   void Run() override {
-    set_is_running(true);
+    LOG(FATAL, "Run from the factory instead\n");
     scheduler_->Run();
   }
   void Exit() override {
-    set_is_running(false);
     scheduler_->Exit();
   }
 
@@ -180,7 +181,36 @@
   events_list_.erase(token);
 }
 
+void EventScheduler::RunFor(monotonic_clock::duration duration) {
+  const ::aos::monotonic_clock::time_point end_time =
+      monotonic_now() + duration;
+  for (RawEventLoop *event_loop : raw_event_loops_) {
+    event_loop->set_is_running(true);
+  }
+  is_running_ = true;
+  while (!events_list_.empty() && is_running_) {
+    auto iter = events_list_.begin();
+    ::aos::monotonic_clock::time_point next_time = iter->first;
+    if (next_time > end_time) {
+      break;
+    }
+    now_ = iter->first;
+    ::std::function<void()> callback = ::std::move(iter->second);
+    events_list_.erase(iter);
+    callback();
+  }
+  now_ = end_time;
+  if (!is_running_) {
+    for (RawEventLoop *event_loop : raw_event_loops_) {
+      event_loop->set_is_running(false);
+    }
+  }
+}
+
 void EventScheduler::Run() {
+  for (RawEventLoop *event_loop : raw_event_loops_) {
+    event_loop->set_is_running(true);
+  }
   is_running_ = true;
   while (!events_list_.empty() && is_running_) {
     auto iter = events_list_.begin();
@@ -189,6 +219,11 @@
     events_list_.erase(iter);
     callback();
   }
+  if (!is_running_) {
+    for (RawEventLoop *event_loop : raw_event_loops_) {
+      event_loop->set_is_running(false);
+    }
+  }
 }
 
 void SimulatedEventLoop::MakeRawWatcher(
diff --git a/aos/events/simulated-event-loop.h b/aos/events/simulated-event-loop.h
index 9a0aa6e..f2981c8 100644
--- a/aos/events/simulated-event-loop.h
+++ b/aos/events/simulated-event-loop.h
@@ -1,6 +1,7 @@
 #ifndef _AOS_EVENTS_SIMULATED_EVENT_LOOP_H_
 #define _AOS_EVENTS_SIMULATED_EVENT_LOOP_H_
 
+#include <algorithm>
 #include <map>
 #include <memory>
 #include <unordered_set>
@@ -92,8 +93,19 @@
   void Deschedule(Token token);
 
   void Run();
+  void RunFor(::aos::monotonic_clock::duration duration);
 
-  void Exit() { is_running_ = false; }
+  void Exit() {
+    is_running_ = false;
+  }
+
+  void AddRawEventLoop(RawEventLoop *event_loop) {
+    raw_event_loops_.push_back(event_loop);
+  }
+  void RemoveRawEventLoop(RawEventLoop *event_loop) {
+    raw_event_loops_.erase(::std::find(raw_event_loops_.begin(),
+                                       raw_event_loops_.end(), event_loop));
+  }
 
   ::aos::monotonic_clock::time_point monotonic_now() const { return now_; }
 
@@ -101,6 +113,7 @@
   ::aos::monotonic_clock::time_point now_ = ::aos::monotonic_clock::epoch();
   QueueType events_list_;
   bool is_running_ = false;
+  ::std::vector<RawEventLoop *> raw_event_loops_;
 };
 
 class SimulatedQueue {
@@ -147,6 +160,9 @@
   ::std::unique_ptr<EventLoop> MakeEventLoop();
 
   void Run() { scheduler_.Run(); }
+  void RunFor(monotonic_clock::duration duration) {
+    scheduler_.RunFor(duration);
+  }
 
   monotonic_clock::time_point monotonic_now() const {
     return scheduler_.monotonic_now();
diff --git a/aos/events/simulated-event-loop_test.cc b/aos/events/simulated-event-loop_test.cc
index f652fd9..41d4dde 100644
--- a/aos/events/simulated-event-loop_test.cc
+++ b/aos/events/simulated-event-loop_test.cc
@@ -10,10 +10,16 @@
 class SimulatedEventLoopTestFactory : public EventLoopTestFactory {
  public:
   ::std::unique_ptr<EventLoop> Make() override {
-    return event_loop.MakeEventLoop();
+    return event_loop_factory_.MakeEventLoop();
   }
+  ::std::unique_ptr<EventLoop> MakePrimary() override {
+    return event_loop_factory_.MakeEventLoop();
+  }
+
+  void Run() override { event_loop_factory_.Run(); }
+
  private:
-   SimulatedEventLoopFactory event_loop;
+   SimulatedEventLoopFactory event_loop_factory_;
 };
 
 INSTANTIATE_TEST_CASE_P(SimulatedEventLoopTest, AbstractEventLoopTest,
@@ -48,5 +54,44 @@
   scheduler.Run();
   EXPECT_EQ(counter, 0);
 }
+
+// Test that running for a time period with no handlers causes time to progress
+// correctly.
+TEST(SimulatedEventLoopTest, RunForNoHandlers) {
+  SimulatedEventLoopFactory simulated_event_loop_factory;
+  ::std::unique_ptr<EventLoop> event_loop =
+      simulated_event_loop_factory.MakeEventLoop();
+
+  simulated_event_loop_factory.RunFor(chrono::seconds(1));
+
+  EXPECT_EQ(::aos::monotonic_clock::epoch() + chrono::seconds(1),
+            simulated_event_loop_factory.monotonic_now());
+  EXPECT_EQ(::aos::monotonic_clock::epoch() + chrono::seconds(1),
+            event_loop->monotonic_now());
+}
+
+// Test that running for a time with a periodic handler causes time to end
+// correctly.
+TEST(SimulatedEventLoopTest, RunForTimerHandler) {
+  SimulatedEventLoopFactory simulated_event_loop_factory;
+  ::std::unique_ptr<EventLoop> event_loop =
+      simulated_event_loop_factory.MakeEventLoop();
+
+  int counter = 0;
+  auto timer = event_loop->AddTimer([&counter, &event_loop]() { ++counter; });
+  event_loop->OnRun([&event_loop, &timer] {
+    timer->Setup(event_loop->monotonic_now() + chrono::milliseconds(50),
+                 chrono::milliseconds(100));
+  });
+
+  simulated_event_loop_factory.RunFor(chrono::seconds(1));
+
+  EXPECT_EQ(::aos::monotonic_clock::epoch() + chrono::seconds(1),
+            simulated_event_loop_factory.monotonic_now());
+  EXPECT_EQ(::aos::monotonic_clock::epoch() + chrono::seconds(1),
+            event_loop->monotonic_now());
+  EXPECT_EQ(counter, 10);
+}
+
 }  // namespace testing
 }  // namespace aos