1 /**
2 * Copyright © 2025 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 "basic_device.hpp"
18 #include "mock_gpio.hpp"
19 #include "mock_services.hpp"
20 #include "rail.hpp"
21 #include "services.hpp"
22
23 #include <cstdint>
24 #include <map>
25 #include <memory>
26 #include <optional>
27 #include <string>
28 #include <utility>
29 #include <vector>
30
31 #include <gmock/gmock.h>
32 #include <gtest/gtest.h>
33
34 using namespace phosphor::power::sequencer;
35
36 using ::testing::Return;
37 using ::testing::Throw;
38
39 /**
40 * @class BasicDeviceImpl
41 *
42 * Concrete subclass of the BasicDevice abstract class.
43 *
44 * This subclass is required because BasicDevice does not implement all the pure
45 * virtual methods inherited for PowerSequencerDevice, meaning it cannot be
46 * instantiated.
47 *
48 * This class is not intended to be used outside of this file. It is
49 * implementation detail for testing the BasicDevice class.
50 */
51 class BasicDeviceImpl : public BasicDevice
52 {
53 public:
54 BasicDeviceImpl() = delete;
55 BasicDeviceImpl(const BasicDeviceImpl&) = delete;
56 BasicDeviceImpl(BasicDeviceImpl&&) = delete;
57 BasicDeviceImpl& operator=(const BasicDeviceImpl&) = delete;
58 BasicDeviceImpl& operator=(BasicDeviceImpl&&) = delete;
59 virtual ~BasicDeviceImpl() = default;
60
BasicDeviceImpl(const std::string & name,uint8_t bus,uint16_t address,const std::string & powerControlGPIOName,const std::string & powerGoodGPIOName,std::vector<std::unique_ptr<Rail>> rails,Services & services)61 explicit BasicDeviceImpl(
62 const std::string& name, uint8_t bus, uint16_t address,
63 const std::string& powerControlGPIOName,
64 const std::string& powerGoodGPIOName,
65 std::vector<std::unique_ptr<Rail>> rails, Services& services) :
66 BasicDevice(name, bus, address, powerControlGPIOName, powerGoodGPIOName,
67 std::move(rails), services)
68 {}
69
70 // Mock pure virtual methods
71 MOCK_METHOD(std::vector<int>, getGPIOValues, (Services & services),
72 (override));
73 MOCK_METHOD(uint16_t, getStatusWord, (uint8_t page), (override));
74 MOCK_METHOD(uint8_t, getStatusVout, (uint8_t page), (override));
75 MOCK_METHOD(double, getReadVout, (uint8_t page), (override));
76 MOCK_METHOD(double, getVoutUVFaultLimit, (uint8_t page), (override));
77 MOCK_METHOD(std::string, findPgoodFault,
78 (Services & services, const std::string& powerSupplyError,
79 (std::map<std::string, std::string> & additionalData)),
80 (override));
81 };
82
83 /**
84 * Creates a Rail object that checks for a pgood fault using a GPIO.
85 *
86 * @param name Unique name for the rail
87 * @param gpio GPIO line to read to determine the pgood status of the rail
88 * @return Rail object
89 */
createRail(const std::string & name,unsigned int gpioLine)90 static std::unique_ptr<Rail> createRail(const std::string& name,
91 unsigned int gpioLine)
92 {
93 std::optional<std::string> presence{};
94 std::optional<uint8_t> page{};
95 bool isPowerSupplyRail{false};
96 bool checkStatusVout{false};
97 bool compareVoltageToLimit{false};
98 bool activeLow{false};
99 std::optional<PgoodGPIO> gpio{PgoodGPIO{gpioLine, activeLow}};
100 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
101 checkStatusVout, compareVoltageToLimit, gpio);
102 }
103
TEST(BasicDeviceTests,Constructor)104 TEST(BasicDeviceTests, Constructor)
105 {
106 // Test where works: Empty vector of rails
107 {
108 std::string name{"xyz_pseq"};
109 uint8_t bus{3};
110 uint16_t address{0x72};
111 std::string powerControlGPIOName{"power-chassis-control"};
112 std::string powerGoodGPIOName{"power-chassis-good"};
113 std::vector<std::unique_ptr<Rail>> rails{};
114 MockServices services;
115 BasicDeviceImpl device{
116 name,
117 bus,
118 address,
119 powerControlGPIOName,
120 powerGoodGPIOName,
121 std::move(rails),
122 services};
123
124 EXPECT_EQ(device.getName(), name);
125 EXPECT_EQ(device.getBus(), bus);
126 EXPECT_EQ(device.getAddress(), address);
127 EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
128 EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
129 EXPECT_TRUE(device.getRails().empty());
130 }
131
132 // Test where works: Non-empty vector of rails
133 {
134 std::string name{"abc_pseq"};
135 uint8_t bus{0};
136 uint16_t address{0x23};
137 std::string powerControlGPIOName{"power-chassis-control"};
138 std::string powerGoodGPIOName{"power-chassis-good"};
139 std::vector<std::unique_ptr<Rail>> rails{};
140 rails.emplace_back(createRail("VDD", 5));
141 rails.emplace_back(createRail("VIO", 7));
142 MockServices services;
143 BasicDeviceImpl device{
144 name,
145 bus,
146 address,
147 powerControlGPIOName,
148 powerGoodGPIOName,
149 std::move(rails),
150 services};
151
152 EXPECT_EQ(device.getName(), name);
153 EXPECT_EQ(device.getBus(), bus);
154 EXPECT_EQ(device.getAddress(), address);
155 EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
156 EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
157 EXPECT_EQ(device.getRails().size(), 2);
158 EXPECT_EQ(device.getRails()[0]->getName(), "VDD");
159 EXPECT_EQ(device.getRails()[1]->getName(), "VIO");
160 }
161 }
162
TEST(BasicDeviceTests,GetName)163 TEST(BasicDeviceTests, GetName)
164 {
165 std::string name{"xyz_pseq"};
166 uint8_t bus{0};
167 uint16_t address{0x23};
168 std::string powerControlGPIOName{"power-chassis-control"};
169 std::string powerGoodGPIOName{"power-chassis-good"};
170 std::vector<std::unique_ptr<Rail>> rails{};
171 MockServices services;
172 BasicDeviceImpl device{
173 name,
174 bus,
175 address,
176 powerControlGPIOName,
177 powerGoodGPIOName,
178 std::move(rails),
179 services};
180
181 EXPECT_EQ(device.getName(), name);
182 }
183
TEST(BasicDeviceTests,GetBus)184 TEST(BasicDeviceTests, GetBus)
185 {
186 std::string name{"abc_pseq"};
187 uint8_t bus{1};
188 uint16_t address{0x23};
189 std::string powerControlGPIOName{"power-chassis-control"};
190 std::string powerGoodGPIOName{"power-chassis-good"};
191 std::vector<std::unique_ptr<Rail>> rails{};
192 MockServices services;
193 BasicDeviceImpl device{
194 name,
195 bus,
196 address,
197 powerControlGPIOName,
198 powerGoodGPIOName,
199 std::move(rails),
200 services};
201
202 EXPECT_EQ(device.getBus(), bus);
203 }
204
TEST(BasicDeviceTests,GetAddress)205 TEST(BasicDeviceTests, GetAddress)
206 {
207 std::string name{"abc_pseq"};
208 uint8_t bus{1};
209 uint16_t address{0x24};
210 std::string powerControlGPIOName{"power-chassis-control"};
211 std::string powerGoodGPIOName{"power-chassis-good"};
212 std::vector<std::unique_ptr<Rail>> rails{};
213 MockServices services;
214 BasicDeviceImpl device{
215 name,
216 bus,
217 address,
218 powerControlGPIOName,
219 powerGoodGPIOName,
220 std::move(rails),
221 services};
222
223 EXPECT_EQ(device.getAddress(), address);
224 }
225
TEST(BasicDeviceTests,GetPowerControlGPIOName)226 TEST(BasicDeviceTests, GetPowerControlGPIOName)
227 {
228 std::string name{"xyz_pseq"};
229 uint8_t bus{0};
230 uint16_t address{0x23};
231 std::string powerControlGPIOName{"power-on"};
232 std::string powerGoodGPIOName{"chassis-pgood"};
233 std::vector<std::unique_ptr<Rail>> rails{};
234 MockServices services;
235 BasicDeviceImpl device{
236 name,
237 bus,
238 address,
239 powerControlGPIOName,
240 powerGoodGPIOName,
241 std::move(rails),
242 services};
243
244 EXPECT_EQ(device.getPowerControlGPIOName(), powerControlGPIOName);
245 }
246
TEST(BasicDeviceTests,GetPowerGoodGPIOName)247 TEST(BasicDeviceTests, GetPowerGoodGPIOName)
248 {
249 std::string name{"xyz_pseq"};
250 uint8_t bus{0};
251 uint16_t address{0x23};
252 std::string powerControlGPIOName{"power-on"};
253 std::string powerGoodGPIOName{"chassis-pgood"};
254 std::vector<std::unique_ptr<Rail>> rails{};
255 MockServices services;
256 BasicDeviceImpl device{
257 name,
258 bus,
259 address,
260 powerControlGPIOName,
261 powerGoodGPIOName,
262 std::move(rails),
263 services};
264
265 EXPECT_EQ(device.getPowerGoodGPIOName(), powerGoodGPIOName);
266 }
267
TEST(BasicDeviceTests,GetRails)268 TEST(BasicDeviceTests, GetRails)
269 {
270 // Empty vector of rails
271 {
272 std::string name{"xyz_pseq"};
273 uint8_t bus{0};
274 uint16_t address{0x23};
275 std::string powerControlGPIOName{"power-chassis-control"};
276 std::string powerGoodGPIOName{"power-chassis-good"};
277 std::vector<std::unique_ptr<Rail>> rails{};
278 MockServices services;
279 BasicDeviceImpl device{
280 name,
281 bus,
282 address,
283 powerControlGPIOName,
284 powerGoodGPIOName,
285 std::move(rails),
286 services};
287
288 EXPECT_TRUE(device.getRails().empty());
289 }
290
291 // Non-empty vector of rails
292 {
293 std::string name{"abc_pseq"};
294 uint8_t bus{0};
295 uint16_t address{0x23};
296 std::string powerControlGPIOName{"power-chassis-control"};
297 std::string powerGoodGPIOName{"power-chassis-good"};
298 std::vector<std::unique_ptr<Rail>> rails{};
299 rails.emplace_back(createRail("VDD", 5));
300 rails.emplace_back(createRail("VIO", 7));
301 rails.emplace_back(createRail("VDDR", 9));
302 MockServices services;
303 BasicDeviceImpl device{
304 name,
305 bus,
306 address,
307 powerControlGPIOName,
308 powerGoodGPIOName,
309 std::move(rails),
310 services};
311
312 EXPECT_EQ(device.getRails().size(), 3);
313 EXPECT_EQ(device.getRails()[0]->getName(), "VDD");
314 EXPECT_EQ(device.getRails()[1]->getName(), "VIO");
315 EXPECT_EQ(device.getRails()[2]->getName(), "VDDR");
316 }
317 }
318
TEST(BasicDeviceTests,GetPowerControlGPIO)319 TEST(BasicDeviceTests, GetPowerControlGPIO)
320 {
321 std::string name{"xyz_pseq"};
322 uint8_t bus{0};
323 uint16_t address{0x23};
324 std::string powerControlGPIOName{"power-on"};
325 std::string powerGoodGPIOName{"chassis-pgood"};
326 std::vector<std::unique_ptr<Rail>> rails{};
327 MockServices services;
328 BasicDeviceImpl device{
329 name,
330 bus,
331 address,
332 powerControlGPIOName,
333 powerGoodGPIOName,
334 std::move(rails),
335 services};
336
337 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
338 EXPECT_CALL(gpio, setValue(1)).Times(1);
339 device.powerOn();
340 }
341
TEST(BasicDeviceTests,GetPowerGoodGPIO)342 TEST(BasicDeviceTests, GetPowerGoodGPIO)
343 {
344 std::string name{"xyz_pseq"};
345 uint8_t bus{0};
346 uint16_t address{0x23};
347 std::string powerControlGPIOName{"power-on"};
348 std::string powerGoodGPIOName{"chassis-pgood"};
349 std::vector<std::unique_ptr<Rail>> rails{};
350 MockServices services;
351 BasicDeviceImpl device{
352 name,
353 bus,
354 address,
355 powerControlGPIOName,
356 powerGoodGPIOName,
357 std::move(rails),
358 services};
359
360 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
361 EXPECT_CALL(gpio, getValue()).Times(1).WillOnce(Return(0));
362 EXPECT_FALSE(device.getPowerGood());
363 }
364
TEST(BasicDeviceTests,PowerOn)365 TEST(BasicDeviceTests, PowerOn)
366 {
367 // Test where works
368 {
369 std::string name{"xyz_pseq"};
370 uint8_t bus{0};
371 uint16_t address{0x23};
372 std::string powerControlGPIOName{"power-on"};
373 std::string powerGoodGPIOName{"chassis-pgood"};
374 std::vector<std::unique_ptr<Rail>> rails{};
375 MockServices services;
376 BasicDeviceImpl device{
377 name,
378 bus,
379 address,
380 powerControlGPIOName,
381 powerGoodGPIOName,
382 std::move(rails),
383 services};
384
385 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
386 EXPECT_CALL(gpio, requestWrite(1)).Times(1);
387 EXPECT_CALL(gpio, setValue(1)).Times(1);
388 EXPECT_CALL(gpio, release()).Times(1);
389 device.powerOn();
390 }
391
392 // Test where fails with exception
393 try
394 {
395 std::string name{"xyz_pseq"};
396 uint8_t bus{0};
397 uint16_t address{0x23};
398 std::string powerControlGPIOName{"power-on"};
399 std::string powerGoodGPIOName{"chassis-pgood"};
400 std::vector<std::unique_ptr<Rail>> rails{};
401 MockServices services;
402 BasicDeviceImpl device{
403 name,
404 bus,
405 address,
406 powerControlGPIOName,
407 powerGoodGPIOName,
408 std::move(rails),
409 services};
410
411 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
412 EXPECT_CALL(gpio, requestWrite(1))
413 .Times(1)
414 .WillOnce(Throw(std::runtime_error{"Unable to write GPIO"}));
415 device.powerOn();
416 ADD_FAILURE() << "Should not have reached this line.";
417 }
418 catch (const std::exception& e)
419 {
420 EXPECT_STREQ(e.what(), "Unable to write GPIO");
421 }
422 }
423
TEST(BasicDeviceTests,PowerOff)424 TEST(BasicDeviceTests, PowerOff)
425 {
426 // Test where works
427 {
428 std::string name{"xyz_pseq"};
429 uint8_t bus{0};
430 uint16_t address{0x23};
431 std::string powerControlGPIOName{"power-on"};
432 std::string powerGoodGPIOName{"chassis-pgood"};
433 std::vector<std::unique_ptr<Rail>> rails{};
434 MockServices services;
435 BasicDeviceImpl device{
436 name,
437 bus,
438 address,
439 powerControlGPIOName,
440 powerGoodGPIOName,
441 std::move(rails),
442 services};
443
444 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
445 EXPECT_CALL(gpio, requestWrite(0)).Times(1);
446 EXPECT_CALL(gpio, setValue(0)).Times(1);
447 EXPECT_CALL(gpio, release()).Times(1);
448 device.powerOff();
449 }
450
451 // Test where fails with exception
452 try
453 {
454 std::string name{"xyz_pseq"};
455 uint8_t bus{0};
456 uint16_t address{0x23};
457 std::string powerControlGPIOName{"power-on"};
458 std::string powerGoodGPIOName{"chassis-pgood"};
459 std::vector<std::unique_ptr<Rail>> rails{};
460 MockServices services;
461 BasicDeviceImpl device{
462 name,
463 bus,
464 address,
465 powerControlGPIOName,
466 powerGoodGPIOName,
467 std::move(rails),
468 services};
469
470 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerControlGPIO());
471 EXPECT_CALL(gpio, requestWrite(0)).Times(1);
472 EXPECT_CALL(gpio, setValue(0))
473 .Times(1)
474 .WillOnce(Throw(std::runtime_error{"Unable to write GPIO"}));
475 device.powerOff();
476 ADD_FAILURE() << "Should not have reached this line.";
477 }
478 catch (const std::exception& e)
479 {
480 EXPECT_STREQ(e.what(), "Unable to write GPIO");
481 }
482 }
483
TEST(BasicDeviceTests,GetPowerGood)484 TEST(BasicDeviceTests, GetPowerGood)
485 {
486 // Test where works: Value is false
487 {
488 std::string name{"xyz_pseq"};
489 uint8_t bus{0};
490 uint16_t address{0x23};
491 std::string powerControlGPIOName{"power-on"};
492 std::string powerGoodGPIOName{"chassis-pgood"};
493 std::vector<std::unique_ptr<Rail>> rails{};
494 MockServices services;
495 BasicDeviceImpl device{
496 name,
497 bus,
498 address,
499 powerControlGPIOName,
500 powerGoodGPIOName,
501 std::move(rails),
502 services};
503
504 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
505 EXPECT_CALL(gpio, getValue()).Times(1).WillOnce(Return(0));
506 EXPECT_FALSE(device.getPowerGood());
507 }
508
509 // Test where works: Value is true
510 {
511 std::string name{"xyz_pseq"};
512 uint8_t bus{0};
513 uint16_t address{0x23};
514 std::string powerControlGPIOName{"power-on"};
515 std::string powerGoodGPIOName{"chassis-pgood"};
516 std::vector<std::unique_ptr<Rail>> rails{};
517 MockServices services;
518 BasicDeviceImpl device{
519 name,
520 bus,
521 address,
522 powerControlGPIOName,
523 powerGoodGPIOName,
524 std::move(rails),
525 services};
526
527 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
528 EXPECT_CALL(gpio, getValue()).Times(1).WillOnce(Return(1));
529 EXPECT_TRUE(device.getPowerGood());
530 }
531
532 // Test where fails with exception
533 try
534 {
535 std::string name{"xyz_pseq"};
536 uint8_t bus{0};
537 uint16_t address{0x23};
538 std::string powerControlGPIOName{"power-on"};
539 std::string powerGoodGPIOName{"chassis-pgood"};
540 std::vector<std::unique_ptr<Rail>> rails{};
541 MockServices services;
542 BasicDeviceImpl device{
543 name,
544 bus,
545 address,
546 powerControlGPIOName,
547 powerGoodGPIOName,
548 std::move(rails),
549 services};
550
551 MockGPIO& gpio = static_cast<MockGPIO&>(device.getPowerGoodGPIO());
552 EXPECT_CALL(gpio, getValue())
553 .Times(1)
554 .WillOnce(Throw(std::runtime_error{"Unable to read GPIO"}));
555 device.getPowerGood();
556 ADD_FAILURE() << "Should not have reached this line.";
557 }
558 catch (const std::exception& e)
559 {
560 EXPECT_STREQ(e.what(), "Unable to read GPIO");
561 }
562 }
563