PEL: Add PEL class

This class represents a Platform Event Log.

A PEL consists of sections, and this commit just adds support for the
only required sections - the Private Header and User Header, by
including those specific objects.  More will be added in the future.

The only constructor provided in this commit is to construct the object
from an existing flattened PEL buffer.  This is for use in the case when
a PEL is received from off the BMC, such as from the host.  Future
commits will add support for creating PELs from OpenBMC event logs.

Since there aren't objects yet for every PEL section, the class cannot
make a full flattened PEL without still keeping around the original PEL
data it received in the constructor as mentioned above.  So for now it
will keep that data and just overlay the sections it does support when
flattening.  In the future, a fully formed PEL will be able to be
constructed from just flattening the section objects in the correct
order.

This commit provides a few public methods of note:

* data() - returns a flattened PEL
* assignID() - sets a unique ID in the log ID field in the Private
               Header
* setCommitTime() - Sets the commit timestamp in the Private Header
                    to the current time
* valid() - Says if the PEL is properly formed

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I2a9d82df9cd096ce77ecca7b2f73b097b8368aa2
diff --git a/test/openpower-pels/Makefile.include b/test/openpower-pels/Makefile.include
index 0e0257c..052aa2a 100644
--- a/test/openpower-pels/Makefile.include
+++ b/test/openpower-pels/Makefile.include
@@ -4,6 +4,7 @@
 	additional_data_test \
 	bcd_time_test \
 	log_id_test \
+	pel_test \
 	private_header_test \
 	section_header_test \
 	stream_test \
@@ -11,6 +12,8 @@
 
 pel_objects = \
 	$(top_builddir)/extensions/openpower-pels/bcd_time.o \
+	$(top_builddir)/extensions/openpower-pels/log_id.o \
+	$(top_builddir)/extensions/openpower-pels/pel.o \
 	$(top_builddir)/extensions/openpower-pels/private_header.o \
 	$(top_builddir)/extensions/openpower-pels/user_header.o
 
@@ -43,7 +46,7 @@
 section_header_test_LDFLAGS = $(test_ldflags)
 
 private_header_test_SOURCES = \
-	%reldir%/private_header_test.cpp %reldir%/pel_utils.cpp
+	%reldir%/private_header_test.cpp %reldir%/pel_utils.cpp %reldir%/paths.cpp
 private_header_test_CPPFLAGS = $(test_cppflags)
 private_header_test_CXXFLAGS = $(test_cxxflags)
 private_header_test_LDADD = \
@@ -52,7 +55,7 @@
 private_header_test_LDFLAGS = $(test_ldflags)
 
 user_header_test_SOURCES = \
-	%reldir%/user_header_test.cpp %reldir%/pel_utils.cpp
+	%reldir%/user_header_test.cpp %reldir%/pel_utils.cpp %reldir%/paths.cpp
 user_header_test_CPPFLAGS = $(test_cppflags)
 user_header_test_CXXFLAGS = $(test_cxxflags)
 user_header_test_LDADD = \
@@ -68,3 +71,12 @@
 	$(test_ldadd) \
 	$(top_builddir)/extensions/openpower-pels/log_id.o
 log_id_test_LDFLAGS = $(test_ldflags)
+
+pel_test_SOURCES = \
+	%reldir%/pel_test.cpp %reldir%/paths.cpp %reldir%/pel_utils.cpp
+pel_test_CPPFLAGS = $(test_cppflags)
+pel_test_CXXFLAGS = $(test_cxxflags)
+pel_test_LDADD = \
+	$(test_ldadd) \
+	$(pel_objects)
+pel_test_LDFLAGS = $(test_ldflags)
diff --git a/test/openpower-pels/pel_test.cpp b/test/openpower-pels/pel_test.cpp
new file mode 100644
index 0000000..ba71dc2
--- /dev/null
+++ b/test/openpower-pels/pel_test.cpp
@@ -0,0 +1,111 @@
+#include "extensions/openpower-pels/pel.hpp"
+#include "pel_utils.hpp"
+
+#include <filesystem>
+#include <fstream>
+
+#include <gtest/gtest.h>
+
+namespace fs = std::filesystem;
+using namespace openpower::pels;
+
+class PELTest : public CleanLogID
+{
+};
+
+TEST_F(PELTest, FlattenTest)
+{
+    auto data = pelDataFactory(TestPelType::pelSimple);
+    auto pel = std::make_unique<PEL>(*data);
+
+    // Check a few fields
+    EXPECT_TRUE(pel->valid());
+    EXPECT_EQ(pel->id(), 0x80818283);
+    EXPECT_EQ(pel->plid(), 0x50515253);
+    EXPECT_EQ(pel->userHeader()->subsystem(), 0x10);
+    EXPECT_EQ(pel->userHeader()->actionFlags(), 0x80C0);
+
+    // Test that data in == data out
+    auto flattenedData = pel->data();
+    ASSERT_EQ(*data, flattenedData);
+}
+
+TEST_F(PELTest, CommitTimeTest)
+{
+    auto data = pelDataFactory(TestPelType::pelSimple);
+    auto pel = std::make_unique<PEL>(*data);
+
+    auto origTime = pel->commitTime();
+    pel->setCommitTime();
+    auto newTime = pel->commitTime();
+
+    ASSERT_NE(origTime, newTime);
+
+    // Make a new PEL and check new value is still there
+    auto newData = pel->data();
+    auto newPel = std::make_unique<PEL>(newData);
+    ASSERT_EQ(newTime, newPel->commitTime());
+}
+
+TEST_F(PELTest, AssignIDTest)
+{
+    auto data = pelDataFactory(TestPelType::pelSimple);
+    auto pel = std::make_unique<PEL>(*data);
+
+    auto origID = pel->id();
+    pel->assignID();
+    auto newID = pel->id();
+
+    ASSERT_NE(origID, newID);
+
+    // Make a new PEL and check new value is still there
+    auto newData = pel->data();
+    auto newPel = std::make_unique<PEL>(newData);
+    ASSERT_EQ(newID, newPel->id());
+}
+
+TEST_F(PELTest, WithLogIDTest)
+{
+    auto data = pelDataFactory(TestPelType::pelSimple);
+    auto pel = std::make_unique<PEL>(*data, 0x42);
+
+    EXPECT_TRUE(pel->valid());
+    EXPECT_EQ(pel->obmcLogID(), 0x42);
+}
+
+TEST_F(PELTest, InvalidPELTest)
+{
+    auto data = pelDataFactory(TestPelType::pelSimple);
+
+    // Too small
+    data->resize(PrivateHeader::flattenedSize());
+
+    auto pel = std::make_unique<PEL>(*data);
+
+    EXPECT_TRUE(pel->privateHeader()->valid());
+    EXPECT_FALSE(pel->userHeader()->valid());
+    EXPECT_FALSE(pel->valid());
+
+    // Ensure we can still flatten bad data
+    auto newData = pel->data();
+    EXPECT_EQ(*data, newData);
+
+    // Now corrupt the private header
+    data = pelDataFactory(TestPelType::pelSimple);
+    data->at(0) = 0;
+    pel = std::make_unique<PEL>(*data);
+
+    EXPECT_FALSE(pel->privateHeader()->valid());
+    EXPECT_TRUE(pel->userHeader()->valid());
+    EXPECT_FALSE(pel->valid());
+}
+
+TEST_F(PELTest, EmptyDataTest)
+{
+    std::vector<uint8_t> data;
+    auto pel = std::make_unique<PEL>(data);
+
+    EXPECT_FALSE(pel->privateHeader()->valid());
+    EXPECT_FALSE(pel->userHeader()->valid());
+    EXPECT_FALSE(pel->valid());
+}
diff --git a/test/openpower-pels/pel_utils.cpp b/test/openpower-pels/pel_utils.cpp
index b6714eb..4541694 100644
--- a/test/openpower-pels/pel_utils.cpp
+++ b/test/openpower-pels/pel_utils.cpp
@@ -10,6 +10,8 @@
 namespace fs = std::filesystem;
 using namespace openpower::pels;
 
+std::filesystem::path CleanLogID::pelIDFile{};
+
 constexpr uint8_t simplePEL[] = {
     // private header section header
     0x50, 0x48, // ID 'PH'
diff --git a/test/openpower-pels/pel_utils.hpp b/test/openpower-pels/pel_utils.hpp
index 65fa196..7475720 100644
--- a/test/openpower-pels/pel_utils.hpp
+++ b/test/openpower-pels/pel_utils.hpp
@@ -1,3 +1,5 @@
+#include "extensions/openpower-pels/paths.hpp"
+
 #include <filesystem>
 #include <memory>
 #include <vector>
@@ -5,6 +7,26 @@
 #include <gtest/gtest.h>
 
 /**
+ * @brief Test fixture to remove the pelID file that PELs use.
+ */
+class CleanLogID : public ::testing::Test
+{
+  protected:
+    static void SetUpTestCase()
+    {
+        pelIDFile = openpower::pels::getPELIDFile();
+    }
+
+    static void TearDownTestCase()
+    {
+        std::filesystem::remove_all(
+            std::filesystem::path{pelIDFile}.parent_path());
+    }
+
+    static std::filesystem::path pelIDFile;
+};
+
+/**
  * @brief Tells the factory which PEL to create
  */
 enum class TestPelType