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