blob: 2749e46e6636df51796ceb1f36936775094df961 [file] [log] [blame]
Patrick Williamsf0af3582024-10-10 16:32:32 -04001#include "config.h"
2
3#include "log_manager.hpp"
4#include "paths.hpp"
5
6#include <phosphor-logging/commit.hpp>
7#include <sdbusplus/async.hpp>
8#include <sdbusplus/server/manager.hpp>
9#include <xyz/openbmc_project/Logging/Entry/client.hpp>
10#include <xyz/openbmc_project/Logging/event.hpp>
11
12#include <thread>
13
14#include <gmock/gmock.h>
15#include <gtest/gtest.h>
16
17namespace phosphor::logging::test
18{
19using LoggingCleared = sdbusplus::event::xyz::openbmc_project::Logging::Cleared;
20using LoggingEntry = sdbusplus::client::xyz::openbmc_project::logging::Entry<>;
21
22// Fixture to spawn the log-manager for dbus-based testing.
23class TestLogManagerDbus : public ::testing::Test
24{
25 protected:
26 // Create the daemon and sdbusplus::async::contexts.
27 void SetUp() override
28 {
29 // The daemon requires directories to be created first.
30 std::filesystem::create_directories(phosphor::logging::paths::error());
31
32 data = std::make_unique<fixture_data>();
33 }
34
35 // Stop the daemon, etc.
36 void TearDown() override
37 {
38 data.reset();
39 }
40
41 /** Run a client task, wait for it to complete, and stop daemon. */
42 template <typename T>
43 void run(T&& t)
44 {
45 data->client_ctx.spawn(std::move(t) | stdexec::then([this]() {
46 data->stop(data->client_ctx);
47 }));
48 data->client_ctx.run();
49 }
50
51 // Data for the fixture.
52 struct fixture_data
53 {
54 fixture_data() :
55 client_ctx(), server_ctx(), objManager(server_ctx, OBJ_LOGGING),
56 iMgr(server_ctx, OBJ_INTERNAL), mgr(server_ctx, OBJ_LOGGING, iMgr)
57 {
58 // Create a thread for the daemon.
59 task = std::thread([this]() {
60 server_ctx.request_name(BUSNAME_LOGGING);
61 server_ctx.run();
62 });
63 }
64
65 ~fixture_data()
66 {
67 // Stop the server and wait for the thread to exit.
68 stop(server_ctx);
69 task.join();
70 }
71
72 // Spawn a task to gracefully shutdown an sdbusplus::async::context
73 static void stop(sdbusplus::async::context& ctx)
74 {
75 ctx.spawn(stdexec::just() |
76 stdexec::then([&ctx]() { ctx.request_stop(); }));
77 }
78
79 sdbusplus::async::context client_ctx;
80 sdbusplus::async::context server_ctx;
81 sdbusplus::server::manager_t objManager;
82 internal::Manager iMgr;
83 Manager mgr;
84 std::thread task;
85 };
86
87 std::unique_ptr<fixture_data> data;
Patrick Williams6eb96bf2024-11-05 14:59:52 -050088
89 static constexpr auto journal_unavailable = "UNAVAILABLE";
90 std::string last_journal_entry()
91 {
92 if constexpr (LG2_COMMIT_JOURNAL)
93 {
94 // When running under Docker, the journal is not available and
95 // sd-journal calls just silently pass. Return a string to make
96 // it obvious.
97 if (!std::filesystem::exists("/run/systemd/journal/socket"))
98 {
99 return journal_unavailable;
100 }
101
102 sd_journal* j = nullptr;
103
104 sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
105 sd_journal_add_match(j, "SYSLOG_IDENTIFIER=test_manager_dbus_tests",
106 SIZE_MAX);
107
108 SD_JOURNAL_FOREACH_BACKWARDS(j)
109 {
110 const char* data = nullptr;
111 size_t length = 0;
112
113 sd_journal_get_data(j, "MESSAGE", (const void**)&data, &length);
114
115 std::string entry(data, length);
116 if (entry.contains("OPENBMC_MESSAGE_ID="))
117 {
118 return entry;
119 }
120 }
121 }
122
123 return "";
124 }
Patrick Williamsf0af3582024-10-10 16:32:32 -0400125};
126
127// Ensure we can successfully create and throw an sdbusplus event.
128TEST_F(TestLogManagerDbus, GenerateSimpleEvent)
129{
130 EXPECT_THROW(
131 { throw LoggingCleared("NUMBER_OF_LOGS", 1); }, LoggingCleared);
132 return;
133}
134
135// Call the synchronous version of the commit function and verify that the
136// daemon gives us a path.
137TEST_F(TestLogManagerDbus, CallCommitSync)
138{
139 auto path = lg2::commit(LoggingCleared("NUMBER_OF_LOGS", 3));
Patrick Williams6eb96bf2024-11-05 14:59:52 -0500140
141 if constexpr (LG2_COMMIT_DBUS)
142 {
143 ASSERT_FALSE(path.str.empty());
144 EXPECT_THAT(
145 path.str,
146 ::testing::StartsWith(
147 std::filesystem::path(LoggingEntry::namespace_path::value) /
148 LoggingEntry::namespace_path::entry));
149 }
150
151 if constexpr (LG2_COMMIT_JOURNAL)
152 {
153 auto entry = last_journal_entry();
154 if (entry != journal_unavailable)
155 {
156 EXPECT_THAT(entry, ::testing::HasSubstr(
157 "\"xyz.openbmc_project.Logging.Cleared\":"));
158 EXPECT_THAT(entry, ::testing::HasSubstr("\"NUMBER_OF_LOGS\":3"));
159 }
160 }
Patrick Williamsf0af3582024-10-10 16:32:32 -0400161}
162
163// Call the asynchronous version of the commit function and verify that the
164// metadata is saved correctly.
165TEST_F(TestLogManagerDbus, CallCommitAsync)
166{
167 sdbusplus::message::object_path path{};
168 std::string log_count{};
Patrick Williams247fed62024-10-31 15:12:02 -0400169 pid_t pid = 0;
170 std::string source_file{};
Patrick Williamsf0af3582024-10-10 16:32:32 -0400171
Patrick Williams247fed62024-10-31 15:12:02 -0400172 auto create_log = [&, this]() -> sdbusplus::async::task<> {
Patrick Williamsf0af3582024-10-10 16:32:32 -0400173 // Log an event.
174 path = co_await lg2::commit(data->client_ctx,
175 LoggingCleared("NUMBER_OF_LOGS", 6));
176
Patrick Williams6eb96bf2024-11-05 14:59:52 -0500177 if constexpr (LG2_COMMIT_DBUS)
Patrick Williamsf0af3582024-10-10 16:32:32 -0400178 {
Patrick Williams6eb96bf2024-11-05 14:59:52 -0500179 // Grab the additional data.
180 auto additionalData = co_await LoggingEntry(data->client_ctx)
181 .service(Entry::default_service)
182 .path(path.str)
183 .additional_data();
Patrick Williams247fed62024-10-31 15:12:02 -0400184
Patrick Williams6eb96bf2024-11-05 14:59:52 -0500185 // Extract the NUMBER_OF_LOGS, PID, and CODE_FILE.
186 for (const auto& value : additionalData)
Patrick Williamsf0af3582024-10-10 16:32:32 -0400187 {
Patrick Williamsea6d9c42024-12-11 14:58:21 -0500188 if (value.first == "NUMBER_OF_LOGS")
Patrick Williams6eb96bf2024-11-05 14:59:52 -0500189 {
Patrick Williamsea6d9c42024-12-11 14:58:21 -0500190 log_count = value.second;
Patrick Williams6eb96bf2024-11-05 14:59:52 -0500191 }
Patrick Williamsea6d9c42024-12-11 14:58:21 -0500192 if (value.first == "_PID")
Patrick Williams6eb96bf2024-11-05 14:59:52 -0500193 {
Patrick Williamsea6d9c42024-12-11 14:58:21 -0500194 pid = std::stoull(value.second);
Patrick Williams6eb96bf2024-11-05 14:59:52 -0500195 }
Patrick Williamsea6d9c42024-12-11 14:58:21 -0500196 if (value.first == "_CODE_FILE")
Patrick Williams6eb96bf2024-11-05 14:59:52 -0500197 {
Patrick Williamsea6d9c42024-12-11 14:58:21 -0500198 source_file = value.second;
Patrick Williams6eb96bf2024-11-05 14:59:52 -0500199 }
Patrick Williamsf0af3582024-10-10 16:32:32 -0400200 }
201 }
202
203 co_return;
204 };
205
206 run(create_log());
207
Patrick Williams6eb96bf2024-11-05 14:59:52 -0500208 if constexpr (LG2_COMMIT_DBUS)
209 {
210 ASSERT_FALSE(path.str.empty());
211 ASSERT_FALSE(log_count.empty());
Patrick Williamsf0af3582024-10-10 16:32:32 -0400212
Patrick Williams6eb96bf2024-11-05 14:59:52 -0500213 EXPECT_THAT(
214 path.str,
215 ::testing::StartsWith(
216 std::filesystem::path(LoggingEntry::namespace_path::value) /
217 LoggingEntry::namespace_path::entry));
Patrick Williamsf0af3582024-10-10 16:32:32 -0400218
Patrick Williams6eb96bf2024-11-05 14:59:52 -0500219 EXPECT_EQ(log_count, "6");
220 EXPECT_EQ(pid, getpid());
221 EXPECT_EQ(source_file, std::source_location::current().file_name());
222 }
223
224 if constexpr (LG2_COMMIT_JOURNAL)
225 {
226 auto entry = last_journal_entry();
227 if (entry != journal_unavailable)
228 {
229 EXPECT_THAT(entry, ::testing::HasSubstr(
230 "\"xyz.openbmc_project.Logging.Cleared\":"));
231 EXPECT_THAT(entry, ::testing::HasSubstr("\"NUMBER_OF_LOGS\":6"));
232 }
233 }
Patrick Williamsf0af3582024-10-10 16:32:32 -0400234}
235
236} // namespace phosphor::logging::test