1 /** 2 * Copyright © 2019 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "extensions/openpower-pels/manager.hpp" 17 #include "log_manager.hpp" 18 #include "mocks.hpp" 19 #include "pel_utils.hpp" 20 21 #include <fstream> 22 #include <regex> 23 #include <sdbusplus/test/sdbus_mock.hpp> 24 #include <xyz/openbmc_project/Common/error.hpp> 25 26 #include <gtest/gtest.h> 27 28 using namespace openpower::pels; 29 namespace fs = std::filesystem; 30 31 using ::testing::NiceMock; 32 using ::testing::Return; 33 34 class TestLogger 35 { 36 public: 37 void log(const std::string& name, phosphor::logging::Entry::Level level, 38 const EventLogger::ADMap& additionalData) 39 { 40 errName = name; 41 errLevel = level; 42 ad = additionalData; 43 } 44 45 std::string errName; 46 phosphor::logging::Entry::Level errLevel; 47 EventLogger::ADMap ad; 48 }; 49 50 class ManagerTest : public CleanPELFiles 51 { 52 public: 53 ManagerTest() : 54 bus(sdbusplus::get_mocked_new(&sdbusInterface)), 55 logManager(bus, "logging_path") 56 { 57 sd_event_default(&sdEvent); 58 } 59 60 ~ManagerTest() 61 { 62 sd_event_unref(sdEvent); 63 } 64 65 NiceMock<sdbusplus::SdBusMock> sdbusInterface; 66 sdbusplus::bus_t bus; 67 phosphor::logging::internal::Manager logManager; 68 sd_event* sdEvent; 69 TestLogger logger; 70 }; 71 72 fs::path makeTempDir() 73 { 74 char path[] = "/tmp/tempnameXXXXXX"; 75 std::filesystem::path dir = mkdtemp(path); 76 return dir; 77 } 78 79 std::optional<fs::path> findAnyPELInRepo() 80 { 81 // PELs are named <timestamp>_<ID> 82 std::regex expr{"\\d+_\\d+"}; 83 84 for (auto& f : fs::directory_iterator(getPELRepoPath() / "logs")) 85 { 86 if (std::regex_search(f.path().string(), expr)) 87 { 88 return f.path(); 89 } 90 } 91 return std::nullopt; 92 } 93 94 size_t countPELsInRepo() 95 { 96 size_t count = 0; 97 std::regex expr{"\\d+_\\d+"}; 98 99 for (auto& f : fs::directory_iterator(getPELRepoPath() / "logs")) 100 { 101 if (std::regex_search(f.path().string(), expr)) 102 { 103 count++; 104 } 105 } 106 return count; 107 } 108 109 void deletePELFile(uint32_t id) 110 { 111 char search[20]; 112 113 sprintf(search, "\\d+_%.8X", id); 114 std::regex expr{search}; 115 116 for (auto& f : fs::directory_iterator(getPELRepoPath() / "logs")) 117 { 118 if (std::regex_search(f.path().string(), expr)) 119 { 120 fs::remove(f.path()); 121 break; 122 } 123 } 124 } 125 126 // Test that using the RAWPEL=<file> with the Manager::create() call gets 127 // a PEL saved in the repository. 128 TEST_F(ManagerTest, TestCreateWithPEL) 129 { 130 std::unique_ptr<DataInterfaceBase> dataIface = 131 std::make_unique<MockDataInterface>(); 132 133 openpower::pels::Manager manager{ 134 logManager, std::move(dataIface), 135 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1, 136 std::placeholders::_2, std::placeholders::_3)}; 137 138 // Create a PEL, write it to a file, and pass that filename into 139 // the create function. 140 auto data = pelDataFactory(TestPELType::pelSimple); 141 142 fs::path pelFilename = makeTempDir() / "rawpel"; 143 std::ofstream pelFile{pelFilename}; 144 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size()); 145 pelFile.close(); 146 147 std::string adItem = "RAWPEL=" + pelFilename.string(); 148 std::vector<std::string> additionalData{adItem}; 149 std::vector<std::string> associations; 150 151 manager.create("error message", 42, 0, 152 phosphor::logging::Entry::Level::Error, additionalData, 153 associations); 154 155 // Find the file in the PEL repository directory 156 auto pelPathInRepo = findAnyPELInRepo(); 157 158 EXPECT_TRUE(pelPathInRepo); 159 160 // Now remove it based on its OpenBMC event log ID 161 manager.erase(42); 162 163 pelPathInRepo = findAnyPELInRepo(); 164 165 EXPECT_FALSE(pelPathInRepo); 166 167 fs::remove_all(pelFilename.parent_path()); 168 } 169 170 TEST_F(ManagerTest, TestCreateWithInvalidPEL) 171 { 172 std::unique_ptr<DataInterfaceBase> dataIface = 173 std::make_unique<MockDataInterface>(); 174 175 openpower::pels::Manager manager{ 176 logManager, std::move(dataIface), 177 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1, 178 std::placeholders::_2, std::placeholders::_3)}; 179 180 // Create a PEL, write it to a file, and pass that filename into 181 // the create function. 182 auto data = pelDataFactory(TestPELType::pelSimple); 183 184 // Truncate it to make it invalid. 185 data.resize(200); 186 187 fs::path pelFilename = makeTempDir() / "rawpel"; 188 std::ofstream pelFile{pelFilename}; 189 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size()); 190 pelFile.close(); 191 192 std::string adItem = "RAWPEL=" + pelFilename.string(); 193 std::vector<std::string> additionalData{adItem}; 194 std::vector<std::string> associations; 195 196 manager.create("error message", 42, 0, 197 phosphor::logging::Entry::Level::Error, additionalData, 198 associations); 199 200 // Run the event loop to log the bad PEL event 201 sdeventplus::Event e{sdEvent}; 202 e.run(std::chrono::milliseconds(1)); 203 204 PEL invalidPEL{data}; 205 EXPECT_EQ(logger.errName, "org.open_power.Logging.Error.BadHostPEL"); 206 EXPECT_EQ(logger.errLevel, phosphor::logging::Entry::Level::Error); 207 EXPECT_EQ(std::stoi(logger.ad["PLID"], nullptr, 16), invalidPEL.plid()); 208 EXPECT_EQ(logger.ad["OBMC_LOG_ID"], "42"); 209 EXPECT_EQ(logger.ad["SRC"], (*invalidPEL.primarySRC())->asciiString()); 210 EXPECT_EQ(logger.ad["PEL_SIZE"], std::to_string(data.size())); 211 212 // Check that the bad PEL data was saved to a file. 213 auto badPELData = readPELFile(getPELRepoPath() / "badPEL"); 214 EXPECT_EQ(*badPELData, data); 215 216 fs::remove_all(pelFilename.parent_path()); 217 } 218 219 // Test that the message registry can be used to build a PEL. 220 TEST_F(ManagerTest, TestCreateWithMessageRegistry) 221 { 222 const auto registry = R"( 223 { 224 "PELs": 225 [ 226 { 227 "Name": "xyz.openbmc_project.Error.Test", 228 "Subsystem": "power_supply", 229 "ActionFlags": ["service_action", "report"], 230 "SRC": 231 { 232 "ReasonCode": "0x2030" 233 }, 234 "Callouts": [ 235 { 236 "CalloutList": [ 237 {"Priority": "high", "Procedure": "bmc_code"}, 238 {"Priority": "medium", "SymbolicFRU": "service_docs"} 239 ] 240 } 241 ], 242 "Documentation": 243 { 244 "Description": "A PGOOD Fault", 245 "Message": "PS had a PGOOD Fault" 246 } 247 }, 248 { 249 "Name": "xyz.openbmc_project.Logging.Error.Default", 250 "Subsystem": "bmc_firmware", 251 "SRC": 252 { 253 "ReasonCode": "0x2031" 254 }, 255 "Documentation": 256 { 257 "Description": "The entry used when no match found", 258 "Message": "This is a generic SRC" 259 } 260 } 261 ] 262 } 263 )"; 264 265 auto path = getPELReadOnlyDataPath(); 266 fs::create_directories(path); 267 path /= "message_registry.json"; 268 269 std::ofstream registryFile{path}; 270 registryFile << registry; 271 registryFile.close(); 272 273 std::unique_ptr<DataInterfaceBase> dataIface = 274 std::make_unique<MockDataInterface>(); 275 276 MockDataInterface* mockIface = 277 reinterpret_cast<MockDataInterface*>(dataIface.get()); 278 279 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 280 "system/entry"}; 281 EXPECT_CALL(*mockIface, checkDumpStatus(dumpType)) 282 .WillRepeatedly(Return(std::vector<bool>{false, false, false})); 283 284 openpower::pels::Manager manager{ 285 logManager, std::move(dataIface), 286 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1, 287 std::placeholders::_2, std::placeholders::_3)}; 288 289 std::vector<std::string> additionalData{"FOO=BAR"}; 290 std::vector<std::string> associations; 291 292 // Create the event log to create the PEL from. 293 manager.create("xyz.openbmc_project.Error.Test", 33, 0, 294 phosphor::logging::Entry::Level::Error, additionalData, 295 associations); 296 297 // Ensure a PEL was created in the repository 298 auto pelFile = findAnyPELInRepo(); 299 ASSERT_TRUE(pelFile); 300 301 auto data = readPELFile(*pelFile); 302 PEL pel(*data); 303 304 // Spot check it. Other testcases cover the details. 305 EXPECT_TRUE(pel.valid()); 306 EXPECT_EQ(pel.obmcLogID(), 33); 307 EXPECT_EQ(pel.primarySRC().value()->asciiString(), 308 "BD612030 "); 309 // Check if the eventId creation is good 310 EXPECT_EQ(manager.getEventId(pel), 311 "BD612030 00000055 00000010 00000000 00000000 00000000 00000000 " 312 "00000000 00000000"); 313 // Check if resolution property creation is good 314 EXPECT_EQ(manager.getResolution(pel), 315 "1. Priority: High, Procedure: BMC0001\n2. Priority: Medium, PN: " 316 "SVCDOCS\n"); 317 318 // Remove it 319 manager.erase(33); 320 pelFile = findAnyPELInRepo(); 321 EXPECT_FALSE(pelFile); 322 323 // Create an event log that can't be found in the registry. 324 // In this case, xyz.openbmc_project.Logging.Error.Default will 325 // be used as the key instead to find a registry match. 326 manager.create("xyz.openbmc_project.Error.Foo", 42, 0, 327 phosphor::logging::Entry::Level::Error, additionalData, 328 associations); 329 330 // Ensure a PEL was still created in the repository 331 pelFile = findAnyPELInRepo(); 332 ASSERT_TRUE(pelFile); 333 334 data = readPELFile(*pelFile); 335 PEL newPEL(*data); 336 337 EXPECT_TRUE(newPEL.valid()); 338 EXPECT_EQ(newPEL.obmcLogID(), 42); 339 EXPECT_EQ(newPEL.primarySRC().value()->asciiString(), 340 "BD8D2031 "); 341 342 // Check for both the original AdditionalData item as well as 343 // the ERROR_NAME item that should contain the error message 344 // property that wasn't found. 345 std::string errorName; 346 std::string adItem; 347 348 for (const auto& section : newPEL.optionalSections()) 349 { 350 if (SectionID::userData == static_cast<SectionID>(section->header().id)) 351 { 352 if (UserDataFormat::json == 353 static_cast<UserDataFormat>(section->header().subType)) 354 { 355 auto ud = static_cast<UserData*>(section.get()); 356 357 // Check that there was a UserData section added that 358 // contains debug details about the device. 359 const auto& d = ud->data(); 360 std::string jsonString{d.begin(), d.end()}; 361 auto json = nlohmann::json::parse(jsonString); 362 363 if (json.contains("ERROR_NAME")) 364 { 365 errorName = json["ERROR_NAME"].get<std::string>(); 366 } 367 368 if (json.contains("FOO")) 369 { 370 adItem = json["FOO"].get<std::string>(); 371 } 372 } 373 } 374 if (!errorName.empty()) 375 { 376 break; 377 } 378 } 379 380 EXPECT_EQ(errorName, "xyz.openbmc_project.Error.Foo"); 381 EXPECT_EQ(adItem, "BAR"); 382 } 383 384 TEST_F(ManagerTest, TestDBusMethods) 385 { 386 std::unique_ptr<DataInterfaceBase> dataIface = 387 std::make_unique<MockDataInterface>(); 388 389 Manager manager{logManager, std::move(dataIface), 390 std::bind(std::mem_fn(&TestLogger::log), &logger, 391 std::placeholders::_1, std::placeholders::_2, 392 std::placeholders::_3)}; 393 394 // Create a PEL, write it to a file, and pass that filename into 395 // the create function so there's one in the repo. 396 auto data = pelDataFactory(TestPELType::pelSimple); 397 398 fs::path pelFilename = makeTempDir() / "rawpel"; 399 std::ofstream pelFile{pelFilename}; 400 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size()); 401 pelFile.close(); 402 403 std::string adItem = "RAWPEL=" + pelFilename.string(); 404 std::vector<std::string> additionalData{adItem}; 405 std::vector<std::string> associations; 406 407 manager.create("error message", 42, 0, 408 phosphor::logging::Entry::Level::Error, additionalData, 409 associations); 410 411 // getPELFromOBMCID 412 auto newData = manager.getPELFromOBMCID(42); 413 EXPECT_EQ(newData.size(), data.size()); 414 415 // Read the PEL to get the ID for later 416 PEL pel{newData}; 417 auto id = pel.id(); 418 419 EXPECT_THROW( 420 manager.getPELFromOBMCID(id + 1), 421 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 422 423 // getPEL 424 auto unixfd = manager.getPEL(id); 425 426 // Get the size 427 struct stat s; 428 int r = fstat(unixfd, &s); 429 ASSERT_EQ(r, 0); 430 auto size = s.st_size; 431 432 // Open the FD and check the contents 433 FILE* fp = fdopen(unixfd, "r"); 434 ASSERT_NE(fp, nullptr); 435 436 std::vector<uint8_t> fdData; 437 fdData.resize(size); 438 r = fread(fdData.data(), 1, size, fp); 439 EXPECT_EQ(r, size); 440 441 EXPECT_EQ(newData, fdData); 442 443 fclose(fp); 444 445 // Run the event loop to close the FD 446 sdeventplus::Event e{sdEvent}; 447 e.run(std::chrono::milliseconds(1)); 448 449 EXPECT_THROW( 450 manager.getPEL(id + 1), 451 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 452 453 // hostAck 454 manager.hostAck(id); 455 456 EXPECT_THROW( 457 manager.hostAck(id + 1), 458 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 459 460 // hostReject 461 manager.hostReject(id, Manager::RejectionReason::BadPEL); 462 463 // Run the event loop to log the bad PEL event 464 e.run(std::chrono::milliseconds(1)); 465 466 EXPECT_EQ(logger.errName, "org.open_power.Logging.Error.SentBadPELToHost"); 467 EXPECT_EQ(id, std::stoi(logger.ad["BAD_ID"], nullptr, 16)); 468 469 manager.hostReject(id, Manager::RejectionReason::HostFull); 470 471 EXPECT_THROW( 472 manager.hostReject(id + 1, Manager::RejectionReason::BadPEL), 473 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 474 475 fs::remove_all(pelFilename.parent_path()); 476 477 // GetPELIdFromBMCLogId 478 EXPECT_EQ(pel.id(), manager.getPELIdFromBMCLogId(pel.obmcLogID())); 479 EXPECT_THROW( 480 manager.getPELIdFromBMCLogId(pel.obmcLogID() + 1), 481 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 482 483 // GetBMCLogIdFromPELId 484 EXPECT_EQ(pel.obmcLogID(), manager.getBMCLogIdFromPELId(pel.id())); 485 EXPECT_THROW( 486 manager.getBMCLogIdFromPELId(pel.id() + 1), 487 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 488 } 489 490 // An ESEL from the wild 491 const std::string esel{ 492 "00 00 df 00 00 00 00 20 00 04 12 01 6f aa 00 00 " 493 "50 48 00 30 01 00 33 00 00 00 00 07 5c 69 cc 0d 00 00 00 07 5c d5 50 db " 494 "42 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 90 00 00 4e 90 00 00 4e " 495 "55 48 00 18 01 00 09 00 8a 03 40 00 00 00 00 00 ff ff 00 00 00 00 00 00 " 496 "50 53 00 50 01 01 00 00 02 00 00 09 33 2d 00 48 00 00 00 e0 00 00 10 00 " 497 "00 00 00 00 00 20 00 00 00 0c 00 02 00 00 00 fa 00 00 0c e4 00 00 00 12 " 498 "42 43 38 41 33 33 32 44 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 " 499 "20 20 20 20 20 20 20 20 55 44 00 1c 01 06 01 00 02 54 41 4b 00 00 00 06 " 500 "00 00 00 55 00 01 f9 20 00 00 00 00 55 44 00 24 01 06 01 00 01 54 41 4b " 501 "00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 23 01 00 02 00 05 00 00 " 502 "55 44 00 0c 01 0b 01 00 0f 01 00 00 55 44 00 10 01 04 01 00 0f 9f de 6a " 503 "00 01 00 00 55 44 00 7c 00 0c 01 00 00 13 0c 02 00 fa 0c e4 16 00 01 2c " 504 "0c 1c 16 00 00 fa 0a f0 14 00 00 fa 0b b8 14 00 00 be 09 60 12 00 01 2c " 505 "0d 7a 12 00 00 fa 0c 4e 10 00 00 fa 0c e4 10 00 00 be 0a 8c 16 00 01 2c " 506 "0c 1c 16 00 01 09 09 f6 16 00 00 fa 09 f6 14 00 00 fa 0b b8 14 00 00 fa " 507 "0a f0 14 00 00 be 08 ca 12 00 01 2c 0c e4 12 00 00 fa 0b 54 10 00 00 fa " 508 "0c 2d 10 00 00 be 08 ca 55 44 00 58 01 03 01 00 00 00 00 00 00 05 31 64 " 509 "00 00 00 00 00 05 0d d4 00 00 00 00 40 5f 06 e0 00 00 00 00 40 5d d2 00 " 510 "00 00 00 00 40 57 d3 d0 00 00 00 00 40 58 f6 a0 00 00 00 00 40 54 c9 34 " 511 "00 00 00 00 40 55 9a 10 00 00 00 00 40 4c 0a 80 00 00 00 00 00 00 27 14 " 512 "55 44 01 84 01 01 01 00 48 6f 73 74 62 6f 6f 74 20 42 75 69 6c 64 20 49 " 513 "44 3a 20 68 6f 73 74 62 6f 6f 74 2d 66 65 63 37 34 64 66 2d 70 30 61 38 " 514 "37 64 63 34 2f 68 62 69 63 6f 72 65 2e 62 69 6e 00 49 42 4d 2d 77 69 74 " 515 "68 65 72 73 70 6f 6f 6e 2d 4f 50 39 2d 76 32 2e 34 2d 39 2e 32 33 34 0a " 516 "09 6f 70 2d 62 75 69 6c 64 2d 38 32 66 34 63 66 30 0a 09 62 75 69 6c 64 " 517 "72 6f 6f 74 2d 32 30 31 39 2e 30 35 2e 32 2d 31 30 2d 67 38 39 35 39 31 " 518 "31 34 0a 09 73 6b 69 62 6f 6f 74 2d 76 36 2e 35 2d 31 38 2d 67 34 37 30 " 519 "66 66 62 35 66 32 39 64 37 0a 09 68 6f 73 74 62 6f 6f 74 2d 66 65 63 37 " 520 "34 64 66 2d 70 30 61 38 37 64 63 34 0a 09 6f 63 63 2d 65 34 35 39 37 61 " 521 "62 0a 09 6c 69 6e 75 78 2d 35 2e 32 2e 31 37 2d 6f 70 65 6e 70 6f 77 65 " 522 "72 31 2d 70 64 64 63 63 30 33 33 0a 09 70 65 74 69 74 62 6f 6f 74 2d 76 " 523 "31 2e 31 30 2e 34 0a 09 6d 61 63 68 69 6e 65 2d 78 6d 6c 2d 63 36 32 32 " 524 "63 62 35 2d 70 37 65 63 61 62 33 64 0a 09 68 6f 73 74 62 6f 6f 74 2d 62 " 525 "69 6e 61 72 69 65 73 2d 36 36 65 39 61 36 30 0a 09 63 61 70 70 2d 75 63 " 526 "6f 64 65 2d 70 39 2d 64 64 32 2d 76 34 0a 09 73 62 65 2d 36 30 33 33 30 " 527 "65 30 0a 09 68 63 6f 64 65 2d 68 77 30 39 32 31 31 39 61 2e 6f 70 6d 73 " 528 "74 0a 00 00 55 44 00 70 01 04 01 00 0f 9f de 6a 00 05 00 00 07 5f 1d f4 " 529 "30 32 43 59 34 37 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " 530 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " 531 "0b ac 54 02 59 41 31 39 33 34 36 39 37 30 35 38 00 00 00 00 00 00 05 22 " 532 "a1 58 01 8a 00 58 40 20 17 18 4d 2c 00 00 00 fc 01 a1 00 00 55 44 00 14 " 533 "01 08 01 00 00 00 00 01 00 00 00 5a 00 00 00 05 55 44 03 fc 01 15 31 00 " 534 "01 28 00 42 46 41 50 49 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 f4 " 535 "00 00 00 00 00 00 03 f4 00 00 00 0b 00 00 00 00 00 00 00 3d 2c 9b c2 84 " 536 "00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 09 " 537 "00 00 00 00 00 11 bd 20 00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 " 538 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 00 00 00 00 00 00 01 2c " 539 "00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0c 1c 00 00 00 64 00 00 00 3d " 540 "2c 9b d1 11 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 " 541 "00 00 00 0a 00 00 00 00 00 13 b5 a0 00 00 00 00 00 01 f8 80 00 00 00 00 " 542 "00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 " 543 "00 00 00 be 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0a 8c 00 00 00 64 " 544 "00 00 00 3d 2c 9b df 98 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 " 545 "00 00 00 00 00 00 00 0b 00 00 00 00 00 15 ae 20 00 00 00 00 00 01 f8 80 " 546 "00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 " 547 "00 00 00 00 00 00 00 fa 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0c e4 " 548 "00 00 00 64 00 00 00 3d 2c 9b ea b7 00 00 01 e4 00 48 43 4f fb ed 70 b1 " 549 "00 00 02 01 00 00 00 00 00 00 00 0c 00 00 00 00 00 17 a6 a0 00 00 00 00 " 550 "00 01 f8 80 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 " 551 "00 00 00 12 00 00 00 00 00 00 00 fa 00 00 00 00 00 00 07 d0 00 00 00 00 " 552 "00 00 0c 4e 00 00 00 64 00 00 00 3d 2c 9b f6 27 00 00 01 e4 00 48 43 4f " 553 "fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 0d 00 00 00 00 00 19 9f 20 " 554 "00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 " 555 "00 00 00 00 00 00 00 12 00 00 00 00 00 00 01 2c 00 00 00 00 00 00 07 d0 " 556 "00 00 00 00 00 00 0d 7a 00 00 00 64 00 00 00 3d 2c 9c 05 75 00 00 01 e4 " 557 "00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 0e 00 00 00 00 " 558 "00 1b 97 a0 00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 00 00 00 00 " 559 "00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00 be 00 00 00 00 " 560 "00 00 07 d0 00 00 00 00 00 00 09 60 00 00 00 64 00 00 00 3d 2c 9c 11 29 " 561 "00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 0f " 562 "00 00 00 00 00 1d 90 20 00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 " 563 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00 fa " 564 "00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0b b8 00 00 00 64 00 00 00 3d " 565 "2c 9c 1c 45 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 " 566 "00 00 00 10 00 00 00 00 00 1f 88 a0 00 00 00 00 00 01 f8 80 00 00 00 00 " 567 "00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 00 00 00 00 " 568 "00 00 00 fa 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0a f0 00 00 00 64 " 569 "00 00 00 3d 2c 9c 2b 14 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 " 570 "00 00 00 00 00 00 00 11 00 00 00 00 00 21 81 20 00 00 00 00 00 01 f8 80 " 571 "00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 " 572 "00 00 00 00 00 00 01 2c 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0c 1c " 573 "00 00 00 64 00 00 00 3d 2d 6d 8f 9e 00 00 01 e4 00 00 43 4f 52 d7 9c 36 " 574 "00 00 04 73 00 00 00 1c 00 00 00 3d 2d 6d 99 ac 00 00 01 e4 00 10 43 4f " 575 "3f f2 02 3d 00 00 05 58 00 00 00 00 02 00 00 01 00 00 00 00 00 00 00 40 " 576 "00 00 00 2c 55 44 00 30 01 15 31 00 01 28 00 42 46 41 50 49 5f 44 42 47 " 577 "00 00 00 00 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00 28 00 00 00 00 " 578 "00 00 00 00 55 44 01 74 01 15 31 00 01 28 00 42 46 41 50 49 5f 49 00 00 " 579 "00 00 00 00 00 00 00 00 00 00 01 6c 00 00 00 00 00 00 01 6c 00 00 00 0b " 580 "00 00 00 00 00 00 00 3c 0d 52 18 5e 00 00 01 e4 00 08 43 4f 46 79 94 13 " 581 "00 00 0a 5b 00 00 00 00 00 00 2c 00 00 00 00 24 00 00 00 3c 0d 6b 26 6c " 582 "00 00 01 e4 00 00 43 4f 4e 9b 18 74 00 00 01 03 00 00 00 1c 00 00 00 3c " 583 "12 b9 2d 13 00 00 01 e4 00 00 43 4f ea 31 ed d4 00 00 05 c4 00 00 00 1c " 584 "00 00 00 3c 13 02 73 53 00 00 01 e4 00 00 43 4f ea 31 ed d4 00 00 05 c4 " 585 "00 00 00 1c 00 00 00 3c 13 04 7c 94 00 00 01 e4 00 00 43 4f ea 31 ed d4 " 586 "00 00 05 c4 00 00 00 1c 00 00 00 3c 13 06 ad e1 00 00 01 e4 00 00 43 4f " 587 "ea 31 ed d4 00 00 05 c4 00 00 00 1c 00 00 00 3c 13 07 3f 77 00 00 01 e4 " 588 "00 00 43 4f 5e 4a 55 32 00 00 10 f2 00 00 00 1c 00 00 00 3c 13 07 4e e4 " 589 "00 00 01 e4 00 00 43 4f 5e 4a 55 32 00 00 0d 68 00 00 00 1c 00 00 00 3c " 590 "13 36 79 18 00 00 01 e4 00 00 43 4f ea 31 ed d4 00 00 05 c4 00 00 00 1c " 591 "00 00 00 3d 2c 9c 36 70 00 00 01 e4 00 00 43 4f 23 45 90 97 00 00 02 47 " 592 "00 00 00 1c 00 00 00 3d 2d 6d a3 ed 00 00 01 e4 00 08 43 4f 74 3a 5b 1a " 593 "00 00 04 cc 00 00 00 00 02 00 00 01 00 00 00 24 55 44 00 30 01 15 31 00 " 594 "01 28 00 42 53 43 41 4e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 28 " 595 "00 00 00 00 00 00 00 28 00 00 00 00 00 00 00 00"}; 596 597 TEST_F(ManagerTest, TestESELToRawData) 598 { 599 auto data = Manager::eselToRawData(esel); 600 601 EXPECT_EQ(data.size(), 2464); 602 603 PEL pel{data}; 604 EXPECT_TRUE(pel.valid()); 605 } 606 607 TEST_F(ManagerTest, TestCreateWithESEL) 608 { 609 std::unique_ptr<DataInterfaceBase> dataIface = 610 std::make_unique<MockDataInterface>(); 611 612 openpower::pels::Manager manager{ 613 logManager, std::move(dataIface), 614 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1, 615 std::placeholders::_2, std::placeholders::_3)}; 616 617 { 618 std::string adItem = "ESEL=" + esel; 619 std::vector<std::string> additionalData{adItem}; 620 std::vector<std::string> associations; 621 622 manager.create("error message", 37, 0, 623 phosphor::logging::Entry::Level::Error, additionalData, 624 associations); 625 626 auto data = manager.getPELFromOBMCID(37); 627 PEL pel{data}; 628 EXPECT_TRUE(pel.valid()); 629 } 630 631 // Now an invalid one 632 { 633 std::string adItem = "ESEL=" + esel; 634 635 // Crop it 636 adItem.resize(adItem.size() - 300); 637 638 std::vector<std::string> additionalData{adItem}; 639 std::vector<std::string> associations; 640 641 manager.create("error message", 38, 0, 642 phosphor::logging::Entry::Level::Error, additionalData, 643 associations); 644 645 EXPECT_THROW( 646 manager.getPELFromOBMCID(38), 647 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 648 649 // Run the event loop to log the bad PEL event 650 sdeventplus::Event e{sdEvent}; 651 e.run(std::chrono::milliseconds(1)); 652 653 EXPECT_EQ(logger.errName, "org.open_power.Logging.Error.BadHostPEL"); 654 EXPECT_EQ(logger.errLevel, phosphor::logging::Entry::Level::Error); 655 } 656 } 657 658 // Test that PELs will be pruned when necessary 659 TEST_F(ManagerTest, TestPruning) 660 { 661 sdeventplus::Event e{sdEvent}; 662 663 std::unique_ptr<DataInterfaceBase> dataIface = 664 std::make_unique<MockDataInterface>(); 665 666 openpower::pels::Manager manager{ 667 logManager, std::move(dataIface), 668 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1, 669 std::placeholders::_2, std::placeholders::_3)}; 670 671 // Create 25 1000B (4096B on disk each, which is what is used for pruning) 672 // BMC non-informational PELs in the 100KB repository. After the 24th one, 673 // the repo will be 96% full and a prune should be triggered to remove all 674 // but 7 to get under 30% full. Then when the 25th is added there will be 675 // 8 left. 676 677 auto dir = makeTempDir(); 678 for (int i = 1; i <= 25; i++) 679 { 680 auto data = pelFactory(42, 'O', 0x40, 0x8800, 1000); 681 682 fs::path pelFilename = dir / "rawpel"; 683 std::ofstream pelFile{pelFilename}; 684 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size()); 685 pelFile.close(); 686 687 std::string adItem = "RAWPEL=" + pelFilename.string(); 688 std::vector<std::string> additionalData{adItem}; 689 std::vector<std::string> associations; 690 691 manager.create("error message", 42, 0, 692 phosphor::logging::Entry::Level::Error, additionalData, 693 associations); 694 695 // Simulate the code getting back to the event loop 696 // after each create. 697 e.run(std::chrono::milliseconds(1)); 698 699 if (i < 24) 700 { 701 EXPECT_EQ(countPELsInRepo(), i); 702 } 703 else if (i == 24) 704 { 705 // Prune occured 706 EXPECT_EQ(countPELsInRepo(), 7); 707 } 708 else // i == 25 709 { 710 EXPECT_EQ(countPELsInRepo(), 8); 711 } 712 } 713 714 try 715 { 716 // Make sure the 8 newest ones are still found. 717 for (uint32_t i = 0; i < 8; i++) 718 { 719 manager.getPEL(0x50000012 + i); 720 } 721 } 722 catch ( 723 const sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument& 724 e) 725 { 726 ADD_FAILURE() << "PELs should have all been found"; 727 } 728 729 fs::remove_all(dir); 730 } 731 732 // Test that manually deleting a PEL file will be recognized by the code. 733 TEST_F(ManagerTest, TestPELManualDelete) 734 { 735 sdeventplus::Event e{sdEvent}; 736 737 std::unique_ptr<DataInterfaceBase> dataIface = 738 std::make_unique<MockDataInterface>(); 739 740 openpower::pels::Manager manager{ 741 logManager, std::move(dataIface), 742 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1, 743 std::placeholders::_2, std::placeholders::_3)}; 744 745 auto data = pelDataFactory(TestPELType::pelSimple); 746 auto dir = makeTempDir(); 747 fs::path pelFilename = dir / "rawpel"; 748 749 std::string adItem = "RAWPEL=" + pelFilename.string(); 750 std::vector<std::string> additionalData{adItem}; 751 std::vector<std::string> associations; 752 753 // Add 20 PELs, they will get incrementing IDs like 754 // 0x50000001, 0x50000002, etc. 755 for (int i = 1; i <= 20; i++) 756 { 757 std::ofstream pelFile{pelFilename}; 758 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size()); 759 pelFile.close(); 760 761 manager.create("error message", 42, 0, 762 phosphor::logging::Entry::Level::Error, additionalData, 763 associations); 764 765 // Sanity check this ID is really there so we can test 766 // it was deleted later. This will throw an exception if 767 // not present. 768 manager.getPEL(0x50000000 + i); 769 770 // Run an event loop pass where the internal FD is deleted 771 // after the getPEL function call. 772 e.run(std::chrono::milliseconds(1)); 773 } 774 775 EXPECT_EQ(countPELsInRepo(), 20); 776 777 deletePELFile(0x50000001); 778 779 // Run a single event loop pass so the inotify event can run 780 e.run(std::chrono::milliseconds(1)); 781 782 EXPECT_EQ(countPELsInRepo(), 19); 783 784 EXPECT_THROW( 785 manager.getPEL(0x50000001), 786 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 787 788 // Delete a few more, they should all get handled in the same 789 // event loop pass 790 std::vector<uint32_t> toDelete{0x50000002, 0x50000003, 0x50000004, 791 0x50000005, 0x50000006}; 792 std::for_each(toDelete.begin(), toDelete.end(), 793 [](auto i) { deletePELFile(i); }); 794 795 e.run(std::chrono::milliseconds(1)); 796 797 EXPECT_EQ(countPELsInRepo(), 14); 798 799 std::for_each(toDelete.begin(), toDelete.end(), [&manager](const auto i) { 800 EXPECT_THROW( 801 manager.getPEL(i), 802 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 803 }); 804 805 fs::remove_all(dir); 806 } 807 808 // Test that deleting all PELs at once is handled OK. 809 TEST_F(ManagerTest, TestPELManualDeleteAll) 810 { 811 sdeventplus::Event e{sdEvent}; 812 813 std::unique_ptr<DataInterfaceBase> dataIface = 814 std::make_unique<MockDataInterface>(); 815 816 openpower::pels::Manager manager{ 817 logManager, std::move(dataIface), 818 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1, 819 std::placeholders::_2, std::placeholders::_3)}; 820 821 auto data = pelDataFactory(TestPELType::pelSimple); 822 auto dir = makeTempDir(); 823 fs::path pelFilename = dir / "rawpel"; 824 825 std::string adItem = "RAWPEL=" + pelFilename.string(); 826 std::vector<std::string> additionalData{adItem}; 827 std::vector<std::string> associations; 828 829 // Add 200 PELs, they will get incrementing IDs like 830 // 0x50000001, 0x50000002, etc. 831 for (int i = 1; i <= 200; i++) 832 { 833 std::ofstream pelFile{pelFilename}; 834 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size()); 835 pelFile.close(); 836 837 manager.create("error message", 42, 0, 838 phosphor::logging::Entry::Level::Error, additionalData, 839 associations); 840 841 // Sanity check this ID is really there so we can test 842 // it was deleted later. This will throw an exception if 843 // not present. 844 manager.getPEL(0x50000000 + i); 845 846 // Run an event loop pass where the internal FD is deleted 847 // after the getPEL function call. 848 e.run(std::chrono::milliseconds(1)); 849 } 850 851 // Delete them all at once 852 auto logPath = getPELRepoPath() / "logs"; 853 std::string cmd = "rm " + logPath.string() + "/*_*"; 854 855 { 856 auto rc = system(cmd.c_str()); 857 EXPECT_EQ(rc, 0); 858 } 859 860 EXPECT_EQ(countPELsInRepo(), 0); 861 862 // It will take 5 event loop passes to process them all 863 for (int i = 0; i < 5; i++) 864 { 865 e.run(std::chrono::milliseconds(1)); 866 } 867 868 for (int i = 1; i <= 200; i++) 869 { 870 EXPECT_THROW( 871 manager.getPEL(0x50000000 + i), 872 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument); 873 } 874 875 fs::remove_all(dir); 876 } 877 878 // Test that fault LEDs are turned on when PELs are created 879 TEST_F(ManagerTest, TestServiceIndicators) 880 { 881 std::unique_ptr<DataInterfaceBase> dataIface = 882 std::make_unique<MockDataInterface>(); 883 884 MockDataInterface* mockIface = 885 reinterpret_cast<MockDataInterface*>(dataIface.get()); 886 887 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 888 "system/entry"}; 889 EXPECT_CALL(*mockIface, checkDumpStatus(dumpType)) 890 .WillRepeatedly(Return(std::vector<bool>{false, false, false})); 891 892 openpower::pels::Manager manager{ 893 logManager, std::move(dataIface), 894 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1, 895 std::placeholders::_2, std::placeholders::_3)}; 896 897 // Add a PEL with a callout as if hostboot added it 898 { 899 EXPECT_CALL(*mockIface, getInventoryFromLocCode("U42", 0, true)) 900 .WillOnce(Return("/system/chassis/processor")); 901 902 EXPECT_CALL(*mockIface, 903 setFunctional("/system/chassis/processor", false)) 904 .Times(1); 905 906 // This hostboot PEL has a single hardware callout in it. 907 auto data = pelFactory(1, 'B', 0x20, 0xA400, 500); 908 909 fs::path pelFilename = makeTempDir() / "rawpel"; 910 std::ofstream pelFile{pelFilename}; 911 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size()); 912 pelFile.close(); 913 914 std::string adItem = "RAWPEL=" + pelFilename.string(); 915 std::vector<std::string> additionalData{adItem}; 916 std::vector<std::string> associations; 917 918 manager.create("error message", 42, 0, 919 phosphor::logging::Entry::Level::Error, additionalData, 920 associations); 921 922 fs::remove_all(pelFilename.parent_path()); 923 } 924 925 // Add a BMC PEL with a callout that uses the message registry 926 { 927 std::vector<std::string> names{"systemA"}; 928 EXPECT_CALL(*mockIface, getSystemNames) 929 .Times(1) 930 .WillOnce(Return(names)); 931 932 EXPECT_CALL(*mockIface, expandLocationCode("P42-C23", 0)) 933 .WillOnce(Return("U42-P42-C23")); 934 935 // First call to this is when building the Callout section 936 EXPECT_CALL(*mockIface, getInventoryFromLocCode("P42-C23", 0, false)) 937 .WillOnce(Return("/system/chassis/processor")); 938 939 // Second call to this is finding the associated LED group 940 EXPECT_CALL(*mockIface, getInventoryFromLocCode("U42-P42-C23", 0, true)) 941 .WillOnce(Return("/system/chassis/processor")); 942 943 EXPECT_CALL(*mockIface, 944 setFunctional("/system/chassis/processor", false)) 945 .Times(1); 946 947 const auto registry = R"( 948 { 949 "PELs": 950 [ 951 { 952 "Name": "xyz.openbmc_project.Error.Test", 953 "Subsystem": "power_supply", 954 "ActionFlags": ["service_action", "report"], 955 "SRC": 956 { 957 "ReasonCode": "0x2030" 958 }, 959 "Callouts": [ 960 { 961 "CalloutList": [ 962 {"Priority": "high", "LocCode": "P42-C23"} 963 ] 964 } 965 ], 966 "Documentation": 967 { 968 "Description": "Test Error", 969 "Message": "Test Error" 970 } 971 } 972 ] 973 })"; 974 975 auto path = getPELReadOnlyDataPath(); 976 fs::create_directories(path); 977 path /= "message_registry.json"; 978 979 std::ofstream registryFile{path}; 980 registryFile << registry; 981 registryFile.close(); 982 983 std::vector<std::string> additionalData; 984 std::vector<std::string> associations; 985 986 manager.create("xyz.openbmc_project.Error.Test", 42, 0, 987 phosphor::logging::Entry::Level::Error, additionalData, 988 associations); 989 } 990 } 991 992 // Test for duplicate PELs moved to archive folder 993 TEST_F(ManagerTest, TestDuplicatePEL) 994 { 995 sdeventplus::Event e{sdEvent}; 996 size_t count = 0; 997 998 std::unique_ptr<DataInterfaceBase> dataIface = 999 std::make_unique<MockDataInterface>(); 1000 1001 openpower::pels::Manager manager{ 1002 logManager, std::move(dataIface), 1003 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1, 1004 std::placeholders::_2, std::placeholders::_3)}; 1005 1006 for (int i = 0; i < 2; i++) 1007 { 1008 // This hostboot PEL has a single hardware callout in it. 1009 auto data = pelFactory(1, 'B', 0x20, 0xA400, 500); 1010 1011 fs::path pelFilename = makeTempDir() / "rawpel"; 1012 std::ofstream pelFile{pelFilename}; 1013 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size()); 1014 pelFile.close(); 1015 1016 std::string adItem = "RAWPEL=" + pelFilename.string(); 1017 std::vector<std::string> additionalData{adItem}; 1018 std::vector<std::string> associations; 1019 1020 manager.create("error message", 42, 0, 1021 phosphor::logging::Entry::Level::Error, additionalData, 1022 associations); 1023 1024 e.run(std::chrono::milliseconds(1)); 1025 } 1026 1027 for (auto& f : 1028 fs::directory_iterator(getPELRepoPath() / "logs" / "archive")) 1029 { 1030 if (fs::is_regular_file(f.path())) 1031 { 1032 count++; 1033 } 1034 } 1035 1036 // Get count of PELs in the repository & in archive directtory 1037 EXPECT_EQ(countPELsInRepo(), 1); 1038 EXPECT_EQ(count, 1); 1039 } 1040 1041 // Test termination bit set for pel with critical system termination severity 1042 TEST_F(ManagerTest, TestTerminateBitWithPELSevCriticalSysTerminate) 1043 { 1044 const auto registry = R"( 1045 { 1046 "PELs": 1047 [ 1048 { 1049 "Name": "xyz.openbmc_project.Error.Test", 1050 "Subsystem": "power_supply", 1051 "Severity": "critical_system_term", 1052 "ActionFlags": ["service_action", "report"], 1053 "SRC": 1054 { 1055 "ReasonCode": "0x2030" 1056 }, 1057 "Documentation": 1058 { 1059 "Description": "A PGOOD Fault", 1060 "Message": "PS had a PGOOD Fault" 1061 } 1062 } 1063 ] 1064 } 1065 )"; 1066 1067 auto path = getPELReadOnlyDataPath(); 1068 fs::create_directories(path); 1069 path /= "message_registry.json"; 1070 1071 std::ofstream registryFile{path}; 1072 registryFile << registry; 1073 registryFile.close(); 1074 1075 std::unique_ptr<DataInterfaceBase> dataIface = 1076 std::make_unique<MockDataInterface>(); 1077 1078 MockDataInterface* mockIface = 1079 reinterpret_cast<MockDataInterface*>(dataIface.get()); 1080 1081 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1082 "system/entry"}; 1083 EXPECT_CALL(*mockIface, checkDumpStatus(dumpType)) 1084 .WillRepeatedly(Return(std::vector<bool>{false, false, false})); 1085 1086 openpower::pels::Manager manager{ 1087 logManager, std::move(dataIface), 1088 std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1, 1089 std::placeholders::_2, std::placeholders::_3)}; 1090 1091 std::vector<std::string> additionalData{"FOO=BAR"}; 1092 std::vector<std::string> associations; 1093 1094 // Create the event log to create the PEL from. 1095 manager.create("xyz.openbmc_project.Error.Test", 33, 0, 1096 phosphor::logging::Entry::Level::Error, additionalData, 1097 associations); 1098 1099 // Ensure a PEL was created in the repository 1100 auto pelData = findAnyPELInRepo(); 1101 ASSERT_TRUE(pelData); 1102 1103 auto getPELData = readPELFile(*pelData); 1104 PEL pel(*getPELData); 1105 1106 // Spot check it. Other testcases cover the details. 1107 EXPECT_TRUE(pel.valid()); 1108 1109 // Check for terminate bit set 1110 auto& hexwords = pel.primarySRC().value()->hexwordData(); 1111 EXPECT_EQ(hexwords[3] & 0x20000000, 0x20000000); 1112 } 1113 1114 TEST_F(ManagerTest, TestSanitizeFieldforDBus) 1115 { 1116 std::string base{"(test0!}\n\t ~"}; 1117 auto string = base; 1118 string += char{' ' - 1}; 1119 string += char{'~' + 1}; 1120 string += char{0}; 1121 string += char{static_cast<char>(0xFF)}; 1122 1123 // convert the last four chars to spaces 1124 EXPECT_EQ(Manager::sanitizeFieldForDBus(string), base + " "); 1125 } 1126