1 #include "config.h"
2
3 #include "elog_entry.hpp"
4 #include "log_manager.hpp"
5 #include "paths.hpp"
6
7 #include <sdbusplus/bus.hpp>
8 #include <sdbusplus/test/sdbus_mock.hpp>
9
10 #include <filesystem>
11
12 #include <gmock/gmock.h>
13 #include <gtest/gtest.h>
14
15 namespace phosphor
16 {
17 namespace logging
18 {
19 namespace test
20 {
21
22 class TestQuiesceOnError : public testing::Test
23 {
24 public:
25 sdbusplus::SdBusMock sdbusMock;
26 sdbusplus::bus_t mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
27 phosphor::logging::internal::Manager manager;
28
TestQuiesceOnError()29 TestQuiesceOnError() : manager(mockedBus, OBJ_INTERNAL)
30 {
31 // Ensure any errors serializing to filesystem have directory created
32 std::filesystem::create_directories(paths::error());
33 }
34 };
35
36 // Test that false is returned when no callout is present in the log
TEST_F(TestQuiesceOnError,testNoCallout)37 TEST_F(TestQuiesceOnError, testNoCallout)
38 {
39 uint32_t id = 99;
40 uint64_t timestamp{100};
41 std::string message{"test error"};
42 std::string fwLevel{"level42"};
43 std::string path{"/tmp/99"};
44 std::map<std::string, std::string> testData{{"no", "no"},
45 {"callout", "callout"}};
46 phosphor::logging::AssociationList associations{};
47
48 Entry elog{mockedBus,
49 std::string(OBJ_ENTRY) + '/' + std::to_string(id),
50 id,
51 timestamp,
52 Entry::Level::Informational,
53 std::move(message),
54 std::move(testData),
55 std::move(associations),
56 fwLevel,
57 path,
58 manager};
59
60 EXPECT_EQ(manager.isCalloutPresent(elog), false);
61 }
62
63 // Test that trues is returned when a callout is present in the log
TEST_F(TestQuiesceOnError,testCallout)64 TEST_F(TestQuiesceOnError, testCallout)
65 {
66 uint32_t id = 99;
67 uint64_t timestamp{100};
68 std::string message{"test error"};
69 std::string fwLevel{"level42"};
70 std::string path{"/tmp/99"};
71 std::map<std::string, std::string> testData{
72 {"CALLOUT_INVENTORY_PATH",
73 "/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0/"}};
74 phosphor::logging::AssociationList associations{};
75
76 Entry elog{mockedBus,
77 std::string(OBJ_ENTRY) + '/' + std::to_string(id),
78 id,
79 timestamp,
80 Entry::Level::Informational,
81 std::move(message),
82 std::move(testData),
83 std::move(associations),
84 fwLevel,
85 path,
86 manager};
87
88 EXPECT_EQ(manager.isCalloutPresent(elog), true);
89 }
90
91 // Test that a blocking error is created on entry with callout
TEST_F(TestQuiesceOnError,testBlockingErrorsCreated)92 TEST_F(TestQuiesceOnError, testBlockingErrorsCreated)
93 {
94 uint32_t id = 100;
95 uint64_t timestamp{100};
96 std::string message{"test error"};
97 std::string fwLevel{"level42"};
98 std::string path{"/tmp/99"};
99 std::map<std::string, std::string> testData{
100 {"CALLOUT_INVENTORY_PATH",
101 "/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0/"}};
102 phosphor::logging::AssociationList associations{};
103
104 // Ensure D-Bus object created for this blocking error
105 // First allow any number of sd_bus_emit_object_added calls
106 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(testing::_, testing::_))
107 .Times(testing::AnyNumber());
108 // Second verify the new block100 object is created once
109 EXPECT_CALL(sdbusMock,
110 sd_bus_emit_object_added(
111 testing::_, testing::HasSubstr(
112 "/xyz/openbmc_project/logging/block100")))
113 .Times(1);
114
115 Entry elog{mockedBus,
116 std::string(OBJ_ENTRY) + '/' + std::to_string(id),
117 id,
118 timestamp,
119 Entry::Level::Informational,
120 std::move(message),
121 std::move(testData),
122 std::move(associations),
123 fwLevel,
124 path,
125 manager};
126
127 manager.quiesceOnError(id);
128 // Created error with callout so expect a blocking error now
129 EXPECT_EQ(manager.getBlockingErrSize(), 1);
130
131 // Now delete the error and make sure the object and entry go away
132 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(testing::_, testing::_))
133 .Times(testing::AnyNumber());
134 EXPECT_CALL(sdbusMock,
135 sd_bus_emit_object_removed(
136 testing::_, testing::HasSubstr(
137 "/xyz/openbmc_project/logging/block100")))
138 .Times(1);
139
140 // Make sure nothing happens within invalid id
141 manager.checkAndRemoveBlockingError(id + 1);
142 EXPECT_EQ(manager.getBlockingErrSize(), 1);
143
144 manager.checkAndRemoveBlockingError(id);
145 EXPECT_EQ(manager.getBlockingErrSize(), 0);
146 }
147
148 // Test that a blocking error is created on entry with callout
TEST_F(TestQuiesceOnError,testBlockingErrorsResolved)149 TEST_F(TestQuiesceOnError, testBlockingErrorsResolved)
150 {
151 uint32_t id = 101;
152 uint64_t timestamp{100};
153 std::string message{"test error"};
154 std::string fwLevel{"level42"};
155 std::string path{"/tmp/99"};
156 std::map<std::string, std::string> testData{
157 {"CALLOUT_INVENTORY_PATH",
158 "/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0/"}};
159 phosphor::logging::AssociationList associations{};
160
161 // Ensure D-Bus object created for this blocking error
162 // First allow any number of sd_bus_emit_object_added calls
163 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(testing::_, testing::_))
164 .Times(testing::AnyNumber());
165 // Second verify the new block100 object is created once
166 EXPECT_CALL(sdbusMock,
167 sd_bus_emit_object_added(
168 testing::_, testing::HasSubstr(
169 "/xyz/openbmc_project/logging/block101")))
170 .Times(1);
171
172 Entry elog{mockedBus,
173 std::string(OBJ_ENTRY) + '/' + std::to_string(id),
174 id,
175 timestamp,
176 Entry::Level::Informational,
177 std::move(message),
178 std::move(testData),
179 std::move(associations),
180 fwLevel,
181 path,
182 manager};
183
184 manager.quiesceOnError(id);
185 // Created error with callout so expect a blocking error now
186 EXPECT_EQ(manager.getBlockingErrSize(), 1);
187 // Also should have a callback create looking for entry to be resolved
188 EXPECT_EQ(manager.getEntryCallbackSize(), 1);
189
190 // Now resolve the error and make sure the object and entry go away
191 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(testing::_, testing::_))
192 .Times(testing::AnyNumber());
193 EXPECT_CALL(sdbusMock,
194 sd_bus_emit_object_removed(
195 testing::_, testing::HasSubstr(
196 "/xyz/openbmc_project/logging/block101")))
197 .Times(1);
198
199 elog.resolved(true);
200 // Note that property signal callbacks do not work in unit test so directly
201 // call the interface to find and resolve blocking entries
202 manager.checkAndRemoveBlockingError(101);
203 EXPECT_EQ(manager.getBlockingErrSize(), 0);
204 EXPECT_EQ(manager.getEntryCallbackSize(), 0);
205 }
206
207 // Test that a blocking error is only created once for an individual bmc id
TEST_F(TestQuiesceOnError,testBlockingErrorTwice)208 TEST_F(TestQuiesceOnError, testBlockingErrorTwice)
209 {
210 uint32_t id = 100;
211 uint64_t timestamp{100};
212 std::string message{"test error"};
213 std::string fwLevel{"level42"};
214 std::string path{"/tmp/99"};
215 std::map<std::string, std::string> testData{
216 {"CALLOUT_INVENTORY_PATH",
217 "/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0/"}};
218 phosphor::logging::AssociationList associations{};
219
220 // Ensure D-Bus object created for this blocking error
221 // First allow any number of sd_bus_emit_object_added calls
222 EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(testing::_, testing::_))
223 .Times(testing::AnyNumber());
224 // Second verify the new block100 object is created once
225 EXPECT_CALL(sdbusMock,
226 sd_bus_emit_object_added(
227 testing::_, testing::HasSubstr(
228 "/xyz/openbmc_project/logging/block100")))
229 .Times(1);
230
231 Entry elog{mockedBus,
232 std::string(OBJ_ENTRY) + '/' + std::to_string(id),
233 id,
234 timestamp,
235 Entry::Level::Informational,
236 std::move(message),
237 std::move(testData),
238 std::move(associations),
239 fwLevel,
240 path,
241 manager};
242
243 manager.quiesceOnError(id);
244 // Created error with callout so expect a blocking error now
245 EXPECT_EQ(manager.getBlockingErrSize(), 1);
246
247 // Now pass in same ID and make sure it's ignored
248 manager.quiesceOnError(id);
249
250 // Now delete the error and make sure the object and entry go away
251 EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(testing::_, testing::_))
252 .Times(testing::AnyNumber());
253 EXPECT_CALL(sdbusMock,
254 sd_bus_emit_object_removed(
255 testing::_, testing::HasSubstr(
256 "/xyz/openbmc_project/logging/block100")))
257 .Times(1);
258
259 manager.checkAndRemoveBlockingError(id);
260 EXPECT_EQ(manager.getBlockingErrSize(), 0);
261 }
262
263 } // namespace test
264 } // namespace logging
265 } // namespace phosphor
266