blob: 2dc02efa6b7a078f6c22c0fe5714e7aec8d22fa5 [file] [log] [blame]
James Kuszmaul38735e82019-12-07 16:42:06 -08001#include "aos/events/logging/logger.h"
Austin Schuhe309d2a2019-11-29 13:25:21 -08002
3#include "aos/events/event_loop.h"
Austin Schuh01b4c352020-09-21 23:09:39 -07004#include "aos/events/message_counter.h"
Austin Schuhe309d2a2019-11-29 13:25:21 -08005#include "aos/events/ping_lib.h"
6#include "aos/events/pong_lib.h"
7#include "aos/events/simulated_event_loop.h"
Austin Schuh8d7e0bb2020-10-02 17:57:00 -07008#include "aos/network/timestamp_generated.h"
Austin Schuhc243b422020-10-11 15:35:08 -07009#include "aos/testing/tmpdir.h"
Austin Schuh2f8fd752020-09-01 22:38:28 -070010#include "aos/util/file.h"
Austin Schuhe309d2a2019-11-29 13:25:21 -080011#include "glog/logging.h"
Austin Schuh6f3babe2020-01-26 20:34:50 -080012#include "gmock/gmock.h"
Austin Schuhe309d2a2019-11-29 13:25:21 -080013#include "gtest/gtest.h"
14
Austin Schuh3bd4c402020-11-06 18:19:06 -080015#ifdef LZMA
16#include "aos/events/logging/lzma_encoder.h"
17#endif
18
Austin Schuhe309d2a2019-11-29 13:25:21 -080019namespace aos {
20namespace logger {
21namespace testing {
22
23namespace chrono = std::chrono;
Austin Schuh01b4c352020-09-21 23:09:39 -070024using aos::testing::MessageCounter;
Austin Schuhe309d2a2019-11-29 13:25:21 -080025
26class LoggerTest : public ::testing::Test {
27 public:
28 LoggerTest()
29 : config_(
30 aos::configuration::ReadConfig("aos/events/pingpong_config.json")),
31 event_loop_factory_(&config_.message()),
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080032 ping_event_loop_(event_loop_factory_.MakeEventLoop("ping")),
Austin Schuhe309d2a2019-11-29 13:25:21 -080033 ping_(ping_event_loop_.get()),
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080034 pong_event_loop_(event_loop_factory_.MakeEventLoop("pong")),
Austin Schuhe309d2a2019-11-29 13:25:21 -080035 pong_(pong_event_loop_.get()) {}
36
37 // Config and factory.
38 aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
39 SimulatedEventLoopFactory event_loop_factory_;
40
41 // Event loop and app for Ping
42 std::unique_ptr<EventLoop> ping_event_loop_;
43 Ping ping_;
44
45 // Event loop and app for Pong
46 std::unique_ptr<EventLoop> pong_event_loop_;
47 Pong pong_;
48};
49
Brian Silverman1f345222020-09-24 21:14:48 -070050using LoggerDeathTest = LoggerTest;
51
Austin Schuhe309d2a2019-11-29 13:25:21 -080052// Tests that we can startup at all. This confirms that the channels are all in
53// the config.
54TEST_F(LoggerTest, Starts) {
Austin Schuhc243b422020-10-11 15:35:08 -070055 const ::std::string tmpdir = aos::testing::TestTmpDir();
Austin Schuh2f8fd752020-09-01 22:38:28 -070056 const ::std::string base_name = tmpdir + "/logfile";
57 const ::std::string logfile = base_name + ".part0.bfbs";
Austin Schuhe309d2a2019-11-29 13:25:21 -080058 // Remove it.
59 unlink(logfile.c_str());
60
61 LOG(INFO) << "Logging data to " << logfile;
62
63 {
Austin Schuhe309d2a2019-11-29 13:25:21 -080064 std::unique_ptr<EventLoop> logger_event_loop =
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080065 event_loop_factory_.MakeEventLoop("logger");
Austin Schuhe309d2a2019-11-29 13:25:21 -080066
67 event_loop_factory_.RunFor(chrono::milliseconds(95));
68
Brian Silverman1f345222020-09-24 21:14:48 -070069 Logger logger(logger_event_loop.get());
70 logger.set_polling_period(std::chrono::milliseconds(100));
71 logger.StartLoggingLocalNamerOnRun(base_name);
Austin Schuhe309d2a2019-11-29 13:25:21 -080072 event_loop_factory_.RunFor(chrono::milliseconds(20000));
73 }
74
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -080075 // Even though it doesn't make any difference here, exercise the logic for
76 // passing in a separate config.
77 LogReader reader(logfile, &config_.message());
Austin Schuhe309d2a2019-11-29 13:25:21 -080078
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -080079 // Confirm that we can remap logged channels to point to new buses.
80 reader.RemapLoggedChannel<aos::examples::Ping>("/test", "/original");
Austin Schuhe309d2a2019-11-29 13:25:21 -080081
Austin Schuh15649d62019-12-28 16:36:38 -080082 // This sends out the fetched messages and advances time to the start of the
83 // log file.
James Kuszmaul84ff3e52020-01-03 19:48:53 -080084 reader.Register();
Austin Schuhe309d2a2019-11-29 13:25:21 -080085
Austin Schuh6f3babe2020-01-26 20:34:50 -080086 EXPECT_THAT(reader.Nodes(), ::testing::ElementsAre(nullptr));
James Kuszmaul84ff3e52020-01-03 19:48:53 -080087
Austin Schuhe309d2a2019-11-29 13:25:21 -080088 std::unique_ptr<EventLoop> test_event_loop =
James Kuszmaul84ff3e52020-01-03 19:48:53 -080089 reader.event_loop_factory()->MakeEventLoop("log_reader");
Austin Schuhe309d2a2019-11-29 13:25:21 -080090
91 int ping_count = 10;
92 int pong_count = 10;
93
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -080094 // Confirm that the ping value matches in the remapped channel location.
95 test_event_loop->MakeWatcher("/original/test",
Austin Schuhe309d2a2019-11-29 13:25:21 -080096 [&ping_count](const examples::Ping &ping) {
97 EXPECT_EQ(ping.value(), ping_count + 1);
98 ++ping_count;
99 });
100 // Confirm that the ping and pong counts both match, and the value also
101 // matches.
102 test_event_loop->MakeWatcher(
103 "/test", [&pong_count, &ping_count](const examples::Pong &pong) {
104 EXPECT_EQ(pong.value(), pong_count + 1);
105 ++pong_count;
106 EXPECT_EQ(ping_count, pong_count);
107 });
108
James Kuszmaul84ff3e52020-01-03 19:48:53 -0800109 reader.event_loop_factory()->RunFor(std::chrono::seconds(100));
Austin Schuhe309d2a2019-11-29 13:25:21 -0800110 EXPECT_EQ(ping_count, 2010);
Austin Schuhe309d2a2019-11-29 13:25:21 -0800111}
112
Brian Silverman1f345222020-09-24 21:14:48 -0700113// Tests calling StartLogging twice.
114TEST_F(LoggerDeathTest, ExtraStart) {
Austin Schuhc6f8f1b2020-12-02 23:23:39 -0800115 const ::std::string tmpdir = aos::testing::TestTmpDir();
Brian Silverman1f345222020-09-24 21:14:48 -0700116 const ::std::string base_name1 = tmpdir + "/logfile1";
117 const ::std::string logfile1 = base_name1 + ".part0.bfbs";
118 const ::std::string base_name2 = tmpdir + "/logfile2";
119 const ::std::string logfile2 = base_name2 + ".part0.bfbs";
120 unlink(logfile1.c_str());
121 unlink(logfile2.c_str());
122
123 LOG(INFO) << "Logging data to " << logfile1 << " then " << logfile2;
124
125 {
126 std::unique_ptr<EventLoop> logger_event_loop =
127 event_loop_factory_.MakeEventLoop("logger");
128
129 event_loop_factory_.RunFor(chrono::milliseconds(95));
130
131 Logger logger(logger_event_loop.get());
132 logger.set_polling_period(std::chrono::milliseconds(100));
133 logger_event_loop->OnRun(
134 [base_name1, base_name2, &logger_event_loop, &logger]() {
135 logger.StartLogging(std::make_unique<LocalLogNamer>(
136 base_name1, logger_event_loop->node()));
137 EXPECT_DEATH(logger.StartLogging(std::make_unique<LocalLogNamer>(
138 base_name2, logger_event_loop->node())),
139 "Already logging");
140 });
141 event_loop_factory_.RunFor(chrono::milliseconds(20000));
142 }
143}
144
145// Tests calling StopLogging twice.
146TEST_F(LoggerDeathTest, ExtraStop) {
Austin Schuhc6f8f1b2020-12-02 23:23:39 -0800147 const ::std::string tmpdir = aos::testing::TestTmpDir();
Brian Silverman1f345222020-09-24 21:14:48 -0700148 const ::std::string base_name = tmpdir + "/logfile";
149 const ::std::string logfile = base_name + ".part0.bfbs";
150 // Remove it.
151 unlink(logfile.c_str());
152
153 LOG(INFO) << "Logging data to " << logfile;
154
155 {
156 std::unique_ptr<EventLoop> logger_event_loop =
157 event_loop_factory_.MakeEventLoop("logger");
158
159 event_loop_factory_.RunFor(chrono::milliseconds(95));
160
161 Logger logger(logger_event_loop.get());
162 logger.set_polling_period(std::chrono::milliseconds(100));
163 logger_event_loop->OnRun([base_name, &logger_event_loop, &logger]() {
164 logger.StartLogging(std::make_unique<LocalLogNamer>(
165 base_name, logger_event_loop->node()));
166 logger.StopLogging(aos::monotonic_clock::min_time);
167 EXPECT_DEATH(logger.StopLogging(aos::monotonic_clock::min_time),
168 "Not logging right now");
169 });
170 event_loop_factory_.RunFor(chrono::milliseconds(20000));
171 }
172}
173
174// Tests that we can startup twice.
175TEST_F(LoggerTest, StartsTwice) {
Austin Schuhc6f8f1b2020-12-02 23:23:39 -0800176 const ::std::string tmpdir = aos::testing::TestTmpDir();
Brian Silverman1f345222020-09-24 21:14:48 -0700177 const ::std::string base_name1 = tmpdir + "/logfile1";
178 const ::std::string logfile1 = base_name1 + ".part0.bfbs";
179 const ::std::string base_name2 = tmpdir + "/logfile2";
180 const ::std::string logfile2 = base_name2 + ".part0.bfbs";
181 unlink(logfile1.c_str());
182 unlink(logfile2.c_str());
183
184 LOG(INFO) << "Logging data to " << logfile1 << " then " << logfile2;
185
186 {
187 std::unique_ptr<EventLoop> logger_event_loop =
188 event_loop_factory_.MakeEventLoop("logger");
189
190 event_loop_factory_.RunFor(chrono::milliseconds(95));
191
192 Logger logger(logger_event_loop.get());
193 logger.set_polling_period(std::chrono::milliseconds(100));
194 logger.StartLogging(
195 std::make_unique<LocalLogNamer>(base_name1, logger_event_loop->node()));
196 event_loop_factory_.RunFor(chrono::milliseconds(10000));
197 logger.StopLogging(logger_event_loop->monotonic_now());
198 event_loop_factory_.RunFor(chrono::milliseconds(10000));
199 logger.StartLogging(
200 std::make_unique<LocalLogNamer>(base_name2, logger_event_loop->node()));
201 event_loop_factory_.RunFor(chrono::milliseconds(10000));
202 }
203
204 for (const auto &logfile :
205 {std::make_tuple(logfile1, 10), std::make_tuple(logfile2, 2010)}) {
206 SCOPED_TRACE(std::get<0>(logfile));
207 LogReader reader(std::get<0>(logfile));
208 reader.Register();
209
210 EXPECT_THAT(reader.Nodes(), ::testing::ElementsAre(nullptr));
211
212 std::unique_ptr<EventLoop> test_event_loop =
213 reader.event_loop_factory()->MakeEventLoop("log_reader");
214
215 int ping_count = std::get<1>(logfile);
216 int pong_count = std::get<1>(logfile);
217
218 // Confirm that the ping and pong counts both match, and the value also
219 // matches.
220 test_event_loop->MakeWatcher("/test",
221 [&ping_count](const examples::Ping &ping) {
222 EXPECT_EQ(ping.value(), ping_count + 1);
223 ++ping_count;
224 });
225 test_event_loop->MakeWatcher(
226 "/test", [&pong_count, &ping_count](const examples::Pong &pong) {
227 EXPECT_EQ(pong.value(), pong_count + 1);
228 ++pong_count;
229 EXPECT_EQ(ping_count, pong_count);
230 });
231
232 reader.event_loop_factory()->RunFor(std::chrono::seconds(100));
233 EXPECT_EQ(ping_count, std::get<1>(logfile) + 1000);
234 }
235}
236
Austin Schuhfa895892020-01-07 20:07:41 -0800237// Tests that we can read and write rotated log files.
238TEST_F(LoggerTest, RotatedLogFile) {
Austin Schuhc6f8f1b2020-12-02 23:23:39 -0800239 const ::std::string tmpdir = aos::testing::TestTmpDir();
Austin Schuh2f8fd752020-09-01 22:38:28 -0700240 const ::std::string base_name = tmpdir + "/logfile";
241 const ::std::string logfile0 = base_name + ".part0.bfbs";
242 const ::std::string logfile1 = base_name + ".part1.bfbs";
Austin Schuhfa895892020-01-07 20:07:41 -0800243 // Remove it.
244 unlink(logfile0.c_str());
245 unlink(logfile1.c_str());
246
247 LOG(INFO) << "Logging data to " << logfile0 << " and " << logfile1;
248
249 {
Austin Schuhfa895892020-01-07 20:07:41 -0800250 std::unique_ptr<EventLoop> logger_event_loop =
251 event_loop_factory_.MakeEventLoop("logger");
252
253 event_loop_factory_.RunFor(chrono::milliseconds(95));
254
Brian Silverman1f345222020-09-24 21:14:48 -0700255 Logger logger(logger_event_loop.get());
256 logger.set_polling_period(std::chrono::milliseconds(100));
257 logger.StartLoggingLocalNamerOnRun(base_name);
Austin Schuhfa895892020-01-07 20:07:41 -0800258 event_loop_factory_.RunFor(chrono::milliseconds(10000));
Austin Schuh2f8fd752020-09-01 22:38:28 -0700259 logger.Rotate();
Austin Schuhfa895892020-01-07 20:07:41 -0800260 event_loop_factory_.RunFor(chrono::milliseconds(10000));
261 }
262
Austin Schuh64fab802020-09-09 22:47:47 -0700263 {
264 // Confirm that the UUIDs match for both the parts and the logger, and the
265 // parts_index increments.
Austin Schuhadd6eb32020-11-09 21:24:26 -0800266 std::vector<SizePrefixedFlatbufferVector<LogFileHeader>> log_header;
Austin Schuh64fab802020-09-09 22:47:47 -0700267 for (std::string_view f : {logfile0, logfile1}) {
Austin Schuh3bd4c402020-11-06 18:19:06 -0800268 log_header.emplace_back(ReadHeader(f).value());
Austin Schuh64fab802020-09-09 22:47:47 -0700269 }
270
Brian Silvermanae7c0332020-09-30 16:58:23 -0700271 EXPECT_EQ(log_header[0].message().log_event_uuid()->string_view(),
272 log_header[1].message().log_event_uuid()->string_view());
Austin Schuh64fab802020-09-09 22:47:47 -0700273 EXPECT_EQ(log_header[0].message().parts_uuid()->string_view(),
274 log_header[1].message().parts_uuid()->string_view());
275
276 EXPECT_EQ(log_header[0].message().parts_index(), 0);
277 EXPECT_EQ(log_header[1].message().parts_index(), 1);
278 }
279
Austin Schuhfa895892020-01-07 20:07:41 -0800280 // Even though it doesn't make any difference here, exercise the logic for
281 // passing in a separate config.
282 LogReader reader(std::vector<std::string>{logfile0, logfile1},
283 &config_.message());
284
285 // Confirm that we can remap logged channels to point to new buses.
286 reader.RemapLoggedChannel<aos::examples::Ping>("/test", "/original");
287
288 // This sends out the fetched messages and advances time to the start of the
289 // log file.
290 reader.Register();
291
Austin Schuh6f3babe2020-01-26 20:34:50 -0800292 EXPECT_THAT(reader.Nodes(), ::testing::ElementsAre(nullptr));
Austin Schuhfa895892020-01-07 20:07:41 -0800293
294 std::unique_ptr<EventLoop> test_event_loop =
295 reader.event_loop_factory()->MakeEventLoop("log_reader");
296
297 int ping_count = 10;
298 int pong_count = 10;
299
300 // Confirm that the ping value matches in the remapped channel location.
301 test_event_loop->MakeWatcher("/original/test",
302 [&ping_count](const examples::Ping &ping) {
303 EXPECT_EQ(ping.value(), ping_count + 1);
304 ++ping_count;
305 });
306 // Confirm that the ping and pong counts both match, and the value also
307 // matches.
308 test_event_loop->MakeWatcher(
309 "/test", [&pong_count, &ping_count](const examples::Pong &pong) {
310 EXPECT_EQ(pong.value(), pong_count + 1);
311 ++pong_count;
312 EXPECT_EQ(ping_count, pong_count);
313 });
314
315 reader.event_loop_factory()->RunFor(std::chrono::seconds(100));
316 EXPECT_EQ(ping_count, 2010);
317}
318
Austin Schuh4c4e0092019-12-22 16:18:03 -0800319// Tests that a large number of messages per second doesn't overwhelm writev.
320TEST_F(LoggerTest, ManyMessages) {
Austin Schuhc6f8f1b2020-12-02 23:23:39 -0800321 const ::std::string tmpdir = aos::testing::TestTmpDir();
Austin Schuh2f8fd752020-09-01 22:38:28 -0700322 const ::std::string base_name = tmpdir + "/logfile";
323 const ::std::string logfile = base_name + ".part0.bfbs";
Austin Schuh4c4e0092019-12-22 16:18:03 -0800324 // Remove the log file.
325 unlink(logfile.c_str());
326
327 LOG(INFO) << "Logging data to " << logfile;
Austin Schuh4c3b9702020-08-30 11:34:55 -0700328 ping_.set_quiet(true);
Austin Schuh4c4e0092019-12-22 16:18:03 -0800329
330 {
Austin Schuh4c4e0092019-12-22 16:18:03 -0800331 std::unique_ptr<EventLoop> logger_event_loop =
332 event_loop_factory_.MakeEventLoop("logger");
333
334 std::unique_ptr<EventLoop> ping_spammer_event_loop =
335 event_loop_factory_.MakeEventLoop("ping_spammer");
336 aos::Sender<examples::Ping> ping_sender =
337 ping_spammer_event_loop->MakeSender<examples::Ping>("/test");
338
339 aos::TimerHandler *timer_handler =
340 ping_spammer_event_loop->AddTimer([&ping_sender]() {
341 aos::Sender<examples::Ping>::Builder builder =
342 ping_sender.MakeBuilder();
343 examples::Ping::Builder ping_builder =
344 builder.MakeBuilder<examples::Ping>();
345 CHECK(builder.Send(ping_builder.Finish()));
346 });
347
348 // 100 ms / 0.05 ms -> 2000 messages. Should be enough to crash it.
349 ping_spammer_event_loop->OnRun([&ping_spammer_event_loop, timer_handler]() {
350 timer_handler->Setup(ping_spammer_event_loop->monotonic_now(),
351 chrono::microseconds(50));
352 });
353
Brian Silverman1f345222020-09-24 21:14:48 -0700354 Logger logger(logger_event_loop.get());
355 logger.set_polling_period(std::chrono::milliseconds(100));
356 logger.StartLoggingLocalNamerOnRun(base_name);
Austin Schuh4c4e0092019-12-22 16:18:03 -0800357
358 event_loop_factory_.RunFor(chrono::milliseconds(1000));
359 }
360}
361
Austin Schuh15649d62019-12-28 16:36:38 -0800362class MultinodeLoggerTest : public ::testing::Test {
363 public:
364 MultinodeLoggerTest()
365 : config_(aos::configuration::ReadConfig(
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800366 "aos/events/logging/multinode_pingpong_config.json")),
Austin Schuhac0771c2020-01-07 18:36:30 -0800367 event_loop_factory_(&config_.message()),
Austin Schuhcde938c2020-02-02 17:30:07 -0800368 pi1_(
369 configuration::GetNode(event_loop_factory_.configuration(), "pi1")),
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700370 pi2_(
371 configuration::GetNode(event_loop_factory_.configuration(), "pi2")),
Austin Schuhc6f8f1b2020-12-02 23:23:39 -0800372 tmp_dir_(aos::testing::TestTmpDir()),
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700373 logfile_base_(tmp_dir_ + "/multi_logfile"),
Austin Schuh2f8fd752020-09-01 22:38:28 -0700374 logfiles_(
375 {logfile_base_ + "_pi1_data.part0.bfbs",
376 logfile_base_ + "_pi2_data/test/aos.examples.Pong.part0.bfbs",
377 logfile_base_ + "_pi2_data/test/aos.examples.Pong.part1.bfbs",
378 logfile_base_ + "_pi2_data.part0.bfbs",
379 logfile_base_ + "_timestamps/pi1/aos/remote_timestamps/pi2/"
380 "aos.logger.MessageHeader.part0.bfbs",
381 logfile_base_ + "_timestamps/pi1/aos/remote_timestamps/pi2/"
382 "aos.logger.MessageHeader.part1.bfbs",
383 logfile_base_ + "_timestamps/pi2/aos/remote_timestamps/pi1/"
384 "aos.logger.MessageHeader.part0.bfbs",
385 logfile_base_ + "_timestamps/pi2/aos/remote_timestamps/pi1/"
386 "aos.logger.MessageHeader.part1.bfbs",
387 logfile_base_ +
388 "_pi1_data/pi1/aos/aos.message_bridge.Timestamp.part0.bfbs",
389 logfile_base_ +
390 "_pi1_data/pi1/aos/aos.message_bridge.Timestamp.part1.bfbs",
391 logfile_base_ +
392 "_pi2_data/pi2/aos/aos.message_bridge.Timestamp.part0.bfbs",
393 logfile_base_ +
394 "_pi2_data/pi2/aos/aos.message_bridge.Timestamp.part1.bfbs"}),
395 structured_logfiles_{
396 std::vector<std::string>{logfiles_[0]},
397 std::vector<std::string>{logfiles_[1], logfiles_[2]},
398 std::vector<std::string>{logfiles_[3]},
399 std::vector<std::string>{logfiles_[4], logfiles_[5]},
400 std::vector<std::string>{logfiles_[6], logfiles_[7]},
401 std::vector<std::string>{logfiles_[8], logfiles_[9]},
402 std::vector<std::string>{logfiles_[10], logfiles_[11]}},
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700403 ping_event_loop_(event_loop_factory_.MakeEventLoop("ping", pi1_)),
404 ping_(ping_event_loop_.get()),
405 pong_event_loop_(event_loop_factory_.MakeEventLoop("pong", pi2_)),
406 pong_(pong_event_loop_.get()) {
407 // Go through and remove the logfiles if they already exist.
408 for (const auto file : logfiles_) {
409 unlink(file.c_str());
Austin Schuhc6f8f1b2020-12-02 23:23:39 -0800410 unlink((file + ".xz").c_str());
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700411 }
412
413 LOG(INFO) << "Logging data to " << logfiles_[0] << ", " << logfiles_[1]
414 << " and " << logfiles_[2];
415 }
416
417 struct LoggerState {
418 std::unique_ptr<EventLoop> event_loop;
419 std::unique_ptr<Logger> logger;
420 };
421
Austin Schuh8d7e0bb2020-10-02 17:57:00 -0700422 LoggerState MakeLogger(const Node *node,
423 SimulatedEventLoopFactory *factory = nullptr) {
424 if (factory == nullptr) {
425 factory = &event_loop_factory_;
426 }
427 return {factory->MakeEventLoop("logger", node), {}};
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700428 }
429
Austin Schuh3bd4c402020-11-06 18:19:06 -0800430 void StartLogger(LoggerState *logger, std::string logfile_base = "",
431 bool compress = false) {
Austin Schuh8d7e0bb2020-10-02 17:57:00 -0700432 if (logfile_base.empty()) {
433 logfile_base = logfile_base_;
434 }
435
Brian Silverman1f345222020-09-24 21:14:48 -0700436 logger->logger = std::make_unique<Logger>(logger->event_loop.get());
437 logger->logger->set_polling_period(std::chrono::milliseconds(100));
Austin Schuh3bd4c402020-11-06 18:19:06 -0800438 logger->event_loop->OnRun([logger, logfile_base, compress]() {
439 std::unique_ptr<MultiNodeLogNamer> namer =
440 std::make_unique<MultiNodeLogNamer>(
441 logfile_base, logger->event_loop->configuration(),
442 logger->event_loop->node());
443 if (compress) {
444#ifdef LZMA
445 namer->set_extension(".xz");
446 namer->set_encoder_factory(
447 []() { return std::make_unique<aos::logger::LzmaEncoder>(3); });
448#else
449 LOG(FATAL) << "Compression unsupported";
450#endif
451 }
452
453 logger->logger->StartLogging(std::move(namer));
Brian Silverman1f345222020-09-24 21:14:48 -0700454 });
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700455 }
Austin Schuh15649d62019-12-28 16:36:38 -0800456
Austin Schuh3bd4c402020-11-06 18:19:06 -0800457 void VerifyParts(const std::vector<LogFile> &sorted_parts,
458 const std::vector<std::string> &corrupted_parts = {}) {
459 EXPECT_EQ(sorted_parts.size(), 2u);
460
461 // Count up the number of UUIDs and make sure they are what we expect as a
462 // sanity check.
463 std::set<std::string> log_event_uuids;
464 std::set<std::string> parts_uuids;
465 std::set<std::string> both_uuids;
466
467 size_t missing_rt_count = 0;
468
469 std::vector<std::string> logger_nodes;
470 for (const LogFile &log_file : sorted_parts) {
471 EXPECT_FALSE(log_file.log_event_uuid.empty());
472 log_event_uuids.insert(log_file.log_event_uuid);
473 logger_nodes.emplace_back(log_file.logger_node);
474 both_uuids.insert(log_file.log_event_uuid);
475
476 for (const LogParts &part : log_file.parts) {
477 EXPECT_NE(part.monotonic_start_time, aos::monotonic_clock::min_time)
478 << ": " << part;
479 missing_rt_count +=
480 part.realtime_start_time == aos::realtime_clock::min_time;
481
482 EXPECT_TRUE(log_event_uuids.find(part.log_event_uuid) !=
483 log_event_uuids.end());
484 EXPECT_NE(part.node, "");
485 parts_uuids.insert(part.parts_uuid);
486 both_uuids.insert(part.parts_uuid);
487 }
488 }
489
490 // We won't have RT timestamps for 5 log files. We don't log the RT start
491 // time on remote nodes because we don't know it and would be guessing. And
492 // the log reader can actually do a better job.
493 EXPECT_EQ(missing_rt_count, 5u);
494
495 EXPECT_EQ(log_event_uuids.size(), 2u);
496 EXPECT_EQ(parts_uuids.size(), ToLogReaderVector(sorted_parts).size());
497 EXPECT_EQ(log_event_uuids.size() + parts_uuids.size(), both_uuids.size());
498
499 // Test that each list of parts is in order. Don't worry about the ordering
500 // between part file lists though.
501 // (inner vectors all need to be in order, but outer one doesn't matter).
502 EXPECT_THAT(ToLogReaderVector(sorted_parts),
503 ::testing::UnorderedElementsAreArray(structured_logfiles_));
504
505 EXPECT_THAT(logger_nodes, ::testing::UnorderedElementsAre("pi1", "pi2"));
506
507 EXPECT_NE(sorted_parts[0].realtime_start_time,
508 aos::realtime_clock::min_time);
509 EXPECT_NE(sorted_parts[1].realtime_start_time,
510 aos::realtime_clock::min_time);
511
512 EXPECT_NE(sorted_parts[0].monotonic_start_time,
513 aos::monotonic_clock::min_time);
514 EXPECT_NE(sorted_parts[1].monotonic_start_time,
515 aos::monotonic_clock::min_time);
516
517 EXPECT_THAT(sorted_parts[0].corrupted, ::testing::Eq(corrupted_parts));
518 EXPECT_THAT(sorted_parts[1].corrupted, ::testing::Eq(corrupted_parts));
519 }
520
521 void AddExtension(std::string_view extension) {
522 std::transform(logfiles_.begin(), logfiles_.end(), logfiles_.begin(),
523 [extension](const std::string &in) {
524 return absl::StrCat(in, extension);
525 });
526
527 std::transform(structured_logfiles_.begin(), structured_logfiles_.end(),
528 structured_logfiles_.begin(),
529 [extension](std::vector<std::string> in) {
530 std::transform(in.begin(), in.end(), in.begin(),
531 [extension](const std::string &in_str) {
532 return absl::StrCat(in_str, extension);
533 });
534 return in;
535 });
536 }
537
Austin Schuh15649d62019-12-28 16:36:38 -0800538 // Config and factory.
539 aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
540 SimulatedEventLoopFactory event_loop_factory_;
541
Austin Schuhcde938c2020-02-02 17:30:07 -0800542 const Node *pi1_;
543 const Node *pi2_;
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700544
545 std::string tmp_dir_;
546 std::string logfile_base_;
Austin Schuh2f8fd752020-09-01 22:38:28 -0700547 std::vector<std::string> logfiles_;
548
549 std::vector<std::vector<std::string>> structured_logfiles_;
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700550
551 std::unique_ptr<EventLoop> ping_event_loop_;
552 Ping ping_;
553 std::unique_ptr<EventLoop> pong_event_loop_;
554 Pong pong_;
Austin Schuh15649d62019-12-28 16:36:38 -0800555};
556
Austin Schuh391e3172020-09-01 22:48:18 -0700557// Counts the number of messages on a channel. Returns (channel name, channel
558// type, count) for every message matching matcher()
559std::vector<std::tuple<std::string, std::string, int>> CountChannelsMatching(
Austin Schuh6f3babe2020-01-26 20:34:50 -0800560 std::string_view filename,
561 std::function<bool(const MessageHeader *)> matcher) {
562 MessageReader message_reader(filename);
563 std::vector<int> counts(
564 message_reader.log_file_header()->configuration()->channels()->size(), 0);
Austin Schuh15649d62019-12-28 16:36:38 -0800565
Austin Schuh6f3babe2020-01-26 20:34:50 -0800566 while (true) {
Austin Schuhadd6eb32020-11-09 21:24:26 -0800567 std::optional<SizePrefixedFlatbufferVector<MessageHeader>> msg =
Austin Schuh6f3babe2020-01-26 20:34:50 -0800568 message_reader.ReadMessage();
569 if (!msg) {
570 break;
571 }
572
573 if (matcher(&msg.value().message())) {
574 counts[msg.value().message().channel_index()]++;
575 }
576 }
577
Austin Schuh391e3172020-09-01 22:48:18 -0700578 std::vector<std::tuple<std::string, std::string, int>> result;
Austin Schuh6f3babe2020-01-26 20:34:50 -0800579 int channel = 0;
580 for (size_t i = 0; i < counts.size(); ++i) {
581 if (counts[i] != 0) {
Austin Schuh391e3172020-09-01 22:48:18 -0700582 const Channel *channel =
583 message_reader.log_file_header()->configuration()->channels()->Get(i);
584 result.push_back(std::make_tuple(channel->name()->str(),
585 channel->type()->str(), counts[i]));
Austin Schuh6f3babe2020-01-26 20:34:50 -0800586 }
587 ++channel;
588 }
589
590 return result;
591}
592
593// Counts the number of messages (channel, count) for all data messages.
Austin Schuh391e3172020-09-01 22:48:18 -0700594std::vector<std::tuple<std::string, std::string, int>> CountChannelsData(
595 std::string_view filename) {
Austin Schuh6f3babe2020-01-26 20:34:50 -0800596 return CountChannelsMatching(filename, [](const MessageHeader *msg) {
597 if (msg->has_data()) {
598 CHECK(!msg->has_monotonic_remote_time());
599 CHECK(!msg->has_realtime_remote_time());
600 CHECK(!msg->has_remote_queue_index());
601 return true;
602 }
603 return false;
604 });
605}
606
607// Counts the number of messages (channel, count) for all timestamp messages.
Austin Schuh391e3172020-09-01 22:48:18 -0700608std::vector<std::tuple<std::string, std::string, int>> CountChannelsTimestamp(
Austin Schuh6f3babe2020-01-26 20:34:50 -0800609 std::string_view filename) {
610 return CountChannelsMatching(filename, [](const MessageHeader *msg) {
611 if (!msg->has_data()) {
612 CHECK(msg->has_monotonic_remote_time());
613 CHECK(msg->has_realtime_remote_time());
614 CHECK(msg->has_remote_queue_index());
615 return true;
616 }
617 return false;
618 });
619}
620
Austin Schuhcde938c2020-02-02 17:30:07 -0800621// Tests that we can write and read simple multi-node log files.
622TEST_F(MultinodeLoggerTest, SimpleMultiNode) {
Austin Schuh15649d62019-12-28 16:36:38 -0800623 {
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700624 LoggerState pi1_logger = MakeLogger(pi1_);
625 LoggerState pi2_logger = MakeLogger(pi2_);
Austin Schuh15649d62019-12-28 16:36:38 -0800626
627 event_loop_factory_.RunFor(chrono::milliseconds(95));
628
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700629 StartLogger(&pi1_logger);
630 StartLogger(&pi2_logger);
Austin Schuhcde938c2020-02-02 17:30:07 -0800631
Austin Schuh15649d62019-12-28 16:36:38 -0800632 event_loop_factory_.RunFor(chrono::milliseconds(20000));
633 }
634
Austin Schuh6f3babe2020-01-26 20:34:50 -0800635 {
Austin Schuh64fab802020-09-09 22:47:47 -0700636 std::set<std::string> logfile_uuids;
637 std::set<std::string> parts_uuids;
638 // Confirm that we have the expected number of UUIDs for both the logfile
639 // UUIDs and parts UUIDs.
Austin Schuhadd6eb32020-11-09 21:24:26 -0800640 std::vector<SizePrefixedFlatbufferVector<LogFileHeader>> log_header;
Austin Schuh64fab802020-09-09 22:47:47 -0700641 for (std::string_view f : logfiles_) {
Austin Schuh3bd4c402020-11-06 18:19:06 -0800642 log_header.emplace_back(ReadHeader(f).value());
Brian Silvermanae7c0332020-09-30 16:58:23 -0700643 logfile_uuids.insert(log_header.back().message().log_event_uuid()->str());
Austin Schuh64fab802020-09-09 22:47:47 -0700644 parts_uuids.insert(log_header.back().message().parts_uuid()->str());
645 }
Austin Schuh15649d62019-12-28 16:36:38 -0800646
Austin Schuh64fab802020-09-09 22:47:47 -0700647 EXPECT_EQ(logfile_uuids.size(), 2u);
648 EXPECT_EQ(parts_uuids.size(), 7u);
649
650 // And confirm everything is on the correct node.
651 EXPECT_EQ(log_header[0].message().node()->name()->string_view(), "pi1");
652 EXPECT_EQ(log_header[1].message().node()->name()->string_view(), "pi2");
653 EXPECT_EQ(log_header[2].message().node()->name()->string_view(), "pi2");
654 EXPECT_EQ(log_header[3].message().node()->name()->string_view(), "pi2");
655 EXPECT_EQ(log_header[4].message().node()->name()->string_view(), "pi2");
656 EXPECT_EQ(log_header[5].message().node()->name()->string_view(), "pi2");
657 EXPECT_EQ(log_header[6].message().node()->name()->string_view(), "pi1");
658 EXPECT_EQ(log_header[7].message().node()->name()->string_view(), "pi1");
659 EXPECT_EQ(log_header[8].message().node()->name()->string_view(), "pi1");
660 EXPECT_EQ(log_header[9].message().node()->name()->string_view(), "pi1");
661 EXPECT_EQ(log_header[10].message().node()->name()->string_view(), "pi2");
662 EXPECT_EQ(log_header[11].message().node()->name()->string_view(), "pi2");
663
664 // And the parts index matches.
665 EXPECT_EQ(log_header[0].message().parts_index(), 0);
666 EXPECT_EQ(log_header[1].message().parts_index(), 0);
667 EXPECT_EQ(log_header[2].message().parts_index(), 1);
668 EXPECT_EQ(log_header[3].message().parts_index(), 0);
669 EXPECT_EQ(log_header[4].message().parts_index(), 0);
670 EXPECT_EQ(log_header[5].message().parts_index(), 1);
671 EXPECT_EQ(log_header[6].message().parts_index(), 0);
672 EXPECT_EQ(log_header[7].message().parts_index(), 1);
673 EXPECT_EQ(log_header[8].message().parts_index(), 0);
674 EXPECT_EQ(log_header[9].message().parts_index(), 1);
675 EXPECT_EQ(log_header[10].message().parts_index(), 0);
676 EXPECT_EQ(log_header[11].message().parts_index(), 1);
677 }
678
679 {
Austin Schuh391e3172020-09-01 22:48:18 -0700680 using ::testing::UnorderedElementsAre;
681
Austin Schuh6f3babe2020-01-26 20:34:50 -0800682 // Timing reports, pings
Austin Schuh2f8fd752020-09-01 22:38:28 -0700683 EXPECT_THAT(
684 CountChannelsData(logfiles_[0]),
685 UnorderedElementsAre(
686 std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 200),
687 std::make_tuple("/pi1/aos", "aos.timing.Report", 40),
688 std::make_tuple("/test", "aos.examples.Ping", 2001)));
Austin Schuh6f3babe2020-01-26 20:34:50 -0800689 // Timestamps for pong
Austin Schuh2f8fd752020-09-01 22:38:28 -0700690 EXPECT_THAT(
691 CountChannelsTimestamp(logfiles_[0]),
692 UnorderedElementsAre(
693 std::make_tuple("/test", "aos.examples.Pong", 2001),
694 std::make_tuple("/pi2/aos", "aos.message_bridge.Timestamp", 200)));
Austin Schuh6f3babe2020-01-26 20:34:50 -0800695
696 // Pong data.
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700697 EXPECT_THAT(CountChannelsData(logfiles_[1]),
Austin Schuh391e3172020-09-01 22:48:18 -0700698 UnorderedElementsAre(
Austin Schuh2f8fd752020-09-01 22:38:28 -0700699 std::make_tuple("/test", "aos.examples.Pong", 101)));
700 EXPECT_THAT(CountChannelsData(logfiles_[2]),
701 UnorderedElementsAre(
702 std::make_tuple("/test", "aos.examples.Pong", 1900)));
Austin Schuh391e3172020-09-01 22:48:18 -0700703
Austin Schuh6f3babe2020-01-26 20:34:50 -0800704 // No timestamps
Austin Schuh391e3172020-09-01 22:48:18 -0700705 EXPECT_THAT(CountChannelsTimestamp(logfiles_[1]), UnorderedElementsAre());
Austin Schuh2f8fd752020-09-01 22:38:28 -0700706 EXPECT_THAT(CountChannelsTimestamp(logfiles_[2]), UnorderedElementsAre());
Austin Schuh6f3babe2020-01-26 20:34:50 -0800707
708 // Timing reports and pongs.
Austin Schuh2f8fd752020-09-01 22:38:28 -0700709 EXPECT_THAT(
710 CountChannelsData(logfiles_[3]),
711 UnorderedElementsAre(
712 std::make_tuple("/pi2/aos", "aos.message_bridge.Timestamp", 200),
713 std::make_tuple("/pi2/aos", "aos.timing.Report", 40),
714 std::make_tuple("/test", "aos.examples.Pong", 2001)));
Austin Schuh6f3babe2020-01-26 20:34:50 -0800715 // And ping timestamps.
Austin Schuh2f8fd752020-09-01 22:38:28 -0700716 EXPECT_THAT(
717 CountChannelsTimestamp(logfiles_[3]),
718 UnorderedElementsAre(
719 std::make_tuple("/test", "aos.examples.Ping", 2001),
720 std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 200)));
721
722 // Timestamps from pi2 on pi1, and the other way.
723 EXPECT_THAT(CountChannelsData(logfiles_[4]), UnorderedElementsAre());
724 EXPECT_THAT(CountChannelsData(logfiles_[5]), UnorderedElementsAre());
725 EXPECT_THAT(CountChannelsData(logfiles_[6]), UnorderedElementsAre());
726 EXPECT_THAT(CountChannelsData(logfiles_[7]), UnorderedElementsAre());
727 EXPECT_THAT(
728 CountChannelsTimestamp(logfiles_[4]),
729 UnorderedElementsAre(
730 std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 10),
731 std::make_tuple("/test", "aos.examples.Ping", 101)));
732 EXPECT_THAT(
733 CountChannelsTimestamp(logfiles_[5]),
734 UnorderedElementsAre(
735 std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 190),
736 std::make_tuple("/test", "aos.examples.Ping", 1900)));
737 EXPECT_THAT(CountChannelsTimestamp(logfiles_[6]),
738 UnorderedElementsAre(std::make_tuple(
739 "/pi2/aos", "aos.message_bridge.Timestamp", 10)));
740 EXPECT_THAT(CountChannelsTimestamp(logfiles_[7]),
741 UnorderedElementsAre(std::make_tuple(
742 "/pi2/aos", "aos.message_bridge.Timestamp", 190)));
743
744 // And then test that the remotely logged timestamp data files only have
745 // timestamps in them.
746 EXPECT_THAT(CountChannelsTimestamp(logfiles_[8]), UnorderedElementsAre());
747 EXPECT_THAT(CountChannelsTimestamp(logfiles_[9]), UnorderedElementsAre());
748 EXPECT_THAT(CountChannelsTimestamp(logfiles_[10]), UnorderedElementsAre());
749 EXPECT_THAT(CountChannelsTimestamp(logfiles_[11]), UnorderedElementsAre());
750
751 EXPECT_THAT(CountChannelsData(logfiles_[8]),
752 UnorderedElementsAre(std::make_tuple(
753 "/pi1/aos", "aos.message_bridge.Timestamp", 10)));
754 EXPECT_THAT(CountChannelsData(logfiles_[9]),
755 UnorderedElementsAre(std::make_tuple(
756 "/pi1/aos", "aos.message_bridge.Timestamp", 190)));
757
758 EXPECT_THAT(CountChannelsData(logfiles_[10]),
759 UnorderedElementsAre(std::make_tuple(
760 "/pi2/aos", "aos.message_bridge.Timestamp", 10)));
761 EXPECT_THAT(CountChannelsData(logfiles_[11]),
762 UnorderedElementsAre(std::make_tuple(
763 "/pi2/aos", "aos.message_bridge.Timestamp", 190)));
Austin Schuh6f3babe2020-01-26 20:34:50 -0800764 }
765
Austin Schuh2f8fd752020-09-01 22:38:28 -0700766 LogReader reader(structured_logfiles_);
Austin Schuh6f3babe2020-01-26 20:34:50 -0800767
Austin Schuh8d7e0bb2020-10-02 17:57:00 -0700768 SimulatedEventLoopFactory log_reader_factory(reader.configuration());
Austin Schuh6331ef92020-01-07 18:28:09 -0800769 log_reader_factory.set_send_delay(chrono::microseconds(0));
Austin Schuh15649d62019-12-28 16:36:38 -0800770
771 // This sends out the fetched messages and advances time to the start of the
772 // log file.
Austin Schuh6331ef92020-01-07 18:28:09 -0800773 reader.Register(&log_reader_factory);
James Kuszmaul84ff3e52020-01-03 19:48:53 -0800774
Austin Schuhac0771c2020-01-07 18:36:30 -0800775 const Node *pi1 =
776 configuration::GetNode(log_reader_factory.configuration(), "pi1");
Austin Schuh6f3babe2020-01-26 20:34:50 -0800777 const Node *pi2 =
778 configuration::GetNode(log_reader_factory.configuration(), "pi2");
Austin Schuhac0771c2020-01-07 18:36:30 -0800779
Austin Schuh2f8fd752020-09-01 22:38:28 -0700780 LOG(INFO) << "Start time " << reader.monotonic_start_time(pi1) << " pi1";
781 LOG(INFO) << "Start time " << reader.monotonic_start_time(pi2) << " pi2";
782 LOG(INFO) << "now pi1 "
783 << log_reader_factory.GetNodeEventLoopFactory(pi1)->monotonic_now();
784 LOG(INFO) << "now pi2 "
785 << log_reader_factory.GetNodeEventLoopFactory(pi2)->monotonic_now();
786
Austin Schuh6f3babe2020-01-26 20:34:50 -0800787 EXPECT_THAT(reader.Nodes(), ::testing::ElementsAre(pi1, pi2));
James Kuszmaul84ff3e52020-01-03 19:48:53 -0800788
789 reader.event_loop_factory()->set_send_delay(chrono::microseconds(0));
Austin Schuh15649d62019-12-28 16:36:38 -0800790
Austin Schuh6f3babe2020-01-26 20:34:50 -0800791 std::unique_ptr<EventLoop> pi1_event_loop =
Austin Schuhac0771c2020-01-07 18:36:30 -0800792 log_reader_factory.MakeEventLoop("test", pi1);
Austin Schuh6f3babe2020-01-26 20:34:50 -0800793 std::unique_ptr<EventLoop> pi2_event_loop =
794 log_reader_factory.MakeEventLoop("test", pi2);
Austin Schuh15649d62019-12-28 16:36:38 -0800795
Austin Schuh6f3babe2020-01-26 20:34:50 -0800796 int pi1_ping_count = 10;
797 int pi2_ping_count = 10;
798 int pi1_pong_count = 10;
799 int pi2_pong_count = 10;
Austin Schuh15649d62019-12-28 16:36:38 -0800800
801 // Confirm that the ping value matches.
Austin Schuh6f3babe2020-01-26 20:34:50 -0800802 pi1_event_loop->MakeWatcher(
803 "/test", [&pi1_ping_count, &pi1_event_loop](const examples::Ping &ping) {
Austin Schuh2f8fd752020-09-01 22:38:28 -0700804 VLOG(1) << "Pi1 ping " << FlatbufferToJson(&ping) << " at "
Austin Schuh6f3babe2020-01-26 20:34:50 -0800805 << pi1_event_loop->context().monotonic_remote_time << " -> "
806 << pi1_event_loop->context().monotonic_event_time;
807 EXPECT_EQ(ping.value(), pi1_ping_count + 1);
808 EXPECT_EQ(pi1_event_loop->context().monotonic_remote_time,
809 pi1_ping_count * chrono::milliseconds(10) +
810 monotonic_clock::epoch());
811 EXPECT_EQ(pi1_event_loop->context().realtime_remote_time,
812 pi1_ping_count * chrono::milliseconds(10) +
813 realtime_clock::epoch());
814 EXPECT_EQ(pi1_event_loop->context().monotonic_remote_time,
815 pi1_event_loop->context().monotonic_event_time);
816 EXPECT_EQ(pi1_event_loop->context().realtime_remote_time,
817 pi1_event_loop->context().realtime_event_time);
Austin Schuh15649d62019-12-28 16:36:38 -0800818
Austin Schuh6f3babe2020-01-26 20:34:50 -0800819 ++pi1_ping_count;
820 });
821 pi2_event_loop->MakeWatcher(
822 "/test", [&pi2_ping_count, &pi2_event_loop](const examples::Ping &ping) {
Austin Schuh2f8fd752020-09-01 22:38:28 -0700823 VLOG(1) << "Pi2 ping " << FlatbufferToJson(&ping) << " at "
Austin Schuh6f3babe2020-01-26 20:34:50 -0800824 << pi2_event_loop->context().monotonic_remote_time << " -> "
825 << pi2_event_loop->context().monotonic_event_time;
826 EXPECT_EQ(ping.value(), pi2_ping_count + 1);
827
828 EXPECT_EQ(pi2_event_loop->context().monotonic_remote_time,
829 pi2_ping_count * chrono::milliseconds(10) +
830 monotonic_clock::epoch());
831 EXPECT_EQ(pi2_event_loop->context().realtime_remote_time,
832 pi2_ping_count * chrono::milliseconds(10) +
833 realtime_clock::epoch());
834 EXPECT_EQ(pi2_event_loop->context().monotonic_remote_time +
835 chrono::microseconds(150),
836 pi2_event_loop->context().monotonic_event_time);
837 EXPECT_EQ(pi2_event_loop->context().realtime_remote_time +
838 chrono::microseconds(150),
839 pi2_event_loop->context().realtime_event_time);
840 ++pi2_ping_count;
Austin Schuh15649d62019-12-28 16:36:38 -0800841 });
842
Austin Schuh8d7e0bb2020-10-02 17:57:00 -0700843 constexpr ssize_t kQueueIndexOffset = -9;
Austin Schuh6f3babe2020-01-26 20:34:50 -0800844 // Confirm that the ping and pong counts both match, and the value also
845 // matches.
846 pi1_event_loop->MakeWatcher(
847 "/test", [&pi1_event_loop, &pi1_ping_count,
848 &pi1_pong_count](const examples::Pong &pong) {
849 VLOG(1) << "Pi1 pong " << FlatbufferToJson(&pong) << " at "
850 << pi1_event_loop->context().monotonic_remote_time << " -> "
851 << pi1_event_loop->context().monotonic_event_time;
852
853 EXPECT_EQ(pi1_event_loop->context().remote_queue_index,
854 pi1_pong_count + kQueueIndexOffset);
855 EXPECT_EQ(pi1_event_loop->context().monotonic_remote_time,
856 chrono::microseconds(200) +
857 pi1_pong_count * chrono::milliseconds(10) +
858 monotonic_clock::epoch());
859 EXPECT_EQ(pi1_event_loop->context().realtime_remote_time,
860 chrono::microseconds(200) +
861 pi1_pong_count * chrono::milliseconds(10) +
862 realtime_clock::epoch());
863
864 EXPECT_EQ(pi1_event_loop->context().monotonic_remote_time +
865 chrono::microseconds(150),
866 pi1_event_loop->context().monotonic_event_time);
867 EXPECT_EQ(pi1_event_loop->context().realtime_remote_time +
868 chrono::microseconds(150),
869 pi1_event_loop->context().realtime_event_time);
870
871 EXPECT_EQ(pong.value(), pi1_pong_count + 1);
872 ++pi1_pong_count;
873 EXPECT_EQ(pi1_ping_count, pi1_pong_count);
874 });
875 pi2_event_loop->MakeWatcher(
876 "/test", [&pi2_event_loop, &pi2_ping_count,
877 &pi2_pong_count](const examples::Pong &pong) {
878 VLOG(1) << "Pi2 pong " << FlatbufferToJson(&pong) << " at "
879 << pi2_event_loop->context().monotonic_remote_time << " -> "
880 << pi2_event_loop->context().monotonic_event_time;
881
882 EXPECT_EQ(pi2_event_loop->context().remote_queue_index,
Austin Schuh8d7e0bb2020-10-02 17:57:00 -0700883 pi2_pong_count + kQueueIndexOffset);
Austin Schuh6f3babe2020-01-26 20:34:50 -0800884
885 EXPECT_EQ(pi2_event_loop->context().monotonic_remote_time,
886 chrono::microseconds(200) +
887 pi2_pong_count * chrono::milliseconds(10) +
888 monotonic_clock::epoch());
889 EXPECT_EQ(pi2_event_loop->context().realtime_remote_time,
890 chrono::microseconds(200) +
891 pi2_pong_count * chrono::milliseconds(10) +
892 realtime_clock::epoch());
893
894 EXPECT_EQ(pi2_event_loop->context().monotonic_remote_time,
895 pi2_event_loop->context().monotonic_event_time);
896 EXPECT_EQ(pi2_event_loop->context().realtime_remote_time,
897 pi2_event_loop->context().realtime_event_time);
898
899 EXPECT_EQ(pong.value(), pi2_pong_count + 1);
900 ++pi2_pong_count;
901 EXPECT_EQ(pi2_ping_count, pi2_pong_count);
902 });
903
904 log_reader_factory.Run();
905 EXPECT_EQ(pi1_ping_count, 2010);
906 EXPECT_EQ(pi2_ping_count, 2010);
907 EXPECT_EQ(pi1_pong_count, 2010);
908 EXPECT_EQ(pi2_pong_count, 2010);
Austin Schuh6331ef92020-01-07 18:28:09 -0800909
910 reader.Deregister();
Austin Schuh15649d62019-12-28 16:36:38 -0800911}
912
James Kuszmaul46d82582020-05-09 19:50:09 -0700913typedef MultinodeLoggerTest MultinodeLoggerDeathTest;
914
915// Test that if we feed the replay with a mismatched node list that we die on
916// the LogReader constructor.
917TEST_F(MultinodeLoggerDeathTest, MultiNodeBadReplayConfig) {
James Kuszmaul46d82582020-05-09 19:50:09 -0700918 {
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700919 LoggerState pi1_logger = MakeLogger(pi1_);
920 LoggerState pi2_logger = MakeLogger(pi2_);
James Kuszmaul46d82582020-05-09 19:50:09 -0700921
922 event_loop_factory_.RunFor(chrono::milliseconds(95));
923
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700924 StartLogger(&pi1_logger);
925 StartLogger(&pi2_logger);
James Kuszmaul46d82582020-05-09 19:50:09 -0700926
James Kuszmaul46d82582020-05-09 19:50:09 -0700927 event_loop_factory_.RunFor(chrono::milliseconds(20000));
928 }
929
930 // Test that, if we add an additional node to the replay config that the
931 // logger complains about the mismatch in number of nodes.
932 FlatbufferDetachedBuffer<Configuration> extra_nodes_config =
933 configuration::MergeWithConfig(&config_.message(), R"({
934 "nodes": [
935 {
936 "name": "extra-node"
937 }
938 ]
939 }
940 )");
941
Austin Schuh2f8fd752020-09-01 22:38:28 -0700942 EXPECT_DEATH(LogReader(structured_logfiles_, &extra_nodes_config.message()),
James Kuszmaul46d82582020-05-09 19:50:09 -0700943 "Log file and replay config need to have matching nodes lists.");
James Kuszmaul46d82582020-05-09 19:50:09 -0700944}
945
Austin Schuhcde938c2020-02-02 17:30:07 -0800946// Tests that we can read log files where they don't start at the same monotonic
947// time.
948TEST_F(MultinodeLoggerTest, StaggeredStart) {
Austin Schuhcde938c2020-02-02 17:30:07 -0800949 {
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700950 LoggerState pi1_logger = MakeLogger(pi1_);
951 LoggerState pi2_logger = MakeLogger(pi2_);
Austin Schuhcde938c2020-02-02 17:30:07 -0800952
953 event_loop_factory_.RunFor(chrono::milliseconds(95));
954
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700955 StartLogger(&pi1_logger);
Austin Schuhcde938c2020-02-02 17:30:07 -0800956
957 event_loop_factory_.RunFor(chrono::milliseconds(200));
958
James Kuszmaulbb28ef22020-05-09 22:30:38 -0700959 StartLogger(&pi2_logger);
960
Austin Schuhcde938c2020-02-02 17:30:07 -0800961 event_loop_factory_.RunFor(chrono::milliseconds(20000));
962 }
963
Austin Schuh2f8fd752020-09-01 22:38:28 -0700964 LogReader reader(structured_logfiles_);
Austin Schuhcde938c2020-02-02 17:30:07 -0800965
Austin Schuh8d7e0bb2020-10-02 17:57:00 -0700966 SimulatedEventLoopFactory log_reader_factory(reader.configuration());
Austin Schuhcde938c2020-02-02 17:30:07 -0800967 log_reader_factory.set_send_delay(chrono::microseconds(0));
968
969 // This sends out the fetched messages and advances time to the start of the
970 // log file.
971 reader.Register(&log_reader_factory);
972
973 const Node *pi1 =
974 configuration::GetNode(log_reader_factory.configuration(), "pi1");
975 const Node *pi2 =
976 configuration::GetNode(log_reader_factory.configuration(), "pi2");
977
978 EXPECT_THAT(reader.Nodes(), ::testing::ElementsAre(pi1, pi2));
979
980 reader.event_loop_factory()->set_send_delay(chrono::microseconds(0));
981
982 std::unique_ptr<EventLoop> pi1_event_loop =
983 log_reader_factory.MakeEventLoop("test", pi1);
984 std::unique_ptr<EventLoop> pi2_event_loop =
985 log_reader_factory.MakeEventLoop("test", pi2);
986
987 int pi1_ping_count = 30;
988 int pi2_ping_count = 30;
989 int pi1_pong_count = 30;
990 int pi2_pong_count = 30;
991
992 // Confirm that the ping value matches.
993 pi1_event_loop->MakeWatcher(
994 "/test", [&pi1_ping_count, &pi1_event_loop](const examples::Ping &ping) {
995 VLOG(1) << "Pi1 ping " << FlatbufferToJson(&ping)
996 << pi1_event_loop->context().monotonic_remote_time << " -> "
997 << pi1_event_loop->context().monotonic_event_time;
998 EXPECT_EQ(ping.value(), pi1_ping_count + 1);
999
1000 ++pi1_ping_count;
1001 });
1002 pi2_event_loop->MakeWatcher(
1003 "/test", [&pi2_ping_count, &pi2_event_loop](const examples::Ping &ping) {
1004 VLOG(1) << "Pi2 ping " << FlatbufferToJson(&ping)
1005 << pi2_event_loop->context().monotonic_remote_time << " -> "
1006 << pi2_event_loop->context().monotonic_event_time;
1007 EXPECT_EQ(ping.value(), pi2_ping_count + 1);
1008
1009 ++pi2_ping_count;
1010 });
1011
1012 // Confirm that the ping and pong counts both match, and the value also
1013 // matches.
1014 pi1_event_loop->MakeWatcher(
1015 "/test", [&pi1_event_loop, &pi1_ping_count,
1016 &pi1_pong_count](const examples::Pong &pong) {
1017 VLOG(1) << "Pi1 pong " << FlatbufferToJson(&pong) << " at "
1018 << pi1_event_loop->context().monotonic_remote_time << " -> "
1019 << pi1_event_loop->context().monotonic_event_time;
1020
1021 EXPECT_EQ(pong.value(), pi1_pong_count + 1);
1022 ++pi1_pong_count;
1023 EXPECT_EQ(pi1_ping_count, pi1_pong_count);
1024 });
1025 pi2_event_loop->MakeWatcher(
1026 "/test", [&pi2_event_loop, &pi2_ping_count,
1027 &pi2_pong_count](const examples::Pong &pong) {
1028 VLOG(1) << "Pi2 pong " << FlatbufferToJson(&pong) << " at "
1029 << pi2_event_loop->context().monotonic_remote_time << " -> "
1030 << pi2_event_loop->context().monotonic_event_time;
1031
1032 EXPECT_EQ(pong.value(), pi2_pong_count + 1);
1033 ++pi2_pong_count;
1034 EXPECT_EQ(pi2_ping_count, pi2_pong_count);
1035 });
1036
1037 log_reader_factory.Run();
1038 EXPECT_EQ(pi1_ping_count, 2030);
1039 EXPECT_EQ(pi2_ping_count, 2030);
1040 EXPECT_EQ(pi1_pong_count, 2030);
1041 EXPECT_EQ(pi2_pong_count, 2030);
1042
1043 reader.Deregister();
1044}
Austin Schuh6f3babe2020-01-26 20:34:50 -08001045
Austin Schuh8bd96322020-02-13 21:18:22 -08001046// Tests that we can read log files where the monotonic clocks drift and don't
1047// match correctly. While we are here, also test that different ending times
1048// also is readable.
1049TEST_F(MultinodeLoggerTest, MismatchedClocks) {
Austin Schuhcde938c2020-02-02 17:30:07 -08001050 {
James Kuszmaulbb28ef22020-05-09 22:30:38 -07001051 LoggerState pi2_logger = MakeLogger(pi2_);
1052
Austin Schuh8bd96322020-02-13 21:18:22 -08001053 NodeEventLoopFactory *pi2 =
1054 event_loop_factory_.GetNodeEventLoopFactory(pi2_);
1055 LOG(INFO) << "pi2 times: " << pi2->monotonic_now() << " "
1056 << pi2->realtime_now() << " distributed "
1057 << pi2->ToDistributedClock(pi2->monotonic_now());
Austin Schuhcde938c2020-02-02 17:30:07 -08001058
Austin Schuh8bd96322020-02-13 21:18:22 -08001059 const chrono::nanoseconds initial_pi2_offset = -chrono::seconds(1000);
1060 chrono::nanoseconds pi2_offset = initial_pi2_offset;
Austin Schuhcde938c2020-02-02 17:30:07 -08001061
Austin Schuhbe69cf32020-08-27 11:38:33 -07001062 pi2->SetDistributedOffset(-pi2_offset, 1.0);
Austin Schuh8bd96322020-02-13 21:18:22 -08001063 LOG(INFO) << "pi2 times: " << pi2->monotonic_now() << " "
1064 << pi2->realtime_now() << " distributed "
1065 << pi2->ToDistributedClock(pi2->monotonic_now());
Austin Schuhcde938c2020-02-02 17:30:07 -08001066
Austin Schuh8bd96322020-02-13 21:18:22 -08001067 for (int i = 0; i < 95; ++i) {
1068 pi2_offset += chrono::nanoseconds(200);
Austin Schuhbe69cf32020-08-27 11:38:33 -07001069 pi2->SetDistributedOffset(-pi2_offset, 1.0);
Austin Schuh8bd96322020-02-13 21:18:22 -08001070 event_loop_factory_.RunFor(chrono::milliseconds(1));
1071 }
Austin Schuhcde938c2020-02-02 17:30:07 -08001072
James Kuszmaulbb28ef22020-05-09 22:30:38 -07001073 StartLogger(&pi2_logger);
Austin Schuhcde938c2020-02-02 17:30:07 -08001074
Austin Schuh8bd96322020-02-13 21:18:22 -08001075 event_loop_factory_.RunFor(chrono::milliseconds(200));
Austin Schuhcde938c2020-02-02 17:30:07 -08001076
Austin Schuh8bd96322020-02-13 21:18:22 -08001077 {
1078 // Run pi1's logger for only part of the time.
James Kuszmaulbb28ef22020-05-09 22:30:38 -07001079 LoggerState pi1_logger = MakeLogger(pi1_);
Austin Schuh8bd96322020-02-13 21:18:22 -08001080
James Kuszmaulbb28ef22020-05-09 22:30:38 -07001081 StartLogger(&pi1_logger);
Austin Schuh8bd96322020-02-13 21:18:22 -08001082
1083 for (int i = 0; i < 20000; ++i) {
1084 pi2_offset += chrono::nanoseconds(200);
Austin Schuhbe69cf32020-08-27 11:38:33 -07001085 pi2->SetDistributedOffset(-pi2_offset, 1.0);
Austin Schuh8bd96322020-02-13 21:18:22 -08001086 event_loop_factory_.RunFor(chrono::milliseconds(1));
1087 }
1088
1089 EXPECT_GT(pi2_offset - initial_pi2_offset,
1090 event_loop_factory_.send_delay() +
1091 event_loop_factory_.network_delay());
1092
1093 for (int i = 0; i < 40000; ++i) {
1094 pi2_offset -= chrono::nanoseconds(200);
Austin Schuhbe69cf32020-08-27 11:38:33 -07001095 pi2->SetDistributedOffset(-pi2_offset, 1.0);
Austin Schuh8bd96322020-02-13 21:18:22 -08001096 event_loop_factory_.RunFor(chrono::milliseconds(1));
1097 }
1098 }
1099
1100 // And log a bit more on pi2.
1101 event_loop_factory_.RunFor(chrono::milliseconds(400));
Austin Schuhcde938c2020-02-02 17:30:07 -08001102 }
1103
Austin Schuh2f8fd752020-09-01 22:38:28 -07001104 LogReader reader(structured_logfiles_);
Austin Schuhcde938c2020-02-02 17:30:07 -08001105
Austin Schuh8d7e0bb2020-10-02 17:57:00 -07001106 SimulatedEventLoopFactory log_reader_factory(reader.configuration());
Austin Schuhcde938c2020-02-02 17:30:07 -08001107 log_reader_factory.set_send_delay(chrono::microseconds(0));
1108
Austin Schuhcde938c2020-02-02 17:30:07 -08001109 const Node *pi1 =
1110 configuration::GetNode(log_reader_factory.configuration(), "pi1");
1111 const Node *pi2 =
1112 configuration::GetNode(log_reader_factory.configuration(), "pi2");
1113
Austin Schuh2f8fd752020-09-01 22:38:28 -07001114 // This sends out the fetched messages and advances time to the start of the
1115 // log file.
1116 reader.Register(&log_reader_factory);
1117
1118 LOG(INFO) << "Start time " << reader.monotonic_start_time(pi1) << " pi1";
1119 LOG(INFO) << "Start time " << reader.monotonic_start_time(pi2) << " pi2";
1120 LOG(INFO) << "now pi1 "
1121 << log_reader_factory.GetNodeEventLoopFactory(pi1)->monotonic_now();
1122 LOG(INFO) << "now pi2 "
1123 << log_reader_factory.GetNodeEventLoopFactory(pi2)->monotonic_now();
1124
Austin Schuhcde938c2020-02-02 17:30:07 -08001125 LOG(INFO) << "Done registering (pi1) "
Austin Schuh391e3172020-09-01 22:48:18 -07001126 << log_reader_factory.GetNodeEventLoopFactory(pi1)->monotonic_now()
1127 << " "
Austin Schuhcde938c2020-02-02 17:30:07 -08001128 << log_reader_factory.GetNodeEventLoopFactory(pi1)->realtime_now();
1129 LOG(INFO) << "Done registering (pi2) "
Austin Schuh391e3172020-09-01 22:48:18 -07001130 << log_reader_factory.GetNodeEventLoopFactory(pi2)->monotonic_now()
1131 << " "
Austin Schuhcde938c2020-02-02 17:30:07 -08001132 << log_reader_factory.GetNodeEventLoopFactory(pi2)->realtime_now();
1133
1134 EXPECT_THAT(reader.Nodes(), ::testing::ElementsAre(pi1, pi2));
1135
1136 reader.event_loop_factory()->set_send_delay(chrono::microseconds(0));
1137
1138 std::unique_ptr<EventLoop> pi1_event_loop =
1139 log_reader_factory.MakeEventLoop("test", pi1);
1140 std::unique_ptr<EventLoop> pi2_event_loop =
1141 log_reader_factory.MakeEventLoop("test", pi2);
1142
1143 int pi1_ping_count = 30;
1144 int pi2_ping_count = 30;
1145 int pi1_pong_count = 30;
1146 int pi2_pong_count = 30;
1147
1148 // Confirm that the ping value matches.
1149 pi1_event_loop->MakeWatcher(
1150 "/test", [&pi1_ping_count, &pi1_event_loop](const examples::Ping &ping) {
1151 VLOG(1) << "Pi1 ping " << FlatbufferToJson(&ping)
1152 << pi1_event_loop->context().monotonic_remote_time << " -> "
1153 << pi1_event_loop->context().monotonic_event_time;
1154 EXPECT_EQ(ping.value(), pi1_ping_count + 1);
1155
1156 ++pi1_ping_count;
1157 });
1158 pi2_event_loop->MakeWatcher(
1159 "/test", [&pi2_ping_count, &pi2_event_loop](const examples::Ping &ping) {
1160 VLOG(1) << "Pi2 ping " << FlatbufferToJson(&ping)
1161 << pi2_event_loop->context().monotonic_remote_time << " -> "
1162 << pi2_event_loop->context().monotonic_event_time;
1163 EXPECT_EQ(ping.value(), pi2_ping_count + 1);
1164
1165 ++pi2_ping_count;
1166 });
1167
1168 // Confirm that the ping and pong counts both match, and the value also
1169 // matches.
1170 pi1_event_loop->MakeWatcher(
1171 "/test", [&pi1_event_loop, &pi1_ping_count,
1172 &pi1_pong_count](const examples::Pong &pong) {
1173 VLOG(1) << "Pi1 pong " << FlatbufferToJson(&pong) << " at "
1174 << pi1_event_loop->context().monotonic_remote_time << " -> "
1175 << pi1_event_loop->context().monotonic_event_time;
1176
1177 EXPECT_EQ(pong.value(), pi1_pong_count + 1);
1178 ++pi1_pong_count;
1179 EXPECT_EQ(pi1_ping_count, pi1_pong_count);
1180 });
1181 pi2_event_loop->MakeWatcher(
1182 "/test", [&pi2_event_loop, &pi2_ping_count,
1183 &pi2_pong_count](const examples::Pong &pong) {
1184 VLOG(1) << "Pi2 pong " << FlatbufferToJson(&pong) << " at "
1185 << pi2_event_loop->context().monotonic_remote_time << " -> "
1186 << pi2_event_loop->context().monotonic_event_time;
1187
1188 EXPECT_EQ(pong.value(), pi2_pong_count + 1);
1189 ++pi2_pong_count;
1190 EXPECT_EQ(pi2_ping_count, pi2_pong_count);
1191 });
1192
1193 log_reader_factory.Run();
Austin Schuh8bd96322020-02-13 21:18:22 -08001194 EXPECT_EQ(pi1_ping_count, 6030);
1195 EXPECT_EQ(pi2_ping_count, 6030);
1196 EXPECT_EQ(pi1_pong_count, 6030);
1197 EXPECT_EQ(pi2_pong_count, 6030);
Austin Schuhcde938c2020-02-02 17:30:07 -08001198
1199 reader.Deregister();
1200}
1201
Austin Schuh5212cad2020-09-09 23:12:09 -07001202// Tests that we can sort a bunch of parts into the pre-determined sorted parts.
1203TEST_F(MultinodeLoggerTest, SortParts) {
1204 // Make a bunch of parts.
1205 {
1206 LoggerState pi1_logger = MakeLogger(pi1_);
1207 LoggerState pi2_logger = MakeLogger(pi2_);
1208
1209 event_loop_factory_.RunFor(chrono::milliseconds(95));
1210
1211 StartLogger(&pi1_logger);
1212 StartLogger(&pi2_logger);
1213
1214 event_loop_factory_.RunFor(chrono::milliseconds(2000));
1215 }
1216
Austin Schuh11d43732020-09-21 17:28:30 -07001217 const std::vector<LogFile> sorted_parts = SortParts(logfiles_);
Austin Schuh3bd4c402020-11-06 18:19:06 -08001218 VerifyParts(sorted_parts);
1219}
Austin Schuh11d43732020-09-21 17:28:30 -07001220
Austin Schuh3bd4c402020-11-06 18:19:06 -08001221// Tests that we can sort a bunch of parts with an empty part. We should ignore
1222// it and remove it from the sorted list.
1223TEST_F(MultinodeLoggerTest, SortEmptyParts) {
1224 // Make a bunch of parts.
1225 {
1226 LoggerState pi1_logger = MakeLogger(pi1_);
1227 LoggerState pi2_logger = MakeLogger(pi2_);
Austin Schuh11d43732020-09-21 17:28:30 -07001228
Austin Schuh3bd4c402020-11-06 18:19:06 -08001229 event_loop_factory_.RunFor(chrono::milliseconds(95));
Austin Schuh11d43732020-09-21 17:28:30 -07001230
Austin Schuh3bd4c402020-11-06 18:19:06 -08001231 StartLogger(&pi1_logger);
1232 StartLogger(&pi2_logger);
Austin Schuh11d43732020-09-21 17:28:30 -07001233
Austin Schuh3bd4c402020-11-06 18:19:06 -08001234 event_loop_factory_.RunFor(chrono::milliseconds(2000));
Austin Schuh11d43732020-09-21 17:28:30 -07001235 }
1236
Austin Schuh3bd4c402020-11-06 18:19:06 -08001237 // TODO(austin): Should we flip out if the file can't open?
1238 const std::string kEmptyFile("foobarinvalidfiledoesnotexist.bfbs");
Austin Schuh11d43732020-09-21 17:28:30 -07001239
Austin Schuh3bd4c402020-11-06 18:19:06 -08001240 aos::util::WriteStringToFileOrDie(kEmptyFile, "");
1241 logfiles_.emplace_back(kEmptyFile);
Austin Schuh5212cad2020-09-09 23:12:09 -07001242
Austin Schuh3bd4c402020-11-06 18:19:06 -08001243 const std::vector<LogFile> sorted_parts = SortParts(logfiles_);
1244 VerifyParts(sorted_parts, {kEmptyFile});
Austin Schuh5212cad2020-09-09 23:12:09 -07001245}
1246
Austin Schuh3bd4c402020-11-06 18:19:06 -08001247#ifdef LZMA
1248// Tests that we can sort a bunch of parts with an empty .xz file in there. The
1249// empty file should be ignored.
1250TEST_F(MultinodeLoggerTest, SortEmptyCompressedParts) {
1251 // Make a bunch of parts.
1252 {
1253 LoggerState pi1_logger = MakeLogger(pi1_);
1254 LoggerState pi2_logger = MakeLogger(pi2_);
1255
1256 event_loop_factory_.RunFor(chrono::milliseconds(95));
1257
1258 StartLogger(&pi1_logger, "", true);
1259 StartLogger(&pi2_logger, "", true);
1260
1261 event_loop_factory_.RunFor(chrono::milliseconds(2000));
1262 }
1263
1264 // TODO(austin): Should we flip out if the file can't open?
1265 const std::string kEmptyFile("foobarinvalidfiledoesnotexist.bfbs.xz");
1266
1267 AddExtension(".xz");
1268
1269 aos::util::WriteStringToFileOrDie(kEmptyFile, "");
1270 logfiles_.emplace_back(kEmptyFile);
1271
1272 const std::vector<LogFile> sorted_parts = SortParts(logfiles_);
1273 VerifyParts(sorted_parts, {kEmptyFile});
1274}
1275
1276// Tests that we can sort a bunch of parts with the end missing off a compressed
1277// file. We should use the part we can read.
1278TEST_F(MultinodeLoggerTest, SortTruncatedCompressedParts) {
1279 // Make a bunch of parts.
1280 {
1281 LoggerState pi1_logger = MakeLogger(pi1_);
1282 LoggerState pi2_logger = MakeLogger(pi2_);
1283
1284 event_loop_factory_.RunFor(chrono::milliseconds(95));
1285
1286 StartLogger(&pi1_logger, "", true);
1287 StartLogger(&pi2_logger, "", true);
1288
1289 event_loop_factory_.RunFor(chrono::milliseconds(2000));
1290 }
1291
1292 // Append everything with .xz.
1293 AddExtension(".xz");
1294
1295 // Strip off the end of one of the files. Pick one with a lot of data.
1296 ::std::string compressed_contents =
1297 aos::util::ReadFileToStringOrDie(logfiles_[0]);
1298
1299 aos::util::WriteStringToFileOrDie(
1300 logfiles_[0],
1301 compressed_contents.substr(0, compressed_contents.size() - 100));
1302
1303 const std::vector<LogFile> sorted_parts = SortParts(logfiles_);
1304 VerifyParts(sorted_parts);
1305}
1306#endif
1307
Austin Schuh01b4c352020-09-21 23:09:39 -07001308// Tests that if we remap a remapped channel, it shows up correctly.
1309TEST_F(MultinodeLoggerTest, RemapLoggedChannel) {
1310 {
1311 LoggerState pi1_logger = MakeLogger(pi1_);
1312 LoggerState pi2_logger = MakeLogger(pi2_);
1313
1314 event_loop_factory_.RunFor(chrono::milliseconds(95));
1315
1316 StartLogger(&pi1_logger);
1317 StartLogger(&pi2_logger);
1318
1319 event_loop_factory_.RunFor(chrono::milliseconds(20000));
1320 }
1321
1322 LogReader reader(structured_logfiles_);
1323
1324 // Remap just on pi1.
1325 reader.RemapLoggedChannel<aos::timing::Report>(
1326 "/aos", configuration::GetNode(reader.configuration(), "pi1"));
1327
1328 SimulatedEventLoopFactory log_reader_factory(reader.configuration());
1329 log_reader_factory.set_send_delay(chrono::microseconds(0));
1330
1331 reader.Register(&log_reader_factory);
1332
1333 const Node *pi1 =
1334 configuration::GetNode(log_reader_factory.configuration(), "pi1");
1335 const Node *pi2 =
1336 configuration::GetNode(log_reader_factory.configuration(), "pi2");
1337
1338 // Confirm we can read the data on the remapped channel, just for pi1. Nothing
1339 // else should have moved.
1340 std::unique_ptr<EventLoop> pi1_event_loop =
1341 log_reader_factory.MakeEventLoop("test", pi1);
1342 pi1_event_loop->SkipTimingReport();
1343 std::unique_ptr<EventLoop> full_pi1_event_loop =
1344 log_reader_factory.MakeEventLoop("test", pi1);
1345 full_pi1_event_loop->SkipTimingReport();
1346 std::unique_ptr<EventLoop> pi2_event_loop =
1347 log_reader_factory.MakeEventLoop("test", pi2);
1348 pi2_event_loop->SkipTimingReport();
1349
1350 MessageCounter<aos::timing::Report> pi1_timing_report(pi1_event_loop.get(),
1351 "/aos");
1352 MessageCounter<aos::timing::Report> full_pi1_timing_report(
1353 full_pi1_event_loop.get(), "/pi1/aos");
1354 MessageCounter<aos::timing::Report> pi1_original_timing_report(
1355 pi1_event_loop.get(), "/original/aos");
1356 MessageCounter<aos::timing::Report> full_pi1_original_timing_report(
1357 full_pi1_event_loop.get(), "/original/pi1/aos");
1358 MessageCounter<aos::timing::Report> pi2_timing_report(pi2_event_loop.get(),
1359 "/aos");
1360
1361 log_reader_factory.Run();
1362
1363 EXPECT_EQ(pi1_timing_report.count(), 0u);
1364 EXPECT_EQ(full_pi1_timing_report.count(), 0u);
1365 EXPECT_NE(pi1_original_timing_report.count(), 0u);
1366 EXPECT_NE(full_pi1_original_timing_report.count(), 0u);
1367 EXPECT_NE(pi2_timing_report.count(), 0u);
1368
1369 reader.Deregister();
1370}
1371
Austin Schuh8d7e0bb2020-10-02 17:57:00 -07001372// Tests that we properly recreate forwarded timestamps when replaying a log.
1373// This should be enough that we can then re-run the logger and get a valid log
1374// back.
1375TEST_F(MultinodeLoggerTest, MessageHeader) {
1376 {
1377 LoggerState pi1_logger = MakeLogger(pi1_);
1378 LoggerState pi2_logger = MakeLogger(pi2_);
1379
1380 event_loop_factory_.RunFor(chrono::milliseconds(95));
1381
1382 StartLogger(&pi1_logger);
1383 StartLogger(&pi2_logger);
1384
1385 event_loop_factory_.RunFor(chrono::milliseconds(20000));
1386 }
1387
1388 LogReader reader(structured_logfiles_);
1389
1390 SimulatedEventLoopFactory log_reader_factory(reader.configuration());
1391 log_reader_factory.set_send_delay(chrono::microseconds(0));
1392
1393 // This sends out the fetched messages and advances time to the start of the
1394 // log file.
1395 reader.Register(&log_reader_factory);
1396
1397 const Node *pi1 =
1398 configuration::GetNode(log_reader_factory.configuration(), "pi1");
1399 const Node *pi2 =
1400 configuration::GetNode(log_reader_factory.configuration(), "pi2");
1401
1402 LOG(INFO) << "Start time " << reader.monotonic_start_time(pi1) << " pi1";
1403 LOG(INFO) << "Start time " << reader.monotonic_start_time(pi2) << " pi2";
1404 LOG(INFO) << "now pi1 "
1405 << log_reader_factory.GetNodeEventLoopFactory(pi1)->monotonic_now();
1406 LOG(INFO) << "now pi2 "
1407 << log_reader_factory.GetNodeEventLoopFactory(pi2)->monotonic_now();
1408
1409 EXPECT_THAT(reader.Nodes(), ::testing::ElementsAre(pi1, pi2));
1410
1411 reader.event_loop_factory()->set_send_delay(chrono::microseconds(0));
1412
1413 std::unique_ptr<EventLoop> pi1_event_loop =
1414 log_reader_factory.MakeEventLoop("test", pi1);
1415 std::unique_ptr<EventLoop> pi2_event_loop =
1416 log_reader_factory.MakeEventLoop("test", pi2);
1417
1418 MessageCounter<MessageHeader> pi1_original_message_header_counter(
1419 pi1_event_loop.get(), "/original/aos/remote_timestamps/pi2");
1420 MessageCounter<MessageHeader> pi2_original_message_header_counter(
1421 pi2_event_loop.get(), "/original/aos/remote_timestamps/pi1");
1422
1423 aos::Fetcher<message_bridge::Timestamp> pi1_timestamp_on_pi1_fetcher =
1424 pi1_event_loop->MakeFetcher<message_bridge::Timestamp>("/pi1/aos");
1425 aos::Fetcher<message_bridge::Timestamp> pi1_timestamp_on_pi2_fetcher =
1426 pi2_event_loop->MakeFetcher<message_bridge::Timestamp>("/pi1/aos");
1427
1428 aos::Fetcher<examples::Ping> ping_on_pi1_fetcher =
1429 pi1_event_loop->MakeFetcher<examples::Ping>("/test");
1430 aos::Fetcher<examples::Ping> ping_on_pi2_fetcher =
1431 pi2_event_loop->MakeFetcher<examples::Ping>("/test");
1432
1433 aos::Fetcher<message_bridge::Timestamp> pi2_timestamp_on_pi2_fetcher =
1434 pi2_event_loop->MakeFetcher<message_bridge::Timestamp>("/pi2/aos");
1435 aos::Fetcher<message_bridge::Timestamp> pi2_timestamp_on_pi1_fetcher =
1436 pi1_event_loop->MakeFetcher<message_bridge::Timestamp>("/pi2/aos");
1437
1438 aos::Fetcher<examples::Pong> pong_on_pi2_fetcher =
1439 pi2_event_loop->MakeFetcher<examples::Pong>("/test");
1440 aos::Fetcher<examples::Pong> pong_on_pi1_fetcher =
1441 pi1_event_loop->MakeFetcher<examples::Pong>("/test");
1442
1443 const size_t pi1_timestamp_channel = configuration::ChannelIndex(
1444 pi1_event_loop->configuration(), pi1_timestamp_on_pi1_fetcher.channel());
1445 const size_t ping_timestamp_channel = configuration::ChannelIndex(
1446 pi2_event_loop->configuration(), ping_on_pi2_fetcher.channel());
1447
1448 const size_t pi2_timestamp_channel = configuration::ChannelIndex(
1449 pi2_event_loop->configuration(), pi2_timestamp_on_pi2_fetcher.channel());
1450 const size_t pong_timestamp_channel = configuration::ChannelIndex(
1451 pi1_event_loop->configuration(), pong_on_pi1_fetcher.channel());
1452
1453 pi1_event_loop->MakeWatcher(
1454 "/aos/remote_timestamps/pi2",
1455 [&pi1_event_loop, pi1_timestamp_channel, ping_timestamp_channel,
1456 &pi1_timestamp_on_pi1_fetcher, &pi1_timestamp_on_pi2_fetcher,
1457 &ping_on_pi1_fetcher,
1458 &ping_on_pi2_fetcher](const MessageHeader &header) {
1459 const aos::monotonic_clock::time_point header_monotonic_sent_time(
1460 chrono::nanoseconds(header.monotonic_sent_time()));
1461 const aos::realtime_clock::time_point header_realtime_sent_time(
1462 chrono::nanoseconds(header.realtime_sent_time()));
1463 const aos::monotonic_clock::time_point header_monotonic_remote_time(
1464 chrono::nanoseconds(header.monotonic_remote_time()));
1465 const aos::realtime_clock::time_point header_realtime_remote_time(
1466 chrono::nanoseconds(header.realtime_remote_time()));
1467
1468 const Context *pi1_context = nullptr;
1469 const Context *pi2_context = nullptr;
1470
1471 if (header.channel_index() == pi1_timestamp_channel) {
1472 ASSERT_TRUE(pi1_timestamp_on_pi1_fetcher.FetchNext());
1473 ASSERT_TRUE(pi1_timestamp_on_pi2_fetcher.FetchNext());
1474 pi1_context = &pi1_timestamp_on_pi1_fetcher.context();
1475 pi2_context = &pi1_timestamp_on_pi2_fetcher.context();
1476 } else if (header.channel_index() == ping_timestamp_channel) {
1477 ASSERT_TRUE(ping_on_pi1_fetcher.FetchNext());
1478 ASSERT_TRUE(ping_on_pi2_fetcher.FetchNext());
1479 pi1_context = &ping_on_pi1_fetcher.context();
1480 pi2_context = &ping_on_pi2_fetcher.context();
1481 } else {
1482 LOG(FATAL) << "Unknown channel " << FlatbufferToJson(&header) << " "
1483 << configuration::CleanedChannelToString(
1484 pi1_event_loop->configuration()->channels()->Get(
1485 header.channel_index()));
1486 }
1487
1488 EXPECT_EQ(pi1_context->queue_index, header.remote_queue_index());
1489 EXPECT_EQ(pi2_context->remote_queue_index, header.remote_queue_index());
1490 EXPECT_EQ(pi2_context->queue_index, header.queue_index());
1491
1492 EXPECT_EQ(pi2_context->monotonic_event_time,
1493 header_monotonic_sent_time);
1494 EXPECT_EQ(pi2_context->realtime_event_time, header_realtime_sent_time);
1495 EXPECT_EQ(pi2_context->realtime_remote_time,
1496 header_realtime_remote_time);
1497 EXPECT_EQ(pi2_context->monotonic_remote_time,
1498 header_monotonic_remote_time);
1499
1500 EXPECT_EQ(pi1_context->realtime_event_time,
1501 header_realtime_remote_time);
1502 EXPECT_EQ(pi1_context->monotonic_event_time,
1503 header_monotonic_remote_time);
1504 });
1505 pi2_event_loop->MakeWatcher(
1506 "/aos/remote_timestamps/pi1",
1507 [&pi2_event_loop, pi2_timestamp_channel, pong_timestamp_channel,
1508 &pi2_timestamp_on_pi2_fetcher, &pi2_timestamp_on_pi1_fetcher,
1509 &pong_on_pi2_fetcher,
1510 &pong_on_pi1_fetcher](const MessageHeader &header) {
1511 const aos::monotonic_clock::time_point header_monotonic_sent_time(
1512 chrono::nanoseconds(header.monotonic_sent_time()));
1513 const aos::realtime_clock::time_point header_realtime_sent_time(
1514 chrono::nanoseconds(header.realtime_sent_time()));
1515 const aos::monotonic_clock::time_point header_monotonic_remote_time(
1516 chrono::nanoseconds(header.monotonic_remote_time()));
1517 const aos::realtime_clock::time_point header_realtime_remote_time(
1518 chrono::nanoseconds(header.realtime_remote_time()));
1519
1520 const Context *pi2_context = nullptr;
1521 const Context *pi1_context = nullptr;
1522
1523 if (header.channel_index() == pi2_timestamp_channel) {
1524 ASSERT_TRUE(pi2_timestamp_on_pi2_fetcher.FetchNext());
1525 ASSERT_TRUE(pi2_timestamp_on_pi1_fetcher.FetchNext());
1526 pi2_context = &pi2_timestamp_on_pi2_fetcher.context();
1527 pi1_context = &pi2_timestamp_on_pi1_fetcher.context();
1528 } else if (header.channel_index() == pong_timestamp_channel) {
1529 ASSERT_TRUE(pong_on_pi2_fetcher.FetchNext());
1530 ASSERT_TRUE(pong_on_pi1_fetcher.FetchNext());
1531 pi2_context = &pong_on_pi2_fetcher.context();
1532 pi1_context = &pong_on_pi1_fetcher.context();
1533 } else {
1534 LOG(FATAL) << "Unknown channel " << FlatbufferToJson(&header) << " "
1535 << configuration::CleanedChannelToString(
1536 pi2_event_loop->configuration()->channels()->Get(
1537 header.channel_index()));
1538 }
1539
1540 EXPECT_EQ(pi2_context->queue_index, header.remote_queue_index());
1541 EXPECT_EQ(pi1_context->remote_queue_index, header.remote_queue_index());
1542 EXPECT_EQ(pi1_context->queue_index, header.queue_index());
1543
1544 EXPECT_EQ(pi1_context->monotonic_event_time,
1545 header_monotonic_sent_time);
1546 EXPECT_EQ(pi1_context->realtime_event_time, header_realtime_sent_time);
1547 EXPECT_EQ(pi1_context->realtime_remote_time,
1548 header_realtime_remote_time);
1549 EXPECT_EQ(pi1_context->monotonic_remote_time,
1550 header_monotonic_remote_time);
1551
1552 EXPECT_EQ(pi2_context->realtime_event_time,
1553 header_realtime_remote_time);
1554 EXPECT_EQ(pi2_context->monotonic_event_time,
1555 header_monotonic_remote_time);
1556 });
1557
1558 // And confirm we can re-create a log again, while checking the contents.
1559 {
1560 LoggerState pi1_logger = MakeLogger(
1561 configuration::GetNode(log_reader_factory.configuration(), pi1_),
1562 &log_reader_factory);
1563 LoggerState pi2_logger = MakeLogger(
1564 configuration::GetNode(log_reader_factory.configuration(), pi2_),
1565 &log_reader_factory);
1566
1567 StartLogger(&pi1_logger, "relogged");
1568 StartLogger(&pi2_logger, "relogged");
1569
1570 log_reader_factory.Run();
1571 }
1572
1573 EXPECT_EQ(pi2_original_message_header_counter.count(), 0u);
1574 EXPECT_EQ(pi1_original_message_header_counter.count(), 0u);
1575
1576 reader.Deregister();
1577}
1578
Austin Schuh8bd96322020-02-13 21:18:22 -08001579// TODO(austin): We can write a test which recreates a logfile and confirms that
1580// we get it back. That is the ultimate test.
1581
Austin Schuhe309d2a2019-11-29 13:25:21 -08001582} // namespace testing
1583} // namespace logger
1584} // namespace aos