blob: ea5071eeac42ec006ea28ecb665ea31b9c8747b8 [file] [log] [blame]
Austin Schuh6bb8a822021-03-31 23:04:39 -07001#include <sys/stat.h>
2
Alexei Strots01395492023-03-20 13:59:56 -07003#include <filesystem>
4
Austin Schuh315b96b2020-12-11 21:21:12 -08005#include "absl/strings/str_format.h"
Philipp Schrader790cb542023-07-05 21:06:52 -07006#include "glog/logging.h"
7#include "gmock/gmock.h"
8#include "gtest/gtest.h"
9
Austin Schuhe309d2a2019-11-29 13:25:21 -080010#include "aos/events/event_loop.h"
Tyler Chatowb7c6eba2021-07-28 14:43:23 -070011#include "aos/events/logging/log_reader.h"
Austin Schuhb06f03b2021-02-17 22:00:37 -080012#include "aos/events/logging/log_writer.h"
milind1f1dca32021-07-03 13:50:07 -070013#include "aos/events/logging/snappy_encoder.h"
Austin Schuh01b4c352020-09-21 23:09:39 -070014#include "aos/events/message_counter.h"
Austin Schuhe309d2a2019-11-29 13:25:21 -080015#include "aos/events/ping_lib.h"
16#include "aos/events/pong_lib.h"
17#include "aos/events/simulated_event_loop.h"
Austin Schuh0de30f32020-12-06 12:44:28 -080018#include "aos/network/remote_message_generated.h"
Austin Schuh87dd3832021-01-01 23:07:31 -080019#include "aos/network/testing_time_converter.h"
Austin Schuh8d7e0bb2020-10-02 17:57:00 -070020#include "aos/network/timestamp_generated.h"
Austin Schuh373f1762021-06-02 21:07:09 -070021#include "aos/testing/path.h"
Austin Schuhc243b422020-10-11 15:35:08 -070022#include "aos/testing/tmpdir.h"
Austin Schuh2f8fd752020-09-01 22:38:28 -070023#include "aos/util/file.h"
Austin Schuhe309d2a2019-11-29 13:25:21 -080024
Austin Schuh3bd4c402020-11-06 18:19:06 -080025#ifdef LZMA
26#include "aos/events/logging/lzma_encoder.h"
27#endif
28
Stephan Pleinesf63bde82024-01-13 15:59:33 -080029namespace aos::logger::testing {
Austin Schuhe309d2a2019-11-29 13:25:21 -080030
31namespace chrono = std::chrono;
Austin Schuh0de30f32020-12-06 12:44:28 -080032using aos::message_bridge::RemoteMessage;
Naman Guptaa63aa132023-03-22 20:06:34 -070033using aos::testing::ArtifactPath;
Austin Schuh01b4c352020-09-21 23:09:39 -070034using aos::testing::MessageCounter;
Austin Schuhe309d2a2019-11-29 13:25:21 -080035
Austin Schuhee4713b2021-03-21 19:25:17 -070036constexpr std::string_view kSingleConfigSha256(
Austin Schuhb8bca732021-07-30 22:32:00 -070037 "bbe1b563139273b23a5405eebc2f2740cefcda5f96681acd0a84b8ff9ab93ea4");
Austin Schuh8c399962020-12-25 21:51:45 -080038
Austin Schuhe309d2a2019-11-29 13:25:21 -080039class LoggerTest : public ::testing::Test {
40 public:
41 LoggerTest()
Austin Schuh373f1762021-06-02 21:07:09 -070042 : config_(aos::configuration::ReadConfig(
43 ArtifactPath("aos/events/pingpong_config.json"))),
Austin Schuhe309d2a2019-11-29 13:25:21 -080044 event_loop_factory_(&config_.message()),
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080045 ping_event_loop_(event_loop_factory_.MakeEventLoop("ping")),
Austin Schuhe309d2a2019-11-29 13:25:21 -080046 ping_(ping_event_loop_.get()),
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080047 pong_event_loop_(event_loop_factory_.MakeEventLoop("pong")),
Austin Schuhe309d2a2019-11-29 13:25:21 -080048 pong_(pong_event_loop_.get()) {}
49
50 // Config and factory.
51 aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
52 SimulatedEventLoopFactory event_loop_factory_;
53
54 // Event loop and app for Ping
55 std::unique_ptr<EventLoop> ping_event_loop_;
56 Ping ping_;
57
58 // Event loop and app for Pong
59 std::unique_ptr<EventLoop> pong_event_loop_;
60 Pong pong_;
61};
62
Brian Silverman1f345222020-09-24 21:14:48 -070063using LoggerDeathTest = LoggerTest;
64
Austin Schuhe309d2a2019-11-29 13:25:21 -080065// Tests that we can startup at all. This confirms that the channels are all in
66// the config.
67TEST_F(LoggerTest, Starts) {
Austin Schuhc243b422020-10-11 15:35:08 -070068 const ::std::string tmpdir = aos::testing::TestTmpDir();
Austin Schuh2f8fd752020-09-01 22:38:28 -070069 const ::std::string base_name = tmpdir + "/logfile";
Austin Schuh25b46712021-01-03 00:04:38 -080070 const ::std::string config =
Austin Schuhee4713b2021-03-21 19:25:17 -070071 absl::StrCat(base_name, kSingleConfigSha256, ".bfbs");
Austin Schuh11717692022-10-16 17:11:28 -070072 const ::std::string logfile = base_name + "_data.part0.bfbs";
Austin Schuhe309d2a2019-11-29 13:25:21 -080073 // Remove it.
Austin Schuh25b46712021-01-03 00:04:38 -080074 unlink(config.c_str());
Austin Schuhe309d2a2019-11-29 13:25:21 -080075 unlink(logfile.c_str());
76
77 LOG(INFO) << "Logging data to " << logfile;
78
79 {
Austin Schuhe309d2a2019-11-29 13:25:21 -080080 event_loop_factory_.RunFor(chrono::milliseconds(95));
81
Austin Schuheeb86fc2024-04-04 20:12:39 -070082 std::unique_ptr<EventLoop> logger_event_loop =
83 event_loop_factory_.MakeEventLoop("logger");
Brian Silverman1f345222020-09-24 21:14:48 -070084 Logger logger(logger_event_loop.get());
Austin Schuh8c399962020-12-25 21:51:45 -080085 logger.set_separate_config(false);
Brian Silverman1f345222020-09-24 21:14:48 -070086 logger.set_polling_period(std::chrono::milliseconds(100));
Austin Schuh11717692022-10-16 17:11:28 -070087 logger.StartLoggingOnRun(base_name);
Austin Schuhe309d2a2019-11-29 13:25:21 -080088 event_loop_factory_.RunFor(chrono::milliseconds(20000));
89 }
90
Alexei Strots01395492023-03-20 13:59:56 -070091 ASSERT_TRUE(std::filesystem::exists(logfile));
92
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -080093 // Even though it doesn't make any difference here, exercise the logic for
94 // passing in a separate config.
95 LogReader reader(logfile, &config_.message());
Austin Schuhe309d2a2019-11-29 13:25:21 -080096
Austin Schuh15649d62019-12-28 16:36:38 -080097 // This sends out the fetched messages and advances time to the start of the
98 // log file.
James Kuszmaul84ff3e52020-01-03 19:48:53 -080099 reader.Register();
Austin Schuhe309d2a2019-11-29 13:25:21 -0800100
Austin Schuh07676622021-01-21 18:59:17 -0800101 EXPECT_THAT(reader.LoggedNodes(), ::testing::ElementsAre(nullptr));
James Kuszmaul84ff3e52020-01-03 19:48:53 -0800102
Austin Schuhe309d2a2019-11-29 13:25:21 -0800103 std::unique_ptr<EventLoop> test_event_loop =
James Kuszmaul84ff3e52020-01-03 19:48:53 -0800104 reader.event_loop_factory()->MakeEventLoop("log_reader");
Austin Schuhe309d2a2019-11-29 13:25:21 -0800105
106 int ping_count = 10;
107 int pong_count = 10;
108
Sanjay Narayanan5ec00232022-07-08 15:21:30 -0700109 // Confirm that the ping value matches.
110 test_event_loop->MakeWatcher("/test",
Austin Schuhe309d2a2019-11-29 13:25:21 -0800111 [&ping_count](const examples::Ping &ping) {
112 EXPECT_EQ(ping.value(), ping_count + 1);
113 ++ping_count;
114 });
115 // Confirm that the ping and pong counts both match, and the value also
116 // matches.
117 test_event_loop->MakeWatcher(
118 "/test", [&pong_count, &ping_count](const examples::Pong &pong) {
119 EXPECT_EQ(pong.value(), pong_count + 1);
120 ++pong_count;
121 EXPECT_EQ(ping_count, pong_count);
122 });
123
James Kuszmaul84ff3e52020-01-03 19:48:53 -0800124 reader.event_loop_factory()->RunFor(std::chrono::seconds(100));
Austin Schuhe309d2a2019-11-29 13:25:21 -0800125 EXPECT_EQ(ping_count, 2010);
Sanjay Narayanan5ec00232022-07-08 15:21:30 -0700126 EXPECT_EQ(pong_count, ping_count);
Austin Schuhe309d2a2019-11-29 13:25:21 -0800127}
128
Eric Schmiedebergae00e732023-04-12 15:53:17 -0600129// Tests that we can mutate a message before sending
130TEST_F(LoggerTest, MutateCallback) {
131 const ::std::string tmpdir = aos::testing::TestTmpDir();
132 const ::std::string base_name = tmpdir + "/logfile";
133 const ::std::string config =
134 absl::StrCat(base_name, kSingleConfigSha256, ".bfbs");
135 const ::std::string logfile = base_name + "_data.part0.bfbs";
136 // Remove it.
137 unlink(config.c_str());
138 unlink(logfile.c_str());
139
140 LOG(INFO) << "Logging data to " << logfile;
141
142 {
Eric Schmiedebergae00e732023-04-12 15:53:17 -0600143 event_loop_factory_.RunFor(chrono::milliseconds(95));
144
Austin Schuheeb86fc2024-04-04 20:12:39 -0700145 std::unique_ptr<EventLoop> logger_event_loop =
146 event_loop_factory_.MakeEventLoop("logger");
Eric Schmiedebergae00e732023-04-12 15:53:17 -0600147 Logger logger(logger_event_loop.get());
148 logger.set_separate_config(false);
149 logger.set_polling_period(std::chrono::milliseconds(100));
150 logger.StartLoggingOnRun(base_name);
151 event_loop_factory_.RunFor(chrono::milliseconds(20000));
152 }
153
154 // Even though it doesn't make any difference here, exercise the logic for
155 // passing in a separate config.
156 LogReader reader(logfile, &config_.message());
157
158 reader.AddBeforeSendCallback("/test", [](aos::examples::Ping *ping) {
159 ping->mutate_value(ping->value() + 1);
160 });
161
162 // This sends out the fetched messages and advances time to the start of the
163 // log file.
164 reader.Register();
165
166 EXPECT_THAT(reader.LoggedNodes(), ::testing::ElementsAre(nullptr));
167
168 std::unique_ptr<EventLoop> test_event_loop =
169 reader.event_loop_factory()->MakeEventLoop("log_reader");
170
171 // Confirm that the ping and pong counts both match, and the value also
172 // matches.
173 int ping_count = 10;
174 test_event_loop->MakeWatcher("/test",
175 [&ping_count](const examples::Ping &ping) {
176 ++ping_count;
177 EXPECT_EQ(ping.value(), ping_count);
178 });
179
180 reader.event_loop_factory()->RunFor(std::chrono::seconds(100));
181 EXPECT_EQ(ping_count, 2010);
182}
183
Brian Silverman1f345222020-09-24 21:14:48 -0700184// Tests calling StartLogging twice.
185TEST_F(LoggerDeathTest, ExtraStart) {
Austin Schuhc6f8f1b2020-12-02 23:23:39 -0800186 const ::std::string tmpdir = aos::testing::TestTmpDir();
Brian Silverman1f345222020-09-24 21:14:48 -0700187 const ::std::string base_name1 = tmpdir + "/logfile1";
Austin Schuh25b46712021-01-03 00:04:38 -0800188 const ::std::string config1 =
Austin Schuhee4713b2021-03-21 19:25:17 -0700189 absl::StrCat(base_name1, kSingleConfigSha256, ".bfbs");
Austin Schuh11717692022-10-16 17:11:28 -0700190 const ::std::string logfile1 = base_name1 + "_data.part0.bfbs";
Brian Silverman1f345222020-09-24 21:14:48 -0700191 const ::std::string base_name2 = tmpdir + "/logfile2";
Austin Schuh25b46712021-01-03 00:04:38 -0800192 const ::std::string config2 =
Austin Schuhee4713b2021-03-21 19:25:17 -0700193 absl::StrCat(base_name2, kSingleConfigSha256, ".bfbs");
Austin Schuh11717692022-10-16 17:11:28 -0700194 const ::std::string logfile2 = base_name2 + "_data.part0.bfbs";
Brian Silverman1f345222020-09-24 21:14:48 -0700195 unlink(logfile1.c_str());
Austin Schuh25b46712021-01-03 00:04:38 -0800196 unlink(config1.c_str());
Brian Silverman1f345222020-09-24 21:14:48 -0700197 unlink(logfile2.c_str());
Austin Schuh25b46712021-01-03 00:04:38 -0800198 unlink(config2.c_str());
Brian Silverman1f345222020-09-24 21:14:48 -0700199
200 LOG(INFO) << "Logging data to " << logfile1 << " then " << logfile2;
201
202 {
Brian Silverman1f345222020-09-24 21:14:48 -0700203 event_loop_factory_.RunFor(chrono::milliseconds(95));
204
Austin Schuheeb86fc2024-04-04 20:12:39 -0700205 std::unique_ptr<EventLoop> logger_event_loop =
206 event_loop_factory_.MakeEventLoop("logger");
Brian Silverman1f345222020-09-24 21:14:48 -0700207 Logger logger(logger_event_loop.get());
208 logger.set_polling_period(std::chrono::milliseconds(100));
Alexei Strots01395492023-03-20 13:59:56 -0700209 logger_event_loop->OnRun([base_name1, base_name2, &logger_event_loop,
210 &logger]() {
211 logger.StartLogging(std::make_unique<MultiNodeFilesLogNamer>(
212 base_name1, logger_event_loop->configuration(),
213 logger_event_loop.get(), logger_event_loop->node()));
214 EXPECT_DEATH(logger.StartLogging(std::make_unique<MultiNodeFilesLogNamer>(
215 base_name2, logger_event_loop->configuration(),
216 logger_event_loop.get(), logger_event_loop->node())),
217 "Already logging");
218 });
Brian Silverman1f345222020-09-24 21:14:48 -0700219 event_loop_factory_.RunFor(chrono::milliseconds(20000));
220 }
221}
222
James Kuszmaul94ca5132022-07-19 09:11:08 -0700223// Tests that we die if the replayer attempts to send on a logged channel.
224TEST_F(LoggerDeathTest, DieOnDuplicateReplayChannels) {
225 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
226 aos::configuration::ReadConfig(
227 ArtifactPath("aos/events/pingpong_config.json"));
228 SimulatedEventLoopFactory event_loop_factory(&config.message());
229 const ::std::string tmpdir = aos::testing::TestTmpDir();
230 const ::std::string base_name = tmpdir + "/logfile";
231 const ::std::string config_file =
232 absl::StrCat(base_name, kSingleConfigSha256, ".bfbs");
Austin Schuh11717692022-10-16 17:11:28 -0700233 const ::std::string logfile = base_name + "_data.part0.bfbs";
James Kuszmaul94ca5132022-07-19 09:11:08 -0700234 // Remove the log file.
235 unlink(config_file.c_str());
236 unlink(logfile.c_str());
237
238 LOG(INFO) << "Logging data to " << logfile;
239
240 {
241 std::unique_ptr<EventLoop> logger_event_loop =
242 event_loop_factory.MakeEventLoop("logger");
243
244 Logger logger(logger_event_loop.get());
245 logger.set_separate_config(false);
246 logger.set_polling_period(std::chrono::milliseconds(100));
Austin Schuh11717692022-10-16 17:11:28 -0700247 logger.StartLoggingOnRun(base_name);
James Kuszmaul94ca5132022-07-19 09:11:08 -0700248
249 event_loop_factory.RunFor(chrono::seconds(2));
250 }
251
252 LogReader reader(logfile);
253
254 reader.Register();
255
256 std::unique_ptr<EventLoop> test_event_loop =
257 reader.event_loop_factory()->MakeEventLoop("log_reader");
258
259 EXPECT_DEATH(test_event_loop->MakeSender<examples::Ping>("/test"),
260 "exclusive channel.*examples.Ping");
261}
262
Brian Silverman1f345222020-09-24 21:14:48 -0700263// Tests calling StopLogging twice.
264TEST_F(LoggerDeathTest, ExtraStop) {
Austin Schuhc6f8f1b2020-12-02 23:23:39 -0800265 const ::std::string tmpdir = aos::testing::TestTmpDir();
Brian Silverman1f345222020-09-24 21:14:48 -0700266 const ::std::string base_name = tmpdir + "/logfile";
Austin Schuh25b46712021-01-03 00:04:38 -0800267 const ::std::string config =
Austin Schuhee4713b2021-03-21 19:25:17 -0700268 absl::StrCat(base_name, kSingleConfigSha256, ".bfbs");
Brian Silverman1f345222020-09-24 21:14:48 -0700269 const ::std::string logfile = base_name + ".part0.bfbs";
270 // Remove it.
Austin Schuh25b46712021-01-03 00:04:38 -0800271 unlink(config.c_str());
Brian Silverman1f345222020-09-24 21:14:48 -0700272 unlink(logfile.c_str());
273
274 LOG(INFO) << "Logging data to " << logfile;
275
276 {
Brian Silverman1f345222020-09-24 21:14:48 -0700277 event_loop_factory_.RunFor(chrono::milliseconds(95));
278
Austin Schuheeb86fc2024-04-04 20:12:39 -0700279 std::unique_ptr<EventLoop> logger_event_loop =
280 event_loop_factory_.MakeEventLoop("logger");
Brian Silverman1f345222020-09-24 21:14:48 -0700281 Logger logger(logger_event_loop.get());
Austin Schuh8c399962020-12-25 21:51:45 -0800282 logger.set_separate_config(false);
Brian Silverman1f345222020-09-24 21:14:48 -0700283 logger.set_polling_period(std::chrono::milliseconds(100));
284 logger_event_loop->OnRun([base_name, &logger_event_loop, &logger]() {
Alexei Strots01395492023-03-20 13:59:56 -0700285 logger.StartLogging(std::make_unique<MultiNodeFilesLogNamer>(
Austin Schuh11717692022-10-16 17:11:28 -0700286 base_name, logger_event_loop->configuration(),
287 logger_event_loop.get(), logger_event_loop->node()));
Brian Silverman1f345222020-09-24 21:14:48 -0700288 logger.StopLogging(aos::monotonic_clock::min_time);
289 EXPECT_DEATH(logger.StopLogging(aos::monotonic_clock::min_time),
290 "Not logging right now");
291 });
292 event_loop_factory_.RunFor(chrono::milliseconds(20000));
293 }
294}
295
296// Tests that we can startup twice.
297TEST_F(LoggerTest, StartsTwice) {
Austin Schuhc6f8f1b2020-12-02 23:23:39 -0800298 const ::std::string tmpdir = aos::testing::TestTmpDir();
Brian Silverman1f345222020-09-24 21:14:48 -0700299 const ::std::string base_name1 = tmpdir + "/logfile1";
Austin Schuh25b46712021-01-03 00:04:38 -0800300 const ::std::string config1 =
Austin Schuhee4713b2021-03-21 19:25:17 -0700301 absl::StrCat(base_name1, kSingleConfigSha256, ".bfbs");
Austin Schuh11717692022-10-16 17:11:28 -0700302 const ::std::string logfile1 = base_name1 + "_data.part0.bfbs";
Brian Silverman1f345222020-09-24 21:14:48 -0700303 const ::std::string base_name2 = tmpdir + "/logfile2";
Austin Schuh25b46712021-01-03 00:04:38 -0800304 const ::std::string config2 =
Austin Schuhee4713b2021-03-21 19:25:17 -0700305 absl::StrCat(base_name2, kSingleConfigSha256, ".bfbs");
Austin Schuh11717692022-10-16 17:11:28 -0700306 const ::std::string logfile2 = base_name2 + "_data.part0.bfbs";
Brian Silverman1f345222020-09-24 21:14:48 -0700307 unlink(logfile1.c_str());
Austin Schuh25b46712021-01-03 00:04:38 -0800308 unlink(config1.c_str());
Brian Silverman1f345222020-09-24 21:14:48 -0700309 unlink(logfile2.c_str());
Austin Schuh25b46712021-01-03 00:04:38 -0800310 unlink(config2.c_str());
Brian Silverman1f345222020-09-24 21:14:48 -0700311
312 LOG(INFO) << "Logging data to " << logfile1 << " then " << logfile2;
313
314 {
Brian Silverman1f345222020-09-24 21:14:48 -0700315 event_loop_factory_.RunFor(chrono::milliseconds(95));
316
Austin Schuheeb86fc2024-04-04 20:12:39 -0700317 std::unique_ptr<EventLoop> logger_event_loop =
318 event_loop_factory_.MakeEventLoop("logger");
Brian Silverman1f345222020-09-24 21:14:48 -0700319 Logger logger(logger_event_loop.get());
Austin Schuh8c399962020-12-25 21:51:45 -0800320 logger.set_separate_config(false);
Brian Silverman1f345222020-09-24 21:14:48 -0700321 logger.set_polling_period(std::chrono::milliseconds(100));
Alexei Strots01395492023-03-20 13:59:56 -0700322 logger.StartLogging(std::make_unique<MultiNodeFilesLogNamer>(
Austin Schuh11717692022-10-16 17:11:28 -0700323 base_name1, logger_event_loop->configuration(), logger_event_loop.get(),
324 logger_event_loop->node()));
Brian Silverman1f345222020-09-24 21:14:48 -0700325 event_loop_factory_.RunFor(chrono::milliseconds(10000));
326 logger.StopLogging(logger_event_loop->monotonic_now());
327 event_loop_factory_.RunFor(chrono::milliseconds(10000));
Alexei Strots01395492023-03-20 13:59:56 -0700328 logger.StartLogging(std::make_unique<MultiNodeFilesLogNamer>(
Austin Schuh11717692022-10-16 17:11:28 -0700329 base_name2, logger_event_loop->configuration(), logger_event_loop.get(),
330 logger_event_loop->node()));
Brian Silverman1f345222020-09-24 21:14:48 -0700331 event_loop_factory_.RunFor(chrono::milliseconds(10000));
332 }
333
334 for (const auto &logfile :
335 {std::make_tuple(logfile1, 10), std::make_tuple(logfile2, 2010)}) {
336 SCOPED_TRACE(std::get<0>(logfile));
337 LogReader reader(std::get<0>(logfile));
338 reader.Register();
339
Austin Schuh07676622021-01-21 18:59:17 -0800340 EXPECT_THAT(reader.LoggedNodes(), ::testing::ElementsAre(nullptr));
Brian Silverman1f345222020-09-24 21:14:48 -0700341
342 std::unique_ptr<EventLoop> test_event_loop =
343 reader.event_loop_factory()->MakeEventLoop("log_reader");
344
345 int ping_count = std::get<1>(logfile);
346 int pong_count = std::get<1>(logfile);
347
348 // Confirm that the ping and pong counts both match, and the value also
349 // matches.
350 test_event_loop->MakeWatcher("/test",
351 [&ping_count](const examples::Ping &ping) {
352 EXPECT_EQ(ping.value(), ping_count + 1);
353 ++ping_count;
354 });
355 test_event_loop->MakeWatcher(
356 "/test", [&pong_count, &ping_count](const examples::Pong &pong) {
357 EXPECT_EQ(pong.value(), pong_count + 1);
358 ++pong_count;
359 EXPECT_EQ(ping_count, pong_count);
360 });
361
362 reader.event_loop_factory()->RunFor(std::chrono::seconds(100));
363 EXPECT_EQ(ping_count, std::get<1>(logfile) + 1000);
364 }
365}
366
Austin Schuhfa895892020-01-07 20:07:41 -0800367// Tests that we can read and write rotated log files.
368TEST_F(LoggerTest, RotatedLogFile) {
Austin Schuhc6f8f1b2020-12-02 23:23:39 -0800369 const ::std::string tmpdir = aos::testing::TestTmpDir();
Austin Schuh2f8fd752020-09-01 22:38:28 -0700370 const ::std::string base_name = tmpdir + "/logfile";
Austin Schuh25b46712021-01-03 00:04:38 -0800371 const ::std::string config =
Austin Schuhee4713b2021-03-21 19:25:17 -0700372 absl::StrCat(base_name, kSingleConfigSha256, ".bfbs");
Austin Schuh11717692022-10-16 17:11:28 -0700373 const ::std::string logfile0 = base_name + "_data.part0.bfbs";
374 const ::std::string logfile1 = base_name + "_data.part1.bfbs";
Austin Schuhfa895892020-01-07 20:07:41 -0800375 // Remove it.
Austin Schuh25b46712021-01-03 00:04:38 -0800376 unlink(config.c_str());
Austin Schuhfa895892020-01-07 20:07:41 -0800377 unlink(logfile0.c_str());
378 unlink(logfile1.c_str());
379
380 LOG(INFO) << "Logging data to " << logfile0 << " and " << logfile1;
381
382 {
Austin Schuhfa895892020-01-07 20:07:41 -0800383 event_loop_factory_.RunFor(chrono::milliseconds(95));
384
Austin Schuheeb86fc2024-04-04 20:12:39 -0700385 std::unique_ptr<EventLoop> logger_event_loop =
386 event_loop_factory_.MakeEventLoop("logger");
Brian Silverman1f345222020-09-24 21:14:48 -0700387 Logger logger(logger_event_loop.get());
Austin Schuh8c399962020-12-25 21:51:45 -0800388 logger.set_separate_config(false);
Brian Silverman1f345222020-09-24 21:14:48 -0700389 logger.set_polling_period(std::chrono::milliseconds(100));
Austin Schuh11717692022-10-16 17:11:28 -0700390 logger.StartLoggingOnRun(base_name);
Austin Schuhfa895892020-01-07 20:07:41 -0800391 event_loop_factory_.RunFor(chrono::milliseconds(10000));
Austin Schuh2f8fd752020-09-01 22:38:28 -0700392 logger.Rotate();
Austin Schuhfa895892020-01-07 20:07:41 -0800393 event_loop_factory_.RunFor(chrono::milliseconds(10000));
394 }
395
Austin Schuh64fab802020-09-09 22:47:47 -0700396 {
397 // Confirm that the UUIDs match for both the parts and the logger, and the
398 // parts_index increments.
Austin Schuhadd6eb32020-11-09 21:24:26 -0800399 std::vector<SizePrefixedFlatbufferVector<LogFileHeader>> log_header;
Austin Schuh64fab802020-09-09 22:47:47 -0700400 for (std::string_view f : {logfile0, logfile1}) {
Austin Schuh3bd4c402020-11-06 18:19:06 -0800401 log_header.emplace_back(ReadHeader(f).value());
Austin Schuh64fab802020-09-09 22:47:47 -0700402 }
403
Brian Silvermanae7c0332020-09-30 16:58:23 -0700404 EXPECT_EQ(log_header[0].message().log_event_uuid()->string_view(),
405 log_header[1].message().log_event_uuid()->string_view());
Austin Schuh64fab802020-09-09 22:47:47 -0700406 EXPECT_EQ(log_header[0].message().parts_uuid()->string_view(),
407 log_header[1].message().parts_uuid()->string_view());
408
409 EXPECT_EQ(log_header[0].message().parts_index(), 0);
410 EXPECT_EQ(log_header[1].message().parts_index(), 1);
411 }
412
Austin Schuhfa895892020-01-07 20:07:41 -0800413 // Even though it doesn't make any difference here, exercise the logic for
414 // passing in a separate config.
Austin Schuh287d43d2020-12-04 20:19:33 -0800415 LogReader reader(SortParts({logfile0, logfile1}), &config_.message());
Austin Schuhfa895892020-01-07 20:07:41 -0800416
417 // Confirm that we can remap logged channels to point to new buses.
418 reader.RemapLoggedChannel<aos::examples::Ping>("/test", "/original");
419
420 // This sends out the fetched messages and advances time to the start of the
421 // log file.
422 reader.Register();
423
Austin Schuh07676622021-01-21 18:59:17 -0800424 EXPECT_THAT(reader.LoggedNodes(), ::testing::ElementsAre(nullptr));
Austin Schuhfa895892020-01-07 20:07:41 -0800425
426 std::unique_ptr<EventLoop> test_event_loop =
427 reader.event_loop_factory()->MakeEventLoop("log_reader");
428
429 int ping_count = 10;
430 int pong_count = 10;
431
432 // Confirm that the ping value matches in the remapped channel location.
433 test_event_loop->MakeWatcher("/original/test",
434 [&ping_count](const examples::Ping &ping) {
435 EXPECT_EQ(ping.value(), ping_count + 1);
436 ++ping_count;
437 });
438 // Confirm that the ping and pong counts both match, and the value also
439 // matches.
440 test_event_loop->MakeWatcher(
441 "/test", [&pong_count, &ping_count](const examples::Pong &pong) {
442 EXPECT_EQ(pong.value(), pong_count + 1);
443 ++pong_count;
444 EXPECT_EQ(ping_count, pong_count);
445 });
446
447 reader.event_loop_factory()->RunFor(std::chrono::seconds(100));
448 EXPECT_EQ(ping_count, 2010);
449}
450
Austin Schuh4c4e0092019-12-22 16:18:03 -0800451// Tests that a large number of messages per second doesn't overwhelm writev.
452TEST_F(LoggerTest, ManyMessages) {
Austin Schuhc6f8f1b2020-12-02 23:23:39 -0800453 const ::std::string tmpdir = aos::testing::TestTmpDir();
Austin Schuh2f8fd752020-09-01 22:38:28 -0700454 const ::std::string base_name = tmpdir + "/logfile";
Austin Schuh25b46712021-01-03 00:04:38 -0800455 const ::std::string config =
Austin Schuhee4713b2021-03-21 19:25:17 -0700456 absl::StrCat(base_name, kSingleConfigSha256, ".bfbs");
Austin Schuh2f8fd752020-09-01 22:38:28 -0700457 const ::std::string logfile = base_name + ".part0.bfbs";
Austin Schuh4c4e0092019-12-22 16:18:03 -0800458 // Remove the log file.
Austin Schuh25b46712021-01-03 00:04:38 -0800459 unlink(config.c_str());
Austin Schuh4c4e0092019-12-22 16:18:03 -0800460 unlink(logfile.c_str());
461
462 LOG(INFO) << "Logging data to " << logfile;
Austin Schuh4c3b9702020-08-30 11:34:55 -0700463 ping_.set_quiet(true);
Austin Schuh4c4e0092019-12-22 16:18:03 -0800464
465 {
Austin Schuh4c4e0092019-12-22 16:18:03 -0800466 std::unique_ptr<EventLoop> logger_event_loop =
467 event_loop_factory_.MakeEventLoop("logger");
468
469 std::unique_ptr<EventLoop> ping_spammer_event_loop =
470 event_loop_factory_.MakeEventLoop("ping_spammer");
471 aos::Sender<examples::Ping> ping_sender =
472 ping_spammer_event_loop->MakeSender<examples::Ping>("/test");
473
474 aos::TimerHandler *timer_handler =
475 ping_spammer_event_loop->AddTimer([&ping_sender]() {
476 aos::Sender<examples::Ping>::Builder builder =
477 ping_sender.MakeBuilder();
478 examples::Ping::Builder ping_builder =
479 builder.MakeBuilder<examples::Ping>();
Austin Schuhbfe6c572022-01-27 20:48:20 -0800480 CHECK_EQ(builder.Send(ping_builder.Finish()), RawSender::Error::kOk);
Austin Schuh4c4e0092019-12-22 16:18:03 -0800481 });
482
483 // 100 ms / 0.05 ms -> 2000 messages. Should be enough to crash it.
484 ping_spammer_event_loop->OnRun([&ping_spammer_event_loop, timer_handler]() {
Philipp Schradera6712522023-07-05 20:25:11 -0700485 timer_handler->Schedule(ping_spammer_event_loop->monotonic_now(),
486 chrono::microseconds(50));
Austin Schuh4c4e0092019-12-22 16:18:03 -0800487 });
488
Brian Silverman1f345222020-09-24 21:14:48 -0700489 Logger logger(logger_event_loop.get());
Austin Schuh8c399962020-12-25 21:51:45 -0800490 logger.set_separate_config(false);
Brian Silverman1f345222020-09-24 21:14:48 -0700491 logger.set_polling_period(std::chrono::milliseconds(100));
Austin Schuh11717692022-10-16 17:11:28 -0700492 logger.StartLoggingOnRun(base_name);
Austin Schuh4c4e0092019-12-22 16:18:03 -0800493
494 event_loop_factory_.RunFor(chrono::milliseconds(1000));
495 }
496}
497
James Kuszmaul890c2492022-04-06 14:59:31 -0700498// Tests that we can read a logfile that has channels which were sent too fast.
499TEST(SingleNodeLoggerNoFixtureTest, ReadTooFast) {
500 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
501 aos::configuration::ReadConfig(
502 ArtifactPath("aos/events/pingpong_config.json"));
503 SimulatedEventLoopFactory event_loop_factory(&config.message());
504 const ::std::string tmpdir = aos::testing::TestTmpDir();
505 const ::std::string base_name = tmpdir + "/logfile";
506 const ::std::string config_file =
507 absl::StrCat(base_name, kSingleConfigSha256, ".bfbs");
Austin Schuh11717692022-10-16 17:11:28 -0700508 const ::std::string logfile = base_name + "_data.part0.bfbs";
James Kuszmaul890c2492022-04-06 14:59:31 -0700509 // Remove the log file.
510 unlink(config_file.c_str());
511 unlink(logfile.c_str());
512
513 LOG(INFO) << "Logging data to " << logfile;
514
515 int sent_messages = 0;
516
517 {
518 std::unique_ptr<EventLoop> logger_event_loop =
519 event_loop_factory.MakeEventLoop("logger");
520
521 std::unique_ptr<EventLoop> ping_spammer_event_loop =
522 event_loop_factory.GetNodeEventLoopFactory(nullptr)->MakeEventLoop(
523 "ping_spammer", {NodeEventLoopFactory::CheckSentTooFast::kNo,
James Kuszmaul94ca5132022-07-19 09:11:08 -0700524 NodeEventLoopFactory::ExclusiveSenders::kNo,
525 {}});
James Kuszmaul890c2492022-04-06 14:59:31 -0700526 aos::Sender<examples::Ping> ping_sender =
527 ping_spammer_event_loop->MakeSender<examples::Ping>("/test");
528
529 aos::TimerHandler *timer_handler =
530 ping_spammer_event_loop->AddTimer([&ping_sender, &sent_messages]() {
531 aos::Sender<examples::Ping>::Builder builder =
532 ping_sender.MakeBuilder();
533 examples::Ping::Builder ping_builder =
534 builder.MakeBuilder<examples::Ping>();
535 CHECK_EQ(builder.Send(ping_builder.Finish()), RawSender::Error::kOk);
536 ++sent_messages;
537 });
538
539 constexpr std::chrono::microseconds kSendPeriod{10};
Austin Schuhfff9c3a2023-06-16 18:48:23 -0700540 const int max_legal_messages = configuration::QueueSize(
541 event_loop_factory.configuration(), ping_sender.channel());
James Kuszmaul890c2492022-04-06 14:59:31 -0700542
543 ping_spammer_event_loop->OnRun(
544 [&ping_spammer_event_loop, kSendPeriod, timer_handler]() {
Philipp Schradera6712522023-07-05 20:25:11 -0700545 timer_handler->Schedule(
James Kuszmaul890c2492022-04-06 14:59:31 -0700546 ping_spammer_event_loop->monotonic_now() + kSendPeriod / 2,
547 kSendPeriod);
548 });
549
550 Logger logger(logger_event_loop.get());
551 logger.set_separate_config(false);
552 logger.set_polling_period(std::chrono::milliseconds(100));
Austin Schuh11717692022-10-16 17:11:28 -0700553 logger.StartLoggingOnRun(base_name);
James Kuszmaul890c2492022-04-06 14:59:31 -0700554
555 event_loop_factory.RunFor(kSendPeriod * max_legal_messages * 2);
556 }
557
558 LogReader reader(logfile);
559
560 reader.Register();
561
562 std::unique_ptr<EventLoop> test_event_loop =
563 reader.event_loop_factory()->MakeEventLoop("log_reader");
564
565 int replay_count = 0;
566
567 test_event_loop->MakeWatcher(
568 "/test", [&replay_count](const examples::Ping &) { ++replay_count; });
569
570 reader.event_loop_factory()->Run();
571 EXPECT_EQ(replay_count, sent_messages);
572}
573
Maxwell Gumleyd26e6292024-04-24 10:45:07 -0600574// Helper function to verify the contents of the profiling data file.
575void VerifyProfilingData(const std::filesystem::path &profiling_path) {
576 std::ifstream file(profiling_path);
577 ASSERT_TRUE(file.is_open()) << "Failed to open profiling data file.";
578
579 std::string line;
580
581 // Verify that the header is a comment starting with '#'.
582 std::getline(file, line);
583 ASSERT_THAT(line, ::testing::StartsWith("#"));
584
585 // Now, verify the contents of each line.
586 int record_count = 0;
587
588 // Track the total encoding duration.
589 uint64_t total_encoding_duration_ns = 0;
590
591 while (std::getline(file, line)) {
592 std::stringstream line_stream(line);
593 std::string cell;
594
595 // Extract each cell from the CSV line.
596 std::vector<std::string> cells;
597 while (std::getline(line_stream, cell, ',')) {
598 cells.push_back(cell);
599 }
600
601 // Expecting 5 fields: channel_name, channel_type, encode_duration_ns,
602 // encoding_start_time_ns, message_time_s.
603 ASSERT_EQ(cells.size(), 5) << "Incorrect number of fields in the CSV line.";
604
605 // Channel name and type are strings and just need to not be empty.
606 EXPECT_FALSE(cells[0].empty()) << "Channel name is empty.";
607 EXPECT_FALSE(cells[1].empty()) << "Channel type is empty.";
608
609 // Encode duration, encoding start time should be positive numbers.
610 const int64_t encode_duration_ns = std::stoll(cells[2]);
611 const int64_t encoding_start_time_ns = std::stoll(cells[3]);
612
613 ASSERT_GT(encode_duration_ns, 0)
614 << "Encode duration is not positive. Line: " << line;
615 ASSERT_GT(encoding_start_time_ns, 0)
616 << "Encoding start time is not positive. Line: " << line;
617
618 // Message time should be non-negative.
619 const double message_time_s = std::stod(cells[4]);
620 EXPECT_GE(message_time_s, 0) << "Message time is negative";
621 ++record_count;
622 total_encoding_duration_ns += encode_duration_ns;
623 }
624
625 EXPECT_GT(record_count, 0) << "Profiling data file is empty.";
626 LOG(INFO) << "Total encoding duration: " << total_encoding_duration_ns;
627}
628
629// Tests logging many messages with LZMA compression.
630TEST_F(LoggerTest, ManyMessagesLzmaWithProfiling) {
631 const std::string tmpdir = aos::testing::TestTmpDir();
632 const std::string base_name = tmpdir + "/lzma_logfile";
633 const std::string config_sha256 =
634 absl::StrCat(base_name, kSingleConfigSha256, ".bfbs");
635 const std::string logfile = absl::StrCat(base_name, ".part0.xz");
636 const std::string profiling_path =
637 absl::StrCat(tmpdir, "/encoding_profile.csv");
638
639 // Clean up any previous test artifacts.
640 unlink(config_sha256.c_str());
641 unlink(logfile.c_str());
642
643 LOG(INFO) << "Logging data to " << logfile;
644 ping_.set_quiet(true);
645
646 {
647 std::unique_ptr<aos::EventLoop> logger_event_loop =
648 event_loop_factory_.MakeEventLoop("logger");
649
650 std::unique_ptr<aos::EventLoop> ping_spammer_event_loop =
651 event_loop_factory_.MakeEventLoop("ping_spammer");
652 aos::Sender<examples::Ping> ping_sender =
653 ping_spammer_event_loop->MakeSender<examples::Ping>("/test");
654
655 aos::TimerHandler *timer_handler =
656 ping_spammer_event_loop->AddTimer([&ping_sender]() {
657 aos::Sender<examples::Ping>::Builder builder =
658 ping_sender.MakeBuilder();
659 examples::Ping::Builder ping_builder =
660 builder.MakeBuilder<examples::Ping>();
661 CHECK_EQ(builder.Send(ping_builder.Finish()),
662 aos::RawSender::Error::kOk);
663 });
664
665 // Send a message every 50 microseconds to simulate high throughput.
666 ping_spammer_event_loop->OnRun([&ping_spammer_event_loop, timer_handler]() {
667 timer_handler->Schedule(ping_spammer_event_loop->monotonic_now(),
668 std::chrono::microseconds(50));
669 });
670
671 aos::logger::Logger logger(logger_event_loop.get());
672 logger.set_separate_config(false);
673 logger.set_polling_period(std::chrono::milliseconds(100));
674
675 // Enable logger profiling.
676 logger.SetProfilingPath(profiling_path);
677
678 std::unique_ptr<aos::logger::MultiNodeFilesLogNamer> log_namer =
679 std::make_unique<aos::logger::MultiNodeFilesLogNamer>(
680 base_name, logger_event_loop->configuration(),
681 logger_event_loop.get(), logger_event_loop->node());
682#ifdef LZMA
683 // Set up LZMA encoder.
684 log_namer->set_encoder_factory([](size_t max_message_size) {
685 return std::make_unique<aos::logger::LzmaEncoder>(max_message_size, 1);
686 });
687#endif
688
689 logger.StartLogging(std::move(log_namer));
690
691 event_loop_factory_.RunFor(std::chrono::seconds(1));
692 }
693
694#ifdef LZMA
695 VerifyProfilingData(profiling_path);
696#endif
697}
698
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800699} // namespace aos::logger::testing