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, 869 getLocationCode( 870 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0")) 871 .Times(3) 872 .WillRepeatedly(Return("Ufcs-P1-C40")); 873 EXPECT_CALL( 874 dataIface, 875 getLocationCode("/xyz/openbmc_project/inventory/chassis/motherboard")) 876 .Times(3) 877 .WillRepeatedly(Return("Ufcs-P1")); 878 EXPECT_CALL(dataIface, 879 getLocationCode( 880 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc")) 881 .Times(3) 882 .WillRepeatedly(Return("Ufcs-P1-C15")); 883 884 EXPECT_CALL( 885 dataIface, 886 getHWCalloutFields( 887 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _)) 888 .Times(3) 889 .WillRepeatedly(DoAll(SetArgReferee<1>("1234567"), 890 SetArgReferee<2>("CCCC"), 891 SetArgReferee<3>("123456789ABC"))); 892 EXPECT_CALL( 893 dataIface, 894 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard", 895 _, _, _)) 896 .Times(3) 897 .WillRepeatedly(DoAll(SetArgReferee<1>("7654321"), 898 SetArgReferee<2>("MMMM"), 899 SetArgReferee<3>("CBA987654321"))); 900 EXPECT_CALL( 901 dataIface, 902 getHWCalloutFields( 903 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _, _, _)) 904 .Times(3) 905 .WillRepeatedly(DoAll(SetArgReferee<1>("7123456"), 906 SetArgReferee<2>("BBBB"), 907 SetArgReferee<3>("C123456789AB"))); 908 909 // Call this below with different AdditionalData values that 910 // result in the same callouts. 911 auto checkCallouts = [&entry, &dataIface](const auto& items) { 912 AdditionalData ad{items}; 913 SRC src{entry, ad, dataIface}; 914 915 ASSERT_TRUE(src.callouts()); 916 auto& callouts = src.callouts()->callouts(); 917 918 ASSERT_EQ(callouts.size(), 3); 919 920 { 921 EXPECT_EQ(callouts[0]->priority(), 'H'); 922 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40"); 923 924 auto& fru = callouts[0]->fruIdentity(); 925 EXPECT_EQ(fru->getPN().value(), "1234567"); 926 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 927 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 928 } 929 { 930 EXPECT_EQ(callouts[1]->priority(), 'M'); 931 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1"); 932 933 auto& fru = callouts[1]->fruIdentity(); 934 EXPECT_EQ(fru->getPN().value(), "7654321"); 935 EXPECT_EQ(fru->getCCIN().value(), "MMMM"); 936 EXPECT_EQ(fru->getSN().value(), "CBA987654321"); 937 } 938 { 939 EXPECT_EQ(callouts[2]->priority(), 'L'); 940 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15"); 941 942 auto& fru = callouts[2]->fruIdentity(); 943 EXPECT_EQ(fru->getPN().value(), "7123456"); 944 EXPECT_EQ(fru->getCCIN().value(), "BBBB"); 945 EXPECT_EQ(fru->getSN().value(), "C123456789AB"); 946 } 947 }; 948 949 { 950 // Callouts based on the device path 951 std::vector<std::string> items{ 952 "CALLOUT_ERRNO=5", 953 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/" 954 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"}; 955 956 checkCallouts(items); 957 } 958 959 { 960 // Callouts based on the I2C bus and address 961 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14", 962 "CALLOUT_IIC_ADDR=0x72"}; 963 checkCallouts(items); 964 } 965 966 { 967 // Also based on I2C bus and address, but with bus = /dev/i2c-14 968 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14", 969 "CALLOUT_IIC_ADDR=0x72"}; 970 checkCallouts(items); 971 } 972 973 { 974 // Callout not found 975 std::vector<std::string> items{ 976 "CALLOUT_ERRNO=5", 977 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/" 978 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"}; 979 980 AdditionalData ad{items}; 981 SRC src{entry, ad, dataIface}; 982 983 EXPECT_FALSE(src.callouts()); 984 ASSERT_EQ(src.getDebugData().size(), 1); 985 EXPECT_EQ(src.getDebugData()[0], 986 "Problem looking up I2C callouts on 24 18: " 987 "[json.exception.out_of_range.403] key '24' not found"); 988 } 989 990 { 991 // Callout not found 992 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=22", 993 "CALLOUT_IIC_ADDR=0x99"}; 994 AdditionalData ad{items}; 995 SRC src{entry, ad, dataIface}; 996 997 EXPECT_FALSE(src.callouts()); 998 ASSERT_EQ(src.getDebugData().size(), 1); 999 EXPECT_EQ(src.getDebugData()[0], 1000 "Problem looking up I2C callouts on 22 153: " 1001 "[json.exception.out_of_range.403] key '22' not found"); 1002 } 1003 1004 fs::remove_all(dataPath); 1005 } 1006 1007 // Test when callouts are passed in via JSON 1008 TEST_F(SRCTest, JsonCalloutsTest) 1009 { 1010 const auto jsonCallouts = R"( 1011 [ 1012 { 1013 "LocationCode": "P0-C1", 1014 "Priority": "H", 1015 "MRUs": [ 1016 { 1017 "ID": 42, 1018 "Priority": "H" 1019 }, 1020 { 1021 "ID": 43, 1022 "Priority": "M" 1023 } 1024 ] 1025 }, 1026 { 1027 "InventoryPath": "/inv/system/chassis/motherboard/cpu0", 1028 "Priority": "M", 1029 "Guarded": true, 1030 "Deconfigured": true 1031 }, 1032 { 1033 "Procedure": "PROCEDU", 1034 "Priority": "A" 1035 }, 1036 { 1037 "SymbolicFRU": "TRUSTED", 1038 "Priority": "B", 1039 "TrustedLocationCode": true, 1040 "LocationCode": "P1-C23" 1041 }, 1042 { 1043 "SymbolicFRU": "FRUTST1", 1044 "Priority": "C", 1045 "LocationCode": "P1-C24" 1046 }, 1047 { 1048 "SymbolicFRU": "FRUTST2LONG", 1049 "Priority": "L" 1050 } 1051 ] 1052 )"_json; 1053 1054 message::Entry entry; 1055 entry.src.type = 0xBD; 1056 entry.src.reasonCode = 0xABCD; 1057 entry.subsystem = 0x42; 1058 entry.src.powerFault = false; 1059 1060 AdditionalData ad; 1061 NiceMock<MockDataInterface> dataIface; 1062 1063 // Callout 0 mock calls 1064 { 1065 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0)) 1066 .Times(1) 1067 .WillOnce(Return("UXXX-P0-C1")); 1068 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false)) 1069 .Times(1) 1070 .WillOnce(Return("/inv/system/chassis/motherboard/bmc")); 1071 EXPECT_CALL( 1072 dataIface, 1073 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _)) 1074 .Times(1) 1075 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 1076 SetArgReferee<2>("CCCC"), 1077 SetArgReferee<3>("123456789ABC"))); 1078 } 1079 // Callout 1 mock calls 1080 { 1081 EXPECT_CALL(dataIface, 1082 getLocationCode("/inv/system/chassis/motherboard/cpu0")) 1083 .WillOnce(Return("UYYY-P5")); 1084 EXPECT_CALL( 1085 dataIface, 1086 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _)) 1087 .Times(1) 1088 .WillOnce(DoAll(SetArgReferee<1>("2345678"), 1089 SetArgReferee<2>("DDDD"), 1090 SetArgReferee<3>("23456789ABCD"))); 1091 } 1092 // Callout 3 mock calls 1093 { 1094 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0)) 1095 .Times(1) 1096 .WillOnce(Return("UXXX-P1-C23")); 1097 } 1098 // Callout 4 mock calls 1099 { 1100 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0)) 1101 .Times(1) 1102 .WillOnce(Return("UXXX-P1-C24")); 1103 } 1104 1105 SRC src{entry, ad, jsonCallouts, dataIface}; 1106 ASSERT_TRUE(src.callouts()); 1107 1108 // Check the guarded and deconfigured flags 1109 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000); 1110 1111 const auto& callouts = src.callouts()->callouts(); 1112 ASSERT_EQ(callouts.size(), 6); 1113 1114 // Check callout 0 1115 { 1116 EXPECT_EQ(callouts[0]->priority(), 'H'); 1117 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1"); 1118 1119 auto& fru = callouts[0]->fruIdentity(); 1120 EXPECT_EQ(fru->getPN().value(), "1234567"); 1121 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 1122 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 1123 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1124 1125 auto& mruCallouts = callouts[0]->mru(); 1126 ASSERT_TRUE(mruCallouts); 1127 auto& mrus = mruCallouts->mrus(); 1128 ASSERT_EQ(mrus.size(), 2); 1129 EXPECT_EQ(mrus[0].id, 42); 1130 EXPECT_EQ(mrus[0].priority, 'H'); 1131 EXPECT_EQ(mrus[1].id, 43); 1132 EXPECT_EQ(mrus[1].priority, 'M'); 1133 } 1134 1135 // Check callout 1 1136 { 1137 EXPECT_EQ(callouts[1]->priority(), 'M'); 1138 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5"); 1139 1140 auto& fru = callouts[1]->fruIdentity(); 1141 EXPECT_EQ(fru->getPN().value(), "2345678"); 1142 EXPECT_EQ(fru->getCCIN().value(), "DDDD"); 1143 EXPECT_EQ(fru->getSN().value(), "23456789ABCD"); 1144 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1145 } 1146 1147 // Check callout 2 1148 { 1149 EXPECT_EQ(callouts[2]->priority(), 'A'); 1150 EXPECT_EQ(callouts[2]->locationCode(), ""); 1151 1152 auto& fru = callouts[2]->fruIdentity(); 1153 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU"); 1154 EXPECT_EQ(fru->failingComponentType(), 1155 src::FRUIdentity::maintenanceProc); 1156 } 1157 1158 // Check callout 3 1159 { 1160 EXPECT_EQ(callouts[3]->priority(), 'B'); 1161 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23"); 1162 1163 auto& fru = callouts[3]->fruIdentity(); 1164 EXPECT_EQ(fru->getPN().value(), "TRUSTED"); 1165 EXPECT_EQ(fru->failingComponentType(), 1166 src::FRUIdentity::symbolicFRUTrustedLocCode); 1167 } 1168 1169 // Check callout 4 1170 { 1171 EXPECT_EQ(callouts[4]->priority(), 'C'); 1172 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24"); 1173 1174 auto& fru = callouts[4]->fruIdentity(); 1175 EXPECT_EQ(fru->getPN().value(), "FRUTST1"); 1176 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU); 1177 } 1178 1179 // Check callout 5 1180 { 1181 EXPECT_EQ(callouts[5]->priority(), 'L'); 1182 EXPECT_EQ(callouts[5]->locationCode(), ""); 1183 1184 auto& fru = callouts[5]->fruIdentity(); 1185 EXPECT_EQ(fru->getPN().value(), "FRUTST2"); 1186 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU); 1187 } 1188 1189 // Check that it didn't find any errors 1190 const auto& data = src.getDebugData(); 1191 EXPECT_TRUE(data.empty()); 1192 } 1193 1194 TEST_F(SRCTest, JsonBadCalloutsTest) 1195 { 1196 // The first call will have a Throw in a mock call. 1197 // The second will have a different Throw in a mock call. 1198 // The others have issues with the Priority field. 1199 const auto jsonCallouts = R"( 1200 [ 1201 { 1202 "LocationCode": "P0-C1", 1203 "Priority": "H" 1204 }, 1205 { 1206 "LocationCode": "P0-C2", 1207 "Priority": "H" 1208 }, 1209 { 1210 "LocationCode": "P0-C3" 1211 }, 1212 { 1213 "LocationCode": "P0-C4", 1214 "Priority": "X" 1215 } 1216 ] 1217 )"_json; 1218 1219 message::Entry entry; 1220 entry.src.type = 0xBD; 1221 entry.src.reasonCode = 0xABCD; 1222 entry.subsystem = 0x42; 1223 entry.src.powerFault = false; 1224 1225 AdditionalData ad; 1226 NiceMock<MockDataInterface> dataIface; 1227 1228 // Callout 0 mock calls 1229 // Expand location code will fail, so the unexpanded location 1230 // code should show up in the callout instead. 1231 { 1232 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0)) 1233 .WillOnce(Throw(std::runtime_error("Fail"))); 1234 1235 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false)) 1236 .Times(1) 1237 .WillOnce(Return("/inv/system/chassis/motherboard/bmc")); 1238 EXPECT_CALL( 1239 dataIface, 1240 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _)) 1241 .Times(1) 1242 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 1243 SetArgReferee<2>("CCCC"), 1244 SetArgReferee<3>("123456789ABC"))); 1245 } 1246 1247 // Callout 1 mock calls 1248 // getInventoryFromLocCode will fail 1249 { 1250 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0)) 1251 .Times(1) 1252 .WillOnce(Return("UXXX-P0-C2")); 1253 1254 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false)) 1255 .Times(1) 1256 .WillOnce(Throw(std::runtime_error("Fail"))); 1257 } 1258 1259 SRC src{entry, ad, jsonCallouts, dataIface}; 1260 1261 ASSERT_TRUE(src.callouts()); 1262 1263 const auto& callouts = src.callouts()->callouts(); 1264 1265 // Only the first callout was successful 1266 ASSERT_EQ(callouts.size(), 1); 1267 1268 { 1269 EXPECT_EQ(callouts[0]->priority(), 'H'); 1270 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1"); 1271 1272 auto& fru = callouts[0]->fruIdentity(); 1273 EXPECT_EQ(fru->getPN().value(), "1234567"); 1274 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 1275 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 1276 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1277 } 1278 1279 const auto& data = src.getDebugData(); 1280 ASSERT_EQ(data.size(), 4); 1281 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail"); 1282 EXPECT_STREQ(data[1].c_str(), 1283 "Failed extracting callout data from JSON: Unable to " 1284 "get inventory path from location code: P0-C2: Fail"); 1285 EXPECT_STREQ(data[2].c_str(), 1286 "Failed extracting callout data from JSON: " 1287 "[json.exception.out_of_range.403] key 'Priority' not found"); 1288 EXPECT_STREQ(data[3].c_str(), 1289 "Failed extracting callout data from JSON: Invalid " 1290 "priority 'X' found in JSON callout"); 1291 } 1292 1293 // Test that an inventory path callout can have 1294 // a different priority than H. 1295 TEST_F(SRCTest, InventoryCalloutTestPriority) 1296 { 1297 message::Entry entry; 1298 entry.src.type = 0xBD; 1299 entry.src.reasonCode = 0xABCD; 1300 entry.subsystem = 0x42; 1301 entry.src.powerFault = false; 1302 1303 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard", 1304 "CALLOUT_PRIORITY=M"}; 1305 AdditionalData ad{adData}; 1306 NiceMock<MockDataInterface> dataIface; 1307 1308 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 1309 .WillOnce(Return("UTMS-P1")); 1310 1311 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _)) 1312 .Times(1) 1313 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"), 1314 SetArgReferee<3>("123456789ABC"))); 1315 1316 SRC src{entry, ad, dataIface}; 1317 EXPECT_TRUE(src.valid()); 1318 1319 ASSERT_TRUE(src.callouts()); 1320 1321 EXPECT_EQ(src.callouts()->callouts().size(), 1); 1322 1323 auto& callout = src.callouts()->callouts().front(); 1324 1325 EXPECT_EQ(callout->locationCode(), "UTMS-P1"); 1326 EXPECT_EQ(callout->priority(), 'M'); 1327 } 1328