blob: c5c99489fbd539e76c28615b36042707d70402ca [file] [edit]
/*
* SPDX-FileCopyrightText: Copyright OpenBMC Authors
* SPDX-License-Identifier: Apache-2.0
*/
#include "MctpMockTestBase.hpp"
#include "MockMctpRequester.hpp"
#include "NvidiaGpuMctpVdm.hpp"
#include "NvidiaSmaLeakSensor.hpp"
#include "OcpMctpVdm.hpp"
#include "TestUtils.hpp"
#include "Thresholds.hpp"
#include <MessagePackUnpackUtils.hpp>
#include <cmath>
#include <cstdint>
#include <memory>
#include <string>
#include <system_error>
#include <utility>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace
{
std::vector<uint8_t> buildLeakDetectionInfoResponse(uint16_t adcReadingMv)
{
// dataSize = numSensors(1) + numThresholdLevels(1) +
// numSensors * (sensorId(1) + leakState(1) +
// thresholds(2*numThresholdLevels) + adcReadingMv(2))
// We mock 1 sensor with 0 thresholds
uint16_t dataSize = 1 + 1 + 1 * (1 + 1 + 0 + 2);
std::vector<uint8_t> buf(
ocp::accelerator_management::commonResponseSize + dataSize);
PackBuffer pack(buf);
ocp::accelerator_management::packHeader(
pack, gpu::nvidiaPciVendorId,
ocp::accelerator_management::MessageType::RESPONSE, 0,
static_cast<uint8_t>(gpu::MessageType::PLATFORM_ENVIRONMENTAL));
pack.pack(static_cast<uint8_t>(
gpu::PlatformEnvironmentalCommands::GET_LEAK_DETECTION_INFO));
pack.pack(static_cast<uint8_t>(
ocp::accelerator_management::CompletionCode::SUCCESS));
pack.pack(static_cast<uint16_t>(0)); // reserved
pack.pack(dataSize);
// payload
pack.pack(static_cast<uint8_t>(1)); // numSensors
pack.pack(static_cast<uint8_t>(0)); // numThresholdLevels
// sensor 1
pack.pack(static_cast<uint8_t>(0)); // sensorId
pack.pack(static_cast<uint8_t>(0)); // leakState
pack.pack(adcReadingMv); // adcReadingMv
return buf;
}
std::vector<uint8_t> buildLeakDetectionInfoEmptyResponse()
{
// dataSize = numSensors(1) + numThresholdLevels(1)
// We mock 0 sensors
uint16_t dataSize = 1 + 1;
std::vector<uint8_t> buf(
ocp::accelerator_management::commonResponseSize + dataSize);
PackBuffer pack(buf);
ocp::accelerator_management::packHeader(
pack, gpu::nvidiaPciVendorId,
ocp::accelerator_management::MessageType::RESPONSE, 0,
static_cast<uint8_t>(gpu::MessageType::PLATFORM_ENVIRONMENTAL));
pack.pack(static_cast<uint8_t>(
gpu::PlatformEnvironmentalCommands::GET_LEAK_DETECTION_INFO));
pack.pack(static_cast<uint8_t>(
ocp::accelerator_management::CompletionCode::SUCCESS));
pack.pack(static_cast<uint16_t>(0)); // reserved
pack.pack(dataSize);
// payload
pack.pack(static_cast<uint8_t>(0)); // numSensors
pack.pack(static_cast<uint8_t>(0)); // numThresholdLevels
return buf;
}
std::vector<uint8_t> buildLeakErrorResponse(uint8_t cc, uint16_t reasonCode)
{
return test_utils::buildPlatformEnvErrorResponse(
gpu::PlatformEnvironmentalCommands::GET_LEAK_DETECTION_INFO, cc,
reasonCode);
}
class NvidiaSmaLeakSensorTest : public MctpMockTestBase
{
protected:
static std::shared_ptr<NvidiaSmaLeakSensor> createSensor(
const std::string& name = "GPU_LEAK_0",
std::vector<thresholds::Threshold> thresholds = {},
gpu::DeviceIdentification deviceType =
gpu::DeviceIdentification::DEVICE_GPU,
uint8_t eid = test_utils::defaultEid, uint8_t sensorId = 0)
{
return std::make_shared<NvidiaSmaLeakSensor>(
bus(), requester(), name, "/test/config", eid, sensorId, objects(),
std::move(thresholds), deviceType);
}
static std::string leakPath(const std::string& name)
{
// sensor path prefix in NvidiaSmaLeakSensor.cpp is
// "/xyz/openbmc_project/sensors/" and it appends "voltage/" +
// escapeName(name)
return "/xyz/openbmc_project/sensors/voltage/" + name;
}
};
// Constructor — D-Bus interface creation
TEST_F(NvidiaSmaLeakSensorTest, ConstructorCreatesDbusInterfaces)
{
const std::string name = "leak_ctor_iface";
const std::shared_ptr<NvidiaSmaLeakSensor> sensor = createSensor(name);
const std::string path = leakPath(name);
EXPECT_TRUE(std::isnan(getProperty<double>(
path, "xyz.openbmc_project.Sensor.Value", "Value")));
EXPECT_EQ(getProperty<double>(path, "xyz.openbmc_project.Sensor.Value",
"MaxValue"),
5.0);
EXPECT_EQ(getProperty<double>(path, "xyz.openbmc_project.Sensor.Value",
"MinValue"),
0.0);
EXPECT_EQ(getProperty<std::string>(path, "xyz.openbmc_project.Sensor.Value",
"Unit"),
"xyz.openbmc_project.Sensor.Value.Unit.Volts");
}
// Constructor — threshold interfaces
TEST_F(NvidiaSmaLeakSensorTest, ConstructorWithWarningThreshold)
{
const std::string name = "warn_thr";
const std::shared_ptr<NvidiaSmaLeakSensor> sensor = createSensor(
name, {thresholds::Threshold{thresholds::Level::WARNING,
thresholds::Direction::HIGH, 1.7}});
const std::string path = leakPath(name);
EXPECT_EQ(getProperty<double>(
path, "xyz.openbmc_project.Sensor.Threshold.Warning",
"WarningHigh"),
1.7);
}
TEST_F(NvidiaSmaLeakSensorTest, ConstructorWithCriticalThreshold)
{
const std::string name = "crit_thr";
const std::shared_ptr<NvidiaSmaLeakSensor> sensor = createSensor(
name, {thresholds::Threshold{thresholds::Level::CRITICAL,
thresholds::Direction::HIGH, 1.8}});
const std::string path = leakPath(name);
EXPECT_EQ(getProperty<double>(
path, "xyz.openbmc_project.Sensor.Threshold.Critical",
"CriticalHigh"),
1.8);
}
TEST_F(NvidiaSmaLeakSensorTest, ConstructorWithMultipleThresholds)
{
const std::string name = "multi_thr";
const std::shared_ptr<NvidiaSmaLeakSensor> sensor = createSensor(
name, {thresholds::Threshold{thresholds::Level::WARNING,
thresholds::Direction::HIGH, 1.7},
thresholds::Threshold{thresholds::Level::CRITICAL,
thresholds::Direction::HIGH, 1.8}});
const std::string path = leakPath(name);
EXPECT_EQ(getProperty<double>(
path, "xyz.openbmc_project.Sensor.Threshold.Warning",
"WarningHigh"),
1.7);
EXPECT_EQ(getProperty<double>(
path, "xyz.openbmc_project.Sensor.Threshold.Critical",
"CriticalHigh"),
1.8);
}
// Sensor Update — successful readings
TEST_F(NvidiaSmaLeakSensorTest, UpdateSuccessValidReading)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce(
mock_mctp::respondWith({}, buildLeakDetectionInfoResponse(1500)));
const std::shared_ptr<NvidiaSmaLeakSensor> sensor =
createSensor("leak_valid_0");
sensor->update();
EXPECT_DOUBLE_EQ(
getProperty<double>(leakPath("leak_valid_0"),
"xyz.openbmc_project.Sensor.Value", "Value"),
1.5);
}
TEST_F(NvidiaSmaLeakSensorTest, UpdateSuccessZeroReading)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce(
mock_mctp::respondWith({}, buildLeakDetectionInfoResponse(0)));
const std::shared_ptr<NvidiaSmaLeakSensor> sensor =
createSensor("leak_zero_0");
sensor->update();
EXPECT_DOUBLE_EQ(
getProperty<double>(leakPath("leak_zero_0"),
"xyz.openbmc_project.Sensor.Value", "Value"),
0.0);
}
TEST_F(NvidiaSmaLeakSensorTest, UpdateSuccessEmptySensors)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce(
mock_mctp::respondWith({}, buildLeakDetectionInfoEmptyResponse()));
const std::shared_ptr<NvidiaSmaLeakSensor> sensor =
createSensor("leak_empty_0");
sensor->update();
// With empty response, the sensor retains its initial NaN value
EXPECT_TRUE(std::isnan(
getProperty<double>(leakPath("leak_empty_0"),
"xyz.openbmc_project.Sensor.Value", "Value")));
}
// Sensor Update — threshold crossed reading
TEST_F(NvidiaSmaLeakSensorTest, UpdateWarningThresholdCrossed)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce(
mock_mctp::respondWith({}, buildLeakDetectionInfoResponse(1000)));
const std::string name = "warn_thr_cros_0";
const std::shared_ptr<NvidiaSmaLeakSensor> sensor = createSensor(
name, {thresholds::Threshold{thresholds::Level::WARNING,
thresholds::Direction::LOW, 1.5}});
sensor->update();
EXPECT_DOUBLE_EQ(
getProperty<double>(leakPath(name), "xyz.openbmc_project.Sensor.Value",
"Value"),
1.0);
// Verify the dbus alarm property was asserted
EXPECT_TRUE(getProperty<bool>(
leakPath(name), "xyz.openbmc_project.Sensor.Threshold.Warning",
"WarningAlarmLow"));
}
TEST_F(NvidiaSmaLeakSensorTest, UpdateCriticalThresholdCrossed)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce(
mock_mctp::respondWith({}, buildLeakDetectionInfoResponse(1000)));
const std::string name = "crit_thr_cros_0";
const std::shared_ptr<NvidiaSmaLeakSensor> sensor = createSensor(
name, {thresholds::Threshold{thresholds::Level::CRITICAL,
thresholds::Direction::LOW, 1.2}});
sensor->update();
EXPECT_DOUBLE_EQ(
getProperty<double>(leakPath(name), "xyz.openbmc_project.Sensor.Value",
"Value"),
1.0);
// Verify the dbus alarm property was asserted
EXPECT_TRUE(getProperty<bool>(
leakPath(name), "xyz.openbmc_project.Sensor.Threshold.Critical",
"CriticalAlarmLow"));
}
TEST_F(NvidiaSmaLeakSensorTest, UpdateMultipleThresholdsCrossed)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce(
mock_mctp::respondWith({}, buildLeakDetectionInfoResponse(1000)));
const std::string name = "mult_thr_cros_0";
const std::shared_ptr<NvidiaSmaLeakSensor> sensor = createSensor(
name, {thresholds::Threshold{thresholds::Level::WARNING,
thresholds::Direction::LOW, 1.5},
thresholds::Threshold{thresholds::Level::CRITICAL,
thresholds::Direction::LOW, 1.2}});
sensor->update();
EXPECT_DOUBLE_EQ(
getProperty<double>(leakPath(name), "xyz.openbmc_project.Sensor.Value",
"Value"),
1.0);
// Verify the dbus alarm property was asserted
EXPECT_TRUE(getProperty<bool>(
leakPath(name), "xyz.openbmc_project.Sensor.Threshold.Critical",
"CriticalAlarmLow"));
EXPECT_TRUE(getProperty<bool>(
leakPath(name), "xyz.openbmc_project.Sensor.Threshold.Warning",
"WarningAlarmLow"));
}
// Sensor Update — error handling
TEST_F(NvidiaSmaLeakSensorTest, UpdateMctpTransportError)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce(mock_mctp::respondWith(
std::make_error_code(std::errc::timed_out), {}));
const std::shared_ptr<NvidiaSmaLeakSensor> sensor =
createSensor("leak_mctp_err_0");
sensor->update();
EXPECT_TRUE(std::isnan(
getProperty<double>(leakPath("leak_mctp_err_0"),
"xyz.openbmc_project.Sensor.Value", "Value")));
}
TEST_F(NvidiaSmaLeakSensorTest, UpdateBadCompletionCodeError)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce(mock_mctp::respondWith(
{}, buildLeakErrorResponse(
static_cast<uint8_t>(
ocp::accelerator_management::CompletionCode::ERROR),
0)));
const std::shared_ptr<NvidiaSmaLeakSensor> sensor =
createSensor("leak_bad_cc_0");
sensor->update();
EXPECT_TRUE(std::isnan(
getProperty<double>(leakPath("leak_bad_cc_0"),
"xyz.openbmc_project.Sensor.Value", "Value")));
}
// Destructor
TEST_F(NvidiaSmaLeakSensorTest, DestructorDoesNotCrash)
{
std::shared_ptr<NvidiaSmaLeakSensor> sensor = createSensor("leak_dtor");
ASSERT_NE(sensor, nullptr);
ASSERT_NE(sensor->sensorInterface, nullptr);
sensor.reset();
}
} // namespace