Fix reporting errors from ShmEventLoop falling behind before Run

In this case, we try to send the timing report right before going
down.  If we haven't Run yet, that timing report may not exist.
Skip in that case to avoid a follow-on and distracting crash.

Change-Id: Icf5a81e9468b6a7068e7062e5cb0a48d671889f6
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
diff --git a/aos/events/event_loop.cc b/aos/events/event_loop.cc
index 68cc923..644d998 100644
--- a/aos/events/event_loop.cc
+++ b/aos/events/event_loop.cc
@@ -202,6 +202,11 @@
 }
 
 void EventLoop::SendTimingReport() {
+  if (!timing_report_sender_) {
+    // Timing reports are disabled, so nothing for us to do.
+    return;
+  }
+
   // We need to do a fancy dance here to get all the accounting to work right.
   // We want to copy the memory here, but then send after resetting. Otherwise
   // the send for the timing report won't be counted in the timing report.
diff --git a/aos/events/event_loop.h b/aos/events/event_loop.h
index 0e7b66a..6f51fb0 100644
--- a/aos/events/event_loop.h
+++ b/aos/events/event_loop.h
@@ -734,7 +734,9 @@
   std::vector<std::unique_ptr<PhasedLoopHandler>> phased_loops_;
   std::vector<std::unique_ptr<WatcherState>> watchers_;
 
+  // Does nothing if timing reports are disabled.
   void SendTimingReport();
+
   void UpdateTimingReport();
   void MaybeScheduleTimingReports();
 
diff --git a/aos/events/shm_event_loop_test.cc b/aos/events/shm_event_loop_test.cc
index 4c12213..7e1444b 100644
--- a/aos/events/shm_event_loop_test.cc
+++ b/aos/events/shm_event_loop_test.cc
@@ -77,10 +77,10 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(ShmEventLoopCommonTest, AbstractEventLoopTest,
-                        CommonParameters());
+                         CommonParameters());
 
-INSTANTIATE_TEST_SUITE_P(ShmEventLoopCommonDeathTest, AbstractEventLoopDeathTest,
-                        CommonParameters());
+INSTANTIATE_TEST_SUITE_P(ShmEventLoopCommonDeathTest,
+                         AbstractEventLoopDeathTest, CommonParameters());
 
 }  // namespace
 
@@ -348,16 +348,104 @@
   }
 }
 
+// Tests that the next message not being available prints a helpful error in the
+// normal case.
+TEST_P(ShmEventLoopDeathTest, NextMessageNotAvailable) {
+  auto loop1 = factory()->MakePrimary("loop1");
+  auto fetcher = loop1->MakeFetcher<TestMessage>("/test");
+  auto loop2 = factory()->Make("loop2");
+  auto sender = loop2->MakeSender<TestMessage>("/test");
+  bool ran = false;
+  loop1->OnRun([this, &sender, &fetcher, &ran]() {
+    for (int i = 0; i < 2000; ++i) {
+      auto builder = sender.MakeBuilder();
+      TestMessage::Builder test_builder(*builder.fbb());
+      test_builder.add_value(0);
+      CHECK(builder.Send(test_builder.Finish()));
+    }
+    EXPECT_DEATH(fetcher.FetchNext(),
+                 "The next message is no longer "
+                 "available.*\"/test\".*\"aos\\.TestMessage\"");
+    factory()->Exit();
+    ran = true;
+  });
+  factory()->Run();
+  EXPECT_TRUE(ran);
+}
+
+// Tests that the next message not being available prints a helpful error with
+// timing reports disabled.
+TEST_P(ShmEventLoopDeathTest, NextMessageNotAvailableNoTimingReports) {
+  auto loop1 = factory()->MakePrimary("loop1");
+  loop1->SkipTimingReport();
+  auto fetcher = loop1->MakeFetcher<TestMessage>("/test");
+  auto loop2 = factory()->Make("loop2");
+  auto sender = loop2->MakeSender<TestMessage>("/test");
+  bool ran = false;
+  loop1->OnRun([this, &sender, &fetcher, &ran]() {
+    for (int i = 0; i < 2000; ++i) {
+      auto builder = sender.MakeBuilder();
+      TestMessage::Builder test_builder(*builder.fbb());
+      test_builder.add_value(0);
+      CHECK(builder.Send(test_builder.Finish()));
+    }
+    EXPECT_DEATH(fetcher.FetchNext(),
+                 "The next message is no longer "
+                 "available.*\"/test\".*\"aos\\.TestMessage\"");
+    factory()->Exit();
+    ran = true;
+  });
+  factory()->Run();
+  EXPECT_TRUE(ran);
+}
+
+// Tests that the next message not being available prints a helpful error even
+// when Run is never called.
+TEST_P(ShmEventLoopDeathTest, NextMessageNotAvailableNoRun) {
+  auto loop1 = factory()->MakePrimary("loop1");
+  auto fetcher = loop1->MakeFetcher<TestMessage>("/test");
+  auto loop2 = factory()->Make("loop2");
+  auto sender = loop2->MakeSender<TestMessage>("/test");
+  for (int i = 0; i < 2000; ++i) {
+    auto builder = sender.MakeBuilder();
+    TestMessage::Builder test_builder(*builder.fbb());
+    test_builder.add_value(0);
+    CHECK(builder.Send(test_builder.Finish()));
+  }
+  EXPECT_DEATH(fetcher.FetchNext(),
+               "The next message is no longer "
+               "available.*\"/test\".*\"aos\\.TestMessage\"");
+}
+
+// Tests that the next message not being available prints a helpful error even
+// when Run is never called without timing reports.
+TEST_P(ShmEventLoopDeathTest, NextMessageNotAvailableNoRunNoTimingReports) {
+  auto loop1 = factory()->MakePrimary("loop1");
+  loop1->SkipTimingReport();
+  auto fetcher = loop1->MakeFetcher<TestMessage>("/test");
+  auto loop2 = factory()->Make("loop2");
+  auto sender = loop2->MakeSender<TestMessage>("/test");
+  for (int i = 0; i < 2000; ++i) {
+    auto builder = sender.MakeBuilder();
+    TestMessage::Builder test_builder(*builder.fbb());
+    test_builder.add_value(0);
+    CHECK(builder.Send(test_builder.Finish()));
+  }
+  EXPECT_DEATH(fetcher.FetchNext(),
+               "The next message is no longer "
+               "available.*\"/test\".*\"aos\\.TestMessage\"");
+}
+
 // TODO(austin): Test that missing a deadline with a timer recovers as expected.
 
 INSTANTIATE_TEST_SUITE_P(ShmEventLoopCopyTest, ShmEventLoopTest,
-                        ::testing::Values(ReadMethod::COPY));
+                         ::testing::Values(ReadMethod::COPY));
 INSTANTIATE_TEST_SUITE_P(ShmEventLoopPinTest, ShmEventLoopTest,
-                        ::testing::Values(ReadMethod::PIN));
+                         ::testing::Values(ReadMethod::PIN));
 INSTANTIATE_TEST_SUITE_P(ShmEventLoopCopyDeathTest, ShmEventLoopDeathTest,
-                        ::testing::Values(ReadMethod::COPY));
+                         ::testing::Values(ReadMethod::COPY));
 INSTANTIATE_TEST_SUITE_P(ShmEventLoopPinDeathTest, ShmEventLoopDeathTest,
-                        ::testing::Values(ReadMethod::PIN));
+                         ::testing::Values(ReadMethod::PIN));
 
 }  // namespace testing
 }  // namespace aos