PEL: MRU callout SRC substructure

This substructure is part of the callout subsection in the SRC section
of a PEL, and contains information about Manufacturing Replaceable Units
(MRUs).  MRUs are components on a parent FRU (Field Replaceable Unit)
that may be able to be replaced in a manufacturing environment, hence
the name.

This substructure includes a list of <priority, MRU ID> pairs, where the
priority is the same priority value type as used elsewhere in the SRC
section ('H', 'M', 'L', etc), and the MRU ID is a 4B ID that development
will tell manufacturing the meanings of.

This commit only adds support for creating an object from a flattened PEL,
such as one that comes down from the host.  A future commit will handle
creating it from scratch for BMC errors.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I6352e1a3cb84db0516902786faca4c387afef411
diff --git a/test/openpower-pels/Makefile.include b/test/openpower-pels/Makefile.include
index 74e33c6..e54bce8 100644
--- a/test/openpower-pels/Makefile.include
+++ b/test/openpower-pels/Makefile.include
@@ -8,6 +8,7 @@
 	fru_identity_test \
 	generic_section_test \
 	log_id_test \
+	mru_test \
 	mtms_test \
 	pce_identity_test \
 	pel_test \
@@ -218,3 +219,11 @@
 	$(top_builddir)/extensions/openpower-pels/pce_identity.o \
 	$(top_builddir)/extensions/openpower-pels/mtms.o
 pce_identity_test_LDFLAGS = $(test_ldflags)
+
+mru_test_SOURCES = %reldir%/mru_test.cpp
+mru_test_CPPFLAGS = $(test_cppflags)
+mru_test_CXXFLAGS = $(test_cxxflags)
+mru_test_LDADD = \
+	$(test_ldadd) \
+	$(top_builddir)/extensions/openpower-pels/mru.o
+mru_test_LDFLAGS = $(test_ldflags)
diff --git a/test/openpower-pels/mru_test.cpp b/test/openpower-pels/mru_test.cpp
new file mode 100644
index 0000000..38e80f9
--- /dev/null
+++ b/test/openpower-pels/mru_test.cpp
@@ -0,0 +1,59 @@
+#include "extensions/openpower-pels/mru.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+using namespace openpower::pels::src;
+
+TEST(MRUTest, TestConstructor)
+{
+    std::vector<uint8_t> data{
+        'M',  'R',  0x28, 0x04, // ID, size, flags
+        0x00, 0x00, 0x00, 0x00, // Reserved
+        0x00, 0x00, 0x00, 'H',  // priority for MRU ID 0
+        0x01, 0x01, 0x01, 0x01, // MRU ID 0
+        0x00, 0x00, 0x00, 'M',  // priority for MRU ID 1
+        0x02, 0x02, 0x02, 0x02, // MRU ID 1
+        0x00, 0x00, 0x00, 'L',  // priority for MRU ID 2
+        0x03, 0x03, 0x03, 0x03, // MRU ID 2
+        0x00, 0x00, 0x00, 'H',  // priority for MRU ID 3
+        0x04, 0x04, 0x04, 0x04, // MRU ID 3
+    };
+
+    Stream stream{data};
+
+    MRU mru{stream};
+
+    EXPECT_EQ(mru.flattenedSize(), data.size());
+    EXPECT_EQ(mru.mrus().size(), 4);
+
+    EXPECT_EQ(mru.mrus().at(0).priority, 'H');
+    EXPECT_EQ(mru.mrus().at(0).id, 0x01010101);
+    EXPECT_EQ(mru.mrus().at(1).priority, 'M');
+    EXPECT_EQ(mru.mrus().at(1).id, 0x02020202);
+    EXPECT_EQ(mru.mrus().at(2).priority, 'L');
+    EXPECT_EQ(mru.mrus().at(2).id, 0x03030303);
+    EXPECT_EQ(mru.mrus().at(3).priority, 'H');
+    EXPECT_EQ(mru.mrus().at(3).id, 0x04040404);
+
+    // Now flatten
+    std::vector<uint8_t> newData;
+    Stream newStream{newData};
+
+    mru.flatten(newStream);
+    EXPECT_EQ(data, newData);
+}
+
+TEST(MRUTest, TestBadData)
+{
+    // 4 MRUs expected, but only 1
+    std::vector<uint8_t> data{
+        'M',  'R',  0x28, 0x04, // ID, size, flags
+        0x00, 0x00, 0x00, 0x00, // Reserved
+        0x00, 0x00, 0x00, 'H',  // priority 0
+        0x01, 0x01, 0x01, 0x01, // MRU ID 0
+    };
+
+    Stream stream{data};
+    EXPECT_THROW(MRU mru{stream}, std::out_of_range);
+}