blob: 44a3ab5e942e8a5d3efb24ea47232f9eedb096df [file] [edit]
/*
* SPDX-FileCopyrightText: Copyright (c) 2023-2024 NVIDIA CORPORATION &
* AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0
*/
// Branch Coverage Tests for common/utils.cpp
// Focus: Bitfield operations, conversions, validations
#include "utils.hpp"
#include <fcntl.h>
#include <unistd.h>
#include <sdbusplus/exception.hpp>
#include <gtest/gtest.h>
using namespace utils;
// Branch Coverage Tests for isValidDbusString
// Note: isValidDbusString validates UTF-8 encoding, not DBus naming rules
TEST(CommonUtilsBranch, IsValidDbusStringEmptyString)
{
bool result = isValidDbusString("");
EXPECT_TRUE(result); // Empty string is valid UTF-8
}
TEST(CommonUtilsBranch, IsValidDbusStringStartsWithDigit)
{
bool result = isValidDbusString("1InvalidName");
EXPECT_TRUE(
result); // Valid UTF-8 (ASCII), function doesn't check naming rules
}
TEST(CommonUtilsBranch, IsValidDbusStringContainsInvalidChar)
{
bool result = isValidDbusString("Invalid-Name");
EXPECT_TRUE(
result); // Hyphen is valid UTF-8, function doesn't check naming rules
}
TEST(CommonUtilsBranch, IsValidDbusStringValidName)
{
bool result = isValidDbusString("ValidName123");
EXPECT_TRUE(result);
}
// Branch Coverage Tests for Bitfield256 class
TEST(CommonUtilsBranch, Bitfield256SetBitValid)
{
Bitfield256 bf;
bool result = bf.setBit(100);
EXPECT_TRUE(result);
EXPECT_TRUE(bf.isAnyBitSet());
}
TEST(CommonUtilsBranch, Bitfield256IsAnyBitSetEmpty)
{
Bitfield256 bf;
EXPECT_FALSE(bf.isAnyBitSet());
}
TEST(CommonUtilsBranch, Bitfield256IsAnyBitSetAfterSet)
{
Bitfield256 bf;
bf.setBit(50);
EXPECT_TRUE(bf.isAnyBitSet());
}
TEST(CommonUtilsBranch, Bitfield256ClearAfterSet)
{
Bitfield256 bf;
bf.setBit(10);
bf.setBit(20);
bf.clear();
EXPECT_FALSE(bf.isAnyBitSet());
}
TEST(CommonUtilsBranch, Bitfield256GetSetBitsEmpty)
{
Bitfield256 bf;
std::string result = bf.getSetBits();
EXPECT_TRUE(result.empty());
}
TEST(CommonUtilsBranch, Bitfield256GetSetBitsMultiple)
{
Bitfield256 bf;
bf.setBit(5);
bf.setBit(10);
std::string result = bf.getSetBits();
EXPECT_FALSE(result.empty());
}
// Branch Coverage Tests for double conversion functions
TEST(CommonUtilsBranch, ConvertAndScaleDownUint32ZeroValue)
{
double result = convertAndScaleDownUint32ToDouble(0, 100.0);
EXPECT_DOUBLE_EQ(result, 0.0);
}
TEST(CommonUtilsBranch, ConvertAndScaleDownUint32MaxValue)
{
double result = convertAndScaleDownUint32ToDouble(UINT32_MAX, 1.0);
EXPECT_DOUBLE_EQ(result, static_cast<double>(UINT32_MAX));
}
TEST(CommonUtilsBranch, ConvertAndScaleDownUint8ZeroValue)
{
double result = convertAndScaleDownUint8ToDouble(0);
EXPECT_DOUBLE_EQ(result, 0.0);
}
TEST(CommonUtilsBranch, Uint64ToDoubleSafeConvertMaxSafeValue)
{
uint64_t value = (1ULL << 53);
double result = uint64ToDoubleSafeConvert(value);
EXPECT_DOUBLE_EQ(result, static_cast<double>(value));
}
TEST(CommonUtilsBranch, Uint64ToDoubleSafeConvertExceedsMax)
{
uint64_t value = (1ULL << 54);
double result = uint64ToDoubleSafeConvert(value);
EXPECT_LE(result, static_cast<double>(1ULL << 53));
}
TEST(CommonUtilsBranch, Int64ToDoubleSafeConvertPositiveMax)
{
int64_t value = (1LL << 53);
double result = int64ToDoubleSafeConvert(value);
EXPECT_DOUBLE_EQ(result, static_cast<double>(value));
}
TEST(CommonUtilsBranch, Int64ToDoubleSafeConvertNegativeMin)
{
int64_t value = -(1LL << 53);
double result = int64ToDoubleSafeConvert(value);
// Use 1LL (signed) to get correct negative value; 1ULL would wrap around
EXPECT_DOUBLE_EQ(result, static_cast<double>(-((1LL << 53) - 1)));
}
TEST(CommonUtilsBranch, Int64ToDoubleSafeConvertExceedsPositiveMax)
{
int64_t value = (1LL << 54);
double result = int64ToDoubleSafeConvert(value);
EXPECT_LE(result, static_cast<double>(1LL << 53));
}
TEST(CommonUtilsBranch, Int64ToDoubleSafeConvertExceedsNegativeMin)
{
int64_t value = -(1LL << 54);
double result = int64ToDoubleSafeConvert(value);
EXPECT_GE(result, static_cast<double>(-(1LL << 53)));
}
// Branch Coverage Tests for combineDeviceTypeAndRole
// Note: Function combines as (role << 8) | type (role in high byte, type in low
// byte)
TEST(CommonUtilsBranch, CombineDeviceTypeAndRoleZeroValues)
{
uint16_t result = combineDeviceTypeAndRole(0, 0);
EXPECT_EQ(result, 0);
}
TEST(CommonUtilsBranch, CombineDeviceTypeAndRoleMaxValues)
{
uint16_t result = combineDeviceTypeAndRole(0xFF, 0xFF);
EXPECT_EQ(result, 0xFFFF);
}
TEST(CommonUtilsBranch, CombineDeviceTypeAndRoleMixedValues)
{
uint16_t result = combineDeviceTypeAndRole(0x12, 0x34);
EXPECT_EQ(result, 0x3412); // (0x34 << 8) | 0x12 = 0x3412
}
// Branch Coverage Tests for getDeviceTypeAndRole
// Note: Function extracts type from low byte, role from high byte
TEST(CommonUtilsBranch, GetDeviceTypeAndRoleValid)
{
uint8_t deviceType = 0;
uint8_t deviceRole = 0;
getDeviceTypeAndRole(0xABCD, &deviceType, &deviceRole);
EXPECT_EQ(deviceType, 0xCD); // Low byte
EXPECT_EQ(deviceRole, 0xAB); // High byte
}
TEST(CommonUtilsBranch, GetDeviceTypeAndRoleZeroCombined)
{
uint8_t deviceType = 0xFF;
uint8_t deviceRole = 0xFF;
getDeviceTypeAndRole(0, &deviceType, &deviceRole);
EXPECT_EQ(deviceType, 0);
EXPECT_EQ(deviceRole, 0);
}
// ============================================================================
// Branch coverage: readFdToBuffer / writeBufferToFd error paths
// ============================================================================
// Line 763: lseek fails on a pipe → readFdToBuffer throws
TEST(CommonUtilsBranch, ReadFdToBuffer_LseekFails_Throws)
{
int pipefd[2];
ASSERT_EQ(pipe(pipefd), 0);
std::vector<uint8_t> buffer;
EXPECT_THROW(utils::readFdToBuffer(pipefd[0], buffer), std::runtime_error);
close(pipefd[0]);
close(pipefd[1]);
}
// Lines 779-780: lseek OK (regular file), but read() returns EBADF on
// a write-only fd → readFdToBuffer throws
TEST(CommonUtilsBranch, ReadFdToBuffer_ReadFails_Throws)
{
char tmpPath[] = "/tmp/utils_branch_test_XXXXXX";
int tmpFd = mkstemp(tmpPath);
ASSERT_GE(tmpFd, 0);
const uint8_t data[] = {0xAA, 0xBB, 0xCC};
ASSERT_EQ(::write(tmpFd, data, sizeof(data)), (ssize_t)sizeof(data));
close(tmpFd);
// Reopen write-only: lseek and fstat succeed, but read() fails with EBADF
int fd = open(tmpPath, O_WRONLY);
unlink(tmpPath);
ASSERT_GE(fd, 0);
std::vector<uint8_t> buffer;
EXPECT_THROW(utils::readFdToBuffer(fd, buffer), std::runtime_error);
close(fd);
}
// Line 797: lseek fails on a pipe → writeBufferToFd throws
TEST(CommonUtilsBranch, WriteBufferToFd_LseekFails_Throws)
{
int pipefd[2];
ASSERT_EQ(pipe(pipefd), 0);
std::vector<uint8_t> buffer = {1, 2, 3};
EXPECT_THROW(utils::writeBufferToFd(pipefd[1], buffer), std::runtime_error);
close(pipefd[0]);
close(pipefd[1]);
}
// Lines 806-807: lseek OK (regular file), but write() returns EBADF on
// a read-only fd with non-empty buffer → writeBufferToFd throws
TEST(CommonUtilsBranch, WriteBufferToFd_WriteFails_Throws)
{
char tmpPath[] = "/tmp/utils_branch_write_XXXXXX";
int tmpFd = mkstemp(tmpPath);
ASSERT_GE(tmpFd, 0);
close(tmpFd);
// Reopen read-only: lseek succeeds, write() fails with EBADF
int fd = open(tmpPath, O_RDONLY);
unlink(tmpPath);
ASSERT_GE(fd, 0);
std::vector<uint8_t> buffer = {1, 2, 3};
EXPECT_THROW(utils::writeBufferToFd(fd, buffer), std::runtime_error);
close(fd);
}
// Test for IDBusHandler::tryGetDbusProperty catch block (L269-271 in utils.hpp)
// Create a concrete subclass of IDBusHandler that throws sdbusplus::exception
// from getDbusPropertyVariant to cover the catch block.
struct ThrowingSdBusHandler : public IDBusHandler
{
std::string getService(const char*, const char*) const override
{
return "";
}
MapperServiceMap getServiceMap(const char*,
const dbus::Interfaces&) const override
{
return {};
}
GetSubTreeResponse getSubtree(const std::string&, int,
const dbus::Interfaces&) const override
{
return {};
}
void setDbusProperty(const DBusMapping&,
const PropertyValue&) const override
{}
PropertyValue getDbusPropertyVariant(const char*, const char*,
const char*) const override
{
// Throw a concrete sdbusplus exception to exercise the catch block
// in tryGetDbusProperty
throw sdbusplus::exception::InvalidEnumString{};
}
PropertyValuesCollection getDbusProperties(const char*,
const char*) const override
{
return {};
}
GetAssociatedObjectsResponse
getAssociatedObjects(const std::string&,
const std::string&) const override
{
return {};
}
};
// L269-271: tryGetDbusProperty catch block
// When getDbusPropertyVariant throws sdbusplus::exception, returns defValue
TEST(CommonUtilsBranch, TryGetDbusProperty_SdBusException_ReturnsDefault)
{
ThrowingSdBusHandler handler;
const std::string defValue = "default";
auto result = handler.tryGetDbusProperty<std::string>("path", "prop",
"intf", defValue);
EXPECT_EQ(result, defValue);
}