1 #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 
17 namespace phosphor::logging::test
18 {
19 using LoggingCleared = sdbusplus::event::xyz::openbmc_project::Logging::Cleared;
20 using LoggingEntry = sdbusplus::client::xyz::openbmc_project::logging::Entry<>;
21 
22 // Fixture to spawn the log-manager for dbus-based testing.
23 class 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;
88 };
89 
90 // Ensure we can successfully create and throw an sdbusplus event.
91 TEST_F(TestLogManagerDbus, GenerateSimpleEvent)
92 {
93     EXPECT_THROW(
94         { throw LoggingCleared("NUMBER_OF_LOGS", 1); }, LoggingCleared);
95     return;
96 }
97 
98 // Call the synchronous version of the commit function and verify that the
99 // daemon gives us a path.
100 TEST_F(TestLogManagerDbus, CallCommitSync)
101 {
102     auto path = lg2::commit(LoggingCleared("NUMBER_OF_LOGS", 3));
103     ASSERT_FALSE(path.str.empty());
104     EXPECT_THAT(path.str,
105                 ::testing::StartsWith(
106                     std::filesystem::path(LoggingEntry::namespace_path::value) /
107                     LoggingEntry::namespace_path::entry));
108 }
109 
110 // Call the asynchronous version of the commit function and verify that the
111 // metadata is saved correctly.
112 TEST_F(TestLogManagerDbus, CallCommitAsync)
113 {
114     sdbusplus::message::object_path path{};
115     std::string log_count{};
116 
117     auto create_log = [this, &path, &log_count]() -> sdbusplus::async::task<> {
118         // Log an event.
119         path = co_await lg2::commit(data->client_ctx,
120                                     LoggingCleared("NUMBER_OF_LOGS", 6));
121 
122         // Grab the additional data.
123         auto additionalData = co_await LoggingEntry(data->client_ctx)
124                                   .service(Entry::default_service)
125                                   .path(path.str)
126                                   .additional_data();
127 
128         // Extract the NUMBER_OF_LOGS.
129         for (const auto& value : additionalData)
130         {
131             if (value.starts_with("NUMBER_OF_LOGS="))
132             {
133                 log_count = value.substr(value.find_first_of('=') + 1);
134             }
135         }
136 
137         co_return;
138     };
139 
140     run(create_log());
141 
142     ASSERT_FALSE(path.str.empty());
143     ASSERT_FALSE(log_count.empty());
144 
145     EXPECT_THAT(path.str,
146                 ::testing::StartsWith(
147                     std::filesystem::path(LoggingEntry::namespace_path::value) /
148                     LoggingEntry::namespace_path::entry));
149 
150     EXPECT_EQ(log_count, "6");
151 }
152 
153 } // namespace phosphor::logging::test
154