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 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 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 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 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 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 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