1 /**
2  * Copyright © 2020 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 #include "action_environment.hpp"
17 #include "action_error.hpp"
18 #include "device.hpp"
19 #include "i2c_interface.hpp"
20 #include "id_map.hpp"
21 #include "mock_services.hpp"
22 #include "mocked_i2c_interface.hpp"
23 #include "pmbus_error.hpp"
24 #include "pmbus_utils.hpp"
25 #include "pmbus_write_vout_command_action.hpp"
26 #include "write_verification_error.hpp"
27 
28 #include <cstdint>
29 #include <memory>
30 #include <optional>
31 #include <stdexcept>
32 #include <string>
33 #include <utility>
34 
35 #include <gmock/gmock.h>
36 #include <gtest/gtest.h>
37 
38 using namespace phosphor::power::regulators;
39 
40 using ::testing::A;
41 using ::testing::Return;
42 using ::testing::SetArgReferee;
43 using ::testing::Throw;
44 using ::testing::TypedEq;
45 
TEST(PMBusWriteVoutCommandActionTests,Constructor)46 TEST(PMBusWriteVoutCommandActionTests, Constructor)
47 {
48     // Test where works: Volts value and exponent value are specified
49     try
50     {
51         std::optional<double> volts{1.3};
52         pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
53         std::optional<int8_t> exponent{-8};
54         bool isVerified{true};
55         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
56         EXPECT_EQ(action.getVolts().has_value(), true);
57         EXPECT_EQ(action.getVolts().value(), 1.3);
58         EXPECT_EQ(action.getFormat(), pmbus_utils::VoutDataFormat::linear);
59         EXPECT_EQ(action.getExponent().has_value(), true);
60         EXPECT_EQ(action.getExponent().value(), -8);
61         EXPECT_EQ(action.isVerified(), true);
62     }
63     catch (...)
64     {
65         ADD_FAILURE() << "Should not have caught exception.";
66     }
67 
68     // Test where works: Volts value and exponent value are not specified
69     try
70     {
71         std::optional<double> volts{};
72         pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
73         std::optional<int8_t> exponent{};
74         bool isVerified{false};
75         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
76         EXPECT_EQ(action.getVolts().has_value(), false);
77         EXPECT_EQ(action.getFormat(), pmbus_utils::VoutDataFormat::linear);
78         EXPECT_EQ(action.getExponent().has_value(), false);
79         EXPECT_EQ(action.isVerified(), false);
80     }
81     catch (...)
82     {
83         ADD_FAILURE() << "Should not have caught exception.";
84     }
85 
86     // Test where fails: Data format is not linear
87     try
88     {
89         std::optional<double> volts{};
90         pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::direct};
91         std::optional<int8_t> exponent{};
92         bool isVerified{false};
93         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
94         ADD_FAILURE() << "Should not have reached this line.";
95     }
96     catch (const std::invalid_argument& e)
97     {
98         EXPECT_STREQ(e.what(), "Unsupported data format specified");
99     }
100     catch (...)
101     {
102         ADD_FAILURE() << "Should not have caught exception.";
103     }
104 }
105 
TEST(PMBusWriteVoutCommandActionTests,Execute)106 TEST(PMBusWriteVoutCommandActionTests, Execute)
107 {
108     // Test where works: Volts value and exponent value defined in action;
109     // write is verified.
110     try
111     {
112         // Create mock I2CInterface.  Expect action to do the following:
113         // * will not read from VOUT_MODE (command/register 0x20)
114         // * will write 0x014D to VOUT_COMMAND (command/register 0x21)
115         // * will read 0x014D from VOUT_COMMAND
116         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
117             std::make_unique<i2c::MockedI2CInterface>();
118         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
119         EXPECT_CALL(*i2cInterface, read(A<uint8_t>(), A<uint8_t&>())).Times(0);
120         EXPECT_CALL(*i2cInterface,
121                     write(TypedEq<uint8_t>(0x21), TypedEq<uint16_t>(0x014D)))
122             .Times(1);
123         EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x21), A<uint16_t&>()))
124             .Times(1)
125             .WillOnce(SetArgReferee<1>(0x014D));
126 
127         // Create Device, IDMap, MockServices, and ActionEnvironment
128         Device device{
129             "reg1", true,
130             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
131             std::move(i2cInterface)};
132         IDMap idMap{};
133         idMap.addDevice(device);
134         MockServices services{};
135         ActionEnvironment env{idMap, "reg1", services};
136 
137         // Create and execute action
138         // Linear format volts value = (1.3 / 2^(-8)) = 332.8 = 333 = 0x014D
139         std::optional<double> volts{1.3};
140         pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
141         std::optional<int8_t> exponent{-8};
142         bool isVerified{true};
143         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
144         EXPECT_EQ(action.execute(env), true);
145     }
146     catch (...)
147     {
148         ADD_FAILURE() << "Should not have caught exception.";
149     }
150 
151     // Test where works: Volts value defined in ActionEnvironment; exponent
152     // value defined in VOUT_MODE; write is not verified.
153     try
154     {
155         // Create mock I2CInterface.  Expect action to do the following:
156         // * will read 0b0001'0111 (linear format, -9 exponent) from VOUT_MODE
157         // * will write 0x069A to VOUT_COMMAND
158         // * will not read from VOUT_COMMAND
159         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
160             std::make_unique<i2c::MockedI2CInterface>();
161         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
162         EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x20), A<uint8_t&>()))
163             .Times(1)
164             .WillOnce(SetArgReferee<1>(0b0001'0111));
165         EXPECT_CALL(*i2cInterface,
166                     write(TypedEq<uint8_t>(0x21), TypedEq<uint16_t>(0x069A)))
167             .Times(1);
168         EXPECT_CALL(*i2cInterface, read(A<uint8_t>(), A<uint16_t&>())).Times(0);
169 
170         // Create Device, IDMap, MockServices, and ActionEnvironment.  Set
171         // volts value to 3.3 in ActionEnvironment.
172         Device device{
173             "reg1", true,
174             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
175             std::move(i2cInterface)};
176         IDMap idMap{};
177         idMap.addDevice(device);
178         MockServices services{};
179         ActionEnvironment env{idMap, "reg1", services};
180         env.setVolts(3.3);
181 
182         // Create and execute action
183         // Linear format volts value = (3.3 / 2^(-9)) = 1689.6 = 1690 = 0x069A
184         std::optional<double> volts{};
185         pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
186         std::optional<int8_t> exponent{};
187         bool isVerified{false};
188         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
189         EXPECT_EQ(action.execute(env), true);
190     }
191     catch (...)
192     {
193         ADD_FAILURE() << "Should not have caught exception.";
194     }
195 
196     // Test where fails: No volts value defined
197     try
198     {
199         // Create IDMap, MockServices, and ActionEnvironment
200         IDMap idMap{};
201         MockServices services{};
202         ActionEnvironment env{idMap, "reg1", services};
203 
204         // Create and execute action
205         std::optional<double> volts{};
206         pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
207         std::optional<int8_t> exponent{-8};
208         bool isVerified{false};
209         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
210         action.execute(env);
211         ADD_FAILURE() << "Should not have reached this line.";
212     }
213     catch (const ActionError& e)
214     {
215         EXPECT_STREQ(
216             e.what(),
217             "ActionError: pmbus_write_vout_command: { format: linear, "
218             "exponent: -8, is_verified: false }: No volts value defined");
219     }
220     catch (...)
221     {
222         ADD_FAILURE() << "Should not have caught exception.";
223     }
224 
225     // Test where fails: Unable to get I2C interface to current device
226     try
227     {
228         // Create IDMap, MockServices, and ActionEnvironment
229         IDMap idMap{};
230         MockServices services{};
231         ActionEnvironment env{idMap, "reg1", services};
232 
233         // Create and execute action
234         std::optional<double> volts{1.3};
235         pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
236         std::optional<int8_t> exponent{-8};
237         bool isVerified{false};
238         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
239         action.execute(env);
240         ADD_FAILURE() << "Should not have reached this line.";
241     }
242     catch (const std::invalid_argument& e)
243     {
244         EXPECT_STREQ(e.what(), "Unable to find device with ID \"reg1\"");
245     }
246     catch (...)
247     {
248         ADD_FAILURE() << "Should not have caught exception.";
249     }
250 
251     // Test where fails: Unable to read VOUT_MODE to get exponent
252     try
253     {
254         // Create mock I2CInterface.  Expect action to do the following:
255         // * will try to read VOUT_MODE; exception will be thrown
256         // * will not write to VOUT_COMMAND due to exception
257         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
258             std::make_unique<i2c::MockedI2CInterface>();
259         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
260         EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x20), A<uint8_t&>()))
261             .Times(1)
262             .WillOnce(Throw(
263                 i2c::I2CException{"Failed to read byte", "/dev/i2c-1", 0x70}));
264         EXPECT_CALL(*i2cInterface, write(A<uint8_t>(), A<uint16_t>())).Times(0);
265 
266         // Create Device, IDMap, MockServices, and ActionEnvironment
267         Device device{
268             "reg1", true,
269             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
270             std::move(i2cInterface)};
271         IDMap idMap{};
272         idMap.addDevice(device);
273         MockServices services{};
274         ActionEnvironment env{idMap, "reg1", services};
275 
276         // Create and execute action
277         std::optional<double> volts{3.3};
278         pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
279         std::optional<int8_t> exponent{};
280         bool isVerified{false};
281         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
282         action.execute(env);
283         ADD_FAILURE() << "Should not have reached this line.";
284     }
285     catch (const ActionError& e)
286     {
287         EXPECT_STREQ(e.what(),
288                      "ActionError: pmbus_write_vout_command: { volts: 3.3, "
289                      "format: linear, is_verified: false }");
290         try
291         {
292             // Re-throw inner I2CException
293             std::rethrow_if_nested(e);
294             ADD_FAILURE() << "Should not have reached this line.";
295         }
296         catch (const i2c::I2CException& ie)
297         {
298             EXPECT_STREQ(
299                 ie.what(),
300                 "I2CException: Failed to read byte: bus /dev/i2c-1, addr 0x70");
301         }
302         catch (...)
303         {
304             ADD_FAILURE() << "Should not have caught exception.";
305         }
306     }
307     catch (...)
308     {
309         ADD_FAILURE() << "Should not have caught exception.";
310     }
311 
312     // Test where fails: VOUT_MODE data format is not linear
313     try
314     {
315         // Create mock I2CInterface.  Expect action to do the following:
316         // * will read 0b0010'0000 (vid data format) from VOUT_MODE
317         // * will not write to VOUT_COMMAND due to data format error
318         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
319             std::make_unique<i2c::MockedI2CInterface>();
320         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
321         EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x20), A<uint8_t&>()))
322             .Times(1)
323             .WillOnce(SetArgReferee<1>(0b0010'0000));
324         EXPECT_CALL(*i2cInterface, write(A<uint8_t>(), A<uint16_t>())).Times(0);
325 
326         // Create Device, IDMap, MockServices, and ActionEnvironment
327         Device device{
328             "reg1", true,
329             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
330             std::move(i2cInterface)};
331         IDMap idMap{};
332         idMap.addDevice(device);
333         MockServices services{};
334         ActionEnvironment env{idMap, "reg1", services};
335 
336         // Create and execute action
337         std::optional<double> volts{3.3};
338         pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
339         std::optional<int8_t> exponent{};
340         bool isVerified{false};
341         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
342         action.execute(env);
343         ADD_FAILURE() << "Should not have reached this line.";
344     }
345     catch (const ActionError& e)
346     {
347         EXPECT_STREQ(e.what(),
348                      "ActionError: pmbus_write_vout_command: { volts: 3.3, "
349                      "format: linear, is_verified: false }");
350         try
351         {
352             // Re-throw inner PMBusError
353             std::rethrow_if_nested(e);
354             ADD_FAILURE() << "Should not have reached this line.";
355         }
356         catch (const PMBusError& pe)
357         {
358             EXPECT_STREQ(
359                 pe.what(),
360                 "PMBusError: VOUT_MODE contains unsupported data format");
361             EXPECT_EQ(pe.getDeviceID(), "reg1");
362             EXPECT_EQ(pe.getInventoryPath(), "/xyz/openbmc_project/inventory/"
363                                              "system/chassis/motherboard/reg1");
364         }
365         catch (...)
366         {
367             ADD_FAILURE() << "Should not have caught exception.";
368         }
369     }
370     catch (...)
371     {
372         ADD_FAILURE() << "Should not have caught exception.";
373     }
374 
375     // Test where fails: Unable to write VOUT_COMMAND
376     try
377     {
378         // Create mock I2CInterface.  Expect action to do the following:
379         // * will not read from VOUT_MODE
380         // * will try to write 0x014D to VOUT_COMMAND; exception will be thrown
381         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
382             std::make_unique<i2c::MockedI2CInterface>();
383         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
384         EXPECT_CALL(*i2cInterface, read(A<uint8_t>(), A<uint8_t&>())).Times(0);
385         EXPECT_CALL(*i2cInterface,
386                     write(TypedEq<uint8_t>(0x21), TypedEq<uint16_t>(0x014D)))
387             .Times(1)
388             .WillOnce(Throw(i2c::I2CException{"Failed to write word data",
389                                               "/dev/i2c-1", 0x70}));
390 
391         // Create Device, IDMap, MockServices, and ActionEnvironment
392         Device device{
393             "reg1", true,
394             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
395             std::move(i2cInterface)};
396         IDMap idMap{};
397         idMap.addDevice(device);
398         MockServices services{};
399         ActionEnvironment env{idMap, "reg1", services};
400 
401         // Create and execute action
402         // Linear format volts value = (1.3 / 2^(-8)) = 332.8 = 333 = 0x014D
403         std::optional<double> volts{1.3};
404         pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
405         std::optional<int8_t> exponent{-8};
406         bool isVerified{false};
407         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
408         action.execute(env);
409         ADD_FAILURE() << "Should not have reached this line.";
410     }
411     catch (const ActionError& e)
412     {
413         EXPECT_STREQ(e.what(),
414                      "ActionError: pmbus_write_vout_command: { volts: 1.3, "
415                      "format: linear, exponent: -8, is_verified: false }");
416         try
417         {
418             // Re-throw inner I2CException
419             std::rethrow_if_nested(e);
420             ADD_FAILURE() << "Should not have reached this line.";
421         }
422         catch (const i2c::I2CException& ie)
423         {
424             EXPECT_STREQ(ie.what(), "I2CException: Failed to write word data: "
425                                     "bus /dev/i2c-1, addr 0x70");
426         }
427         catch (...)
428         {
429             ADD_FAILURE() << "Should not have caught exception.";
430         }
431     }
432     catch (...)
433     {
434         ADD_FAILURE() << "Should not have caught exception.";
435     }
436 
437     // Test where fails: Unable to read VOUT_COMMAND
438     try
439     {
440         // Create mock I2CInterface.  Expect action to do the following:
441         // * will not read from VOUT_MODE
442         // * will write 0x014D to VOUT_COMMAND
443         // * will try to read from VOUT_COMMAND; exception will be thrown
444         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
445             std::make_unique<i2c::MockedI2CInterface>();
446         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
447         EXPECT_CALL(*i2cInterface, read(A<uint8_t>(), A<uint8_t&>())).Times(0);
448         EXPECT_CALL(*i2cInterface,
449                     write(TypedEq<uint8_t>(0x21), TypedEq<uint16_t>(0x014D)))
450             .Times(1);
451         EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x21), A<uint16_t&>()))
452             .Times(1)
453             .WillOnce(Throw(i2c::I2CException{"Failed to read word data",
454                                               "/dev/i2c-1", 0x70}));
455 
456         // Create Device, IDMap, MockServices, and ActionEnvironment
457         Device device{
458             "reg1", true,
459             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
460             std::move(i2cInterface)};
461         IDMap idMap{};
462         idMap.addDevice(device);
463         MockServices services{};
464         ActionEnvironment env{idMap, "reg1", services};
465 
466         // Create and execute action
467         // Linear format volts value = (1.3 / 2^(-8)) = 332.8 = 333 = 0x014D
468         std::optional<double> volts{1.3};
469         pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
470         std::optional<int8_t> exponent{-8};
471         bool isVerified{true};
472         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
473         action.execute(env);
474         ADD_FAILURE() << "Should not have reached this line.";
475     }
476     catch (const ActionError& e)
477     {
478         EXPECT_STREQ(e.what(),
479                      "ActionError: pmbus_write_vout_command: { volts: 1.3, "
480                      "format: linear, exponent: -8, is_verified: true }");
481         try
482         {
483             // Re-throw inner I2CException
484             std::rethrow_if_nested(e);
485             ADD_FAILURE() << "Should not have reached this line.";
486         }
487         catch (const i2c::I2CException& ie)
488         {
489             EXPECT_STREQ(ie.what(), "I2CException: Failed to read word data: "
490                                     "bus /dev/i2c-1, addr 0x70");
491         }
492         catch (...)
493         {
494             ADD_FAILURE() << "Should not have caught exception.";
495         }
496     }
497     catch (...)
498     {
499         ADD_FAILURE() << "Should not have caught exception.";
500     }
501 
502     // Test where fails: Write verification error
503     try
504     {
505         // Create mock I2CInterface.  Expect action to do the following:
506         // * will not read from VOUT_MODE
507         // * will write 0x014D to VOUT_COMMAND
508         // * will read 0x014C from VOUT_COMMAND (not equal to 0x014D)
509         std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
510             std::make_unique<i2c::MockedI2CInterface>();
511         EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
512         EXPECT_CALL(*i2cInterface, read(A<uint8_t>(), A<uint8_t&>())).Times(0);
513         EXPECT_CALL(*i2cInterface,
514                     write(TypedEq<uint8_t>(0x21), TypedEq<uint16_t>(0x014D)))
515             .Times(1);
516         EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x21), A<uint16_t&>()))
517             .Times(1)
518             .WillOnce(SetArgReferee<1>(0x014C));
519 
520         // Create Device, IDMap, MockServices, and ActionEnvironment
521         Device device{
522             "reg1", true,
523             "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
524             std::move(i2cInterface)};
525         IDMap idMap{};
526         idMap.addDevice(device);
527         MockServices services{};
528         ActionEnvironment env{idMap, "reg1", services};
529 
530         // Create and execute action
531         // Linear format volts value = (1.3 / 2^(-8)) = 332.8 = 333 = 0x014D
532         std::optional<double> volts{1.3};
533         pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
534         std::optional<int8_t> exponent{-8};
535         bool isVerified{true};
536         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
537         action.execute(env);
538         ADD_FAILURE() << "Should not have reached this line.";
539     }
540     catch (const ActionError& e)
541     {
542         EXPECT_STREQ(e.what(),
543                      "ActionError: pmbus_write_vout_command: { volts: 1.3, "
544                      "format: linear, exponent: -8, is_verified: true }");
545         try
546         {
547             // Re-throw inner WriteVerificationError
548             std::rethrow_if_nested(e);
549             ADD_FAILURE() << "Should not have reached this line.";
550         }
551         catch (const WriteVerificationError& we)
552         {
553             EXPECT_STREQ(
554                 we.what(),
555                 "WriteVerificationError: device: reg1, register: VOUT_COMMAND, "
556                 "value_written: 0x14D, value_read: 0x14C");
557             EXPECT_EQ(we.getDeviceID(), "reg1");
558             EXPECT_EQ(we.getInventoryPath(), "/xyz/openbmc_project/inventory/"
559                                              "system/chassis/motherboard/reg1");
560         }
561         catch (...)
562         {
563             ADD_FAILURE() << "Should not have caught exception.";
564         }
565     }
566     catch (...)
567     {
568         ADD_FAILURE() << "Should not have caught exception.";
569     }
570 }
571 
TEST(PMBusWriteVoutCommandActionTests,GetExponent)572 TEST(PMBusWriteVoutCommandActionTests, GetExponent)
573 {
574     std::optional<double> volts{1.3};
575     pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
576     bool isVerified{true};
577 
578     // Exponent value was specified
579     {
580         std::optional<int8_t> exponent{-9};
581         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
582         EXPECT_EQ(action.getExponent().has_value(), true);
583         EXPECT_EQ(action.getExponent().value(), -9);
584     }
585 
586     // Exponent value was not specified
587     {
588         std::optional<int8_t> exponent{};
589         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
590         EXPECT_EQ(action.getExponent().has_value(), false);
591     }
592 }
593 
TEST(PMBusWriteVoutCommandActionTests,GetFormat)594 TEST(PMBusWriteVoutCommandActionTests, GetFormat)
595 {
596     std::optional<double> volts{};
597     pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
598     std::optional<int8_t> exponent{};
599     bool isVerified{false};
600     PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
601     EXPECT_EQ(action.getFormat(), pmbus_utils::VoutDataFormat::linear);
602 }
603 
TEST(PMBusWriteVoutCommandActionTests,GetVolts)604 TEST(PMBusWriteVoutCommandActionTests, GetVolts)
605 {
606     pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
607     std::optional<int8_t> exponent{-8};
608     bool isVerified{true};
609 
610     // Volts value was specified
611     {
612         std::optional<double> volts{1.3};
613         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
614         EXPECT_EQ(action.getVolts().has_value(), true);
615         EXPECT_EQ(action.getVolts().value(), 1.3);
616     }
617 
618     // Volts value was not specified
619     {
620         std::optional<double> volts{};
621         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
622         EXPECT_EQ(action.getVolts().has_value(), false);
623     }
624 }
625 
TEST(PMBusWriteVoutCommandActionTests,IsVerified)626 TEST(PMBusWriteVoutCommandActionTests, IsVerified)
627 {
628     std::optional<double> volts{1.3};
629     pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
630     std::optional<int8_t> exponent{-8};
631     bool isVerified{true};
632     PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
633     EXPECT_EQ(action.isVerified(), true);
634 }
635 
TEST(PMBusWriteVoutCommandActionTests,ToString)636 TEST(PMBusWriteVoutCommandActionTests, ToString)
637 {
638     // Test where volts value and exponent value are specified
639     {
640         std::optional<double> volts{1.3};
641         pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
642         std::optional<int8_t> exponent{-8};
643         bool isVerified{true};
644         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
645         EXPECT_EQ(action.toString(),
646                   "pmbus_write_vout_command: { volts: 1.3, format: linear, "
647                   "exponent: -8, is_verified: true }");
648     }
649 
650     // Test where volts value and exponent value are not specified
651     {
652         std::optional<double> volts{};
653         pmbus_utils::VoutDataFormat format{pmbus_utils::VoutDataFormat::linear};
654         std::optional<int8_t> exponent{};
655         bool isVerified{false};
656         PMBusWriteVoutCommandAction action{volts, format, exponent, isVerified};
657         EXPECT_EQ(
658             action.toString(),
659             "pmbus_write_vout_command: { format: linear, is_verified: false }");
660     }
661 }
662