Add unit test for full FRU decode
To start refactoring this, it is helpful if we have a representative
unit test that decodes a full FRU.
To simplify this, the internal interfaces have been updated to support
passing values by std::span<const uint8_t> instead of by reference to
std::vector.
Tested: Unit tests pass
Change-Id: If8a618969b99d3d8a32bc7203aa2f57d0f999bdf
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/meson.build b/meson.build
index f789a0d..cc5a78e 100644
--- a/meson.build
+++ b/meson.build
@@ -160,7 +160,7 @@
'src/fru_utils.cpp',
'src/fru_reader.cpp',
cpp_args: test_boost_args,
- dependencies: [boost, gtest, phosphor_logging_dep, sdbusplus],
+ dependencies: [boost, gtest, gmock, phosphor_logging_dep, sdbusplus],
include_directories: 'src',
),
)
diff --git a/src/fru_utils.cpp b/src/fru_utils.cpp
index 3ce9ef4..b5c3152 100644
--- a/src/fru_utils.cpp
+++ b/src/fru_utils.cpp
@@ -100,8 +100,8 @@
* iterator is no longer usable.
*/
std::pair<DecodeState, std::string> decodeFRUData(
- std::vector<uint8_t>::const_iterator& iter,
- const std::vector<uint8_t>::const_iterator& end, bool isLangEng)
+ std::span<const uint8_t>::const_iterator& iter,
+ std::span<const uint8_t>::const_iterator& end, bool isLangEng)
{
std::string value;
unsigned int i = 0;
@@ -224,7 +224,7 @@
* len: Length of current area space and it is a multiple of 8 bytes
* as per specification
*/
-bool verifyOffset(const std::vector<uint8_t>& fruBytes, fruAreas currentArea,
+bool verifyOffset(std::span<const uint8_t> fruBytes, fruAreas currentArea,
uint8_t len)
{
unsigned int fruBytesSize = fruBytes.size();
@@ -290,7 +290,7 @@
}
static void parseMultirecordUUID(
- const std::vector<uint8_t>& device,
+ std::span<const uint8_t> device,
boost::container::flat_map<std::string, std::string>& result)
{
constexpr size_t uuidDataLen = 16;
@@ -304,8 +304,12 @@
*/
const std::array<uint8_t, uuidDataLen> uuidCharOrder = {
3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15};
- uint32_t areaOffset =
- device.at(getHeaderAreaFieldOffset(fruAreas::fruAreaMultirecord));
+ size_t offset = getHeaderAreaFieldOffset(fruAreas::fruAreaMultirecord);
+ if (offset >= device.size())
+ {
+ throw std::runtime_error("Multirecord UUID offset is out of range");
+ }
+ uint32_t areaOffset = device[offset];
if (areaOffset == 0)
{
@@ -313,7 +317,7 @@
}
areaOffset *= fruBlockSize;
- std::vector<uint8_t>::const_iterator fruBytesIter =
+ std::span<const uint8_t>::const_iterator fruBytesIter =
device.begin() + areaOffset;
/* Verify area offset */
@@ -370,7 +374,7 @@
}
resCodes formatIPMIFRU(
- const std::vector<uint8_t>& fruBytes,
+ std::span<const uint8_t> fruBytes,
boost::container::flat_map<std::string, std::string>& result)
{
resCodes ret = resCodes::resOK;
@@ -394,7 +398,7 @@
continue;
}
offset *= fruBlockSize;
- std::vector<uint8_t>::const_iterator fruBytesIter =
+ std::span<const uint8_t>::const_iterator fruBytesIter =
fruBytes.begin() + offset;
if (fruBytesIter + fruBlockSize >= fruBytes.end())
{
@@ -418,7 +422,7 @@
}
size_t fruAreaSize = *fruBytesIter * fruBlockSize;
- std::vector<uint8_t>::const_iterator fruBytesIterEndArea =
+ std::span<const uint8_t>::const_iterator fruBytesIterEndArea =
fruBytes.begin() + offset + fruAreaSize - 1;
++fruBytesIter;
@@ -579,15 +583,15 @@
}
// Calculate new checksum for fru info area
-uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
- std::vector<uint8_t>::const_iterator end)
+uint8_t calculateChecksum(std::span<const uint8_t>::const_iterator iter,
+ std::span<const uint8_t>::const_iterator end)
{
constexpr int checksumMod = 256;
uint8_t sum = std::accumulate(iter, end, static_cast<uint8_t>(0));
return (checksumMod - sum) % checksumMod;
}
-uint8_t calculateChecksum(std::vector<uint8_t>& fruAreaData)
+uint8_t calculateChecksum(std::span<const uint8_t> fruAreaData)
{
return calculateChecksum(fruAreaData.begin(), fruAreaData.end());
}
diff --git a/src/fru_utils.hpp b/src/fru_utils.hpp
index dcd9752..b427c25 100644
--- a/src/fru_utils.hpp
+++ b/src/fru_utils.hpp
@@ -114,25 +114,25 @@
char bcdPlusToChar(uint8_t val);
-bool verifyOffset(const std::vector<uint8_t>& fruBytes, fruAreas currentArea,
+bool verifyOffset(std::span<const uint8_t> fruBytes, fruAreas currentArea,
uint8_t len);
std::pair<DecodeState, std::string> decodeFRUData(
- std::vector<uint8_t>::const_iterator& iter,
- const std::vector<uint8_t>::const_iterator& end, bool isLangEng);
+ std::span<const uint8_t>::const_iterator& iter,
+ std::span<const uint8_t>::const_iterator& end, bool isLangEng);
bool checkLangEng(uint8_t lang);
resCodes formatIPMIFRU(
- const std::vector<uint8_t>& fruBytes,
+ std::span<const uint8_t> fruBytes,
boost::container::flat_map<std::string, std::string>& result);
std::vector<uint8_t>& getFRUInfo(const uint16_t& bus, const uint8_t& address);
-uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
- std::vector<uint8_t>::const_iterator end);
+uint8_t calculateChecksum(std::span<const uint8_t>::const_iterator iter,
+ std::span<const uint8_t>::const_iterator end);
-uint8_t calculateChecksum(std::vector<uint8_t>& fruAreaData);
+uint8_t calculateChecksum(std::span<const uint8_t> fruAreaData);
unsigned int updateFRUAreaLenAndChecksum(
std::vector<uint8_t>& fruData, size_t fruAreaStart,
diff --git a/test/test_fru-utils.cpp b/test/test_fru-utils.cpp
index 70330a7..19aff02 100644
--- a/test/test_fru-utils.cpp
+++ b/test/test_fru-utils.cpp
@@ -4,8 +4,12 @@
#include <array>
#include <iterator>
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
+using ::testing::Pair;
+using ::testing::UnorderedElementsAre;
+
extern "C"
{
// Include for I2C_SMBUS_BLOCK_MAX
@@ -389,3 +393,45 @@
EXPECT_TRUE(findFRUHeader(reader, "error", blockData, offset));
EXPECT_EQ(0x6000, offset);
}
+
+TEST(formatIPMIFRU, FullDecode)
+{
+ const std::array<uint8_t, 176> bmcFru = {
+ 0x01, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0xf3, 0x01, 0x0a, 0x19, 0x1f,
+ 0x0f, 0xe6, 0xc6, 0x4e, 0x56, 0x49, 0x44, 0x49, 0x41, 0xc5, 0x50, 0x33,
+ 0x38, 0x30, 0x39, 0xcd, 0x31, 0x35, 0x38, 0x33, 0x33, 0x32, 0x34, 0x38,
+ 0x30, 0x30, 0x31, 0x35, 0x30, 0xd2, 0x36, 0x39, 0x39, 0x2d, 0x31, 0x33,
+ 0x38, 0x30, 0x39, 0x2d, 0x30, 0x34, 0x30, 0x34, 0x2d, 0x36, 0x30, 0x30,
+ 0xc0, 0x01, 0x01, 0xd6, 0x4d, 0x41, 0x43, 0x3a, 0x20, 0x33, 0x43, 0x3a,
+ 0x36, 0x44, 0x3a, 0x36, 0x36, 0x3a, 0x31, 0x34, 0x3a, 0x43, 0x38, 0x3a,
+ 0x37, 0x41, 0xc1, 0x3b, 0x01, 0x09, 0x19, 0xc6, 0x4e, 0x56, 0x49, 0x44,
+ 0x49, 0x41, 0xc9, 0x50, 0x33, 0x38, 0x30, 0x39, 0x2d, 0x42, 0x4d, 0x43,
+ 0xd2, 0x36, 0x39, 0x39, 0x2d, 0x31, 0x33, 0x38, 0x30, 0x39, 0x2d, 0x30,
+ 0x34, 0x30, 0x34, 0x2d, 0x36, 0x30, 0x30, 0xc4, 0x41, 0x45, 0x2e, 0x31,
+ 0xcd, 0x31, 0x35, 0x38, 0x33, 0x33, 0x32, 0x34, 0x38, 0x30, 0x30, 0x31,
+ 0x35, 0x30, 0xc0, 0xc4, 0x76, 0x30, 0x2e, 0x31, 0xc1, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xb4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ boost::container::flat_map<std::string, std::string> result;
+ ASSERT_EQ(formatIPMIFRU(bmcFru, result), resCodes::resOK);
+
+ EXPECT_THAT(
+ result,
+ UnorderedElementsAre(
+ Pair("BOARD_FRU_VERSION_ID", ""), Pair("BOARD_INFO_AM1", "01"),
+ Pair("BOARD_INFO_AM2", "MAC: 3C:6D:66:14:C8:7A"),
+ Pair("BOARD_LANGUAGE_CODE", "25"),
+ Pair("BOARD_MANUFACTURER", "NVIDIA"),
+ Pair("BOARD_MANUFACTURE_DATE", "20240831T055100Z"),
+ Pair("BOARD_PART_NUMBER", "699-13809-0404-600"),
+ Pair("BOARD_PRODUCT_NAME", "P3809"),
+ Pair("BOARD_SERIAL_NUMBER", "1583324800150"),
+ Pair("Common_Format_Version", "1"), Pair("PRODUCT_ASSET_TAG", ""),
+ Pair("PRODUCT_FRU_VERSION_ID", "v0.1"),
+ Pair("PRODUCT_LANGUAGE_CODE", "25"),
+ Pair("PRODUCT_MANUFACTURER", "NVIDIA"),
+ Pair("PRODUCT_PART_NUMBER", "699-13809-0404-600"),
+ Pair("PRODUCT_PRODUCT_NAME", "P3809-BMC"),
+ Pair("PRODUCT_SERIAL_NUMBER", "1583324800150"),
+ Pair("PRODUCT_VERSION", "AE.1")));
+}