blob: d24a04adad621e1f2687891e5a5c747f53649864 [file] [log] [blame]
Bob Kingd6820bb2020-04-28 15:37:02 +08001/**
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"
Bob King717d2da2020-06-02 11:11:15 +080017#include "action_error.hpp"
18#include "device.hpp"
Bob Kingd6820bb2020-04-28 15:37:02 +080019#include "i2c_action.hpp"
20#include "i2c_interface.hpp"
Bob King717d2da2020-06-02 11:11:15 +080021#include "id_map.hpp"
22#include "mocked_i2c_interface.hpp"
23#include "pmbus_error.hpp"
Bob Kingd6820bb2020-04-28 15:37:02 +080024#include "pmbus_read_sensor_action.hpp"
25#include "pmbus_utils.hpp"
26
27#include <cstdint>
Bob King717d2da2020-06-02 11:11:15 +080028#include <memory>
Bob Kingd6820bb2020-04-28 15:37:02 +080029#include <optional>
30#include <stdexcept>
31#include <string>
Bob King717d2da2020-06-02 11:11:15 +080032#include <utility>
Bob Kingd6820bb2020-04-28 15:37:02 +080033
Bob King717d2da2020-06-02 11:11:15 +080034#include <gmock/gmock.h>
Bob Kingd6820bb2020-04-28 15:37:02 +080035#include <gtest/gtest.h>
36
37using namespace phosphor::power::regulators;
38
Bob King717d2da2020-06-02 11:11:15 +080039using ::testing::A;
40using ::testing::Return;
41using ::testing::SetArgReferee;
42using ::testing::Throw;
43using ::testing::TypedEq;
44
Bob Kingd6820bb2020-04-28 15:37:02 +080045TEST(PMBusReadSensorActionTests, Constructor)
46{
47 // Test where works: exponent value is specified
48 try
49 {
50 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
51 uint8_t command = 0x8C;
52 pmbus_utils::SensorDataFormat format{
53 pmbus_utils::SensorDataFormat::linear_16};
54 std::optional<int8_t> exponent{-8};
55 PMBusReadSensorAction action{type, command, format, exponent};
56 EXPECT_EQ(action.getType(), pmbus_utils::SensorValueType::iout);
57 EXPECT_EQ(action.getCommand(), 0x8C);
58 EXPECT_EQ(action.getFormat(), pmbus_utils::SensorDataFormat::linear_16);
59 EXPECT_EQ(action.getExponent().has_value(), true);
60 EXPECT_EQ(action.getExponent().value(), -8);
61 }
62 catch (...)
63 {
64 ADD_FAILURE() << "Should not have caught exception.";
65 }
66
67 // Test where works: exponent value is not specified
68 try
69 {
70 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
71 uint8_t command = 0x8C;
72 pmbus_utils::SensorDataFormat format{
73 pmbus_utils::SensorDataFormat::linear_11};
74 std::optional<int8_t> exponent{};
75 PMBusReadSensorAction action{type, command, format, exponent};
76 EXPECT_EQ(action.getType(), pmbus_utils::SensorValueType::iout);
77 EXPECT_EQ(action.getCommand(), 0x8C);
78 EXPECT_EQ(action.getFormat(), pmbus_utils::SensorDataFormat::linear_11);
79 EXPECT_EQ(action.getExponent().has_value(), false);
80 }
81 catch (...)
82 {
83 ADD_FAILURE() << "Should not have caught exception.";
84 }
85}
86
87TEST(PMBusReadSensorActionTests, Execute)
88{
Bob King717d2da2020-06-02 11:11:15 +080089 // Test where works: linear_11 defined in action
90 try
91 {
92 // Create mock I2CInterface.
93 // * will read 0xD2E0 from READ_IOUT (command/register 0x8C)
94 // * will not read from VOUT_MODE (command/register 0x20)
95 // assume output current is 11.5 amps,
96 // exponent = -6 = 11010, mantissa = 736 = 010 1110 0000
97 // linear data format = 1101 0010 1110 0000 = 0xD2E0
98 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
99 std::make_unique<i2c::MockedI2CInterface>();
100 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
101 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>()))
102 .Times(1)
103 .WillOnce(SetArgReferee<1>(0xD2E0));
104 EXPECT_CALL(*i2cInterface, read(A<uint8_t>(), A<uint8_t&>())).Times(0);
105
106 // Create Device, IDMap, and ActionEnvironment
107 Device device{"reg1", true, "/system/chassis/motherboard/reg1",
108 std::move(i2cInterface)};
109 IDMap idMap{};
110 idMap.addDevice(device);
111 ActionEnvironment env{idMap, "reg1"};
112
113 // Create and execute action
114 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
115 uint8_t command = 0x8C;
116 pmbus_utils::SensorDataFormat format{
117 pmbus_utils::SensorDataFormat::linear_11};
118 std::optional<int8_t> exponent{};
119 PMBusReadSensorAction action{type, command, format, exponent};
120 EXPECT_EQ(action.execute(env), true);
121 EXPECT_EQ(env.getSensorReadings().size(), 1);
122 EXPECT_EQ(env.getSensorReadings()[0].type,
123 pmbus_utils::SensorValueType::iout);
124 EXPECT_DOUBLE_EQ(env.getSensorReadings()[0].value, 11.5);
125 }
126 catch (...)
127 {
128 ADD_FAILURE() << "Should not have caught exception.";
129 }
130
131 // Test where works: linear_16 with exponent defined in action
132 try
133 {
134 // Create mock I2CInterface.
135 // * will read 0x0002 from READ_VOUT (command/register 0x8B)
136 // * will not read from VOUT_MODE (command/register 0x20)
137 // assume output voltage is 16 volts,
138 // exponent = 3
139 // linear data format = 0000 0000 0000 0010 = 0x0002 = 2
140 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
141 std::make_unique<i2c::MockedI2CInterface>();
142 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
143 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8B), A<uint16_t&>()))
144 .Times(1)
145 .WillOnce(SetArgReferee<1>(0x0002));
146 EXPECT_CALL(*i2cInterface, read(A<uint8_t>(), A<uint8_t&>())).Times(0);
147
148 // Create Device, IDMap, and ActionEnvironment
149 Device device{"reg1", true, "/system/chassis/motherboard/reg1",
150 std::move(i2cInterface)};
151 IDMap idMap{};
152 idMap.addDevice(device);
153 ActionEnvironment env{idMap, "reg1"};
154
155 // Create and execute action
156 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::vout};
157 uint8_t command = 0x8B;
158 pmbus_utils::SensorDataFormat format{
159 pmbus_utils::SensorDataFormat::linear_16};
160 std::optional<int8_t> exponent{3};
161 PMBusReadSensorAction action{type, command, format, exponent};
162 EXPECT_EQ(action.execute(env), true);
163 EXPECT_EQ(env.getSensorReadings().size(), 1);
164 EXPECT_EQ(env.getSensorReadings()[0].type,
165 pmbus_utils::SensorValueType::vout);
166 EXPECT_DOUBLE_EQ(env.getSensorReadings()[0].value, 16);
167 }
168 catch (...)
169 {
170 ADD_FAILURE() << "Should not have caught exception.";
171 }
172
173 // Test where works: linear_16 with no exponent defined in action
174 try
175 {
176 // Create mock I2CInterface.
177 // * will read 0xB877 from vout_peak (command/register 0xC6)
178 // * will read 0b0001'0111 (linear format, -9 exponent) from VOUT_MODE
179 // assume output voltage is 0.232421875 volts,
180 // linear data format = 0000 0000 0111 0111 = 0x0077
181 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
182 std::make_unique<i2c::MockedI2CInterface>();
183 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
184 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0xC6), A<uint16_t&>()))
185 .Times(1)
186 .WillOnce(SetArgReferee<1>(0x0077));
187 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x20), A<uint8_t&>()))
188 .Times(1)
189 .WillOnce(SetArgReferee<1>(0b0001'0111));
190 // Create Device, IDMap, and ActionEnvironment
191 Device device{"reg1", true, "/system/chassis/motherboard/reg1",
192 std::move(i2cInterface)};
193 IDMap idMap{};
194 idMap.addDevice(device);
195 ActionEnvironment env{idMap, "reg1"};
196
197 // Create and execute action
198 pmbus_utils::SensorValueType type{
199 pmbus_utils::SensorValueType::vout_peak};
200 uint8_t command = 0xC6;
201 pmbus_utils::SensorDataFormat format{
202 pmbus_utils::SensorDataFormat::linear_16};
203 std::optional<int8_t> exponent{};
204 PMBusReadSensorAction action{type, command, format, exponent};
205 EXPECT_EQ(action.execute(env), true);
206 EXPECT_EQ(env.getSensorReadings().size(), 1);
207 EXPECT_EQ(env.getSensorReadings()[0].type,
208 pmbus_utils::SensorValueType::vout_peak);
209 EXPECT_DOUBLE_EQ(env.getSensorReadings()[0].value, 0.232421875);
210 }
211 catch (...)
212 {
213 ADD_FAILURE() << "Should not have caught exception.";
214 }
215
216 // Test where fails: Unable to get I2C interface to current device
217 try
218 {
219 // Create IDMap and ActionEnvironment
220 IDMap idMap{};
221 ActionEnvironment env{idMap, "reg1"};
222
223 // Create and execute action
224 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::pout};
225 uint8_t command = 0x96;
226 pmbus_utils::SensorDataFormat format{
227 pmbus_utils::SensorDataFormat::linear_11};
228 std::optional<int8_t> exponent{};
229 PMBusReadSensorAction action{type, command, format, exponent};
230 action.execute(env);
231 ADD_FAILURE() << "Should not have reached this line.";
232 }
233 catch (const std::invalid_argument& e)
234 {
235 EXPECT_STREQ(e.what(), "Unable to find device with ID \"reg1\"");
236 }
237 catch (...)
238 {
239 ADD_FAILURE() << "Should not have caught exception.";
240 }
241
242 // Test where fails: VOUT_MODE data format is not linear
243 try
244 {
245 // Create mock I2CInterface. Expect action to do the following:
246 // * will read 0b0010'0000 (vid data format) from VOUT_MODE
247 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
248 std::make_unique<i2c::MockedI2CInterface>();
249 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
Bob Kingab7d6cb2020-07-17 10:24:07 +0800250 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8B), A<uint16_t&>()))
251 .Times(1);
Bob King717d2da2020-06-02 11:11:15 +0800252 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x20), A<uint8_t&>()))
253 .Times(1)
254 .WillOnce(SetArgReferee<1>(0b0010'0000));
255
256 // Create Device, IDMap, and ActionEnvironment
257 Device device{"reg1", true, "/system/chassis/motherboard/reg1",
258 std::move(i2cInterface)};
259 IDMap idMap{};
260 idMap.addDevice(device);
261 ActionEnvironment env{idMap, "reg1"};
262
263 // Create and execute action
264 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::vout};
265 uint8_t command = 0x8B;
266 pmbus_utils::SensorDataFormat format{
267 pmbus_utils::SensorDataFormat::linear_16};
268 std::optional<int8_t> exponent{};
269 PMBusReadSensorAction action{type, command, format, exponent};
270 action.execute(env);
271 ADD_FAILURE() << "Should not have reached this line.";
272 }
273 catch (const ActionError& e)
274 {
275 EXPECT_STREQ(e.what(), "ActionError: pmbus_read_sensor: { type: vout, "
276 "command: 0x8B, format: linear_16 }");
277 try
278 {
279 // Re-throw inner PMBusError
280 std::rethrow_if_nested(e);
281 ADD_FAILURE() << "Should not have reached this line.";
282 }
283 catch (const PMBusError& pe)
284 {
285 EXPECT_STREQ(
286 pe.what(),
287 "PMBusError: VOUT_MODE contains unsupported data format");
288 }
289 catch (...)
290 {
291 ADD_FAILURE() << "Should not have caught exception.";
292 }
293 }
294 catch (...)
295 {
296 ADD_FAILURE() << "Should not have caught exception.";
297 }
298
299 // Test where fails: Reading VOUT_MODE fails
300 try
301 {
302 // Create mock I2CInterface. Expect action to do the following:
303 // * will try to read VOUT_MODE; exception will be thrown
304 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
305 std::make_unique<i2c::MockedI2CInterface>();
306 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
Bob Kingab7d6cb2020-07-17 10:24:07 +0800307 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0xC6), A<uint16_t&>()))
308 .Times(1);
Bob King717d2da2020-06-02 11:11:15 +0800309 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x20), A<uint8_t&>()))
310 .Times(1)
311 .WillOnce(Throw(
312 i2c::I2CException{"Failed to read byte", "/dev/i2c-1", 0x70}));
313
314 // Create Device, IDMap, and ActionEnvironment
315 Device device{"reg1", true, "/system/chassis/motherboard/reg1",
316 std::move(i2cInterface)};
317 IDMap idMap{};
318 idMap.addDevice(device);
319 ActionEnvironment env{idMap, "reg1"};
320
321 // Create and execute action
322 pmbus_utils::SensorValueType type{
323 pmbus_utils::SensorValueType::vout_peak};
324 uint8_t command = 0xC6;
325 pmbus_utils::SensorDataFormat format{
326 pmbus_utils::SensorDataFormat::linear_16};
327 std::optional<int8_t> exponent{};
328 PMBusReadSensorAction action{type, command, format, exponent};
329 action.execute(env);
330 ADD_FAILURE() << "Should not have reached this line.";
331 }
332 catch (const ActionError& e)
333 {
334 EXPECT_STREQ(e.what(),
335 "ActionError: pmbus_read_sensor: { type: vout_peak, "
336 "command: 0xC6, format: linear_16 }");
337 try
338 {
339 // Re-throw inner I2CException
340 std::rethrow_if_nested(e);
341 ADD_FAILURE() << "Should not have reached this line.";
342 }
343 catch (const i2c::I2CException& ie)
344 {
345 EXPECT_STREQ(
346 ie.what(),
347 "I2CException: Failed to read byte: bus /dev/i2c-1, addr 0x70");
348 }
349 catch (...)
350 {
351 ADD_FAILURE() << "Should not have caught exception.";
352 }
353 }
354 catch (...)
355 {
356 ADD_FAILURE() << "Should not have caught exception.";
357 }
358
359 // Test where fails: Reading PMBus command code with sensor value fails
360 try
361 {
362 // Create mock I2CInterface. Expect action to do the following:
363 // * will try to read PMBus command(0x96); exception will be thrown
364 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
365 std::make_unique<i2c::MockedI2CInterface>();
366 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
367 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x96), A<uint16_t&>()))
368 .Times(1)
369 .WillOnce(Throw(
370 i2c::I2CException{"Failed to read word", "/dev/i2c-1", 0x70}));
371
372 // Create Device, IDMap, and ActionEnvironment
373 Device device{"reg1", true, "/system/chassis/motherboard/reg1",
374 std::move(i2cInterface)};
375 IDMap idMap{};
376 idMap.addDevice(device);
377 ActionEnvironment env{idMap, "reg1"};
378
379 // Create and execute action
380 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::pout};
381 uint8_t command = 0x96;
382 pmbus_utils::SensorDataFormat format{
383 pmbus_utils::SensorDataFormat::linear_11};
384 std::optional<int8_t> exponent{};
385 PMBusReadSensorAction action{type, command, format, exponent};
386 action.execute(env);
387 ADD_FAILURE() << "Should not have reached this line.";
388 }
389 catch (const ActionError& e)
390 {
391 EXPECT_STREQ(e.what(), "ActionError: pmbus_read_sensor: { type: pout, "
392 "command: 0x96, format: linear_11 }");
393 try
394 {
395 // Re-throw inner I2CException
396 std::rethrow_if_nested(e);
397 ADD_FAILURE() << "Should not have reached this line.";
398 }
399 catch (const i2c::I2CException& ie)
400 {
401 EXPECT_STREQ(
402 ie.what(),
403 "I2CException: Failed to read word: bus /dev/i2c-1, addr 0x70");
404 }
405 catch (...)
406 {
407 ADD_FAILURE() << "Should not have caught exception.";
408 }
409 }
410 catch (...)
411 {
412 ADD_FAILURE() << "Should not have caught exception.";
413 }
Bob Kingd6820bb2020-04-28 15:37:02 +0800414}
415
416TEST(PMBusReadSensorActionTests, GetCommand)
417{
418 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
419 uint8_t command = 0x8C;
420 pmbus_utils::SensorDataFormat format{
421 pmbus_utils::SensorDataFormat::linear_16};
422 std::optional<int8_t> exponent{-8};
423 PMBusReadSensorAction action{type, command, format, exponent};
424 EXPECT_EQ(action.getCommand(), 0x8C);
425}
426
427TEST(PMBusReadSensorActionTests, GetExponent)
428{
429 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
430 uint8_t command = 0x8C;
431 pmbus_utils::SensorDataFormat format{
432 pmbus_utils::SensorDataFormat::linear_16};
433
434 // Exponent value is specified
435 {
436 std::optional<int8_t> exponent{-9};
437 PMBusReadSensorAction action{type, command, format, exponent};
438 EXPECT_EQ(action.getExponent().has_value(), true);
439 EXPECT_EQ(action.getExponent().value(), -9);
440 }
441
442 // Exponent value is not specified
443 {
444 std::optional<int8_t> exponent{};
445 PMBusReadSensorAction action{type, command, format, exponent};
446 EXPECT_EQ(action.getExponent().has_value(), false);
447 }
448}
449
450TEST(PMBusReadSensorActionTests, GetFormat)
451{
452 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
453 uint8_t command = 0x8C;
454 pmbus_utils::SensorDataFormat format{
455 pmbus_utils::SensorDataFormat::linear_16};
456 std::optional<int8_t> exponent{-8};
457 PMBusReadSensorAction action{type, command, format, exponent};
458 EXPECT_EQ(action.getFormat(), pmbus_utils::SensorDataFormat::linear_16);
459}
460
461TEST(PMBusReadSensorActionTests, GetType)
462{
463 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::pout};
464 uint8_t command = 0x8C;
465 pmbus_utils::SensorDataFormat format{
466 pmbus_utils::SensorDataFormat::linear_16};
467 std::optional<int8_t> exponent{-8};
468 PMBusReadSensorAction action{type, command, format, exponent};
469 EXPECT_EQ(action.getType(), pmbus_utils::SensorValueType::pout);
470}
471
472TEST(PMBusReadSensorActionTests, ToString)
473{
474 // Test where exponent value is specified
475 {
Bob King717d2da2020-06-02 11:11:15 +0800476 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::vout};
477 uint8_t command = 0x8B;
Bob Kingd6820bb2020-04-28 15:37:02 +0800478 pmbus_utils::SensorDataFormat format{
479 pmbus_utils::SensorDataFormat::linear_16};
480 std::optional<int8_t> exponent{-8};
481 PMBusReadSensorAction action{type, command, format, exponent};
482 EXPECT_EQ(action.toString(), "pmbus_read_sensor: { type: "
Bob King717d2da2020-06-02 11:11:15 +0800483 "vout, command: 0x8B, format: "
Bob Kingd6820bb2020-04-28 15:37:02 +0800484 "linear_16, exponent: -8 }");
485 }
486
487 // Test where exponent value is not specified
488 {
Bob King717d2da2020-06-02 11:11:15 +0800489 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
Bob Kingd6820bb2020-04-28 15:37:02 +0800490 uint8_t command = 0x8C;
491 pmbus_utils::SensorDataFormat format{
492 pmbus_utils::SensorDataFormat::linear_11};
493 std::optional<int8_t> exponent{};
494 PMBusReadSensorAction action{type, command, format, exponent};
Bob King717d2da2020-06-02 11:11:15 +0800495 EXPECT_EQ(action.toString(), "pmbus_read_sensor: { type: iout, "
Bob Kingd6820bb2020-04-28 15:37:02 +0800496 "command: 0x8C, format: linear_11 }");
497 }
498}