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 SRC src{entry, ad, dataIface}; 210 211 EXPECT_TRUE(src.valid()); 212 EXPECT_TRUE(src.isPowerFaultEvent()); 213 EXPECT_EQ(src.size(), baseSRCSize); 214 215 const auto& hexwords = src.hexwordData(); 216 217 // The spec always refers to SRC words 2 - 9, and as the hexwordData() 218 // array index starts at 0 use the math in the [] below to make it easier 219 // to tell what is being accessed. 220 EXPECT_EQ(hexwords[2 - 2] & 0xF0000000, 0); // Partition dump status 221 EXPECT_EQ(hexwords[2 - 2] & 0x00F00000, 0); // Partition boot type 222 EXPECT_EQ(hexwords[2 - 2] & 0x000000FF, 0x55); // SRC format 223 EXPECT_EQ(hexwords[3 - 2] & 0x000000FF, 0x10); // BMC position 224 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0xABCD0000); // Motherboard CCIN 225 226 // Validate more fields here as the code starts filling them in. 227 228 // Ensure hex word 5 wasn't allowed to be set to TEST1's contents 229 EXPECT_EQ(hexwords[5 - 2], 0); 230 231 // The user defined hex word fields specifed in the additional data. 232 EXPECT_EQ(hexwords[6 - 2], 0x12345678); // TEST1 233 EXPECT_EQ(hexwords[7 - 2], 12345678); // TEST2 234 EXPECT_EQ(hexwords[8 - 2], 0xdef); // TEST3 235 EXPECT_EQ(hexwords[9 - 2], 0); // TEST4, but can't convert a 'Z' 236 237 EXPECT_EQ(src.asciiString(), "BD42ABCD "); 238 239 // No callouts 240 EXPECT_FALSE(src.callouts()); 241 242 // May as well spot check the flatten/unflatten 243 std::vector<uint8_t> data; 244 Stream stream{data}; 245 src.flatten(stream); 246 247 stream.offset(0); 248 SRC newSRC{stream}; 249 250 EXPECT_TRUE(newSRC.valid()); 251 EXPECT_EQ(newSRC.isPowerFaultEvent(), src.isPowerFaultEvent()); 252 EXPECT_EQ(newSRC.asciiString(), src.asciiString()); 253 EXPECT_FALSE(newSRC.callouts()); 254 } 255 256 // Create an SRC to test POWER_THERMAL_CRITICAL_FAULT set to TRUE 257 // sets the power fault bit in SRC 258 TEST_F(SRCTest, PowerFaultTest) 259 { 260 message::Entry entry; 261 entry.src.type = 0xBD; 262 entry.src.reasonCode = 0xABCD; 263 entry.subsystem = 0x42; 264 entry.src.powerFault = false; 265 266 // Values for the SRC words pointed to above 267 std::vector<std::string> adData{"POWER_THERMAL_CRITICAL_FAULT=TRUE", 268 "TEST2=12345678", "TEST3=0XDEF", "TEST4=Z"}; 269 AdditionalData ad{adData}; 270 NiceMock<MockDataInterface> dataIface; 271 272 SRC src{entry, ad, dataIface}; 273 274 EXPECT_TRUE(src.valid()); 275 EXPECT_TRUE(src.isPowerFaultEvent()); 276 EXPECT_EQ(src.size(), baseSRCSize); 277 } 278 279 // Test when the CCIN string isn't a 4 character number 280 TEST_F(SRCTest, BadCCINTest) 281 { 282 message::Entry entry; 283 entry.src.type = 0xBD; 284 entry.src.reasonCode = 0xABCD; 285 entry.subsystem = 0x42; 286 entry.src.powerFault = false; 287 288 std::vector<std::string> adData{}; 289 AdditionalData ad{adData}; 290 NiceMock<MockDataInterface> dataIface; 291 292 // First it isn't a number, then it is too long, 293 // then it is empty. 294 EXPECT_CALL(dataIface, getMotherboardCCIN) 295 .WillOnce(Return("X")) 296 .WillOnce(Return("12345")) 297 .WillOnce(Return("")); 298 299 // The CCIN in the first half should still be 0 each time. 300 { 301 SRC src{entry, ad, dataIface}; 302 EXPECT_TRUE(src.valid()); 303 const auto& hexwords = src.hexwordData(); 304 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000); 305 } 306 307 { 308 SRC src{entry, ad, dataIface}; 309 EXPECT_TRUE(src.valid()); 310 const auto& hexwords = src.hexwordData(); 311 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000); 312 } 313 314 { 315 SRC src{entry, ad, dataIface}; 316 EXPECT_TRUE(src.valid()); 317 const auto& hexwords = src.hexwordData(); 318 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000); 319 } 320 } 321 322 // Test the getErrorDetails function 323 TEST_F(SRCTest, MessageSubstitutionTest) 324 { 325 auto path = SRCTest::writeData(testRegistry); 326 message::Registry registry{path}; 327 auto entry = registry.lookup("0xABCD", message::LookupType::reasonCode); 328 329 std::vector<std::string> adData{"COMPID=0x1", "FREQUENCY=0x4", 330 "DURATION=30", "ERRORCODE=0x01ABCDEF"}; 331 AdditionalData ad{adData}; 332 NiceMock<MockDataInterface> dataIface; 333 334 SRC src{*entry, ad, dataIface}; 335 EXPECT_TRUE(src.valid()); 336 337 auto errorDetails = src.getErrorDetails(registry, DetailLevel::message); 338 ASSERT_TRUE(errorDetails); 339 EXPECT_EQ( 340 errorDetails.value(), 341 "Comp 0x1 failed 0x4 times over 0x1E secs with ErrorCode 0x1ABCDEF"); 342 } 343 // Test that an inventory path callout string is 344 // converted into the appropriate FRU callout. 345 TEST_F(SRCTest, InventoryCalloutTest) 346 { 347 message::Entry entry; 348 entry.src.type = 0xBD; 349 entry.src.reasonCode = 0xABCD; 350 entry.subsystem = 0x42; 351 entry.src.powerFault = false; 352 353 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"}; 354 AdditionalData ad{adData}; 355 NiceMock<MockDataInterface> dataIface; 356 357 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 358 .WillOnce(Return("UTMS-P1")); 359 360 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _)) 361 .Times(1) 362 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"), 363 SetArgReferee<3>("123456789ABC"))); 364 365 SRC src{entry, ad, dataIface}; 366 EXPECT_TRUE(src.valid()); 367 368 ASSERT_TRUE(src.callouts()); 369 370 EXPECT_EQ(src.callouts()->callouts().size(), 1); 371 372 auto& callout = src.callouts()->callouts().front(); 373 374 EXPECT_EQ(callout->locationCode(), "UTMS-P1"); 375 EXPECT_EQ(callout->priority(), 'H'); 376 377 auto& fru = callout->fruIdentity(); 378 379 EXPECT_EQ(fru->getPN().value(), "1234567"); 380 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 381 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 382 383 // flatten and unflatten 384 std::vector<uint8_t> data; 385 Stream stream{data}; 386 src.flatten(stream); 387 388 stream.offset(0); 389 SRC newSRC{stream}; 390 EXPECT_TRUE(newSRC.valid()); 391 ASSERT_TRUE(src.callouts()); 392 EXPECT_EQ(src.callouts()->callouts().size(), 1); 393 } 394 395 // Test that when the location code can't be obtained that 396 // a procedure callout is used. 397 TEST_F(SRCTest, InventoryCalloutNoLocCodeTest) 398 { 399 message::Entry entry; 400 entry.src.type = 0xBD; 401 entry.src.reasonCode = 0xABCD; 402 entry.subsystem = 0x42; 403 entry.src.powerFault = false; 404 405 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"}; 406 AdditionalData ad{adData}; 407 NiceMock<MockDataInterface> dataIface; 408 409 auto func = []() { 410 throw sdbusplus::exception::SdBusError(5, "Error"); 411 return std::string{}; 412 }; 413 414 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 415 .Times(1) 416 .WillOnce(InvokeWithoutArgs(func)); 417 418 EXPECT_CALL(dataIface, getHWCalloutFields(_, _, _, _)).Times(0); 419 420 SRC src{entry, ad, dataIface}; 421 EXPECT_TRUE(src.valid()); 422 423 ASSERT_TRUE(src.callouts()); 424 425 EXPECT_EQ(src.callouts()->callouts().size(), 1); 426 427 auto& callout = src.callouts()->callouts().front(); 428 EXPECT_EQ(callout->locationCodeSize(), 0); 429 EXPECT_EQ(callout->priority(), 'H'); 430 431 auto& fru = callout->fruIdentity(); 432 433 EXPECT_EQ(fru->getMaintProc().value(), "BMCSP01"); 434 EXPECT_FALSE(fru->getPN()); 435 EXPECT_FALSE(fru->getSN()); 436 EXPECT_FALSE(fru->getCCIN()); 437 438 // flatten and unflatten 439 std::vector<uint8_t> data; 440 Stream stream{data}; 441 src.flatten(stream); 442 443 stream.offset(0); 444 SRC newSRC{stream}; 445 EXPECT_TRUE(newSRC.valid()); 446 ASSERT_TRUE(src.callouts()); 447 EXPECT_EQ(src.callouts()->callouts().size(), 1); 448 } 449 450 // Test that when the VPD can't be obtained that 451 // a callout is still created. 452 TEST_F(SRCTest, InventoryCalloutNoVPDTest) 453 { 454 message::Entry entry; 455 entry.src.type = 0xBD; 456 entry.src.reasonCode = 0xABCD; 457 entry.subsystem = 0x42; 458 entry.src.powerFault = false; 459 460 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"}; 461 AdditionalData ad{adData}; 462 NiceMock<MockDataInterface> dataIface; 463 464 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 465 .Times(1) 466 .WillOnce(Return("UTMS-P10")); 467 468 auto func = []() { throw sdbusplus::exception::SdBusError(5, "Error"); }; 469 470 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _)) 471 .Times(1) 472 .WillOnce(InvokeWithoutArgs(func)); 473 474 SRC src{entry, ad, dataIface}; 475 EXPECT_TRUE(src.valid()); 476 ASSERT_TRUE(src.callouts()); 477 EXPECT_EQ(src.callouts()->callouts().size(), 1); 478 479 auto& callout = src.callouts()->callouts().front(); 480 EXPECT_EQ(callout->locationCode(), "UTMS-P10"); 481 EXPECT_EQ(callout->priority(), 'H'); 482 483 auto& fru = callout->fruIdentity(); 484 485 EXPECT_EQ(fru->getPN(), ""); 486 EXPECT_EQ(fru->getCCIN(), ""); 487 EXPECT_EQ(fru->getSN(), ""); 488 EXPECT_FALSE(fru->getMaintProc()); 489 490 // flatten and unflatten 491 std::vector<uint8_t> data; 492 Stream stream{data}; 493 src.flatten(stream); 494 495 stream.offset(0); 496 SRC newSRC{stream}; 497 EXPECT_TRUE(newSRC.valid()); 498 ASSERT_TRUE(src.callouts()); 499 EXPECT_EQ(src.callouts()->callouts().size(), 1); 500 } 501 502 TEST_F(SRCTest, RegistryCalloutTest) 503 { 504 message::Entry entry; 505 entry.src.type = 0xBD; 506 entry.src.reasonCode = 0xABCD; 507 entry.subsystem = 0x42; 508 entry.src.powerFault = false; 509 510 entry.callouts = R"( 511 [ 512 { 513 "System": "systemA", 514 "CalloutList": 515 [ 516 { 517 "Priority": "high", 518 "SymbolicFRU": "service_docs" 519 }, 520 { 521 "Priority": "medium", 522 "Procedure": "no_vpd_for_fru" 523 } 524 ] 525 }, 526 { 527 "System": "systemB", 528 "CalloutList": 529 [ 530 { 531 "Priority": "high", 532 "LocCode": "P0-C8", 533 "SymbolicFRUTrusted": "service_docs" 534 }, 535 { 536 "Priority": "medium", 537 "SymbolicFRUTrusted": "service_docs" 538 } 539 ] 540 }, 541 { 542 "System": "systemC", 543 "CalloutList": 544 [ 545 { 546 "Priority": "high", 547 "LocCode": "P0-C8" 548 }, 549 { 550 "Priority": "medium", 551 "LocCode": "P0-C9" 552 } 553 ] 554 } 555 ])"_json; 556 557 { 558 // Call out a symbolic FRU and a procedure 559 AdditionalData ad; 560 NiceMock<MockDataInterface> dataIface; 561 std::vector<std::string> names{"systemA"}; 562 563 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 564 565 SRC src{entry, ad, dataIface}; 566 567 auto& callouts = src.callouts()->callouts(); 568 ASSERT_EQ(callouts.size(), 2); 569 570 EXPECT_EQ(callouts[0]->locationCodeSize(), 0); 571 EXPECT_EQ(callouts[0]->priority(), 'H'); 572 573 EXPECT_EQ(callouts[1]->locationCodeSize(), 0); 574 EXPECT_EQ(callouts[1]->priority(), 'M'); 575 576 auto& fru1 = callouts[0]->fruIdentity(); 577 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 578 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU); 579 EXPECT_FALSE(fru1->getMaintProc()); 580 EXPECT_FALSE(fru1->getSN()); 581 EXPECT_FALSE(fru1->getCCIN()); 582 583 auto& fru2 = callouts[1]->fruIdentity(); 584 EXPECT_EQ(fru2->getMaintProc().value(), "BMCSP01"); 585 EXPECT_EQ(fru2->failingComponentType(), 586 src::FRUIdentity::maintenanceProc); 587 EXPECT_FALSE(fru2->getPN()); 588 EXPECT_FALSE(fru2->getSN()); 589 EXPECT_FALSE(fru2->getCCIN()); 590 } 591 592 { 593 // Call out a trusted symbolic FRU with a location code, and 594 // another one without. 595 AdditionalData ad; 596 NiceMock<MockDataInterface> dataIface; 597 std::vector<std::string> names{"systemB"}; 598 599 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8")); 600 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 601 602 SRC src{entry, ad, dataIface}; 603 604 auto& callouts = src.callouts()->callouts(); 605 EXPECT_EQ(callouts.size(), 2); 606 607 EXPECT_EQ(callouts[0]->locationCode(), "P0-C8"); 608 EXPECT_EQ(callouts[0]->priority(), 'H'); 609 610 EXPECT_EQ(callouts[1]->locationCodeSize(), 0); 611 EXPECT_EQ(callouts[1]->priority(), 'M'); 612 613 auto& fru1 = callouts[0]->fruIdentity(); 614 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 615 EXPECT_EQ(fru1->failingComponentType(), 616 src::FRUIdentity::symbolicFRUTrustedLocCode); 617 EXPECT_FALSE(fru1->getMaintProc()); 618 EXPECT_FALSE(fru1->getSN()); 619 EXPECT_FALSE(fru1->getCCIN()); 620 621 // It asked for a trusted symbolic FRU, but no location code 622 // was provided so it is switched back to a normal one 623 auto& fru2 = callouts[1]->fruIdentity(); 624 EXPECT_EQ(fru2->getPN().value(), "SVCDOCS"); 625 EXPECT_EQ(fru2->failingComponentType(), src::FRUIdentity::symbolicFRU); 626 EXPECT_FALSE(fru2->getMaintProc()); 627 EXPECT_FALSE(fru2->getSN()); 628 EXPECT_FALSE(fru2->getCCIN()); 629 } 630 631 { 632 // Two hardware callouts 633 AdditionalData ad; 634 NiceMock<MockDataInterface> dataIface; 635 std::vector<std::string> names{"systemC"}; 636 637 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 638 639 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0)) 640 .WillOnce(Return("UXXX-P0-C8")); 641 642 EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0)) 643 .WillOnce(Return("UXXX-P0-C9")); 644 645 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0, false)) 646 .WillOnce(Return( 647 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0")); 648 649 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false)) 650 .WillOnce(Return( 651 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1")); 652 653 EXPECT_CALL( 654 dataIface, 655 getHWCalloutFields( 656 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, 657 _)) 658 .Times(1) 659 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 660 SetArgReferee<2>("CCCC"), 661 SetArgReferee<3>("123456789ABC"))); 662 663 EXPECT_CALL( 664 dataIface, 665 getHWCalloutFields( 666 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _, 667 _)) 668 .Times(1) 669 .WillOnce(DoAll(SetArgReferee<1>("2345678"), 670 SetArgReferee<2>("DDDD"), 671 SetArgReferee<3>("23456789ABCD"))); 672 673 SRC src{entry, ad, dataIface}; 674 675 auto& callouts = src.callouts()->callouts(); 676 EXPECT_EQ(callouts.size(), 2); 677 678 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C8"); 679 EXPECT_EQ(callouts[0]->priority(), 'H'); 680 681 auto& fru1 = callouts[0]->fruIdentity(); 682 EXPECT_EQ(fru1->getPN().value(), "1234567"); 683 EXPECT_EQ(fru1->getCCIN().value(), "CCCC"); 684 EXPECT_EQ(fru1->getSN().value(), "123456789ABC"); 685 686 EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C9"); 687 EXPECT_EQ(callouts[1]->priority(), 'M'); 688 689 auto& fru2 = callouts[1]->fruIdentity(); 690 EXPECT_EQ(fru2->getPN().value(), "2345678"); 691 EXPECT_EQ(fru2->getCCIN().value(), "DDDD"); 692 EXPECT_EQ(fru2->getSN().value(), "23456789ABCD"); 693 } 694 } 695 696 // Test that a symbolic FRU with a trusted location code callout 697 // from the registry can get its location from the 698 // CALLOUT_INVENTORY_PATH AdditionalData entry. 699 TEST_F(SRCTest, SymbolicFRUWithInvPathTest) 700 { 701 message::Entry entry; 702 entry.src.type = 0xBD; 703 entry.src.reasonCode = 0xABCD; 704 entry.subsystem = 0x42; 705 entry.src.powerFault = false; 706 707 entry.callouts = R"( 708 [{ 709 "CalloutList": 710 [ 711 { 712 "Priority": "high", 713 "SymbolicFRUTrusted": "service_docs", 714 "UseInventoryLocCode": true 715 }, 716 { 717 "Priority": "medium", 718 "LocCode": "P0-C8", 719 "SymbolicFRUTrusted": "pwrsply" 720 } 721 ] 722 }])"_json; 723 724 { 725 // The location code for the first symbolic FRU callout will 726 // come from this inventory path since UseInventoryLocCode is set. 727 // In this case there will be no normal FRU callout for the motherboard. 728 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"}; 729 AdditionalData ad{adData}; 730 NiceMock<MockDataInterface> dataIface; 731 std::vector<std::string> names{"systemA"}; 732 733 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 734 735 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 736 .Times(1) 737 .WillOnce(Return("Ufcs-P10")); 738 739 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0)) 740 .WillOnce(Return("Ufcs-P0-C8")); 741 742 SRC src{entry, ad, dataIface}; 743 744 auto& callouts = src.callouts()->callouts(); 745 EXPECT_EQ(callouts.size(), 2); 746 747 // The location code for the first symbolic FRU callout with a 748 // trusted location code comes from the motherboard. 749 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P10"); 750 EXPECT_EQ(callouts[0]->priority(), 'H'); 751 auto& fru1 = callouts[0]->fruIdentity(); 752 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 753 EXPECT_EQ(fru1->failingComponentType(), 754 src::FRUIdentity::symbolicFRUTrustedLocCode); 755 756 // The second trusted symbolic FRU callouts uses the location 757 // code in the registry as usual. 758 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P0-C8"); 759 EXPECT_EQ(callouts[1]->priority(), 'M'); 760 auto& fru2 = callouts[1]->fruIdentity(); 761 EXPECT_EQ(fru2->getPN().value(), "PWRSPLY"); 762 EXPECT_EQ(fru2->failingComponentType(), 763 src::FRUIdentity::symbolicFRUTrustedLocCode); 764 } 765 766 { 767 // This time say we want to use the location code from 768 // the inventory, but don't pass it in and the callout should 769 // end up a regular symbolic FRU 770 entry.callouts = R"( 771 [{ 772 "CalloutList": 773 [ 774 { 775 "Priority": "high", 776 "SymbolicFRUTrusted": "service_docs", 777 "UseInventoryLocCode": true 778 } 779 ] 780 }])"_json; 781 782 AdditionalData ad; 783 NiceMock<MockDataInterface> dataIface; 784 std::vector<std::string> names{"systemA"}; 785 786 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 787 788 SRC src{entry, ad, dataIface}; 789 790 auto& callouts = src.callouts()->callouts(); 791 EXPECT_EQ(callouts.size(), 1); 792 793 EXPECT_EQ(callouts[0]->locationCode(), ""); 794 EXPECT_EQ(callouts[0]->priority(), 'H'); 795 auto& fru1 = callouts[0]->fruIdentity(); 796 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 797 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU); 798 } 799 } 800 801 // Test looking up device path fails in the callout jSON. 802 TEST_F(SRCTest, DevicePathCalloutTest) 803 { 804 message::Entry entry; 805 entry.src.type = 0xBD; 806 entry.src.reasonCode = 0xABCD; 807 entry.subsystem = 0x42; 808 entry.src.powerFault = false; 809 810 const auto calloutJSON = R"( 811 { 812 "I2C": 813 { 814 "14": 815 { 816 "114": 817 { 818 "Callouts":[ 819 { 820 "Name": "/chassis/motherboard/cpu0", 821 "LocationCode": "P1-C40", 822 "Priority": "H" 823 }, 824 { 825 "Name": "/chassis/motherboard", 826 "LocationCode": "P1", 827 "Priority": "M" 828 }, 829 { 830 "Name": "/chassis/motherboard/bmc", 831 "LocationCode": "P1-C15", 832 "Priority": "L" 833 } 834 ], 835 "Dest": "proc 0 target" 836 } 837 } 838 } 839 })"; 840 841 auto dataPath = getPELReadOnlyDataPath(); 842 std::ofstream file{dataPath / "systemA_dev_callouts.json"}; 843 file << calloutJSON; 844 file.close(); 845 846 NiceMock<MockDataInterface> dataIface; 847 std::vector<std::string> names{"systemA"}; 848 849 EXPECT_CALL(dataIface, getSystemNames) 850 .Times(5) 851 .WillRepeatedly(Return(names)); 852 853 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0, false)) 854 .Times(3) 855 .WillRepeatedly( 856 Return("/xyz/openbmc_project/inventory/chassis/motherboard/cpu0")); 857 858 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false)) 859 .Times(3) 860 .WillRepeatedly( 861 Return("/xyz/openbmc_project/inventory/chassis/motherboard")); 862 863 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false)) 864 .Times(3) 865 .WillRepeatedly( 866 Return("/xyz/openbmc_project/inventory/chassis/motherboard/bmc")); 867 868 EXPECT_CALL(dataIface, expandLocationCode("P1-C40", 0)) 869 .Times(3) 870 .WillRepeatedly(Return("Ufcs-P1-C40")); 871 872 EXPECT_CALL(dataIface, expandLocationCode("P1", 0)) 873 .Times(3) 874 .WillRepeatedly(Return("Ufcs-P1")); 875 876 EXPECT_CALL(dataIface, expandLocationCode("P1-C15", 0)) 877 .Times(3) 878 .WillRepeatedly(Return("Ufcs-P1-C15")); 879 880 EXPECT_CALL( 881 dataIface, 882 getHWCalloutFields( 883 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _)) 884 .Times(3) 885 .WillRepeatedly(DoAll(SetArgReferee<1>("1234567"), 886 SetArgReferee<2>("CCCC"), 887 SetArgReferee<3>("123456789ABC"))); 888 EXPECT_CALL( 889 dataIface, 890 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard", 891 _, _, _)) 892 .Times(3) 893 .WillRepeatedly(DoAll(SetArgReferee<1>("7654321"), 894 SetArgReferee<2>("MMMM"), 895 SetArgReferee<3>("CBA987654321"))); 896 EXPECT_CALL( 897 dataIface, 898 getHWCalloutFields( 899 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _, _, _)) 900 .Times(3) 901 .WillRepeatedly(DoAll(SetArgReferee<1>("7123456"), 902 SetArgReferee<2>("BBBB"), 903 SetArgReferee<3>("C123456789AB"))); 904 905 // Call this below with different AdditionalData values that 906 // result in the same callouts. 907 auto checkCallouts = [&entry, &dataIface](const auto& items) { 908 AdditionalData ad{items}; 909 SRC src{entry, ad, dataIface}; 910 911 ASSERT_TRUE(src.callouts()); 912 auto& callouts = src.callouts()->callouts(); 913 914 ASSERT_EQ(callouts.size(), 3); 915 916 { 917 EXPECT_EQ(callouts[0]->priority(), 'H'); 918 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40"); 919 920 auto& fru = callouts[0]->fruIdentity(); 921 EXPECT_EQ(fru->getPN().value(), "1234567"); 922 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 923 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 924 } 925 { 926 EXPECT_EQ(callouts[1]->priority(), 'M'); 927 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1"); 928 929 auto& fru = callouts[1]->fruIdentity(); 930 EXPECT_EQ(fru->getPN().value(), "7654321"); 931 EXPECT_EQ(fru->getCCIN().value(), "MMMM"); 932 EXPECT_EQ(fru->getSN().value(), "CBA987654321"); 933 } 934 { 935 EXPECT_EQ(callouts[2]->priority(), 'L'); 936 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15"); 937 938 auto& fru = callouts[2]->fruIdentity(); 939 EXPECT_EQ(fru->getPN().value(), "7123456"); 940 EXPECT_EQ(fru->getCCIN().value(), "BBBB"); 941 EXPECT_EQ(fru->getSN().value(), "C123456789AB"); 942 } 943 }; 944 945 { 946 // Callouts based on the device path 947 std::vector<std::string> items{ 948 "CALLOUT_ERRNO=5", 949 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/" 950 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"}; 951 952 checkCallouts(items); 953 } 954 955 { 956 // Callouts based on the I2C bus and address 957 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14", 958 "CALLOUT_IIC_ADDR=0x72"}; 959 checkCallouts(items); 960 } 961 962 { 963 // Also based on I2C bus and address, but with bus = /dev/i2c-14 964 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14", 965 "CALLOUT_IIC_ADDR=0x72"}; 966 checkCallouts(items); 967 } 968 969 { 970 // Callout not found 971 std::vector<std::string> items{ 972 "CALLOUT_ERRNO=5", 973 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/" 974 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"}; 975 976 AdditionalData ad{items}; 977 SRC src{entry, ad, dataIface}; 978 979 EXPECT_FALSE(src.callouts()); 980 ASSERT_EQ(src.getDebugData().size(), 1); 981 EXPECT_EQ(src.getDebugData()[0], 982 "Problem looking up I2C callouts on 24 18: " 983 "[json.exception.out_of_range.403] key '24' not found"); 984 } 985 986 { 987 // Callout not found 988 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=22", 989 "CALLOUT_IIC_ADDR=0x99"}; 990 AdditionalData ad{items}; 991 SRC src{entry, ad, dataIface}; 992 993 EXPECT_FALSE(src.callouts()); 994 ASSERT_EQ(src.getDebugData().size(), 1); 995 EXPECT_EQ(src.getDebugData()[0], 996 "Problem looking up I2C callouts on 22 153: " 997 "[json.exception.out_of_range.403] key '22' not found"); 998 } 999 1000 fs::remove_all(dataPath); 1001 } 1002 1003 // Test when callouts are passed in via JSON 1004 TEST_F(SRCTest, JsonCalloutsTest) 1005 { 1006 const auto jsonCallouts = R"( 1007 [ 1008 { 1009 "LocationCode": "P0-C1", 1010 "Priority": "H", 1011 "MRUs": [ 1012 { 1013 "ID": 42, 1014 "Priority": "H" 1015 }, 1016 { 1017 "ID": 43, 1018 "Priority": "M" 1019 } 1020 ] 1021 }, 1022 { 1023 "InventoryPath": "/inv/system/chassis/motherboard/cpu0", 1024 "Priority": "M", 1025 "Guarded": true, 1026 "Deconfigured": true 1027 }, 1028 { 1029 "Procedure": "PROCEDU", 1030 "Priority": "A" 1031 }, 1032 { 1033 "SymbolicFRU": "TRUSTED", 1034 "Priority": "B", 1035 "TrustedLocationCode": true, 1036 "LocationCode": "P1-C23" 1037 }, 1038 { 1039 "SymbolicFRU": "FRUTST1", 1040 "Priority": "C", 1041 "LocationCode": "P1-C24" 1042 }, 1043 { 1044 "SymbolicFRU": "FRUTST2LONG", 1045 "Priority": "L" 1046 } 1047 ] 1048 )"_json; 1049 1050 message::Entry entry; 1051 entry.src.type = 0xBD; 1052 entry.src.reasonCode = 0xABCD; 1053 entry.subsystem = 0x42; 1054 entry.src.powerFault = false; 1055 1056 AdditionalData ad; 1057 NiceMock<MockDataInterface> dataIface; 1058 1059 // Callout 0 mock calls 1060 { 1061 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0)) 1062 .Times(1) 1063 .WillOnce(Return("UXXX-P0-C1")); 1064 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false)) 1065 .Times(1) 1066 .WillOnce(Return("/inv/system/chassis/motherboard/bmc")); 1067 EXPECT_CALL( 1068 dataIface, 1069 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _)) 1070 .Times(1) 1071 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 1072 SetArgReferee<2>("CCCC"), 1073 SetArgReferee<3>("123456789ABC"))); 1074 } 1075 // Callout 1 mock calls 1076 { 1077 EXPECT_CALL(dataIface, 1078 getLocationCode("/inv/system/chassis/motherboard/cpu0")) 1079 .WillOnce(Return("UYYY-P5")); 1080 EXPECT_CALL( 1081 dataIface, 1082 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _)) 1083 .Times(1) 1084 .WillOnce(DoAll(SetArgReferee<1>("2345678"), 1085 SetArgReferee<2>("DDDD"), 1086 SetArgReferee<3>("23456789ABCD"))); 1087 } 1088 // Callout 3 mock calls 1089 { 1090 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0)) 1091 .Times(1) 1092 .WillOnce(Return("UXXX-P1-C23")); 1093 } 1094 // Callout 4 mock calls 1095 { 1096 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0)) 1097 .Times(1) 1098 .WillOnce(Return("UXXX-P1-C24")); 1099 } 1100 1101 SRC src{entry, ad, jsonCallouts, dataIface}; 1102 ASSERT_TRUE(src.callouts()); 1103 1104 // Check the guarded and deconfigured flags 1105 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000); 1106 1107 const auto& callouts = src.callouts()->callouts(); 1108 ASSERT_EQ(callouts.size(), 6); 1109 1110 // Check callout 0 1111 { 1112 EXPECT_EQ(callouts[0]->priority(), 'H'); 1113 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1"); 1114 1115 auto& fru = callouts[0]->fruIdentity(); 1116 EXPECT_EQ(fru->getPN().value(), "1234567"); 1117 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 1118 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 1119 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1120 1121 auto& mruCallouts = callouts[0]->mru(); 1122 ASSERT_TRUE(mruCallouts); 1123 auto& mrus = mruCallouts->mrus(); 1124 ASSERT_EQ(mrus.size(), 2); 1125 EXPECT_EQ(mrus[0].id, 42); 1126 EXPECT_EQ(mrus[0].priority, 'H'); 1127 EXPECT_EQ(mrus[1].id, 43); 1128 EXPECT_EQ(mrus[1].priority, 'M'); 1129 } 1130 1131 // Check callout 1 1132 { 1133 EXPECT_EQ(callouts[1]->priority(), 'M'); 1134 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5"); 1135 1136 auto& fru = callouts[1]->fruIdentity(); 1137 EXPECT_EQ(fru->getPN().value(), "2345678"); 1138 EXPECT_EQ(fru->getCCIN().value(), "DDDD"); 1139 EXPECT_EQ(fru->getSN().value(), "23456789ABCD"); 1140 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1141 } 1142 1143 // Check callout 2 1144 { 1145 EXPECT_EQ(callouts[2]->priority(), 'A'); 1146 EXPECT_EQ(callouts[2]->locationCode(), ""); 1147 1148 auto& fru = callouts[2]->fruIdentity(); 1149 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU"); 1150 EXPECT_EQ(fru->failingComponentType(), 1151 src::FRUIdentity::maintenanceProc); 1152 } 1153 1154 // Check callout 3 1155 { 1156 EXPECT_EQ(callouts[3]->priority(), 'B'); 1157 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23"); 1158 1159 auto& fru = callouts[3]->fruIdentity(); 1160 EXPECT_EQ(fru->getPN().value(), "TRUSTED"); 1161 EXPECT_EQ(fru->failingComponentType(), 1162 src::FRUIdentity::symbolicFRUTrustedLocCode); 1163 } 1164 1165 // Check callout 4 1166 { 1167 EXPECT_EQ(callouts[4]->priority(), 'C'); 1168 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24"); 1169 1170 auto& fru = callouts[4]->fruIdentity(); 1171 EXPECT_EQ(fru->getPN().value(), "FRUTST1"); 1172 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU); 1173 } 1174 1175 // Check callout 5 1176 { 1177 EXPECT_EQ(callouts[5]->priority(), 'L'); 1178 EXPECT_EQ(callouts[5]->locationCode(), ""); 1179 1180 auto& fru = callouts[5]->fruIdentity(); 1181 EXPECT_EQ(fru->getPN().value(), "FRUTST2"); 1182 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU); 1183 } 1184 1185 // Check that it didn't find any errors 1186 const auto& data = src.getDebugData(); 1187 EXPECT_TRUE(data.empty()); 1188 } 1189 1190 TEST_F(SRCTest, JsonBadCalloutsTest) 1191 { 1192 // The first call will have a Throw in a mock call. 1193 // The second will have a different Throw in a mock call. 1194 // The others have issues with the Priority field. 1195 const auto jsonCallouts = R"( 1196 [ 1197 { 1198 "LocationCode": "P0-C1", 1199 "Priority": "H" 1200 }, 1201 { 1202 "LocationCode": "P0-C2", 1203 "Priority": "H" 1204 }, 1205 { 1206 "LocationCode": "P0-C3" 1207 }, 1208 { 1209 "LocationCode": "P0-C4", 1210 "Priority": "X" 1211 } 1212 ] 1213 )"_json; 1214 1215 message::Entry entry; 1216 entry.src.type = 0xBD; 1217 entry.src.reasonCode = 0xABCD; 1218 entry.subsystem = 0x42; 1219 entry.src.powerFault = false; 1220 1221 AdditionalData ad; 1222 NiceMock<MockDataInterface> dataIface; 1223 1224 // Callout 0 mock calls 1225 // Expand location code will fail, so the unexpanded location 1226 // code should show up in the callout instead. 1227 { 1228 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0)) 1229 .WillOnce(Throw(std::runtime_error("Fail"))); 1230 1231 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false)) 1232 .Times(1) 1233 .WillOnce(Return("/inv/system/chassis/motherboard/bmc")); 1234 EXPECT_CALL( 1235 dataIface, 1236 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _)) 1237 .Times(1) 1238 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 1239 SetArgReferee<2>("CCCC"), 1240 SetArgReferee<3>("123456789ABC"))); 1241 } 1242 1243 // Callout 1 mock calls 1244 // getInventoryFromLocCode will fail 1245 { 1246 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0)) 1247 .Times(1) 1248 .WillOnce(Return("UXXX-P0-C2")); 1249 1250 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false)) 1251 .Times(1) 1252 .WillOnce(Throw(std::runtime_error("Fail"))); 1253 } 1254 1255 SRC src{entry, ad, jsonCallouts, dataIface}; 1256 1257 ASSERT_TRUE(src.callouts()); 1258 1259 const auto& callouts = src.callouts()->callouts(); 1260 1261 // Only the first callout was successful 1262 ASSERT_EQ(callouts.size(), 1); 1263 1264 { 1265 EXPECT_EQ(callouts[0]->priority(), 'H'); 1266 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1"); 1267 1268 auto& fru = callouts[0]->fruIdentity(); 1269 EXPECT_EQ(fru->getPN().value(), "1234567"); 1270 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 1271 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 1272 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1273 } 1274 1275 const auto& data = src.getDebugData(); 1276 ASSERT_EQ(data.size(), 4); 1277 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail"); 1278 EXPECT_STREQ(data[1].c_str(), 1279 "Failed extracting callout data from JSON: Unable to " 1280 "get inventory path from location code: P0-C2: Fail"); 1281 EXPECT_STREQ(data[2].c_str(), 1282 "Failed extracting callout data from JSON: " 1283 "[json.exception.out_of_range.403] key 'Priority' not found"); 1284 EXPECT_STREQ(data[3].c_str(), 1285 "Failed extracting callout data from JSON: Invalid " 1286 "priority 'X' found in JSON callout"); 1287 } 1288 1289 // Test that an inventory path callout can have 1290 // a different priority than H. 1291 TEST_F(SRCTest, InventoryCalloutTestPriority) 1292 { 1293 message::Entry entry; 1294 entry.src.type = 0xBD; 1295 entry.src.reasonCode = 0xABCD; 1296 entry.subsystem = 0x42; 1297 entry.src.powerFault = false; 1298 1299 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard", 1300 "CALLOUT_PRIORITY=M"}; 1301 AdditionalData ad{adData}; 1302 NiceMock<MockDataInterface> dataIface; 1303 1304 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 1305 .WillOnce(Return("UTMS-P1")); 1306 1307 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _)) 1308 .Times(1) 1309 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"), 1310 SetArgReferee<3>("123456789ABC"))); 1311 1312 SRC src{entry, ad, dataIface}; 1313 EXPECT_TRUE(src.valid()); 1314 1315 ASSERT_TRUE(src.callouts()); 1316 1317 EXPECT_EQ(src.callouts()->callouts().size(), 1); 1318 1319 auto& callout = src.callouts()->callouts().front(); 1320 1321 EXPECT_EQ(callout->locationCode(), "UTMS-P1"); 1322 EXPECT_EQ(callout->priority(), 'M'); 1323 } 1324