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