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/src.hpp" 17 #include "mocks.hpp" 18 #include "pel_utils.hpp" 19 20 #include <fstream> 21 22 #include <gtest/gtest.h> 23 24 using namespace openpower::pels; 25 using ::testing::_; 26 using ::testing::DoAll; 27 using ::testing::InvokeWithoutArgs; 28 using ::testing::NiceMock; 29 using ::testing::Return; 30 using ::testing::SetArgReferee; 31 using ::testing::Throw; 32 namespace fs = std::filesystem; 33 34 const auto testRegistry = R"( 35 { 36 "PELs": 37 [ 38 { 39 "Name": "xyz.openbmc_project.Error.Test", 40 "Subsystem": "bmc_firmware", 41 "SRC": 42 { 43 "ReasonCode": "0xABCD", 44 "Words6To9": 45 { 46 "6": 47 { 48 "Description": "Component ID", 49 "AdditionalDataPropSource": "COMPID" 50 }, 51 "7": 52 { 53 "Description": "Failure count", 54 "AdditionalDataPropSource": "FREQUENCY" 55 }, 56 "8": 57 { 58 "Description": "Time period", 59 "AdditionalDataPropSource": "DURATION" 60 }, 61 "9": 62 { 63 "Description": "Error code", 64 "AdditionalDataPropSource": "ERRORCODE" 65 } 66 } 67 }, 68 "Documentation": 69 { 70 "Description": "A Component Fault", 71 "Message": "Comp %1 failed %2 times over %3 secs with ErrorCode %4", 72 "MessageArgSources": 73 [ 74 "SRCWord6", "SRCWord7", "SRCWord8", "SRCWord9" 75 ] 76 } 77 } 78 ] 79 } 80 )"; 81 82 class SRCTest : public ::testing::Test 83 { 84 protected: 85 static void SetUpTestCase() 86 { 87 char path[] = "/tmp/srctestXXXXXX"; 88 regDir = mkdtemp(path); 89 } 90 91 static void TearDownTestCase() 92 { 93 fs::remove_all(regDir); 94 } 95 96 static std::string writeData(const char* data) 97 { 98 fs::path path = regDir / "registry.json"; 99 std::ofstream stream{path}; 100 stream << data; 101 return path; 102 } 103 104 static fs::path regDir; 105 }; 106 107 fs::path SRCTest::regDir{}; 108 109 TEST_F(SRCTest, UnflattenFlattenTestNoCallouts) 110 { 111 auto data = pelDataFactory(TestPELType::primarySRCSection); 112 113 Stream stream{data}; 114 SRC src{stream}; 115 116 EXPECT_TRUE(src.valid()); 117 118 EXPECT_EQ(src.header().id, 0x5053); 119 EXPECT_EQ(src.header().size, 0x50); 120 EXPECT_EQ(src.header().version, 0x01); 121 EXPECT_EQ(src.header().subType, 0x01); 122 EXPECT_EQ(src.header().componentID, 0x0202); 123 124 EXPECT_EQ(src.version(), 0x02); 125 EXPECT_EQ(src.flags(), 0x00); 126 EXPECT_EQ(src.hexWordCount(), 9); 127 EXPECT_EQ(src.size(), 0x48); 128 129 const auto& hexwords = src.hexwordData(); 130 EXPECT_EQ(0x02020255, hexwords[0]); 131 EXPECT_EQ(0x03030310, hexwords[1]); 132 EXPECT_EQ(0x04040404, hexwords[2]); 133 EXPECT_EQ(0x05050505, hexwords[3]); 134 EXPECT_EQ(0x06060606, hexwords[4]); 135 EXPECT_EQ(0x07070707, hexwords[5]); 136 EXPECT_EQ(0x08080808, hexwords[6]); 137 EXPECT_EQ(0x09090909, hexwords[7]); 138 139 EXPECT_EQ(src.asciiString(), "BD8D5678 "); 140 EXPECT_FALSE(src.callouts()); 141 142 // Flatten 143 std::vector<uint8_t> newData; 144 Stream newStream{newData}; 145 146 src.flatten(newStream); 147 EXPECT_EQ(data, newData); 148 } 149 150 TEST_F(SRCTest, UnflattenFlattenTest2Callouts) 151 { 152 auto data = pelDataFactory(TestPELType::primarySRCSection2Callouts); 153 154 Stream stream{data}; 155 SRC src{stream}; 156 157 EXPECT_TRUE(src.valid()); 158 EXPECT_EQ(src.flags(), 0x01); // Additional sections within the SRC. 159 160 // Spot check the SRC fields, but they're the same as above 161 EXPECT_EQ(src.asciiString(), "BD8D5678 "); 162 163 // There should be 2 callouts 164 const auto& calloutsSection = src.callouts(); 165 ASSERT_TRUE(calloutsSection); 166 const auto& callouts = calloutsSection->callouts(); 167 EXPECT_EQ(callouts.size(), 2); 168 169 // spot check that each callout has the right substructures 170 EXPECT_TRUE(callouts.front()->fruIdentity()); 171 EXPECT_FALSE(callouts.front()->pceIdentity()); 172 EXPECT_FALSE(callouts.front()->mru()); 173 174 EXPECT_TRUE(callouts.back()->fruIdentity()); 175 EXPECT_TRUE(callouts.back()->pceIdentity()); 176 EXPECT_TRUE(callouts.back()->mru()); 177 178 // Flatten 179 std::vector<uint8_t> newData; 180 Stream newStream{newData}; 181 182 src.flatten(newStream); 183 EXPECT_EQ(data, newData); 184 } 185 186 // Create an SRC from the message registry 187 TEST_F(SRCTest, CreateTestNoCallouts) 188 { 189 message::Entry entry; 190 entry.src.type = 0xBD; 191 entry.src.reasonCode = 0xABCD; 192 entry.subsystem = 0x42; 193 entry.src.powerFault = true; 194 entry.src.hexwordADFields = { 195 {5, {"TEST1", "DESCR1"}}, // Not a user defined word 196 {6, {"TEST1", "DESCR1"}}, 197 {7, {"TEST2", "DESCR2"}}, 198 {8, {"TEST3", "DESCR3"}}, 199 {9, {"TEST4", "DESCR4"}}}; 200 201 // Values for the SRC words pointed to above 202 std::vector<std::string> adData{"TEST1=0x12345678", "TEST2=12345678", 203 "TEST3=0XDEF", "TEST4=Z"}; 204 AdditionalData ad{adData}; 205 NiceMock<MockDataInterface> dataIface; 206 207 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD")); 208 209 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 210 "system/entry"}; 211 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 212 .WillOnce(Return(std::vector<bool>{false, false, false})); 213 214 SRC src{entry, ad, dataIface}; 215 216 EXPECT_TRUE(src.valid()); 217 EXPECT_TRUE(src.isPowerFaultEvent()); 218 EXPECT_EQ(src.size(), baseSRCSize); 219 220 const auto& hexwords = src.hexwordData(); 221 222 // The spec always refers to SRC words 2 - 9, and as the hexwordData() 223 // array index starts at 0 use the math in the [] below to make it easier 224 // to tell what is being accessed. 225 EXPECT_EQ(hexwords[2 - 2] & 0xF0000000, 0); // Partition dump status 226 EXPECT_EQ(hexwords[2 - 2] & 0x00F00000, 0); // Partition boot type 227 EXPECT_EQ(hexwords[2 - 2] & 0x000000FF, 0x55); // SRC format 228 EXPECT_EQ(hexwords[3 - 2] & 0x000000FF, 0x10); // BMC position 229 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0xABCD0000); // Motherboard CCIN 230 231 // Validate more fields here as the code starts filling them in. 232 233 // Ensure hex word 5 wasn't allowed to be set to TEST1's contents 234 EXPECT_EQ(hexwords[5 - 2], 0); 235 236 // The user defined hex word fields specifed in the additional data. 237 EXPECT_EQ(hexwords[6 - 2], 0x12345678); // TEST1 238 EXPECT_EQ(hexwords[7 - 2], 12345678); // TEST2 239 EXPECT_EQ(hexwords[8 - 2], 0xdef); // TEST3 240 EXPECT_EQ(hexwords[9 - 2], 0); // TEST4, but can't convert a 'Z' 241 242 EXPECT_EQ(src.asciiString(), "BD42ABCD "); 243 244 // No callouts 245 EXPECT_FALSE(src.callouts()); 246 247 // May as well spot check the flatten/unflatten 248 std::vector<uint8_t> data; 249 Stream stream{data}; 250 src.flatten(stream); 251 252 stream.offset(0); 253 SRC newSRC{stream}; 254 255 EXPECT_TRUE(newSRC.valid()); 256 EXPECT_EQ(newSRC.isPowerFaultEvent(), src.isPowerFaultEvent()); 257 EXPECT_EQ(newSRC.asciiString(), src.asciiString()); 258 EXPECT_FALSE(newSRC.callouts()); 259 } 260 261 // Create an SRC to test POWER_THERMAL_CRITICAL_FAULT set to TRUE 262 // sets the power fault bit in SRC 263 TEST_F(SRCTest, PowerFaultTest) 264 { 265 message::Entry entry; 266 entry.src.type = 0xBD; 267 entry.src.reasonCode = 0xABCD; 268 entry.subsystem = 0x42; 269 entry.src.powerFault = false; 270 271 // Values for the SRC words pointed to above 272 std::vector<std::string> adData{"POWER_THERMAL_CRITICAL_FAULT=TRUE", 273 "TEST2=12345678", "TEST3=0XDEF", "TEST4=Z"}; 274 AdditionalData ad{adData}; 275 NiceMock<MockDataInterface> dataIface; 276 277 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 278 "system/entry"}; 279 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 280 .WillOnce(Return(std::vector<bool>{false, false, false})); 281 282 SRC src{entry, ad, dataIface}; 283 284 EXPECT_TRUE(src.valid()); 285 EXPECT_TRUE(src.isPowerFaultEvent()); 286 EXPECT_EQ(src.size(), baseSRCSize); 287 } 288 289 // Test when the CCIN string isn't a 4 character number 290 TEST_F(SRCTest, BadCCINTest) 291 { 292 message::Entry entry; 293 entry.src.type = 0xBD; 294 entry.src.reasonCode = 0xABCD; 295 entry.subsystem = 0x42; 296 entry.src.powerFault = false; 297 298 std::vector<std::string> adData{}; 299 AdditionalData ad{adData}; 300 NiceMock<MockDataInterface> dataIface; 301 302 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 303 "system/entry"}; 304 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 305 .WillRepeatedly(Return(std::vector<bool>{false, false, false})); 306 307 // First it isn't a number, then it is too long, 308 // then it is empty. 309 EXPECT_CALL(dataIface, getMotherboardCCIN) 310 .WillOnce(Return("X")) 311 .WillOnce(Return("12345")) 312 .WillOnce(Return("")); 313 314 // The CCIN in the first half should still be 0 each time. 315 { 316 SRC src{entry, ad, dataIface}; 317 EXPECT_TRUE(src.valid()); 318 const auto& hexwords = src.hexwordData(); 319 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000); 320 } 321 322 { 323 SRC src{entry, ad, dataIface}; 324 EXPECT_TRUE(src.valid()); 325 const auto& hexwords = src.hexwordData(); 326 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000); 327 } 328 329 { 330 SRC src{entry, ad, dataIface}; 331 EXPECT_TRUE(src.valid()); 332 const auto& hexwords = src.hexwordData(); 333 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000); 334 } 335 } 336 337 // Test the getErrorDetails function 338 TEST_F(SRCTest, MessageSubstitutionTest) 339 { 340 auto path = SRCTest::writeData(testRegistry); 341 message::Registry registry{path}; 342 auto entry = registry.lookup("0xABCD", message::LookupType::reasonCode); 343 344 std::vector<std::string> adData{"COMPID=0x1", "FREQUENCY=0x4", 345 "DURATION=30", "ERRORCODE=0x01ABCDEF"}; 346 AdditionalData ad{adData}; 347 NiceMock<MockDataInterface> dataIface; 348 349 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 350 "system/entry"}; 351 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 352 .WillOnce(Return(std::vector<bool>{false, false, false})); 353 354 SRC src{*entry, ad, dataIface}; 355 EXPECT_TRUE(src.valid()); 356 357 auto errorDetails = src.getErrorDetails(registry, DetailLevel::message); 358 ASSERT_TRUE(errorDetails); 359 EXPECT_EQ( 360 errorDetails.value(), 361 "Comp 0x1 failed 0x4 times over 0x1E secs with ErrorCode 0x1ABCDEF"); 362 } 363 // Test that an inventory path callout string is 364 // converted into the appropriate FRU callout. 365 TEST_F(SRCTest, InventoryCalloutTest) 366 { 367 message::Entry entry; 368 entry.src.type = 0xBD; 369 entry.src.reasonCode = 0xABCD; 370 entry.subsystem = 0x42; 371 entry.src.powerFault = false; 372 373 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"}; 374 AdditionalData ad{adData}; 375 NiceMock<MockDataInterface> dataIface; 376 377 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 378 .WillOnce(Return("UTMS-P1")); 379 380 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _)) 381 .Times(1) 382 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"), 383 SetArgReferee<3>("123456789ABC"))); 384 385 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 386 "system/entry"}; 387 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 388 .WillOnce(Return(std::vector<bool>{false, false, false})); 389 390 SRC src{entry, ad, dataIface}; 391 EXPECT_TRUE(src.valid()); 392 393 ASSERT_TRUE(src.callouts()); 394 395 EXPECT_EQ(src.callouts()->callouts().size(), 1); 396 397 auto& callout = src.callouts()->callouts().front(); 398 399 EXPECT_EQ(callout->locationCode(), "UTMS-P1"); 400 EXPECT_EQ(callout->priority(), 'H'); 401 402 auto& fru = callout->fruIdentity(); 403 404 EXPECT_EQ(fru->getPN().value(), "1234567"); 405 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 406 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 407 408 // flatten and unflatten 409 std::vector<uint8_t> data; 410 Stream stream{data}; 411 src.flatten(stream); 412 413 stream.offset(0); 414 SRC newSRC{stream}; 415 EXPECT_TRUE(newSRC.valid()); 416 ASSERT_TRUE(src.callouts()); 417 EXPECT_EQ(src.callouts()->callouts().size(), 1); 418 } 419 420 // Test that when the location code can't be obtained that 421 // no callout is added. 422 TEST_F(SRCTest, InventoryCalloutNoLocCodeTest) 423 { 424 message::Entry entry; 425 entry.src.type = 0xBD; 426 entry.src.reasonCode = 0xABCD; 427 entry.subsystem = 0x42; 428 entry.src.powerFault = false; 429 430 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"}; 431 AdditionalData ad{adData}; 432 NiceMock<MockDataInterface> dataIface; 433 434 auto func = []() { 435 throw sdbusplus::exception::SdBusError(5, "Error"); 436 return std::string{}; 437 }; 438 439 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 440 .Times(1) 441 .WillOnce(InvokeWithoutArgs(func)); 442 443 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 444 "system/entry"}; 445 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 446 .WillOnce(Return(std::vector<bool>{false, false, false})); 447 448 EXPECT_CALL(dataIface, getHWCalloutFields(_, _, _, _)).Times(0); 449 450 SRC src{entry, ad, dataIface}; 451 EXPECT_TRUE(src.valid()); 452 453 ASSERT_FALSE(src.callouts()); 454 455 // flatten and unflatten 456 std::vector<uint8_t> data; 457 Stream stream{data}; 458 src.flatten(stream); 459 460 stream.offset(0); 461 SRC newSRC{stream}; 462 EXPECT_TRUE(newSRC.valid()); 463 ASSERT_FALSE(src.callouts()); 464 } 465 466 // Test that when the VPD can't be obtained that 467 // a callout is still created. 468 TEST_F(SRCTest, InventoryCalloutNoVPDTest) 469 { 470 message::Entry entry; 471 entry.src.type = 0xBD; 472 entry.src.reasonCode = 0xABCD; 473 entry.subsystem = 0x42; 474 entry.src.powerFault = false; 475 476 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"}; 477 AdditionalData ad{adData}; 478 NiceMock<MockDataInterface> dataIface; 479 480 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 481 .Times(1) 482 .WillOnce(Return("UTMS-P10")); 483 484 auto func = []() { throw sdbusplus::exception::SdBusError(5, "Error"); }; 485 486 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _)) 487 .Times(1) 488 .WillOnce(InvokeWithoutArgs(func)); 489 490 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 491 "system/entry"}; 492 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 493 .WillOnce(Return(std::vector<bool>{false, false, false})); 494 495 SRC src{entry, ad, dataIface}; 496 EXPECT_TRUE(src.valid()); 497 ASSERT_TRUE(src.callouts()); 498 EXPECT_EQ(src.callouts()->callouts().size(), 1); 499 500 auto& callout = src.callouts()->callouts().front(); 501 EXPECT_EQ(callout->locationCode(), "UTMS-P10"); 502 EXPECT_EQ(callout->priority(), 'H'); 503 504 auto& fru = callout->fruIdentity(); 505 506 EXPECT_EQ(fru->getPN(), ""); 507 EXPECT_EQ(fru->getCCIN(), ""); 508 EXPECT_EQ(fru->getSN(), ""); 509 EXPECT_FALSE(fru->getMaintProc()); 510 511 // flatten and unflatten 512 std::vector<uint8_t> data; 513 Stream stream{data}; 514 src.flatten(stream); 515 516 stream.offset(0); 517 SRC newSRC{stream}; 518 EXPECT_TRUE(newSRC.valid()); 519 ASSERT_TRUE(src.callouts()); 520 EXPECT_EQ(src.callouts()->callouts().size(), 1); 521 } 522 523 TEST_F(SRCTest, RegistryCalloutTest) 524 { 525 message::Entry entry; 526 entry.src.type = 0xBD; 527 entry.src.reasonCode = 0xABCD; 528 entry.subsystem = 0x42; 529 entry.src.powerFault = false; 530 531 entry.callouts = R"( 532 [ 533 { 534 "System": "systemA", 535 "CalloutList": 536 [ 537 { 538 "Priority": "high", 539 "SymbolicFRU": "service_docs" 540 }, 541 { 542 "Priority": "medium", 543 "Procedure": "bmc_code" 544 } 545 ] 546 }, 547 { 548 "System": "systemB", 549 "CalloutList": 550 [ 551 { 552 "Priority": "high", 553 "LocCode": "P0-C8", 554 "SymbolicFRUTrusted": "service_docs" 555 }, 556 { 557 "Priority": "medium", 558 "SymbolicFRUTrusted": "service_docs" 559 } 560 ] 561 }, 562 { 563 "System": "systemC", 564 "CalloutList": 565 [ 566 { 567 "Priority": "high", 568 "LocCode": "P0-C8" 569 }, 570 { 571 "Priority": "medium", 572 "LocCode": "P0-C9" 573 } 574 ] 575 } 576 ])"_json; 577 578 { 579 // Call out a symbolic FRU and a procedure 580 AdditionalData ad; 581 NiceMock<MockDataInterface> dataIface; 582 std::vector<std::string> names{"systemA"}; 583 584 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 585 586 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 587 "system/entry"}; 588 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 589 .WillOnce(Return(std::vector<bool>{false, false, false})); 590 591 SRC src{entry, ad, dataIface}; 592 593 auto& callouts = src.callouts()->callouts(); 594 ASSERT_EQ(callouts.size(), 2); 595 596 EXPECT_EQ(callouts[0]->locationCodeSize(), 0); 597 EXPECT_EQ(callouts[0]->priority(), 'H'); 598 599 EXPECT_EQ(callouts[1]->locationCodeSize(), 0); 600 EXPECT_EQ(callouts[1]->priority(), 'M'); 601 602 auto& fru1 = callouts[0]->fruIdentity(); 603 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 604 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU); 605 EXPECT_FALSE(fru1->getMaintProc()); 606 EXPECT_FALSE(fru1->getSN()); 607 EXPECT_FALSE(fru1->getCCIN()); 608 609 auto& fru2 = callouts[1]->fruIdentity(); 610 EXPECT_EQ(fru2->getMaintProc().value(), "BMC0001"); 611 EXPECT_EQ(fru2->failingComponentType(), 612 src::FRUIdentity::maintenanceProc); 613 EXPECT_FALSE(fru2->getPN()); 614 EXPECT_FALSE(fru2->getSN()); 615 EXPECT_FALSE(fru2->getCCIN()); 616 } 617 618 { 619 // Call out a trusted symbolic FRU with a location code, and 620 // another one without. 621 AdditionalData ad; 622 NiceMock<MockDataInterface> dataIface; 623 std::vector<std::string> names{"systemB"}; 624 625 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8")); 626 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 627 628 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 629 "system/entry"}; 630 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 631 .WillOnce(Return(std::vector<bool>{false, false, false})); 632 633 SRC src{entry, ad, dataIface}; 634 635 auto& callouts = src.callouts()->callouts(); 636 EXPECT_EQ(callouts.size(), 2); 637 638 EXPECT_EQ(callouts[0]->locationCode(), "P0-C8"); 639 EXPECT_EQ(callouts[0]->priority(), 'H'); 640 641 EXPECT_EQ(callouts[1]->locationCodeSize(), 0); 642 EXPECT_EQ(callouts[1]->priority(), 'M'); 643 644 auto& fru1 = callouts[0]->fruIdentity(); 645 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 646 EXPECT_EQ(fru1->failingComponentType(), 647 src::FRUIdentity::symbolicFRUTrustedLocCode); 648 EXPECT_FALSE(fru1->getMaintProc()); 649 EXPECT_FALSE(fru1->getSN()); 650 EXPECT_FALSE(fru1->getCCIN()); 651 652 // It asked for a trusted symbolic FRU, but no location code 653 // was provided so it is switched back to a normal one 654 auto& fru2 = callouts[1]->fruIdentity(); 655 EXPECT_EQ(fru2->getPN().value(), "SVCDOCS"); 656 EXPECT_EQ(fru2->failingComponentType(), src::FRUIdentity::symbolicFRU); 657 EXPECT_FALSE(fru2->getMaintProc()); 658 EXPECT_FALSE(fru2->getSN()); 659 EXPECT_FALSE(fru2->getCCIN()); 660 } 661 662 { 663 // Two hardware callouts 664 AdditionalData ad; 665 NiceMock<MockDataInterface> dataIface; 666 std::vector<std::string> names{"systemC"}; 667 668 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 669 "system/entry"}; 670 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 671 .WillOnce(Return(std::vector<bool>{false, false, false})); 672 673 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 674 675 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0)) 676 .WillOnce(Return("UXXX-P0-C8")); 677 678 EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0)) 679 .WillOnce(Return("UXXX-P0-C9")); 680 681 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0, false)) 682 .WillOnce(Return( 683 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0")); 684 685 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false)) 686 .WillOnce(Return( 687 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1")); 688 689 EXPECT_CALL( 690 dataIface, 691 getHWCalloutFields( 692 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, 693 _)) 694 .Times(1) 695 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 696 SetArgReferee<2>("CCCC"), 697 SetArgReferee<3>("123456789ABC"))); 698 699 EXPECT_CALL( 700 dataIface, 701 getHWCalloutFields( 702 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _, 703 _)) 704 .Times(1) 705 .WillOnce(DoAll(SetArgReferee<1>("2345678"), 706 SetArgReferee<2>("DDDD"), 707 SetArgReferee<3>("23456789ABCD"))); 708 709 SRC src{entry, ad, dataIface}; 710 711 auto& callouts = src.callouts()->callouts(); 712 EXPECT_EQ(callouts.size(), 2); 713 714 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C8"); 715 EXPECT_EQ(callouts[0]->priority(), 'H'); 716 717 auto& fru1 = callouts[0]->fruIdentity(); 718 EXPECT_EQ(fru1->getPN().value(), "1234567"); 719 EXPECT_EQ(fru1->getCCIN().value(), "CCCC"); 720 EXPECT_EQ(fru1->getSN().value(), "123456789ABC"); 721 722 EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C9"); 723 EXPECT_EQ(callouts[1]->priority(), 'M'); 724 725 auto& fru2 = callouts[1]->fruIdentity(); 726 EXPECT_EQ(fru2->getPN().value(), "2345678"); 727 EXPECT_EQ(fru2->getCCIN().value(), "DDDD"); 728 EXPECT_EQ(fru2->getSN().value(), "23456789ABCD"); 729 } 730 } 731 732 // Test that a symbolic FRU with a trusted location code callout 733 // from the registry can get its location from the 734 // CALLOUT_INVENTORY_PATH AdditionalData entry. 735 TEST_F(SRCTest, SymbolicFRUWithInvPathTest) 736 { 737 message::Entry entry; 738 entry.src.type = 0xBD; 739 entry.src.reasonCode = 0xABCD; 740 entry.subsystem = 0x42; 741 entry.src.powerFault = false; 742 743 entry.callouts = R"( 744 [{ 745 "CalloutList": 746 [ 747 { 748 "Priority": "high", 749 "SymbolicFRUTrusted": "service_docs", 750 "UseInventoryLocCode": true 751 }, 752 { 753 "Priority": "medium", 754 "LocCode": "P0-C8", 755 "SymbolicFRUTrusted": "pwrsply" 756 } 757 ] 758 }])"_json; 759 760 { 761 // The location code for the first symbolic FRU callout will 762 // come from this inventory path since UseInventoryLocCode is set. 763 // In this case there will be no normal FRU callout for the motherboard. 764 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"}; 765 AdditionalData ad{adData}; 766 NiceMock<MockDataInterface> dataIface; 767 std::vector<std::string> names{"systemA"}; 768 769 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 770 "system/entry"}; 771 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 772 .WillOnce(Return(std::vector<bool>{false, false, false})); 773 774 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 775 776 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 777 .Times(1) 778 .WillOnce(Return("Ufcs-P10")); 779 780 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0)) 781 .WillOnce(Return("Ufcs-P0-C8")); 782 783 SRC src{entry, ad, dataIface}; 784 785 auto& callouts = src.callouts()->callouts(); 786 EXPECT_EQ(callouts.size(), 2); 787 788 // The location code for the first symbolic FRU callout with a 789 // trusted location code comes from the motherboard. 790 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P10"); 791 EXPECT_EQ(callouts[0]->priority(), 'H'); 792 auto& fru1 = callouts[0]->fruIdentity(); 793 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 794 EXPECT_EQ(fru1->failingComponentType(), 795 src::FRUIdentity::symbolicFRUTrustedLocCode); 796 797 // The second trusted symbolic FRU callouts uses the location 798 // code in the registry as usual. 799 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P0-C8"); 800 EXPECT_EQ(callouts[1]->priority(), 'M'); 801 auto& fru2 = callouts[1]->fruIdentity(); 802 EXPECT_EQ(fru2->getPN().value(), "PWRSPLY"); 803 EXPECT_EQ(fru2->failingComponentType(), 804 src::FRUIdentity::symbolicFRUTrustedLocCode); 805 } 806 807 { 808 // This time say we want to use the location code from 809 // the inventory, but don't pass it in and the callout should 810 // end up a regular symbolic FRU 811 entry.callouts = R"( 812 [{ 813 "CalloutList": 814 [ 815 { 816 "Priority": "high", 817 "SymbolicFRUTrusted": "service_docs", 818 "UseInventoryLocCode": true 819 } 820 ] 821 }])"_json; 822 823 AdditionalData ad; 824 NiceMock<MockDataInterface> dataIface; 825 std::vector<std::string> names{"systemA"}; 826 827 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 828 "system/entry"}; 829 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 830 .WillOnce(Return(std::vector<bool>{false, false, false})); 831 832 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 833 834 SRC src{entry, ad, dataIface}; 835 836 auto& callouts = src.callouts()->callouts(); 837 EXPECT_EQ(callouts.size(), 1); 838 839 EXPECT_EQ(callouts[0]->locationCode(), ""); 840 EXPECT_EQ(callouts[0]->priority(), 'H'); 841 auto& fru1 = callouts[0]->fruIdentity(); 842 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 843 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU); 844 } 845 } 846 847 // Test looking up device path fails in the callout jSON. 848 TEST_F(SRCTest, DevicePathCalloutTest) 849 { 850 message::Entry entry; 851 entry.src.type = 0xBD; 852 entry.src.reasonCode = 0xABCD; 853 entry.subsystem = 0x42; 854 entry.src.powerFault = false; 855 856 const auto calloutJSON = R"( 857 { 858 "I2C": 859 { 860 "14": 861 { 862 "114": 863 { 864 "Callouts":[ 865 { 866 "Name": "/chassis/motherboard/cpu0", 867 "LocationCode": "P1-C40", 868 "Priority": "H" 869 }, 870 { 871 "Name": "/chassis/motherboard", 872 "LocationCode": "P1", 873 "Priority": "M" 874 }, 875 { 876 "Name": "/chassis/motherboard/bmc", 877 "LocationCode": "P1-C15", 878 "Priority": "L" 879 } 880 ], 881 "Dest": "proc 0 target" 882 } 883 } 884 } 885 })"; 886 887 auto dataPath = getPELReadOnlyDataPath(); 888 std::ofstream file{dataPath / "systemA_dev_callouts.json"}; 889 file << calloutJSON; 890 file.close(); 891 892 NiceMock<MockDataInterface> dataIface; 893 std::vector<std::string> names{"systemA"}; 894 895 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 896 "system/entry"}; 897 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 898 .WillRepeatedly(Return(std::vector<bool>{false, false, false})); 899 900 EXPECT_CALL(dataIface, getSystemNames) 901 .Times(5) 902 .WillRepeatedly(Return(names)); 903 904 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0, false)) 905 .Times(3) 906 .WillRepeatedly( 907 Return("/xyz/openbmc_project/inventory/chassis/motherboard/cpu0")); 908 909 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false)) 910 .Times(3) 911 .WillRepeatedly( 912 Return("/xyz/openbmc_project/inventory/chassis/motherboard")); 913 914 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false)) 915 .Times(3) 916 .WillRepeatedly( 917 Return("/xyz/openbmc_project/inventory/chassis/motherboard/bmc")); 918 919 EXPECT_CALL(dataIface, expandLocationCode("P1-C40", 0)) 920 .Times(3) 921 .WillRepeatedly(Return("Ufcs-P1-C40")); 922 923 EXPECT_CALL(dataIface, expandLocationCode("P1", 0)) 924 .Times(3) 925 .WillRepeatedly(Return("Ufcs-P1")); 926 927 EXPECT_CALL(dataIface, expandLocationCode("P1-C15", 0)) 928 .Times(3) 929 .WillRepeatedly(Return("Ufcs-P1-C15")); 930 931 EXPECT_CALL( 932 dataIface, 933 getHWCalloutFields( 934 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _)) 935 .Times(3) 936 .WillRepeatedly(DoAll(SetArgReferee<1>("1234567"), 937 SetArgReferee<2>("CCCC"), 938 SetArgReferee<3>("123456789ABC"))); 939 EXPECT_CALL( 940 dataIface, 941 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard", 942 _, _, _)) 943 .Times(3) 944 .WillRepeatedly(DoAll(SetArgReferee<1>("7654321"), 945 SetArgReferee<2>("MMMM"), 946 SetArgReferee<3>("CBA987654321"))); 947 EXPECT_CALL( 948 dataIface, 949 getHWCalloutFields( 950 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _, _, _)) 951 .Times(3) 952 .WillRepeatedly(DoAll(SetArgReferee<1>("7123456"), 953 SetArgReferee<2>("BBBB"), 954 SetArgReferee<3>("C123456789AB"))); 955 956 // Call this below with different AdditionalData values that 957 // result in the same callouts. 958 auto checkCallouts = [&entry, &dataIface](const auto& items) { 959 AdditionalData ad{items}; 960 SRC src{entry, ad, dataIface}; 961 962 ASSERT_TRUE(src.callouts()); 963 auto& callouts = src.callouts()->callouts(); 964 965 ASSERT_EQ(callouts.size(), 3); 966 967 { 968 EXPECT_EQ(callouts[0]->priority(), 'H'); 969 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40"); 970 971 auto& fru = callouts[0]->fruIdentity(); 972 EXPECT_EQ(fru->getPN().value(), "1234567"); 973 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 974 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 975 } 976 { 977 EXPECT_EQ(callouts[1]->priority(), 'M'); 978 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1"); 979 980 auto& fru = callouts[1]->fruIdentity(); 981 EXPECT_EQ(fru->getPN().value(), "7654321"); 982 EXPECT_EQ(fru->getCCIN().value(), "MMMM"); 983 EXPECT_EQ(fru->getSN().value(), "CBA987654321"); 984 } 985 { 986 EXPECT_EQ(callouts[2]->priority(), 'L'); 987 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15"); 988 989 auto& fru = callouts[2]->fruIdentity(); 990 EXPECT_EQ(fru->getPN().value(), "7123456"); 991 EXPECT_EQ(fru->getCCIN().value(), "BBBB"); 992 EXPECT_EQ(fru->getSN().value(), "C123456789AB"); 993 } 994 }; 995 996 { 997 // Callouts based on the device path 998 std::vector<std::string> items{ 999 "CALLOUT_ERRNO=5", 1000 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/" 1001 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"}; 1002 1003 checkCallouts(items); 1004 } 1005 1006 { 1007 // Callouts based on the I2C bus and address 1008 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14", 1009 "CALLOUT_IIC_ADDR=0x72"}; 1010 checkCallouts(items); 1011 } 1012 1013 { 1014 // Also based on I2C bus and address, but with bus = /dev/i2c-14 1015 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14", 1016 "CALLOUT_IIC_ADDR=0x72"}; 1017 checkCallouts(items); 1018 } 1019 1020 { 1021 // Callout not found 1022 std::vector<std::string> items{ 1023 "CALLOUT_ERRNO=5", 1024 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/" 1025 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"}; 1026 1027 AdditionalData ad{items}; 1028 SRC src{entry, ad, dataIface}; 1029 1030 EXPECT_FALSE(src.callouts()); 1031 ASSERT_EQ(src.getDebugData().size(), 1); 1032 EXPECT_EQ(src.getDebugData()[0], 1033 "Problem looking up I2C callouts on 24 18: " 1034 "[json.exception.out_of_range.403] key '24' not found"); 1035 } 1036 1037 { 1038 // Callout not found 1039 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=22", 1040 "CALLOUT_IIC_ADDR=0x99"}; 1041 AdditionalData ad{items}; 1042 SRC src{entry, ad, dataIface}; 1043 1044 EXPECT_FALSE(src.callouts()); 1045 ASSERT_EQ(src.getDebugData().size(), 1); 1046 EXPECT_EQ(src.getDebugData()[0], 1047 "Problem looking up I2C callouts on 22 153: " 1048 "[json.exception.out_of_range.403] key '22' not found"); 1049 } 1050 1051 fs::remove_all(dataPath); 1052 } 1053 1054 // Test when callouts are passed in via JSON 1055 TEST_F(SRCTest, JsonCalloutsTest) 1056 { 1057 const auto jsonCallouts = R"( 1058 [ 1059 { 1060 "LocationCode": "P0-C1", 1061 "Priority": "H", 1062 "MRUs": [ 1063 { 1064 "ID": 42, 1065 "Priority": "H" 1066 }, 1067 { 1068 "ID": 43, 1069 "Priority": "M" 1070 } 1071 ] 1072 }, 1073 { 1074 "InventoryPath": "/inv/system/chassis/motherboard/cpu0", 1075 "Priority": "M", 1076 "Guarded": true, 1077 "Deconfigured": true 1078 }, 1079 { 1080 "Procedure": "PROCEDU", 1081 "Priority": "A" 1082 }, 1083 { 1084 "SymbolicFRU": "TRUSTED", 1085 "Priority": "B", 1086 "TrustedLocationCode": true, 1087 "LocationCode": "P1-C23" 1088 }, 1089 { 1090 "SymbolicFRU": "FRUTST1", 1091 "Priority": "C", 1092 "LocationCode": "P1-C24" 1093 }, 1094 { 1095 "SymbolicFRU": "FRUTST2LONG", 1096 "Priority": "L" 1097 } 1098 ] 1099 )"_json; 1100 1101 message::Entry entry; 1102 entry.src.type = 0xBD; 1103 entry.src.reasonCode = 0xABCD; 1104 entry.subsystem = 0x42; 1105 entry.src.powerFault = false; 1106 1107 AdditionalData ad; 1108 NiceMock<MockDataInterface> dataIface; 1109 1110 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1111 "system/entry"}; 1112 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1113 .WillOnce(Return(std::vector<bool>{false, false, false})); 1114 1115 // Callout 0 mock calls 1116 { 1117 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0)) 1118 .Times(1) 1119 .WillOnce(Return("UXXX-P0-C1")); 1120 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false)) 1121 .Times(1) 1122 .WillOnce(Return("/inv/system/chassis/motherboard/bmc")); 1123 EXPECT_CALL( 1124 dataIface, 1125 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _)) 1126 .Times(1) 1127 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 1128 SetArgReferee<2>("CCCC"), 1129 SetArgReferee<3>("123456789ABC"))); 1130 } 1131 // Callout 1 mock calls 1132 { 1133 EXPECT_CALL(dataIface, 1134 getLocationCode("/inv/system/chassis/motherboard/cpu0")) 1135 .WillOnce(Return("UYYY-P5")); 1136 EXPECT_CALL( 1137 dataIface, 1138 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _)) 1139 .Times(1) 1140 .WillOnce(DoAll(SetArgReferee<1>("2345678"), 1141 SetArgReferee<2>("DDDD"), 1142 SetArgReferee<3>("23456789ABCD"))); 1143 } 1144 // Callout 3 mock calls 1145 { 1146 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0)) 1147 .Times(1) 1148 .WillOnce(Return("UXXX-P1-C23")); 1149 } 1150 // Callout 4 mock calls 1151 { 1152 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0)) 1153 .Times(1) 1154 .WillOnce(Return("UXXX-P1-C24")); 1155 } 1156 1157 SRC src{entry, ad, jsonCallouts, dataIface}; 1158 ASSERT_TRUE(src.callouts()); 1159 1160 // Check the guarded and deconfigured flags 1161 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000); 1162 1163 const auto& callouts = src.callouts()->callouts(); 1164 ASSERT_EQ(callouts.size(), 6); 1165 1166 // Check callout 0 1167 { 1168 EXPECT_EQ(callouts[0]->priority(), 'H'); 1169 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1"); 1170 1171 auto& fru = callouts[0]->fruIdentity(); 1172 EXPECT_EQ(fru->getPN().value(), "1234567"); 1173 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 1174 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 1175 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1176 1177 auto& mruCallouts = callouts[0]->mru(); 1178 ASSERT_TRUE(mruCallouts); 1179 auto& mrus = mruCallouts->mrus(); 1180 ASSERT_EQ(mrus.size(), 2); 1181 EXPECT_EQ(mrus[0].id, 42); 1182 EXPECT_EQ(mrus[0].priority, 'H'); 1183 EXPECT_EQ(mrus[1].id, 43); 1184 EXPECT_EQ(mrus[1].priority, 'M'); 1185 } 1186 1187 // Check callout 1 1188 { 1189 EXPECT_EQ(callouts[1]->priority(), 'M'); 1190 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5"); 1191 1192 auto& fru = callouts[1]->fruIdentity(); 1193 EXPECT_EQ(fru->getPN().value(), "2345678"); 1194 EXPECT_EQ(fru->getCCIN().value(), "DDDD"); 1195 EXPECT_EQ(fru->getSN().value(), "23456789ABCD"); 1196 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1197 } 1198 1199 // Check callout 2 1200 { 1201 EXPECT_EQ(callouts[2]->priority(), 'A'); 1202 EXPECT_EQ(callouts[2]->locationCode(), ""); 1203 1204 auto& fru = callouts[2]->fruIdentity(); 1205 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU"); 1206 EXPECT_EQ(fru->failingComponentType(), 1207 src::FRUIdentity::maintenanceProc); 1208 } 1209 1210 // Check callout 3 1211 { 1212 EXPECT_EQ(callouts[3]->priority(), 'B'); 1213 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23"); 1214 1215 auto& fru = callouts[3]->fruIdentity(); 1216 EXPECT_EQ(fru->getPN().value(), "TRUSTED"); 1217 EXPECT_EQ(fru->failingComponentType(), 1218 src::FRUIdentity::symbolicFRUTrustedLocCode); 1219 } 1220 1221 // Check callout 4 1222 { 1223 EXPECT_EQ(callouts[4]->priority(), 'C'); 1224 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24"); 1225 1226 auto& fru = callouts[4]->fruIdentity(); 1227 EXPECT_EQ(fru->getPN().value(), "FRUTST1"); 1228 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU); 1229 } 1230 1231 // Check callout 5 1232 { 1233 EXPECT_EQ(callouts[5]->priority(), 'L'); 1234 EXPECT_EQ(callouts[5]->locationCode(), ""); 1235 1236 auto& fru = callouts[5]->fruIdentity(); 1237 EXPECT_EQ(fru->getPN().value(), "FRUTST2"); 1238 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU); 1239 } 1240 1241 // Check that it didn't find any errors 1242 const auto& data = src.getDebugData(); 1243 EXPECT_TRUE(data.empty()); 1244 } 1245 1246 TEST_F(SRCTest, JsonBadCalloutsTest) 1247 { 1248 // The first call will have a Throw in a mock call. 1249 // The second will have a different Throw in a mock call. 1250 // The others have issues with the Priority field. 1251 const auto jsonCallouts = R"( 1252 [ 1253 { 1254 "LocationCode": "P0-C1", 1255 "Priority": "H" 1256 }, 1257 { 1258 "LocationCode": "P0-C2", 1259 "Priority": "H" 1260 }, 1261 { 1262 "LocationCode": "P0-C3" 1263 }, 1264 { 1265 "LocationCode": "P0-C4", 1266 "Priority": "X" 1267 } 1268 ] 1269 )"_json; 1270 1271 message::Entry entry; 1272 entry.src.type = 0xBD; 1273 entry.src.reasonCode = 0xABCD; 1274 entry.subsystem = 0x42; 1275 entry.src.powerFault = false; 1276 1277 AdditionalData ad; 1278 NiceMock<MockDataInterface> dataIface; 1279 1280 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1281 "system/entry"}; 1282 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1283 .WillRepeatedly(Return(std::vector<bool>{false, false, false})); 1284 1285 // Callout 0 mock calls 1286 // Expand location code will fail, so the unexpanded location 1287 // code should show up in the callout instead. 1288 { 1289 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0)) 1290 .WillOnce(Throw(std::runtime_error("Fail"))); 1291 1292 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false)) 1293 .Times(1) 1294 .WillOnce(Return("/inv/system/chassis/motherboard/bmc")); 1295 EXPECT_CALL( 1296 dataIface, 1297 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _)) 1298 .Times(1) 1299 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 1300 SetArgReferee<2>("CCCC"), 1301 SetArgReferee<3>("123456789ABC"))); 1302 } 1303 1304 // Callout 1 mock calls 1305 // getInventoryFromLocCode will fail 1306 { 1307 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0)) 1308 .Times(1) 1309 .WillOnce(Return("UXXX-P0-C2")); 1310 1311 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false)) 1312 .Times(1) 1313 .WillOnce(Throw(std::runtime_error("Fail"))); 1314 } 1315 1316 SRC src{entry, ad, jsonCallouts, dataIface}; 1317 1318 ASSERT_TRUE(src.callouts()); 1319 1320 const auto& callouts = src.callouts()->callouts(); 1321 1322 // Only the first callout was successful 1323 ASSERT_EQ(callouts.size(), 1); 1324 1325 { 1326 EXPECT_EQ(callouts[0]->priority(), 'H'); 1327 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1"); 1328 1329 auto& fru = callouts[0]->fruIdentity(); 1330 EXPECT_EQ(fru->getPN().value(), "1234567"); 1331 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 1332 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 1333 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1334 } 1335 1336 const auto& data = src.getDebugData(); 1337 ASSERT_EQ(data.size(), 4); 1338 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail"); 1339 EXPECT_STREQ(data[1].c_str(), 1340 "Failed extracting callout data from JSON: Unable to " 1341 "get inventory path from location code: P0-C2: Fail"); 1342 EXPECT_STREQ(data[2].c_str(), 1343 "Failed extracting callout data from JSON: " 1344 "[json.exception.out_of_range.403] key 'Priority' not found"); 1345 EXPECT_STREQ(data[3].c_str(), 1346 "Failed extracting callout data from JSON: Invalid " 1347 "priority 'X' found in JSON callout"); 1348 } 1349 1350 // Test that an inventory path callout can have 1351 // a different priority than H. 1352 TEST_F(SRCTest, InventoryCalloutTestPriority) 1353 { 1354 message::Entry entry; 1355 entry.src.type = 0xBD; 1356 entry.src.reasonCode = 0xABCD; 1357 entry.subsystem = 0x42; 1358 entry.src.powerFault = false; 1359 1360 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard", 1361 "CALLOUT_PRIORITY=M"}; 1362 AdditionalData ad{adData}; 1363 NiceMock<MockDataInterface> dataIface; 1364 1365 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1366 "system/entry"}; 1367 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1368 .WillOnce(Return(std::vector<bool>{false, false, false})); 1369 1370 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 1371 .WillOnce(Return("UTMS-P1")); 1372 1373 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _)) 1374 .Times(1) 1375 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"), 1376 SetArgReferee<3>("123456789ABC"))); 1377 1378 SRC src{entry, ad, dataIface}; 1379 EXPECT_TRUE(src.valid()); 1380 1381 ASSERT_TRUE(src.callouts()); 1382 1383 EXPECT_EQ(src.callouts()->callouts().size(), 1); 1384 1385 auto& callout = src.callouts()->callouts().front(); 1386 1387 EXPECT_EQ(callout->locationCode(), "UTMS-P1"); 1388 EXPECT_EQ(callout->priority(), 'M'); 1389 } 1390 1391 // Test for bmc & platform dump status bits 1392 TEST_F(SRCTest, DumpStatusBitsCheck) 1393 { 1394 message::Entry entry; 1395 entry.src.type = 0xBD; 1396 entry.src.reasonCode = 0xABCD; 1397 entry.subsystem = 0x42; 1398 entry.src.powerFault = false; 1399 1400 AdditionalData ad; 1401 NiceMock<MockDataInterface> dataIface; 1402 std::vector<std::string> dumpType{"bmc/entry", "resource/entry", 1403 "system/entry"}; 1404 1405 { 1406 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1407 .WillOnce(Return(std::vector<bool>{true, false, false})); 1408 1409 SRC src{entry, ad, dataIface}; 1410 EXPECT_TRUE(src.valid()); 1411 1412 const auto& hexwords = src.hexwordData(); 1413 EXPECT_EQ(0x00080055, hexwords[0]); 1414 } 1415 1416 { 1417 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1418 .WillOnce(Return(std::vector<bool>{false, true, false})); 1419 1420 SRC src{entry, ad, dataIface}; 1421 EXPECT_TRUE(src.valid()); 1422 1423 const auto& hexwords = src.hexwordData(); 1424 EXPECT_EQ(0x00000255, hexwords[0]); 1425 } 1426 1427 { 1428 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1429 .WillOnce(Return(std::vector<bool>{false, false, true})); 1430 1431 SRC src{entry, ad, dataIface}; 1432 EXPECT_TRUE(src.valid()); 1433 1434 const auto& hexwords = src.hexwordData(); 1435 EXPECT_EQ(0x00000455, hexwords[0]); 1436 } 1437 1438 { 1439 EXPECT_CALL(dataIface, checkDumpStatus(dumpType)) 1440 .WillOnce(Return(std::vector<bool>{true, true, true})); 1441 1442 SRC src{entry, ad, dataIface}; 1443 EXPECT_TRUE(src.valid()); 1444 1445 const auto& hexwords = src.hexwordData(); 1446 EXPECT_EQ(0x00080655, hexwords[0]); 1447 } 1448 } 1449