/** * Copyright © 2024 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mock_device.hpp" #include "mock_services.hpp" #include "rail.hpp" #include #include #include #include #include #include #include using namespace phosphor::power::sequencer; using ::testing::Return; using ::testing::Throw; TEST(GPIOTests, Initialization) { // Default initialization { GPIO gpio; EXPECT_EQ(gpio.line, 0); EXPECT_FALSE(gpio.activeLow); } // Explicit initialization { GPIO gpio{48, true}; EXPECT_EQ(gpio.line, 48); EXPECT_TRUE(gpio.activeLow); } } TEST(RailTests, Constructor) { // Test where succeeds: No optional parameters have values { std::string name{"12.0V"}; std::optional presence{}; std::optional page{}; bool isPowerSupplyRail{true}; bool checkStatusVout{false}; bool compareVoltageToLimit{false}; std::optional gpio{}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; EXPECT_EQ(rail.getName(), "12.0V"); EXPECT_FALSE(rail.getPresence().has_value()); EXPECT_FALSE(rail.getPage().has_value()); EXPECT_TRUE(rail.isPowerSupplyRail()); EXPECT_FALSE(rail.getCheckStatusVout()); EXPECT_FALSE(rail.getCompareVoltageToLimit()); EXPECT_FALSE(rail.getGPIO().has_value()); } // Test where succeeds: All optional parameters have values { std::string name{"VCS_CPU1"}; std::optional presence{ "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1"}; std::optional page{11}; bool isPowerSupplyRail{false}; bool checkStatusVout{true}; bool compareVoltageToLimit{true}; std::optional gpio{GPIO(60, true)}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; EXPECT_EQ(rail.getName(), "VCS_CPU1"); EXPECT_TRUE(rail.getPresence().has_value()); EXPECT_EQ( rail.getPresence().value(), "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1"); EXPECT_TRUE(rail.getPage().has_value()); EXPECT_EQ(rail.getPage().value(), 11); EXPECT_FALSE(rail.isPowerSupplyRail()); EXPECT_TRUE(rail.getCheckStatusVout()); EXPECT_TRUE(rail.getCompareVoltageToLimit()); EXPECT_TRUE(rail.getGPIO().has_value()); EXPECT_EQ(rail.getGPIO().value().line, 60); EXPECT_TRUE(rail.getGPIO().value().activeLow); } // Test where fails: checkStatusVout is true and page has no value { std::string name{"VDD1"}; std::optional presence{}; std::optional page{}; bool isPowerSupplyRail{false}; bool checkStatusVout{true}; bool compareVoltageToLimit{false}; std::optional gpio{}; EXPECT_THROW((Rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}), std::invalid_argument); } // Test where fails: compareVoltageToLimit is true and page has no value { std::string name{"VDD1"}; std::optional presence{}; std::optional page{}; bool isPowerSupplyRail{false}; bool checkStatusVout{false}; bool compareVoltageToLimit{true}; std::optional gpio{}; EXPECT_THROW((Rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}), std::invalid_argument); } } TEST(RailTests, GetName) { std::string name{"VDD2"}; std::optional presence{}; std::optional page{}; bool isPowerSupplyRail{false}; bool checkStatusVout{false}; bool compareVoltageToLimit{false}; std::optional gpio{}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; EXPECT_EQ(rail.getName(), "VDD2"); } TEST(RailTests, GetPresence) { std::string name{"VDDR2"}; std::optional page{}; bool isPowerSupplyRail{false}; bool checkStatusVout{false}; bool compareVoltageToLimit{false}; std::optional gpio{}; // Test where presence has no value { std::optional presence{}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; EXPECT_FALSE(rail.getPresence().has_value()); } // Test where presence has a value { std::optional presence{ "/xyz/openbmc_project/inventory/system/chassis/motherboard/dimm2"}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; EXPECT_TRUE(rail.getPresence().has_value()); EXPECT_EQ( rail.getPresence().value(), "/xyz/openbmc_project/inventory/system/chassis/motherboard/dimm2"); } } TEST(RailTests, GetPage) { std::string name{"VDD2"}; std::optional presence{}; bool isPowerSupplyRail{false}; bool checkStatusVout{false}; bool compareVoltageToLimit{false}; std::optional gpio{}; // Test where page has no value { std::optional page{}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; EXPECT_FALSE(rail.getPage().has_value()); } // Test where page has a value { std::optional page{7}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; EXPECT_TRUE(rail.getPage().has_value()); EXPECT_EQ(rail.getPage().value(), 7); } } TEST(RailTests, IsPowerSupplyRail) { std::string name{"12.0V"}; std::optional presence{}; std::optional page{}; bool isPowerSupplyRail{true}; bool checkStatusVout{false}; bool compareVoltageToLimit{false}; std::optional gpio{}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; EXPECT_TRUE(rail.isPowerSupplyRail()); } TEST(RailTests, GetCheckStatusVout) { std::string name{"VDD2"}; std::optional presence{}; std::optional page{}; bool isPowerSupplyRail{false}; bool checkStatusVout{false}; bool compareVoltageToLimit{false}; std::optional gpio{}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; EXPECT_FALSE(rail.getCheckStatusVout()); } TEST(RailTests, GetCompareVoltageToLimit) { std::string name{"VDD2"}; std::optional presence{}; std::optional page{13}; bool isPowerSupplyRail{false}; bool checkStatusVout{false}; bool compareVoltageToLimit{true}; std::optional gpio{}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; EXPECT_TRUE(rail.getCompareVoltageToLimit()); } TEST(RailTests, GetGPIO) { std::string name{"VDD2"}; std::optional presence{}; std::optional page{}; bool isPowerSupplyRail{false}; bool checkStatusVout{false}; bool compareVoltageToLimit{false}; // Test where gpio has no value { std::optional gpio{}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; EXPECT_FALSE(rail.getGPIO().has_value()); } // Test where gpio has a value { std::optional gpio{GPIO(12, false)}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; EXPECT_TRUE(rail.getGPIO().has_value()); EXPECT_EQ(rail.getGPIO().value().line, 12); EXPECT_FALSE(rail.getGPIO().value().activeLow); } } TEST(RailTests, IsPresent) { std::string name{"VDD2"}; std::optional page{}; bool isPowerSupplyRail{false}; bool checkStatusVout{false}; bool compareVoltageToLimit{false}; std::optional gpio{}; // Test where inventory path not specified; always returns true { std::optional presence{}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockServices services{}; EXPECT_CALL(services, isPresent).Times(0); EXPECT_TRUE(rail.isPresent(services)); } // Test where inventory path is not present { std::optional presence{ "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockServices services{}; EXPECT_CALL(services, isPresent(*presence)) .Times(1) .WillOnce(Return(false)); EXPECT_FALSE(rail.isPresent(services)); } // Test where inventory path is present { std::optional presence{ "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockServices services{}; EXPECT_CALL(services, isPresent(*presence)) .Times(1) .WillOnce(Return(true)); EXPECT_TRUE(rail.isPresent(services)); } // Test where exception occurs trying to get presence { std::optional presence{ "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockServices services{}; EXPECT_CALL(services, isPresent(*presence)) .Times(1) .WillOnce(Throw(std::runtime_error{"Invalid object path"})); try { rail.isPresent(services); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::exception& e) { EXPECT_STREQ( e.what(), "Unable to determine presence of rail VDD2 using " "inventory path " "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2: " "Invalid object path"); } } } TEST(RailTests, GetStatusWord) { std::string name{"VDD2"}; std::optional presence{}; bool isPowerSupplyRail{false}; bool checkStatusVout{false}; bool compareVoltageToLimit{false}; std::optional gpio{}; // Test where page was not specified: Throws exception { std::optional page{}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getStatusWord).Times(0); try { rail.getStatusWord(device); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::exception& e) { EXPECT_STREQ(e.what(), "Unable to read STATUS_WORD value for rail VDD2: " "No PAGE number defined for rail VDD2"); } } // Test where value read successfully { std::optional page{2}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef)); EXPECT_EQ(rail.getStatusWord(device), 0xbeef); } // Test where exception occurs trying to read value { std::optional page{2}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getStatusWord(2)) .Times(1) .WillOnce(Throw(std::runtime_error{"File does not exist"})); try { rail.getStatusWord(device); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::exception& e) { EXPECT_STREQ(e.what(), "Unable to read STATUS_WORD value for rail VDD2: " "File does not exist"); } } } TEST(RailTests, GetStatusVout) { std::string name{"VDD2"}; std::optional presence{}; bool isPowerSupplyRail{false}; bool checkStatusVout{false}; bool compareVoltageToLimit{false}; std::optional gpio{}; // Test where page was not specified: Throws exception { std::optional page{}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getStatusVout).Times(0); try { rail.getStatusVout(device); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::exception& e) { EXPECT_STREQ(e.what(), "Unable to read STATUS_VOUT value for rail VDD2: " "No PAGE number defined for rail VDD2"); } } // Test where value read successfully { std::optional page{2}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0xad)); EXPECT_EQ(rail.getStatusVout(device), 0xad); } // Test where exception occurs trying to read value { std::optional page{2}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getStatusVout(2)) .Times(1) .WillOnce(Throw(std::runtime_error{"File does not exist"})); try { rail.getStatusVout(device); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::exception& e) { EXPECT_STREQ(e.what(), "Unable to read STATUS_VOUT value for rail VDD2: " "File does not exist"); } } } TEST(RailTests, GetReadVout) { std::string name{"VDD2"}; std::optional presence{}; bool isPowerSupplyRail{false}; bool checkStatusVout{false}; bool compareVoltageToLimit{false}; std::optional gpio{}; // Test where page was not specified: Throws exception { std::optional page{}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getReadVout).Times(0); try { rail.getReadVout(device); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::exception& e) { EXPECT_STREQ(e.what(), "Unable to read READ_VOUT value for rail VDD2: " "No PAGE number defined for rail VDD2"); } } // Test where value read successfully { std::optional page{2}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.23)); EXPECT_EQ(rail.getReadVout(device), 1.23); } // Test where exception occurs trying to read value { std::optional page{2}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getReadVout(2)) .Times(1) .WillOnce(Throw(std::runtime_error{"File does not exist"})); try { rail.getReadVout(device); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::exception& e) { EXPECT_STREQ(e.what(), "Unable to read READ_VOUT value for rail VDD2: " "File does not exist"); } } } TEST(RailTests, GetVoutUVFaultLimit) { std::string name{"VDD2"}; std::optional presence{}; bool isPowerSupplyRail{false}; bool checkStatusVout{false}; bool compareVoltageToLimit{false}; std::optional gpio{}; // Test where page was not specified: Throws exception { std::optional page{}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getVoutUVFaultLimit).Times(0); try { rail.getVoutUVFaultLimit(device); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::exception& e) { EXPECT_STREQ( e.what(), "Unable to read VOUT_UV_FAULT_LIMIT value for rail VDD2: " "No PAGE number defined for rail VDD2"); } } // Test where value read successfully { std::optional page{2}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getVoutUVFaultLimit(2)) .Times(1) .WillOnce(Return(0.9)); EXPECT_EQ(rail.getVoutUVFaultLimit(device), 0.9); } // Test where exception occurs trying to read value { std::optional page{2}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getVoutUVFaultLimit(2)) .Times(1) .WillOnce(Throw(std::runtime_error{"File does not exist"})); try { rail.getVoutUVFaultLimit(device); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::exception& e) { EXPECT_STREQ( e.what(), "Unable to read VOUT_UV_FAULT_LIMIT value for rail VDD2: " "File does not exist"); } } } TEST(RailTests, HasPgoodFault) { std::string name{"VDD2"}; std::optional presence{}; std::optional page{2}; bool isPowerSupplyRail{false}; bool checkStatusVout{true}; bool compareVoltageToLimit{true}; bool activeLow{true}; std::optional gpio{GPIO(3, activeLow)}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; // No fault detected { MockDevice device{}; EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x00)); EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1)); EXPECT_CALL(device, getVoutUVFaultLimit(2)) .Times(1) .WillOnce(Return(1.0)); MockServices services{}; std::vector gpioValues{0, 0, 0, 0, 0, 0}; std::map additionalData{}; EXPECT_FALSE( rail.hasPgoodFault(device, services, gpioValues, additionalData)); EXPECT_EQ(additionalData.size(), 0); } // Fault detected via STATUS_VOUT { MockDevice device{}; EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x10)); EXPECT_CALL(device, getReadVout(2)).Times(0); EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef)); MockServices services{}; EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef")) .Times(1); EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2")) .Times(1); EXPECT_CALL( services, logErrorMsg("Rail VDD2 has fault bits set in STATUS_VOUT: 0x10")) .Times(1); std::vector gpioValues{0, 0, 0, 0, 0, 0}; std::map additionalData{}; EXPECT_TRUE( rail.hasPgoodFault(device, services, gpioValues, additionalData)); EXPECT_EQ(additionalData.size(), 3); EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2"); EXPECT_EQ(additionalData["STATUS_VOUT"], "0x10"); EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); } // Fault detected via GPIO { MockDevice device{}; EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x00)); EXPECT_CALL(device, getReadVout(2)).Times(0); EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef)); MockServices services{}; EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef")) .Times(1); EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2")) .Times(1); EXPECT_CALL( services, logErrorMsg( "Rail VDD2 pgood GPIO line offset 3 has inactive value 1")) .Times(1); std::vector gpioValues{0, 0, 0, 1, 0, 0}; std::map additionalData{}; EXPECT_TRUE( rail.hasPgoodFault(device, services, gpioValues, additionalData)); EXPECT_EQ(additionalData.size(), 4); EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2"); EXPECT_EQ(additionalData["GPIO_LINE"], "3"); EXPECT_EQ(additionalData["GPIO_VALUE"], "1"); EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); } // Fault detected via output voltage { MockDevice device{}; EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x00)); EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1)); EXPECT_CALL(device, getVoutUVFaultLimit(2)) .Times(1) .WillOnce(Return(1.1)); EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef)); MockServices services{}; EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef")) .Times(1); EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2")) .Times(1); EXPECT_CALL( services, logErrorMsg( "Rail VDD2 output voltage 1.1V is <= UV fault limit 1.1V")) .Times(1); std::vector gpioValues{0, 0, 0, 0, 0, 0}; std::map additionalData{}; EXPECT_TRUE( rail.hasPgoodFault(device, services, gpioValues, additionalData)); EXPECT_EQ(additionalData.size(), 4); EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2"); EXPECT_EQ(additionalData["READ_VOUT"], "1.1"); EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.1"); EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); } } TEST(RailTests, HasPgoodFaultStatusVout) { std::string name{"VDD2"}; std::optional page{3}; bool isPowerSupplyRail{false}; bool compareVoltageToLimit{false}; std::optional gpio{}; // Test where presence check defined: Rail is not present { std::optional presence{ "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"}; bool checkStatusVout{true}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getStatusVout(3)).Times(0); MockServices services{}; EXPECT_CALL(services, isPresent(*presence)) .Times(1) .WillOnce(Return(false)); std::map additionalData{}; EXPECT_FALSE( rail.hasPgoodFaultStatusVout(device, services, additionalData)); EXPECT_EQ(additionalData.size(), 0); } // Test where presence check defined: Rail is present: No fault detected { std::optional presence{ "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"}; bool checkStatusVout{true}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00)); MockServices services{}; EXPECT_CALL(services, isPresent(*presence)) .Times(1) .WillOnce(Return(true)); std::map additionalData{}; EXPECT_FALSE( rail.hasPgoodFaultStatusVout(device, services, additionalData)); EXPECT_EQ(additionalData.size(), 0); } // Test where STATUS_VOUT check is not defined { std::optional presence{}; bool checkStatusVout{false}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getStatusVout(3)).Times(0); MockServices services{}; std::map additionalData{}; EXPECT_FALSE( rail.hasPgoodFaultStatusVout(device, services, additionalData)); EXPECT_EQ(additionalData.size(), 0); } // Test where no fault detected: No warning bits set { std::optional presence{}; bool checkStatusVout{true}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00)); MockServices services{}; EXPECT_CALL(services, logInfoMsg).Times(0); std::map additionalData{}; EXPECT_FALSE( rail.hasPgoodFaultStatusVout(device, services, additionalData)); EXPECT_EQ(additionalData.size(), 0); } // Test where no fault detected: Warning bits set { std::optional presence{}; bool checkStatusVout{true}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x6a)); MockServices services{}; EXPECT_CALL( services, logInfoMsg("Rail VDD2 has warning bits set in STATUS_VOUT: 0x6a")) .Times(1); std::map additionalData{}; EXPECT_FALSE( rail.hasPgoodFaultStatusVout(device, services, additionalData)); EXPECT_EQ(additionalData.size(), 0); } // Test where fault detected // STATUS_WORD captured in additional data { std::optional presence{}; bool checkStatusVout{true}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x10)); EXPECT_CALL(device, getStatusWord(3)).Times(1).WillOnce(Return(0xbeef)); MockServices services{}; EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef")) .Times(1); EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2")) .Times(1); EXPECT_CALL( services, logErrorMsg("Rail VDD2 has fault bits set in STATUS_VOUT: 0x10")) .Times(1); std::map additionalData{}; EXPECT_TRUE( rail.hasPgoodFaultStatusVout(device, services, additionalData)); EXPECT_EQ(additionalData.size(), 3); EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2"); EXPECT_EQ(additionalData["STATUS_VOUT"], "0x10"); EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); } // Test where exception thrown { std::optional presence{}; bool checkStatusVout{true}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getStatusVout(3)) .Times(1) .WillOnce(Throw(std::runtime_error{"File does not exist"})); MockServices services{}; std::map additionalData{}; try { rail.hasPgoodFaultStatusVout(device, services, additionalData); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::exception& e) { EXPECT_STREQ(e.what(), "Unable to read STATUS_VOUT value for rail VDD2: " "File does not exist"); } } } TEST(RailTests, HasPgoodFaultGPIO) { std::string name{"VDD2"}; bool isPowerSupplyRail{false}; bool checkStatusVout{false}; bool compareVoltageToLimit{false}; // Test where presence check defined: Rail is not present { std::optional presence{ "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"}; std::optional page{3}; bool activeLow{false}; std::optional gpio{GPIO(3, activeLow)}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; MockServices services{}; EXPECT_CALL(services, isPresent(*presence)) .Times(1) .WillOnce(Return(false)); std::vector gpioValues{1, 1, 1, 0, 1, 1}; std::map additionalData{}; EXPECT_FALSE(rail.hasPgoodFaultGPIO(device, services, gpioValues, additionalData)); EXPECT_EQ(additionalData.size(), 0); } // Test where presence check defined: Rail is present: No fault detected { std::optional presence{ "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"}; std::optional page{3}; bool activeLow{false}; std::optional gpio{GPIO(3, activeLow)}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; MockServices services{}; EXPECT_CALL(services, isPresent(*presence)) .Times(1) .WillOnce(Return(true)); std::vector gpioValues{1, 1, 1, 1, 1, 1}; std::map additionalData{}; EXPECT_FALSE(rail.hasPgoodFaultGPIO(device, services, gpioValues, additionalData)); EXPECT_EQ(additionalData.size(), 0); } // Test where GPIO check not defined { std::optional presence{}; std::optional page{3}; std::optional gpio{}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; MockServices services{}; std::vector gpioValues{0, 0, 0, 0, 0, 0}; std::map additionalData{}; EXPECT_FALSE(rail.hasPgoodFaultGPIO(device, services, gpioValues, additionalData)); EXPECT_EQ(additionalData.size(), 0); } // Test where no fault detected // GPIO value is 1 and GPIO is active high { std::optional presence{}; std::optional page{}; bool activeLow{false}; std::optional gpio{GPIO(3, activeLow)}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; MockServices services{}; std::vector gpioValues{1, 1, 1, 1, 1, 1}; std::map additionalData{}; EXPECT_FALSE(rail.hasPgoodFaultGPIO(device, services, gpioValues, additionalData)); EXPECT_EQ(additionalData.size(), 0); } // Test where fault detected // GPIO value is 0 and GPIO is active high // STATUS_WORD not captured since no PMBus page defined { std::optional presence{}; std::optional page{}; bool activeLow{false}; std::optional gpio{GPIO(3, activeLow)}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; MockServices services{}; EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2")) .Times(1); EXPECT_CALL( services, logErrorMsg( "Rail VDD2 pgood GPIO line offset 3 has inactive value 0")) .Times(1); std::vector gpioValues{1, 1, 1, 0, 1, 1}; std::map additionalData{}; EXPECT_TRUE(rail.hasPgoodFaultGPIO(device, services, gpioValues, additionalData)); EXPECT_EQ(additionalData.size(), 3); EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2"); EXPECT_EQ(additionalData["GPIO_LINE"], "3"); EXPECT_EQ(additionalData["GPIO_VALUE"], "0"); } // Test where fault detected // GPIO value is 1 and GPIO is active low { std::optional presence{}; std::optional page{2}; bool activeLow{true}; std::optional gpio{GPIO(3, activeLow)}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef)); MockServices services{}; EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef")) .Times(1); EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2")) .Times(1); EXPECT_CALL( services, logErrorMsg( "Rail VDD2 pgood GPIO line offset 3 has inactive value 1")) .Times(1); std::vector gpioValues{0, 0, 0, 1, 0, 0}; std::map additionalData{}; EXPECT_TRUE(rail.hasPgoodFaultGPIO(device, services, gpioValues, additionalData)); EXPECT_EQ(additionalData.size(), 4); EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2"); EXPECT_EQ(additionalData["GPIO_LINE"], "3"); EXPECT_EQ(additionalData["GPIO_VALUE"], "1"); EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); } // Test where exception thrown { std::optional presence{}; std::optional page{}; bool activeLow{false}; std::optional gpio{GPIO(6, activeLow)}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; MockServices services{}; std::vector gpioValues{1, 1, 1, 1, 1, 1}; std::map additionalData{}; try { rail.hasPgoodFaultGPIO(device, services, gpioValues, additionalData); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::exception& e) { EXPECT_STREQ(e.what(), "Invalid GPIO line offset 6 for rail VDD2: " "Device only has 6 GPIO values"); } } } TEST(RailTests, HasPgoodFaultOutputVoltage) { std::string name{"VDD2"}; std::optional page{2}; bool isPowerSupplyRail{false}; bool checkStatusVout{false}; std::optional gpio{}; // Test where presence check defined: Rail is not present { std::optional presence{ "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"}; bool compareVoltageToLimit{true}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getReadVout(2)).Times(0); EXPECT_CALL(device, getVoutUVFaultLimit(2)).Times(0); MockServices services{}; EXPECT_CALL(services, isPresent(*presence)) .Times(1) .WillOnce(Return(false)); std::map additionalData{}; EXPECT_FALSE( rail.hasPgoodFaultOutputVoltage(device, services, additionalData)); EXPECT_EQ(additionalData.size(), 0); } // Test where presence check defined: Rail is present: No fault detected { std::optional presence{ "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"}; bool compareVoltageToLimit{true}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1)); EXPECT_CALL(device, getVoutUVFaultLimit(2)) .Times(1) .WillOnce(Return(1.0)); MockServices services{}; EXPECT_CALL(services, isPresent(*presence)) .Times(1) .WillOnce(Return(true)); std::map additionalData{}; EXPECT_FALSE( rail.hasPgoodFaultOutputVoltage(device, services, additionalData)); EXPECT_EQ(additionalData.size(), 0); } // Test where voltage output check not specified { std::optional presence{}; bool compareVoltageToLimit{false}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getReadVout(2)).Times(0); EXPECT_CALL(device, getVoutUVFaultLimit(2)).Times(0); MockServices services{}; std::map additionalData{}; EXPECT_FALSE( rail.hasPgoodFaultOutputVoltage(device, services, additionalData)); EXPECT_EQ(additionalData.size(), 0); } // Test where no fault detected: Output voltage > UV limit { std::optional presence{}; bool compareVoltageToLimit{true}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1)); EXPECT_CALL(device, getVoutUVFaultLimit(2)) .Times(1) .WillOnce(Return(1.0)); MockServices services{}; std::map additionalData{}; EXPECT_FALSE( rail.hasPgoodFaultOutputVoltage(device, services, additionalData)); EXPECT_EQ(additionalData.size(), 0); } // Test where fault detected: Output voltage < UV limit { std::optional presence{}; bool compareVoltageToLimit{true}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1)); EXPECT_CALL(device, getVoutUVFaultLimit(2)) .Times(1) .WillOnce(Return(1.2)); EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef)); MockServices services{}; EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef")) .Times(1); EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2")) .Times(1); EXPECT_CALL( services, logErrorMsg( "Rail VDD2 output voltage 1.1V is <= UV fault limit 1.2V")) .Times(1); std::map additionalData{}; EXPECT_TRUE( rail.hasPgoodFaultOutputVoltage(device, services, additionalData)); EXPECT_EQ(additionalData.size(), 4); EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2"); EXPECT_EQ(additionalData["READ_VOUT"], "1.1"); EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2"); EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); } // Test where fault detected: Output voltage == UV limit // STATUS_WORD not captured because reading it caused an exception { std::optional presence{}; bool compareVoltageToLimit{true}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1)); EXPECT_CALL(device, getVoutUVFaultLimit(2)) .Times(1) .WillOnce(Return(1.1)); EXPECT_CALL(device, getStatusWord(2)) .Times(1) .WillOnce(Throw(std::runtime_error{"File does not exist"})); MockServices services{}; EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2")) .Times(1); EXPECT_CALL( services, logErrorMsg( "Rail VDD2 output voltage 1.1V is <= UV fault limit 1.1V")) .Times(1); std::map additionalData{}; EXPECT_TRUE( rail.hasPgoodFaultOutputVoltage(device, services, additionalData)); EXPECT_EQ(additionalData.size(), 3); EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2"); EXPECT_EQ(additionalData["READ_VOUT"], "1.1"); EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.1"); } // Test where exception thrown { std::optional presence{}; bool compareVoltageToLimit{true}; Rail rail{name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio}; MockDevice device{}; EXPECT_CALL(device, getReadVout(2)) .Times(1) .WillOnce(Throw(std::runtime_error{"File does not exist"})); MockServices services{}; std::map additionalData{}; try { rail.hasPgoodFaultOutputVoltage(device, services, additionalData); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::exception& e) { EXPECT_STREQ(e.what(), "Unable to read READ_VOUT value for rail VDD2: " "File does not exist"); } } }