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 // no callout is added. 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_FALSE(src.callouts()); 424 425 // flatten and unflatten 426 std::vector<uint8_t> data; 427 Stream stream{data}; 428 src.flatten(stream); 429 430 stream.offset(0); 431 SRC newSRC{stream}; 432 EXPECT_TRUE(newSRC.valid()); 433 ASSERT_FALSE(src.callouts()); 434 } 435 436 // Test that when the VPD can't be obtained that 437 // a callout is still created. 438 TEST_F(SRCTest, InventoryCalloutNoVPDTest) 439 { 440 message::Entry entry; 441 entry.src.type = 0xBD; 442 entry.src.reasonCode = 0xABCD; 443 entry.subsystem = 0x42; 444 entry.src.powerFault = false; 445 446 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"}; 447 AdditionalData ad{adData}; 448 NiceMock<MockDataInterface> dataIface; 449 450 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 451 .Times(1) 452 .WillOnce(Return("UTMS-P10")); 453 454 auto func = []() { throw sdbusplus::exception::SdBusError(5, "Error"); }; 455 456 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _)) 457 .Times(1) 458 .WillOnce(InvokeWithoutArgs(func)); 459 460 SRC src{entry, ad, dataIface}; 461 EXPECT_TRUE(src.valid()); 462 ASSERT_TRUE(src.callouts()); 463 EXPECT_EQ(src.callouts()->callouts().size(), 1); 464 465 auto& callout = src.callouts()->callouts().front(); 466 EXPECT_EQ(callout->locationCode(), "UTMS-P10"); 467 EXPECT_EQ(callout->priority(), 'H'); 468 469 auto& fru = callout->fruIdentity(); 470 471 EXPECT_EQ(fru->getPN(), ""); 472 EXPECT_EQ(fru->getCCIN(), ""); 473 EXPECT_EQ(fru->getSN(), ""); 474 EXPECT_FALSE(fru->getMaintProc()); 475 476 // flatten and unflatten 477 std::vector<uint8_t> data; 478 Stream stream{data}; 479 src.flatten(stream); 480 481 stream.offset(0); 482 SRC newSRC{stream}; 483 EXPECT_TRUE(newSRC.valid()); 484 ASSERT_TRUE(src.callouts()); 485 EXPECT_EQ(src.callouts()->callouts().size(), 1); 486 } 487 488 TEST_F(SRCTest, RegistryCalloutTest) 489 { 490 message::Entry entry; 491 entry.src.type = 0xBD; 492 entry.src.reasonCode = 0xABCD; 493 entry.subsystem = 0x42; 494 entry.src.powerFault = false; 495 496 entry.callouts = R"( 497 [ 498 { 499 "System": "systemA", 500 "CalloutList": 501 [ 502 { 503 "Priority": "high", 504 "SymbolicFRU": "service_docs" 505 }, 506 { 507 "Priority": "medium", 508 "Procedure": "bmc_code" 509 } 510 ] 511 }, 512 { 513 "System": "systemB", 514 "CalloutList": 515 [ 516 { 517 "Priority": "high", 518 "LocCode": "P0-C8", 519 "SymbolicFRUTrusted": "service_docs" 520 }, 521 { 522 "Priority": "medium", 523 "SymbolicFRUTrusted": "service_docs" 524 } 525 ] 526 }, 527 { 528 "System": "systemC", 529 "CalloutList": 530 [ 531 { 532 "Priority": "high", 533 "LocCode": "P0-C8" 534 }, 535 { 536 "Priority": "medium", 537 "LocCode": "P0-C9" 538 } 539 ] 540 } 541 ])"_json; 542 543 { 544 // Call out a symbolic FRU and a procedure 545 AdditionalData ad; 546 NiceMock<MockDataInterface> dataIface; 547 std::vector<std::string> names{"systemA"}; 548 549 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 550 551 SRC src{entry, ad, dataIface}; 552 553 auto& callouts = src.callouts()->callouts(); 554 ASSERT_EQ(callouts.size(), 2); 555 556 EXPECT_EQ(callouts[0]->locationCodeSize(), 0); 557 EXPECT_EQ(callouts[0]->priority(), 'H'); 558 559 EXPECT_EQ(callouts[1]->locationCodeSize(), 0); 560 EXPECT_EQ(callouts[1]->priority(), 'M'); 561 562 auto& fru1 = callouts[0]->fruIdentity(); 563 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 564 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU); 565 EXPECT_FALSE(fru1->getMaintProc()); 566 EXPECT_FALSE(fru1->getSN()); 567 EXPECT_FALSE(fru1->getCCIN()); 568 569 auto& fru2 = callouts[1]->fruIdentity(); 570 EXPECT_EQ(fru2->getMaintProc().value(), "BMC0001"); 571 EXPECT_EQ(fru2->failingComponentType(), 572 src::FRUIdentity::maintenanceProc); 573 EXPECT_FALSE(fru2->getPN()); 574 EXPECT_FALSE(fru2->getSN()); 575 EXPECT_FALSE(fru2->getCCIN()); 576 } 577 578 { 579 // Call out a trusted symbolic FRU with a location code, and 580 // another one without. 581 AdditionalData ad; 582 NiceMock<MockDataInterface> dataIface; 583 std::vector<std::string> names{"systemB"}; 584 585 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8")); 586 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 587 588 SRC src{entry, ad, dataIface}; 589 590 auto& callouts = src.callouts()->callouts(); 591 EXPECT_EQ(callouts.size(), 2); 592 593 EXPECT_EQ(callouts[0]->locationCode(), "P0-C8"); 594 EXPECT_EQ(callouts[0]->priority(), 'H'); 595 596 EXPECT_EQ(callouts[1]->locationCodeSize(), 0); 597 EXPECT_EQ(callouts[1]->priority(), 'M'); 598 599 auto& fru1 = callouts[0]->fruIdentity(); 600 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 601 EXPECT_EQ(fru1->failingComponentType(), 602 src::FRUIdentity::symbolicFRUTrustedLocCode); 603 EXPECT_FALSE(fru1->getMaintProc()); 604 EXPECT_FALSE(fru1->getSN()); 605 EXPECT_FALSE(fru1->getCCIN()); 606 607 // It asked for a trusted symbolic FRU, but no location code 608 // was provided so it is switched back to a normal one 609 auto& fru2 = callouts[1]->fruIdentity(); 610 EXPECT_EQ(fru2->getPN().value(), "SVCDOCS"); 611 EXPECT_EQ(fru2->failingComponentType(), src::FRUIdentity::symbolicFRU); 612 EXPECT_FALSE(fru2->getMaintProc()); 613 EXPECT_FALSE(fru2->getSN()); 614 EXPECT_FALSE(fru2->getCCIN()); 615 } 616 617 { 618 // Two hardware callouts 619 AdditionalData ad; 620 NiceMock<MockDataInterface> dataIface; 621 std::vector<std::string> names{"systemC"}; 622 623 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 624 625 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0)) 626 .WillOnce(Return("UXXX-P0-C8")); 627 628 EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0)) 629 .WillOnce(Return("UXXX-P0-C9")); 630 631 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0, false)) 632 .WillOnce(Return( 633 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0")); 634 635 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false)) 636 .WillOnce(Return( 637 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1")); 638 639 EXPECT_CALL( 640 dataIface, 641 getHWCalloutFields( 642 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, 643 _)) 644 .Times(1) 645 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 646 SetArgReferee<2>("CCCC"), 647 SetArgReferee<3>("123456789ABC"))); 648 649 EXPECT_CALL( 650 dataIface, 651 getHWCalloutFields( 652 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _, 653 _)) 654 .Times(1) 655 .WillOnce(DoAll(SetArgReferee<1>("2345678"), 656 SetArgReferee<2>("DDDD"), 657 SetArgReferee<3>("23456789ABCD"))); 658 659 SRC src{entry, ad, dataIface}; 660 661 auto& callouts = src.callouts()->callouts(); 662 EXPECT_EQ(callouts.size(), 2); 663 664 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C8"); 665 EXPECT_EQ(callouts[0]->priority(), 'H'); 666 667 auto& fru1 = callouts[0]->fruIdentity(); 668 EXPECT_EQ(fru1->getPN().value(), "1234567"); 669 EXPECT_EQ(fru1->getCCIN().value(), "CCCC"); 670 EXPECT_EQ(fru1->getSN().value(), "123456789ABC"); 671 672 EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C9"); 673 EXPECT_EQ(callouts[1]->priority(), 'M'); 674 675 auto& fru2 = callouts[1]->fruIdentity(); 676 EXPECT_EQ(fru2->getPN().value(), "2345678"); 677 EXPECT_EQ(fru2->getCCIN().value(), "DDDD"); 678 EXPECT_EQ(fru2->getSN().value(), "23456789ABCD"); 679 } 680 } 681 682 // Test that a symbolic FRU with a trusted location code callout 683 // from the registry can get its location from the 684 // CALLOUT_INVENTORY_PATH AdditionalData entry. 685 TEST_F(SRCTest, SymbolicFRUWithInvPathTest) 686 { 687 message::Entry entry; 688 entry.src.type = 0xBD; 689 entry.src.reasonCode = 0xABCD; 690 entry.subsystem = 0x42; 691 entry.src.powerFault = false; 692 693 entry.callouts = R"( 694 [{ 695 "CalloutList": 696 [ 697 { 698 "Priority": "high", 699 "SymbolicFRUTrusted": "service_docs", 700 "UseInventoryLocCode": true 701 }, 702 { 703 "Priority": "medium", 704 "LocCode": "P0-C8", 705 "SymbolicFRUTrusted": "pwrsply" 706 } 707 ] 708 }])"_json; 709 710 { 711 // The location code for the first symbolic FRU callout will 712 // come from this inventory path since UseInventoryLocCode is set. 713 // In this case there will be no normal FRU callout for the motherboard. 714 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"}; 715 AdditionalData ad{adData}; 716 NiceMock<MockDataInterface> dataIface; 717 std::vector<std::string> names{"systemA"}; 718 719 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 720 721 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 722 .Times(1) 723 .WillOnce(Return("Ufcs-P10")); 724 725 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0)) 726 .WillOnce(Return("Ufcs-P0-C8")); 727 728 SRC src{entry, ad, dataIface}; 729 730 auto& callouts = src.callouts()->callouts(); 731 EXPECT_EQ(callouts.size(), 2); 732 733 // The location code for the first symbolic FRU callout with a 734 // trusted location code comes from the motherboard. 735 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P10"); 736 EXPECT_EQ(callouts[0]->priority(), 'H'); 737 auto& fru1 = callouts[0]->fruIdentity(); 738 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 739 EXPECT_EQ(fru1->failingComponentType(), 740 src::FRUIdentity::symbolicFRUTrustedLocCode); 741 742 // The second trusted symbolic FRU callouts uses the location 743 // code in the registry as usual. 744 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P0-C8"); 745 EXPECT_EQ(callouts[1]->priority(), 'M'); 746 auto& fru2 = callouts[1]->fruIdentity(); 747 EXPECT_EQ(fru2->getPN().value(), "PWRSPLY"); 748 EXPECT_EQ(fru2->failingComponentType(), 749 src::FRUIdentity::symbolicFRUTrustedLocCode); 750 } 751 752 { 753 // This time say we want to use the location code from 754 // the inventory, but don't pass it in and the callout should 755 // end up a regular symbolic FRU 756 entry.callouts = R"( 757 [{ 758 "CalloutList": 759 [ 760 { 761 "Priority": "high", 762 "SymbolicFRUTrusted": "service_docs", 763 "UseInventoryLocCode": true 764 } 765 ] 766 }])"_json; 767 768 AdditionalData ad; 769 NiceMock<MockDataInterface> dataIface; 770 std::vector<std::string> names{"systemA"}; 771 772 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names)); 773 774 SRC src{entry, ad, dataIface}; 775 776 auto& callouts = src.callouts()->callouts(); 777 EXPECT_EQ(callouts.size(), 1); 778 779 EXPECT_EQ(callouts[0]->locationCode(), ""); 780 EXPECT_EQ(callouts[0]->priority(), 'H'); 781 auto& fru1 = callouts[0]->fruIdentity(); 782 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS"); 783 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU); 784 } 785 } 786 787 // Test looking up device path fails in the callout jSON. 788 TEST_F(SRCTest, DevicePathCalloutTest) 789 { 790 message::Entry entry; 791 entry.src.type = 0xBD; 792 entry.src.reasonCode = 0xABCD; 793 entry.subsystem = 0x42; 794 entry.src.powerFault = false; 795 796 const auto calloutJSON = R"( 797 { 798 "I2C": 799 { 800 "14": 801 { 802 "114": 803 { 804 "Callouts":[ 805 { 806 "Name": "/chassis/motherboard/cpu0", 807 "LocationCode": "P1-C40", 808 "Priority": "H" 809 }, 810 { 811 "Name": "/chassis/motherboard", 812 "LocationCode": "P1", 813 "Priority": "M" 814 }, 815 { 816 "Name": "/chassis/motherboard/bmc", 817 "LocationCode": "P1-C15", 818 "Priority": "L" 819 } 820 ], 821 "Dest": "proc 0 target" 822 } 823 } 824 } 825 })"; 826 827 auto dataPath = getPELReadOnlyDataPath(); 828 std::ofstream file{dataPath / "systemA_dev_callouts.json"}; 829 file << calloutJSON; 830 file.close(); 831 832 NiceMock<MockDataInterface> dataIface; 833 std::vector<std::string> names{"systemA"}; 834 835 EXPECT_CALL(dataIface, getSystemNames) 836 .Times(5) 837 .WillRepeatedly(Return(names)); 838 839 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0, false)) 840 .Times(3) 841 .WillRepeatedly( 842 Return("/xyz/openbmc_project/inventory/chassis/motherboard/cpu0")); 843 844 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false)) 845 .Times(3) 846 .WillRepeatedly( 847 Return("/xyz/openbmc_project/inventory/chassis/motherboard")); 848 849 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false)) 850 .Times(3) 851 .WillRepeatedly( 852 Return("/xyz/openbmc_project/inventory/chassis/motherboard/bmc")); 853 854 EXPECT_CALL(dataIface, expandLocationCode("P1-C40", 0)) 855 .Times(3) 856 .WillRepeatedly(Return("Ufcs-P1-C40")); 857 858 EXPECT_CALL(dataIface, expandLocationCode("P1", 0)) 859 .Times(3) 860 .WillRepeatedly(Return("Ufcs-P1")); 861 862 EXPECT_CALL(dataIface, expandLocationCode("P1-C15", 0)) 863 .Times(3) 864 .WillRepeatedly(Return("Ufcs-P1-C15")); 865 866 EXPECT_CALL( 867 dataIface, 868 getHWCalloutFields( 869 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _)) 870 .Times(3) 871 .WillRepeatedly(DoAll(SetArgReferee<1>("1234567"), 872 SetArgReferee<2>("CCCC"), 873 SetArgReferee<3>("123456789ABC"))); 874 EXPECT_CALL( 875 dataIface, 876 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard", 877 _, _, _)) 878 .Times(3) 879 .WillRepeatedly(DoAll(SetArgReferee<1>("7654321"), 880 SetArgReferee<2>("MMMM"), 881 SetArgReferee<3>("CBA987654321"))); 882 EXPECT_CALL( 883 dataIface, 884 getHWCalloutFields( 885 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _, _, _)) 886 .Times(3) 887 .WillRepeatedly(DoAll(SetArgReferee<1>("7123456"), 888 SetArgReferee<2>("BBBB"), 889 SetArgReferee<3>("C123456789AB"))); 890 891 // Call this below with different AdditionalData values that 892 // result in the same callouts. 893 auto checkCallouts = [&entry, &dataIface](const auto& items) { 894 AdditionalData ad{items}; 895 SRC src{entry, ad, dataIface}; 896 897 ASSERT_TRUE(src.callouts()); 898 auto& callouts = src.callouts()->callouts(); 899 900 ASSERT_EQ(callouts.size(), 3); 901 902 { 903 EXPECT_EQ(callouts[0]->priority(), 'H'); 904 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40"); 905 906 auto& fru = callouts[0]->fruIdentity(); 907 EXPECT_EQ(fru->getPN().value(), "1234567"); 908 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 909 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 910 } 911 { 912 EXPECT_EQ(callouts[1]->priority(), 'M'); 913 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1"); 914 915 auto& fru = callouts[1]->fruIdentity(); 916 EXPECT_EQ(fru->getPN().value(), "7654321"); 917 EXPECT_EQ(fru->getCCIN().value(), "MMMM"); 918 EXPECT_EQ(fru->getSN().value(), "CBA987654321"); 919 } 920 { 921 EXPECT_EQ(callouts[2]->priority(), 'L'); 922 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15"); 923 924 auto& fru = callouts[2]->fruIdentity(); 925 EXPECT_EQ(fru->getPN().value(), "7123456"); 926 EXPECT_EQ(fru->getCCIN().value(), "BBBB"); 927 EXPECT_EQ(fru->getSN().value(), "C123456789AB"); 928 } 929 }; 930 931 { 932 // Callouts based on the device path 933 std::vector<std::string> items{ 934 "CALLOUT_ERRNO=5", 935 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/" 936 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"}; 937 938 checkCallouts(items); 939 } 940 941 { 942 // Callouts based on the I2C bus and address 943 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14", 944 "CALLOUT_IIC_ADDR=0x72"}; 945 checkCallouts(items); 946 } 947 948 { 949 // Also based on I2C bus and address, but with bus = /dev/i2c-14 950 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14", 951 "CALLOUT_IIC_ADDR=0x72"}; 952 checkCallouts(items); 953 } 954 955 { 956 // Callout not found 957 std::vector<std::string> items{ 958 "CALLOUT_ERRNO=5", 959 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/" 960 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"}; 961 962 AdditionalData ad{items}; 963 SRC src{entry, ad, dataIface}; 964 965 EXPECT_FALSE(src.callouts()); 966 ASSERT_EQ(src.getDebugData().size(), 1); 967 EXPECT_EQ(src.getDebugData()[0], 968 "Problem looking up I2C callouts on 24 18: " 969 "[json.exception.out_of_range.403] key '24' not found"); 970 } 971 972 { 973 // Callout not found 974 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=22", 975 "CALLOUT_IIC_ADDR=0x99"}; 976 AdditionalData ad{items}; 977 SRC src{entry, ad, dataIface}; 978 979 EXPECT_FALSE(src.callouts()); 980 ASSERT_EQ(src.getDebugData().size(), 1); 981 EXPECT_EQ(src.getDebugData()[0], 982 "Problem looking up I2C callouts on 22 153: " 983 "[json.exception.out_of_range.403] key '22' not found"); 984 } 985 986 fs::remove_all(dataPath); 987 } 988 989 // Test when callouts are passed in via JSON 990 TEST_F(SRCTest, JsonCalloutsTest) 991 { 992 const auto jsonCallouts = R"( 993 [ 994 { 995 "LocationCode": "P0-C1", 996 "Priority": "H", 997 "MRUs": [ 998 { 999 "ID": 42, 1000 "Priority": "H" 1001 }, 1002 { 1003 "ID": 43, 1004 "Priority": "M" 1005 } 1006 ] 1007 }, 1008 { 1009 "InventoryPath": "/inv/system/chassis/motherboard/cpu0", 1010 "Priority": "M", 1011 "Guarded": true, 1012 "Deconfigured": true 1013 }, 1014 { 1015 "Procedure": "PROCEDU", 1016 "Priority": "A" 1017 }, 1018 { 1019 "SymbolicFRU": "TRUSTED", 1020 "Priority": "B", 1021 "TrustedLocationCode": true, 1022 "LocationCode": "P1-C23" 1023 }, 1024 { 1025 "SymbolicFRU": "FRUTST1", 1026 "Priority": "C", 1027 "LocationCode": "P1-C24" 1028 }, 1029 { 1030 "SymbolicFRU": "FRUTST2LONG", 1031 "Priority": "L" 1032 } 1033 ] 1034 )"_json; 1035 1036 message::Entry entry; 1037 entry.src.type = 0xBD; 1038 entry.src.reasonCode = 0xABCD; 1039 entry.subsystem = 0x42; 1040 entry.src.powerFault = false; 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("/inv/system/chassis/motherboard/bmc")); 1053 EXPECT_CALL( 1054 dataIface, 1055 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _)) 1056 .Times(1) 1057 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 1058 SetArgReferee<2>("CCCC"), 1059 SetArgReferee<3>("123456789ABC"))); 1060 } 1061 // Callout 1 mock calls 1062 { 1063 EXPECT_CALL(dataIface, 1064 getLocationCode("/inv/system/chassis/motherboard/cpu0")) 1065 .WillOnce(Return("UYYY-P5")); 1066 EXPECT_CALL( 1067 dataIface, 1068 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _)) 1069 .Times(1) 1070 .WillOnce(DoAll(SetArgReferee<1>("2345678"), 1071 SetArgReferee<2>("DDDD"), 1072 SetArgReferee<3>("23456789ABCD"))); 1073 } 1074 // Callout 3 mock calls 1075 { 1076 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0)) 1077 .Times(1) 1078 .WillOnce(Return("UXXX-P1-C23")); 1079 } 1080 // Callout 4 mock calls 1081 { 1082 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0)) 1083 .Times(1) 1084 .WillOnce(Return("UXXX-P1-C24")); 1085 } 1086 1087 SRC src{entry, ad, jsonCallouts, dataIface}; 1088 ASSERT_TRUE(src.callouts()); 1089 1090 // Check the guarded and deconfigured flags 1091 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000); 1092 1093 const auto& callouts = src.callouts()->callouts(); 1094 ASSERT_EQ(callouts.size(), 6); 1095 1096 // Check callout 0 1097 { 1098 EXPECT_EQ(callouts[0]->priority(), 'H'); 1099 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1"); 1100 1101 auto& fru = callouts[0]->fruIdentity(); 1102 EXPECT_EQ(fru->getPN().value(), "1234567"); 1103 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 1104 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 1105 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1106 1107 auto& mruCallouts = callouts[0]->mru(); 1108 ASSERT_TRUE(mruCallouts); 1109 auto& mrus = mruCallouts->mrus(); 1110 ASSERT_EQ(mrus.size(), 2); 1111 EXPECT_EQ(mrus[0].id, 42); 1112 EXPECT_EQ(mrus[0].priority, 'H'); 1113 EXPECT_EQ(mrus[1].id, 43); 1114 EXPECT_EQ(mrus[1].priority, 'M'); 1115 } 1116 1117 // Check callout 1 1118 { 1119 EXPECT_EQ(callouts[1]->priority(), 'M'); 1120 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5"); 1121 1122 auto& fru = callouts[1]->fruIdentity(); 1123 EXPECT_EQ(fru->getPN().value(), "2345678"); 1124 EXPECT_EQ(fru->getCCIN().value(), "DDDD"); 1125 EXPECT_EQ(fru->getSN().value(), "23456789ABCD"); 1126 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1127 } 1128 1129 // Check callout 2 1130 { 1131 EXPECT_EQ(callouts[2]->priority(), 'A'); 1132 EXPECT_EQ(callouts[2]->locationCode(), ""); 1133 1134 auto& fru = callouts[2]->fruIdentity(); 1135 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU"); 1136 EXPECT_EQ(fru->failingComponentType(), 1137 src::FRUIdentity::maintenanceProc); 1138 } 1139 1140 // Check callout 3 1141 { 1142 EXPECT_EQ(callouts[3]->priority(), 'B'); 1143 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23"); 1144 1145 auto& fru = callouts[3]->fruIdentity(); 1146 EXPECT_EQ(fru->getPN().value(), "TRUSTED"); 1147 EXPECT_EQ(fru->failingComponentType(), 1148 src::FRUIdentity::symbolicFRUTrustedLocCode); 1149 } 1150 1151 // Check callout 4 1152 { 1153 EXPECT_EQ(callouts[4]->priority(), 'C'); 1154 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24"); 1155 1156 auto& fru = callouts[4]->fruIdentity(); 1157 EXPECT_EQ(fru->getPN().value(), "FRUTST1"); 1158 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU); 1159 } 1160 1161 // Check callout 5 1162 { 1163 EXPECT_EQ(callouts[5]->priority(), 'L'); 1164 EXPECT_EQ(callouts[5]->locationCode(), ""); 1165 1166 auto& fru = callouts[5]->fruIdentity(); 1167 EXPECT_EQ(fru->getPN().value(), "FRUTST2"); 1168 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU); 1169 } 1170 1171 // Check that it didn't find any errors 1172 const auto& data = src.getDebugData(); 1173 EXPECT_TRUE(data.empty()); 1174 } 1175 1176 TEST_F(SRCTest, JsonBadCalloutsTest) 1177 { 1178 // The first call will have a Throw in a mock call. 1179 // The second will have a different Throw in a mock call. 1180 // The others have issues with the Priority field. 1181 const auto jsonCallouts = R"( 1182 [ 1183 { 1184 "LocationCode": "P0-C1", 1185 "Priority": "H" 1186 }, 1187 { 1188 "LocationCode": "P0-C2", 1189 "Priority": "H" 1190 }, 1191 { 1192 "LocationCode": "P0-C3" 1193 }, 1194 { 1195 "LocationCode": "P0-C4", 1196 "Priority": "X" 1197 } 1198 ] 1199 )"_json; 1200 1201 message::Entry entry; 1202 entry.src.type = 0xBD; 1203 entry.src.reasonCode = 0xABCD; 1204 entry.subsystem = 0x42; 1205 entry.src.powerFault = false; 1206 1207 AdditionalData ad; 1208 NiceMock<MockDataInterface> dataIface; 1209 1210 // Callout 0 mock calls 1211 // Expand location code will fail, so the unexpanded location 1212 // code should show up in the callout instead. 1213 { 1214 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0)) 1215 .WillOnce(Throw(std::runtime_error("Fail"))); 1216 1217 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false)) 1218 .Times(1) 1219 .WillOnce(Return("/inv/system/chassis/motherboard/bmc")); 1220 EXPECT_CALL( 1221 dataIface, 1222 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _)) 1223 .Times(1) 1224 .WillOnce(DoAll(SetArgReferee<1>("1234567"), 1225 SetArgReferee<2>("CCCC"), 1226 SetArgReferee<3>("123456789ABC"))); 1227 } 1228 1229 // Callout 1 mock calls 1230 // getInventoryFromLocCode will fail 1231 { 1232 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0)) 1233 .Times(1) 1234 .WillOnce(Return("UXXX-P0-C2")); 1235 1236 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false)) 1237 .Times(1) 1238 .WillOnce(Throw(std::runtime_error("Fail"))); 1239 } 1240 1241 SRC src{entry, ad, jsonCallouts, dataIface}; 1242 1243 ASSERT_TRUE(src.callouts()); 1244 1245 const auto& callouts = src.callouts()->callouts(); 1246 1247 // Only the first callout was successful 1248 ASSERT_EQ(callouts.size(), 1); 1249 1250 { 1251 EXPECT_EQ(callouts[0]->priority(), 'H'); 1252 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1"); 1253 1254 auto& fru = callouts[0]->fruIdentity(); 1255 EXPECT_EQ(fru->getPN().value(), "1234567"); 1256 EXPECT_EQ(fru->getCCIN().value(), "CCCC"); 1257 EXPECT_EQ(fru->getSN().value(), "123456789ABC"); 1258 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); 1259 } 1260 1261 const auto& data = src.getDebugData(); 1262 ASSERT_EQ(data.size(), 4); 1263 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail"); 1264 EXPECT_STREQ(data[1].c_str(), 1265 "Failed extracting callout data from JSON: Unable to " 1266 "get inventory path from location code: P0-C2: Fail"); 1267 EXPECT_STREQ(data[2].c_str(), 1268 "Failed extracting callout data from JSON: " 1269 "[json.exception.out_of_range.403] key 'Priority' not found"); 1270 EXPECT_STREQ(data[3].c_str(), 1271 "Failed extracting callout data from JSON: Invalid " 1272 "priority 'X' found in JSON callout"); 1273 } 1274 1275 // Test that an inventory path callout can have 1276 // a different priority than H. 1277 TEST_F(SRCTest, InventoryCalloutTestPriority) 1278 { 1279 message::Entry entry; 1280 entry.src.type = 0xBD; 1281 entry.src.reasonCode = 0xABCD; 1282 entry.subsystem = 0x42; 1283 entry.src.powerFault = false; 1284 1285 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard", 1286 "CALLOUT_PRIORITY=M"}; 1287 AdditionalData ad{adData}; 1288 NiceMock<MockDataInterface> dataIface; 1289 1290 EXPECT_CALL(dataIface, getLocationCode("motherboard")) 1291 .WillOnce(Return("UTMS-P1")); 1292 1293 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _)) 1294 .Times(1) 1295 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"), 1296 SetArgReferee<3>("123456789ABC"))); 1297 1298 SRC src{entry, ad, dataIface}; 1299 EXPECT_TRUE(src.valid()); 1300 1301 ASSERT_TRUE(src.callouts()); 1302 1303 EXPECT_EQ(src.callouts()->callouts().size(), 1); 1304 1305 auto& callout = src.callouts()->callouts().front(); 1306 1307 EXPECT_EQ(callout->locationCode(), "UTMS-P1"); 1308 EXPECT_EQ(callout->priority(), 'M'); 1309 } 1310