|  | /** | 
|  | * 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_services.hpp" | 
|  | #include "rail.hpp" | 
|  | #include "services.hpp" | 
|  | #include "standard_device.hpp" | 
|  |  | 
|  | #include <cstdint> | 
|  | #include <map> | 
|  | #include <memory> | 
|  | #include <optional> | 
|  | #include <string> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include <gmock/gmock.h> | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | using namespace phosphor::power::sequencer; | 
|  |  | 
|  | using ::testing::Return; | 
|  | using ::testing::Throw; | 
|  |  | 
|  | /** | 
|  | * @class StandardDeviceImpl | 
|  | * | 
|  | * Concrete subclass of the StandardDevice abstract class. | 
|  | * | 
|  | * This subclass is required for two reasons: | 
|  | * - StandardDevice has some pure virtual methods so it cannot be instantiated. | 
|  | * - The pure virtual methods provide the PMBus and GPIO information.  Mocking | 
|  | *   these makes it possible to test the pgood fault detection algorithm. | 
|  | * | 
|  | * This class is not intended to be used outside of this file.  It is | 
|  | * implementation detail for testing the the StandardDevice class. | 
|  | */ | 
|  | class StandardDeviceImpl : public StandardDevice | 
|  | { | 
|  | public: | 
|  | // Specify which compiler-generated methods we want | 
|  | StandardDeviceImpl() = delete; | 
|  | StandardDeviceImpl(const StandardDeviceImpl&) = delete; | 
|  | StandardDeviceImpl(StandardDeviceImpl&&) = delete; | 
|  | StandardDeviceImpl& operator=(const StandardDeviceImpl&) = delete; | 
|  | StandardDeviceImpl& operator=(StandardDeviceImpl&&) = delete; | 
|  | virtual ~StandardDeviceImpl() = default; | 
|  |  | 
|  | // Constructor just calls StandardDevice constructor | 
|  | explicit StandardDeviceImpl(const std::string& name, | 
|  | std::vector<std::unique_ptr<Rail>> rails) : | 
|  | StandardDevice(name, std::move(rails)) | 
|  | {} | 
|  |  | 
|  | // Mock pure virtual methods | 
|  | MOCK_METHOD(std::vector<int>, getGPIOValues, (Services & services), | 
|  | (override)); | 
|  | MOCK_METHOD(uint16_t, getStatusWord, (uint8_t page), (override)); | 
|  | MOCK_METHOD(uint8_t, getStatusVout, (uint8_t page), (override)); | 
|  | MOCK_METHOD(double, getReadVout, (uint8_t page), (override)); | 
|  | MOCK_METHOD(double, getVoutUVFaultLimit, (uint8_t page), (override)); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Creates a Rail object that checks for a pgood fault using STATUS_VOUT. | 
|  | * | 
|  | * @param name Unique name for the rail | 
|  | * @param isPowerSupplyRail Specifies whether the rail is produced by a | 
|  | power supply | 
|  | * @param pageNum PMBus PAGE number of the rail | 
|  | * @return Rail object | 
|  | */ | 
|  | std::unique_ptr<Rail> createRailStatusVout(const std::string& name, | 
|  | bool isPowerSupplyRail, | 
|  | uint8_t pageNum) | 
|  | { | 
|  | std::optional<std::string> presence{}; | 
|  | std::optional<uint8_t> page{pageNum}; | 
|  | bool checkStatusVout{true}; | 
|  | bool compareVoltageToLimit{false}; | 
|  | std::optional<GPIO> gpio{}; | 
|  | return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail, | 
|  | checkStatusVout, compareVoltageToLimit, gpio); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Creates a Rail object that checks for a pgood fault using a GPIO. | 
|  | * | 
|  | * @param name Unique name for the rail | 
|  | * @param isPowerSupplyRail Specifies whether the rail is produced by a | 
|  | power supply | 
|  | * @param gpio GPIO line to read to determine the pgood status of the rail | 
|  | * @return Rail object | 
|  | */ | 
|  | std::unique_ptr<Rail> createRailGPIO(const std::string& name, | 
|  | bool isPowerSupplyRail, | 
|  | unsigned int gpioLine) | 
|  | { | 
|  | std::optional<std::string> presence{}; | 
|  | std::optional<uint8_t> page{}; | 
|  | bool checkStatusVout{false}; | 
|  | bool compareVoltageToLimit{false}; | 
|  | bool activeLow{false}; | 
|  | std::optional<GPIO> gpio{GPIO{gpioLine, activeLow}}; | 
|  | return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail, | 
|  | checkStatusVout, compareVoltageToLimit, gpio); | 
|  | } | 
|  |  | 
|  | TEST(StandardDeviceTests, Constructor) | 
|  | { | 
|  | // Empty vector of rails | 
|  | { | 
|  | std::vector<std::unique_ptr<Rail>> rails{}; | 
|  | StandardDeviceImpl device{"xyz_pseq", std::move(rails)}; | 
|  |  | 
|  | EXPECT_EQ(device.getName(), "xyz_pseq"); | 
|  | EXPECT_TRUE(device.getRails().empty()); | 
|  | } | 
|  |  | 
|  | // Non-empty vector of rails | 
|  | { | 
|  | std::vector<std::unique_ptr<Rail>> rails{}; | 
|  | rails.emplace_back(createRailGPIO("PSU", true, 3)); | 
|  | rails.emplace_back(createRailStatusVout("VDD", false, 5)); | 
|  | rails.emplace_back(createRailStatusVout("VIO", false, 7)); | 
|  | StandardDeviceImpl device{"abc_pseq", std::move(rails)}; | 
|  |  | 
|  | EXPECT_EQ(device.getName(), "abc_pseq"); | 
|  | EXPECT_EQ(device.getRails().size(), 3); | 
|  | EXPECT_EQ(device.getRails()[0]->getName(), "PSU"); | 
|  | EXPECT_EQ(device.getRails()[1]->getName(), "VDD"); | 
|  | EXPECT_EQ(device.getRails()[2]->getName(), "VIO"); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(StandardDeviceTests, GetName) | 
|  | { | 
|  | std::vector<std::unique_ptr<Rail>> rails{}; | 
|  | StandardDeviceImpl device{"xyz_pseq", std::move(rails)}; | 
|  |  | 
|  | EXPECT_EQ(device.getName(), "xyz_pseq"); | 
|  | } | 
|  |  | 
|  | TEST(StandardDeviceTests, GetRails) | 
|  | { | 
|  | // Empty vector of rails | 
|  | { | 
|  | std::vector<std::unique_ptr<Rail>> rails{}; | 
|  | StandardDeviceImpl device{"xyz_pseq", std::move(rails)}; | 
|  |  | 
|  | EXPECT_TRUE(device.getRails().empty()); | 
|  | } | 
|  |  | 
|  | // Non-empty vector of rails | 
|  | { | 
|  | std::vector<std::unique_ptr<Rail>> rails{}; | 
|  | rails.emplace_back(createRailGPIO("PSU", true, 3)); | 
|  | rails.emplace_back(createRailStatusVout("VDD", false, 5)); | 
|  | rails.emplace_back(createRailStatusVout("VIO", false, 7)); | 
|  | StandardDeviceImpl device{"abc_pseq", std::move(rails)}; | 
|  |  | 
|  | EXPECT_EQ(device.getRails().size(), 3); | 
|  | EXPECT_EQ(device.getRails()[0]->getName(), "PSU"); | 
|  | EXPECT_EQ(device.getRails()[1]->getName(), "VDD"); | 
|  | EXPECT_EQ(device.getRails()[2]->getName(), "VIO"); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(StandardDeviceTests, FindPgoodFault) | 
|  | { | 
|  | // No rail has a pgood fault | 
|  | { | 
|  | std::vector<std::unique_ptr<Rail>> rails{}; | 
|  | rails.emplace_back(createRailGPIO("PSU", true, 2)); | 
|  | rails.emplace_back(createRailStatusVout("VDD", false, 5)); | 
|  | rails.emplace_back(createRailStatusVout("VIO", false, 7)); | 
|  | StandardDeviceImpl device{"abc_pseq", std::move(rails)}; | 
|  |  | 
|  | std::vector<int> gpioValues{1, 1, 1}; | 
|  | EXPECT_CALL(device, getGPIOValues) | 
|  | .Times(1) | 
|  | .WillOnce(Return(gpioValues)); | 
|  | EXPECT_CALL(device, getStatusVout(5)).Times(1).WillOnce(Return(0x00)); | 
|  | EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00)); | 
|  |  | 
|  | MockServices services{}; | 
|  |  | 
|  | std::string powerSupplyError{}; | 
|  | std::map<std::string, std::string> additionalData{}; | 
|  | std::string error = device.findPgoodFault(services, powerSupplyError, | 
|  | additionalData); | 
|  | EXPECT_TRUE(error.empty()); | 
|  | EXPECT_EQ(additionalData.size(), 0); | 
|  | } | 
|  |  | 
|  | // First rail has a pgood fault | 
|  | // Is a PSU rail: No PSU error specified | 
|  | { | 
|  | std::vector<std::unique_ptr<Rail>> rails{}; | 
|  | rails.emplace_back(createRailGPIO("PSU", true, 2)); | 
|  | rails.emplace_back(createRailStatusVout("VDD", false, 5)); | 
|  | rails.emplace_back(createRailStatusVout("VIO", false, 7)); | 
|  | StandardDeviceImpl device{"abc_pseq", std::move(rails)}; | 
|  |  | 
|  | std::vector<int> gpioValues{1, 1, 0}; | 
|  | EXPECT_CALL(device, getGPIOValues) | 
|  | .Times(1) | 
|  | .WillOnce(Return(gpioValues)); | 
|  | EXPECT_CALL(device, getStatusVout).Times(0); | 
|  |  | 
|  | MockServices services{}; | 
|  | EXPECT_CALL(services, | 
|  | logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]")) | 
|  | .Times(1); | 
|  | EXPECT_CALL( | 
|  | services, | 
|  | logErrorMsg( | 
|  | "Pgood fault found in rail monitored by device abc_pseq")) | 
|  | .Times(1); | 
|  | EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU")) | 
|  | .Times(1); | 
|  | EXPECT_CALL( | 
|  | services, | 
|  | logErrorMsg( | 
|  | "Rail PSU pgood GPIO line offset 2 has inactive value 0")) | 
|  | .Times(1); | 
|  |  | 
|  | std::string powerSupplyError{}; | 
|  | std::map<std::string, std::string> additionalData{}; | 
|  | std::string error = device.findPgoodFault(services, powerSupplyError, | 
|  | additionalData); | 
|  | EXPECT_EQ(error, | 
|  | "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); | 
|  | EXPECT_EQ(additionalData.size(), 5); | 
|  | EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); | 
|  | EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]"); | 
|  | EXPECT_EQ(additionalData["RAIL_NAME"], "PSU"); | 
|  | EXPECT_EQ(additionalData["GPIO_LINE"], "2"); | 
|  | EXPECT_EQ(additionalData["GPIO_VALUE"], "0"); | 
|  | } | 
|  |  | 
|  | // First rail has a pgood fault | 
|  | // Is a PSU rail: PSU error specified | 
|  | { | 
|  | std::vector<std::unique_ptr<Rail>> rails{}; | 
|  | rails.emplace_back(createRailGPIO("PSU", true, 2)); | 
|  | rails.emplace_back(createRailStatusVout("VDD", false, 5)); | 
|  | rails.emplace_back(createRailStatusVout("VIO", false, 7)); | 
|  | StandardDeviceImpl device{"abc_pseq", std::move(rails)}; | 
|  |  | 
|  | std::vector<int> gpioValues{1, 1, 0}; | 
|  | EXPECT_CALL(device, getGPIOValues) | 
|  | .Times(1) | 
|  | .WillOnce(Return(gpioValues)); | 
|  | EXPECT_CALL(device, getStatusVout).Times(0); | 
|  |  | 
|  | MockServices services{}; | 
|  | EXPECT_CALL(services, | 
|  | logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]")) | 
|  | .Times(1); | 
|  | EXPECT_CALL( | 
|  | services, | 
|  | logErrorMsg( | 
|  | "Pgood fault found in rail monitored by device abc_pseq")) | 
|  | .Times(1); | 
|  | EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail PSU")) | 
|  | .Times(1); | 
|  | EXPECT_CALL( | 
|  | services, | 
|  | logErrorMsg( | 
|  | "Rail PSU pgood GPIO line offset 2 has inactive value 0")) | 
|  | .Times(1); | 
|  |  | 
|  | std::string powerSupplyError{"Undervoltage fault: PSU1"}; | 
|  | std::map<std::string, std::string> additionalData{}; | 
|  | std::string error = device.findPgoodFault(services, powerSupplyError, | 
|  | additionalData); | 
|  | EXPECT_EQ(error, powerSupplyError); | 
|  | EXPECT_EQ(additionalData.size(), 5); | 
|  | EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); | 
|  | EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]"); | 
|  | EXPECT_EQ(additionalData["RAIL_NAME"], "PSU"); | 
|  | EXPECT_EQ(additionalData["GPIO_LINE"], "2"); | 
|  | EXPECT_EQ(additionalData["GPIO_VALUE"], "0"); | 
|  | } | 
|  |  | 
|  | // Middle rail has a pgood fault | 
|  | // Not a PSU rail: PSU error specified | 
|  | { | 
|  | std::vector<std::unique_ptr<Rail>> rails{}; | 
|  | rails.emplace_back(createRailGPIO("PSU", true, 2)); | 
|  | rails.emplace_back(createRailStatusVout("VDD", false, 5)); | 
|  | rails.emplace_back(createRailStatusVout("VIO", false, 7)); | 
|  | StandardDeviceImpl device{"abc_pseq", std::move(rails)}; | 
|  |  | 
|  | std::vector<int> gpioValues{1, 1, 1}; | 
|  | EXPECT_CALL(device, getGPIOValues) | 
|  | .Times(1) | 
|  | .WillOnce(Return(gpioValues)); | 
|  | EXPECT_CALL(device, getStatusVout(5)).Times(1).WillOnce(Return(0x10)); | 
|  | EXPECT_CALL(device, getStatusVout(7)).Times(0); | 
|  | EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef)); | 
|  |  | 
|  | MockServices services{}; | 
|  | EXPECT_CALL(services, | 
|  | logInfoMsg("Device abc_pseq GPIO values: [1, 1, 1]")) | 
|  | .Times(1); | 
|  | EXPECT_CALL( | 
|  | services, | 
|  | logErrorMsg( | 
|  | "Pgood fault found in rail monitored by device abc_pseq")) | 
|  | .Times(1); | 
|  | EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef")) | 
|  | .Times(1); | 
|  | EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD")) | 
|  | .Times(1); | 
|  | EXPECT_CALL( | 
|  | services, | 
|  | logErrorMsg("Rail VDD has fault bits set in STATUS_VOUT: 0x10")) | 
|  | .Times(1); | 
|  |  | 
|  | std::string powerSupplyError{"Undervoltage fault: PSU1"}; | 
|  | std::map<std::string, std::string> additionalData{}; | 
|  | std::string error = device.findPgoodFault(services, powerSupplyError, | 
|  | additionalData); | 
|  | EXPECT_EQ(error, | 
|  | "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); | 
|  | EXPECT_EQ(additionalData.size(), 5); | 
|  | EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); | 
|  | EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 1]"); | 
|  | EXPECT_EQ(additionalData["RAIL_NAME"], "VDD"); | 
|  | EXPECT_EQ(additionalData["STATUS_VOUT"], "0x10"); | 
|  | EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); | 
|  | } | 
|  |  | 
|  | // Last rail has a pgood fault | 
|  | // Device returns 0 GPIO values | 
|  | // Does not halt pgood fault detection because GPIO values not used by rails | 
|  | { | 
|  | std::vector<std::unique_ptr<Rail>> rails{}; | 
|  | rails.emplace_back(createRailStatusVout("PSU", true, 3)); | 
|  | rails.emplace_back(createRailStatusVout("VDD", false, 5)); | 
|  | rails.emplace_back(createRailStatusVout("VIO", false, 7)); | 
|  | StandardDeviceImpl device{"abc_pseq", std::move(rails)}; | 
|  |  | 
|  | std::vector<int> gpioValues{}; | 
|  | EXPECT_CALL(device, getGPIOValues) | 
|  | .Times(1) | 
|  | .WillOnce(Return(gpioValues)); | 
|  | EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00)); | 
|  | EXPECT_CALL(device, getStatusVout(5)).Times(1).WillOnce(Return(0x00)); | 
|  | EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11)); | 
|  | EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef)); | 
|  |  | 
|  | MockServices services{}; | 
|  | EXPECT_CALL( | 
|  | services, | 
|  | logErrorMsg( | 
|  | "Pgood fault found in rail monitored by device abc_pseq")) | 
|  | .Times(1); | 
|  | EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef")) | 
|  | .Times(1); | 
|  | EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO")) | 
|  | .Times(1); | 
|  | EXPECT_CALL( | 
|  | services, | 
|  | logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11")) | 
|  | .Times(1); | 
|  |  | 
|  | std::string powerSupplyError{}; | 
|  | std::map<std::string, std::string> additionalData{}; | 
|  | std::string error = device.findPgoodFault(services, powerSupplyError, | 
|  | additionalData); | 
|  | EXPECT_EQ(error, | 
|  | "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); | 
|  | EXPECT_EQ(additionalData.size(), 4); | 
|  | EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); | 
|  | EXPECT_EQ(additionalData["RAIL_NAME"], "VIO"); | 
|  | EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11"); | 
|  | EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); | 
|  | } | 
|  |  | 
|  | // Last rail has a pgood fault | 
|  | // Exception occurs trying to obtain GPIO values from device | 
|  | // Does not halt pgood fault detection because GPIO values not used by rails | 
|  | { | 
|  | std::vector<std::unique_ptr<Rail>> rails{}; | 
|  | rails.emplace_back(createRailStatusVout("PSU", true, 3)); | 
|  | rails.emplace_back(createRailStatusVout("VDD", false, 5)); | 
|  | rails.emplace_back(createRailStatusVout("VIO", false, 7)); | 
|  | StandardDeviceImpl device{"abc_pseq", std::move(rails)}; | 
|  |  | 
|  | EXPECT_CALL(device, getGPIOValues) | 
|  | .Times(1) | 
|  | .WillOnce(Throw(std::runtime_error{"Unable to acquire GPIO line"})); | 
|  | EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00)); | 
|  | EXPECT_CALL(device, getStatusVout(5)).Times(1).WillOnce(Return(0x00)); | 
|  | EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11)); | 
|  | EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef)); | 
|  |  | 
|  | MockServices services{}; | 
|  | EXPECT_CALL( | 
|  | services, | 
|  | logErrorMsg( | 
|  | "Pgood fault found in rail monitored by device abc_pseq")) | 
|  | .Times(1); | 
|  | EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef")) | 
|  | .Times(1); | 
|  | EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO")) | 
|  | .Times(1); | 
|  | EXPECT_CALL( | 
|  | services, | 
|  | logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11")) | 
|  | .Times(1); | 
|  |  | 
|  | std::string powerSupplyError{}; | 
|  | std::map<std::string, std::string> additionalData{}; | 
|  | std::string error = device.findPgoodFault(services, powerSupplyError, | 
|  | additionalData); | 
|  | EXPECT_EQ(error, | 
|  | "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault"); | 
|  | EXPECT_EQ(additionalData.size(), 4); | 
|  | EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq"); | 
|  | EXPECT_EQ(additionalData["RAIL_NAME"], "VIO"); | 
|  | EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11"); | 
|  | EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef"); | 
|  | } | 
|  |  | 
|  | // Exception is thrown during pgood fault detection | 
|  | { | 
|  | std::vector<std::unique_ptr<Rail>> rails{}; | 
|  | rails.emplace_back(createRailGPIO("PSU", true, 2)); | 
|  | rails.emplace_back(createRailStatusVout("VDD", false, 5)); | 
|  | rails.emplace_back(createRailStatusVout("VIO", false, 7)); | 
|  | StandardDeviceImpl device{"abc_pseq", std::move(rails)}; | 
|  |  | 
|  | std::vector<int> gpioValues{1, 1, 1}; | 
|  | EXPECT_CALL(device, getGPIOValues) | 
|  | .Times(1) | 
|  | .WillOnce(Return(gpioValues)); | 
|  | EXPECT_CALL(device, getStatusVout(5)) | 
|  | .Times(1) | 
|  | .WillOnce(Throw(std::runtime_error{"File does not exist"})); | 
|  | EXPECT_CALL(device, getStatusVout(7)).Times(0); | 
|  |  | 
|  | MockServices services{}; | 
|  |  | 
|  | std::string powerSupplyError{}; | 
|  | std::map<std::string, std::string> additionalData{}; | 
|  | try | 
|  | { | 
|  | device.findPgoodFault(services, powerSupplyError, additionalData); | 
|  | ADD_FAILURE() << "Should not have reached this line."; | 
|  | } | 
|  | catch (const std::exception& e) | 
|  | { | 
|  | EXPECT_STREQ( | 
|  | e.what(), | 
|  | "Unable to determine if a pgood fault occurred in device abc_pseq: " | 
|  | "Unable to read STATUS_VOUT value for rail VDD: " | 
|  | "File does not exist"); | 
|  | } | 
|  | } | 
|  | } |