Add polymorphic cper encoder class

This class is intended to be the base of other CperEncoders, the
mockEncoder is a generic example of how this would look.

Change-Id: Ic0126a767ca8c2827ae32b6716c27c3b42803c5a
Signed-off-by: Aryk Ledet <arykledet@google.com>
diff --git a/README.md b/README.md
index d1e877b..548cd57 100644
--- a/README.md
+++ b/README.md
@@ -13,3 +13,49 @@
 gBMC applications to log faults in the CPER format the user only needs to
 specify the type of Record and Section type(s) that the encoder will need to
 create.
+
+When developing and testing your application specific encoders, you will need to
+install this library into your development container.
+
+Below is some pseudo code of how a user would setup their application specific
+CPER encoder which takes in char array types.
+
+```c++
+class MyCperEncoder : public CperEncoder<char>
+{
+    RecordHeader createRecordHeader(...) override
+    {...}
+
+    SectionDescriptor createSectionDescriptor(...) override
+    {
+        if (sectionType == kPlatformMemory)
+        {
+            return createPlatformMemoryDescriptor();
+        }
+        else if (sectionType == kOemSection)
+        {
+            return createOemDescriptor();
+        }
+        ...
+    }
+
+    SectionDescriptor createPlatformMemoryDescriptor()
+    {...}
+
+    SectionDescriptor createOemDescriptor()
+    {...}
+}
+```
+
+## To install
+
+```sh
+meson setup -C builddir
+meson install -C builddir
+```
+
+## To run unit tests
+
+```sh
+meson test -C builddir
+```
diff --git a/include/cper_encoder.hpp b/include/cper_encoder.hpp
index 20df915..aea2743 100644
--- a/include/cper_encoder.hpp
+++ b/include/cper_encoder.hpp
@@ -4,6 +4,9 @@
 
 #include <chrono>
 #include <concepts>
+#include <cstdint>
+#include <cstring>
+#include <string>
 
 namespace uefi::cper
 {
@@ -95,4 +98,167 @@
     std::span<ByteType> baseDest_;
     uint64_t offset_;
 };
+
+template <ByteLike BodyType>
+class CperEncoder
+{
+  public:
+    CperEncoder() : totalSectionBodySizeBytes_(0)
+    {}
+
+    /**
+     * @brief Implementation specific CPER encoders must define how to create
+     * RecordHeaders.
+     * https://uefi.org/specs/UEFI/2.10/Apx_N_Common_Platform_Error_Record.html#record-header
+     *
+     * @param[in] recordLength - The length in bytes of the entire record.
+     * @param[in] sectionCount - The number of sections in the record.
+     */
+    virtual RecordHeader createRecordHeader(const uint32_t recordLength,
+                                            const uint16_t sectionCount) = 0;
+
+    /**
+     * @brief Implementation specific CPER encoders must define how to create
+     * new sectionDescriptors.
+     * https://uefi.org/specs/UEFI/2.10/Apx_N_Common_Platform_Error_Record.html#section-descriptor
+     *
+     * @param[in] sectionLength - The length in bytes of the section body.
+     * @param[in] sectionOffset - The offset in bytes from the section body from
+     * the base of the record header.
+     * @param[in] sectionFlags - Information that describes the error section.
+     * @param[in] sectionType - The ID of the sectionType; useful for
+     * implementations with multiple section types.
+     *
+     * @return A SectionDescriptor struct.
+     */
+    virtual SectionDescriptor createSectionDescriptor(
+        const uint32_t sectionLength, const uint32_t sectionOffset,
+        const uint32_t sectionFlags, const guid_t sectionType) = 0;
+
+    /**
+     * @brief Adds a new CPER section for the encoder to track.
+     *
+     * @note Cper logs have a max size of 4GiB including the header and
+     * descriptors.
+     *
+     * @param[in] sectionBody - A span containing the section data as a byte
+     * array.
+     * @param[in] sectionFlags - The CPER flags for the section.
+     * @param[in] sectionType - The guid_t for the section.
+     *
+     * @warning The CperEncoder class does not own the ptrs of the
+     * sectionBodies, it merely tracks the addresses and size. It is expected
+     * that the data added to the encoder exists for the entire lifetime of the
+     * CperEncoder object.
+     *
+     * @returns true on success, false if new section goes over the 4GiB
+     * threshold.
+     */
+    bool addSection(const std::span<const BodyType> sectionBody,
+                    const uint32_t sectionFlags, const guid_t sectionType)
+    {
+        if ((calculateRecordLengthBytes() + sectionBody.size_bytes() +
+             kSectionDescriptorSizeBytes) >
+            std::numeric_limits<uint32_t>::max())
+        {
+            return false;
+        }
+
+        sectionSeeds_.emplace_back(sectionFlags, sectionType, sectionBody);
+        totalSectionBodySizeBytes_ += sectionBody.size_bytes();
+        return true;
+    }
+
+    /**
+     * @brief Serializes the CPER Log into a vector of bytes.
+     *
+     * @return A byte array containing the CPER log contents.
+     */
+    std::vector<uint8_t> serializeToByteArray()
+    {
+        const uint64_t totalBytes = calculateRecordLengthBytes();
+        std::vector<uint8_t> byteArray(totalBytes);
+
+        encode<uint8_t>(byteArray);
+        return byteArray;
+    }
+
+    /**
+     * @brief Serializes the CPER Log into a string.
+     *
+     * @return A string containing the CPER log contents.
+     */
+    std::string serializeToString()
+    {
+        const uint64_t totalBytes = calculateRecordLengthBytes();
+        std::string str(totalBytes, 0);
+
+        encode<char>(str);
+        return str;
+    }
+
+  private:
+    // Save ~3x memory by only caching the section "seeds" and keeping a
+    // single full sectionDescriptor on the stack at a time while encoding
+    // the log.
+    struct SectionSeed
+    {
+        uint32_t sectionFlags;
+        guid_t sectionType;
+        std::span<const BodyType> body;
+    };
+
+    /**
+     * @brief Calculates the total size of the CPER log.
+     *
+     * @return The total size in bytes.
+     */
+    uint32_t calculateRecordLengthBytes() const
+    {
+        return kRecordHeaderSizeBytes +
+               (sectionSeeds_.size() * kSectionDescriptorSizeBytes) +
+               totalSectionBodySizeBytes_;
+    }
+
+    /**
+     * @brief Encodes the CPER byte array to the destination pointer.
+     *
+     * @tparam ByteType - Allows for different cpp byte representations (char,
+     * unsigned char, std::byte, etc.)
+     *
+     * @param[in,out] dest - The destination span to copy the data to.
+     *
+     * @throws If unable to successfully encode the CPER log.
+     */
+    template <ByteLike ByteType>
+    void encode(std::span<ByteType> dest)
+    {
+        SeqMemcopy seqMemcpy(dest);
+        {
+            const RecordHeader header =
+                createRecordHeader(dest.size_bytes(), sectionSeeds_.size());
+
+            seqMemcpy.copy(&header, kRecordHeaderSizeBytes);
+        }
+
+        uint64_t offset = 0;
+        for (const SectionSeed& seed : sectionSeeds_)
+        {
+            const SectionDescriptor descriptor =
+                createSectionDescriptor(seed.body.size_bytes(), offset,
+                                        seed.sectionFlags, seed.sectionType);
+            offset += seed.body.size_bytes();
+
+            seqMemcpy.copy(&descriptor, kSectionDescriptorSizeBytes);
+        }
+
+        for (const auto& seed : sectionSeeds_)
+        {
+            seqMemcpy.copy(seed.body.data(), seed.body.size_bytes());
+        }
+    }
+
+    uint32_t totalSectionBodySizeBytes_;
+    std::vector<SectionSeed> sectionSeeds_;
+};
 } // namespace uefi::cper
diff --git a/test/cper_encoder_test.cpp b/test/cper_encoder_test.cpp
index c1e756f..f38cc72 100644
--- a/test/cper_encoder_test.cpp
+++ b/test/cper_encoder_test.cpp
@@ -1,18 +1,113 @@
 #include "cper_encoder.hpp"
+#include "mock_cper_encoder.hpp"
 
 #include <chrono>
+#include <concepts>
 
 #include "gtest/gtest.h"
 #include <gmock/gmock.h>
 
+using testing::_;
+using testing::Contains;
+
 namespace uefi::cper
 {
+template <typename T>
+concept StringOrVector =
+    std::is_same_v<T, std::string> || std::is_same_v<T, std::vector<uint8_t>>;
+
 class CperEncoderTest : public testing::Test
 {
   public:
     void SetUp() override
     {}
 
+    bool isArrayEq(const void* arr1, const void* arr2, const uint64_t arr1Size,
+                   const uint64_t arr2Size)
+    {
+        if (arr1Size != arr2Size)
+        {
+            return false;
+        }
+        return memcmp(arr1, arr2, arr1Size) == 0;
+    }
+
+    template <StringOrVector CperType>
+    void expectValidHeader(const CperType& cperArray,
+                           const uint16_t numSections, const uint64_t cperSize,
+                           const uint64_t timestamp)
+    {
+        RecordHeader header;
+        memcpy(&header, cperArray.data(), sizeof(header));
+
+        EXPECT_EQ(header.signature, kRecordSignature);
+        EXPECT_EQ(header.revision, kRecordRevision);
+        EXPECT_EQ(header.signatureEnd, kRecordSignatureEnd);
+        EXPECT_EQ(header.sectionCount, numSections);
+        EXPECT_EQ(header.errorSeverity, kMockRecordSeverity);
+        EXPECT_EQ(header.validationBits, kMockValidationBits);
+        EXPECT_EQ(header.recordLength, cperSize);
+        EXPECT_EQ(header.timestamp, timestamp);
+        EXPECT_EQ(header.platformId, kMockPlatformId);
+        EXPECT_EQ(header.partitionId, kMockPartitionId);
+        EXPECT_EQ(header.creatorId, kMockCreatorId);
+        EXPECT_EQ(header.notificationType, kMockNotificationType);
+        EXPECT_EQ(header.recordId, kMockRecordId);
+        EXPECT_EQ(header.flags, kMockHeaderFlags);
+        EXPECT_EQ(header.persistenceInformation, kMockPersistenceInformation);
+        EXPECT_THAT(header.reserved, Contains(0).Times(header.reserved.size()));
+    }
+
+    template <StringOrVector CperType>
+    void expectValidSectionDescriptor(const CperType& cperArray,
+                                      const uint64_t sectionNum,
+                                      const uint32_t expectedOffset,
+                                      const uint32_t expectedLength,
+                                      const uint32_t expectedFlag,
+                                      const guid_t expectedSectionType)
+    {
+        const uint64_t sectionOffset =
+            kRecordHeaderSizeBytes + (kSectionDescriptorSizeBytes * sectionNum);
+
+        SectionDescriptor descriptor;
+        memcpy(&descriptor, &cperArray[sectionOffset], sizeof(descriptor));
+
+        EXPECT_EQ(descriptor.sectionOffset, expectedOffset);
+        EXPECT_EQ(descriptor.sectionLength, expectedLength);
+        EXPECT_EQ(descriptor.revision, kMockSectionRevision);
+        EXPECT_EQ(descriptor.validationBits, kMockSectionValidationBits);
+        EXPECT_EQ(descriptor.reserved, 0);
+        EXPECT_EQ(descriptor.flags, expectedFlag);
+        EXPECT_EQ(descriptor.sectionType, expectedSectionType);
+        EXPECT_EQ(descriptor.fruId, kMockFruId);
+        EXPECT_EQ(descriptor.sectionSeverity, kMockSectionSeverity);
+        EXPECT_EQ(descriptor.fruText, kMockFruText);
+    }
+
+    template <ByteLike BodyType, StringOrVector CperType>
+    void expectValidSectionBody(
+        const CperType& cperArray,
+        const std::span<const BodyType> expectedSectionBody,
+        const uint64_t totalSections, const uint32_t sectionOffset)
+    {
+        // The offset set in the section descriptors is the offset in bytes
+        // after the header and descriptors.
+        const uint32_t kTrueBodyOffset =
+            sectionOffset + kRecordHeaderSizeBytes +
+            (kSectionDescriptorSizeBytes * totalSections);
+
+        const uint64_t kSectionBodySize = expectedSectionBody.size();
+
+        ASSERT_GE(cperArray.size(), (kTrueBodyOffset + kSectionBodySize));
+
+        CperType sectionBody(kSectionBodySize, 0);
+        memcpy(sectionBody.data(), &cperArray[kTrueBodyOffset],
+               sectionBody.size());
+
+        EXPECT_TRUE(isArrayEq(sectionBody.data(), expectedSectionBody.data(),
+                              sectionBody.size(), expectedSectionBody.size()));
+    }
+
     void expectEqTimestamp(
         const uint64_t cperTimestamp,
         const std::chrono::time_point<std::chrono::system_clock> ref,
@@ -35,6 +130,207 @@
     }
 };
 
+TEST_F(CperEncoderTest, TooMuchData)
+{
+    MockCperEncoder<uint8_t> mockEncoder;
+    uint64_t totalSize = kRecordHeaderSizeBytes;
+
+    // Exceed the 4GiB CPER Log max size by adding 2^16 * 2^16 Bytes worth of
+    // section bodies along with their descriptors.
+    for (uint64_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++)
+    {
+        std::array<uint8_t, std::numeric_limits<uint16_t>::max()> sectionBody;
+        totalSize += sectionBody.size() + kSectionDescriptorSizeBytes;
+
+        // Note the encoder does not own the sectionBody, it just tracks the
+        // body ptrs and size. This would never work in a real application since
+        // the sectionBody only exists within each loops iteration.
+        if (totalSize < std::numeric_limits<uint32_t>::max())
+        {
+
+            EXPECT_TRUE(mockEncoder.addSection(sectionBody, kMockSectionFlag,
+                                               kMockSectionType));
+        }
+        else
+        {
+            EXPECT_FALSE(mockEncoder.addSection(sectionBody, kMockSectionFlag,
+                                                kMockSectionType));
+            return;
+        }
+    }
+
+    FAIL() << "Should never be able to complete the loop";
+}
+
+TEST_F(CperEncoderTest, EncodeMultipleCharSectionsToArrayAndString)
+{
+    MockCperEncoder<char> mockEncoder;
+
+    // Fill our CPER Log with some quotes to get through the work week.
+    std::vector<std::string> sectionBodies;
+    sectionBodies.push_back("The first principle is that you must not fool "
+                            "yourself and you are the easiest person to fool.");
+    sectionBodies.push_back("Spread love everywhere you go.");
+    sectionBodies.push_back("The only thing we have to fear is fear itself.");
+    sectionBodies.push_back("If you can't explain it to a six year old, you "
+                            "don't understand it yourself.");
+    sectionBodies.push_back("I have no wings, so I guess I'll look up at this "
+                            "sky, and crawl along the Earth.");
+    sectionBodies.push_back("If I have seen further, it is by standing on the "
+                            "shoulders of giants.");
+
+    const uint16_t kNumSections = sectionBodies.size();
+
+    // Add the sections to our encoder.
+    uint64_t totalSectionBodySize = 0;
+    for (const std::string& body : sectionBodies)
+    {
+        mockEncoder.addSection(body, kMockSectionFlag, kMockSectionType);
+        totalSectionBodySize += body.size();
+    }
+
+    const uint64_t kExpectedCperSize =
+        (kRecordHeaderSizeBytes + (kSectionDescriptorSizeBytes * kNumSections) +
+         totalSectionBodySize);
+
+    // Serialize to an array.
+    EXPECT_CALL(mockEncoder, mockCreateRecordHeader).Times(1);
+    EXPECT_CALL(mockEncoder, mockCreateSectionDescriptor).Times(kNumSections);
+    const std::vector<uint8_t> cperArray = mockEncoder.serializeToByteArray();
+    ASSERT_EQ(cperArray.size(), kExpectedCperSize);
+
+    // Check that the serialized arrays header contains the same contents we
+    // configured the encoder to use.
+    expectValidHeader(cperArray, kNumSections, kExpectedCperSize,
+                      mockEncoder.timestamp);
+
+    // Check that the section descriptors and bodies all contain the correct
+    // information about their respective section.
+    uint32_t expectedOffset = 0;
+    uint64_t sectionIdx = 0;
+    for (const std::string& body : sectionBodies)
+    {
+        const uint32_t kBodyLength = body.size();
+        expectValidSectionDescriptor(cperArray, sectionIdx, expectedOffset,
+                                     kBodyLength, kMockSectionFlag,
+                                     kMockSectionType);
+
+        expectValidSectionBody<char>(cperArray, body, kNumSections,
+                                     expectedOffset);
+
+        expectedOffset += kBodyLength;
+        sectionIdx++;
+    }
+
+    // Serialize to a string.
+    EXPECT_CALL(mockEncoder, mockCreateRecordHeader).Times(1);
+    EXPECT_CALL(mockEncoder, mockCreateSectionDescriptor).Times(kNumSections);
+    const std::string cperStr = mockEncoder.serializeToString();
+    ASSERT_EQ(cperStr.size(), kExpectedCperSize);
+
+    // Check that the serialized arrays header contains the same contents we
+    // configured the encoder to use.
+    expectValidHeader(cperStr, kNumSections, kExpectedCperSize,
+                      mockEncoder.timestamp);
+
+    // Check that the section descriptors and bodies all contain the correct
+    // information about their respective section.
+    expectedOffset = 0;
+    sectionIdx = 0;
+    for (const std::string& body : sectionBodies)
+    {
+        const uint32_t kBodyLength = body.size();
+        expectValidSectionDescriptor(cperStr, sectionIdx, expectedOffset,
+                                     kBodyLength, kMockSectionFlag,
+                                     kMockSectionType);
+
+        expectValidSectionBody<char>(cperStr, body, kNumSections,
+                                     expectedOffset);
+
+        expectedOffset += kBodyLength;
+        sectionIdx++;
+    }
+}
+
+TEST_F(CperEncoderTest, EncodeOneU8SectionToString)
+{
+    MockCperEncoder<uint8_t> mockEncoder;
+
+    const std::array<uint8_t, 10> sectionBody = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+    const uint64_t kSectionBodySize = sectionBody.size();
+    constexpr uint16_t kNumSections = 1;
+
+    // Add 1 section
+    mockEncoder.addSection(sectionBody, kMockSectionFlag, kMockSectionType);
+
+    // Serialize the data into a CPER byte array.
+    EXPECT_CALL(mockEncoder, mockCreateRecordHeader).Times(1);
+    EXPECT_CALL(mockEncoder, mockCreateSectionDescriptor).Times(kNumSections);
+    const std::string cperArray = mockEncoder.serializeToString();
+
+    // If this bricks, abort ship - there is likely memory corruption.
+    const uint64_t kExpectedCperSize =
+        (kSectionBodySize + kSectionDescriptorSizeBytes +
+         kRecordHeaderSizeBytes);
+
+    ASSERT_EQ(cperArray.size(), kExpectedCperSize);
+
+    // Check that the serialized arrays header contains the same contents we
+    // configured the encoder to use.
+    expectValidHeader(cperArray, kNumSections, kExpectedCperSize,
+                      mockEncoder.timestamp);
+
+    // Check that the section descriptor contains the correct information about
+    // the section.
+    const uint32_t kExpectedOffset = 0;
+    expectValidSectionDescriptor(cperArray, 0, kExpectedOffset,
+                                 kSectionBodySize, kMockSectionFlag,
+                                 kMockSectionType);
+
+    expectValidSectionBody<uint8_t>(cperArray, sectionBody, kNumSections,
+                                    kExpectedOffset);
+}
+
+TEST_F(CperEncoderTest, EncodeOneCharSectionToArray)
+{
+    MockCperEncoder<char> mockEncoder;
+
+    const std::string sectionBody =
+        "Hello world, this is a simplistic example of a CPER Section Body.";
+    const uint64_t kSectionBodySize = sectionBody.size();
+    constexpr uint16_t kNumSections = 1;
+
+    // Add 1 section
+    mockEncoder.addSection(sectionBody, kMockSectionFlag, kMockSectionType);
+
+    // Serialize the data into a CPER byte array.
+    EXPECT_CALL(mockEncoder, mockCreateRecordHeader).Times(1);
+    EXPECT_CALL(mockEncoder, mockCreateSectionDescriptor).Times(kNumSections);
+    const std::vector<uint8_t> cperArray = mockEncoder.serializeToByteArray();
+
+    // If this bricks, abort ship - there is likely memory corruption.
+    const uint64_t kExpectedCperSize =
+        (kSectionBodySize + kSectionDescriptorSizeBytes +
+         kRecordHeaderSizeBytes);
+
+    ASSERT_EQ(cperArray.size(), kExpectedCperSize);
+
+    // Check that the serialized arrays header contains the same contents we
+    // configured the encoder to use.
+    expectValidHeader(cperArray, kNumSections, kExpectedCperSize,
+                      mockEncoder.timestamp);
+
+    // Check that the section descriptor contains the correct information about
+    // the section.
+    const uint32_t kExpectedOffset = 0;
+    expectValidSectionDescriptor(cperArray, 0, kExpectedOffset,
+                                 kSectionBodySize, kMockSectionFlag,
+                                 kMockSectionType);
+
+    expectValidSectionBody<char>(cperArray, sectionBody, kNumSections,
+                                 kExpectedOffset);
+}
+
 TEST_F(CperEncoderTest, SeqMemcopy)
 {
     std::string section1 = "Hello";
diff --git a/test/mock_cper_encoder.hpp b/test/mock_cper_encoder.hpp
new file mode 100644
index 0000000..d314aeb
--- /dev/null
+++ b/test/mock_cper_encoder.hpp
@@ -0,0 +1,93 @@
+#pragma once
+
+#include "cper_encoder.hpp"
+
+#include "gmock/gmock.h"
+
+namespace uefi::cper
+{
+
+// Record Header
+inline constexpr uint8_t kMockRecordSeverity =
+    static_cast<uint8_t>(ErrorSeverity::kFatal);
+inline constexpr uint32_t kMockValidationBits = 0x5;
+
+inline constexpr guid_t kMockPlatformId{
+    0xF70B188B,
+    0x8431,
+    0x4AC0,
+    {0x85, 0x3B, 0x73, 0x44, 0x58, 0x82, 0x3E, 0xC5}};
+inline constexpr guid_t kMockPartitionId{
+    0xCAFDBB26,
+    0x7ED7,
+    0x4D4E,
+    {0x82, 0xBA, 0x68, 0xB0, 0x91, 0x20, 0xD5, 0x0B}};
+inline constexpr guid_t kMockCreatorId{
+    0x294C5454,
+    0x6809,
+    0x4C55,
+    {0xAD, 0x04, 0x82, 0x6B, 0xAD, 0xD8, 0x0A, 0x50}};
+
+inline constexpr guid_t kMockNotificationType = notification_type::kPEI;
+inline constexpr uint64_t kMockRecordId = 0xDEADBEEF;
+inline constexpr uint32_t kMockHeaderFlags = record_flags::kRecovered;
+inline constexpr uint64_t kMockPersistenceInformation = 0;
+
+// Section Descriptor
+inline constexpr uint16_t kMockSectionRevision = 0x0002;
+inline constexpr uint32_t kMockSectionValidationBits = 0x3;
+inline constexpr uint32_t kMockSectionFlag = 0x1;
+
+inline constexpr guid_t kMockSectionType{
+    0xF0E2FCA7,
+    0xAD2D,
+    0x4B17,
+    {0x95, 0x0C, 0x76, 0x88, 0x7E, 0x6A, 0x30, 0xF3}};
+inline constexpr guid_t kMockFruId{
+    0x1103ED84,
+    0x62DD,
+    0x4028,
+    {0xAE, 0x9D, 0x20, 0xCE, 0xF6, 0xE7, 0xB5, 0xA9}};
+
+inline constexpr uint8_t kMockSectionSeverity =
+    static_cast<uint8_t>(ErrorSeverity::kFatal);
+constexpr std::array<uint8_t, 20> kMockFruText = {'m', 'o', 'c', 'k'};
+
+template <ByteLike BodyType>
+class MockCperEncoder : public CperEncoder<BodyType>
+{
+  public:
+    const uint64_t timestamp;
+
+    MockCperEncoder() :
+        timestamp(createCperTimestamp(std::chrono::system_clock::now()))
+    {}
+
+    MOCK_METHOD((void), mockCreateRecordHeader, ());
+    MOCK_METHOD((void), mockCreateSectionDescriptor, ());
+
+    RecordHeader createRecordHeader(const uint32_t recordLength,
+                                    const uint16_t sectionCount) override
+    {
+        mockCreateRecordHeader();
+        return RecordHeader(sectionCount, kMockRecordSeverity,
+                            kMockValidationBits, recordLength, timestamp,
+                            kMockPlatformId, kMockPartitionId, kMockCreatorId,
+                            kMockNotificationType, kMockRecordId,
+                            kMockHeaderFlags, kMockPersistenceInformation);
+    }
+
+    SectionDescriptor createSectionDescriptor(const uint32_t sectionLength,
+                                              const uint32_t sectionOffset,
+                                              const uint32_t sectionFlags,
+                                              const guid_t sectionType) override
+    {
+        mockCreateSectionDescriptor();
+        return SectionDescriptor(
+            sectionOffset, sectionLength, kMockSectionRevision,
+            kMockSectionValidationBits, sectionFlags, sectionType, kMockFruId,
+            kMockSectionSeverity, kMockFruText);
+    }
+};
+
+}; // namespace uefi::cper