blob: d24a04adad621e1f2687891e5a5c747f53649864 [file] [log] [blame]
/**
* Copyright © 2020 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "action_environment.hpp"
#include "action_error.hpp"
#include "device.hpp"
#include "i2c_action.hpp"
#include "i2c_interface.hpp"
#include "id_map.hpp"
#include "mocked_i2c_interface.hpp"
#include "pmbus_error.hpp"
#include "pmbus_read_sensor_action.hpp"
#include "pmbus_utils.hpp"
#include <cstdint>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <utility>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using namespace phosphor::power::regulators;
using ::testing::A;
using ::testing::Return;
using ::testing::SetArgReferee;
using ::testing::Throw;
using ::testing::TypedEq;
TEST(PMBusReadSensorActionTests, Constructor)
{
// Test where works: exponent value is specified
try
{
pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
uint8_t command = 0x8C;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_16};
std::optional<int8_t> exponent{-8};
PMBusReadSensorAction action{type, command, format, exponent};
EXPECT_EQ(action.getType(), pmbus_utils::SensorValueType::iout);
EXPECT_EQ(action.getCommand(), 0x8C);
EXPECT_EQ(action.getFormat(), pmbus_utils::SensorDataFormat::linear_16);
EXPECT_EQ(action.getExponent().has_value(), true);
EXPECT_EQ(action.getExponent().value(), -8);
}
catch (...)
{
ADD_FAILURE() << "Should not have caught exception.";
}
// Test where works: exponent value is not specified
try
{
pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
uint8_t command = 0x8C;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_11};
std::optional<int8_t> exponent{};
PMBusReadSensorAction action{type, command, format, exponent};
EXPECT_EQ(action.getType(), pmbus_utils::SensorValueType::iout);
EXPECT_EQ(action.getCommand(), 0x8C);
EXPECT_EQ(action.getFormat(), pmbus_utils::SensorDataFormat::linear_11);
EXPECT_EQ(action.getExponent().has_value(), false);
}
catch (...)
{
ADD_FAILURE() << "Should not have caught exception.";
}
}
TEST(PMBusReadSensorActionTests, Execute)
{
// Test where works: linear_11 defined in action
try
{
// Create mock I2CInterface.
// * will read 0xD2E0 from READ_IOUT (command/register 0x8C)
// * will not read from VOUT_MODE (command/register 0x20)
// assume output current is 11.5 amps,
// exponent = -6 = 11010, mantissa = 736 = 010 1110 0000
// linear data format = 1101 0010 1110 0000 = 0xD2E0
std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
std::make_unique<i2c::MockedI2CInterface>();
EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8C), A<uint16_t&>()))
.Times(1)
.WillOnce(SetArgReferee<1>(0xD2E0));
EXPECT_CALL(*i2cInterface, read(A<uint8_t>(), A<uint8_t&>())).Times(0);
// Create Device, IDMap, and ActionEnvironment
Device device{"reg1", true, "/system/chassis/motherboard/reg1",
std::move(i2cInterface)};
IDMap idMap{};
idMap.addDevice(device);
ActionEnvironment env{idMap, "reg1"};
// Create and execute action
pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
uint8_t command = 0x8C;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_11};
std::optional<int8_t> exponent{};
PMBusReadSensorAction action{type, command, format, exponent};
EXPECT_EQ(action.execute(env), true);
EXPECT_EQ(env.getSensorReadings().size(), 1);
EXPECT_EQ(env.getSensorReadings()[0].type,
pmbus_utils::SensorValueType::iout);
EXPECT_DOUBLE_EQ(env.getSensorReadings()[0].value, 11.5);
}
catch (...)
{
ADD_FAILURE() << "Should not have caught exception.";
}
// Test where works: linear_16 with exponent defined in action
try
{
// Create mock I2CInterface.
// * will read 0x0002 from READ_VOUT (command/register 0x8B)
// * will not read from VOUT_MODE (command/register 0x20)
// assume output voltage is 16 volts,
// exponent = 3
// linear data format = 0000 0000 0000 0010 = 0x0002 = 2
std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
std::make_unique<i2c::MockedI2CInterface>();
EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8B), A<uint16_t&>()))
.Times(1)
.WillOnce(SetArgReferee<1>(0x0002));
EXPECT_CALL(*i2cInterface, read(A<uint8_t>(), A<uint8_t&>())).Times(0);
// Create Device, IDMap, and ActionEnvironment
Device device{"reg1", true, "/system/chassis/motherboard/reg1",
std::move(i2cInterface)};
IDMap idMap{};
idMap.addDevice(device);
ActionEnvironment env{idMap, "reg1"};
// Create and execute action
pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::vout};
uint8_t command = 0x8B;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_16};
std::optional<int8_t> exponent{3};
PMBusReadSensorAction action{type, command, format, exponent};
EXPECT_EQ(action.execute(env), true);
EXPECT_EQ(env.getSensorReadings().size(), 1);
EXPECT_EQ(env.getSensorReadings()[0].type,
pmbus_utils::SensorValueType::vout);
EXPECT_DOUBLE_EQ(env.getSensorReadings()[0].value, 16);
}
catch (...)
{
ADD_FAILURE() << "Should not have caught exception.";
}
// Test where works: linear_16 with no exponent defined in action
try
{
// Create mock I2CInterface.
// * will read 0xB877 from vout_peak (command/register 0xC6)
// * will read 0b0001'0111 (linear format, -9 exponent) from VOUT_MODE
// assume output voltage is 0.232421875 volts,
// linear data format = 0000 0000 0111 0111 = 0x0077
std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
std::make_unique<i2c::MockedI2CInterface>();
EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0xC6), A<uint16_t&>()))
.Times(1)
.WillOnce(SetArgReferee<1>(0x0077));
EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x20), A<uint8_t&>()))
.Times(1)
.WillOnce(SetArgReferee<1>(0b0001'0111));
// Create Device, IDMap, and ActionEnvironment
Device device{"reg1", true, "/system/chassis/motherboard/reg1",
std::move(i2cInterface)};
IDMap idMap{};
idMap.addDevice(device);
ActionEnvironment env{idMap, "reg1"};
// Create and execute action
pmbus_utils::SensorValueType type{
pmbus_utils::SensorValueType::vout_peak};
uint8_t command = 0xC6;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_16};
std::optional<int8_t> exponent{};
PMBusReadSensorAction action{type, command, format, exponent};
EXPECT_EQ(action.execute(env), true);
EXPECT_EQ(env.getSensorReadings().size(), 1);
EXPECT_EQ(env.getSensorReadings()[0].type,
pmbus_utils::SensorValueType::vout_peak);
EXPECT_DOUBLE_EQ(env.getSensorReadings()[0].value, 0.232421875);
}
catch (...)
{
ADD_FAILURE() << "Should not have caught exception.";
}
// Test where fails: Unable to get I2C interface to current device
try
{
// Create IDMap and ActionEnvironment
IDMap idMap{};
ActionEnvironment env{idMap, "reg1"};
// Create and execute action
pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::pout};
uint8_t command = 0x96;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_11};
std::optional<int8_t> exponent{};
PMBusReadSensorAction action{type, command, format, exponent};
action.execute(env);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
{
EXPECT_STREQ(e.what(), "Unable to find device with ID \"reg1\"");
}
catch (...)
{
ADD_FAILURE() << "Should not have caught exception.";
}
// Test where fails: VOUT_MODE data format is not linear
try
{
// Create mock I2CInterface. Expect action to do the following:
// * will read 0b0010'0000 (vid data format) from VOUT_MODE
std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
std::make_unique<i2c::MockedI2CInterface>();
EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x8B), A<uint16_t&>()))
.Times(1);
EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x20), A<uint8_t&>()))
.Times(1)
.WillOnce(SetArgReferee<1>(0b0010'0000));
// Create Device, IDMap, and ActionEnvironment
Device device{"reg1", true, "/system/chassis/motherboard/reg1",
std::move(i2cInterface)};
IDMap idMap{};
idMap.addDevice(device);
ActionEnvironment env{idMap, "reg1"};
// Create and execute action
pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::vout};
uint8_t command = 0x8B;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_16};
std::optional<int8_t> exponent{};
PMBusReadSensorAction action{type, command, format, exponent};
action.execute(env);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const ActionError& e)
{
EXPECT_STREQ(e.what(), "ActionError: pmbus_read_sensor: { type: vout, "
"command: 0x8B, format: linear_16 }");
try
{
// Re-throw inner PMBusError
std::rethrow_if_nested(e);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const PMBusError& pe)
{
EXPECT_STREQ(
pe.what(),
"PMBusError: VOUT_MODE contains unsupported data format");
}
catch (...)
{
ADD_FAILURE() << "Should not have caught exception.";
}
}
catch (...)
{
ADD_FAILURE() << "Should not have caught exception.";
}
// Test where fails: Reading VOUT_MODE fails
try
{
// Create mock I2CInterface. Expect action to do the following:
// * will try to read VOUT_MODE; exception will be thrown
std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
std::make_unique<i2c::MockedI2CInterface>();
EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0xC6), A<uint16_t&>()))
.Times(1);
EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x20), A<uint8_t&>()))
.Times(1)
.WillOnce(Throw(
i2c::I2CException{"Failed to read byte", "/dev/i2c-1", 0x70}));
// Create Device, IDMap, and ActionEnvironment
Device device{"reg1", true, "/system/chassis/motherboard/reg1",
std::move(i2cInterface)};
IDMap idMap{};
idMap.addDevice(device);
ActionEnvironment env{idMap, "reg1"};
// Create and execute action
pmbus_utils::SensorValueType type{
pmbus_utils::SensorValueType::vout_peak};
uint8_t command = 0xC6;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_16};
std::optional<int8_t> exponent{};
PMBusReadSensorAction action{type, command, format, exponent};
action.execute(env);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const ActionError& e)
{
EXPECT_STREQ(e.what(),
"ActionError: pmbus_read_sensor: { type: vout_peak, "
"command: 0xC6, format: linear_16 }");
try
{
// Re-throw inner I2CException
std::rethrow_if_nested(e);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const i2c::I2CException& ie)
{
EXPECT_STREQ(
ie.what(),
"I2CException: Failed to read byte: bus /dev/i2c-1, addr 0x70");
}
catch (...)
{
ADD_FAILURE() << "Should not have caught exception.";
}
}
catch (...)
{
ADD_FAILURE() << "Should not have caught exception.";
}
// Test where fails: Reading PMBus command code with sensor value fails
try
{
// Create mock I2CInterface. Expect action to do the following:
// * will try to read PMBus command(0x96); exception will be thrown
std::unique_ptr<i2c::MockedI2CInterface> i2cInterface =
std::make_unique<i2c::MockedI2CInterface>();
EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true));
EXPECT_CALL(*i2cInterface, read(TypedEq<uint8_t>(0x96), A<uint16_t&>()))
.Times(1)
.WillOnce(Throw(
i2c::I2CException{"Failed to read word", "/dev/i2c-1", 0x70}));
// Create Device, IDMap, and ActionEnvironment
Device device{"reg1", true, "/system/chassis/motherboard/reg1",
std::move(i2cInterface)};
IDMap idMap{};
idMap.addDevice(device);
ActionEnvironment env{idMap, "reg1"};
// Create and execute action
pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::pout};
uint8_t command = 0x96;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_11};
std::optional<int8_t> exponent{};
PMBusReadSensorAction action{type, command, format, exponent};
action.execute(env);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const ActionError& e)
{
EXPECT_STREQ(e.what(), "ActionError: pmbus_read_sensor: { type: pout, "
"command: 0x96, format: linear_11 }");
try
{
// Re-throw inner I2CException
std::rethrow_if_nested(e);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const i2c::I2CException& ie)
{
EXPECT_STREQ(
ie.what(),
"I2CException: Failed to read word: bus /dev/i2c-1, addr 0x70");
}
catch (...)
{
ADD_FAILURE() << "Should not have caught exception.";
}
}
catch (...)
{
ADD_FAILURE() << "Should not have caught exception.";
}
}
TEST(PMBusReadSensorActionTests, GetCommand)
{
pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
uint8_t command = 0x8C;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_16};
std::optional<int8_t> exponent{-8};
PMBusReadSensorAction action{type, command, format, exponent};
EXPECT_EQ(action.getCommand(), 0x8C);
}
TEST(PMBusReadSensorActionTests, GetExponent)
{
pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
uint8_t command = 0x8C;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_16};
// Exponent value is specified
{
std::optional<int8_t> exponent{-9};
PMBusReadSensorAction action{type, command, format, exponent};
EXPECT_EQ(action.getExponent().has_value(), true);
EXPECT_EQ(action.getExponent().value(), -9);
}
// Exponent value is not specified
{
std::optional<int8_t> exponent{};
PMBusReadSensorAction action{type, command, format, exponent};
EXPECT_EQ(action.getExponent().has_value(), false);
}
}
TEST(PMBusReadSensorActionTests, GetFormat)
{
pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
uint8_t command = 0x8C;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_16};
std::optional<int8_t> exponent{-8};
PMBusReadSensorAction action{type, command, format, exponent};
EXPECT_EQ(action.getFormat(), pmbus_utils::SensorDataFormat::linear_16);
}
TEST(PMBusReadSensorActionTests, GetType)
{
pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::pout};
uint8_t command = 0x8C;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_16};
std::optional<int8_t> exponent{-8};
PMBusReadSensorAction action{type, command, format, exponent};
EXPECT_EQ(action.getType(), pmbus_utils::SensorValueType::pout);
}
TEST(PMBusReadSensorActionTests, ToString)
{
// Test where exponent value is specified
{
pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::vout};
uint8_t command = 0x8B;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_16};
std::optional<int8_t> exponent{-8};
PMBusReadSensorAction action{type, command, format, exponent};
EXPECT_EQ(action.toString(), "pmbus_read_sensor: { type: "
"vout, command: 0x8B, format: "
"linear_16, exponent: -8 }");
}
// Test where exponent value is not specified
{
pmbus_utils::SensorValueType type{pmbus_utils::SensorValueType::iout};
uint8_t command = 0x8C;
pmbus_utils::SensorDataFormat format{
pmbus_utils::SensorDataFormat::linear_11};
std::optional<int8_t> exponent{};
PMBusReadSensorAction action{type, command, format, exponent};
EXPECT_EQ(action.toString(), "pmbus_read_sensor: { type: iout, "
"command: 0x8C, format: linear_11 }");
}
}