1 /**
2  * Copyright © 2024 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 
17 #include "mock_pmbus.hpp"
18 #include "mock_services.hpp"
19 #include "pmbus.hpp"
20 #include "rail.hpp"
21 #include "services.hpp"
22 #include "ucd90x_device.hpp"
23 
24 #include <cstdint>
25 #include <exception>
26 #include <map>
27 #include <memory>
28 #include <optional>
29 #include <string>
30 #include <utility>
31 #include <vector>
32 
33 #include <gmock/gmock.h>
34 #include <gtest/gtest.h>
35 
36 using namespace phosphor::power::sequencer;
37 using namespace phosphor::pmbus;
38 
39 using ::testing::Return;
40 using ::testing::Throw;
41 
42 /**
43  * Creates a Rail object that checks for a pgood fault using a GPIO.
44  *
45  * @param name Unique name for the rail
46  * @param gpio GPIO line to read to determine the pgood status of the rail
47  * @return Rail object
48  */
49 static std::unique_ptr<Rail> createRail(const std::string& name,
50                                         unsigned int gpioLine)
51 {
52     std::optional<std::string> presence{};
53     std::optional<uint8_t> page{};
54     bool isPowerSupplyRail{false};
55     bool checkStatusVout{false};
56     bool compareVoltageToLimit{false};
57     bool activeLow{false};
58     std::optional<GPIO> gpio{GPIO{gpioLine, activeLow}};
59     return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
60                                   checkStatusVout, compareVoltageToLimit, gpio);
61 }
62 
63 TEST(UCD90xDeviceTests, Constructor)
64 {
65     MockServices services;
66 
67     std::string name{"ucd90320"};
68     std::vector<std::unique_ptr<Rail>> rails;
69     rails.emplace_back(createRail("VDD", 5));
70     rails.emplace_back(createRail("VIO", 7));
71     uint8_t bus{3};
72     uint16_t address{0x72};
73     UCD90xDevice device{name, std::move(rails), services, bus, address};
74 
75     EXPECT_EQ(device.getName(), name);
76     EXPECT_EQ(device.getRails().size(), 2);
77     EXPECT_EQ(device.getRails()[0]->getName(), "VDD");
78     EXPECT_EQ(device.getRails()[1]->getName(), "VIO");
79     EXPECT_EQ(device.getBus(), bus);
80     EXPECT_EQ(device.getAddress(), address);
81     EXPECT_EQ(device.getDriverName(), "ucd9000");
82     EXPECT_EQ(device.getInstance(), 0);
83     EXPECT_NE(&(device.getPMBusInterface()), nullptr);
84 }
85 
86 TEST(UCD90xDeviceTests, GetMfrStatus)
87 {
88     // Test where works
89     {
90         MockServices services;
91 
92         std::string name{"ucd90320"};
93         std::vector<std::unique_ptr<Rail>> rails;
94         uint8_t bus{3};
95         uint16_t address{0x72};
96         UCD90xDevice device{name, std::move(rails), services, bus, address};
97 
98         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
99         uint64_t mfrStatus{0x123456789abcull};
100         EXPECT_CALL(pmbus, read("mfr_status", Type::HwmonDeviceDebug, true))
101             .Times(1)
102             .WillOnce(Return(mfrStatus));
103 
104         EXPECT_EQ(device.getMfrStatus(), mfrStatus);
105     }
106 
107     // Test where fails with exception
108     {
109         MockServices services;
110 
111         std::string name{"ucd90320"};
112         std::vector<std::unique_ptr<Rail>> rails;
113         uint8_t bus{3};
114         uint16_t address{0x72};
115         UCD90xDevice device{name, std::move(rails), services, bus, address};
116 
117         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
118         EXPECT_CALL(pmbus, read("mfr_status", Type::HwmonDeviceDebug, true))
119             .Times(1)
120             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
121 
122         try
123         {
124             device.getMfrStatus();
125             ADD_FAILURE() << "Should not have reached this line.";
126         }
127         catch (const std::exception& e)
128         {
129             EXPECT_STREQ(e.what(),
130                          "Unable to read MFR_STATUS for device ucd90320: "
131                          "File does not exist");
132         }
133     }
134 }
135 
136 TEST(UCD90xDeviceTests, StorePgoodFaultDebugData)
137 {
138     // This is a protected method and cannot be called directly from a gtest.
139     // Call findPgoodFault() which calls storePgoodFaultDebugData().
140 
141     // Test where works
142     {
143         MockServices services;
144         std::vector<int> gpioValues{1, 1, 0};
145         EXPECT_CALL(services, getGPIOValues("ucd90320"))
146             .Times(1)
147             .WillOnce(Return(gpioValues));
148         EXPECT_CALL(services,
149                     logInfoMsg("Device ucd90320 GPIO values: [1, 1, 0]"))
150             .Times(1);
151         EXPECT_CALL(services,
152                     logInfoMsg("Device ucd90320 MFR_STATUS: 0x123456789abc"))
153             .Times(1);
154         EXPECT_CALL(
155             services,
156             logErrorMsg(
157                 "Pgood fault found in rail monitored by device ucd90320"))
158             .Times(1);
159         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
160             .Times(1);
161         EXPECT_CALL(
162             services,
163             logErrorMsg(
164                 "Rail VDD pgood GPIO line offset 2 has inactive value 0"))
165             .Times(1);
166 
167         std::string name{"ucd90320"};
168         std::vector<std::unique_ptr<Rail>> rails;
169         rails.emplace_back(createRail("VDD", 2));
170         uint8_t bus{3};
171         uint16_t address{0x72};
172         UCD90xDevice device{name, std::move(rails), services, bus, address};
173 
174         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
175         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
176             .Times(1)
177             .WillOnce(Return("/tmp"));
178         EXPECT_CALL(pmbus, read("mfr_status", Type::HwmonDeviceDebug, true))
179             .Times(1)
180             .WillOnce(Return(0x123456789abcull));
181 
182         // Call findPgoodFault() which calls storePgoodFaultDebugData()
183         std::string powerSupplyError{};
184         std::map<std::string, std::string> additionalData{};
185         std::string error = device.findPgoodFault(services, powerSupplyError,
186                                                   additionalData);
187         EXPECT_EQ(error,
188                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
189         EXPECT_EQ(additionalData.size(), 6);
190         EXPECT_EQ(additionalData["MFR_STATUS"], "0x123456789abc");
191         EXPECT_EQ(additionalData["DEVICE_NAME"], "ucd90320");
192         EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
193         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
194         EXPECT_EQ(additionalData["GPIO_LINE"], "2");
195         EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
196     }
197 
198     // Test where exception thrown trying to get MFR_STATUS
199     {
200         MockServices services;
201         std::vector<int> gpioValues{1, 1, 0};
202         EXPECT_CALL(services, getGPIOValues("ucd90320"))
203             .Times(1)
204             .WillOnce(Return(gpioValues));
205         EXPECT_CALL(services,
206                     logInfoMsg("Device ucd90320 GPIO values: [1, 1, 0]"))
207             .Times(1);
208         EXPECT_CALL(
209             services,
210             logErrorMsg(
211                 "Pgood fault found in rail monitored by device ucd90320"))
212             .Times(1);
213         EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
214             .Times(1);
215         EXPECT_CALL(
216             services,
217             logErrorMsg(
218                 "Rail VDD pgood GPIO line offset 2 has inactive value 0"))
219             .Times(1);
220 
221         std::string name{"ucd90320"};
222         std::vector<std::unique_ptr<Rail>> rails;
223         rails.emplace_back(createRail("VDD", 2));
224         uint8_t bus{3};
225         uint16_t address{0x72};
226         UCD90xDevice device{name, std::move(rails), services, bus, address};
227 
228         MockPMBus& pmbus = static_cast<MockPMBus&>(device.getPMBusInterface());
229         EXPECT_CALL(pmbus, getPath(Type::Hwmon))
230             .Times(1)
231             .WillOnce(Return("/tmp"));
232         EXPECT_CALL(pmbus, read("mfr_status", Type::HwmonDeviceDebug, true))
233             .Times(1)
234             .WillOnce(Throw(std::runtime_error{"File does not exist"}));
235 
236         // Call findPgoodFault() which calls storePgoodFaultDebugData()
237         std::string powerSupplyError{};
238         std::map<std::string, std::string> additionalData{};
239         std::string error = device.findPgoodFault(services, powerSupplyError,
240                                                   additionalData);
241         EXPECT_EQ(error,
242                   "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
243         EXPECT_EQ(additionalData.size(), 5);
244         EXPECT_EQ(additionalData["DEVICE_NAME"], "ucd90320");
245         EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
246         EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
247         EXPECT_EQ(additionalData["GPIO_LINE"], "2");
248         EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
249     }
250 }
251