blob: 192bdcfcc3f410e658b2ac52953f830c1d105a43 [file] [edit]
/*
* SPDX-FileCopyrightText: Copyright (c) 2023-2024 NVIDIA CORPORATION &
* AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0
*
* 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 "../utils.hpp"
#include <gtest/gtest.h>
// Tests for isPreferred function
// Note: Lower priority number = higher preference (0=best, 6=worst)
// isPreferred returns true when current >= new in priority number,
// meaning current is worse or equal (confusing naming!)
TEST(UtilsTest, IsPreferredSameMediumDifferentBinding)
{
std::tuple<MctpMedium, MctpBinding> current = {
"xyz.openbmc_project.MCTP.Endpoint.MediaTypes.PCIe",
"xyz.openbmc_project.MCTP.Binding.BindingTypes.PCIe"};
std::tuple<MctpMedium, MctpBinding> newInfo = {
"xyz.openbmc_project.MCTP.Endpoint.MediaTypes.PCIe",
"xyz.openbmc_project.MCTP.Binding.BindingTypes.USB"};
// Current binding PCIe(0) < USB(1), so current has lower number = better
// Function returns false when current is better
EXPECT_FALSE(utils::isPreferred(current, newInfo));
}
TEST(UtilsTest, IsPreferredDifferentMediumSameBinding)
{
std::tuple<MctpMedium, MctpBinding> current = {
"xyz.openbmc_project.MCTP.Endpoint.MediaTypes.USB",
"xyz.openbmc_project.MCTP.Binding.BindingTypes.USB"};
std::tuple<MctpMedium, MctpBinding> newInfo = {
"xyz.openbmc_project.MCTP.Endpoint.MediaTypes.PCIe",
"xyz.openbmc_project.MCTP.Binding.BindingTypes.USB"};
// Current medium USB(1) > PCIe(0), so 1 >= 0 = true
// Current is worse, function returns true
EXPECT_TRUE(utils::isPreferred(current, newInfo));
}
TEST(UtilsTest, IsPreferredIdenticalInfo)
{
std::tuple<MctpMedium, MctpBinding> info = {
"xyz.openbmc_project.MCTP.Endpoint.MediaTypes.PCIe",
"xyz.openbmc_project.MCTP.Binding.BindingTypes.PCIe"};
// Identical info: 0 >= 0 = true
EXPECT_TRUE(utils::isPreferred(info, info));
}
TEST(UtilsTest, IsPreferredLowerPriorityMedium)
{
std::tuple<MctpMedium, MctpBinding> current = {
"xyz.openbmc_project.MCTP.Endpoint.MediaTypes.SMBus",
"xyz.openbmc_project.MCTP.Binding.BindingTypes.PCIe"};
std::tuple<MctpMedium, MctpBinding> newInfo = {
"xyz.openbmc_project.MCTP.Endpoint.MediaTypes.PCIe",
"xyz.openbmc_project.MCTP.Binding.BindingTypes.SMBus"};
// Current medium SMBus(6) > PCIe(0), so 6 >= 0 = true
// Current is worse, function returns true
EXPECT_TRUE(utils::isPreferred(current, newInfo));
}
// Tests for convertHexToString function
TEST(UtilsTest, ConvertHexToStringEmptyData)
{
std::vector<uint8_t> data;
auto result = utils::convertHexToString(data, 0);
EXPECT_EQ(result, "");
}
TEST(UtilsTest, ConvertHexToStringSingleByte)
{
std::vector<uint8_t> data = {0xAB};
auto result = utils::convertHexToString(data, 1);
EXPECT_EQ(result, "ab");
}
TEST(UtilsTest, ConvertHexToStringMultipleBytes)
{
std::vector<uint8_t> data = {0x01, 0x23, 0x45, 0x67,
0x89, 0xAB, 0xCD, 0xEF};
auto result = utils::convertHexToString(data, 8);
EXPECT_EQ(result, "0123456789abcdef");
}
TEST(UtilsTest, ConvertHexToStringZeroPadding)
{
std::vector<uint8_t> data = {0x00, 0x0F, 0xF0, 0xFF};
auto result = utils::convertHexToString(data, 4);
EXPECT_EQ(result, "000ff0ff");
}
// Tests for convertMsgToString function
TEST(UtilsTest, ConvertMsgToStringEmptyBuffer)
{
std::vector<uint8_t> buffer;
auto result = utils::convertMsgToString(true, buffer, 0x03, 0x1D);
EXPECT_EQ(result, "");
}
TEST(UtilsTest, ConvertMsgToStringTxSingleByte)
{
std::vector<uint8_t> buffer = {0xAB};
auto result = utils::convertMsgToString(true, buffer, 0x03, 0x1D);
EXPECT_NE(result.find("EID: 1d"), std::string::npos);
EXPECT_NE(result.find("TAG: 03"), std::string::npos);
EXPECT_NE(result.find("Tx:"), std::string::npos);
EXPECT_NE(result.find("ab"), std::string::npos);
}
TEST(UtilsTest, ConvertMsgToStringRxMultipleBytes)
{
std::vector<uint8_t> buffer = {0x01, 0x23, 0x45};
auto result = utils::convertMsgToString(false, buffer, 0x07, 0xAB);
EXPECT_NE(result.find("EID: ab"), std::string::npos);
EXPECT_NE(result.find("TAG: 07"), std::string::npos);
EXPECT_NE(result.find("Rx:"), std::string::npos);
EXPECT_NE(result.find("01 23 45"), std::string::npos);
}
// Tests for isValidDbusString function
TEST(UtilsTest, IsValidDbusStringAscii)
{
EXPECT_TRUE(utils::isValidDbusString("ValidString123"));
}
TEST(UtilsTest, IsValidDbusStringEmptyString)
{
EXPECT_TRUE(utils::isValidDbusString(""));
}
TEST(UtilsTest, IsValidDbusStringUtf8TwoBytes)
{
// © symbol (U+00A9): 0xC2 0xA9
EXPECT_TRUE(utils::isValidDbusString("\xC2\xA9"));
}
TEST(UtilsTest, IsValidDbusStringUtf8ThreeBytes)
{
// € symbol (U+20AC): 0xE2 0x82 0xAC
EXPECT_TRUE(utils::isValidDbusString("\xE2\x82\xAC"));
}
TEST(UtilsTest, IsValidDbusStringUtf8FourBytes)
{
// 😀 emoji (U+1F600): 0xF0 0x9F 0x98 0x80
EXPECT_TRUE(utils::isValidDbusString("\xF0\x9F\x98\x80"));
}
TEST(UtilsTest, IsValidDbusStringInvalidLeadingByte)
{
// Invalid UTF-8 leading byte 0xFF
EXPECT_FALSE(utils::isValidDbusString("\xFF"));
}
TEST(UtilsTest, IsValidDbusStringIncompleteTwoByteSequence)
{
// Incomplete 2-byte sequence (missing second byte)
EXPECT_FALSE(utils::isValidDbusString("\xC2"));
}
TEST(UtilsTest, IsValidDbusStringInvalidContinuationByte)
{
// Invalid continuation byte (should be 10xxxxxx, but is 11xxxxxx)
EXPECT_FALSE(utils::isValidDbusString("\xC2\xC0"));
}
TEST(UtilsTest, IsValidDbusStringOverlongEncoding)
{
// Overlong encoding for ASCII 'A' (should be 0x41, not 0xC1 0x81)
EXPECT_FALSE(utils::isValidDbusString("\xC1\x81"));
}
TEST(UtilsTest, IsValidDbusStringNullCodepoint)
{
// NULL codepoint is invalid in D-Bus strings
EXPECT_FALSE(utils::isValidDbusString(std::string("\0", 1)));
}
TEST(UtilsTest, IsValidDbusStringCodepointTooLarge)
{
// Codepoint > 0x10FFFF is invalid
// 0xF4 0x90 0x80 0x80 would decode to 0x110000
EXPECT_FALSE(utils::isValidDbusString("\xF4\x90\x80\x80"));
}
// Tests for split function
TEST(UtilsTest, SplitBasicCommaDelimiter)
{
auto result = utils::split("a,b,c", ",");
ASSERT_EQ(result.size(), 3);
EXPECT_EQ(result[0], "a");
EXPECT_EQ(result[1], "b");
EXPECT_EQ(result[2], "c");
}
TEST(UtilsTest, SplitWithSpaceTrimming)
{
auto result = utils::split(" a , b , c ", ",", " ");
ASSERT_EQ(result.size(), 3);
EXPECT_EQ(result[0], "a");
EXPECT_EQ(result[1], "b");
EXPECT_EQ(result[2], "c");
}
TEST(UtilsTest, SplitEmptyString)
{
auto result = utils::split("", ",");
EXPECT_TRUE(result.empty());
}
TEST(UtilsTest, SplitNoDelimiterFound)
{
auto result = utils::split("abc", ",");
ASSERT_EQ(result.size(), 1);
EXPECT_EQ(result[0], "abc");
}
TEST(UtilsTest, SplitMultipleConsecutiveDelimiters)
{
auto result = utils::split("a,,b,,,c", ",");
ASSERT_EQ(result.size(), 3);
EXPECT_EQ(result[0], "a");
EXPECT_EQ(result[1], "b");
EXPECT_EQ(result[2], "c");
}
TEST(UtilsTest, SplitLeadingAndTrailingDelimiters)
{
auto result = utils::split(",a,b,c,", ",");
ASSERT_EQ(result.size(), 3);
EXPECT_EQ(result[0], "a");
EXPECT_EQ(result[1], "b");
EXPECT_EQ(result[2], "c");
}
TEST(UtilsTest, SplitMultiCharacterDelimiter)
{
auto result = utils::split("a::b::c", "::");
ASSERT_EQ(result.size(), 3);
EXPECT_EQ(result[0], "a");
EXPECT_EQ(result[1], "b");
EXPECT_EQ(result[2], "c");
}
// Tests for getCurrentSystemTime function
TEST(UtilsTest, GetCurrentSystemTimeReturnsNonEmpty)
{
auto result = utils::getCurrentSystemTime();
EXPECT_FALSE(result.empty());
}
TEST(UtilsTest, GetCurrentSystemTimeContainsDateComponents)
{
auto result = utils::getCurrentSystemTime();
// Should contain date separator '-' or '/'
EXPECT_TRUE(result.find('-') != std::string::npos ||
result.find('/') != std::string::npos);
}
TEST(UtilsTest, GetCurrentSystemTimeContainsTimeComponents)
{
auto result = utils::getCurrentSystemTime();
// Should contain time separator ':'
EXPECT_NE(result.find(':'), std::string::npos);
}
// Tests for getUUIDFromEID function
TEST(UtilsTest, GetUUIDFromEIDFound)
{
std::multimap<std::string, std::tuple<eid_t, MctpMedium, MctpBinding>>
eidTable;
eidTable.insert({"uuid-1234", {0x1D, "PCIe", "BindingPCIe"}});
eidTable.insert({"uuid-5678", {0xAB, "USB", "BindingUSB"}});
auto result = utils::getUUIDFromEID(eidTable, 0x1D);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result.value(), "uuid-1234");
}
TEST(UtilsTest, GetUUIDFromEIDNotFound)
{
std::multimap<std::string, std::tuple<eid_t, MctpMedium, MctpBinding>>
eidTable;
eidTable.insert({"uuid-1234", {0x1D, "PCIe", "BindingPCIe"}});
auto result = utils::getUUIDFromEID(eidTable, 0xFF);
EXPECT_FALSE(result.has_value());
}
TEST(UtilsTest, GetUUIDFromEIDEmptyTable)
{
std::multimap<std::string, std::tuple<eid_t, MctpMedium, MctpBinding>>
eidTable;
auto result = utils::getUUIDFromEID(eidTable, 0x1D);
EXPECT_FALSE(result.has_value());
}
TEST(UtilsTest, GetUUIDFromEIDMultipleEntries)
{
std::multimap<std::string, std::tuple<eid_t, MctpMedium, MctpBinding>>
eidTable;
eidTable.insert({"uuid-1", {0x01, "PCIe", "BindingPCIe"}});
eidTable.insert({"uuid-2", {0x02, "USB", "BindingUSB"}});
eidTable.insert({"uuid-3", {0x03, "SMBus", "BindingSMBus"}});
auto result = utils::getUUIDFromEID(eidTable, 0x02);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result.value(), "uuid-2");
}
// Tests for convertUUIDToString function
TEST(UtilsTest, ConvertUUIDToStringValidUUID)
{
std::vector<uint8_t> uuidInt = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC,
0xDE, 0xF0, 0x11, 0x22, 0x33, 0x44,
0x55, 0x66, 0x77, 0x88};
auto result = utils::convertUUIDToString(uuidInt);
// Use c_str() to handle null terminator correctly
EXPECT_STREQ(result.c_str(), "12345678-9abc-def0-1122-334455667788");
}
TEST(UtilsTest, ConvertUUIDToStringInvalidSize)
{
std::vector<uint8_t> uuidInt = {0x12, 0x34, 0x56}; // Too short
auto result = utils::convertUUIDToString(uuidInt);
EXPECT_STREQ(result.c_str(), ""); // Should return empty string on error
}
TEST(UtilsTest, ConvertUUIDToStringAllZeros)
{
std::vector<uint8_t> uuidInt(16, 0x00);
auto result = utils::convertUUIDToString(uuidInt);
EXPECT_STREQ(result.c_str(), "00000000-0000-0000-0000-000000000000");
}
TEST(UtilsTest, ConvertUUIDToStringAllFFs)
{
std::vector<uint8_t> uuidInt(16, 0xFF);
auto result = utils::convertUUIDToString(uuidInt);
EXPECT_STREQ(result.c_str(), "ffffffff-ffff-ffff-ffff-ffffffffffff");
}
// Tests for bitmapToIndices function
// Converts bitmap (bit array) to pair of (zero indices, one indices)
TEST(UtilsTest, BitmapToIndicesEmptyBitmap)
{
std::vector<uint8_t> bitmap = {};
auto [zeroIndices, oneIndices] = utils::bitmapToIndices(bitmap);
EXPECT_TRUE(zeroIndices.empty());
EXPECT_TRUE(oneIndices.empty());
}
TEST(UtilsTest, BitmapToIndicesAllZeros)
{
std::vector<uint8_t> bitmap = {0x00, 0x00};
auto [zeroIndices, oneIndices] = utils::bitmapToIndices(bitmap);
EXPECT_EQ(zeroIndices.size(), 16); // 2 bytes * 8 bits
EXPECT_TRUE(oneIndices.empty());
// Verify all indices 0-15 are in zeroIndices
for (uint8_t i = 0; i < 16; i++)
{
EXPECT_EQ(zeroIndices[i], i);
}
}
TEST(UtilsTest, BitmapToIndicesAllOnes)
{
std::vector<uint8_t> bitmap = {0xFF, 0xFF};
auto [zeroIndices, oneIndices] = utils::bitmapToIndices(bitmap);
EXPECT_TRUE(zeroIndices.empty());
EXPECT_EQ(oneIndices.size(), 16);
// Verify all indices 0-15 are in oneIndices
for (uint8_t i = 0; i < 16; i++)
{
EXPECT_EQ(oneIndices[i], i);
}
}
TEST(UtilsTest, BitmapToIndicesSingleBit)
{
std::vector<uint8_t> bitmap = {0x01}; // Bit 0 set
auto [zeroIndices, oneIndices] = utils::bitmapToIndices(bitmap);
EXPECT_EQ(zeroIndices.size(), 7); // Bits 1-7 are zero
EXPECT_EQ(oneIndices.size(), 1);
EXPECT_EQ(oneIndices[0], 0);
}
TEST(UtilsTest, BitmapToIndicesMixedBits)
{
std::vector<uint8_t> bitmap = {0b10101010}; // Alternating bits
auto [zeroIndices, oneIndices] = utils::bitmapToIndices(bitmap);
EXPECT_EQ(zeroIndices.size(), 4); // Bits 0,2,4,6 are zero (LSB first)
EXPECT_EQ(oneIndices.size(), 4); // Bits 1,3,5,7 are one
// Bitmap is processed LSB first
EXPECT_EQ(zeroIndices[0], 0);
EXPECT_EQ(oneIndices[0], 1);
EXPECT_EQ(zeroIndices[1], 2);
EXPECT_EQ(oneIndices[1], 3);
}
TEST(UtilsTest, BitmapToIndicesMultipleBytes)
{
std::vector<uint8_t> bitmap = {
0x0F, 0xF0}; // First byte: low nibble, second byte: high nibble
auto [zeroIndices, oneIndices] = utils::bitmapToIndices(bitmap);
EXPECT_EQ(zeroIndices.size(), 8);
EXPECT_EQ(oneIndices.size(), 8);
// First byte 0x0F: bits 0-3 are one, bits 4-7 are zero
// Second byte 0xF0: bits 8-11 are zero, bits 12-15 are one
}
// Tests for indicesToBitmap function
// Converts list of bit indices to bitmap representation
TEST(UtilsTest, IndicesToBitmapEmptyIndices)
{
std::vector<uint8_t> indices = {};
auto bitmap = utils::indicesToBitmap(indices, 2);
EXPECT_EQ(bitmap.size(), 2);
EXPECT_EQ(bitmap[0], 0x00);
EXPECT_EQ(bitmap[1], 0x00);
}
TEST(UtilsTest, IndicesToBitmapSingleIndex)
{
std::vector<uint8_t> indices = {0};
auto bitmap = utils::indicesToBitmap(indices, 1);
EXPECT_EQ(bitmap.size(), 1);
EXPECT_EQ(bitmap[0], 0x01); // Bit 0 set (LSB)
}
TEST(UtilsTest, IndicesToBitmapMultipleIndicesSameByte)
{
std::vector<uint8_t> indices = {0, 2, 4, 6};
auto bitmap = utils::indicesToBitmap(indices, 1);
EXPECT_EQ(bitmap.size(), 1);
EXPECT_EQ(bitmap[0], 0b01010101); // Bits 0,2,4,6 set
}
TEST(UtilsTest, IndicesToBitmapMultipleIndicesMultipleBytes)
{
std::vector<uint8_t> indices = {0, 8, 15};
auto bitmap = utils::indicesToBitmap(indices, 2);
EXPECT_EQ(bitmap.size(), 2);
EXPECT_EQ(bitmap[0], 0x01); // Bit 0 set
EXPECT_EQ(bitmap[1], 0x81); // Bits 8 and 15 set (0x01 | 0x80)
}
TEST(UtilsTest, IndicesToBitmapSizeZero)
{
std::vector<uint8_t> indices = {0, 7, 15};
auto bitmap = utils::indicesToBitmap(indices, 0); // Size 0 means auto-size
EXPECT_EQ(bitmap.size(), 2); // Should size to fit index 15 (0-15 = 2 bytes)
EXPECT_EQ(bitmap[0], 0x81); // Bits 0 and 7 set
EXPECT_EQ(bitmap[1], 0x80); // Bit 15 set
}
TEST(UtilsTest, IndicesToBitmapMaxSize)
{
std::vector<uint8_t> indices = {0, 63}; // Max index for 8 bytes
auto bitmap = utils::indicesToBitmap(indices, 8);
EXPECT_EQ(bitmap.size(), 8);
EXPECT_EQ(bitmap[0], 0x01); // Bit 0 set
EXPECT_EQ(bitmap[7], 0x80); // Bit 63 set
}
TEST(UtilsTest, IndicesToBitmapSizeExceedsMax)
{
std::vector<uint8_t> indices = {0};
EXPECT_THROW(utils::indicesToBitmap(indices, 9), std::invalid_argument);
}
TEST(UtilsTest, IndicesToBitmapIndexOutOfBounds)
{
std::vector<uint8_t> indices = {
0, 16}; // Index 16 exceeds 2-byte bitmap (0-15)
EXPECT_THROW(utils::indicesToBitmap(indices, 2), std::invalid_argument);
}
TEST(UtilsTest, IndicesToBitmapRoundTrip)
{
// Test that bitmapToIndices(indicesToBitmap(x)) == x
std::vector<uint8_t> originalIndices = {1, 3, 5, 10, 15};
auto bitmap = utils::indicesToBitmap(originalIndices, 2);
auto [zeroIndices, oneIndices] = utils::bitmapToIndices(bitmap);
EXPECT_EQ(oneIndices.size(), originalIndices.size());
for (size_t i = 0; i < originalIndices.size(); i++)
{
EXPECT_EQ(oneIndices[i], originalIndices[i]);
}
}
// Tests for getEidFromUUID function
// Searches multimap for UUID and returns corresponding EID
TEST(UtilsTest, GetEidFromUUIDFound)
{
// Create test EID table with UUID -> (EID, Medium, Binding) mappings
std::multimap<uuid_t, std::tuple<eid_t, MctpMedium, MctpBinding>> eidTable;
uuid_t uuid1 = "12345678-1234-5678-1234-567812345678";
uuid_t uuid2 = "87654321-4321-8765-4321-876543218765";
eidTable.insert({uuid1, std::make_tuple(0x10, "PCIe", "PCIe")});
eidTable.insert({uuid2, std::make_tuple(0x20, "USB", "USB")});
eid_t result = utils::getEidFromUUID(eidTable, uuid1);
EXPECT_EQ(result, 0x10);
}
TEST(UtilsTest, GetEidFromUUIDNotFound)
{
std::multimap<uuid_t, std::tuple<eid_t, MctpMedium, MctpBinding>> eidTable;
uuid_t uuid1 = "12345678-1234-5678-1234-567812345678";
uuid_t searchUuid = "00000000-0000-0000-0000-000000000000";
eidTable.insert({uuid1, std::make_tuple(0x10, "PCIe", "PCIe")});
eid_t result = utils::getEidFromUUID(eidTable, searchUuid);
EXPECT_EQ(
result,
std::numeric_limits<uint8_t>::max()); // Max value indicates not found
}
TEST(UtilsTest, GetEidFromUUIDEmptyTable)
{
std::multimap<uuid_t, std::tuple<eid_t, MctpMedium, MctpBinding>> eidTable;
uuid_t searchUuid = "12345678-1234-5678-1234-567812345678";
eid_t result = utils::getEidFromUUID(eidTable, searchUuid);
EXPECT_EQ(result, std::numeric_limits<uint8_t>::max());
}
TEST(UtilsTest, GetEidFromUUIDMultipleEntries)
{
// Test that it returns the first matching UUID's EID
std::multimap<uuid_t, std::tuple<eid_t, MctpMedium, MctpBinding>> eidTable;
uuid_t uuid = "12345678-1234-5678-1234-567812345678";
eidTable.insert({uuid, std::make_tuple(0x10, "PCIe", "PCIe")});
eidTable.insert(
{uuid, std::make_tuple(0x20, "USB", "USB")}); // Duplicate UUID
eid_t result = utils::getEidFromUUID(eidTable, uuid);
EXPECT_EQ(result, 0x10); // Should return first match
}
TEST(UtilsTest, GetEidFromUUIDPartialMatch)
{
// Function uses starts_with for UUID matching (length UUID_LEN)
std::multimap<uuid_t, std::tuple<eid_t, MctpMedium, MctpBinding>> eidTable;
uuid_t storedUuid = "12345678-1234-5678-1234-567812345678";
uuid_t searchPrefix = "12345678-1234-5678-1234-567812345678"; // Exact match
eidTable.insert({storedUuid, std::make_tuple(0x15, "PCIe", "PCIe")});
eid_t result = utils::getEidFromUUID(eidTable, searchPrefix);
EXPECT_EQ(result, 0x15);
}
// Tests for convertBitMaskToVector function
// Converts bitfield8_t array to vector of set bit indices
TEST(UtilsTest, ConvertBitMaskToVectorAllZeros)
{
bitfield8_t mask[2] = {{.byte = 0x00}, {.byte = 0x00}};
std::vector<uint8_t> data;
utils::convertBitMaskToVector(data, mask, 2);
EXPECT_TRUE(data.empty()); // No bits set
}
TEST(UtilsTest, ConvertBitMaskToVectorAllOnes)
{
bitfield8_t mask[2] = {{.byte = 0xFF}, {.byte = 0xFF}};
std::vector<uint8_t> data;
utils::convertBitMaskToVector(data, mask, 2);
EXPECT_EQ(data.size(), 16); // All 16 bits set
// Verify all indices 0-15 are present
for (uint8_t i = 0; i < 16; i++)
{
EXPECT_EQ(data[i], i);
}
}
TEST(UtilsTest, ConvertBitMaskToVectorSingleBit)
{
bitfield8_t mask[1] = {{.byte = 0x01}}; // Only bit0 set
std::vector<uint8_t> data;
utils::convertBitMaskToVector(data, mask, 1);
EXPECT_EQ(data.size(), 1);
EXPECT_EQ(data[0], 0);
}
TEST(UtilsTest, ConvertBitMaskToVectorMultipleBitsSameByte)
{
bitfield8_t mask[1] = {{.byte = 0b10101010}}; // Bits 1,3,5,7 set
std::vector<uint8_t> data;
utils::convertBitMaskToVector(data, mask, 1);
EXPECT_EQ(data.size(), 4);
EXPECT_EQ(data[0], 1);
EXPECT_EQ(data[1], 3);
EXPECT_EQ(data[2], 5);
EXPECT_EQ(data[3], 7);
}
TEST(UtilsTest, ConvertBitMaskToVectorMultipleBytes)
{
bitfield8_t mask[2] = {{.byte = 0x0F}, {.byte = 0xF0}};
std::vector<uint8_t> data;
utils::convertBitMaskToVector(data, mask, 2);
EXPECT_EQ(data.size(), 8);
// First byte 0x0F: bits 0,1,2,3 set
EXPECT_EQ(data[0], 0);
EXPECT_EQ(data[1], 1);
EXPECT_EQ(data[2], 2);
EXPECT_EQ(data[3], 3);
// Second byte 0xF0: bits 12,13,14,15 set (byte 1 offset = 8)
EXPECT_EQ(data[4], 12);
EXPECT_EQ(data[5], 13);
EXPECT_EQ(data[6], 14);
EXPECT_EQ(data[7], 15);
}
TEST(UtilsTest, ConvertBitMaskToVectorUsingBitfieldStructure)
{
// Test using the bits structure directly
bitfield8_t mask[1] = {};
mask[0].bits.bit0 = 1;
mask[0].bits.bit3 = 1;
mask[0].bits.bit7 = 1;
std::vector<uint8_t> data;
utils::convertBitMaskToVector(data, mask, 1);
EXPECT_EQ(data.size(), 3);
EXPECT_EQ(data[0], 0);
EXPECT_EQ(data[1], 3);
EXPECT_EQ(data[2], 7);
}
TEST(UtilsTest, ConvertBitMaskToVectorSizeZero)
{
bitfield8_t mask[1] = {{.byte = 0xFF}};
std::vector<uint8_t> data;
utils::convertBitMaskToVector(data, mask,
0); // Size 0 means process nothing
EXPECT_TRUE(data.empty());
}
TEST(UtilsTest, ConvertBitMaskToVectorLargeArray)
{
bitfield8_t mask[4] = {
{.byte = 0x01}, // Bit 0 set
{.byte = 0x00}, // No bits
{.byte = 0x80}, // Bit 23 set (byte 2, bit 7)
{.byte = 0x01} // Bit 24 set (byte 3, bit 0)
};
std::vector<uint8_t> data;
utils::convertBitMaskToVector(data, mask, 4);
EXPECT_EQ(data.size(), 3);
EXPECT_EQ(data[0], 0);
EXPECT_EQ(data[1], 23);
EXPECT_EQ(data[2], 24);
}
TEST(UtilsTest, ConvertBitMaskToVectorAppendToExisting)
{
bitfield8_t mask[1] = {{.byte = 0x03}}; // Bits 0,1 set
std::vector<uint8_t> data = {99, 100}; // Pre-existing data
utils::convertBitMaskToVector(data, mask, 1);
EXPECT_EQ(data.size(), 4);
EXPECT_EQ(data[0], 99); // Original data preserved
EXPECT_EQ(data[1], 100); // Original data preserved
EXPECT_EQ(data[2], 0); // New data appended
EXPECT_EQ(data[3], 1); // New data appended
}
// ===========================================================================
// Tests for typeName<T>() template function
// ===========================================================================
TEST(UtilsTest, TypeNamePrimitiveTypes)
{
// Test fundamental types
EXPECT_EQ(utils::typeName<int>(), "int");
EXPECT_EQ(utils::typeName<unsigned int>(), "unsigned int");
EXPECT_EQ(utils::typeName<long>(), "long");
EXPECT_EQ(utils::typeName<unsigned long>(), "unsigned long");
EXPECT_EQ(utils::typeName<float>(), "float");
EXPECT_EQ(utils::typeName<double>(), "double");
EXPECT_EQ(utils::typeName<bool>(), "bool");
EXPECT_EQ(utils::typeName<char>(), "char");
}
TEST(UtilsTest, TypeNameStdTypes)
{
// Test standard library types
EXPECT_EQ(utils::typeName<std::string>(),
"std::__cxx11::basic_string<char, std::char_traits<char>, "
"std::allocator<char> >");
EXPECT_EQ(utils::typeName<std::vector<int>>(),
"std::vector<int, std::allocator<int> >");
EXPECT_EQ(utils::typeName<std::vector<uint8_t>>(),
"std::vector<unsigned char, std::allocator<unsigned char> >");
}
TEST(UtilsTest, TypeNameFixedWidthIntegers)
{
// Test fixed-width integer types from <cstdint>
EXPECT_EQ(utils::typeName<uint8_t>(), "unsigned char");
EXPECT_EQ(utils::typeName<int8_t>(), "signed char");
EXPECT_EQ(utils::typeName<uint16_t>(), "unsigned short");
EXPECT_EQ(utils::typeName<int16_t>(), "short");
EXPECT_EQ(utils::typeName<uint32_t>(), "unsigned int");
EXPECT_EQ(utils::typeName<int32_t>(), "int");
EXPECT_EQ(utils::typeName<uint64_t>(), "unsigned long");
EXPECT_EQ(utils::typeName<int64_t>(), "long");
}
TEST(UtilsTest, TypeNamePointerTypes)
{
// Test pointer types
EXPECT_EQ(utils::typeName<int*>(), "int*");
EXPECT_EQ(utils::typeName<const char*>(), "char const*");
EXPECT_EQ(utils::typeName<void*>(), "void*");
}
TEST(UtilsTest, TypeNameConstTypes)
{
// Test const-qualified types
EXPECT_EQ(utils::typeName<const int>(), "int");
EXPECT_EQ(utils::typeName<const double>(), "double");
}
TEST(UtilsTest, TypeNameReferenceTypes)
{
// Test reference types (references are stripped by typeid)
EXPECT_EQ(utils::typeName<int&>(), "int");
EXPECT_EQ(utils::typeName<const std::string&>(),
"std::__cxx11::basic_string<char, std::char_traits<char>, "
"std::allocator<char> >");
}
TEST(UtilsTest, TypeNameComplexTypes)
{
// Test more complex nested types
using ComplexType = std::vector<std::pair<int, std::string>>;
std::string result = utils::typeName<ComplexType>();
// Just verify it contains expected components
EXPECT_NE(result.find("vector"), std::string::npos);
EXPECT_NE(result.find("pair"), std::string::npos);
}
TEST(UtilsTest, TypeNameTupleType)
{
// Test tuple type
using TupleType = std::tuple<int, double, std::string>;
std::string result = utils::typeName<TupleType>();
EXPECT_NE(result.find("tuple"), std::string::npos);
}
TEST(UtilsTest, TypeNameOptionalType)
{
// Test std::optional
using OptType = std::optional<int>;
std::string result = utils::typeName<OptType>();
EXPECT_NE(result.find("optional"), std::string::npos);
}
TEST(UtilsTest, TypeNameCustomStructs)
{
// Test with custom types defined in project
struct TestStruct
{
int x;
double y;
};
std::string result = utils::typeName<TestStruct>();
// Should contain the struct name
EXPECT_NE(result.find("TestStruct"), std::string::npos);
}
// ===========================================================================
// Tests for getPropertyFromCollection<T>() template function
// ===========================================================================
TEST(UtilsTest, GetPropertyFromCollectionBoolType)
{
// Test with bool type
PropertyValuesCollection collection = {
{"property_a", false}, {"property_b", true}, {"property_c", false}};
auto result = utils::getPropertyFromCollection<bool>(collection,
"property_b");
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result.value(), true);
// Test not found case
auto notFound = utils::getPropertyFromCollection<bool>(collection,
"property_z");
EXPECT_FALSE(notFound.has_value());
}
TEST(UtilsTest, GetPropertyFromCollectionIntegerTypes)
{
// Test with various integer types from PropertyValue variant
PropertyValuesCollection collection = {
{"prop_int16", static_cast<int16_t>(100)},
{"prop_int32", static_cast<int32_t>(200)},
{"prop_int64", static_cast<int64_t>(300)},
{"prop_uint16", static_cast<uint16_t>(400)},
{"prop_uint32", static_cast<uint32_t>(500)},
{"prop_uint64", static_cast<uint64_t>(600)},
{"prop_uint8", static_cast<uint8_t>(50)}};
auto int16_result = utils::getPropertyFromCollection<int16_t>(collection,
"prop_int16");
ASSERT_TRUE(int16_result.has_value());
EXPECT_EQ(int16_result.value(), 100);
auto int32_result = utils::getPropertyFromCollection<int32_t>(collection,
"prop_int32");
ASSERT_TRUE(int32_result.has_value());
EXPECT_EQ(int32_result.value(), 200);
auto int64_result = utils::getPropertyFromCollection<int64_t>(collection,
"prop_int64");
ASSERT_TRUE(int64_result.has_value());
EXPECT_EQ(int64_result.value(), 300);
auto uint16_result =
utils::getPropertyFromCollection<uint16_t>(collection, "prop_uint16");
ASSERT_TRUE(uint16_result.has_value());
EXPECT_EQ(uint16_result.value(), 400);
auto uint32_result =
utils::getPropertyFromCollection<uint32_t>(collection, "prop_uint32");
ASSERT_TRUE(uint32_result.has_value());
EXPECT_EQ(uint32_result.value(), 500);
auto uint64_result =
utils::getPropertyFromCollection<uint64_t>(collection, "prop_uint64");
ASSERT_TRUE(uint64_result.has_value());
EXPECT_EQ(uint64_result.value(), 600);
auto uint8_result = utils::getPropertyFromCollection<uint8_t>(collection,
"prop_uint8");
ASSERT_TRUE(uint8_result.has_value());
EXPECT_EQ(uint8_result.value(), 50);
}
TEST(UtilsTest, GetPropertyFromCollectionDoubleType)
{
// Test with double type
PropertyValuesCollection collection = {
{"temperature", 25.5}, {"voltage", 3.3}, {"current", 1.2}};
auto result = utils::getPropertyFromCollection<double>(collection,
"voltage");
ASSERT_TRUE(result.has_value());
EXPECT_DOUBLE_EQ(result.value(), 3.3);
}
TEST(UtilsTest, GetPropertyFromCollectionStringType)
{
// Test with std::string type
PropertyValuesCollection collection = {
{"name", std::string("sensor1")},
{"status", std::string("active")},
{"type", std::string("temperature")}};
auto result = utils::getPropertyFromCollection<std::string>(collection,
"status");
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result.value(), "active");
// Test not found
auto notFound = utils::getPropertyFromCollection<std::string>(collection,
"missing");
EXPECT_FALSE(notFound.has_value());
}
TEST(UtilsTest, GetPropertyFromCollectionVectorStringType)
{
// Test with std::vector<std::string> type
std::vector<std::string> sensorNames = {"sensor1", "sensor2", "sensor3"};
PropertyValuesCollection collection = {{"sensors", sensorNames}};
auto result = utils::getPropertyFromCollection<std::vector<std::string>>(
collection, "sensors");
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result.value().size(), 3);
EXPECT_EQ(result.value()[0], "sensor1");
EXPECT_EQ(result.value()[1], "sensor2");
EXPECT_EQ(result.value()[2], "sensor3");
}
TEST(UtilsTest, GetPropertyFromCollectionVectorUint64Type)
{
// Test with std::vector<uint64_t> type
std::vector<uint64_t> values = {100, 200, 300, 400};
PropertyValuesCollection collection = {{"data_points", values}};
auto result = utils::getPropertyFromCollection<std::vector<uint64_t>>(
collection, "data_points");
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result.value().size(), 4);
EXPECT_EQ(result.value()[0], 100);
EXPECT_EQ(result.value()[3], 400);
}
TEST(UtilsTest, GetPropertyFromCollectionNotFoundReturnsNullopt)
{
// Test that missing properties return std::nullopt
PropertyValuesCollection collection = {{"existing_prop", 42}};
auto result = utils::getPropertyFromCollection<int32_t>(collection,
"non_existent");
EXPECT_FALSE(result.has_value());
}
TEST(UtilsTest, GetPropertyFromCollectionEmptyCollection)
{
// Test with empty collection
PropertyValuesCollection emptyCollection;
auto result = utils::getPropertyFromCollection<int32_t>(emptyCollection,
"any_prop");
EXPECT_FALSE(result.has_value());
}
TEST(UtilsTest, GetPropertyFromCollectionSortedLookup)
{
// Test that lower_bound search works correctly with sorted collection
// Properties should be in alphabetical order for std::lower_bound
PropertyValuesCollection collection = {
{"aaa", 1}, {"bbb", 2}, {"ccc", 3}, {"ddd", 4}, {"eee", 5}};
// Test finding first element
auto first = utils::getPropertyFromCollection<int32_t>(collection, "aaa");
ASSERT_TRUE(first.has_value());
EXPECT_EQ(first.value(), 1);
// Test finding middle element
auto middle = utils::getPropertyFromCollection<int32_t>(collection, "ccc");
ASSERT_TRUE(middle.has_value());
EXPECT_EQ(middle.value(), 3);
// Test finding last element
auto last = utils::getPropertyFromCollection<int32_t>(collection, "eee");
ASSERT_TRUE(last.has_value());
EXPECT_EQ(last.value(), 5);
// Test not found (before first)
auto before = utils::getPropertyFromCollection<int32_t>(collection, "000");
EXPECT_FALSE(before.has_value());
// Test not found (after last)
auto after = utils::getPropertyFromCollection<int32_t>(collection, "zzz");
EXPECT_FALSE(after.has_value());
// Test not found (between existing)
auto between = utils::getPropertyFromCollection<int32_t>(collection, "abc");
EXPECT_FALSE(between.has_value());
}
// ===========================================================================
// Tests for getAssociations() function
// ===========================================================================
TEST(UtilsTest, GetAssociationsEmptyVector)
{
// Test with empty vector
std::vector<utils::Association> emptyAssociations;
auto result = utils::getAssociations(emptyAssociations);
EXPECT_TRUE(result.empty());
EXPECT_EQ(result.size(), 0);
}
TEST(UtilsTest, GetAssociationsSingleElement)
{
// Test with single association
std::vector<utils::Association> associations = {
{"contains", "contained_by", "/xyz/openbmc_project/inventory/system"}};
auto result = utils::getAssociations(associations);
ASSERT_EQ(result.size(), 1);
EXPECT_EQ(std::get<0>(result[0]), "contains");
EXPECT_EQ(std::get<1>(result[0]), "contained_by");
EXPECT_EQ(std::get<2>(result[0]), "/xyz/openbmc_project/inventory/system");
}
TEST(UtilsTest, GetAssociationsMultipleElements)
{
// Test with multiple associations
std::vector<utils::Association> associations = {
{"contains", "contained_by", "/xyz/openbmc_project/inventory/system"},
{"cooled_by", "cooling", "/xyz/openbmc_project/inventory/cooling"},
{"powered_by", "powering", "/xyz/openbmc_project/inventory/power"}};
auto result = utils::getAssociations(associations);
ASSERT_EQ(result.size(), 3);
// Check first association
EXPECT_EQ(std::get<0>(result[0]), "contains");
EXPECT_EQ(std::get<1>(result[0]), "contained_by");
EXPECT_EQ(std::get<2>(result[0]), "/xyz/openbmc_project/inventory/system");
// Check second association
EXPECT_EQ(std::get<0>(result[1]), "cooled_by");
EXPECT_EQ(std::get<1>(result[1]), "cooling");
EXPECT_EQ(std::get<2>(result[1]), "/xyz/openbmc_project/inventory/cooling");
// Check third association
EXPECT_EQ(std::get<0>(result[2]), "powered_by");
EXPECT_EQ(std::get<1>(result[2]), "powering");
EXPECT_EQ(std::get<2>(result[2]), "/xyz/openbmc_project/inventory/power");
}
TEST(UtilsTest, GetAssociationsPreservesOrder)
{
// Test that order is preserved
std::vector<utils::Association> associations = {
{"first", "first_back", "/path/first"},
{"second", "second_back", "/path/second"},
{"third", "third_back", "/path/third"}};
auto result = utils::getAssociations(associations);
ASSERT_EQ(result.size(), 3);
EXPECT_EQ(std::get<0>(result[0]), "first");
EXPECT_EQ(std::get<0>(result[1]), "second");
EXPECT_EQ(std::get<0>(result[2]), "third");
}
TEST(UtilsTest, GetAssociationsWithEmptyStrings)
{
// Test with empty strings (edge case but valid)
std::vector<utils::Association> associations = {{"", "", ""}};
auto result = utils::getAssociations(associations);
ASSERT_EQ(result.size(), 1);
EXPECT_EQ(std::get<0>(result[0]), "");
EXPECT_EQ(std::get<1>(result[0]), "");
EXPECT_EQ(std::get<2>(result[0]), "");
}
TEST(UtilsTest, GetAssociationsWithSpecialCharacters)
{
// Test with special characters in strings
std::vector<utils::Association> associations = {
{"forward/path", "backward\\path", "/special/!@#$%^&*()"}};
auto result = utils::getAssociations(associations);
ASSERT_EQ(result.size(), 1);
EXPECT_EQ(std::get<0>(result[0]), "forward/path");
EXPECT_EQ(std::get<1>(result[0]), "backward\\path");
EXPECT_EQ(std::get<2>(result[0]), "/special/!@#$%^&*()");
}
// ============================================================================
// Branch coverage: readFdToBuffer / writeBufferToFd / appendBufferToFd
// ============================================================================
TEST(UtilsTest, ReadFdToBuffer_InvalidFd_Throws)
{
std::vector<uint8_t> buffer;
EXPECT_THROW(utils::readFdToBuffer(-1, buffer), std::runtime_error);
}
TEST(UtilsTest, ReadFdToBuffer_Success)
{
int fd = memfd_create("test_read", 0);
ASSERT_GE(fd, 0);
const uint8_t data[] = {1, 2, 3, 4, 5};
ASSERT_EQ(::write(fd, data, sizeof(data)), (ssize_t)sizeof(data));
std::vector<uint8_t> buffer;
EXPECT_NO_THROW(utils::readFdToBuffer(fd, buffer));
ASSERT_EQ(buffer.size(), 5u);
for (int i = 0; i < 5; i++)
{
EXPECT_EQ(buffer[i], (uint8_t)(i + 1));
}
close(fd);
}
TEST(UtilsTest, ReadFdToBuffer_EmptyFile)
{
int fd = memfd_create("test_empty", 0);
ASSERT_GE(fd, 0);
std::vector<uint8_t> buffer = {99, 100};
EXPECT_NO_THROW(utils::readFdToBuffer(fd, buffer));
EXPECT_TRUE(buffer.empty());
close(fd);
}
TEST(UtilsTest, WriteBufferToFd_InvalidFd_Throws)
{
std::vector<uint8_t> buffer = {1, 2, 3};
EXPECT_THROW(utils::writeBufferToFd(-1, buffer), std::runtime_error);
}
TEST(UtilsTest, WriteBufferToFd_Success)
{
int fd = memfd_create("test_write", 0);
ASSERT_GE(fd, 0);
std::vector<uint8_t> buffer = {10, 20, 30, 40, 50};
EXPECT_NO_THROW(utils::writeBufferToFd(fd, buffer));
std::vector<uint8_t> readBack;
EXPECT_NO_THROW(utils::readFdToBuffer(fd, readBack));
ASSERT_EQ(readBack.size(), 5u);
EXPECT_EQ(readBack, buffer);
close(fd);
}
TEST(UtilsTest, AppendBufferToFd_InvalidFd_Throws)
{
std::vector<uint8_t> buffer = {1, 2, 3};
EXPECT_THROW(utils::appendBufferToFd(-1, buffer), std::runtime_error);
}
TEST(UtilsTest, AppendBufferToFd_Success)
{
int fd = memfd_create("test_append", 0);
ASSERT_GE(fd, 0);
std::vector<uint8_t> buf1 = {1, 2, 3};
std::vector<uint8_t> buf2 = {4, 5, 6};
EXPECT_NO_THROW(utils::appendBufferToFd(fd, buf1));
EXPECT_NO_THROW(utils::appendBufferToFd(fd, buf2));
lseek(fd, 0, SEEK_SET);
uint8_t result[6] = {};
EXPECT_EQ(::read(fd, result, 6), 6);
EXPECT_EQ(result[0], 1);
EXPECT_EQ(result[5], 6);
close(fd);
}
// ============================================================================
// Branch coverage: CustomFD
// ============================================================================
TEST(UtilsTest, CustomFD_InvalidFd_Throws)
{
EXPECT_THROW((void)utils::CustomFD(-1), std::runtime_error);
}
TEST(UtilsTest, CustomFD_ValidFd_OperationsWork)
{
int fd = memfd_create("test_customfd", 0);
ASSERT_GE(fd, 0);
// Pre-allocate so write can work (pos <= fileSize check)
ASSERT_EQ(ftruncate(fd, 16), 0);
utils::CustomFD cfd(fd);
EXPECT_EQ(cfd.size(), 16u);
// write: pos > fileSize → return false
uint8_t buf[4] = {1, 2, 3, 4};
EXPECT_FALSE(cfd.write(20, buf, 4)); // pos 20 > fileSize 16
// write: null data → return false
EXPECT_FALSE(cfd.write(0, nullptr, 4));
// write: size == 0 → return false
EXPECT_FALSE(cfd.write(0, buf, 0));
// write success
EXPECT_TRUE(cfd.write(0, buf, 4));
// read: pos+size > fileSize → return false
uint8_t rbuf[4] = {};
EXPECT_FALSE(cfd.read(14, rbuf, 4)); // 14+4=18 > 16
// read: null data → return false
EXPECT_FALSE(cfd.read(0, nullptr, 4));
// read: size == 0 → return false
EXPECT_FALSE(cfd.read(0, rbuf, 0));
// read success
EXPECT_TRUE(cfd.read(0, rbuf, 4));
EXPECT_EQ(rbuf[0], 1);
EXPECT_EQ(rbuf[3], 4);
// fd is closed by CustomFD destructor
}
// ============================================================================
// Branch coverage: bitMapToBitfield256_t wrong size → returns zero bitfield
// ============================================================================
TEST(UtilsTest, BitMapToBitfield256t_WrongSize_ReturnsZero)
{
std::vector<uint8_t> wrongSizeBitmap(16, 0xFF); // should be 32
bitfield256_t result = utils::bitMapToBitfield256_t(wrongSizeBitmap);
for (int i = 0; i < 8; i++)
{
EXPECT_EQ(result.fields[i].byte, 0u);
}
}
// ============================================================================
// Branch coverage: isPreferred with unknown medium/binding
// (triggers defaultPriority = INT_MIN path in getMediumPriority lambda)
// ============================================================================
TEST(UtilsTest, IsPreferred_UnknownMedium_UsesDefaultPriority)
{
// Current medium unknown (INT_MIN), new medium PCIe (0)
// INT_MIN != 0 → else: INT_MIN >= 0 → false
std::tuple<MctpMedium, MctpBinding> current = {
"unknown_medium_type",
"xyz.openbmc_project.MCTP.Binding.BindingTypes.PCIe"};
std::tuple<MctpMedium, MctpBinding> newInfo = {
"xyz.openbmc_project.MCTP.Endpoint.MediaTypes.PCIe",
"xyz.openbmc_project.MCTP.Binding.BindingTypes.PCIe"};
EXPECT_FALSE(utils::isPreferred(current, newInfo));
}
TEST(UtilsTest, IsPreferred_BothUnknownMedium_FallsToBinding)
{
// Both mediums unknown (INT_MIN == INT_MIN) → compare binding
// Current binding unknown (INT_MIN), new binding PCIe (0)
// INT_MIN >= 0 → false
std::tuple<MctpMedium, MctpBinding> current = {"unknown_medium_type",
"unknown_binding_type"};
std::tuple<MctpMedium, MctpBinding> newInfo = {
"unknown_medium_type",
"xyz.openbmc_project.MCTP.Binding.BindingTypes.PCIe"};
EXPECT_FALSE(utils::isPreferred(current, newInfo));
}
// ============================================================================
// Branch coverage: isValidDbusString overlong 3-byte and 4-byte encodings
// ============================================================================
TEST(UtilsTest, IsValidDbusString_Overlong3Byte)
{
// 0xE0 0x80 0xBF: 3-byte encoding of codepoint 63 (< 0x800) → overlong
EXPECT_FALSE(utils::isValidDbusString("\xE0\x80\xBF"));
}
TEST(UtilsTest, IsValidDbusString_Overlong4Byte)
{
// 0xF0 0x80 0x80 0xBF: 4-byte encoding of codepoint 63 (< 0x10000) →
// overlong
EXPECT_FALSE(utils::isValidDbusString("\xF0\x80\x80\xBF"));
}
// ============================================================================
// Branch coverage: parseStaticUuid n1/n2 out-of-range paths
// ============================================================================
TEST(ParseStaticUuid, N1Negative_ReturnsMinusOne)
{
uuid_t uuid = "STATIC:-1:0:PROP:VAL";
uint8_t deviceType = 0, instanceNumber = 0, deviceRole = 0;
std::string remapPropName;
std::vector<std::string> remapPropValues;
int result = utils::parseStaticUuid(uuid, deviceType, instanceNumber,
deviceRole, remapPropName,
remapPropValues);
EXPECT_EQ(result, -1);
}
TEST(ParseStaticUuid, N1TooLarge_ReturnsMinusOne)
{
uuid_t uuid = "STATIC:65536:0:PROP:VAL"; // 65536 > 0xffff
uint8_t deviceType = 0, instanceNumber = 0, deviceRole = 0;
std::string remapPropName;
std::vector<std::string> remapPropValues;
int result = utils::parseStaticUuid(uuid, deviceType, instanceNumber,
deviceRole, remapPropName,
remapPropValues);
EXPECT_EQ(result, -1);
}
TEST(ParseStaticUuid, N2TooLarge_ReturnsMinusTwo)
{
uuid_t uuid = "STATIC:0:256:PROP:VAL"; // 256 > 0xff
uint8_t deviceType = 0, instanceNumber = 0, deviceRole = 0;
std::string remapPropName;
std::vector<std::string> remapPropValues;
int result = utils::parseStaticUuid(uuid, deviceType, instanceNumber,
deviceRole, remapPropName,
remapPropValues);
EXPECT_EQ(result, -2);
}