blob: cfc4f7efd0ed1086c63f909c6764790c050aaae0 [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
Bob Kinga76898f2020-10-13 15:08:33 +0800107 Device device{
108 "reg1", true,
109 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
110 std::move(i2cInterface)};
Bob King717d2da2020-06-02 11:11:15 +0800111 IDMap idMap{};
112 idMap.addDevice(device);
113 ActionEnvironment env{idMap, "reg1"};
114
115 // Create and execute action
116 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
117 uint8_t command = 0x8C;
118 pmbus_utils::SensorDataFormat format{
119 pmbus_utils::SensorDataFormat::linear_11};
120 std::optional<int8_t> exponent{};
121 PMBusReadSensorAction action{type, command, format, exponent};
122 EXPECT_EQ(action.execute(env), true);
123 EXPECT_EQ(env.getSensorReadings().size(), 1);
124 EXPECT_EQ(env.getSensorReadings()[0].type,
125 pmbus_utils::SensorValueType::iout);
126 EXPECT_DOUBLE_EQ(env.getSensorReadings()[0].value, 11.5);
127 }
128 catch (...)
129 {
130 ADD_FAILURE() << "Should not have caught exception.";
131 }
132
133 // Test where works: linear_16 with exponent defined in action
134 try
135 {
136 // Create mock I2CInterface.
137 // * will read 0x0002 from READ_VOUT (command/register 0x8B)
138 // * will not read from VOUT_MODE (command/register 0x20)
139 // assume output voltage is 16 volts,
140 // exponent = 3
141 // linear data format = 0000 0000 0000 0010 = 0x0002 = 2
142 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
143 std::make_unique<i2c::MockedI2CInterface>();
144 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
145 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8B), A<uint16_t&>()))
146 .Times(1)
147 .WillOnce(SetArgReferee<1>(0x0002));
148 EXPECT_CALL(*i2cInterface, read(A<uint8_t>(), A<uint8_t&>())).Times(0);
149
150 // Create Device, IDMap, and ActionEnvironment
Bob Kinga76898f2020-10-13 15:08:33 +0800151 Device device{
152 "reg1", true,
153 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
154 std::move(i2cInterface)};
Bob King717d2da2020-06-02 11:11:15 +0800155 IDMap idMap{};
156 idMap.addDevice(device);
157 ActionEnvironment env{idMap, "reg1"};
158
159 // Create and execute action
160 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::vout};
161 uint8_t command = 0x8B;
162 pmbus_utils::SensorDataFormat format{
163 pmbus_utils::SensorDataFormat::linear_16};
164 std::optional<int8_t> exponent{3};
165 PMBusReadSensorAction action{type, command, format, exponent};
166 EXPECT_EQ(action.execute(env), true);
167 EXPECT_EQ(env.getSensorReadings().size(), 1);
168 EXPECT_EQ(env.getSensorReadings()[0].type,
169 pmbus_utils::SensorValueType::vout);
170 EXPECT_DOUBLE_EQ(env.getSensorReadings()[0].value, 16);
171 }
172 catch (...)
173 {
174 ADD_FAILURE() << "Should not have caught exception.";
175 }
176
177 // Test where works: linear_16 with no exponent defined in action
178 try
179 {
180 // Create mock I2CInterface.
181 // * will read 0xB877 from vout_peak (command/register 0xC6)
182 // * will read 0b0001'0111 (linear format, -9 exponent) from VOUT_MODE
183 // assume output voltage is 0.232421875 volts,
184 // linear data format = 0000 0000 0111 0111 = 0x0077
185 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
186 std::make_unique<i2c::MockedI2CInterface>();
187 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
188 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0xC6), A<uint16_t&>()))
189 .Times(1)
190 .WillOnce(SetArgReferee<1>(0x0077));
191 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x20), A<uint8_t&>()))
192 .Times(1)
193 .WillOnce(SetArgReferee<1>(0b0001'0111));
194 // Create Device, IDMap, and ActionEnvironment
Bob Kinga76898f2020-10-13 15:08:33 +0800195 Device device{
196 "reg1", true,
197 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
198 std::move(i2cInterface)};
Bob King717d2da2020-06-02 11:11:15 +0800199 IDMap idMap{};
200 idMap.addDevice(device);
201 ActionEnvironment env{idMap, "reg1"};
202
203 // Create and execute action
204 pmbus_utils::SensorValueType type{
205 pmbus_utils::SensorValueType::vout_peak};
206 uint8_t command = 0xC6;
207 pmbus_utils::SensorDataFormat format{
208 pmbus_utils::SensorDataFormat::linear_16};
209 std::optional<int8_t> exponent{};
210 PMBusReadSensorAction action{type, command, format, exponent};
211 EXPECT_EQ(action.execute(env), true);
212 EXPECT_EQ(env.getSensorReadings().size(), 1);
213 EXPECT_EQ(env.getSensorReadings()[0].type,
214 pmbus_utils::SensorValueType::vout_peak);
215 EXPECT_DOUBLE_EQ(env.getSensorReadings()[0].value, 0.232421875);
216 }
217 catch (...)
218 {
219 ADD_FAILURE() << "Should not have caught exception.";
220 }
221
222 // Test where fails: Unable to get I2C interface to current device
223 try
224 {
225 // Create IDMap and ActionEnvironment
226 IDMap idMap{};
227 ActionEnvironment env{idMap, "reg1"};
228
229 // Create and execute action
230 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::pout};
231 uint8_t command = 0x96;
232 pmbus_utils::SensorDataFormat format{
233 pmbus_utils::SensorDataFormat::linear_11};
234 std::optional<int8_t> exponent{};
235 PMBusReadSensorAction action{type, command, format, exponent};
236 action.execute(env);
237 ADD_FAILURE() << "Should not have reached this line.";
238 }
239 catch (const std::invalid_argument& e)
240 {
241 EXPECT_STREQ(e.what(), "Unable to find device with ID \"reg1\"");
242 }
243 catch (...)
244 {
245 ADD_FAILURE() << "Should not have caught exception.";
246 }
247
248 // Test where fails: VOUT_MODE data format is not linear
249 try
250 {
251 // Create mock I2CInterface. Expect action to do the following:
252 // * will read 0b0010'0000 (vid data format) from VOUT_MODE
253 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
254 std::make_unique<i2c::MockedI2CInterface>();
255 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
Bob Kingab7d6cb2020-07-17 10:24:07 +0800256 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8B), A<uint16_t&>()))
257 .Times(1);
Bob King717d2da2020-06-02 11:11:15 +0800258 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x20), A<uint8_t&>()))
259 .Times(1)
260 .WillOnce(SetArgReferee<1>(0b0010'0000));
261
262 // Create Device, IDMap, and ActionEnvironment
Bob Kinga76898f2020-10-13 15:08:33 +0800263 Device device{
264 "reg1", true,
265 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
266 std::move(i2cInterface)};
Bob King717d2da2020-06-02 11:11:15 +0800267 IDMap idMap{};
268 idMap.addDevice(device);
269 ActionEnvironment env{idMap, "reg1"};
270
271 // Create and execute action
272 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::vout};
273 uint8_t command = 0x8B;
274 pmbus_utils::SensorDataFormat format{
275 pmbus_utils::SensorDataFormat::linear_16};
276 std::optional<int8_t> exponent{};
277 PMBusReadSensorAction action{type, command, format, exponent};
278 action.execute(env);
279 ADD_FAILURE() << "Should not have reached this line.";
280 }
281 catch (const ActionError& e)
282 {
283 EXPECT_STREQ(e.what(), "ActionError: pmbus_read_sensor: { type: vout, "
284 "command: 0x8B, format: linear_16 }");
285 try
286 {
287 // Re-throw inner PMBusError
288 std::rethrow_if_nested(e);
289 ADD_FAILURE() << "Should not have reached this line.";
290 }
291 catch (const PMBusError& pe)
292 {
293 EXPECT_STREQ(
294 pe.what(),
295 "PMBusError: VOUT_MODE contains unsupported data format");
296 }
297 catch (...)
298 {
299 ADD_FAILURE() << "Should not have caught exception.";
300 }
301 }
302 catch (...)
303 {
304 ADD_FAILURE() << "Should not have caught exception.";
305 }
306
307 // Test where fails: Reading VOUT_MODE fails
308 try
309 {
310 // Create mock I2CInterface. Expect action to do the following:
311 // * will try to read VOUT_MODE; exception will be thrown
312 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
313 std::make_unique<i2c::MockedI2CInterface>();
314 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
Bob Kingab7d6cb2020-07-17 10:24:07 +0800315 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0xC6), A<uint16_t&>()))
316 .Times(1);
Bob King717d2da2020-06-02 11:11:15 +0800317 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x20), A<uint8_t&>()))
318 .Times(1)
319 .WillOnce(Throw(
320 i2c::I2CException{"Failed to read byte", "/dev/i2c-1", 0x70}));
321
322 // Create Device, IDMap, and ActionEnvironment
Bob Kinga76898f2020-10-13 15:08:33 +0800323 Device device{
324 "reg1", true,
325 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
326 std::move(i2cInterface)};
Bob King717d2da2020-06-02 11:11:15 +0800327 IDMap idMap{};
328 idMap.addDevice(device);
329 ActionEnvironment env{idMap, "reg1"};
330
331 // Create and execute action
332 pmbus_utils::SensorValueType type{
333 pmbus_utils::SensorValueType::vout_peak};
334 uint8_t command = 0xC6;
335 pmbus_utils::SensorDataFormat format{
336 pmbus_utils::SensorDataFormat::linear_16};
337 std::optional<int8_t> exponent{};
338 PMBusReadSensorAction action{type, command, format, exponent};
339 action.execute(env);
340 ADD_FAILURE() << "Should not have reached this line.";
341 }
342 catch (const ActionError& e)
343 {
344 EXPECT_STREQ(e.what(),
345 "ActionError: pmbus_read_sensor: { type: vout_peak, "
346 "command: 0xC6, format: linear_16 }");
347 try
348 {
349 // Re-throw inner I2CException
350 std::rethrow_if_nested(e);
351 ADD_FAILURE() << "Should not have reached this line.";
352 }
353 catch (const i2c::I2CException& ie)
354 {
355 EXPECT_STREQ(
356 ie.what(),
357 "I2CException: Failed to read byte: bus /dev/i2c-1, addr 0x70");
358 }
359 catch (...)
360 {
361 ADD_FAILURE() << "Should not have caught exception.";
362 }
363 }
364 catch (...)
365 {
366 ADD_FAILURE() << "Should not have caught exception.";
367 }
368
369 // Test where fails: Reading PMBus command code with sensor value fails
370 try
371 {
372 // Create mock I2CInterface. Expect action to do the following:
373 // * will try to read PMBus command(0x96); exception will be thrown
374 std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
375 std::make_unique<i2c::MockedI2CInterface>();
376 EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
377 EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x96), A<uint16_t&>()))
378 .Times(1)
379 .WillOnce(Throw(
380 i2c::I2CException{"Failed to read word", "/dev/i2c-1", 0x70}));
381
382 // Create Device, IDMap, and ActionEnvironment
Bob Kinga76898f2020-10-13 15:08:33 +0800383 Device device{
384 "reg1", true,
385 "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1",
386 std::move(i2cInterface)};
Bob King717d2da2020-06-02 11:11:15 +0800387 IDMap idMap{};
388 idMap.addDevice(device);
389 ActionEnvironment env{idMap, "reg1"};
390
391 // Create and execute action
392 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::pout};
393 uint8_t command = 0x96;
394 pmbus_utils::SensorDataFormat format{
395 pmbus_utils::SensorDataFormat::linear_11};
396 std::optional<int8_t> exponent{};
397 PMBusReadSensorAction action{type, command, format, exponent};
398 action.execute(env);
399 ADD_FAILURE() << "Should not have reached this line.";
400 }
401 catch (const ActionError& e)
402 {
403 EXPECT_STREQ(e.what(), "ActionError: pmbus_read_sensor: { type: pout, "
404 "command: 0x96, format: linear_11 }");
405 try
406 {
407 // Re-throw inner I2CException
408 std::rethrow_if_nested(e);
409 ADD_FAILURE() << "Should not have reached this line.";
410 }
411 catch (const i2c::I2CException& ie)
412 {
413 EXPECT_STREQ(
414 ie.what(),
415 "I2CException: Failed to read word: bus /dev/i2c-1, addr 0x70");
416 }
417 catch (...)
418 {
419 ADD_FAILURE() << "Should not have caught exception.";
420 }
421 }
422 catch (...)
423 {
424 ADD_FAILURE() << "Should not have caught exception.";
425 }
Bob Kingd6820bb2020-04-28 15:37:02 +0800426}
427
428TEST(PMBusReadSensorActionTests, GetCommand)
429{
430 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
431 uint8_t command = 0x8C;
432 pmbus_utils::SensorDataFormat format{
433 pmbus_utils::SensorDataFormat::linear_16};
434 std::optional<int8_t> exponent{-8};
435 PMBusReadSensorAction action{type, command, format, exponent};
436 EXPECT_EQ(action.getCommand(), 0x8C);
437}
438
439TEST(PMBusReadSensorActionTests, GetExponent)
440{
441 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
442 uint8_t command = 0x8C;
443 pmbus_utils::SensorDataFormat format{
444 pmbus_utils::SensorDataFormat::linear_16};
445
446 // Exponent value is specified
447 {
448 std::optional<int8_t> exponent{-9};
449 PMBusReadSensorAction action{type, command, format, exponent};
450 EXPECT_EQ(action.getExponent().has_value(), true);
451 EXPECT_EQ(action.getExponent().value(), -9);
452 }
453
454 // Exponent value is not specified
455 {
456 std::optional<int8_t> exponent{};
457 PMBusReadSensorAction action{type, command, format, exponent};
458 EXPECT_EQ(action.getExponent().has_value(), false);
459 }
460}
461
462TEST(PMBusReadSensorActionTests, GetFormat)
463{
464 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
465 uint8_t command = 0x8C;
466 pmbus_utils::SensorDataFormat format{
467 pmbus_utils::SensorDataFormat::linear_16};
468 std::optional<int8_t> exponent{-8};
469 PMBusReadSensorAction action{type, command, format, exponent};
470 EXPECT_EQ(action.getFormat(), pmbus_utils::SensorDataFormat::linear_16);
471}
472
473TEST(PMBusReadSensorActionTests, GetType)
474{
475 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::pout};
476 uint8_t command = 0x8C;
477 pmbus_utils::SensorDataFormat format{
478 pmbus_utils::SensorDataFormat::linear_16};
479 std::optional<int8_t> exponent{-8};
480 PMBusReadSensorAction action{type, command, format, exponent};
481 EXPECT_EQ(action.getType(), pmbus_utils::SensorValueType::pout);
482}
483
484TEST(PMBusReadSensorActionTests, ToString)
485{
486 // Test where exponent value is specified
487 {
Bob King717d2da2020-06-02 11:11:15 +0800488 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::vout};
489 uint8_t command = 0x8B;
Bob Kingd6820bb2020-04-28 15:37:02 +0800490 pmbus_utils::SensorDataFormat format{
491 pmbus_utils::SensorDataFormat::linear_16};
492 std::optional<int8_t> exponent{-8};
493 PMBusReadSensorAction action{type, command, format, exponent};
494 EXPECT_EQ(action.toString(), "pmbus_read_sensor: { type: "
Bob King717d2da2020-06-02 11:11:15 +0800495 "vout, command: 0x8B, format: "
Bob Kingd6820bb2020-04-28 15:37:02 +0800496 "linear_16, exponent: -8 }");
497 }
498
499 // Test where exponent value is not specified
500 {
Bob King717d2da2020-06-02 11:11:15 +0800501 pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
Bob Kingd6820bb2020-04-28 15:37:02 +0800502 uint8_t command = 0x8C;
503 pmbus_utils::SensorDataFormat format{
504 pmbus_utils::SensorDataFormat::linear_11};
505 std::optional<int8_t> exponent{};
506 PMBusReadSensorAction action{type, command, format, exponent};
Bob King717d2da2020-06-02 11:11:15 +0800507 EXPECT_EQ(action.toString(), "pmbus_read_sensor: { type: iout, "
Bob Kingd6820bb2020-04-28 15:37:02 +0800508 "command: 0x8C, format: linear_11 }");
509 }
510}