// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright 2019 IBM Corporation #include "extensions/openpower-pels/callout.hpp" #include "pel_utils.hpp" #include using namespace openpower::pels; using namespace openpower::pels::src; // Unflatten the callout section with all three substructures TEST(CalloutTest, TestUnflattenAllSubstructures) { // The base data. std::vector data{ 0xFF, 0x2F, 'H', 8, // size, flags, priority, LC length 'U', '1', '2', '-', 'P', '1', 0x00, 0x00 // LC }; auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure); auto pceIdentity = srcDataFactory(TestSRCType::pceIdentityStructure); auto mrus = srcDataFactory(TestSRCType::mruStructure); // Add all 3 substructures data.insert(data.end(), fruIdentity.begin(), fruIdentity.end()); data.insert(data.end(), pceIdentity.begin(), pceIdentity.end()); data.insert(data.end(), mrus.begin(), mrus.end()); // The final size data[0] = data.size(); Stream stream{data}; Callout callout{stream}; EXPECT_EQ(callout.flattenedSize(), data.size()); EXPECT_EQ(callout.priority(), 'H'); EXPECT_EQ(callout.locationCode(), "U12-P1"); // Spot check the 3 substructures EXPECT_TRUE(callout.fruIdentity()); EXPECT_EQ(callout.fruIdentity()->getSN(), "123456789ABC"); EXPECT_TRUE(callout.pceIdentity()); EXPECT_EQ(callout.pceIdentity()->enclosureName(), "PCENAME12"); EXPECT_TRUE(callout.mru()); EXPECT_EQ(callout.mru()->mrus().size(), 4); EXPECT_EQ(callout.mru()->mrus().at(3).id, 0x04040404); // Now flatten std::vector newData; Stream newStream{newData}; callout.flatten(newStream); EXPECT_EQ(data, newData); } TEST(CalloutTest, TestUnflattenOneSubstructure) { std::vector data{ 0xFF, 0x28, 'H', 0x08, // size, flags, priority, LC length 'U', '1', '2', '-', 'P', '1', 0x00, 0x00 // LC }; auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure); data.insert(data.end(), fruIdentity.begin(), fruIdentity.end()); // The final size data[0] = data.size(); Stream stream{data}; Callout callout{stream}; EXPECT_EQ(callout.flattenedSize(), data.size()); // Spot check the substructure EXPECT_TRUE(callout.fruIdentity()); EXPECT_EQ(callout.fruIdentity()->getSN(), "123456789ABC"); // Not present EXPECT_FALSE(callout.pceIdentity()); EXPECT_FALSE(callout.mru()); // Now flatten std::vector newData; Stream newStream{newData}; callout.flatten(newStream); EXPECT_EQ(data, newData); } TEST(CalloutTest, TestUnflattenTwoSubstructures) { std::vector data{ 0xFF, 0x2B, 'H', 0x08, // size, flags, priority, LC length 'U', '1', '2', '-', 'P', '1', 0x00, 0x00 // LC }; auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure); auto pceIdentity = srcDataFactory(TestSRCType::pceIdentityStructure); data.insert(data.end(), fruIdentity.begin(), fruIdentity.end()); data.insert(data.end(), pceIdentity.begin(), pceIdentity.end()); // The final size data[0] = data.size(); Stream stream{data}; Callout callout{stream}; EXPECT_EQ(callout.flattenedSize(), data.size()); // Spot check the 2 substructures EXPECT_TRUE(callout.fruIdentity()); EXPECT_EQ(callout.fruIdentity()->getSN(), "123456789ABC"); EXPECT_TRUE(callout.pceIdentity()); EXPECT_EQ(callout.pceIdentity()->enclosureName(), "PCENAME12"); // Not present EXPECT_FALSE(callout.mru()); // Now flatten std::vector newData; Stream newStream{newData}; callout.flatten(newStream); EXPECT_EQ(data, newData); } TEST(CalloutTest, TestNoLocationCode) { std::vector data{ 0xFF, 0x2B, 'H', 0x00 // size, flags, priority, LC length }; auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure); data.insert(data.end(), fruIdentity.begin(), fruIdentity.end()); // The final size data[0] = data.size(); Stream stream{data}; Callout callout{stream}; EXPECT_TRUE(callout.locationCode().empty()); } // Create a callout object by passing in the hardware fields to add TEST(CalloutTest, TestHardwareCallout) { constexpr size_t fruIdentitySize = 28; { Callout callout{CalloutPriority::high, "U99-42.5-P1-C2-E1", "1234567", "ABCD", "123456789ABC"}; // size/flags/pri/locsize fields + // rounded up location code length + // FRUIdentity size size_t size = 4 + 20 + fruIdentitySize; EXPECT_EQ(callout.flags(), Callout::calloutType | Callout::fruIdentIncluded); EXPECT_EQ(callout.flattenedSize(), size); EXPECT_EQ(callout.priority(), 'H'); EXPECT_EQ(callout.locationCode(), "U99-42.5-P1-C2-E1"); EXPECT_EQ(callout.locationCodeSize(), 20); auto& fru = callout.fruIdentity(); EXPECT_EQ(fru->getPN().value(), "1234567"); EXPECT_EQ(fru->getCCIN().value(), "ABCD"); EXPECT_EQ(fru->getSN().value(), "123456789ABC"); } { // A 3B location code, plus null = 4 Callout callout{CalloutPriority::high, "123", "1234567", "ABCD", "123456789ABC"}; size_t size = 4 + 4 + fruIdentitySize; EXPECT_EQ(callout.locationCodeSize(), 4); EXPECT_EQ(callout.flattenedSize(), size); EXPECT_EQ(callout.locationCode(), "123"); } { // A 4B location code, plus null = 5, then pad to 8 Callout callout{CalloutPriority::high, "1234", "1234567", "ABCD", "123456789ABC"}; size_t size = 4 + 8 + fruIdentitySize; EXPECT_EQ(callout.locationCodeSize(), 8); EXPECT_EQ(callout.flattenedSize(), size); EXPECT_EQ(callout.locationCode(), "1234"); } { // A truncated location code (80 is max size, including null) std::string locCode(81, 'L'); Callout callout{CalloutPriority::high, locCode, "1234567", "ABCD", "123456789ABC"}; size_t size = 4 + 80 + fruIdentitySize; EXPECT_EQ(callout.locationCodeSize(), 80); EXPECT_EQ(callout.flattenedSize(), size); // take off 1 to get to 80, and another for the null locCode = locCode.substr(0, locCode.size() - 2); EXPECT_EQ(callout.locationCode(), locCode); } { // A truncated location code by 1 because of the null std::string locCode(80, 'L'); Callout callout{CalloutPriority::high, locCode, "1234567", "ABCD", "123456789ABC"}; size_t size = 4 + 80 + fruIdentitySize; EXPECT_EQ(callout.locationCodeSize(), 80); EXPECT_EQ(callout.flattenedSize(), size); locCode.pop_back(); EXPECT_EQ(callout.locationCode(), locCode); } { // Max size location code std::string locCode(79, 'L'); Callout callout{CalloutPriority::low, locCode, "1234567", "ABCD", "123456789ABC"}; size_t size = 4 + 80 + fruIdentitySize; EXPECT_EQ(callout.locationCodeSize(), 80); EXPECT_EQ(callout.flattenedSize(), size); EXPECT_EQ(callout.locationCode(), locCode); // How about we flatten/unflatten this last one std::vector data; Stream stream{data}; callout.flatten(stream); { Stream newStream{data}; Callout newCallout{newStream}; EXPECT_EQ(newCallout.flags(), Callout::calloutType | Callout::fruIdentIncluded); EXPECT_EQ(newCallout.flattenedSize(), callout.flattenedSize()); EXPECT_EQ(newCallout.priority(), callout.priority()); EXPECT_EQ(newCallout.locationCode(), callout.locationCode()); EXPECT_EQ(newCallout.locationCodeSize(), callout.locationCodeSize()); auto& fru = newCallout.fruIdentity(); EXPECT_EQ(fru->getPN().value(), "1234567"); EXPECT_EQ(fru->getCCIN().value(), "ABCD"); EXPECT_EQ(fru->getSN().value(), "123456789ABC"); } } { // With MRUs std::vector mruList{{'H', 1}, {'H', 2}}; Callout callout{CalloutPriority::high, "U99-P5", "1234567", "ABCD", "123456789ABC", mruList}; EXPECT_EQ(callout.flags(), Callout::calloutType | Callout::fruIdentIncluded | Callout::mruIncluded); EXPECT_EQ(callout.priority(), 'H'); EXPECT_EQ(callout.locationCode(), "U99-P5"); EXPECT_EQ(callout.locationCodeSize(), 8); auto& fru = callout.fruIdentity(); EXPECT_EQ(fru->getPN().value(), "1234567"); EXPECT_EQ(fru->getCCIN().value(), "ABCD"); EXPECT_EQ(fru->getSN().value(), "123456789ABC"); auto& mruSection = callout.mru(); EXPECT_EQ(mruSection->mrus().size(), 2); EXPECT_EQ(mruSection->mrus().at(0).priority, 'H'); EXPECT_EQ(mruSection->mrus().at(0).id, 1); EXPECT_EQ(mruSection->mrus().at(1).priority, 'H'); EXPECT_EQ(mruSection->mrus().at(1).id, 2); } } // Create a callout object by passing in the maintenance procedure to add. TEST(CalloutTest, TestProcedureCallout) { Callout callout{CalloutPriority::medium, "bmc_code"}; // size/flags/pri/locsize fields + FRUIdentity size // No location code. size_t size = 4 + 12; EXPECT_EQ(callout.flags(), Callout::calloutType | Callout::fruIdentIncluded); EXPECT_EQ(callout.flattenedSize(), size); EXPECT_EQ(callout.priority(), 'M'); EXPECT_EQ(callout.locationCode(), ""); EXPECT_EQ(callout.locationCodeSize(), 0); auto& fru = callout.fruIdentity(); EXPECT_EQ(fru->getMaintProc().value(), "BMC0001"); // flatten/unflatten std::vector data; Stream stream{data}; callout.flatten(stream); Stream newStream{data}; Callout newCallout{newStream}; EXPECT_EQ(newCallout.flags(), Callout::calloutType | Callout::fruIdentIncluded); EXPECT_EQ(newCallout.flattenedSize(), callout.flattenedSize()); EXPECT_EQ(newCallout.priority(), callout.priority()); EXPECT_EQ(newCallout.locationCode(), callout.locationCode()); EXPECT_EQ(newCallout.locationCodeSize(), callout.locationCodeSize()); auto& newFRU = newCallout.fruIdentity(); EXPECT_EQ(newFRU->getMaintProc().value(), fru->getMaintProc().value()); // Use raw procedure value Callout rawCallout{CalloutPriority::medium, "BMCXXXX", CalloutValueType::raw}; auto& rawFRU = rawCallout.fruIdentity(); EXPECT_EQ(rawFRU->getMaintProc().value(), "BMCXXXX"); } // Create a callout object by passing in the symbolic FRU to add. TEST(CalloutTest, TestSymbolicFRUCallout) { // symbolic FRU with a location code { Callout callout{CalloutPriority::high, "service_docs", "P1-C3", false}; // size/flags/pri/locsize fields + plus loc + FRUIdentity size size_t size = 4 + 8 + 12; EXPECT_EQ(callout.flags(), Callout::calloutType | Callout::fruIdentIncluded); EXPECT_EQ(callout.flattenedSize(), size); EXPECT_EQ(callout.priority(), 'H'); EXPECT_EQ(callout.locationCode(), "P1-C3"); EXPECT_EQ(callout.locationCodeSize(), 8); auto& fru = callout.fruIdentity(); EXPECT_EQ(fru->failingComponentType(), FRUIdentity::symbolicFRU); EXPECT_EQ(fru->getPN().value(), "SVCDOCS"); } // symbolic FRU without a location code { Callout callout{CalloutPriority::high, "service_docs", "", false}; // size/flags/pri/locsize fields + plus loc + FRUIdentity size size_t size = 4 + 0 + 12; EXPECT_EQ(callout.flags(), Callout::calloutType | Callout::fruIdentIncluded); EXPECT_EQ(callout.flattenedSize(), size); EXPECT_EQ(callout.priority(), 'H'); EXPECT_EQ(callout.locationCode(), ""); EXPECT_EQ(callout.locationCodeSize(), 0); auto& fru = callout.fruIdentity(); EXPECT_EQ(fru->failingComponentType(), FRUIdentity::symbolicFRU); EXPECT_EQ(fru->getPN().value(), "SVCDOCS"); } // symbolic FRU with a trusted location code { Callout callout{CalloutPriority::high, "service_docs", "P1-C3", true}; // size/flags/pri/locsize fields + plus loc + FRUIdentity size size_t size = 4 + 8 + 12; EXPECT_EQ(callout.flags(), Callout::calloutType | Callout::fruIdentIncluded); EXPECT_EQ(callout.flattenedSize(), size); EXPECT_EQ(callout.priority(), 'H'); EXPECT_EQ(callout.locationCode(), "P1-C3"); EXPECT_EQ(callout.locationCodeSize(), 8); auto& fru = callout.fruIdentity(); EXPECT_EQ(fru->failingComponentType(), FRUIdentity::symbolicFRUTrustedLocCode); EXPECT_EQ(fru->getPN().value(), "SVCDOCS"); } // symbolic FRU with raw FRU value { { Callout callout{CalloutPriority::high, "SYMBFRU", CalloutValueType::raw, "", false}; auto& fru = callout.fruIdentity(); EXPECT_EQ(fru->getPN().value(), "SYMBFRU"); } } } TEST(CalloutTest, OperatorEqualTest) { { Callout c1{CalloutPriority::high, "A1", "1234567", "ABCD", "123456789ABC"}; Callout c2{CalloutPriority::high, "A1", "1234567", "ABCD", "123456789ABC"}; EXPECT_EQ(c1, c2); } { // Different location code Callout c1{CalloutPriority::high, "A1", "1234567", "ABCD", "123456789ABC"}; Callout c2{CalloutPriority::high, "A2", "1234567", "ABCD", "123456789ABC"}; EXPECT_NE(c1, c2); } { // maintenance procedure Callout c1{CalloutPriority::medium, "bmc_code"}; Callout c2{CalloutPriority::medium, "bmc_code"}; EXPECT_EQ(c1, c2); } { // different maintenance procedures Callout c1{CalloutPriority::medium, "bmc_code"}; Callout c2{CalloutPriority::medium, "sbe_code"}; EXPECT_NE(c1, c2); } { // symbolic FRU Callout c1{CalloutPriority::high, "service_docs", "", false}; Callout c2{CalloutPriority::high, "service_docs", "", false}; EXPECT_EQ(c1, c2); } { // different symbolic FRUs Callout c1{CalloutPriority::high, "service_docs", "", false}; Callout c2{CalloutPriority::high, "air_mover", "", false}; EXPECT_NE(c1, c2); } { // HW callout vs symbolic FRU Callout c1{CalloutPriority::high, "A1", "1234567", "ABCD", "123456789ABC"}; Callout c2{CalloutPriority::high, "service_docs", "", false}; EXPECT_NE(c1, c2); } { // HW callout vs maintenance procedure Callout c1{CalloutPriority::high, "A1", "1234567", "ABCD", "123456789ABC"}; Callout c2{CalloutPriority::medium, "bmc_code"}; EXPECT_NE(c1, c2); } { // symbolic FRU vs maintenance procedure Callout c1{CalloutPriority::high, "service_docs", "", false}; Callout c2{CalloutPriority::medium, "bmc_code"}; EXPECT_NE(c1, c2); } { // HW callout vs symbolic FRU is still considered equal if // the location code is the same Callout c1{CalloutPriority::high, "A1", "1234567", "ABCD", "123456789ABC"}; Callout c2{CalloutPriority::high, "service_docs", "A1", true}; EXPECT_EQ(c1, c2); } } TEST(CalloutTest, OperatorGreaterThanTest) { { Callout c1{CalloutPriority::high, "bmc_code"}; Callout c2{CalloutPriority::medium, "bmc_code"}; EXPECT_TRUE(c1 > c2); } { Callout c1{CalloutPriority::high, "bmc_code"}; Callout c2{CalloutPriority::low, "bmc_code"}; EXPECT_TRUE(c1 > c2); } { Callout c1{CalloutPriority::medium, "bmc_code"}; Callout c2{CalloutPriority::low, "bmc_code"}; EXPECT_TRUE(c1 > c2); } { Callout c1{CalloutPriority::mediumGroupA, "bmc_code"}; Callout c2{CalloutPriority::low, "bmc_code"}; EXPECT_TRUE(c1 > c2); } { Callout c1{CalloutPriority::medium, "bmc_code"}; Callout c2{CalloutPriority::high, "bmc_code"}; EXPECT_FALSE(c1 > c2); } { Callout c1{CalloutPriority::high, "bmc_code"}; Callout c2{CalloutPriority::high, "bmc_code"}; EXPECT_FALSE(c1 > c2); } { Callout c1{CalloutPriority::low, "bmc_code"}; Callout c2{CalloutPriority::high, "bmc_code"}; EXPECT_FALSE(c1 > c2); } { // Treat the different mediums the same Callout c1{CalloutPriority::medium, "bmc_code"}; Callout c2{CalloutPriority::mediumGroupA, "bmc_code"}; EXPECT_FALSE(c1 > c2); } }