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