Add logic for mixed reliable/unreliable boot order

Tests are present that exercise the Unreliable vs Reliable added if
statement.  Both possible time orders are tested
Unreliable < Reliable and Unreliable > Reliable.

I am unable to produce a test that exercises the
Reliable vs Unreliable added if without introducing artificial test
conditions into logfile_sorting.cc. Manual testing shows that it
works as expected, but there is no automated test present.

Change-Id: Ia0e6e3ccc1e42763465e8506bfeb7bec0838782b
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
diff --git a/aos/events/logging/multinode_logger_test.cc b/aos/events/logging/multinode_logger_test.cc
index 5d18d3d..d9d04b2 100644
--- a/aos/events/logging/multinode_logger_test.cc
+++ b/aos/events/logging/multinode_logger_test.cc
@@ -1,3 +1,5 @@
+#include <algorithm>
+
 #include "aos/events/logging/log_reader.h"
 #include "aos/events/logging/multinode_logger_test_lib.h"
 #include "aos/events/message_counter.h"
@@ -3593,6 +3595,146 @@
   ConfirmReadable(filenames);
 }
 
+// Tests that we properly handle only one direction ever existing after a reboot
+// with mixed unreliable vs reliable, where reliable has an earlier timestamp
+// than unreliable.
+TEST(MissingDirectionTest, OneDirectionAfterRebootMixedCase1) {
+  aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+      aos::configuration::ReadConfig(ArtifactPath(
+          "aos/events/logging/multinode_pingpong_split4_mixed1_config.json"));
+  message_bridge::TestingTimeConverter time_converter(
+      configuration::NodesCount(&config.message()));
+  SimulatedEventLoopFactory event_loop_factory(&config.message());
+  event_loop_factory.SetTimeConverter(&time_converter);
+
+  NodeEventLoopFactory *const pi1 =
+      event_loop_factory.GetNodeEventLoopFactory("pi1");
+  const size_t pi1_index = configuration::GetNodeIndex(
+      event_loop_factory.configuration(), pi1->node());
+  NodeEventLoopFactory *const pi2 =
+      event_loop_factory.GetNodeEventLoopFactory("pi2");
+  const size_t pi2_index = configuration::GetNodeIndex(
+      event_loop_factory.configuration(), pi2->node());
+  std::vector<std::string> filenames;
+
+  {
+    CHECK_EQ(pi1_index, 0u);
+    CHECK_EQ(pi2_index, 1u);
+
+    time_converter.AddNextTimestamp(
+        distributed_clock::epoch(),
+        {BootTimestamp::epoch(), BootTimestamp::epoch()});
+
+    const chrono::nanoseconds reboot_time = chrono::milliseconds(5000);
+    time_converter.AddNextTimestamp(
+        distributed_clock::epoch() + reboot_time,
+        {BootTimestamp{.boot = 1, .time = monotonic_clock::epoch()},
+         BootTimestamp::epoch() + reboot_time});
+  }
+
+  const std::string kLogfile2_1 =
+      aos::testing::TestTmpDir() + "/multi_logfile2.1/";
+  util::UnlinkRecursive(kLogfile2_1);
+
+  // The following sequence using the above reference config creates
+  // a reliable message timestamp < unreliable message timestamp.
+  {
+    pi1->DisableStatistics();
+    pi2->DisableStatistics();
+
+    event_loop_factory.RunFor(chrono::milliseconds(95));
+
+    pi1->AlwaysStart<Ping>("ping");
+
+    event_loop_factory.RunFor(chrono::milliseconds(5250));
+
+    pi1->EnableStatistics();
+
+    event_loop_factory.RunFor(chrono::milliseconds(1000));
+
+    LoggerState pi2_logger = MakeLoggerState(
+        pi2, &event_loop_factory, SupportedCompressionAlgorithms()[0]);
+
+    pi2_logger.StartLogger(kLogfile2_1);
+
+    event_loop_factory.RunFor(chrono::milliseconds(5000));
+    pi2_logger.AppendAllFilenames(&filenames);
+  }
+
+  const std::vector<LogFile> sorted_parts = SortParts(filenames);
+  ConfirmReadable(filenames);
+}
+
+// Tests that we properly handle only one direction ever existing after a reboot
+// with mixed unreliable vs reliable, where unreliable has an earlier timestamp
+// than reliable.
+TEST(MissingDirectionTest, OneDirectionAfterRebootMixedCase2) {
+  aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+      aos::configuration::ReadConfig(ArtifactPath(
+          "aos/events/logging/multinode_pingpong_split4_mixed2_config.json"));
+  message_bridge::TestingTimeConverter time_converter(
+      configuration::NodesCount(&config.message()));
+  SimulatedEventLoopFactory event_loop_factory(&config.message());
+  event_loop_factory.SetTimeConverter(&time_converter);
+
+  NodeEventLoopFactory *const pi1 =
+      event_loop_factory.GetNodeEventLoopFactory("pi1");
+  const size_t pi1_index = configuration::GetNodeIndex(
+      event_loop_factory.configuration(), pi1->node());
+  NodeEventLoopFactory *const pi2 =
+      event_loop_factory.GetNodeEventLoopFactory("pi2");
+  const size_t pi2_index = configuration::GetNodeIndex(
+      event_loop_factory.configuration(), pi2->node());
+  std::vector<std::string> filenames;
+
+  {
+    CHECK_EQ(pi1_index, 0u);
+    CHECK_EQ(pi2_index, 1u);
+
+    time_converter.AddNextTimestamp(
+        distributed_clock::epoch(),
+        {BootTimestamp::epoch(), BootTimestamp::epoch()});
+
+    const chrono::nanoseconds reboot_time = chrono::milliseconds(5000);
+    time_converter.AddNextTimestamp(
+        distributed_clock::epoch() + reboot_time,
+        {BootTimestamp{.boot = 1, .time = monotonic_clock::epoch()},
+         BootTimestamp::epoch() + reboot_time});
+  }
+
+  const std::string kLogfile2_1 =
+      aos::testing::TestTmpDir() + "/multi_logfile2.1/";
+  util::UnlinkRecursive(kLogfile2_1);
+
+  // The following sequence using the above reference config creates
+  // an unreliable message timestamp < reliable message timestamp.
+  {
+    pi1->DisableStatistics();
+    pi2->DisableStatistics();
+
+    event_loop_factory.RunFor(chrono::milliseconds(95));
+
+    pi1->AlwaysStart<Ping>("ping");
+
+    event_loop_factory.RunFor(chrono::milliseconds(5250));
+
+    pi1->EnableStatistics();
+
+    event_loop_factory.RunFor(chrono::milliseconds(1000));
+
+    LoggerState pi2_logger = MakeLoggerState(
+        pi2, &event_loop_factory, SupportedCompressionAlgorithms()[0]);
+
+    pi2_logger.StartLogger(kLogfile2_1);
+
+    event_loop_factory.RunFor(chrono::milliseconds(5000));
+    pi2_logger.AppendAllFilenames(&filenames);
+  }
+
+  const std::vector<LogFile> sorted_parts = SortParts(filenames);
+  ConfirmReadable(filenames);
+}
+
 // Tests that we properly handle what used to be a time violation in one
 // direction.  This can occur when one direction goes down after sending some
 // data, but the other keeps working.  The down direction ends up resolving to a