summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/Makefile.am44
-rw-r--r--test/extensions_test.cpp98
-rw-r--r--test/openpower-pels/Makefile.include364
-rw-r--r--test/openpower-pels/additional_data_test.cpp65
-rw-r--r--test/openpower-pels/ascii_string_test.cpp86
-rw-r--r--test/openpower-pels/bcd_time_test.cpp105
-rw-r--r--test/openpower-pels/event_logger_test.cpp126
-rw-r--r--test/openpower-pels/extended_user_header_test.cpp301
-rw-r--r--test/openpower-pels/failing_mtms_test.cpp133
-rw-r--r--test/openpower-pels/fru_identity_test.cpp89
-rw-r--r--test/openpower-pels/generic_section_test.cpp64
-rw-r--r--test/openpower-pels/host_notifier_test.cpp680
-rw-r--r--test/openpower-pels/json_utils_test.cpp50
-rw-r--r--test/openpower-pels/log_id_test.cpp57
-rw-r--r--test/openpower-pels/mocks.hpp239
-rw-r--r--test/openpower-pels/mru_test.cpp74
-rw-r--r--test/openpower-pels/mtms_test.cpp122
-rw-r--r--test/openpower-pels/paths.cpp67
-rw-r--r--test/openpower-pels/pce_identity_test.cpp57
-rw-r--r--test/openpower-pels/pel_manager_test.cpp514
-rw-r--r--test/openpower-pels/pel_rules_test.cpp72
-rw-r--r--test/openpower-pels/pel_test.cpp387
-rw-r--r--test/openpower-pels/pel_utils.cpp297
-rw-r--r--test/openpower-pels/pel_utils.hpp106
-rw-r--r--test/openpower-pels/pel_values_test.cpp40
-rw-r--r--test/openpower-pels/private_header_test.cpp194
-rw-r--r--test/openpower-pels/real_pel_test.cpp570
-rw-r--r--test/openpower-pels/registry_test.cpp336
-rw-r--r--test/openpower-pels/repository_test.cpp436
-rw-r--r--test/openpower-pels/section_header_test.cpp55
-rw-r--r--test/openpower-pels/severity_test.cpp33
-rw-r--r--test/openpower-pels/src_callout_test.cpp161
-rw-r--r--test/openpower-pels/src_callouts_test.cpp91
-rw-r--r--test/openpower-pels/src_test.cpp261
-rw-r--r--test/openpower-pels/stream_test.cpp197
-rw-r--r--test/openpower-pels/user_data_test.cpp106
-rw-r--r--test/openpower-pels/user_header_test.cpp163
-rw-r--r--test/remote_logging_test_config.cpp21
38 files changed, 6828 insertions, 33 deletions
diff --git a/test/Makefile.am b/test/Makefile.am
index 7fd38d1..64aa6d8 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -9,7 +9,8 @@ check_PROGRAMS = \
remote_logging_test_address \
remote_logging_test_port \
remote_logging_test_config \
- sdjournal_mock_test
+ sdjournal_mock_test \
+ extensions_test
test_cppflags = \
-Igtest \
@@ -23,7 +24,7 @@ test_cxxflags = \
test_ldflags = \
-lgtest_main -lgtest \
- -lgmock \
+ -lgmock -lstdc++fs \
$(PTHREAD_LIBS) \
$(OESDK_TESTCASE_FLAGS) \
$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
@@ -33,12 +34,12 @@ test_ldadd = \
$(top_builddir)/elog_serialize.o \
$(top_builddir)/elog_entry.o \
$(top_builddir)/log_manager.o \
- $(top_builddir)/org.openbmc.Associations.o \
$(top_builddir)/xyz/openbmc_project/Logging/Internal/Manager/server.o \
$(top_builddir)/elog_meta.o \
$(top_builddir)/elog-lookup.o \
$(top_builddir)/elog-process-metadata.o \
- $(top_builddir)/sdjournal.o
+ $(top_builddir)/sdjournal.o \
+ $(top_builddir)/extensions.o
remote_logging_test_ldadd = \
$(top_builddir)/phosphor-rsyslog-config/server-conf.o \
@@ -49,48 +50,42 @@ elog_errorwrap_test_CXXFLAGS = $(test_cxxflags)
elog_errorwrap_test_SOURCES = elog_errorwrap_test.cpp
elog_errorwrap_test_LDADD = $(test_ldadd)
elog_errorwrap_test_LDFLAGS = \
- $(test_ldflags) \
- -lstdc++fs
+ $(test_ldflags)
serialization_test_path_CPPFLAGS = $(test_cppflags)
serialization_test_path_CXXFLAGS = $(test_cxxflags)
serialization_test_path_SOURCES = serialization_test_path.cpp
serialization_test_path_LDADD = $(test_ldadd)
serialization_test_path_LDFLAGS = \
- $(test_ldflags) \
- -lstdc++fs
+ $(test_ldflags)
serialization_test_properties_CPPFLAGS = $(test_cppflags)
serialization_test_properties_CXXFLAGS = $(test_cxxflags)
serialization_test_properties_SOURCES = serialization_test_properties.cpp
serialization_test_properties_LDADD = $(test_ldadd)
serialization_test_properties_LDFLAGS = \
- $(test_ldflags) \
- -lstdc++fs
+ $(test_ldflags)
remote_logging_test_address_CPPFLAGS = $(test_cppflags)
remote_logging_test_address_CXXFLAGS = $(test_cxxflags)
remote_logging_test_address_SOURCES = remote_logging_test_address.cpp
remote_logging_test_address_LDADD = $(remote_logging_test_ldadd)
remote_logging_test_address_LDFLAGS = \
- $(test_ldflags) \
- -lstdc++fs
+ $(test_ldflags)
remote_logging_test_port_CPPFLAGS = $(test_cppflags)
remote_logging_test_port_CXXFLAGS = $(test_cxxflags)
remote_logging_test_port_SOURCES = remote_logging_test_port.cpp
remote_logging_test_port_LDADD = $(remote_logging_test_ldadd)
remote_logging_test_port_LDFLAGS = \
- $(test_ldflags) \
- -lstdc++fs
+ $(test_ldflags)
remote_logging_test_config_CPPFLAGS = $(test_cppflags)
remote_logging_test_config_CXXFLAGS = $(test_cxxflags)
remote_logging_test_config_SOURCES = remote_logging_test_config.cpp
remote_logging_test_config_LDADD = $(remote_logging_test_ldadd)
remote_logging_test_config_LDFLAGS = \
- $(test_ldflags) \
- -lstdc++fs
+ $(test_ldflags)
sdjournal_mock_test_CPPFLAGS = $(test_cppflags)
sdjournal_mock_test_CXXFLAGS = $(test_cxxflags)
@@ -98,5 +93,22 @@ sdjournal_mock_test_SOURCES = sdtest.cpp
sdjournal_mock_test_LDADD = $(top_builddir)/sdjournal.o
sdjournal_mock_test_LDFLAGS = $(test_ldflags)
+extensions_test_CPPFLAGS = $(test_cppflags)
+extensions_test_CXXFLAGS = $(test_cxxflags)
+extensions_test_SOURCES = extensions_test.cpp
+extensions_test_LDADD = \
+ $(top_builddir)/elog_entry.o \
+ $(top_builddir)/elog-lookup.o \
+ $(top_builddir)/elog_meta.o \
+ $(top_builddir)/elog-process-metadata.o \
+ $(top_builddir)/elog_serialize.o \
+ $(top_builddir)/log_manager.o \
+ $(top_builddir)/xyz/openbmc_project/Logging/Internal/Manager/server.o
+extensions_test_LDFLAGS = $(test_ldflags)
+
# TODO Remove once the test-case failure is resolved openbmc/phosphor-logging#11
XFAIL_TESTS = elog_errorwrap_test
+
+if ENABLE_PEL_EXTENSION
+include openpower-pels/Makefile.include
+endif
diff --git a/test/extensions_test.cpp b/test/extensions_test.cpp
new file mode 100644
index 0000000..17c3395
--- /dev/null
+++ b/test/extensions_test.cpp
@@ -0,0 +1,98 @@
+#include "elog_entry.hpp"
+#include "extensions.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace phosphor::logging;
+
+// gtest doesn't like this happening in another file, so do it here.
+StartupFunctions Extensions::startupFunctions{};
+CreateFunctions Extensions::createFunctions{};
+DeleteFunctions Extensions::deleteFunctions{};
+DeleteProhibitedFunctions Extensions::deleteProhibitedFunctions{};
+Extensions::DefaultErrorCaps Extensions::defaultErrorCaps =
+ Extensions::DefaultErrorCaps::enable;
+
+void startup1(internal::Manager& manager)
+{
+}
+
+void startup2(internal::Manager& manager)
+{
+}
+
+void create1(const std::string& message, uint32_t id, uint64_t timestamp,
+ Entry::Level severity, const AdditionalDataArg& additionalData,
+ const AssociationEndpointsArg& assocs)
+{
+}
+
+void create2(const std::string& message, uint32_t id, uint64_t timestamp,
+ Entry::Level severity, const AdditionalDataArg& additionalData,
+ const AssociationEndpointsArg& assocs)
+{
+}
+
+void deleteLog1(uint32_t id)
+{
+}
+
+void deleteLog2(uint32_t id)
+{
+}
+
+void deleteProhibited1(uint32_t id, bool& prohibited)
+{
+ prohibited = true;
+}
+
+void deleteProhibited2(uint32_t id, bool& prohibited)
+{
+ prohibited = true;
+}
+
+DISABLE_LOG_ENTRY_CAPS();
+REGISTER_EXTENSION_FUNCTION(startup1);
+REGISTER_EXTENSION_FUNCTION(startup2);
+REGISTER_EXTENSION_FUNCTION(create1);
+REGISTER_EXTENSION_FUNCTION(create2);
+REGISTER_EXTENSION_FUNCTION(deleteProhibited1);
+REGISTER_EXTENSION_FUNCTION(deleteProhibited2);
+REGISTER_EXTENSION_FUNCTION(deleteLog1);
+REGISTER_EXTENSION_FUNCTION(deleteLog2);
+
+TEST(ExtensionsTest, FunctionCallTest)
+{
+ auto bus = sdbusplus::bus::new_default();
+ internal::Manager manager(bus, "testpath");
+
+ EXPECT_EQ(Extensions::getStartupFunctions().size(), 2);
+ for (auto& s : Extensions::getStartupFunctions())
+ {
+ s(manager);
+ }
+
+ AdditionalDataArg ad;
+ AssociationEndpointsArg assocs;
+ EXPECT_EQ(Extensions::getCreateFunctions().size(), 2);
+ for (auto& c : Extensions::getCreateFunctions())
+ {
+ c("test", 5, 6, Entry::Level::Informational, ad, assocs);
+ }
+
+ EXPECT_EQ(Extensions::getDeleteFunctions().size(), 2);
+ for (auto& d : Extensions::getDeleteFunctions())
+ {
+ d(5);
+ }
+
+ EXPECT_EQ(Extensions::getDeleteProhibitedFunctions().size(), 2);
+ for (auto& p : Extensions::getDeleteProhibitedFunctions())
+ {
+ bool prohibited = false;
+ p(5, prohibited);
+ EXPECT_TRUE(prohibited);
+ }
+
+ EXPECT_TRUE(Extensions::disableDefaultLogCaps());
+}
diff --git a/test/openpower-pels/Makefile.include b/test/openpower-pels/Makefile.include
new file mode 100644
index 0000000..09eb36b
--- /dev/null
+++ b/test/openpower-pels/Makefile.include
@@ -0,0 +1,364 @@
+TESTS += $(check_PROGRAMS)
+
+check_PROGRAMS += \
+ additional_data_test \
+ ascii_string_test \
+ bcd_time_test \
+ event_logger_test \
+ extended_user_header_test \
+ failing_mtms_test \
+ fru_identity_test \
+ generic_section_test \
+ host_notifier_test \
+ json_utils_test \
+ log_id_test \
+ mru_test \
+ mtms_test \
+ pce_identity_test \
+ pel_manager_test \
+ pel_rules_test \
+ pel_test \
+ pel_values_test \
+ private_header_test \
+ real_pel_test \
+ registry_test \
+ repository_test \
+ section_header_test \
+ severity_test \
+ src_test \
+ src_callout_test \
+ src_callouts_test \
+ stream_test \
+ user_data_test \
+ user_header_test
+
+pel_objects = \
+ $(top_builddir)/extensions/openpower-pels/ascii_string.o \
+ $(top_builddir)/extensions/openpower-pels/bcd_time.o \
+ $(top_builddir)/extensions/openpower-pels/callout.o \
+ $(top_builddir)/extensions/openpower-pels/callouts.o \
+ $(top_builddir)/extensions/openpower-pels/extended_user_header.o \
+ $(top_builddir)/extensions/openpower-pels/failing_mtms.o \
+ $(top_builddir)/extensions/openpower-pels/fru_identity.o \
+ $(top_builddir)/extensions/openpower-pels/generic.o \
+ $(top_builddir)/extensions/openpower-pels/json_utils.o \
+ $(top_builddir)/extensions/openpower-pels/log_id.o \
+ $(top_builddir)/extensions/openpower-pels/mtms.o \
+ $(top_builddir)/extensions/openpower-pels/mru.o \
+ $(top_builddir)/extensions/openpower-pels/pce_identity.o \
+ $(top_builddir)/extensions/openpower-pels/pel.o \
+ $(top_builddir)/extensions/openpower-pels/pel_rules.o \
+ $(top_builddir)/extensions/openpower-pels/pel_values.o \
+ $(top_builddir)/extensions/openpower-pels/private_header.o \
+ $(top_builddir)/extensions/openpower-pels/registry.o \
+ $(top_builddir)/extensions/openpower-pels/section_factory.o \
+ $(top_builddir)/extensions/openpower-pels/severity.o \
+ $(top_builddir)/extensions/openpower-pels/src.o \
+ $(top_builddir)/extensions/openpower-pels/user_data.o \
+ $(top_builddir)/extensions/openpower-pels/user_header.o
+
+additional_data_test_SOURCES = %reldir%/additional_data_test.cpp
+additional_data_test_CPPFLAGS = $(test_cppflags)
+additional_data_test_CXXFLAGS = $(test_cxxflags)
+additional_data_test_LDADD = $(test_ldadd)
+additional_data_test_LDFLAGS = $(test_ldflags)
+
+stream_test_SOURCES = %reldir%/stream_test.cpp
+stream_test_CPPFLAGS = $(test_cppflags)
+stream_test_CXXFLAGS = $(test_cxxflags)
+stream_test_LDADD = $(test_ldadd)
+stream_test_LDFLAGS = $(test_ldflags)
+
+bcd_time_test_SOURCES = \
+ %reldir%/bcd_time_test.cpp
+bcd_time_test_CPPFLAGS = $(test_cppflags)
+bcd_time_test_CXXFLAGS = $(test_cxxflags)
+bcd_time_test_LDADD = \
+ $(test_ldadd) \
+ $(top_builddir)/extensions/openpower-pels/bcd_time.o
+bcd_time_test_LDFLAGS = $(test_ldflags)
+
+section_header_test_SOURCES = \
+ %reldir%/section_header_test.cpp
+section_header_test_CPPFLAGS = $(test_cppflags)
+section_header_test_CXXFLAGS = $(test_cxxflags)
+section_header_test_LDADD = $(test_ldadd)
+section_header_test_LDFLAGS = $(test_ldflags)
+
+private_header_test_SOURCES = \
+ %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 = \
+ $(test_ldadd) \
+ $(pel_objects)
+private_header_test_LDFLAGS = $(test_ldflags)
+
+user_header_test_SOURCES = \
+ %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 = \
+ $(test_ldadd) \
+ $(pel_objects)
+user_header_test_LDFLAGS = $(test_ldflags)
+
+log_id_test_SOURCES = \
+ %reldir%/log_id_test.cpp %reldir%/paths.cpp
+log_id_test_CPPFLAGS = $(test_cppflags)
+log_id_test_CXXFLAGS = $(test_cxxflags)
+log_id_test_LDADD = \
+ $(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)
+
+real_pel_test_SOURCES = \
+ %reldir%/real_pel_test.cpp %reldir%/paths.cpp %reldir%/pel_utils.cpp
+real_pel_test_CPPFLAGS = $(test_cppflags)
+real_pel_test_CXXFLAGS = $(test_cxxflags)
+real_pel_test_LDADD = \
+ $(test_ldadd) \
+ $(pel_objects)
+real_pel_test_LDFLAGS = $(test_ldflags)
+
+repository_test_SOURCES = \
+ %reldir%/repository_test.cpp %reldir%/paths.cpp %reldir%/pel_utils.cpp
+repository_test_CPPFLAGS = $(test_cppflags)
+repository_test_CXXFLAGS = $(test_cxxflags)
+repository_test_LDADD = \
+ $(test_ldadd) \
+ $(pel_objects) \
+ $(top_builddir)/extensions/openpower-pels/repository.o
+repository_test_LDFLAGS = $(test_ldflags)
+
+pel_manager_test_SOURCES = \
+ %reldir%/pel_manager_test.cpp %reldir%/paths.cpp %reldir%/pel_utils.cpp
+pel_manager_test_CPPFLAGS = $(test_cppflags)
+pel_manager_test_CXXFLAGS = \
+ $(test_cxxflags) \
+ $(SDEVENTPLUS_CFLAGS)
+pel_manager_test_LDADD = \
+ $(test_ldadd) \
+ $(pel_objects) \
+ $(top_builddir)/extensions/openpower-pels/data_interface.o \
+ $(top_builddir)/extensions/openpower-pels/host_notifier.o \
+ $(top_builddir)/extensions/openpower-pels/manager.o \
+ $(top_builddir)/extensions/openpower-pels/repository.o
+pel_manager_test_LDFLAGS = \
+ $(test_ldflags) \
+ $(SDEVENTPLUS_LIBS)
+
+registry_test_SOURCES = \
+ %reldir%/registry_test.cpp %reldir%/paths.cpp
+registry_test_CPPFLAGS = $(test_cppflags)
+registry_test_CXXFLAGS = $(test_cxxflags)
+registry_test_LDADD = \
+ $(test_ldadd) \
+ $(top_builddir)/extensions/openpower-pels/registry.o \
+ $(top_builddir)/extensions/openpower-pels/pel_values.o
+registry_test_LDFLAGS = $(test_ldflags)
+
+severity_test_SOURCES = %reldir%/severity_test.cpp
+severity_test_CPPFLAGS = $(test_cppflags)
+severity_test_CXXFLAGS = $(test_cxxflags)
+severity_test_LDADD = \
+ $(test_ldflags) \
+ $(top_builddir)/extensions/openpower-pels/severity.o
+severity_test_LDFLAGS = $(test_ldflags)
+
+mtms_test_SOURCES = %reldir%/mtms_test.cpp
+mtms_test_CPPFLAGS = $(test_cppflags)
+mtms_test_CXXFLAGS = $(test_cxxflags)
+mtms_test_LDADD = \
+ $(test_ldadd) \
+ $(top_builddir)/extensions/openpower-pels/mtms.o
+mtms_test_LDFLAGS = $(test_ldflags)
+
+failing_mtms_test_SOURCES = %reldir%/failing_mtms_test.cpp
+failing_mtms_test_CPPFLAGS = $(test_cppflags)
+failing_mtms_test_CXXFLAGS = $(test_cxxflags)
+failing_mtms_test_LDADD = \
+ $(test_ldadd) \
+ $(top_builddir)/extensions/openpower-pels/failing_mtms.o \
+ $(top_builddir)/extensions/openpower-pels/json_utils.o \
+ $(top_builddir)/extensions/openpower-pels/mtms.o
+failing_mtms_test_LDFLAGS = $(test_ldflags)
+
+pel_values_test_SOURCES = %reldir%/pel_values_test.cpp
+pel_values_test_CPPFLAGS = $(test_cppflags)
+pel_values_test_CXXFLAGS = $(test_cxxflags)
+pel_values_test_LDADD = \
+ $(test_ldflags) \
+ $(top_builddir)/extensions/openpower-pels/pel_values.o
+pel_values_test_LDFLAGS = $(test_ldflags)
+
+generic_section_test_SOURCES = \
+ %reldir%/generic_section_test.cpp %reldir%/pel_utils.cpp
+generic_section_test_CPPFLAGS = $(test_cppflags)
+generic_section_test_CXXFLAGS = $(test_cxxflags)
+generic_section_test_LDADD = \
+ $(test_ldadd) \
+ $(top_builddir)/extensions/openpower-pels/generic.o
+generic_section_test_LDFLAGS = $(test_ldflags)
+
+user_data_test_SOURCES = \
+ %reldir%/user_data_test.cpp %reldir%/pel_utils.cpp
+user_data_test_CPPFLAGS = $(test_cppflags)
+user_data_test_CXXFLAGS = $(test_cxxflags)
+user_data_test_LDADD = \
+ $(test_ldadd) \
+ $(top_builddir)/extensions/openpower-pels/user_data.o
+user_data_test_LDFLAGS = $(test_ldflags)
+
+ascii_string_test_SOURCES = %reldir%/ascii_string_test.cpp
+ascii_string_test_CPPFLAGS = $(test_cppflags)
+ascii_string_test_CXXFLAGS = $(test_cxxflags)
+ascii_string_test_LDADD = \
+ $(test_ldadd) \
+ $(top_builddir)/extensions/openpower-pels/ascii_string.o
+ascii_string_test_LDFLAGS = $(test_ldflags)
+
+fru_identity_test_SOURCES = %reldir%/fru_identity_test.cpp
+fru_identity_test_CPPFLAGS = $(test_cppflags)
+fru_identity_test_CXXFLAGS = $(test_cxxflags)
+fru_identity_test_LDADD = \
+ $(test_ldadd) \
+ $(top_builddir)/extensions/openpower-pels/fru_identity.o
+fru_identity_test_LDFLAGS = $(test_ldflags)
+
+pce_identity_test_SOURCES = %reldir%/pce_identity_test.cpp
+pce_identity_test_CPPFLAGS = $(test_cppflags)
+pce_identity_test_CXXFLAGS = $(test_cxxflags)
+pce_identity_test_LDADD = \
+ $(test_ldadd) \
+ $(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)
+
+src_callout_test_SOURCES = \
+ %reldir%/src_callout_test.cpp \
+ %reldir%/pel_utils.cpp
+src_callout_test_CPPFLAGS = $(test_cppflags)
+src_callout_test_CXXFLAGS = $(test_cxxflags)
+src_callout_test_LDADD = \
+ $(test_ldadd) \
+ $(top_builddir)/extensions/openpower-pels/callout.o \
+ $(top_builddir)/extensions/openpower-pels/fru_identity.o \
+ $(top_builddir)/extensions/openpower-pels/mru.o \
+ $(top_builddir)/extensions/openpower-pels/mtms.o \
+ $(top_builddir)/extensions/openpower-pels/pce_identity.o
+src_callout_test_LDFLAGS = $(test_ldflags)
+
+src_callouts_test_SOURCES = \
+ %reldir%/src_callouts_test.cpp \
+ %reldir%/pel_utils.cpp
+src_callouts_test_CPPFLAGS = $(test_cppflags)
+src_callouts_test_CXXFLAGS = $(test_cxxflags)
+src_callouts_test_LDADD = \
+ $(test_ldadd) \
+ $(top_builddir)/extensions/openpower-pels/callout.o \
+ $(top_builddir)/extensions/openpower-pels/callouts.o \
+ $(top_builddir)/extensions/openpower-pels/fru_identity.o \
+ $(top_builddir)/extensions/openpower-pels/mru.o \
+ $(top_builddir)/extensions/openpower-pels/mtms.o \
+ $(top_builddir)/extensions/openpower-pels/pce_identity.o
+src_callouts_test_LDFLAGS = $(test_ldflags)
+
+src_test_SOURCES = \
+ %reldir%/src_test.cpp \
+ %reldir%/pel_utils.cpp
+src_test_CPPFLAGS = $(test_cppflags)
+src_test_CXXFLAGS = $(test_cxxflags)
+src_test_LDADD = \
+ $(test_ldadd) \
+ $(top_builddir)/extensions/openpower-pels/ascii_string.o \
+ $(top_builddir)/extensions/openpower-pels/callout.o \
+ $(top_builddir)/extensions/openpower-pels/callouts.o \
+ $(top_builddir)/extensions/openpower-pels/fru_identity.o \
+ $(top_builddir)/extensions/openpower-pels/json_utils.o \
+ $(top_builddir)/extensions/openpower-pels/paths.o \
+ $(top_builddir)/extensions/openpower-pels/mru.o \
+ $(top_builddir)/extensions/openpower-pels/mtms.o \
+ $(top_builddir)/extensions/openpower-pels/pce_identity.o \
+ $(top_builddir)/extensions/openpower-pels/pel_values.o \
+ $(top_builddir)/extensions/openpower-pels/registry.o \
+ $(top_builddir)/extensions/openpower-pels/src.o
+src_test_LDFLAGS = $(test_ldflags)
+
+extended_user_header_test_SOURCES = \
+ %reldir%/extended_user_header_test.cpp \
+ %reldir%/pel_utils.cpp
+extended_user_header_test_CPPFLAGS = $(test_cppflags)
+extended_user_header_test_CXXFLAGS = $(test_cxxflags)
+extended_user_header_test_LDADD = \
+ $(test_ldadd) \
+ $(top_builddir)/extensions/openpower-pels/ascii_string.o \
+ $(top_builddir)/extensions/openpower-pels/bcd_time.o \
+ $(top_builddir)/extensions/openpower-pels/callout.o \
+ $(top_builddir)/extensions/openpower-pels/callouts.o \
+ $(top_builddir)/extensions/openpower-pels/data_interface.o \
+ $(top_builddir)/extensions/openpower-pels/extended_user_header.o \
+ $(top_builddir)/extensions/openpower-pels/fru_identity.o \
+ $(top_builddir)/extensions/openpower-pels/json_utils.o \
+ $(top_builddir)/extensions/openpower-pels/mru.o \
+ $(top_builddir)/extensions/openpower-pels/mtms.o \
+ $(top_builddir)/extensions/openpower-pels/paths.o \
+ $(top_builddir)/extensions/openpower-pels/pel_values.o \
+ $(top_builddir)/extensions/openpower-pels/pce_identity.o \
+ $(top_builddir)/extensions/openpower-pels/registry.o \
+ $(top_builddir)/extensions/openpower-pels/src.o
+extended_user_header_test_LDFLAGS = $(test_ldflags)
+
+pel_rules_test_SOURCES = %reldir%/pel_rules_test.cpp
+pel_rules_test_CPPFLAGS = $(test_cppflags)
+pel_rules_test_CXXFLAGS = $(test_cxxflags)
+pel_rules_test_LDADD = \
+ $(test_ldflags) \
+ $(top_builddir)/extensions/openpower-pels/pel_rules.o
+pel_rules_test_LDFLAGS = $(test_ldflags)
+
+host_notifier_test_SOURCES = \
+ %reldir%/host_notifier_test.cpp \
+ %reldir%/paths.cpp \
+ %reldir%/pel_utils.cpp
+host_notifier_test_CPPFLAGS = $(test_cppflags)
+host_notifier_test_CXXFLAGS = $(test_cxxflags) $(SDEVENTPLUS_CFLAGS)
+host_notifier_test_LDADD = \
+ $(test_ldflags) \
+ $(pel_objects) \
+ $(top_builddir)/extensions/openpower-pels/host_notifier.o \
+ $(top_builddir)/extensions/openpower-pels/repository.o
+host_notifier_test_LDFLAGS = $(test_ldflags) $(SDEVENTPLUS_LIBS)
+
+json_utils_test_SOURCES = %reldir%/json_utils_test.cpp
+json_utils_test_CPPFLAGS = $(test_cppflags)
+json_utils_test_CXXFLAGS = $(test_cxxflags)
+json_utils_test_LDADD = \
+ $(test_ldadd) \
+ $(top_builddir)/extensions/openpower-pels/json_utils.o
+json_utils_test_LDFLAGS = $(test_ldflags)
+
+event_logger_test_SOURCES = \
+ %reldir%/event_logger_test.cpp
+event_logger_test_CPPFLAGS = $(test_cppflags)
+event_logger_test_CXXFLAGS = $(test_cxxflags) $(SDEVENTPLUS_CFLAGS)
+event_logger_test_LDADD = \
+ $(test_ldadd)
+event_logger_test_LDFLAGS = $(test_ldflags) $(SDEVENTPLUS_LIBS)
diff --git a/test/openpower-pels/additional_data_test.cpp b/test/openpower-pels/additional_data_test.cpp
new file mode 100644
index 0000000..c93fdf2
--- /dev/null
+++ b/test/openpower-pels/additional_data_test.cpp
@@ -0,0 +1,65 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/additional_data.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+
+TEST(AdditionalDataTest, GetKeywords)
+{
+ std::vector<std::string> data{"KEY1=VALUE1", "KEY2=VALUE2",
+ "KEY3=", "HELLOWORLD", "=VALUE5"};
+ AdditionalData ad{data};
+
+ EXPECT_TRUE(ad.getValue("KEY1"));
+ EXPECT_EQ(*(ad.getValue("KEY1")), "VALUE1");
+
+ EXPECT_TRUE(ad.getValue("KEY2"));
+ EXPECT_EQ(*(ad.getValue("KEY2")), "VALUE2");
+
+ EXPECT_FALSE(ad.getValue("x"));
+
+ auto value3 = ad.getValue("KEY3");
+ EXPECT_TRUE(value3);
+ EXPECT_TRUE((*value3).empty());
+
+ EXPECT_FALSE(ad.getValue("HELLOWORLD"));
+ EXPECT_FALSE(ad.getValue("VALUE5"));
+
+ auto json = ad.toJSON();
+ std::string expected = R"({"KEY1":"VALUE1","KEY2":"VALUE2","KEY3":""})";
+ EXPECT_EQ(json.dump(), expected);
+
+ ad.remove("KEY1");
+ EXPECT_FALSE(ad.getValue("KEY1"));
+}
+
+TEST(AdditionalDataTest, AddData)
+{
+ AdditionalData ad;
+
+ ad.add("KEY1", "VALUE1");
+ EXPECT_EQ(*(ad.getValue("KEY1")), "VALUE1");
+
+ ad.add("KEY2", "VALUE2");
+ EXPECT_EQ(*(ad.getValue("KEY2")), "VALUE2");
+
+ std::map<std::string, std::string> expected{{"KEY1", "VALUE1"},
+ {"KEY2", "VALUE2"}};
+
+ EXPECT_EQ(expected, ad.getData());
+}
diff --git a/test/openpower-pels/ascii_string_test.cpp b/test/openpower-pels/ascii_string_test.cpp
new file mode 100644
index 0000000..96ee4ad
--- /dev/null
+++ b/test/openpower-pels/ascii_string_test.cpp
@@ -0,0 +1,86 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/ascii_string.hpp"
+#include "extensions/openpower-pels/registry.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+
+TEST(AsciiStringTest, AsciiStringTest)
+{
+ // Build the ASCII string from a message registry entry
+ message::Entry entry;
+ entry.src.type = 0xBD;
+ entry.src.reasonCode = 0xABCD;
+ entry.subsystem = 0x37;
+
+ src::AsciiString as{entry};
+
+ auto data = as.get();
+
+ EXPECT_EQ(data, "BD37ABCD ");
+
+ // Now flatten it
+ std::vector<uint8_t> flattenedData;
+ Stream stream{flattenedData};
+
+ as.flatten(stream);
+
+ for (size_t i = 0; i < 32; i++)
+ {
+ EXPECT_EQ(data[i], flattenedData[i]);
+ }
+}
+
+// A 0x11 power SRC doesn't have the subsystem in it
+TEST(AsciiStringTest, PowerErrorTest)
+{
+ message::Entry entry;
+ entry.src.type = 0x11;
+ entry.src.reasonCode = 0xABCD;
+ entry.subsystem = 0x37;
+
+ src::AsciiString as{entry};
+ auto data = as.get();
+
+ EXPECT_EQ(data, "1100ABCD ");
+}
+
+TEST(AsciiStringTest, UnflattenTest)
+{
+ std::vector<uint8_t> rawData{'B', 'D', '5', '6', '1', '2', 'A', 'B'};
+
+ for (int i = 8; i < 32; i++)
+ {
+ rawData.push_back(' ');
+ }
+
+ Stream stream{rawData};
+ src::AsciiString as{stream};
+
+ auto data = as.get();
+
+ EXPECT_EQ(data, "BD5612AB ");
+}
+
+TEST(AsciiStringTest, UnderflowTest)
+{
+ std::vector<uint8_t> rawData{'B', 'D', '5', '6'};
+ Stream stream{rawData};
+
+ EXPECT_THROW(src::AsciiString as{stream}, std::out_of_range);
+}
diff --git a/test/openpower-pels/bcd_time_test.cpp b/test/openpower-pels/bcd_time_test.cpp
new file mode 100644
index 0000000..6fe761c
--- /dev/null
+++ b/test/openpower-pels/bcd_time_test.cpp
@@ -0,0 +1,105 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/bcd_time.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+
+TEST(BCDTimeTest, ToBCDTest)
+{
+ EXPECT_EQ(toBCD(0), 0x00);
+ EXPECT_EQ(toBCD(1), 0x01);
+ EXPECT_EQ(toBCD(10), 0x10);
+ EXPECT_EQ(toBCD(99), 0x99);
+ EXPECT_EQ(toBCD(37), 0x37);
+ EXPECT_EQ(toBCD(60), 0x60);
+ EXPECT_EQ(toBCD(12345678), 0x12345678);
+ EXPECT_EQ(toBCD(0xF), 0x15);
+}
+
+TEST(BCDTimeTest, FlattenUnflattenTest)
+{
+ std::vector<uint8_t> data{1, 2, 3, 4, 5, 6, 7, 8};
+ Stream stream{data};
+ BCDTime bcd;
+
+ // Unflatten
+ stream >> bcd;
+
+ EXPECT_EQ(bcd.yearMSB, 1);
+ EXPECT_EQ(bcd.yearLSB, 2);
+ EXPECT_EQ(bcd.month, 3);
+ EXPECT_EQ(bcd.day, 4);
+ EXPECT_EQ(bcd.hour, 5);
+ EXPECT_EQ(bcd.minutes, 6);
+ EXPECT_EQ(bcd.seconds, 7);
+ EXPECT_EQ(bcd.hundredths, 8);
+
+ // Flatten
+ uint8_t val = 0x20;
+ bcd.yearMSB = val++;
+ bcd.yearLSB = val++;
+ bcd.month = val++;
+ bcd.day = val++;
+ bcd.hour = val++;
+ bcd.minutes = val++;
+ bcd.seconds = val++;
+ bcd.hundredths = val++;
+
+ stream.offset(0);
+ stream << bcd;
+
+ for (size_t i = 0; i < 8; i++)
+ {
+ EXPECT_EQ(data[i], 0x20 + i);
+ }
+}
+
+TEST(BCDTimeTest, ConvertTest)
+{
+ // Convert a time_point into BCDTime
+ tm time_tm;
+ time_tm.tm_year = 125;
+ time_tm.tm_mon = 11;
+ time_tm.tm_mday = 31;
+ time_tm.tm_hour = 15;
+ time_tm.tm_min = 23;
+ time_tm.tm_sec = 42;
+ time_tm.tm_isdst = 0;
+
+ auto timepoint = std::chrono::system_clock::from_time_t(mktime(&time_tm));
+ auto timeInBCD = getBCDTime(timepoint);
+
+ EXPECT_EQ(timeInBCD.yearMSB, 0x20);
+ EXPECT_EQ(timeInBCD.yearLSB, 0x25);
+ EXPECT_EQ(timeInBCD.month, 0x12);
+ EXPECT_EQ(timeInBCD.day, 0x31);
+ EXPECT_EQ(timeInBCD.hour, 0x15);
+ EXPECT_EQ(timeInBCD.minutes, 0x23);
+ EXPECT_EQ(timeInBCD.seconds, 0x42);
+ EXPECT_EQ(timeInBCD.hundredths, 0x00);
+}
+
+TEST(BCDTimeTest, ConvertFromMSTest)
+{
+ auto now = std::chrono::system_clock::now();
+ uint64_t ms = std::chrono::duration_cast<std::chrono::milliseconds>(
+ now.time_since_epoch())
+ .count();
+
+ ASSERT_EQ(getBCDTime(now), getBCDTime(ms));
+}
diff --git a/test/openpower-pels/event_logger_test.cpp b/test/openpower-pels/event_logger_test.cpp
new file mode 100644
index 0000000..1a4d8fc
--- /dev/null
+++ b/test/openpower-pels/event_logger_test.cpp
@@ -0,0 +1,126 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/event_logger.hpp"
+#include "log_manager.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+using namespace phosphor::logging;
+
+class CreateHelper
+{
+ public:
+ void create(const std::string& name, Entry::Level level,
+ const EventLogger::ADMap& ad)
+ {
+ _createCount++;
+ _prevName = name;
+ _prevLevel = level;
+ _prevAD = ad;
+
+ // Try to create another event from within the creation
+ // function. Should never work or else we could get stuck
+ // infinitely creating events.
+ if (_eventLogger)
+ {
+ AdditionalData d;
+ _eventLogger->log(name, level, d);
+ }
+ }
+
+ size_t _createCount = 0;
+ std::string _prevName;
+ Entry::Level _prevLevel;
+ EventLogger::ADMap _prevAD;
+ EventLogger* _eventLogger = nullptr;
+};
+
+void runEvents(sd_event* event, size_t numEvents)
+{
+ sdeventplus::Event e{event};
+
+ for (size_t i = 0; i < numEvents; i++)
+ {
+ e.run(std::chrono::milliseconds(1));
+ }
+}
+
+TEST(EventLoggerTest, TestCreateEvents)
+{
+ sd_event* sdEvent = nullptr;
+ auto r = sd_event_default(&sdEvent);
+ ASSERT_TRUE(r >= 0);
+
+ CreateHelper ch;
+
+ EventLogger eventLogger(
+ sdEvent, std::bind(std::mem_fn(&CreateHelper::create), &ch,
+ std::placeholders::_1, std::placeholders::_2,
+ std::placeholders::_3));
+
+ ch._eventLogger = &eventLogger;
+
+ AdditionalData ad;
+ ad.add("key1", "value1");
+
+ eventLogger.log("one", Entry::Level::Error, ad);
+ EXPECT_EQ(eventLogger.queueSize(), 1);
+
+ runEvents(sdEvent, 1);
+
+ // Verify 1 event was created
+ EXPECT_EQ(eventLogger.queueSize(), 0);
+ EXPECT_EQ(ch._prevName, "one");
+ EXPECT_EQ(ch._prevLevel, Entry::Level::Error);
+ EXPECT_EQ(ch._prevAD, ad.getData());
+ EXPECT_EQ(ch._createCount, 1);
+
+ // Create 2 more, and run 1 event loop at a time and check the results
+ eventLogger.log("two", Entry::Level::Error, ad);
+ eventLogger.log("three", Entry::Level::Error, ad);
+
+ EXPECT_EQ(eventLogger.queueSize(), 2);
+
+ runEvents(sdEvent, 1);
+
+ EXPECT_EQ(ch._createCount, 2);
+ EXPECT_EQ(ch._prevName, "two");
+ EXPECT_EQ(eventLogger.queueSize(), 1);
+
+ runEvents(sdEvent, 1);
+ EXPECT_EQ(ch._createCount, 3);
+ EXPECT_EQ(ch._prevName, "three");
+ EXPECT_EQ(eventLogger.queueSize(), 0);
+
+ // Add them all again and run them all at once
+ eventLogger.log("three", Entry::Level::Error, ad);
+ eventLogger.log("two", Entry::Level::Error, ad);
+ eventLogger.log("one", Entry::Level::Error, ad);
+ runEvents(sdEvent, 3);
+
+ EXPECT_EQ(ch._createCount, 6);
+ EXPECT_EQ(ch._prevName, "one");
+ EXPECT_EQ(eventLogger.queueSize(), 0);
+
+ // Run extra events - doesn't do anything
+ runEvents(sdEvent, 1);
+ EXPECT_EQ(ch._createCount, 6);
+ EXPECT_EQ(ch._prevName, "one");
+ EXPECT_EQ(eventLogger.queueSize(), 0);
+
+ sd_event_unref(sdEvent);
+}
diff --git a/test/openpower-pels/extended_user_header_test.cpp b/test/openpower-pels/extended_user_header_test.cpp
new file mode 100644
index 0000000..5cb3e9f
--- /dev/null
+++ b/test/openpower-pels/extended_user_header_test.cpp
@@ -0,0 +1,301 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/extended_user_header.hpp"
+#include "mocks.hpp"
+#include "pel_utils.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+using ::testing::Return;
+
+const std::vector<uint8_t> sectionData{
+ // section header
+ 'E', 'H', 0x00, 0x60, // ID and Size
+ 0x01, 0x00, // version, subtype
+ 0x03, 0x04, // comp ID
+
+ // MTMS
+ 'T', 'T', 'T', 'T', '-', 'M', 'M', 'M', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C',
+
+ // Server FW version
+ 'S', 'E', 'R', 'V', 'E', 'R', '_', 'V', 'E', 'R', 'S', 'I', 'O', 'N', '\0',
+ '\0',
+
+ // Subsystem FW Version
+ 'B', 'M', 'C', '_', 'V', 'E', 'R', 'S', 'I', 'O', 'N', '\0', '\0', '\0',
+ '\0', '\0',
+
+ // Reserved
+ 0x00, 0x00, 0x00, 0x00,
+
+ // Reference time
+ 0x20, 0x25, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60,
+
+ // Reserved
+ 0x00, 0x00, 0x00,
+
+ // SymptomID length
+ 20,
+
+ // SymptomID
+ 'B', 'D', '8', 'D', '4', '2', '0', '0', '_', '1', '2', '3', '4', '5', '6',
+ '7', '8', '\0', '\0', '\0'};
+
+// The section size without the symptom ID
+const size_t baseSectionSize = 76;
+
+TEST(ExtUserHeaderTest, StreamConstructorTest)
+{
+ auto data = sectionData;
+ Stream stream{data};
+ ExtendedUserHeader euh{stream};
+
+ EXPECT_EQ(euh.valid(), true);
+ EXPECT_EQ(euh.header().id, 0x4548); // EH
+ EXPECT_EQ(euh.header().size, sectionData.size());
+ EXPECT_EQ(euh.header().version, 0x01);
+ EXPECT_EQ(euh.header().subType, 0x00);
+ EXPECT_EQ(euh.header().componentID, 0x0304);
+
+ EXPECT_EQ(euh.flattenedSize(), sectionData.size());
+ EXPECT_EQ(euh.machineTypeModel(), "TTTT-MMM");
+ EXPECT_EQ(euh.machineSerialNumber(), "123456789ABC");
+ EXPECT_EQ(euh.serverFWVersion(), "SERVER_VERSION");
+ EXPECT_EQ(euh.subsystemFWVersion(), "BMC_VERSION");
+ EXPECT_EQ(euh.symptomID(), "BD8D4200_12345678");
+
+ BCDTime time{0x20, 0x25, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60};
+ EXPECT_EQ(time, euh.refTime());
+
+ // Flatten it and make sure nothing changes
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+
+ euh.flatten(newStream);
+ EXPECT_EQ(sectionData, newData);
+}
+
+// Same as above, with with symptom ID empty
+TEST(ExtUserHeaderTest, StreamConstructorNoIDTest)
+{
+ auto data = sectionData;
+ data.resize(baseSectionSize);
+ data[3] = baseSectionSize; // The size in the header
+ data.back() = 0; // Symptom ID length
+
+ Stream stream{data};
+ ExtendedUserHeader euh{stream};
+
+ EXPECT_EQ(euh.valid(), true);
+ EXPECT_EQ(euh.header().id, 0x4548); // EH
+ EXPECT_EQ(euh.header().size, baseSectionSize);
+ EXPECT_EQ(euh.header().version, 0x01);
+ EXPECT_EQ(euh.header().subType, 0x00);
+ EXPECT_EQ(euh.header().componentID, 0x0304);
+
+ EXPECT_EQ(euh.flattenedSize(), baseSectionSize);
+ EXPECT_EQ(euh.machineTypeModel(), "TTTT-MMM");
+ EXPECT_EQ(euh.machineSerialNumber(), "123456789ABC");
+ EXPECT_EQ(euh.serverFWVersion(), "SERVER_VERSION");
+ EXPECT_EQ(euh.subsystemFWVersion(), "BMC_VERSION");
+ EXPECT_EQ(euh.symptomID(), "");
+
+ BCDTime time{0x20, 0x25, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60};
+ EXPECT_EQ(time, euh.refTime());
+
+ // Flatten it and make sure nothing changes
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+
+ euh.flatten(newStream);
+ EXPECT_EQ(data, newData);
+}
+
+TEST(ExtUserHeaderTest, ConstructorTest)
+{
+ auto srcData = pelDataFactory(TestPELType::primarySRCSection);
+ Stream srcStream{srcData};
+ SRC src{srcStream};
+
+ message::Entry entry; // Empty Symptom ID vector
+
+ {
+ MockDataInterface dataIface;
+
+ EXPECT_CALL(dataIface, getMachineTypeModel())
+ .WillOnce(Return("AAAA-BBB"));
+
+ EXPECT_CALL(dataIface, getMachineSerialNumber())
+ .WillOnce(Return("123456789ABC"));
+
+ EXPECT_CALL(dataIface, getServerFWVersion())
+ .WillOnce(Return("SERVER_VERSION"));
+
+ EXPECT_CALL(dataIface, getBMCFWVersion())
+ .WillOnce(Return("BMC_VERSION"));
+
+ ExtendedUserHeader euh{dataIface, entry, src};
+
+ EXPECT_EQ(euh.valid(), true);
+ EXPECT_EQ(euh.header().id, 0x4548); // EH
+
+ // The symptom ID accounts for the extra 20 bytes
+ EXPECT_EQ(euh.header().size, baseSectionSize + 20);
+ EXPECT_EQ(euh.header().version, 0x01);
+ EXPECT_EQ(euh.header().subType, 0x00);
+ EXPECT_EQ(euh.header().componentID, 0x2000);
+
+ EXPECT_EQ(euh.flattenedSize(), baseSectionSize + 20);
+ EXPECT_EQ(euh.machineTypeModel(), "AAAA-BBB");
+ EXPECT_EQ(euh.machineSerialNumber(), "123456789ABC");
+ EXPECT_EQ(euh.serverFWVersion(), "SERVER_VERSION");
+ EXPECT_EQ(euh.subsystemFWVersion(), "BMC_VERSION");
+
+ // The default symptom ID is the ascii string + word 3
+ EXPECT_EQ(euh.symptomID(), "BD8D5678_03030310");
+
+ BCDTime time;
+ EXPECT_EQ(time, euh.refTime());
+ }
+
+ {
+ MockDataInterface dataIface;
+
+ // These 4 items are too long and will get truncated
+ // in the section.
+ EXPECT_CALL(dataIface, getMachineTypeModel())
+ .WillOnce(Return("AAAA-BBBBBBBBBBB"));
+
+ EXPECT_CALL(dataIface, getMachineSerialNumber())
+ .WillOnce(Return("123456789ABC123456789"));
+
+ EXPECT_CALL(dataIface, getServerFWVersion())
+ .WillOnce(Return("SERVER_VERSION_WAY_TOO_LONG"));
+
+ EXPECT_CALL(dataIface, getBMCFWVersion())
+ .WillOnce(Return("BMC_VERSION_WAY_TOO_LONG"));
+
+ // Use SRC words 3 through 9
+ entry.src.symptomID = {3, 4, 5, 6, 7, 8, 9};
+ ExtendedUserHeader euh{dataIface, entry, src};
+
+ EXPECT_EQ(euh.valid(), true);
+ EXPECT_EQ(euh.header().id, 0x4548); // EH
+ EXPECT_EQ(euh.header().size, baseSectionSize + 72);
+ EXPECT_EQ(euh.header().version, 0x01);
+ EXPECT_EQ(euh.header().subType, 0x00);
+ EXPECT_EQ(euh.header().componentID, 0x2000);
+
+ EXPECT_EQ(euh.flattenedSize(), baseSectionSize + 72);
+ EXPECT_EQ(euh.machineTypeModel(), "AAAA-BBB");
+ EXPECT_EQ(euh.machineSerialNumber(), "123456789ABC");
+ EXPECT_EQ(euh.serverFWVersion(), "SERVER_VERSION_");
+ EXPECT_EQ(euh.subsystemFWVersion(), "BMC_VERSION_WAY");
+
+ EXPECT_EQ(euh.symptomID(), "BD8D5678_03030310_04040404_05050505_"
+ "06060606_07070707_08080808_09090909");
+ BCDTime time;
+ EXPECT_EQ(time, euh.refTime());
+ }
+
+ {
+ MockDataInterface dataIface;
+
+ // Empty fields
+ EXPECT_CALL(dataIface, getMachineTypeModel()).WillOnce(Return(""));
+
+ EXPECT_CALL(dataIface, getMachineSerialNumber()).WillOnce(Return(""));
+
+ EXPECT_CALL(dataIface, getServerFWVersion()).WillOnce(Return(""));
+
+ EXPECT_CALL(dataIface, getBMCFWVersion()).WillOnce(Return(""));
+
+ entry.src.symptomID = {8, 9};
+ ExtendedUserHeader euh{dataIface, entry, src};
+
+ EXPECT_EQ(euh.valid(), true);
+ EXPECT_EQ(euh.header().id, 0x4548); // EH
+ EXPECT_EQ(euh.header().size, baseSectionSize + 28);
+ EXPECT_EQ(euh.header().version, 0x01);
+ EXPECT_EQ(euh.header().subType, 0x00);
+ EXPECT_EQ(euh.header().componentID, 0x2000);
+
+ EXPECT_EQ(euh.flattenedSize(), baseSectionSize + 28);
+ EXPECT_EQ(euh.machineTypeModel(), "");
+ EXPECT_EQ(euh.machineSerialNumber(), "");
+ EXPECT_EQ(euh.serverFWVersion(), "");
+ EXPECT_EQ(euh.subsystemFWVersion(), "");
+
+ EXPECT_EQ(euh.symptomID(), "BD8D5678_08080808_09090909");
+
+ BCDTime time;
+ EXPECT_EQ(time, euh.refTime());
+ }
+
+ {
+ MockDataInterface dataIface;
+
+ EXPECT_CALL(dataIface, getMachineTypeModel())
+ .WillOnce(Return("AAAA-BBB"));
+
+ EXPECT_CALL(dataIface, getMachineSerialNumber())
+ .WillOnce(Return("123456789ABC"));
+
+ EXPECT_CALL(dataIface, getServerFWVersion())
+ .WillOnce(Return("SERVER_VERSION"));
+
+ EXPECT_CALL(dataIface, getBMCFWVersion())
+ .WillOnce(Return("BMC_VERSION"));
+
+ // Way too long, will be truncated
+ entry.src.symptomID = {9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9};
+
+ ExtendedUserHeader euh{dataIface, entry, src};
+
+ EXPECT_EQ(euh.valid(), true);
+ EXPECT_EQ(euh.header().id, 0x4548); // EH
+ EXPECT_EQ(euh.header().size, baseSectionSize + 80);
+ EXPECT_EQ(euh.header().version, 0x01);
+ EXPECT_EQ(euh.header().subType, 0x00);
+ EXPECT_EQ(euh.header().componentID, 0x2000);
+
+ EXPECT_EQ(euh.flattenedSize(), baseSectionSize + 80);
+ EXPECT_EQ(euh.machineTypeModel(), "AAAA-BBB");
+ EXPECT_EQ(euh.machineSerialNumber(), "123456789ABC");
+ EXPECT_EQ(euh.serverFWVersion(), "SERVER_VERSION");
+ EXPECT_EQ(euh.subsystemFWVersion(), "BMC_VERSION");
+
+ EXPECT_EQ(euh.symptomID(),
+ "BD8D5678_09090909_09090909_09090909_09090909_09090909_"
+ "09090909_09090909_0909090");
+
+ BCDTime time;
+ EXPECT_EQ(time, euh.refTime());
+ }
+}
+
+TEST(ExtUserHeaderTest, BadDataTest)
+{
+ auto data = sectionData;
+ data.resize(20);
+
+ Stream stream{data};
+ ExtendedUserHeader euh{stream};
+
+ EXPECT_EQ(euh.valid(), false);
+}
diff --git a/test/openpower-pels/failing_mtms_test.cpp b/test/openpower-pels/failing_mtms_test.cpp
new file mode 100644
index 0000000..e377482
--- /dev/null
+++ b/test/openpower-pels/failing_mtms_test.cpp
@@ -0,0 +1,133 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/failing_mtms.hpp"
+#include "mocks.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+using ::testing::Return;
+
+TEST(FailingMTMSTest, SizeTest)
+{
+ EXPECT_EQ(FailingMTMS::flattenedSize(), 28);
+}
+
+TEST(FailingMTMSTest, ConstructorTest)
+{
+ // Note: the TypeModel field is 8B, and the SN field is 12B
+ {
+ MockDataInterface dataIface;
+
+ EXPECT_CALL(dataIface, getMachineTypeModel())
+ .WillOnce(Return("AAAA-BBB"));
+
+ EXPECT_CALL(dataIface, getMachineSerialNumber())
+ .WillOnce(Return("123456789ABC"));
+
+ FailingMTMS fm{dataIface};
+
+ // Check the section header
+ EXPECT_EQ(fm.header().id, 0x4D54);
+ EXPECT_EQ(fm.header().size, FailingMTMS::flattenedSize());
+ EXPECT_EQ(fm.header().version, 0x01);
+ EXPECT_EQ(fm.header().subType, 0x00);
+ EXPECT_EQ(fm.header().componentID, 0x2000);
+
+ EXPECT_EQ(fm.getMachineTypeModel(), "AAAA-BBB");
+ EXPECT_EQ(fm.getMachineSerialNumber(), "123456789ABC");
+ }
+
+ // longer than the max - will truncate
+ {
+ MockDataInterface dataIface;
+
+ EXPECT_CALL(dataIface, getMachineTypeModel())
+ .WillOnce(Return("AAAA-BBBTOOLONG"));
+
+ EXPECT_CALL(dataIface, getMachineSerialNumber())
+ .WillOnce(Return("123456789ABCTOOLONG"));
+
+ FailingMTMS fm{dataIface};
+
+ EXPECT_EQ(fm.getMachineTypeModel(), "AAAA-BBB");
+ EXPECT_EQ(fm.getMachineSerialNumber(), "123456789ABC");
+ }
+
+ // shorter than the max
+ {
+ MockDataInterface dataIface;
+
+ EXPECT_CALL(dataIface, getMachineTypeModel()).WillOnce(Return("A"));
+
+ EXPECT_CALL(dataIface, getMachineSerialNumber()).WillOnce(Return("1"));
+
+ FailingMTMS fm{dataIface};
+
+ EXPECT_EQ(fm.getMachineTypeModel(), "A");
+ EXPECT_EQ(fm.getMachineSerialNumber(), "1");
+ }
+}
+
+TEST(FailingMTMSTest, StreamConstructorTest)
+{
+ std::vector<uint8_t> data{0x4D, 0x54, 0x00, 0x1C, 0x01, 0x00, 0x20,
+ 0x00, 'T', 'T', 'T', 'T', '-', 'M',
+ 'M', 'M', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'A', 'B', 'C'};
+ Stream stream{data};
+ FailingMTMS fm{stream};
+
+ EXPECT_EQ(fm.valid(), true);
+
+ EXPECT_EQ(fm.header().id, 0x4D54);
+ EXPECT_EQ(fm.header().size, FailingMTMS::flattenedSize());
+ EXPECT_EQ(fm.header().version, 0x01);
+ EXPECT_EQ(fm.header().subType, 0x00);
+ EXPECT_EQ(fm.header().componentID, 0x2000);
+
+ EXPECT_EQ(fm.getMachineTypeModel(), "TTTT-MMM");
+ EXPECT_EQ(fm.getMachineSerialNumber(), "123456789ABC");
+}
+
+TEST(FailingMTMSTest, BadStreamConstructorTest)
+{
+ // too short
+ std::vector<uint8_t> data{
+ 0x4D, 0x54, 0x00, 0x1C, 0x01, 0x00, 0x20, 0x00, 'T', 'T',
+ };
+ Stream stream{data};
+ FailingMTMS fm{stream};
+
+ EXPECT_EQ(fm.valid(), false);
+}
+
+TEST(FailingMTMSTest, FlattenTest)
+{
+ std::vector<uint8_t> data{0x4D, 0x54, 0x00, 0x1C, 0x01, 0x00, 0x20,
+ 0x00, 'T', 'T', 'T', 'T', '-', 'M',
+ 'M', 'M', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'A', 'B', 'C'};
+ Stream stream{data};
+ FailingMTMS fm{stream};
+
+ // flatten and check results
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+
+ fm.flatten(newStream);
+ EXPECT_EQ(data, newData);
+}
diff --git a/test/openpower-pels/fru_identity_test.cpp b/test/openpower-pels/fru_identity_test.cpp
new file mode 100644
index 0000000..a6839b8
--- /dev/null
+++ b/test/openpower-pels/fru_identity_test.cpp
@@ -0,0 +1,89 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/fru_identity.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+using namespace openpower::pels::src;
+
+// Unflatten a FRUIdentity that is a HW FRU callout
+TEST(FRUIdentityTest, TestHardwareFRU)
+{
+ // Has PN, SN, CCIN
+ std::vector<uint8_t> data{'I', 'D', 0x1C, 0x1D, // type, size, flags
+ '1', '2', '3', '4', // PN
+ '5', '6', '7', 0x00, 'A', 'A', 'A', 'A', // CCIN
+ '1', '2', '3', '4', '5', '6', '7', '8', // SN
+ '9', 'A', 'B', 'C'};
+
+ Stream stream{data};
+
+ FRUIdentity fru{stream};
+
+ EXPECT_EQ(fru.failingComponentType(), FRUIdentity::hardwareFRU);
+ EXPECT_EQ(fru.flattenedSize(), data.size());
+
+ EXPECT_EQ(fru.getPN().value(), "1234567");
+ EXPECT_EQ(fru.getCCIN().value(), "AAAA");
+ EXPECT_EQ(fru.getSN().value(), "123456789ABC");
+ EXPECT_FALSE(fru.getMaintProc());
+
+ // Flatten
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+ fru.flatten(newStream);
+ EXPECT_EQ(data, newData);
+}
+
+// Unflatten a FRUIdentity that is a Maintenance Procedure callout
+TEST(FRUIdentityTest, TestMaintProcedure)
+{
+ // Only contains the maintenance procedure
+ std::vector<uint8_t> data{
+ 0x49, 0x44, 0x0C, 0x42, // type, size, flags
+ '1', '2', '3', '4', '5', '6', '7', 0x00 // Procedure
+ };
+
+ Stream stream{data};
+
+ FRUIdentity fru{stream};
+
+ EXPECT_EQ(fru.failingComponentType(), FRUIdentity::maintenanceProc);
+ EXPECT_EQ(fru.flattenedSize(), data.size());
+
+ EXPECT_EQ(fru.getMaintProc().value(), "1234567");
+ EXPECT_FALSE(fru.getPN());
+ EXPECT_FALSE(fru.getCCIN());
+ EXPECT_FALSE(fru.getSN());
+
+ // Flatten
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+ fru.flatten(newStream);
+ EXPECT_EQ(data, newData);
+}
+
+// Try to unflatten garbage data
+TEST(FRUIdentityTest, BadDataTest)
+{
+ std::vector<uint8_t> data{0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF};
+
+ Stream stream{data};
+
+ EXPECT_THROW(FRUIdentity fru{stream}, std::out_of_range);
+}
diff --git a/test/openpower-pels/generic_section_test.cpp b/test/openpower-pels/generic_section_test.cpp
new file mode 100644
index 0000000..7c80894
--- /dev/null
+++ b/test/openpower-pels/generic_section_test.cpp
@@ -0,0 +1,64 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/generic.hpp"
+#include "pel_utils.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+
+TEST(GenericSectionTest, UnflattenFlattenTest)
+{
+ // Use the private header data
+ auto data = pelDataFactory(TestPELType::privateHeaderSection);
+
+ Stream stream(data);
+ Generic section(stream);
+
+ EXPECT_EQ(section.header().id, 0x5048);
+ EXPECT_EQ(section.header().size, data.size());
+ EXPECT_EQ(section.header().version, 0x01);
+ EXPECT_EQ(section.header().subType, 0x02);
+ EXPECT_EQ(section.header().componentID, 0x0304);
+
+ const auto& sectionData = section.data();
+
+ // The data itself starts after the header
+ EXPECT_EQ(sectionData.size(), data.size() - 8);
+
+ for (size_t i = 0; i < sectionData.size(); i++)
+ {
+ EXPECT_EQ(sectionData[i], (data)[i + 8]);
+ }
+
+ // Now flatten
+ std::vector<uint8_t> newData;
+ Stream newStream(newData);
+ section.flatten(newStream);
+
+ EXPECT_EQ(data, newData);
+}
+
+TEST(GenericSectionTest, BadDataTest)
+{
+ // Use the private header data to start with
+ auto data = pelDataFactory(TestPELType::privateHeaderSection);
+ data.resize(4);
+
+ Stream stream(data);
+ Generic section(stream);
+ ASSERT_FALSE(section.valid());
+}
diff --git a/test/openpower-pels/host_notifier_test.cpp b/test/openpower-pels/host_notifier_test.cpp
new file mode 100644
index 0000000..c51d560
--- /dev/null
+++ b/test/openpower-pels/host_notifier_test.cpp
@@ -0,0 +1,680 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/data_interface.hpp"
+#include "extensions/openpower-pels/host_notifier.hpp"
+#include "mocks.hpp"
+#include "pel_utils.hpp"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <chrono>
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::NiceMock;
+using ::testing::Return;
+namespace fs = std::filesystem;
+using namespace std::chrono;
+
+const size_t actionFlags0Offset = 66;
+const size_t actionFlags1Offset = 67;
+
+class HostNotifierTest : public CleanPELFiles
+{
+ public:
+ HostNotifierTest() : repo(repoPath)
+ {
+ auto r = sd_event_default(&event);
+ EXPECT_TRUE(r >= 0);
+
+ hostIface =
+ std::make_unique<NiceMock<MockHostInterface>>(event, dataIface);
+
+ mockHostIface = reinterpret_cast<MockHostInterface*>(hostIface.get());
+
+ auto send = [this](uint32_t id, uint32_t size) {
+ return this->mockHostIface->send(0);
+ };
+
+ // Unless otherwise specified, sendNewLogCmd should always pass.
+ ON_CALL(*mockHostIface, sendNewLogCmd(_, _))
+ .WillByDefault(Invoke(send));
+ }
+
+ ~HostNotifierTest()
+ {
+ sd_event_unref(event);
+ }
+
+ protected:
+ sd_event* event;
+ Repository repo;
+ NiceMock<MockDataInterface> dataIface;
+ std::unique_ptr<HostInterface> hostIface;
+ MockHostInterface* mockHostIface;
+};
+
+/**
+ * @brief Create PEL with the specified action flags
+ *
+ * @param[in] actionFlagsMask - Optional action flags to use
+ *
+ * @return std::unique_ptr<PEL>
+ */
+std::unique_ptr<PEL> makePEL(uint16_t actionFlagsMask = 0)
+{
+ static uint32_t obmcID = 1;
+ auto data = pelDataFactory(TestPELType::pelSimple);
+
+ data[actionFlags0Offset] |= actionFlagsMask >> 8;
+ data[actionFlags1Offset] |= actionFlagsMask & 0xFF;
+
+ auto pel = std::make_unique<PEL>(data, obmcID++);
+ pel->assignID();
+ pel->setCommitTime();
+ return pel;
+}
+
+/**
+ * @brief Run an iteration of the event loop.
+ *
+ * An event loop is used for:
+ * 1) timer expiration callbacks
+ * 2) Dispatches
+ * 3) host interface receive callbacks
+ *
+ * @param[in] event - The event object
+ * @param[in] numEvents - number of times to call Event::run()
+ * @param[in] timeout - timeout value for run()
+ */
+void runEvents(sdeventplus::Event& event, size_t numEvents,
+ milliseconds timeout = milliseconds(1))
+{
+ for (size_t i = 0; i < numEvents; i++)
+ {
+ event.run(timeout);
+ }
+}
+
+// Test that host state change callbacks work
+TEST_F(HostNotifierTest, TestHostStateChange)
+{
+ bool hostState = false;
+ bool called = false;
+ DataInterfaceBase::HostStateChangeFunc func = [&hostState,
+ &called](bool state) {
+ hostState = state;
+ called = true;
+ };
+
+ dataIface.subscribeToHostStateChange("test", func);
+
+ // callback called
+ dataIface.changeHostState(true);
+ EXPECT_TRUE(called);
+ EXPECT_TRUE(hostState);
+
+ // No change, not called
+ called = false;
+ dataIface.changeHostState(true);
+ EXPECT_FALSE(called);
+
+ // Called again
+ dataIface.changeHostState(false);
+ EXPECT_FALSE(hostState);
+ EXPECT_TRUE(called);
+
+ // Shouldn't get called after an unsubscribe
+ dataIface.unsubscribeFromHostStateChange("test");
+
+ called = false;
+
+ dataIface.changeHostState(true);
+ EXPECT_FALSE(called);
+}
+
+// Test dealing with how acked PELs are put on the
+// notification queue.
+TEST_F(HostNotifierTest, TestPolicyAckedPEL)
+{
+ HostNotifier notifier{repo, dataIface, std::move(hostIface)};
+
+ auto pel = makePEL();
+ repo.add(pel);
+
+ // This is required
+ EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
+ EXPECT_TRUE(notifier.notifyRequired(pel->id()));
+
+ // Not in the repo
+ EXPECT_FALSE(notifier.enqueueRequired(42));
+ EXPECT_FALSE(notifier.notifyRequired(42));
+
+ // Now set this PEL to host acked
+ repo.setPELHostTransState(pel->id(), TransmissionState::acked);
+
+ // Since it's acked, doesn't need to be enqueued or transmitted
+ EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
+ EXPECT_FALSE(notifier.notifyRequired(pel->id()));
+}
+
+// Test the 'don't report' PEL flag
+TEST_F(HostNotifierTest, TestPolicyDontReport)
+{
+ HostNotifier notifier{repo, dataIface, std::move(hostIface)};
+
+ // dontReportToHostFlagBit
+ auto pel = makePEL(0x1000);
+
+ // Double check the action flag is still set
+ std::bitset<16> actionFlags = pel->userHeader().actionFlags();
+ EXPECT_TRUE(actionFlags.test(dontReportToHostFlagBit));
+
+ repo.add(pel);
+
+ // Don't need to send this to the host
+ EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
+}
+
+// Test that hidden PELs need notification when there
+// is no HMC.
+TEST_F(HostNotifierTest, TestPolicyHiddenNoHMC)
+{
+ HostNotifier notifier{repo, dataIface, std::move(hostIface)};
+
+ // hiddenFlagBit
+ auto pel = makePEL(0x4000);
+
+ // Double check the action flag is still set
+ std::bitset<16> actionFlags = pel->userHeader().actionFlags();
+ EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
+
+ repo.add(pel);
+
+ // Still need to enqueue this
+ EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
+
+ // Still need to send it
+ EXPECT_TRUE(notifier.notifyRequired(pel->id()));
+}
+
+// Don't need to enqueue a hidden log already acked by the HMC
+TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCAcked)
+{
+ HostNotifier notifier{repo, dataIface, std::move(hostIface)};
+
+ // hiddenFlagBit
+ auto pel = makePEL(0x4000);
+
+ // Double check the action flag is still set
+ std::bitset<16> actionFlags = pel->userHeader().actionFlags();
+ EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
+
+ repo.add(pel);
+
+ // No HMC yet, so required
+ EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
+
+ repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
+
+ // Not required anymore
+ EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
+}
+
+// Test that changing the HMC manage status affects
+// the policy with hidden log notification.
+TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCManaged)
+{
+ HostNotifier notifier{repo, dataIface, std::move(hostIface)};
+
+ // hiddenFlagBit
+ auto pel = makePEL(0x4000);
+
+ repo.add(pel);
+
+ // The first time, the HMC managed is false
+ EXPECT_TRUE(notifier.notifyRequired(pel->id()));
+
+ dataIface.setHMCManaged(true);
+
+ // This time, HMC managed is true so no need to notify
+ EXPECT_FALSE(notifier.notifyRequired(pel->id()));
+}
+
+// Test that PELs are enqueued on startup
+TEST_F(HostNotifierTest, TestStartup)
+{
+ // Give the repo 10 PELs to start with
+ for (int i = 0; i < 10; i++)
+ {
+ auto pel = makePEL();
+ repo.add(pel);
+ }
+
+ HostNotifier notifier{repo, dataIface, std::move(hostIface)};
+
+ ASSERT_EQ(notifier.queueSize(), 10);
+
+ // Now add 10 more after the notifier is watching
+ for (int i = 0; i < 10; i++)
+ {
+ auto pel = makePEL();
+ repo.add(pel);
+ }
+
+ ASSERT_EQ(notifier.queueSize(), 20);
+}
+
+// Test the simple path were PELs get sent to the host
+TEST_F(HostNotifierTest, TestSendCmd)
+{
+ sdeventplus::Event sdEvent{event};
+
+ HostNotifier notifier{repo, dataIface, std::move(hostIface)};
+
+ // Add a PEL with the host off
+ auto pel = makePEL();
+ repo.add(pel);
+
+ EXPECT_EQ(notifier.queueSize(), 1);
+
+ dataIface.changeHostState(true);
+
+ runEvents(sdEvent, 1);
+
+ // It was sent up
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
+ EXPECT_EQ(notifier.queueSize(), 0);
+
+ // Verify the state was written to the PEL.
+ Repository::LogID id{Repository::LogID::Pel{pel->id()}};
+ auto data = repo.getPELData(id);
+ PEL pelFromRepo{*data};
+ EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::sent);
+
+ // Add a few more PELs. They will get sent.
+ pel = makePEL();
+ repo.add(pel);
+
+ // Dispatch it by hitting the event loop (no commands sent yet)
+ // Don't need to test this step discretely in the future
+ runEvents(sdEvent, 1);
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
+ EXPECT_EQ(notifier.queueSize(), 0);
+
+ // Send the command
+ runEvents(sdEvent, 1);
+
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
+ EXPECT_EQ(notifier.queueSize(), 0);
+
+ pel = makePEL();
+ repo.add(pel);
+
+ // dispatch and process the command
+ runEvents(sdEvent, 2);
+
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
+ EXPECT_EQ(notifier.queueSize(), 0);
+}
+
+// Test that if the class is created with the host up,
+// it will send PELs
+TEST_F(HostNotifierTest, TestStartAfterHostUp)
+{
+ // Add PELs right away
+ auto pel = makePEL();
+ repo.add(pel);
+ pel = makePEL();
+ repo.add(pel);
+
+ sdeventplus::Event sdEvent{event};
+
+ // Create the HostNotifier class with the host already up
+ dataIface.changeHostState(true);
+ HostNotifier notifier{repo, dataIface, std::move(hostIface)};
+
+ // It should start sending PELs right away
+ runEvents(sdEvent, 2);
+
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
+ EXPECT_EQ(notifier.queueSize(), 0);
+}
+
+// Test that a single failure will cause a retry
+TEST_F(HostNotifierTest, TestHostRetry)
+{
+ sdeventplus::Event sdEvent{event};
+
+ HostNotifier notifier{repo, dataIface, std::move(hostIface)};
+
+ auto sendFailure = [this](uint32_t id, uint32_t size) {
+ return this->mockHostIface->send(1);
+ };
+ auto sendSuccess = [this](uint32_t id, uint32_t size) {
+ return this->mockHostIface->send(0);
+ };
+
+ EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
+ .WillOnce(Invoke(sendFailure))
+ .WillOnce(Invoke(sendSuccess))
+ .WillOnce(Invoke(sendSuccess));
+
+ dataIface.changeHostState(true);
+
+ auto pel = makePEL();
+ repo.add(pel);
+
+ // Dispatch and handle the command
+ runEvents(sdEvent, 2);
+
+ // The command failed, so the queue isn't empty
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
+ EXPECT_EQ(notifier.queueSize(), 1);
+
+ // Run the events again to let the timer expire and the
+ // command to be retried, which will be successful.
+ runEvents(sdEvent, 2, mockHostIface->getReceiveRetryDelay());
+
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
+ EXPECT_EQ(notifier.queueSize(), 0);
+
+ // This one should pass with no problems
+ pel = makePEL();
+ repo.add(pel);
+
+ // Dispatch and handle the command
+ runEvents(sdEvent, 2);
+
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
+ EXPECT_EQ(notifier.queueSize(), 0);
+}
+
+// Test that all commands fail and notifier will give up
+TEST_F(HostNotifierTest, TestHardFailure)
+{
+ sdeventplus::Event sdEvent{event};
+
+ HostNotifier notifier{repo, dataIface, std::move(hostIface)};
+
+ // Every call will fail
+ auto sendFailure = [this](uint32_t id, uint32_t size) {
+ return this->mockHostIface->send(1);
+ };
+
+ EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
+ .WillRepeatedly(Invoke(sendFailure));
+
+ dataIface.changeHostState(true);
+
+ auto pel = makePEL();
+ repo.add(pel);
+
+ // Clock more retries than necessary
+ runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
+
+ // Should have stopped after the 15 Tries
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 15);
+ EXPECT_EQ(notifier.queueSize(), 1);
+
+ // Now add another PEL, and it should start trying again
+ // though it will also eventually give up
+ pel = makePEL();
+ repo.add(pel);
+
+ runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
+
+ // Tried an additional 15 times
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 30);
+ EXPECT_EQ(notifier.queueSize(), 2);
+}
+
+// Cancel an in progress command
+TEST_F(HostNotifierTest, TestCancelCmd)
+{
+ sdeventplus::Event sdEvent{event};
+ HostNotifier notifier{repo, dataIface, std::move(hostIface)};
+
+ dataIface.changeHostState(true);
+
+ // Add and send one PEL, but don't enter the event loop
+ // so the receive function can't run.
+ auto pel = makePEL();
+ repo.add(pel);
+
+ // Not dispatched yet
+ EXPECT_EQ(notifier.queueSize(), 1);
+
+ // Dispatch it
+ runEvents(sdEvent, 1);
+
+ // It was sent and off the queue
+ EXPECT_EQ(notifier.queueSize(), 0);
+
+ // This will cancel the receive
+ dataIface.changeHostState(false);
+
+ // Back on the queue
+ EXPECT_EQ(notifier.queueSize(), 1);
+
+ // Turn the host back on and make sure
+ // commands will work again
+ dataIface.changeHostState(true);
+
+ runEvents(sdEvent, 1);
+
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
+ EXPECT_EQ(notifier.queueSize(), 0);
+}
+
+// Test that acking a PEL persist across power cycles
+TEST_F(HostNotifierTest, TestPowerCycleAndAcks)
+{
+ sdeventplus::Event sdEvent{event};
+
+ HostNotifier notifier{repo, dataIface, std::move(hostIface)};
+
+ // Add 2 PELs with host off
+ auto pel = makePEL();
+ repo.add(pel);
+ auto id1 = pel->id();
+
+ pel = makePEL();
+ repo.add(pel);
+ auto id2 = pel->id();
+
+ dataIface.changeHostState(true);
+
+ runEvents(sdEvent, 2);
+
+ // The were both sent.
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
+ EXPECT_EQ(notifier.queueSize(), 0);
+
+ dataIface.changeHostState(false);
+
+ // Those PELs weren't acked, so they will get sent again
+ EXPECT_EQ(notifier.queueSize(), 2);
+
+ // Power back on and send them again
+ dataIface.changeHostState(true);
+ runEvents(sdEvent, 2);
+
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 4);
+ EXPECT_EQ(notifier.queueSize(), 0);
+
+ // Ack them and verify the state in the PEL.
+ notifier.ackPEL(id1);
+ notifier.ackPEL(id2);
+
+ Repository::LogID id{Repository::LogID::Pel{id1}};
+ auto data = repo.getPELData(id);
+ PEL pelFromRepo1{*data};
+ EXPECT_EQ(pelFromRepo1.hostTransmissionState(), TransmissionState::acked);
+
+ id.pelID.id = id2;
+ data = repo.getPELData(id);
+ PEL pelFromRepo2{*data};
+ EXPECT_EQ(pelFromRepo2.hostTransmissionState(), TransmissionState::acked);
+
+ // Power back off, and they should't get re-added
+ dataIface.changeHostState(false);
+
+ EXPECT_EQ(notifier.queueSize(), 0);
+}
+
+// Test the host full condition
+TEST_F(HostNotifierTest, TestHostFull)
+{
+ // The full interaction with the host is:
+ // BMC: new PEL available
+ // Host: ReadPELFile (not modeled here)
+ // Host: Ack(id) (if not full), or HostFull(id)
+ // BMC: if full and any new PELs come in, don't sent them
+ // Start a timer and try again
+ // Host responds with either Ack or full
+ // and repeat
+
+ sdeventplus::Event sdEvent{event};
+ HostNotifier notifier{repo, dataIface, std::move(hostIface)};
+
+ dataIface.changeHostState(true);
+
+ // Add and dispatch/send one PEL
+ auto pel = makePEL();
+ auto id = pel->id();
+ repo.add(pel);
+ runEvents(sdEvent, 2);
+
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
+ EXPECT_EQ(notifier.queueSize(), 0);
+
+ // Host is full
+ notifier.setHostFull(id);
+
+ // It goes back on the queue
+ EXPECT_EQ(notifier.queueSize(), 1);
+
+ // The transmission state goes back to new
+ Repository::LogID i{Repository::LogID::Pel{id}};
+ auto data = repo.getPELData(i);
+ PEL pelFromRepo{*data};
+ EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::newPEL);
+
+ // Clock it, nothing should be sent still.
+ runEvents(sdEvent, 1);
+
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
+ EXPECT_EQ(notifier.queueSize(), 1);
+
+ // Add another PEL and clock it, still nothing sent
+ pel = makePEL();
+ repo.add(pel);
+ runEvents(sdEvent, 2);
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
+ EXPECT_EQ(notifier.queueSize(), 2);
+
+ // Let the host full timer expire to trigger a retry.
+ // Add some extra event passes just to be sure nothing new is sent.
+ runEvents(sdEvent, 5, mockHostIface->getHostFullRetryDelay());
+
+ // The timer expiration will send just the 1, not both
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
+ EXPECT_EQ(notifier.queueSize(), 1);
+
+ // Host still full
+ notifier.setHostFull(id);
+
+ // Let the host full timer attempt again
+ runEvents(sdEvent, 2, mockHostIface->getHostFullRetryDelay());
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
+
+ // Add yet another PEL with the retry timer expired.
+ // It shouldn't get sent out.
+ pel = makePEL();
+ repo.add(pel);
+ runEvents(sdEvent, 2);
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
+
+ // The last 2 PELs still on the queue
+ EXPECT_EQ(notifier.queueSize(), 2);
+
+ // Host no longer full, it finally acks the first PEL
+ notifier.ackPEL(id);
+
+ // Now the remaining 2 PELs will be dispatched
+ runEvents(sdEvent, 3);
+
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 5);
+ EXPECT_EQ(notifier.queueSize(), 0);
+}
+
+// Test when the host says it was send a malformed PEL
+TEST_F(HostNotifierTest, TestBadPEL)
+{
+ sdeventplus::Event sdEvent{event};
+
+ {
+ Repository repo1{repoPath};
+ HostNotifier notifier{repo1, dataIface, std::move(hostIface)};
+
+ dataIface.changeHostState(true);
+
+ // Add a PEL and dispatch and send it
+ auto pel = makePEL();
+ auto id = pel->id();
+ repo1.add(pel);
+
+ runEvents(sdEvent, 2);
+ EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
+ EXPECT_EQ(notifier.queueSize(), 0);
+
+ // The host rejected it.
+ notifier.setBadPEL(id);
+
+ // Doesn't go back on the queue
+ EXPECT_EQ(notifier.queueSize(), 0);
+
+ // Check the state was saved in the PEL itself
+ Repository::LogID i{Repository::LogID::Pel{id}};
+ auto data = repo1.getPELData(i);
+ PEL pelFromRepo{*data};
+ EXPECT_EQ(pelFromRepo.hostTransmissionState(),
+ TransmissionState::badPEL);
+
+ dataIface.changeHostState(false);
+
+ // Ensure it doesn't go back on the queue on a power cycle
+ EXPECT_EQ(notifier.queueSize(), 0);
+ }
+
+ // Now restore the repo, and make sure it doesn't come back
+ {
+ Repository repo1{repoPath};
+
+ std::unique_ptr<HostInterface> hostIface1 =
+ std::make_unique<MockHostInterface>(event, dataIface);
+
+ HostNotifier notifier{repo1, dataIface, std::move(hostIface1)};
+
+ EXPECT_EQ(notifier.queueSize(), 0);
+ }
+}
diff --git a/test/openpower-pels/json_utils_test.cpp b/test/openpower-pels/json_utils_test.cpp
new file mode 100644
index 0000000..60ca00d
--- /dev/null
+++ b/test/openpower-pels/json_utils_test.cpp
@@ -0,0 +1,50 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/json_utils.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+
+TEST(JsonUtilsTest, TrimEndTest)
+{
+ std::string testStr("Test string 1");
+ EXPECT_EQ(trimEnd(testStr), "Test string 1");
+ testStr = "Test string 2 ";
+ EXPECT_EQ(trimEnd(testStr), "Test string 2");
+ testStr = " Test string 3 ";
+ EXPECT_EQ(trimEnd(testStr), " Test string 3");
+}
+
+TEST(JsonUtilsTest, NumberToStringTest)
+{
+ size_t number = 123;
+ EXPECT_EQ(getNumberString("%d", number), "123");
+ EXPECT_EQ(getNumberString("%03X", number), "07B");
+ EXPECT_EQ(getNumberString("0x%X", number), "0x7B");
+ ASSERT_EXIT((getNumberString("%123", number), exit(0)),
+ ::testing::KilledBySignal(SIGSEGV), ".*");
+}
+
+TEST(JsonUtilsTest, JsonInsertTest)
+{
+ std::string json;
+ jsonInsert(json, "Key", "Value1", 1);
+ EXPECT_EQ(json, " \"Key\": \"Value1\",\n");
+ jsonInsert(json, "Keyxxxxxxxxxxxxxxxxxxxxxxxxxx", "Value2", 2);
+ EXPECT_EQ(json, " \"Key\": \"Value1\",\n"
+ " \"Keyxxxxxxxxxxxxxxxxxxxxxxxxxx\": \"Value2\",\n");
+}
diff --git a/test/openpower-pels/log_id_test.cpp b/test/openpower-pels/log_id_test.cpp
new file mode 100644
index 0000000..93dff83
--- /dev/null
+++ b/test/openpower-pels/log_id_test.cpp
@@ -0,0 +1,57 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/log_id.hpp"
+#include "extensions/openpower-pels/paths.hpp"
+
+#include <arpa/inet.h>
+
+#include <filesystem>
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+namespace fs = std::filesystem;
+
+TEST(LogIdTest, TimeBasedIDTest)
+{
+ uint32_t lastID = 0;
+ for (int i = 0; i < 10; i++)
+ {
+ auto id = detail::getTimeBasedLogID();
+
+ EXPECT_EQ(id & 0xFF000000, 0x50000000);
+ EXPECT_NE(id, lastID);
+ lastID = id;
+ }
+}
+
+TEST(LogIdTest, IDTest)
+{
+ EXPECT_EQ(generatePELID(), 0x50000001);
+ EXPECT_EQ(generatePELID(), 0x50000002);
+ EXPECT_EQ(generatePELID(), 0x50000003);
+ EXPECT_EQ(generatePELID(), 0x50000004);
+ EXPECT_EQ(generatePELID(), 0x50000005);
+ EXPECT_EQ(generatePELID(), 0x50000006);
+
+ auto backingFile = getPELIDFile();
+ fs::remove(backingFile);
+ EXPECT_EQ(generatePELID(), 0x50000001);
+ EXPECT_EQ(generatePELID(), 0x50000002);
+ EXPECT_EQ(generatePELID(), 0x50000003);
+
+ fs::remove_all(fs::path{backingFile}.parent_path());
+}
diff --git a/test/openpower-pels/mocks.hpp b/test/openpower-pels/mocks.hpp
new file mode 100644
index 0000000..8b055dd
--- /dev/null
+++ b/test/openpower-pels/mocks.hpp
@@ -0,0 +1,239 @@
+#include "extensions/openpower-pels/data_interface.hpp"
+#include "extensions/openpower-pels/host_interface.hpp"
+
+#include <fcntl.h>
+
+#include <filesystem>
+#include <sdeventplus/source/io.hpp>
+
+#include <gmock/gmock.h>
+
+namespace openpower
+{
+namespace pels
+{
+
+class MockDataInterface : public DataInterfaceBase
+{
+ public:
+ MockDataInterface()
+ {
+ }
+ MOCK_METHOD(std::string, getMachineTypeModel, (), (const override));
+ MOCK_METHOD(std::string, getMachineSerialNumber, (), (const override));
+ MOCK_METHOD(std::string, getServerFWVersion, (), (const override));
+ MOCK_METHOD(std::string, getBMCFWVersion, (), (const override));
+ MOCK_METHOD(std::string, getBMCFWVersionID, (), (const override));
+
+ void changeHostState(bool newState)
+ {
+ setHostState(newState);
+ }
+
+ void setHMCManaged(bool managed)
+ {
+ _hmcManaged = managed;
+ }
+};
+
+/**
+ * @brief The mock HostInterface class
+ *
+ * This replaces the PLDM calls with a FIFO for the asynchronous
+ * responses.
+ */
+class MockHostInterface : public HostInterface
+{
+ public:
+ /**
+ * @brief Constructor
+ *
+ * @param[in] event - The sd_event object
+ * @param[in] dataIface - The DataInterface class
+ */
+ MockHostInterface(sd_event* event, DataInterfaceBase& dataIface) :
+ HostInterface(event, dataIface)
+ {
+ char templ[] = "/tmp/cmdfifoXXXXXX";
+ std::filesystem::path dir = mkdtemp(templ);
+ _fifo = dir / "fifo";
+ }
+
+ /**
+ * @brief Destructor
+ */
+ virtual ~MockHostInterface()
+ {
+ std::filesystem::remove_all(_fifo.parent_path());
+ }
+
+ MOCK_METHOD(CmdStatus, sendNewLogCmd, (uint32_t, uint32_t), (override));
+
+ /**
+ * @brief Cancels waiting for a command response
+ */
+ virtual void cancelCmd() override
+ {
+ _inProgress = false;
+ _source = nullptr;
+ }
+
+ /**
+ * @brief Returns the amount of time to wait before retrying after
+ * a failed send command.
+ *
+ * @return milliseconds - The amount of time to wait
+ */
+ virtual std::chrono::milliseconds getSendRetryDelay() const override
+ {
+ return std::chrono::milliseconds(2);
+ }
+
+ /**
+ * @brief Returns the amount of time to wait before retrying after
+ * a command receive.
+ *
+ * @return milliseconds - The amount of time to wait
+ */
+ virtual std::chrono::milliseconds getReceiveRetryDelay() const override
+ {
+ return std::chrono::milliseconds(2);
+ }
+
+ /**
+ * @brief Returns the amount of time to wait before retrying if the
+ * host firmware's PEL storage was full and it can't store
+ * any more logs until it is freed up somehow.
+ *
+ * @return milliseconds - The amount of time to wait
+ */
+ virtual std::chrono::milliseconds getHostFullRetryDelay() const override
+ {
+ return std::chrono::milliseconds(400);
+ }
+
+ /**
+ * @brief Returns the number of commands processed
+ */
+ size_t numCmdsProcessed() const
+ {
+ return _cmdsProcessed;
+ }
+
+ /**
+ * @brief Writes the data passed in to the FIFO
+ *
+ * @param[in] hostResponse - use a 0 to indicate success
+ *
+ * @return CmdStatus - success or failure
+ */
+ CmdStatus send(uint8_t hostResponse)
+ {
+ // Create a FIFO once.
+ if (!std::filesystem::exists(_fifo))
+ {
+ if (mkfifo(_fifo.c_str(), 0622))
+ {
+ ADD_FAILURE() << "Failed mkfifo " << _fifo << strerror(errno);
+ exit(-1);
+ }
+ }
+
+ // Open it and register the reponse callback to
+ // be used on FD activity.
+ int fd = open(_fifo.c_str(), O_NONBLOCK | O_RDWR);
+ EXPECT_TRUE(fd >= 0) << "Unable to open FIFO";
+
+ auto callback = [this](sdeventplus::source::IO& source, int fd,
+ uint32_t events) {
+ this->receive(source, fd, events);
+ };
+
+ try
+ {
+ _source = std::make_unique<sdeventplus::source::IO>(
+ _event, fd, EPOLLIN,
+ std::bind(callback, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3));
+ }
+ catch (std::exception& e)
+ {
+ ADD_FAILURE() << "Event exception: " << e.what();
+ close(fd);
+ return CmdStatus::failure;
+ }
+
+ // Write the fake host reponse to the FIFO
+ auto bytesWritten = write(fd, &hostResponse, sizeof(hostResponse));
+ EXPECT_EQ(bytesWritten, sizeof(hostResponse));
+
+ _inProgress = true;
+
+ return CmdStatus::success;
+ }
+
+ protected:
+ /**
+ * @brief Reads the data written to the fifo and then calls
+ * the subscriber's callback.
+ *
+ * Nonzero data indicates a command failure (for testing bad path).
+ *
+ * @param[in] source - The event source object
+ * @param[in] fd - The file descriptor used
+ * @param[in] events - The event bits
+ */
+ void receive(sdeventplus::source::IO& source, int fd,
+ uint32_t events) override
+ {
+ if (!(events & EPOLLIN))
+ {
+ return;
+ }
+
+ _inProgress = false;
+
+ int newFD = open(_fifo.c_str(), O_NONBLOCK | O_RDONLY);
+ ASSERT_TRUE(newFD >= 0) << "Failed to open FIFO";
+
+ // Read the host success/failure response from the FIFO.
+ uint8_t data;
+ auto bytesRead = read(newFD, &data, sizeof(data));
+ EXPECT_EQ(bytesRead, sizeof(data));
+
+ close(newFD);
+
+ ResponseStatus status = ResponseStatus::success;
+ if (data != 0)
+ {
+ status = ResponseStatus::failure;
+ }
+
+ if (_responseFunc)
+ {
+ (*_responseFunc)(status);
+ }
+
+ // Keep account of the number of commands responses for testing.
+ _cmdsProcessed++;
+ }
+
+ private:
+ /**
+ * @brief The event source for the fifo
+ */
+ std::unique_ptr<sdeventplus::source::IO> _source;
+
+ /**
+ * @brief the path to the fifo
+ */
+ std::filesystem::path _fifo;
+
+ /**
+ * @brief The number of commands processed
+ */
+ size_t _cmdsProcessed = 0;
+};
+
+} // namespace pels
+} // namespace openpower
diff --git a/test/openpower-pels/mru_test.cpp b/test/openpower-pels/mru_test.cpp
new file mode 100644
index 0000000..e73c69a
--- /dev/null
+++ b/test/openpower-pels/mru_test.cpp
@@ -0,0 +1,74 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "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);
+}
diff --git a/test/openpower-pels/mtms_test.cpp b/test/openpower-pels/mtms_test.cpp
new file mode 100644
index 0000000..b9c0929
--- /dev/null
+++ b/test/openpower-pels/mtms_test.cpp
@@ -0,0 +1,122 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/mtms.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+
+TEST(MTMSTest, SizeTest)
+{
+ EXPECT_EQ(MTMS::flattenedSize(), 20);
+}
+
+TEST(MTMSTest, ConstructorTest)
+{
+ {
+ std::string tm{"TTTT-MMM"};
+ std::string sn{"123456789ABC"};
+
+ MTMS mtms{tm, sn};
+
+ std::array<uint8_t, 8> t{'T', 'T', 'T', 'T', '-', 'M', 'M', 'M'};
+ EXPECT_EQ(t, mtms.machineTypeAndModelRaw());
+ EXPECT_EQ("TTTT-MMM", mtms.machineTypeAndModel());
+
+ std::array<uint8_t, 12> s{'1', '2', '3', '4', '5', '6',
+ '7', '8', '9', 'A', 'B', 'C'};
+ EXPECT_EQ(s, mtms.machineSerialNumberRaw());
+ EXPECT_EQ("123456789ABC", mtms.machineSerialNumber());
+ }
+
+ {
+ // too long- truncate it
+ std::string tm{"TTTT-MMME"};
+ std::string sn{"123456789ABCE"};
+
+ MTMS mtms{tm, sn};
+
+ std::array<uint8_t, 8> t{'T', 'T', 'T', 'T', '-', 'M', 'M', 'M'};
+ EXPECT_EQ(t, mtms.machineTypeAndModelRaw());
+
+ std::array<uint8_t, 12> s{'1', '2', '3', '4', '5', '6',
+ '7', '8', '9', 'A', 'B', 'C'};
+ EXPECT_EQ(s, mtms.machineSerialNumberRaw());
+ }
+
+ {
+ // short
+ std::string tm{"TTTT"};
+ std::string sn{"1234"};
+
+ MTMS mtms{tm, sn};
+
+ std::array<uint8_t, 8> t{'T', 'T', 'T', 'T', 0, 0, 0, 0};
+ EXPECT_EQ(t, mtms.machineTypeAndModelRaw());
+ EXPECT_EQ("TTTT", mtms.machineTypeAndModel());
+
+ std::array<uint8_t, 12> s{'1', '2', '3', '4', 0, 0, 0, 0, 0, 0, 0, 0};
+ EXPECT_EQ(s, mtms.machineSerialNumberRaw());
+ EXPECT_EQ("1234", mtms.machineSerialNumber());
+ }
+
+ {
+ // Stream constructor
+ std::vector<uint8_t> data{'T', 'T', 'T', 'T', '-', 'M', 'M',
+ 'M', '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', 'A', 'B', 'C'};
+ Stream stream{data};
+
+ MTMS mtms{stream};
+
+ EXPECT_EQ("TTTT-MMM", mtms.machineTypeAndModel());
+
+ EXPECT_EQ("123456789ABC", mtms.machineSerialNumber());
+ }
+}
+
+TEST(MTMSTest, OperatorExtractTest)
+{
+ std::string tm{"TTTT-MMM"};
+ std::string sn{"123456789ABC"};
+
+ MTMS mtms{tm, sn};
+
+ // Check that we can extract the same data
+ std::vector<uint8_t> data;
+ Stream stream{data};
+ stream << mtms;
+
+ std::vector<uint8_t> expected{'T', 'T', 'T', 'T', '-', 'M', 'M',
+ 'M', '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', 'A', 'B', 'C'};
+ EXPECT_EQ(expected, data);
+}
+
+TEST(MTMSTest, OperatorInsertTest)
+{
+ std::vector<uint8_t> data{'T', 'T', 'T', 'T', '-', 'M', 'M', 'M', '1', '2',
+ '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C'};
+ Stream stream{data};
+
+ // Check that when we insert data it is what's expected
+ MTMS mtms;
+ stream >> mtms;
+
+ EXPECT_EQ("TTTT-MMM", mtms.machineTypeAndModel());
+
+ EXPECT_EQ("123456789ABC", mtms.machineSerialNumber());
+}
diff --git a/test/openpower-pels/paths.cpp b/test/openpower-pels/paths.cpp
new file mode 100644
index 0000000..254d2d0
--- /dev/null
+++ b/test/openpower-pels/paths.cpp
@@ -0,0 +1,67 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/paths.hpp"
+
+#include <filesystem>
+
+namespace openpower
+{
+namespace pels
+{
+
+// Use paths that work in unit tests.
+
+std::filesystem::path getPELIDFile()
+{
+ static std::string idFile;
+
+ if (idFile.empty())
+ {
+ char templ[] = "/tmp/logidtestXXXXXX";
+ std::filesystem::path dir = mkdtemp(templ);
+ idFile = dir / "logid";
+ }
+ return idFile;
+}
+
+std::filesystem::path getPELRepoPath()
+{
+ static std::string repoPath;
+
+ if (repoPath.empty())
+ {
+ char templ[] = "/tmp/repopathtestXXXXXX";
+ std::filesystem::path dir = mkdtemp(templ);
+ repoPath = dir;
+ }
+ return repoPath;
+}
+
+std::filesystem::path getMessageRegistryPath()
+{
+ static std::string registryPath;
+
+ if (registryPath.empty())
+ {
+ char templ[] = "/tmp/msgregtestXXXXXX";
+ registryPath = mkdtemp(templ);
+ }
+
+ return registryPath;
+}
+
+} // namespace pels
+} // namespace openpower
diff --git a/test/openpower-pels/pce_identity_test.cpp b/test/openpower-pels/pce_identity_test.cpp
new file mode 100644
index 0000000..de41dbb
--- /dev/null
+++ b/test/openpower-pels/pce_identity_test.cpp
@@ -0,0 +1,57 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/pce_identity.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+using namespace openpower::pels::src;
+
+TEST(PCEIdentityTest, TestConstructor)
+{
+ std::vector<uint8_t> data{
+ 'P', 'E', 0x24, 0x00, // type, size, flags
+ 'T', 'T', 'T', 'T', '-', 'M', 'M', 'M', // MTM
+ '1', '2', '3', '4', '5', '6', '7', // SN
+ '8', '9', 'A', 'B', 'C', 'P', 'C', 'E', // Name + null padded
+ 'N', 'A', 'M', 'E', '1', '2', 0x00, 0x00, 0x00};
+
+ Stream stream{data};
+
+ PCEIdentity pce{stream};
+
+ EXPECT_EQ(pce.flattenedSize(), data.size());
+ EXPECT_EQ(pce.enclosureName(), "PCENAME12");
+ EXPECT_EQ(pce.mtms().machineTypeAndModel(), "TTTT-MMM");
+ EXPECT_EQ(pce.mtms().machineSerialNumber(), "123456789ABC");
+
+ // Flatten it
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+ pce.flatten(newStream);
+
+ EXPECT_EQ(data, newData);
+}
+
+TEST(PCEIdentityTest, TestBadData)
+{
+ std::vector<uint8_t> data{
+ 'P', 'E', 0x20, 0x00, 'T', 'T', 'T', 'T', '-',
+ };
+
+ Stream stream{data};
+ EXPECT_THROW(PCEIdentity pce{stream}, std::out_of_range);
+}
diff --git a/test/openpower-pels/pel_manager_test.cpp b/test/openpower-pels/pel_manager_test.cpp
new file mode 100644
index 0000000..b020d9d
--- /dev/null
+++ b/test/openpower-pels/pel_manager_test.cpp
@@ -0,0 +1,514 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/manager.hpp"
+#include "log_manager.hpp"
+#include "pel_utils.hpp"
+
+#include <fstream>
+#include <regex>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+namespace fs = std::filesystem;
+
+class TestLogger
+{
+ public:
+ void log(const std::string& name, phosphor::logging::Entry::Level level,
+ const EventLogger::ADMap& additionalData)
+ {
+ errName = name;
+ errLevel = level;
+ ad = additionalData;
+ }
+
+ std::string errName;
+ phosphor::logging::Entry::Level errLevel;
+ EventLogger::ADMap ad;
+};
+
+class ManagerTest : public CleanPELFiles
+{
+ public:
+ ManagerTest() : logManager(bus, "logging_path")
+ {
+ sd_event_default(&sdEvent);
+ bus.attach_event(sdEvent, SD_EVENT_PRIORITY_NORMAL);
+ }
+
+ ~ManagerTest()
+ {
+ sd_event_unref(sdEvent);
+ }
+
+ sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
+ phosphor::logging::internal::Manager logManager;
+ sd_event* sdEvent;
+ TestLogger logger;
+};
+
+fs::path makeTempDir()
+{
+ char path[] = "/tmp/tempnameXXXXXX";
+ std::filesystem::path dir = mkdtemp(path);
+ return dir;
+}
+
+std::optional<fs::path> findAnyPELInRepo()
+{
+ // PELs are named <timestamp>_<ID>
+ std::regex expr{"\\d+_\\d+"};
+
+ for (auto& f : fs::directory_iterator(getPELRepoPath() / "logs"))
+ {
+ if (std::regex_search(f.path().string(), expr))
+ {
+ return f.path();
+ }
+ }
+ return std::nullopt;
+}
+
+// Test that using the RAWPEL=<file> with the Manager::create() call gets
+// a PEL saved in the repository.
+TEST_F(ManagerTest, TestCreateWithPEL)
+{
+ std::unique_ptr<DataInterfaceBase> dataIface =
+ std::make_unique<DataInterface>(bus);
+
+ openpower::pels::Manager manager{
+ logManager, std::move(dataIface),
+ std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3)};
+
+ // Create a PEL, write it to a file, and pass that filename into
+ // the create function.
+ auto data = pelDataFactory(TestPELType::pelSimple);
+
+ fs::path pelFilename = makeTempDir() / "rawpel";
+ std::ofstream pelFile{pelFilename};
+ pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
+ pelFile.close();
+
+ std::string adItem = "RAWPEL=" + pelFilename.string();
+ std::vector<std::string> additionalData{adItem};
+ std::vector<std::string> associations;
+
+ manager.create("error message", 42, 0,
+ phosphor::logging::Entry::Level::Error, additionalData,
+ associations);
+
+ // Find the file in the PEL repository directory
+ auto pelPathInRepo = findAnyPELInRepo();
+
+ EXPECT_TRUE(pelPathInRepo);
+
+ // Now remove it based on its OpenBMC event log ID
+ manager.erase(42);
+
+ pelPathInRepo = findAnyPELInRepo();
+
+ EXPECT_FALSE(pelPathInRepo);
+
+ fs::remove_all(pelFilename.parent_path());
+}
+
+TEST_F(ManagerTest, TestCreateWithInvalidPEL)
+{
+ std::unique_ptr<DataInterfaceBase> dataIface =
+ std::make_unique<DataInterface>(bus);
+
+ openpower::pels::Manager manager{
+ logManager, std::move(dataIface),
+ std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3)};
+
+ // Create a PEL, write it to a file, and pass that filename into
+ // the create function.
+ auto data = pelDataFactory(TestPELType::pelSimple);
+
+ // Truncate it to make it invalid.
+ data.resize(200);
+
+ fs::path pelFilename = makeTempDir() / "rawpel";
+ std::ofstream pelFile{pelFilename};
+ pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
+ pelFile.close();
+
+ std::string adItem = "RAWPEL=" + pelFilename.string();
+ std::vector<std::string> additionalData{adItem};
+ std::vector<std::string> associations;
+
+ manager.create("error message", 42, 0,
+ phosphor::logging::Entry::Level::Error, additionalData,
+ associations);
+
+ // Run the event loop to log the bad PEL event
+ sdeventplus::Event e{sdEvent};
+ e.run(std::chrono::milliseconds(1));
+
+ PEL invalidPEL{data};
+ EXPECT_EQ(logger.errName, "org.open_power.Logging.Error.BadHostPEL");
+ EXPECT_EQ(logger.errLevel, phosphor::logging::Entry::Level::Error);
+ EXPECT_EQ(std::stoi(logger.ad["PLID"], nullptr, 16), invalidPEL.plid());
+ EXPECT_EQ(logger.ad["OBMC_LOG_ID"], "42");
+ EXPECT_EQ(logger.ad["SRC"], (*invalidPEL.primarySRC())->asciiString());
+ EXPECT_EQ(logger.ad["PEL_SIZE"], std::to_string(data.size()));
+
+ fs::remove_all(pelFilename.parent_path());
+}
+
+// Test that the message registry can be used to build a PEL.
+TEST_F(ManagerTest, TestCreateWithMessageRegistry)
+{
+ const auto registry = R"(
+{
+ "PELs":
+ [
+ {
+ "Name": "xyz.openbmc_project.Error.Test",
+ "Subsystem": "power_supply",
+ "ActionFlags": ["service_action", "report"],
+ "SRC":
+ {
+ "ReasonCode": "0x2030"
+ },
+ "Documentation":
+ {
+ "Description": "A PGOOD Fault",
+ "Message": "PS had a PGOOD Fault"
+ }
+ }
+ ]
+}
+)";
+
+ auto path = getMessageRegistryPath();
+ fs::create_directories(path);
+ path /= "message_registry.json";
+
+ std::ofstream registryFile{path};
+ registryFile << registry;
+ registryFile.close();
+
+ std::unique_ptr<DataInterfaceBase> dataIface =
+ std::make_unique<DataInterface>(logManager.getBus());
+
+ openpower::pels::Manager manager{
+ logManager, std::move(dataIface),
+ std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3)};
+
+ std::vector<std::string> additionalData;
+ std::vector<std::string> associations;
+
+ // Create the event log to create the PEL from.
+ manager.create("xyz.openbmc_project.Error.Test", 33, 0,
+ phosphor::logging::Entry::Level::Error, additionalData,
+ associations);
+
+ // Ensure a PEL was created in the repository
+ auto pelFile = findAnyPELInRepo();
+ ASSERT_TRUE(pelFile);
+
+ auto data = readPELFile(*pelFile);
+ PEL pel(*data);
+
+ // Spot check it. Other testcases cover the details.
+ EXPECT_TRUE(pel.valid());
+ EXPECT_EQ(pel.obmcLogID(), 33);
+ EXPECT_EQ(pel.primarySRC().value()->asciiString(),
+ "BD612030 ");
+
+ // Remove it
+ manager.erase(33);
+ pelFile = findAnyPELInRepo();
+ EXPECT_FALSE(pelFile);
+
+ // Create an event log that can't be found in the registry.
+ manager.create("xyz.openbmc_project.Error.Foo", 33, 0,
+ phosphor::logging::Entry::Level::Error, additionalData,
+ associations);
+
+ // Currently, no PEL should be created. Eventually, a 'missing registry
+ // entry' PEL will be there.
+ pelFile = findAnyPELInRepo();
+ EXPECT_FALSE(pelFile);
+}
+
+TEST_F(ManagerTest, TestDBusMethods)
+{
+ std::unique_ptr<DataInterfaceBase> dataIface =
+ std::make_unique<DataInterface>(bus);
+
+ Manager manager{logManager, std::move(dataIface),
+ std::bind(std::mem_fn(&TestLogger::log), &logger,
+ std::placeholders::_1, std::placeholders::_2,
+ std::placeholders::_3)};
+
+ // Create a PEL, write it to a file, and pass that filename into
+ // the create function so there's one in the repo.
+ auto data = pelDataFactory(TestPELType::pelSimple);
+
+ fs::path pelFilename = makeTempDir() / "rawpel";
+ std::ofstream pelFile{pelFilename};
+ pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
+ pelFile.close();
+
+ std::string adItem = "RAWPEL=" + pelFilename.string();
+ std::vector<std::string> additionalData{adItem};
+ std::vector<std::string> associations;
+
+ manager.create("error message", 42, 0,
+ phosphor::logging::Entry::Level::Error, additionalData,
+ associations);
+
+ // getPELFromOBMCID
+ auto newData = manager.getPELFromOBMCID(42);
+ EXPECT_EQ(newData.size(), data.size());
+
+ // Read the PEL to get the ID for later
+ PEL pel{newData};
+ auto id = pel.id();
+
+ EXPECT_THROW(
+ manager.getPELFromOBMCID(id + 1),
+ sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
+
+ // getPEL
+ auto unixfd = manager.getPEL(id);
+
+ // Get the size
+ struct stat s;
+ int r = fstat(unixfd, &s);
+ ASSERT_EQ(r, 0);
+ auto size = s.st_size;
+
+ // Open the FD and check the contents
+ FILE* fp = fdopen(unixfd, "r");
+ ASSERT_NE(fp, nullptr);
+
+ std::vector<uint8_t> fdData;
+ fdData.resize(size);
+ r = fread(fdData.data(), 1, size, fp);
+ EXPECT_EQ(r, size);
+
+ EXPECT_EQ(newData, fdData);
+
+ fclose(fp);
+
+ // Run the event loop to close the FD
+ sdeventplus::Event e{sdEvent};
+ e.run(std::chrono::milliseconds(1));
+
+ EXPECT_THROW(
+ manager.getPEL(id + 1),
+ sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
+
+ // hostAck
+ manager.hostAck(id);
+
+ EXPECT_THROW(
+ manager.hostAck(id + 1),
+ sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
+
+ // hostReject
+ manager.hostReject(id, Manager::RejectionReason::BadPEL);
+
+ // Run the event loop to log the bad PEL event
+ e.run(std::chrono::milliseconds(1));
+
+ EXPECT_EQ(logger.errName, "org.open_power.Logging.Error.SentBadPELToHost");
+ EXPECT_EQ(id, std::stoi(logger.ad["BAD_ID"], nullptr, 16));
+
+ manager.hostReject(id, Manager::RejectionReason::HostFull);
+
+ EXPECT_THROW(
+ manager.hostReject(id + 1, Manager::RejectionReason::BadPEL),
+ sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
+
+ fs::remove_all(pelFilename.parent_path());
+}
+
+// An ESEL from the wild
+const std::string esel{
+ "00 00 df 00 00 00 00 20 00 04 12 01 6f aa 00 00 "
+ "50 48 00 30 01 00 33 00 00 00 00 07 5c 69 cc 0d 00 00 00 07 5c d5 50 db "
+ "42 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 90 00 00 4e 90 00 00 4e "
+ "55 48 00 18 01 00 09 00 8a 03 40 00 00 00 00 00 ff ff 00 00 00 00 00 00 "
+ "50 53 00 50 01 01 00 00 02 00 00 09 33 2d 00 48 00 00 00 e0 00 00 10 00 "
+ "00 00 00 00 00 20 00 00 00 0c 00 02 00 00 00 fa 00 00 0c e4 00 00 00 12 "
+ "42 43 38 41 33 33 32 44 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 "
+ "20 20 20 20 20 20 20 20 55 44 00 1c 01 06 01 00 02 54 41 4b 00 00 00 06 "
+ "00 00 00 55 00 01 f9 20 00 00 00 00 55 44 00 24 01 06 01 00 01 54 41 4b "
+ "00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 23 01 00 02 00 05 00 00 "
+ "55 44 00 0c 01 0b 01 00 0f 01 00 00 55 44 00 10 01 04 01 00 0f 9f de 6a "
+ "00 01 00 00 55 44 00 7c 00 0c 01 00 00 13 0c 02 00 fa 0c e4 16 00 01 2c "
+ "0c 1c 16 00 00 fa 0a f0 14 00 00 fa 0b b8 14 00 00 be 09 60 12 00 01 2c "
+ "0d 7a 12 00 00 fa 0c 4e 10 00 00 fa 0c e4 10 00 00 be 0a 8c 16 00 01 2c "
+ "0c 1c 16 00 01 09 09 f6 16 00 00 fa 09 f6 14 00 00 fa 0b b8 14 00 00 fa "
+ "0a f0 14 00 00 be 08 ca 12 00 01 2c 0c e4 12 00 00 fa 0b 54 10 00 00 fa "
+ "0c 2d 10 00 00 be 08 ca 55 44 00 58 01 03 01 00 00 00 00 00 00 05 31 64 "
+ "00 00 00 00 00 05 0d d4 00 00 00 00 40 5f 06 e0 00 00 00 00 40 5d d2 00 "
+ "00 00 00 00 40 57 d3 d0 00 00 00 00 40 58 f6 a0 00 00 00 00 40 54 c9 34 "
+ "00 00 00 00 40 55 9a 10 00 00 00 00 40 4c 0a 80 00 00 00 00 00 00 27 14 "
+ "55 44 01 84 01 01 01 00 48 6f 73 74 62 6f 6f 74 20 42 75 69 6c 64 20 49 "
+ "44 3a 20 68 6f 73 74 62 6f 6f 74 2d 66 65 63 37 34 64 66 2d 70 30 61 38 "
+ "37 64 63 34 2f 68 62 69 63 6f 72 65 2e 62 69 6e 00 49 42 4d 2d 77 69 74 "
+ "68 65 72 73 70 6f 6f 6e 2d 4f 50 39 2d 76 32 2e 34 2d 39 2e 32 33 34 0a "
+ "09 6f 70 2d 62 75 69 6c 64 2d 38 32 66 34 63 66 30 0a 09 62 75 69 6c 64 "
+ "72 6f 6f 74 2d 32 30 31 39 2e 30 35 2e 32 2d 31 30 2d 67 38 39 35 39 31 "
+ "31 34 0a 09 73 6b 69 62 6f 6f 74 2d 76 36 2e 35 2d 31 38 2d 67 34 37 30 "
+ "66 66 62 35 66 32 39 64 37 0a 09 68 6f 73 74 62 6f 6f 74 2d 66 65 63 37 "
+ "34 64 66 2d 70 30 61 38 37 64 63 34 0a 09 6f 63 63 2d 65 34 35 39 37 61 "
+ "62 0a 09 6c 69 6e 75 78 2d 35 2e 32 2e 31 37 2d 6f 70 65 6e 70 6f 77 65 "
+ "72 31 2d 70 64 64 63 63 30 33 33 0a 09 70 65 74 69 74 62 6f 6f 74 2d 76 "
+ "31 2e 31 30 2e 34 0a 09 6d 61 63 68 69 6e 65 2d 78 6d 6c 2d 63 36 32 32 "
+ "63 62 35 2d 70 37 65 63 61 62 33 64 0a 09 68 6f 73 74 62 6f 6f 74 2d 62 "
+ "69 6e 61 72 69 65 73 2d 36 36 65 39 61 36 30 0a 09 63 61 70 70 2d 75 63 "
+ "6f 64 65 2d 70 39 2d 64 64 32 2d 76 34 0a 09 73 62 65 2d 36 30 33 33 30 "
+ "65 30 0a 09 68 63 6f 64 65 2d 68 77 30 39 32 31 31 39 61 2e 6f 70 6d 73 "
+ "74 0a 00 00 55 44 00 70 01 04 01 00 0f 9f de 6a 00 05 00 00 07 5f 1d f4 "
+ "30 32 43 59 34 37 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
+ "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
+ "0b ac 54 02 59 41 31 39 33 34 36 39 37 30 35 38 00 00 00 00 00 00 05 22 "
+ "a1 58 01 8a 00 58 40 20 17 18 4d 2c 00 00 00 fc 01 a1 00 00 55 44 00 14 "
+ "01 08 01 00 00 00 00 01 00 00 00 5a 00 00 00 05 55 44 03 fc 01 15 31 00 "
+ "01 28 00 42 46 41 50 49 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 f4 "
+ "00 00 00 00 00 00 03 f4 00 00 00 0b 00 00 00 00 00 00 00 3d 2c 9b c2 84 "
+ "00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 09 "
+ "00 00 00 00 00 11 bd 20 00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 "
+ "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 00 00 00 00 00 00 01 2c "
+ "00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0c 1c 00 00 00 64 00 00 00 3d "
+ "2c 9b d1 11 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 "
+ "00 00 00 0a 00 00 00 00 00 13 b5 a0 00 00 00 00 00 01 f8 80 00 00 00 00 "
+ "00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 "
+ "00 00 00 be 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0a 8c 00 00 00 64 "
+ "00 00 00 3d 2c 9b df 98 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 "
+ "00 00 00 00 00 00 00 0b 00 00 00 00 00 15 ae 20 00 00 00 00 00 01 f8 80 "
+ "00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 "
+ "00 00 00 00 00 00 00 fa 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0c e4 "
+ "00 00 00 64 00 00 00 3d 2c 9b ea b7 00 00 01 e4 00 48 43 4f fb ed 70 b1 "
+ "00 00 02 01 00 00 00 00 00 00 00 0c 00 00 00 00 00 17 a6 a0 00 00 00 00 "
+ "00 01 f8 80 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 "
+ "00 00 00 12 00 00 00 00 00 00 00 fa 00 00 00 00 00 00 07 d0 00 00 00 00 "
+ "00 00 0c 4e 00 00 00 64 00 00 00 3d 2c 9b f6 27 00 00 01 e4 00 48 43 4f "
+ "fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 0d 00 00 00 00 00 19 9f 20 "
+ "00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 "
+ "00 00 00 00 00 00 00 12 00 00 00 00 00 00 01 2c 00 00 00 00 00 00 07 d0 "
+ "00 00 00 00 00 00 0d 7a 00 00 00 64 00 00 00 3d 2c 9c 05 75 00 00 01 e4 "
+ "00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 0e 00 00 00 00 "
+ "00 1b 97 a0 00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 00 00 00 00 "
+ "00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00 be 00 00 00 00 "
+ "00 00 07 d0 00 00 00 00 00 00 09 60 00 00 00 64 00 00 00 3d 2c 9c 11 29 "
+ "00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 0f "
+ "00 00 00 00 00 1d 90 20 00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 "
+ "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00 fa "
+ "00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0b b8 00 00 00 64 00 00 00 3d "
+ "2c 9c 1c 45 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 "
+ "00 00 00 10 00 00 00 00 00 1f 88 a0 00 00 00 00 00 01 f8 80 00 00 00 00 "
+ "00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 00 00 00 00 "
+ "00 00 00 fa 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0a f0 00 00 00 64 "
+ "00 00 00 3d 2c 9c 2b 14 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 "
+ "00 00 00 00 00 00 00 11 00 00 00 00 00 21 81 20 00 00 00 00 00 01 f8 80 "
+ "00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 "
+ "00 00 00 00 00 00 01 2c 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0c 1c "
+ "00 00 00 64 00 00 00 3d 2d 6d 8f 9e 00 00 01 e4 00 00 43 4f 52 d7 9c 36 "
+ "00 00 04 73 00 00 00 1c 00 00 00 3d 2d 6d 99 ac 00 00 01 e4 00 10 43 4f "
+ "3f f2 02 3d 00 00 05 58 00 00 00 00 02 00 00 01 00 00 00 00 00 00 00 40 "
+ "00 00 00 2c 55 44 00 30 01 15 31 00 01 28 00 42 46 41 50 49 5f 44 42 47 "
+ "00 00 00 00 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00 28 00 00 00 00 "
+ "00 00 00 00 55 44 01 74 01 15 31 00 01 28 00 42 46 41 50 49 5f 49 00 00 "
+ "00 00 00 00 00 00 00 00 00 00 01 6c 00 00 00 00 00 00 01 6c 00 00 00 0b "
+ "00 00 00 00 00 00 00 3c 0d 52 18 5e 00 00 01 e4 00 08 43 4f 46 79 94 13 "
+ "00 00 0a 5b 00 00 00 00 00 00 2c 00 00 00 00 24 00 00 00 3c 0d 6b 26 6c "
+ "00 00 01 e4 00 00 43 4f 4e 9b 18 74 00 00 01 03 00 00 00 1c 00 00 00 3c "
+ "12 b9 2d 13 00 00 01 e4 00 00 43 4f ea 31 ed d4 00 00 05 c4 00 00 00 1c "
+ "00 00 00 3c 13 02 73 53 00 00 01 e4 00 00 43 4f ea 31 ed d4 00 00 05 c4 "
+ "00 00 00 1c 00 00 00 3c 13 04 7c 94 00 00 01 e4 00 00 43 4f ea 31 ed d4 "
+ "00 00 05 c4 00 00 00 1c 00 00 00 3c 13 06 ad e1 00 00 01 e4 00 00 43 4f "
+ "ea 31 ed d4 00 00 05 c4 00 00 00 1c 00 00 00 3c 13 07 3f 77 00 00 01 e4 "
+ "00 00 43 4f 5e 4a 55 32 00 00 10 f2 00 00 00 1c 00 00 00 3c 13 07 4e e4 "
+ "00 00 01 e4 00 00 43 4f 5e 4a 55 32 00 00 0d 68 00 00 00 1c 00 00 00 3c "
+ "13 36 79 18 00 00 01 e4 00 00 43 4f ea 31 ed d4 00 00 05 c4 00 00 00 1c "
+ "00 00 00 3d 2c 9c 36 70 00 00 01 e4 00 00 43 4f 23 45 90 97 00 00 02 47 "
+ "00 00 00 1c 00 00 00 3d 2d 6d a3 ed 00 00 01 e4 00 08 43 4f 74 3a 5b 1a "
+ "00 00 04 cc 00 00 00 00 02 00 00 01 00 00 00 24 55 44 00 30 01 15 31 00 "
+ "01 28 00 42 53 43 41 4e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 28 "
+ "00 00 00 00 00 00 00 28 00 00 00 00 00 00 00 00"};
+
+TEST_F(ManagerTest, TestESELToRawData)
+{
+ auto data = Manager::eselToRawData(esel);
+
+ EXPECT_EQ(data.size(), 2464);
+
+ PEL pel{data};
+ EXPECT_TRUE(pel.valid());
+}
+
+TEST_F(ManagerTest, TestCreateWithESEL)
+{
+ std::unique_ptr<DataInterfaceBase> dataIface =
+ std::make_unique<DataInterface>(bus);
+
+ openpower::pels::Manager manager{
+ logManager, std::move(dataIface),
+ std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3)};
+
+ {
+ std::string adItem = "ESEL=" + esel;
+ std::vector<std::string> additionalData{adItem};
+ std::vector<std::string> associations;
+
+ manager.create("error message", 37, 0,
+ phosphor::logging::Entry::Level::Error, additionalData,
+ associations);
+
+ auto data = manager.getPELFromOBMCID(37);
+ PEL pel{data};
+ EXPECT_TRUE(pel.valid());
+ }
+
+ // Now an invalid one
+ {
+ std::string adItem = "ESEL=" + esel;
+
+ // Crop it
+ adItem.resize(adItem.size() - 300);
+
+ std::vector<std::string> additionalData{adItem};
+ std::vector<std::string> associations;
+
+ manager.create("error message", 38, 0,
+ phosphor::logging::Entry::Level::Error, additionalData,
+ associations);
+
+ EXPECT_THROW(
+ manager.getPELFromOBMCID(38),
+ sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
+
+ // Run the event loop to log the bad PEL event
+ sdeventplus::Event e{sdEvent};
+ e.run(std::chrono::milliseconds(1));
+
+ EXPECT_EQ(logger.errName, "org.open_power.Logging.Error.BadHostPEL");
+ EXPECT_EQ(logger.errLevel, phosphor::logging::Entry::Level::Error);
+ }
+}
diff --git a/test/openpower-pels/pel_rules_test.cpp b/test/openpower-pels/pel_rules_test.cpp
new file mode 100644
index 0000000..38e5b58
--- /dev/null
+++ b/test/openpower-pels/pel_rules_test.cpp
@@ -0,0 +1,72 @@
+#include "extensions/openpower-pels/pel_rules.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+
+struct CheckParams
+{
+ // pel_rules::check() inputs
+ uint16_t actionFlags;
+ uint8_t eventType;
+ uint8_t severity;
+
+ // pel_rules::check() expected outputs
+ uint16_t expectedActionFlags;
+ uint8_t expectedEventType;
+};
+
+const uint8_t sevInfo = 0x00;
+const uint8_t sevRecovered = 0x10;
+const uint8_t sevPredictive = 0x20;
+const uint8_t sevUnrecov = 0x40;
+const uint8_t sevCrit = 0x50;
+const uint8_t sevDiagnostic = 0x60;
+const uint8_t sevSymptom = 0x70;
+
+const uint8_t typeNA = 0x00;
+const uint8_t typeMisc = 0x01;
+const uint8_t typeTracing = 0x02;
+const uint8_t typeDumpNotif = 0x08;
+
+TEST(PELRulesTest, TestCheckRules)
+{
+ // Impossible to cover all combinations, but
+ // do some interesting ones.
+ std::vector<CheckParams> testParams{
+ // Informational errors w/ empty action flags
+ // and different event types.
+ {0, typeNA, sevInfo, 0x6000, typeMisc},
+ {0, typeMisc, sevInfo, 0x6000, typeMisc},
+ {0, typeTracing, sevInfo, 0x6000, typeTracing},
+ {0, typeDumpNotif, sevInfo, 0x2000, typeDumpNotif},
+
+ // Informational errors with wrong action flags
+ {0x8900, typeNA, sevInfo, 0x6000, typeMisc},
+
+ // Informational errors with extra valid action flags
+ {0x00C0, typeMisc, sevInfo, 0x60C0, typeMisc},
+
+ // Informational - don't report
+ {0x1000, typeMisc, sevInfo, 0x5000, typeMisc},
+
+ // Recovered will report as hidden
+ {0, typeNA, sevRecovered, 0x6000, typeNA},
+
+ // The 5 error severities will have:
+ // service action, report, call home
+ {0, typeNA, sevPredictive, 0xA800, typeNA},
+ {0, typeNA, sevUnrecov, 0xA800, typeNA},
+ {0, typeNA, sevCrit, 0xA800, typeNA},
+ {0, typeNA, sevDiagnostic, 0xA800, typeNA},
+ {0, typeNA, sevSymptom, 0xA800, typeNA}};
+
+ for (const auto& entry : testParams)
+ {
+ auto [actionFlags, type] = pel_rules::check(
+ entry.actionFlags, entry.eventType, entry.severity);
+
+ EXPECT_EQ(actionFlags, entry.expectedActionFlags);
+ EXPECT_EQ(type, entry.expectedEventType);
+ }
+}
diff --git a/test/openpower-pels/pel_test.cpp b/test/openpower-pels/pel_test.cpp
new file mode 100644
index 0000000..2cf58d7
--- /dev/null
+++ b/test/openpower-pels/pel_test.cpp
@@ -0,0 +1,387 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "elog_entry.hpp"
+#include "extensions/openpower-pels/generic.hpp"
+#include "extensions/openpower-pels/pel.hpp"
+#include "mocks.hpp"
+#include "pel_utils.hpp"
+
+#include <filesystem>
+#include <fstream>
+
+#include <gtest/gtest.h>
+
+namespace fs = std::filesystem;
+using namespace openpower::pels;
+using ::testing::Return;
+
+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();
+ EXPECT_EQ(data, flattenedData);
+ EXPECT_EQ(flattenedData.size(), pel->size());
+}
+
+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();
+
+ EXPECT_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);
+ EXPECT_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();
+
+ EXPECT_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);
+ EXPECT_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());
+
+ // 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());
+}
+
+TEST_F(PELTest, CreateFromRegistryTest)
+{
+ message::Entry regEntry;
+ uint64_t timestamp = 5;
+
+ regEntry.name = "test";
+ regEntry.subsystem = 5;
+ regEntry.actionFlags = 0xC000;
+ regEntry.src.type = 0xBD;
+ regEntry.src.reasonCode = 0x1234;
+
+ std::vector<std::string> data{"KEY1=VALUE1"};
+ AdditionalData ad{data};
+ MockDataInterface dataIface;
+
+ PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error, ad,
+ dataIface};
+
+ EXPECT_TRUE(pel.valid());
+ EXPECT_EQ(pel.privateHeader().obmcLogID(), 42);
+ EXPECT_EQ(pel.userHeader().severity(), 0x40);
+
+ EXPECT_EQ(pel.primarySRC().value()->asciiString(),
+ "BD051234 ");
+
+ // Check that certain optional sections have been created
+ size_t mtmsCount = 0;
+ size_t euhCount = 0;
+ size_t udCount = 0;
+
+ for (const auto& section : pel.optionalSections())
+ {
+ if (section->header().id ==
+ static_cast<uint16_t>(SectionID::failingMTMS))
+ {
+ mtmsCount++;
+ }
+ else if (section->header().id ==
+ static_cast<uint16_t>(SectionID::extendedUserHeader))
+ {
+ euhCount++;
+ }
+ else if (section->header().id ==
+ static_cast<uint16_t>(SectionID::userData))
+ {
+ udCount++;
+ }
+ }
+
+ EXPECT_EQ(mtmsCount, 1);
+ EXPECT_EQ(euhCount, 1);
+ EXPECT_EQ(udCount, 2); // AD section and sysInfo section
+}
+
+// Test that we'll create Generic optional sections for sections that
+// there aren't explicit classes for.
+TEST_F(PELTest, GenericSectionTest)
+{
+ auto data = pelDataFactory(TestPELType::pelSimple);
+
+ std::vector<uint8_t> section1{0x58, 0x58, // ID 'XX'
+ 0x00, 0x18, // Size
+ 0x01, 0x02, // version, subtype
+ 0x03, 0x04, // comp ID
+
+ // some data
+ 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63,
+ 0x20, 0x31, 0x06, 0x0F, 0x09, 0x22, 0x3A,
+ 0x00};
+
+ std::vector<uint8_t> section2{
+ 0x59, 0x59, // ID 'YY'
+ 0x00, 0x20, // Size
+ 0x01, 0x02, // version, subtype
+ 0x03, 0x04, // comp ID
+
+ // some data
+ 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63, 0x20, 0x31, 0x06, 0x0F,
+ 0x09, 0x22, 0x3A, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+
+ // Add the new sections at the end
+ data.insert(data.end(), section1.begin(), section1.end());
+ data.insert(data.end(), section2.begin(), section2.end());
+
+ // Increment the section count
+ data.at(27) += 2;
+ auto origData = data;
+
+ PEL pel{data};
+
+ const auto& sections = pel.optionalSections();
+
+ bool foundXX = false;
+ bool foundYY = false;
+
+ // Check that we can find these 2 Generic sections
+ for (const auto& section : sections)
+ {
+ if (section->header().id == 0x5858)
+ {
+ foundXX = true;
+ EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr);
+ }
+ else if (section->header().id == 0x5959)
+ {
+ foundYY = true;
+ EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr);
+ }
+ }
+
+ EXPECT_TRUE(foundXX);
+ EXPECT_TRUE(foundYY);
+
+ // Now flatten and check
+ auto newData = pel.data();
+
+ EXPECT_EQ(origData, newData);
+}
+
+// Test that an invalid section will still get a Generic object
+TEST_F(PELTest, InvalidGenericTest)
+{
+ auto data = pelDataFactory(TestPELType::pelSimple);
+
+ // Not a valid section
+ std::vector<uint8_t> section1{0x01, 0x02, 0x03};
+
+ data.insert(data.end(), section1.begin(), section1.end());
+
+ // Increment the section count
+ data.at(27) += 1;
+
+ PEL pel{data};
+ EXPECT_FALSE(pel.valid());
+
+ const auto& sections = pel.optionalSections();
+
+ bool foundGeneric = false;
+ for (const auto& section : sections)
+ {
+ if (dynamic_cast<Generic*>(section.get()) != nullptr)
+ {
+ foundGeneric = true;
+ EXPECT_EQ(section->valid(), false);
+ break;
+ }
+ }
+
+ EXPECT_TRUE(foundGeneric);
+}
+
+// Create a UserData section out of AdditionalData
+TEST_F(PELTest, MakeUDSectionTest)
+{
+ std::vector<std::string> ad{"KEY1=VALUE1", "KEY2=VALUE2", "KEY3=VALUE3",
+ "ESEL=TEST"};
+ AdditionalData additionalData{ad};
+
+ auto ud = util::makeADUserDataSection(additionalData);
+
+ EXPECT_TRUE(ud->valid());
+ EXPECT_EQ(ud->header().id, 0x5544);
+ EXPECT_EQ(ud->header().version, 0x01);
+ EXPECT_EQ(ud->header().subType, 0x01);
+ EXPECT_EQ(ud->header().componentID, 0x2000);
+
+ const auto& d = ud->data();
+
+ std::string jsonString{d.begin(), d.end()};
+
+ std::string expectedJSON =
+ R"({"KEY1":"VALUE1","KEY2":"VALUE2","KEY3":"VALUE3"})";
+
+ // The actual data is null padded to a 4B boundary.
+ std::vector<uint8_t> expectedData;
+ expectedData.resize(52, '\0');
+ memcpy(expectedData.data(), expectedJSON.data(), expectedJSON.size());
+
+ EXPECT_EQ(d, expectedData);
+
+ // Ensure we can read this as JSON
+ auto newJSON = nlohmann::json::parse(jsonString);
+ EXPECT_EQ(newJSON["KEY1"], "VALUE1");
+ EXPECT_EQ(newJSON["KEY2"], "VALUE2");
+ EXPECT_EQ(newJSON["KEY3"], "VALUE3");
+}
+
+// Create the UserData section that contains system info
+TEST_F(PELTest, SysInfoSectionTest)
+{
+ MockDataInterface dataIface;
+
+ EXPECT_CALL(dataIface, getBMCFWVersionID()).WillOnce(Return("ABCD1234"));
+
+ std::string pid = "_PID=" + std::to_string(getpid());
+ std::vector<std::string> ad{pid};
+ AdditionalData additionalData{ad};
+
+ auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface);
+
+ EXPECT_TRUE(ud->valid());
+ EXPECT_EQ(ud->header().id, 0x5544);
+ EXPECT_EQ(ud->header().version, 0x01);
+ EXPECT_EQ(ud->header().subType, 0x01);
+ EXPECT_EQ(ud->header().componentID, 0x2000);
+
+ // Pull out the JSON data and check it.
+ const auto& d = ud->data();
+ std::string jsonString{d.begin(), d.end()};
+ auto json = nlohmann::json::parse(jsonString);
+
+ // Ensure the 'Process Name' entry contains 'pel_test'
+ auto name = json["Process Name"].get<std::string>();
+ EXPECT_NE(name.find("pel_test"), std::string::npos);
+
+ auto version = json["BMC Version ID"].get<std::string>();
+ EXPECT_EQ(version, "ABCD1234");
+}
+
+// Test that the sections that override
+// virtual std::optional<std::string> Section::getJSON() const
+// return valid JSON.
+TEST_F(PELTest, SectionJSONTest)
+{
+ auto data = pelDataFactory(TestPELType::pelSimple);
+ PEL pel{data};
+
+ // Check that all JSON returned from the sections is
+ // parseable by nlohmann::json, which will throw an
+ // exception and fail the test if there is a problem.
+
+ // The getJSON() response needs to be wrapped in a { } to make
+ // actual valid JSON (PEL::toJSON() usually handles that).
+
+ auto jsonString = pel.privateHeader().getJSON();
+
+ // PrivateHeader always prints JSON
+ ASSERT_TRUE(jsonString);
+ *jsonString = '{' + *jsonString + '}';
+ auto json = nlohmann::json::parse(*jsonString);
+
+ jsonString = pel.userHeader().getJSON();
+
+ // UserHeader always prints JSON
+ ASSERT_TRUE(jsonString);
+ *jsonString = '{' + *jsonString + '}';
+ json = nlohmann::json::parse(*jsonString);
+
+ for (const auto& section : pel.optionalSections())
+ {
+ // The optional sections may or may not have implemented getJSON().
+ jsonString = section->getJSON();
+ if (jsonString)
+ {
+ *jsonString = '{' + *jsonString + '}';
+ auto json = nlohmann::json::parse(*jsonString);
+ }
+ }
+}
diff --git a/test/openpower-pels/pel_utils.cpp b/test/openpower-pels/pel_utils.cpp
new file mode 100644
index 0000000..4560b2f
--- /dev/null
+++ b/test/openpower-pels/pel_utils.cpp
@@ -0,0 +1,297 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "pel_utils.hpp"
+
+#include "extensions/openpower-pels/private_header.hpp"
+#include "extensions/openpower-pels/user_header.hpp"
+
+#include <fstream>
+
+#include <gtest/gtest.h>
+
+namespace fs = std::filesystem;
+using namespace openpower::pels;
+
+std::filesystem::path CleanLogID::pelIDFile{};
+std::filesystem::path CleanPELFiles::pelIDFile{};
+std::filesystem::path CleanPELFiles::repoPath{};
+std::filesystem::path CleanPELFiles::registryPath{};
+
+const std::vector<uint8_t> privateHeaderSection{
+ // section header
+ 0x50, 0x48, // ID 'PH'
+ 0x00, 0x30, // Size
+ 0x01, 0x02, // version, subtype
+ 0x03, 0x04, // comp ID
+
+ 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63, // create timestamp
+ 0x20, 0x31, 0x06, 0x0F, 0x09, 0x22, 0x3A, 0x00, // commit timestamp
+ 0xAA, // creatorID
+ 0x00, // logtype
+ 0x00, // reserved
+ 0x02, // section count
+ 0x90, 0x91, 0x92, 0x93, // OpenBMC log ID
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0, // creator version
+ 0x50, 0x51, 0x52, 0x53, // plid
+ 0x80, 0x81, 0x82, 0x83};
+
+const std::vector<uint8_t> userHeaderSection{
+ // section header
+ 0x55, 0x48, // ID 'UH'
+ 0x00, 0x18, // Size
+ 0x01, 0x0A, // version, subtype
+ 0x0B, 0x0C, // comp ID
+
+ 0x10, 0x04, // subsystem, scope
+ 0x20, 0x00, // severity, type
+ 0x00, 0x00, 0x00, 0x00, // reserved
+ 0x03, 0x04, // problem domain, vector
+ 0x80, 0xC0, // action flags
+ 0x00, 0x00, 0x00, 0x00 // reserved
+};
+
+const std::vector<uint8_t> srcSectionNoCallouts{
+
+ // Header
+ 'P', 'S', 0x00, 0x50, 0x01, 0x01, 0x02, 0x02,
+
+ 0x02, 0x00, 0x00, // version, flags, reserved
+ 0x09, 0x00, 0x00, // hex word count, reserved2B
+ 0x00, 0x48, // SRC structure size
+
+ // Hex words 2 - 9
+ 0x02, 0x02, 0x02, 0x55, 0x03, 0x03, 0x03, 0x10, 0x04, 0x04, 0x04, 0x04,
+ 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07,
+ 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09,
+ // ASCII string
+ 'B', 'D', '8', 'D', '5', '6', '7', '8', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' '};
+
+const std::vector<uint8_t> failingMTMSSection{
+ // Header
+ 0x4D, 0x54, 0x00, 0x1C, 0x01, 0x00, 0x20, 0x00,
+
+ 'T', 'T', 'T', 'T', '-', 'M', 'M', 'M', '1', '2',
+ '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C'};
+
+const std::vector<uint8_t> UserDataSection{
+ // Header
+ 0x55, 0x44, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00,
+
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+
+const std::vector<uint8_t> ExtUserHeaderSection{
+ // Header
+ 'E', 'H', 0x00, 0x60, 0x01, 0x00, 0x03, 0x04,
+
+ // MTMS
+ 'T', 'T', 'T', 'T', '-', 'M', 'M', 'M', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C',
+
+ // Server FW version
+ 'S', 'E', 'R', 'V', 'E', 'R', '_', 'V', 'E', 'R', 'S', 'I', 'O', 'N', '\0',
+ '\0',
+
+ // Subsystem FW Version
+ 'B', 'M', 'C', '_', 'V', 'E', 'R', 'S', 'I', 'O', 'N', '\0', '\0', '\0',
+ '\0', '\0',
+
+ 0x00, 0x00, 0x00, 0x00, // Reserved
+ 0x20, 0x25, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, // Ref time
+ 0x00, 0x00, 0x00, // Reserved
+
+ // SymptomID length and symptom ID
+ 20, 'B', 'D', '8', 'D', '4', '2', '0', '0', '_', '1', '2', '3', '4', '5',
+ '6', '7', '8', '\0', '\0', '\0'};
+
+const std::vector<uint8_t> srcFRUIdentityCallout{
+ 'I', 'D', 0x1C, 0x1D, // type, size, flags
+ '1', '2', '3', '4', // PN
+ '5', '6', '7', 0x00, 'A', 'A', 'A', 'A', // CCIN
+ '1', '2', '3', '4', '5', '6', '7', '8', // SN
+ '9', 'A', 'B', 'C'};
+
+const std::vector<uint8_t> srcPCEIdentityCallout{
+ 'P', 'E', 0x24, 0x00, // type, size, flags
+ 'T', 'T', 'T', 'T', '-', 'M', 'M', 'M', // MTM
+ '1', '2', '3', '4', '5', '6', '7', // SN
+ '8', '9', 'A', 'B', 'C', 'P', 'C', 'E', // Name + null padded
+ 'N', 'A', 'M', 'E', '1', '2', 0x00, 0x00, 0x00};
+
+const std::vector<uint8_t> srcMRUCallout{
+ '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
+ 0x00, 0x00, 0x00, 'M', // priority 1
+ 0x02, 0x02, 0x02, 0x02, // MRU ID 1
+ 0x00, 0x00, 0x00, 'L', // priority 2
+ 0x03, 0x03, 0x03, 0x03, // MRU ID 2
+ 0x00, 0x00, 0x00, 'H', // priority 3
+ 0x04, 0x04, 0x04, 0x04, // MRU ID 3
+};
+
+constexpr size_t sectionCountOffset = 27;
+
+std::vector<uint8_t> pelDataFactory(TestPELType type)
+{
+ std::vector<uint8_t> data;
+
+ switch (type)
+ {
+ case TestPELType::pelSimple:
+ data.insert(data.end(), privateHeaderSection.begin(),
+ privateHeaderSection.end());
+ data.insert(data.end(), userHeaderSection.begin(),
+ userHeaderSection.end());
+ data.insert(data.end(), srcSectionNoCallouts.begin(),
+ srcSectionNoCallouts.end());
+ data.insert(data.end(), failingMTMSSection.begin(),
+ failingMTMSSection.end());
+ data.insert(data.end(), UserDataSection.begin(),
+ UserDataSection.end());
+ data.insert(data.end(), ExtUserHeaderSection.begin(),
+ ExtUserHeaderSection.end());
+ data.at(sectionCountOffset) = 6;
+ break;
+ case TestPELType::privateHeaderSection:
+ data.insert(data.end(), privateHeaderSection.begin(),
+ privateHeaderSection.end());
+ break;
+ case TestPELType::userHeaderSection:
+ data.insert(data.end(), userHeaderSection.begin(),
+ userHeaderSection.end());
+ break;
+ case TestPELType::primarySRCSection:
+ data.insert(data.end(), srcSectionNoCallouts.begin(),
+ srcSectionNoCallouts.end());
+ break;
+ case TestPELType::primarySRCSection2Callouts:
+ {
+ // Start with the no-callouts SRC, and add the callouts section
+ // from above.
+ auto src = srcSectionNoCallouts;
+ auto callouts =
+ srcDataFactory(TestSRCType::calloutSection2Callouts);
+
+ src.insert(src.end(), callouts.begin(), callouts.end());
+
+ // Set the flag that says there are callouts
+ // One byte after the 8B header
+ src[8 + 1] |= 0x01;
+
+ // Set the new sizes
+ uint16_t size = src.size();
+ Stream stream{src};
+
+ stream.offset(2); // In the header
+ stream << size;
+
+ // In the SRC - the size field doesn't include the header
+ size -= 8;
+ stream.offset(8 + 6);
+ stream << size;
+
+ data.insert(data.end(), src.begin(), src.end());
+ break;
+ }
+ case TestPELType::failingMTMSSection:
+ data.insert(data.end(), failingMTMSSection.begin(),
+ failingMTMSSection.end());
+ }
+ return data;
+}
+
+std::vector<uint8_t> srcDataFactory(TestSRCType type)
+{
+ switch (type)
+ {
+ case TestSRCType::fruIdentityStructure:
+ return srcFRUIdentityCallout;
+
+ case TestSRCType::pceIdentityStructure:
+ return srcPCEIdentityCallout;
+
+ case TestSRCType::mruStructure:
+ return srcMRUCallout;
+
+ case TestSRCType::calloutStructureA:
+ {
+ // Add just the FRU identity substructure to the base structure
+ std::vector<uint8_t> data{
+ 0xFF, 0x28, 'H', 4, // size, flags, priority, LC length
+ 'U', '4', '2', 0x00 // LC
+ };
+
+ data.insert(data.end(), srcFRUIdentityCallout.begin(),
+ srcFRUIdentityCallout.end());
+
+ // The final size
+ data[0] = data.size();
+ return data;
+ }
+ case TestSRCType::calloutStructureB:
+ {
+ // Add all 3 substructures to the base structure
+
+ std::vector<uint8_t> data{
+ 0xFF, 0x2F, 'L', 8, // size, flags, priority, LC length
+ 'U', '1', '2', '-', 'P', '1', 0x00, 0x00 // LC
+ };
+ data.insert(data.end(), srcFRUIdentityCallout.begin(),
+ srcFRUIdentityCallout.end());
+ data.insert(data.end(), srcPCEIdentityCallout.begin(),
+ srcPCEIdentityCallout.end());
+ data.insert(data.end(), srcMRUCallout.begin(), srcMRUCallout.end());
+
+ // The final size
+ data[0] = data.size();
+ return data;
+ }
+ case TestSRCType::calloutSection2Callouts:
+ {
+ std::vector<uint8_t> data{0xC0, 0x00, 0x00,
+ 0x00}; // ID, flags, length in words
+
+ // Add 2 callouts
+ auto callout = srcDataFactory(TestSRCType::calloutStructureA);
+ data.insert(data.end(), callout.begin(), callout.end());
+
+ callout = srcDataFactory(TestSRCType::calloutStructureB);
+ data.insert(data.end(), callout.begin(), callout.end());
+
+ // Set the actual word length value at offset 2
+ Stream stream{data};
+ uint16_t wordLength = data.size() / 4;
+ stream.offset(2);
+ stream << wordLength;
+ stream.offset(0);
+
+ return data;
+ }
+ }
+ return {};
+}
+
+std::unique_ptr<std::vector<uint8_t>> readPELFile(const fs::path& path)
+{
+ std::ifstream file{path};
+
+ auto pel = std::make_unique<std::vector<uint8_t>>(
+ std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
+ return pel;
+}
diff --git a/test/openpower-pels/pel_utils.hpp b/test/openpower-pels/pel_utils.hpp
new file mode 100644
index 0000000..fae62cd
--- /dev/null
+++ b/test/openpower-pels/pel_utils.hpp
@@ -0,0 +1,106 @@
+#include "extensions/openpower-pels/paths.hpp"
+
+#include <filesystem>
+#include <memory>
+#include <vector>
+
+#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;
+};
+
+class CleanPELFiles : public ::testing::Test
+{
+ protected:
+ void SetUp() override
+ {
+ pelIDFile = openpower::pels::getPELIDFile();
+ repoPath = openpower::pels::getPELRepoPath();
+ registryPath = openpower::pels::getMessageRegistryPath();
+ }
+
+ void TearDown() override
+ {
+ std::filesystem::remove_all(
+ std::filesystem::path{pelIDFile}.parent_path());
+ std::filesystem::remove_all(repoPath);
+ std::filesystem::remove_all(registryPath);
+ }
+
+ static std::filesystem::path pelIDFile;
+ static std::filesystem::path repoPath;
+ static std::filesystem::path registryPath;
+};
+
+/**
+ * @brief Tells the factory which PEL to create
+ */
+enum class TestPELType
+{
+ pelSimple,
+ privateHeaderSection,
+ userHeaderSection,
+ primarySRCSection,
+ primarySRCSection2Callouts,
+ failingMTMSSection
+};
+
+/**
+ * @brief Tells the SRC factory which data to create
+ */
+enum class TestSRCType
+{
+ fruIdentityStructure,
+ pceIdentityStructure,
+ mruStructure,
+ calloutStructureA,
+ calloutStructureB,
+ calloutSection2Callouts
+};
+
+/**
+ * @brief PEL data factory, for testing
+ *
+ * @param[in] type - the type of data to create
+ *
+ * @return std::vector<uint8_t> - the PEL data
+ */
+std::vector<uint8_t> pelDataFactory(TestPELType type);
+
+/**
+ * @brief SRC data factory, for testing
+ *
+ * Provides pieces of the SRC PEL section, such as a callout.
+ *
+ * @param[in] type - the type of data to create
+ *
+ * @return std::vector<uint8_t> - The SRC data
+ */
+std::vector<uint8_t> srcDataFactory(TestSRCType type);
+
+/**
+ * @brief Helper function to read raw PEL data from a file
+ *
+ * @param[in] path - the path to read
+ *
+ * @return std::unique_ptr<std::vector<uint8_t>> - the data from the file
+ */
+std::unique_ptr<std::vector<uint8_t>>
+ readPELFile(const std::filesystem::path& path);
diff --git a/test/openpower-pels/pel_values_test.cpp b/test/openpower-pels/pel_values_test.cpp
new file mode 100644
index 0000000..843cb07
--- /dev/null
+++ b/test/openpower-pels/pel_values_test.cpp
@@ -0,0 +1,40 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/pel_values.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels::pel_values;
+
+TEST(PELFieldsTest, TestFindFields)
+{
+ auto s = findByValue(0x5D, subsystemValues);
+ ASSERT_NE(s, subsystemValues.end());
+ ASSERT_EQ(0x5D, std::get<fieldValuePos>(*s));
+ ASSERT_EQ("cec_service_network", std::get<registryNamePos>(*s));
+
+ s = findByName("cec_clocks", subsystemValues);
+ ASSERT_NE(s, subsystemValues.end());
+ ASSERT_EQ(0x58, std::get<fieldValuePos>(*s));
+ ASSERT_EQ("cec_clocks", std::get<registryNamePos>(*s));
+ ASSERT_EQ("CEC Hardware: Clock", std::get<descriptionPos>(*s));
+
+ s = findByValue(0xFF, subsystemValues);
+ ASSERT_EQ(s, subsystemValues.end());
+
+ s = findByName("foo", subsystemValues);
+ ASSERT_EQ(s, subsystemValues.end());
+}
diff --git a/test/openpower-pels/private_header_test.cpp b/test/openpower-pels/private_header_test.cpp
new file mode 100644
index 0000000..0349a94
--- /dev/null
+++ b/test/openpower-pels/private_header_test.cpp
@@ -0,0 +1,194 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/private_header.hpp"
+#include "pel_utils.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+
+class PrivateHeaderTest : public CleanLogID
+{
+};
+
+TEST_F(PrivateHeaderTest, SizeTest)
+{
+ EXPECT_EQ(PrivateHeader::flattenedSize(), 48);
+}
+
+TEST_F(PrivateHeaderTest, UnflattenFlattenTest)
+{
+ auto data = pelDataFactory(TestPELType::privateHeaderSection);
+
+ Stream stream(data);
+ PrivateHeader ph(stream);
+ EXPECT_EQ(ph.valid(), true);
+
+ EXPECT_EQ(ph.header().id, 0x5048);
+ EXPECT_EQ(ph.header().size, PrivateHeader::flattenedSize());
+ EXPECT_EQ(ph.header().version, 0x01);
+ EXPECT_EQ(ph.header().subType, 0x02);
+ EXPECT_EQ(ph.header().componentID, 0x0304);
+
+ auto ct = ph.createTimestamp();
+ EXPECT_EQ(ct.yearMSB, 0x20);
+ EXPECT_EQ(ct.yearLSB, 0x30);
+ EXPECT_EQ(ct.month, 0x05);
+ EXPECT_EQ(ct.day, 0x09);
+ EXPECT_EQ(ct.hour, 0x11);
+ EXPECT_EQ(ct.minutes, 0x1E);
+ EXPECT_EQ(ct.seconds, 0x01);
+ EXPECT_EQ(ct.hundredths, 0x63);
+
+ auto mt = ph.commitTimestamp();
+ EXPECT_EQ(mt.yearMSB, 0x20);
+ EXPECT_EQ(mt.yearLSB, 0x31);
+ EXPECT_EQ(mt.month, 0x06);
+ EXPECT_EQ(mt.day, 0x0F);
+ EXPECT_EQ(mt.hour, 0x09);
+ EXPECT_EQ(mt.minutes, 0x22);
+ EXPECT_EQ(mt.seconds, 0x3A);
+ EXPECT_EQ(mt.hundredths, 0x00);
+
+ EXPECT_EQ(ph.creatorID(), 0xAA);
+ EXPECT_EQ(ph.logType(), 0x00);
+ EXPECT_EQ(ph.sectionCount(), 0x02);
+ EXPECT_EQ(ph.obmcLogID(), 0x90919293);
+
+ char expected[] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x00};
+ EXPECT_TRUE(memcmp(ph.creatorVersion().version, expected, 8) == 0);
+
+ EXPECT_EQ(ph.plid(), 0x50515253);
+ EXPECT_EQ(ph.id(), 0x80818283);
+
+ // Now flatten into a vector and check that this vector
+ // matches the original one.
+ std::vector<uint8_t> newData;
+ Stream newStream(newData);
+
+ ph.flatten(newStream);
+ EXPECT_EQ(data, newData);
+
+ // Change a field, then flatten and unflatten again
+ ph.setID(0x55);
+
+ newStream.offset(0);
+ newData.clear();
+ ph.flatten(newStream);
+ EXPECT_NE(data, newData);
+
+ newStream.offset(0);
+ PrivateHeader newPH(newStream);
+
+ EXPECT_TRUE(newPH.valid());
+ EXPECT_EQ(newPH.id(), 0x55);
+}
+
+TEST_F(PrivateHeaderTest, ShortDataTest)
+{
+ auto data = pelDataFactory(TestPELType::privateHeaderSection);
+ data.resize(PrivateHeader::flattenedSize() - 1);
+ Stream stream(data);
+
+ PrivateHeader ph(stream);
+
+ EXPECT_EQ(ph.valid(), false);
+}
+
+TEST_F(PrivateHeaderTest, CorruptDataTest1)
+{
+ auto data = pelDataFactory(TestPELType::privateHeaderSection);
+ Stream stream(data);
+
+ data.at(0) = 0; // corrupt the section ID
+
+ PrivateHeader ph(stream);
+
+ EXPECT_EQ(ph.valid(), false);
+}
+
+TEST_F(PrivateHeaderTest, CorruptDataTest2)
+{
+ auto data = pelDataFactory(TestPELType::privateHeaderSection);
+ Stream stream(data);
+
+ data.at(4) = 0x22; // corrupt the version
+
+ PrivateHeader ph(stream);
+
+ EXPECT_EQ(ph.valid(), false);
+}
+
+TEST_F(PrivateHeaderTest, CorruptDataTest3)
+{
+ auto data = pelDataFactory(TestPELType::privateHeaderSection);
+ Stream stream(data);
+
+ data.at(27) = 1; // corrupt the section count
+
+ PrivateHeader ph(stream);
+
+ EXPECT_EQ(ph.valid(), false);
+}
+
+// Construct a PrivateHeader from scratch
+TEST_F(PrivateHeaderTest, ConstructionTest)
+{
+ tm time_tm;
+ time_tm.tm_year = 125;
+ time_tm.tm_mon = 11;
+ time_tm.tm_mday = 31;
+ time_tm.tm_hour = 15;
+ time_tm.tm_min = 23;
+ time_tm.tm_sec = 42;
+ time_tm.tm_isdst = 0;
+
+ // Convert the above time into a uint64_t in ms since the epoch time
+ auto timepoint = std::chrono::system_clock::from_time_t(mktime(&time_tm));
+ auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
+ timepoint.time_since_epoch())
+ .count();
+
+ PrivateHeader ph(0x3300, 42, timestamp);
+
+ EXPECT_TRUE(ph.valid());
+ EXPECT_EQ(ph.header().id, 0x5048);
+ EXPECT_EQ(ph.header().size, PrivateHeader::flattenedSize());
+ EXPECT_EQ(ph.header().version, 0x01);
+ EXPECT_EQ(ph.header().subType, 0x00);
+ EXPECT_EQ(ph.header().componentID, 0x3300);
+
+ auto& ct = ph.createTimestamp();
+ EXPECT_EQ(ct.yearMSB, 0x20);
+ EXPECT_EQ(ct.yearLSB, 0x25);
+ EXPECT_EQ(ct.month, 0x12);
+ EXPECT_EQ(ct.day, 0x31);
+ EXPECT_EQ(ct.hour, 0x15);
+ EXPECT_EQ(ct.minutes, 0x23);
+ EXPECT_EQ(ct.seconds, 0x42);
+ EXPECT_EQ(ct.hundredths, 0x00);
+
+ EXPECT_EQ(ph.creatorID(), 'O');
+ EXPECT_EQ(ph.logType(), 0x00);
+ EXPECT_EQ(ph.sectionCount(), 0x01);
+ EXPECT_EQ(ph.obmcLogID(), 42);
+
+ char expected[] = {0, 0, 0, 0, 0, 0, 0, 0};
+ EXPECT_TRUE(memcmp(ph.creatorVersion().version, expected, 8) == 0);
+
+ EXPECT_EQ(ph.id(), 0x50000001);
+ EXPECT_EQ(ph.id(), ph.plid());
+}
diff --git a/test/openpower-pels/real_pel_test.cpp b/test/openpower-pels/real_pel_test.cpp
new file mode 100644
index 0000000..a170812
--- /dev/null
+++ b/test/openpower-pels/real_pel_test.cpp
@@ -0,0 +1,570 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "elog_entry.hpp"
+#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
+{
+};
+
+// A PEL from a real system
+const std::vector<uint8_t> realPELData{
+ 0x50, 0x48, 0x0, 0x30, 0x1, 0x0, 0xA8, 0x0, 0x20, 0x19, 0x6, 0x14,
+ 0x12, 0x0, 0x41, 0x51, 0x20, 0x19, 0x6, 0x14, 0x12, 0x0, 0x41, 0x56,
+ 0x45, 0x0, 0x1, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x50, 0x1, 0xF, 0xA5, 0x50, 0x1, 0xF, 0xA5,
+ 0x55, 0x48, 0x0, 0x18, 0x1, 0x0, 0xF1, 0x0, 0x81, 0x3, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x0, 0x0, 0x1, 0x44, 0x0,
+ 0x50, 0x53, 0x0, 0x74, 0x1, 0x1, 0xA8, 0x0, 0x2, 0x1, 0x0, 0x9,
+ 0x0, 0x0, 0x0, 0x6C, 0x3, 0x1, 0x0, 0xF0, 0x2C, 0xC6, 0x1B, 0x10,
+ 0xC1, 0x39, 0x20, 0x0, 0x40, 0x0, 0x0, 0xFF, 0x10, 0x69, 0x14, 0xD8,
+ 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0,
+ 0x42, 0x31, 0x38, 0x31, 0x41, 0x38, 0x30, 0x45, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xC0, 0x0, 0x0, 0x9,
+ 0x10, 0x28, 0x48, 0x0, 0x49, 0x44, 0xC, 0x42, 0x46, 0x53, 0x50, 0x53,
+ 0x50, 0x30, 0x34, 0x0, 0x10, 0x28, 0x4C, 0x0, 0x49, 0x44, 0xC, 0x42,
+ 0x46, 0x53, 0x50, 0x53, 0x50, 0x30, 0x36, 0x0, 0x45, 0x48, 0x0, 0x60,
+ 0x1, 0x0, 0x31, 0x0, 0x38, 0x34, 0x30, 0x38, 0x2D, 0x45, 0x38, 0x45,
+ 0x31, 0x30, 0x36, 0x37, 0x41, 0x44, 0x56, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x54, 0x56, 0x38, 0x36, 0x30, 0x5F, 0x32, 0x30, 0x37, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x62, 0x30, 0x36, 0x31, 0x33, 0x61, 0x5F, 0x31,
+ 0x39, 0x32, 0x34, 0x2E, 0x38, 0x36, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14,
+ 0x42, 0x31, 0x38, 0x31, 0x41, 0x38, 0x30, 0x45, 0x5F, 0x32, 0x43, 0x43,
+ 0x36, 0x31, 0x42, 0x31, 0x30, 0x0, 0x0, 0x0, 0x55, 0x44, 0x0, 0x9C,
+ 0x2, 0x4, 0x31, 0x0, 0x0, 0x0, 0xB, 0x53, 0x2F, 0x6F, 0x70, 0x74,
+ 0x2F, 0x66, 0x69, 0x70, 0x73, 0x2F, 0x62, 0x69, 0x6E, 0x2F, 0x66, 0x77,
+ 0x64, 0x62, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x66, 0x69, 0x70, 0x73,
+ 0x38, 0x36, 0x31, 0x2F, 0x62, 0x30, 0x36, 0x31, 0x33, 0x61, 0x5F, 0x31,
+ 0x39, 0x32, 0x34, 0x2E, 0x38, 0x36, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2,
+ 0x50, 0x0, 0x0, 0x2, 0x20, 0x0, 0x1, 0xC, 0x0, 0x0, 0x0, 0x9,
+ 0x0, 0x4, 0x70, 0xD0, 0x0, 0x0, 0x0, 0x0, 0x4D, 0x54, 0x0, 0x1C,
+ 0x1, 0x0, 0x31, 0x0, 0x38, 0x34, 0x30, 0x38, 0x2D, 0x45, 0x38, 0x45,
+ 0x31, 0x30, 0x36, 0x37, 0x41, 0x44, 0x56, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x55, 0x44, 0x1, 0xF8, 0x1, 0xC, 0x31, 0x0, 0x1, 0x28, 0x4, 0x42,
+ 0x46, 0x57, 0x44, 0x42, 0x45, 0x52, 0x52, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xF0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x1, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7,
+ 0x0, 0x0, 0x5, 0x1C, 0x2D, 0x86, 0x75, 0xD0, 0x0, 0x0, 0xC, 0x9B,
+ 0x0, 0x30, 0x43, 0x4F, 0x27, 0x67, 0x44, 0xB7, 0x0, 0x0, 0x0, 0xB5,
+ 0x66, 0x77, 0x64, 0x62, 0x54, 0x72, 0x61, 0x63, 0x45, 0x72, 0x72, 0x49,
+ 0x6E, 0x66, 0x6F, 0x0, 0x73, 0x65, 0x74, 0x42, 0x75, 0x73, 0x79, 0x54,
+ 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1A,
+ 0x0, 0x0, 0xA8, 0x9, 0x50, 0x1, 0xE, 0xE3, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x4C, 0x0, 0x0, 0x5, 0x33, 0x1F, 0xEA, 0xE7, 0x12,
+ 0x0, 0x0, 0xD, 0x9D, 0x0, 0x30, 0x43, 0x4F, 0x27, 0x67, 0x44, 0xB7,
+ 0x0, 0x0, 0x0, 0xB5, 0x66, 0x77, 0x64, 0x62, 0x54, 0x72, 0x61, 0x63,
+ 0x45, 0x72, 0x72, 0x49, 0x6E, 0x66, 0x6F, 0x0, 0x73, 0x65, 0x74, 0x42,
+ 0x75, 0x73, 0x79, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0xA8, 0x9, 0x50, 0x1, 0xF, 0xA,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4C, 0x0, 0x0, 0x5, 0x38,
+ 0x26, 0x43, 0xFB, 0x66, 0x0, 0x0, 0xB, 0x7D, 0x0, 0x30, 0x43, 0x4F,
+ 0x27, 0x67, 0x44, 0xB7, 0x0, 0x0, 0x0, 0xB5, 0x66, 0x77, 0x64, 0x62,
+ 0x54, 0x72, 0x61, 0x63, 0x45, 0x72, 0x72, 0x49, 0x6E, 0x66, 0x6F, 0x0,
+ 0x73, 0x65, 0x74, 0x42, 0x75, 0x73, 0x79, 0x54, 0x69, 0x6D, 0x65, 0x6F,
+ 0x75, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0xA8, 0x9,
+ 0x50, 0x1, 0xF, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4C,
+ 0x0, 0x0, 0x5, 0x72, 0x13, 0xB8, 0x3D, 0x1C, 0x0, 0x0, 0x23, 0x33,
+ 0x0, 0x30, 0x43, 0x4F, 0x27, 0x67, 0x44, 0xB7, 0x0, 0x0, 0x0, 0xB5,
+ 0x66, 0x77, 0x64, 0x62, 0x54, 0x72, 0x61, 0x63, 0x45, 0x72, 0x72, 0x49,
+ 0x6E, 0x66, 0x6F, 0x0, 0x73, 0x65, 0x74, 0x42, 0x75, 0x73, 0x79, 0x54,
+ 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1A,
+ 0x0, 0x0, 0xA8, 0x9, 0x50, 0x1, 0xF, 0x5D, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x4C, 0x0, 0x0, 0x5, 0x88, 0x20, 0xE1, 0xE0, 0x7,
+ 0x0, 0x0, 0xD, 0x82, 0x0, 0x30, 0x43, 0x4F, 0x27, 0x67, 0x44, 0xB7,
+ 0x0, 0x0, 0x0, 0xB5, 0x66, 0x77, 0x64, 0x62, 0x54, 0x72, 0x61, 0x63,
+ 0x45, 0x72, 0x72, 0x49, 0x6E, 0x66, 0x6F, 0x0, 0x73, 0x65, 0x74, 0x42,
+ 0x75, 0x73, 0x79, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0xA8, 0x9, 0x50, 0x1, 0xF, 0x9B,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4C, 0x0, 0x0, 0x5, 0x89,
+ 0x12, 0xE, 0xFE, 0x6E, 0x0, 0x0, 0xB, 0x69, 0x0, 0x30, 0x43, 0x4F,
+ 0x27, 0x67, 0x44, 0xB7, 0x0, 0x0, 0x0, 0xB5, 0x66, 0x77, 0x64, 0x62,
+ 0x54, 0x72, 0x61, 0x63, 0x45, 0x72, 0x72, 0x49, 0x6E, 0x66, 0x6F, 0x0,
+ 0x73, 0x65, 0x74, 0x42, 0x75, 0x73, 0x79, 0x54, 0x69, 0x6D, 0x65, 0x6F,
+ 0x75, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0xA8, 0x9,
+ 0x50, 0x1, 0xF, 0xA4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4C,
+ 0x55, 0x44, 0x0, 0x40, 0x1, 0x1, 0xA8, 0x0, 0x6E, 0x6F, 0x20, 0x73,
+ 0x75, 0x63, 0x68, 0x20, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x63,
+ 0x62, 0x6C, 0x76, 0x5F, 0x63, 0x61, 0x62, 0x6C, 0x65, 0x5F, 0x63, 0x6F,
+ 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x5F, 0x73, 0x74, 0x61,
+ 0x74, 0x75, 0x73, 0x5F, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x5F, 0x76,
+ 0x69, 0x65, 0x77, 0x0, 0x55, 0x44, 0x1, 0xE8, 0x1, 0xC, 0x31, 0x0,
+ 0x1, 0x28, 0x4, 0x42, 0x46, 0x57, 0x44, 0x42, 0x53, 0x52, 0x56, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xE0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xE0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xB, 0x0, 0x0, 0x5, 0x89, 0x11, 0x9B, 0xBA, 0x9,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x18, 0x43, 0x4F, 0x80, 0x10, 0xFA, 0x28,
+ 0x0, 0x0, 0xC, 0xFD, 0x73, 0x65, 0x74, 0x42, 0x75, 0x73, 0x79, 0x54,
+ 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x0, 0x0, 0xDB, 0xFB, 0xCB, 0x98,
+ 0x0, 0x1, 0xD4, 0xC0, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x5, 0x89,
+ 0x12, 0x4, 0x1A, 0xF1, 0x0, 0x0, 0x23, 0x88, 0x0, 0x18, 0x43, 0x4F,
+ 0xCB, 0x1C, 0xBA, 0x66, 0x0, 0x0, 0x1F, 0x6D, 0x63, 0x68, 0x61, 0x6E,
+ 0x67, 0x65, 0x55, 0x73, 0x65, 0x43, 0x6E, 0x74, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xC, 0xDB, 0xFB, 0xCB, 0x98, 0x0, 0x0, 0x0, 0x34,
+ 0x0, 0x0, 0x5, 0x89, 0x12, 0x36, 0x78, 0xA9, 0x0, 0x0, 0x23, 0x88,
+ 0x0, 0x0, 0x43, 0x4F, 0xCB, 0x12, 0xB6, 0x5D, 0x0, 0x0, 0x9, 0xF2,
+ 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x5, 0x89, 0x12, 0x36, 0xB8, 0x93,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x10, 0x43, 0x4F, 0x89, 0x7D, 0x0, 0x8C,
+ 0x0, 0x0, 0x23, 0x5D, 0x73, 0x65, 0x72, 0x76, 0x44, 0x65, 0x74, 0x61,
+ 0x63, 0x68, 0x0, 0x0, 0xDB, 0xFB, 0xCB, 0x98, 0x0, 0x0, 0x0, 0x2C,
+ 0x0, 0x0, 0x5, 0x89, 0x12, 0x36, 0xD2, 0x7A, 0x0, 0x0, 0x23, 0x88,
+ 0x0, 0x0, 0x43, 0x4F, 0xBD, 0xF0, 0x6E, 0xE3, 0x0, 0x0, 0x9, 0xF4,
+ 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x5, 0x89, 0x12, 0xAA, 0x51, 0x96,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x0, 0x43, 0x4F, 0x8F, 0x6A, 0x83, 0x3,
+ 0x0, 0x0, 0x9, 0xA9, 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x5, 0x89,
+ 0x12, 0xAE, 0x7B, 0x85, 0x0, 0x0, 0x23, 0x88, 0x0, 0x10, 0x43, 0x4F,
+ 0xF3, 0x35, 0x3F, 0x8D, 0x0, 0x0, 0x22, 0x35, 0x73, 0x65, 0x72, 0x76,
+ 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x0, 0x0, 0x9D, 0x86, 0xE6, 0xD3,
+ 0x0, 0x0, 0x0, 0x2C, 0x0, 0x0, 0x5, 0x89, 0x13, 0xA5, 0x23, 0xFA,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x0, 0x43, 0x4F, 0xFC, 0x5A, 0x7F, 0x97,
+ 0x0, 0x0, 0x9, 0xB3, 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x5, 0x89,
+ 0x14, 0x5B, 0x57, 0xCE, 0x0, 0x0, 0x23, 0x88, 0x0, 0x0, 0x43, 0x4F,
+ 0x4, 0x88, 0x89, 0x7B, 0x0, 0x0, 0x9, 0x3B, 0x0, 0x0, 0x0, 0x1C,
+ 0x0, 0x0, 0x5, 0x89, 0x14, 0x5F, 0xCC, 0x54, 0x0, 0x0, 0x23, 0x88,
+ 0x0, 0x50, 0x43, 0x4F, 0x94, 0xA2, 0x26, 0x7C, 0x0, 0x0, 0x14, 0xE1,
+ 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x53, 0x74, 0x6D, 0x74, 0x0,
+ 0x0, 0x0, 0x0, 0x1, 0x6E, 0x6F, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20,
+ 0x74, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x63, 0x62, 0x6C, 0x76, 0x5F,
+ 0x63, 0x61, 0x62, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63,
+ 0x74, 0x69, 0x6F, 0x6E, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5F,
+ 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x5F, 0x76, 0x69, 0x65, 0x77, 0x0,
+ 0x10, 0x69, 0x14, 0xD8, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x6C,
+ 0x55, 0x44, 0x3, 0xC0, 0x1, 0xC, 0x31, 0x0, 0x1, 0x28, 0x4, 0x42,
+ 0x46, 0x57, 0x44, 0x42, 0x53, 0x51, 0x4C, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xB8, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x3, 0xB8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xB,
+ 0x0, 0x0, 0x5, 0x89, 0x5, 0xFC, 0x1E, 0x6C, 0x0, 0x0, 0x23, 0x88,
+ 0x0, 0x40, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4, 0x0, 0x0, 0x21, 0x6,
+ 0x14, 0x9, 0xC2, 0x11, 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x2A,
+ 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x73, 0x79, 0x73, 0x2E, 0x70, 0x6F,
+ 0x6C, 0x69, 0x63, 0x79, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x6E,
+ 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x27, 0x70, 0x6F, 0x77, 0x65, 0x72,
+ 0x2D, 0x64, 0x65, 0x66, 0x2D, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D,
+ 0x65, 0x73, 0x27, 0x0, 0x0, 0x0, 0x0, 0x5C, 0x0, 0x0, 0x5, 0x89,
+ 0x6, 0xB1, 0x42, 0x3B, 0x0, 0x0, 0x23, 0x88, 0x0, 0x40, 0x43, 0x4F,
+ 0x92, 0x41, 0x1B, 0xD4, 0x0, 0x0, 0x21, 0x6, 0x14, 0x9, 0xC2, 0x11,
+ 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x2A, 0x20, 0x66, 0x72, 0x6F,
+ 0x6D, 0x20, 0x73, 0x79, 0x73, 0x2E, 0x70, 0x6F, 0x6C, 0x69, 0x63, 0x79,
+ 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x20,
+ 0x3D, 0x20, 0x27, 0x70, 0x6F, 0x77, 0x65, 0x72, 0x2D, 0x64, 0x65, 0x66,
+ 0x2D, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x73, 0x27, 0x0,
+ 0x0, 0x0, 0x0, 0x5C, 0x0, 0x0, 0x5, 0x89, 0x7, 0x14, 0xD2, 0x68,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x40, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4,
+ 0x0, 0x0, 0x21, 0x6, 0x14, 0x9, 0xC2, 0x11, 0x73, 0x65, 0x6C, 0x65,
+ 0x63, 0x74, 0x20, 0x2A, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x73, 0x79,
+ 0x73, 0x2E, 0x70, 0x6F, 0x6C, 0x69, 0x63, 0x79, 0x20, 0x77, 0x68, 0x65,
+ 0x72, 0x65, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x27, 0x70,
+ 0x6F, 0x77, 0x65, 0x72, 0x2D, 0x64, 0x65, 0x66, 0x2D, 0x66, 0x69, 0x6C,
+ 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x73, 0x27, 0x0, 0x0, 0x0, 0x0, 0x5C,
+ 0x0, 0x0, 0x5, 0x89, 0x7, 0x9A, 0x9D, 0x6F, 0x0, 0x0, 0x23, 0x88,
+ 0x0, 0x40, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4, 0x0, 0x0, 0x21, 0x6,
+ 0x14, 0x9, 0xC2, 0x11, 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x2A,
+ 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x73, 0x79, 0x73, 0x2E, 0x70, 0x6F,
+ 0x6C, 0x69, 0x63, 0x79, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x6E,
+ 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x27, 0x70, 0x6F, 0x77, 0x65, 0x72,
+ 0x2D, 0x64, 0x65, 0x66, 0x2D, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D,
+ 0x65, 0x73, 0x27, 0x0, 0x0, 0x0, 0x0, 0x5C, 0x0, 0x0, 0x5, 0x89,
+ 0x7, 0xD4, 0xF8, 0x2D, 0x0, 0x0, 0x23, 0x88, 0x0, 0x40, 0x43, 0x4F,
+ 0x92, 0x41, 0x1B, 0xD4, 0x0, 0x0, 0x21, 0x6, 0x14, 0x9, 0xC2, 0x11,
+ 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x2A, 0x20, 0x66, 0x72, 0x6F,
+ 0x6D, 0x20, 0x73, 0x79, 0x73, 0x2E, 0x70, 0x6F, 0x6C, 0x69, 0x63, 0x79,
+ 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x20,
+ 0x3D, 0x20, 0x27, 0x70, 0x6F, 0x77, 0x65, 0x72, 0x2D, 0x64, 0x65, 0x66,
+ 0x2D, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x73, 0x27, 0x0,
+ 0x0, 0x0, 0x0, 0x5C, 0x0, 0x0, 0x5, 0x89, 0x12, 0xB4, 0xBC, 0xA6,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x38, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4,
+ 0x0, 0x0, 0x21, 0x6, 0x9D, 0x86, 0xE6, 0xD3, 0x41, 0x54, 0x54, 0x41,
+ 0x43, 0x48, 0x20, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x20,
+ 0x27, 0x2F, 0x6F, 0x70, 0x74, 0x2F, 0x70, 0x31, 0x2F, 0x66, 0x77, 0x73,
+ 0x6D, 0x2F, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x2E, 0x73, 0x79, 0x73,
+ 0x27, 0x20, 0x41, 0x53, 0x20, 0x73, 0x79, 0x73, 0x3B, 0xA, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x5, 0x89, 0x12, 0xBC, 0xD8, 0x8A,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x38, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4,
+ 0x0, 0x0, 0x21, 0x6, 0x9D, 0x86, 0xE6, 0xD3, 0x41, 0x54, 0x54, 0x41,
+ 0x43, 0x48, 0x20, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x20,
+ 0x27, 0x2F, 0x6F, 0x70, 0x74, 0x2F, 0x70, 0x30, 0x2F, 0x66, 0x77, 0x73,
+ 0x6D, 0x2F, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x2E, 0x70, 0x30, 0x27,
+ 0x20, 0x41, 0x53, 0x20, 0x70, 0x30, 0x3B, 0xA, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x5, 0x89, 0x13, 0x97, 0x51, 0x64,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x38, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4,
+ 0x0, 0x0, 0x21, 0x6, 0x9D, 0x86, 0xE6, 0xD3, 0x41, 0x54, 0x54, 0x41,
+ 0x43, 0x48, 0x20, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x20,
+ 0x27, 0x2F, 0x6F, 0x70, 0x74, 0x2F, 0x70, 0x31, 0x2F, 0x66, 0x77, 0x73,
+ 0x6D, 0x2F, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x2E, 0x70, 0x31, 0x27,
+ 0x20, 0x41, 0x53, 0x20, 0x70, 0x31, 0x3B, 0xA, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x5, 0x89, 0x13, 0xA2, 0x85, 0x51,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x38, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4,
+ 0x0, 0x0, 0x21, 0x6, 0x9D, 0x86, 0xE6, 0xD3, 0x41, 0x54, 0x54, 0x41,
+ 0x43, 0x48, 0x20, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x20,
+ 0x27, 0x2F, 0x6F, 0x70, 0x74, 0x2F, 0x70, 0x33, 0x2F, 0x66, 0x77, 0x73,
+ 0x6D, 0x2F, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x2E, 0x70, 0x33, 0x27,
+ 0x20, 0x41, 0x53, 0x20, 0x70, 0x33, 0x3B, 0xA, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x5, 0x89, 0x14, 0x5B, 0x84, 0x7B,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x58, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4,
+ 0x0, 0x0, 0x21, 0x6, 0x9D, 0x86, 0xE6, 0xD3, 0x73, 0x65, 0x6C, 0x65,
+ 0x63, 0x74, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x5F, 0x65, 0x69, 0x64,
+ 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x63, 0x62, 0x6C, 0x76, 0x5F, 0x63,
+ 0x61, 0x62, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74,
+ 0x69, 0x6F, 0x6E, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5F, 0x70,
+ 0x75, 0x62, 0x6C, 0x69, 0x63, 0x5F, 0x76, 0x69, 0x65, 0x77, 0x20, 0x77,
+ 0x68, 0x65, 0x72, 0x65, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x5F, 0x65,
+ 0x69, 0x64, 0x20, 0x21, 0x3D, 0x20, 0x30, 0x0, 0x0, 0x0, 0x0, 0x74,
+ 0x55, 0x44, 0x0, 0xC, 0x1, 0xC, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x53, 0x53, 0x0, 0x50, 0x1, 0x1, 0xA8, 0x0, 0x2, 0x0, 0x0, 0x9,
+ 0x0, 0x0, 0x0, 0x48, 0x3, 0x1, 0x0, 0xF0, 0x2C, 0xC6, 0x19, 0x10,
+ 0xC1, 0x39, 0x20, 0x0, 0x40, 0x0, 0x0, 0xFF, 0x9D, 0x86, 0xE6, 0xD3,
+ 0x0, 0x0, 0x0, 0x3, 0x10, 0x69, 0x14, 0xD8, 0x0, 0x0, 0x0, 0x0,
+ 0x42, 0x31, 0x38, 0x31, 0x41, 0x38, 0x30, 0x45, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x55, 0x44, 0x1, 0xF4,
+ 0x1, 0xC, 0x31, 0x0, 0x1, 0x28, 0x4, 0x42, 0x46, 0x57, 0x44, 0x42,
+ 0x45, 0x52, 0x52, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x1, 0xEC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xEC,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x5, 0x33,
+ 0x1F, 0xEA, 0xE7, 0x12, 0x0, 0x0, 0xD, 0x9D, 0x0, 0x30, 0x43, 0x4F,
+ 0x27, 0x67, 0x44, 0xB7, 0x0, 0x0, 0x0, 0xB5, 0x66, 0x77, 0x64, 0x62,
+ 0x54, 0x72, 0x61, 0x63, 0x45, 0x72, 0x72, 0x49, 0x6E, 0x66, 0x6F, 0x0,
+ 0x73, 0x65, 0x74, 0x42, 0x75, 0x73, 0x79, 0x54, 0x69, 0x6D, 0x65, 0x6F,
+ 0x75, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0xA8, 0x9,
+ 0x50, 0x1, 0xF, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4C,
+ 0x0, 0x0, 0x5, 0x38, 0x26, 0x43, 0xFB, 0x66, 0x0, 0x0, 0xB, 0x7D,
+ 0x0, 0x30, 0x43, 0x4F, 0x27, 0x67, 0x44, 0xB7, 0x0, 0x0, 0x0, 0xB5,
+ 0x66, 0x77, 0x64, 0x62, 0x54, 0x72, 0x61, 0x63, 0x45, 0x72, 0x72, 0x49,
+ 0x6E, 0x66, 0x6F, 0x0, 0x73, 0x65, 0x74, 0x42, 0x75, 0x73, 0x79, 0x54,
+ 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1A,
+ 0x0, 0x0, 0xA8, 0x9, 0x50, 0x1, 0xF, 0xE, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x4C, 0x0, 0x0, 0x5, 0x72, 0x13, 0xB8, 0x3D, 0x1C,
+ 0x0, 0x0, 0x23, 0x33, 0x0, 0x30, 0x43, 0x4F, 0x27, 0x67, 0x44, 0xB7,
+ 0x0, 0x0, 0x0, 0xB5, 0x66, 0x77, 0x64, 0x62, 0x54, 0x72, 0x61, 0x63,
+ 0x45, 0x72, 0x72, 0x49, 0x6E, 0x66, 0x6F, 0x0, 0x73, 0x65, 0x74, 0x42,
+ 0x75, 0x73, 0x79, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0xA8, 0x9, 0x50, 0x1, 0xF, 0x5D,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4C, 0x0, 0x0, 0x5, 0x88,
+ 0x20, 0xE1, 0xE0, 0x7, 0x0, 0x0, 0xD, 0x82, 0x0, 0x30, 0x43, 0x4F,
+ 0x27, 0x67, 0x44, 0xB7, 0x0, 0x0, 0x0, 0xB5, 0x66, 0x77, 0x64, 0x62,
+ 0x54, 0x72, 0x61, 0x63, 0x45, 0x72, 0x72, 0x49, 0x6E, 0x66, 0x6F, 0x0,
+ 0x73, 0x65, 0x74, 0x42, 0x75, 0x73, 0x79, 0x54, 0x69, 0x6D, 0x65, 0x6F,
+ 0x75, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0xA8, 0x9,
+ 0x50, 0x1, 0xF, 0x9B, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4C,
+ 0x0, 0x0, 0x5, 0x89, 0x12, 0xE, 0xFE, 0x6E, 0x0, 0x0, 0xB, 0x69,
+ 0x0, 0x30, 0x43, 0x4F, 0x27, 0x67, 0x44, 0xB7, 0x0, 0x0, 0x0, 0xB5,
+ 0x66, 0x77, 0x64, 0x62, 0x54, 0x72, 0x61, 0x63, 0x45, 0x72, 0x72, 0x49,
+ 0x6E, 0x66, 0x6F, 0x0, 0x73, 0x65, 0x74, 0x42, 0x75, 0x73, 0x79, 0x54,
+ 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1A,
+ 0x0, 0x0, 0xA8, 0x9, 0x50, 0x1, 0xF, 0xA4, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x4C, 0x0, 0x0, 0x5, 0x89, 0x15, 0x7A, 0x13, 0xA4,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x2C, 0x43, 0x4F, 0x27, 0x67, 0x44, 0xB7,
+ 0x0, 0x0, 0x0, 0xB5, 0x66, 0x77, 0x64, 0x62, 0x54, 0x72, 0x61, 0x63,
+ 0x45, 0x72, 0x72, 0x49, 0x6E, 0x66, 0x6F, 0x0, 0x73, 0x65, 0x72, 0x76,
+ 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x0, 0x0, 0x0, 0x0, 0x1B,
+ 0x0, 0x0, 0xA8, 0xE, 0x50, 0x1, 0xF, 0xA5, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x48, 0x55, 0x44, 0x1, 0xE8, 0x1, 0xC, 0x31, 0x0,
+ 0x1, 0x28, 0x4, 0x42, 0x46, 0x57, 0x44, 0x42, 0x53, 0x52, 0x56, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xE0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xE0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x5, 0x89, 0x12, 0x36, 0xB8, 0x93,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x10, 0x43, 0x4F, 0x89, 0x7D, 0x0, 0x8C,
+ 0x0, 0x0, 0x23, 0x5D, 0x73, 0x65, 0x72, 0x76, 0x44, 0x65, 0x74, 0x61,
+ 0x63, 0x68, 0x0, 0x0, 0xDB, 0xFB, 0xCB, 0x98, 0x0, 0x0, 0x0, 0x2C,
+ 0x0, 0x0, 0x5, 0x89, 0x12, 0x36, 0xD2, 0x7A, 0x0, 0x0, 0x23, 0x88,
+ 0x0, 0x0, 0x43, 0x4F, 0xBD, 0xF0, 0x6E, 0xE3, 0x0, 0x0, 0x9, 0xF4,
+ 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x5, 0x89, 0x12, 0xAA, 0x51, 0x96,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x0, 0x43, 0x4F, 0x8F, 0x6A, 0x83, 0x3,
+ 0x0, 0x0, 0x9, 0xA9, 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x5, 0x89,
+ 0x12, 0xAE, 0x7B, 0x85, 0x0, 0x0, 0x23, 0x88, 0x0, 0x10, 0x43, 0x4F,
+ 0xF3, 0x35, 0x3F, 0x8D, 0x0, 0x0, 0x22, 0x35, 0x73, 0x65, 0x72, 0x76,
+ 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x0, 0x0, 0x9D, 0x86, 0xE6, 0xD3,
+ 0x0, 0x0, 0x0, 0x2C, 0x0, 0x0, 0x5, 0x89, 0x13, 0xA5, 0x23, 0xFA,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x0, 0x43, 0x4F, 0xFC, 0x5A, 0x7F, 0x97,
+ 0x0, 0x0, 0x9, 0xB3, 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x5, 0x89,
+ 0x14, 0x5B, 0x57, 0xCE, 0x0, 0x0, 0x23, 0x88, 0x0, 0x0, 0x43, 0x4F,
+ 0x4, 0x88, 0x89, 0x7B, 0x0, 0x0, 0x9, 0x3B, 0x0, 0x0, 0x0, 0x1C,
+ 0x0, 0x0, 0x5, 0x89, 0x14, 0x5F, 0xCC, 0x54, 0x0, 0x0, 0x23, 0x88,
+ 0x0, 0x50, 0x43, 0x4F, 0x94, 0xA2, 0x26, 0x7C, 0x0, 0x0, 0x14, 0xE1,
+ 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x53, 0x74, 0x6D, 0x74, 0x0,
+ 0x0, 0x0, 0x0, 0x1, 0x6E, 0x6F, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20,
+ 0x74, 0x61, 0x62, 0x6C, 0x65, 0x3A, 0x20, 0x63, 0x62, 0x6C, 0x76, 0x5F,
+ 0x63, 0x61, 0x62, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63,
+ 0x74, 0x69, 0x6F, 0x6E, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5F,
+ 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x5F, 0x76, 0x69, 0x65, 0x77, 0x0,
+ 0x10, 0x69, 0x14, 0xD8, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x6C,
+ 0x0, 0x0, 0x5, 0x89, 0x15, 0x79, 0xCE, 0xB1, 0x0, 0x0, 0x23, 0x88,
+ 0x0, 0x68, 0x43, 0x4F, 0xF9, 0xB, 0x8E, 0x3C, 0x0, 0x0, 0x21, 0x3E,
+ 0x73, 0x65, 0x72, 0x76, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x0,
+ 0x9D, 0x86, 0xE6, 0xD3, 0x0, 0x0, 0x0, 0x3, 0x73, 0x65, 0x6C, 0x65,
+ 0x63, 0x74, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x5F, 0x65, 0x69, 0x64,
+ 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x63, 0x62, 0x6C, 0x76, 0x5F, 0x63,
+ 0x61, 0x62, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74,
+ 0x69, 0x6F, 0x6E, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5F, 0x70,
+ 0x75, 0x62, 0x6C, 0x69, 0x63, 0x5F, 0x76, 0x69, 0x65, 0x77, 0x20, 0x77,
+ 0x68, 0x65, 0x72, 0x65, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x5F, 0x65,
+ 0x69, 0x64, 0x20, 0x21, 0x3D, 0x20, 0x30, 0x0, 0x0, 0x0, 0x0, 0x84,
+ 0x55, 0x44, 0x3, 0xC0, 0x1, 0xC, 0x31, 0x0, 0x1, 0x28, 0x4, 0x42,
+ 0x46, 0x57, 0x44, 0x42, 0x53, 0x51, 0x4C, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xB8, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x3, 0xB8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xB,
+ 0x0, 0x0, 0x5, 0x89, 0x5, 0xFC, 0x1E, 0x6C, 0x0, 0x0, 0x23, 0x88,
+ 0x0, 0x40, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4, 0x0, 0x0, 0x21, 0x6,
+ 0x14, 0x9, 0xC2, 0x11, 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x2A,
+ 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x73, 0x79, 0x73, 0x2E, 0x70, 0x6F,
+ 0x6C, 0x69, 0x63, 0x79, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x6E,
+ 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x27, 0x70, 0x6F, 0x77, 0x65, 0x72,
+ 0x2D, 0x64, 0x65, 0x66, 0x2D, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D,
+ 0x65, 0x73, 0x27, 0x0, 0x0, 0x0, 0x0, 0x5C, 0x0, 0x0, 0x5, 0x89,
+ 0x6, 0xB1, 0x42, 0x3B, 0x0, 0x0, 0x23, 0x88, 0x0, 0x40, 0x43, 0x4F,
+ 0x92, 0x41, 0x1B, 0xD4, 0x0, 0x0, 0x21, 0x6, 0x14, 0x9, 0xC2, 0x11,
+ 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x2A, 0x20, 0x66, 0x72, 0x6F,
+ 0x6D, 0x20, 0x73, 0x79, 0x73, 0x2E, 0x70, 0x6F, 0x6C, 0x69, 0x63, 0x79,
+ 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x20,
+ 0x3D, 0x20, 0x27, 0x70, 0x6F, 0x77, 0x65, 0x72, 0x2D, 0x64, 0x65, 0x66,
+ 0x2D, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x73, 0x27, 0x0,
+ 0x0, 0x0, 0x0, 0x5C, 0x0, 0x0, 0x5, 0x89, 0x7, 0x14, 0xD2, 0x68,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x40, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4,
+ 0x0, 0x0, 0x21, 0x6, 0x14, 0x9, 0xC2, 0x11, 0x73, 0x65, 0x6C, 0x65,
+ 0x63, 0x74, 0x20, 0x2A, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x73, 0x79,
+ 0x73, 0x2E, 0x70, 0x6F, 0x6C, 0x69, 0x63, 0x79, 0x20, 0x77, 0x68, 0x65,
+ 0x72, 0x65, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x27, 0x70,
+ 0x6F, 0x77, 0x65, 0x72, 0x2D, 0x64, 0x65, 0x66, 0x2D, 0x66, 0x69, 0x6C,
+ 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x73, 0x27, 0x0, 0x0, 0x0, 0x0, 0x5C,
+ 0x0, 0x0, 0x5, 0x89, 0x7, 0x9A, 0x9D, 0x6F, 0x0, 0x0, 0x23, 0x88,
+ 0x0, 0x40, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4, 0x0, 0x0, 0x21, 0x6,
+ 0x14, 0x9, 0xC2, 0x11, 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x2A,
+ 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x73, 0x79, 0x73, 0x2E, 0x70, 0x6F,
+ 0x6C, 0x69, 0x63, 0x79, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x6E,
+ 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x27, 0x70, 0x6F, 0x77, 0x65, 0x72,
+ 0x2D, 0x64, 0x65, 0x66, 0x2D, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D,
+ 0x65, 0x73, 0x27, 0x0, 0x0, 0x0, 0x0, 0x5C, 0x0, 0x0, 0x5, 0x89,
+ 0x7, 0xD4, 0xF8, 0x2D, 0x0, 0x0, 0x23, 0x88, 0x0, 0x40, 0x43, 0x4F,
+ 0x92, 0x41, 0x1B, 0xD4, 0x0, 0x0, 0x21, 0x6, 0x14, 0x9, 0xC2, 0x11,
+ 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x2A, 0x20, 0x66, 0x72, 0x6F,
+ 0x6D, 0x20, 0x73, 0x79, 0x73, 0x2E, 0x70, 0x6F, 0x6C, 0x69, 0x63, 0x79,
+ 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x20,
+ 0x3D, 0x20, 0x27, 0x70, 0x6F, 0x77, 0x65, 0x72, 0x2D, 0x64, 0x65, 0x66,
+ 0x2D, 0x66, 0x69, 0x6C, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x73, 0x27, 0x0,
+ 0x0, 0x0, 0x0, 0x5C, 0x0, 0x0, 0x5, 0x89, 0x12, 0xB4, 0xBC, 0xA6,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x38, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4,
+ 0x0, 0x0, 0x21, 0x6, 0x9D, 0x86, 0xE6, 0xD3, 0x41, 0x54, 0x54, 0x41,
+ 0x43, 0x48, 0x20, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x20,
+ 0x27, 0x2F, 0x6F, 0x70, 0x74, 0x2F, 0x70, 0x31, 0x2F, 0x66, 0x77, 0x73,
+ 0x6D, 0x2F, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x2E, 0x73, 0x79, 0x73,
+ 0x27, 0x20, 0x41, 0x53, 0x20, 0x73, 0x79, 0x73, 0x3B, 0xA, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x5, 0x89, 0x12, 0xBC, 0xD8, 0x8A,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x38, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4,
+ 0x0, 0x0, 0x21, 0x6, 0x9D, 0x86, 0xE6, 0xD3, 0x41, 0x54, 0x54, 0x41,
+ 0x43, 0x48, 0x20, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x20,
+ 0x27, 0x2F, 0x6F, 0x70, 0x74, 0x2F, 0x70, 0x30, 0x2F, 0x66, 0x77, 0x73,
+ 0x6D, 0x2F, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x2E, 0x70, 0x30, 0x27,
+ 0x20, 0x41, 0x53, 0x20, 0x70, 0x30, 0x3B, 0xA, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x5, 0x89, 0x13, 0x97, 0x51, 0x64,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x38, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4,
+ 0x0, 0x0, 0x21, 0x6, 0x9D, 0x86, 0xE6, 0xD3, 0x41, 0x54, 0x54, 0x41,
+ 0x43, 0x48, 0x20, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x20,
+ 0x27, 0x2F, 0x6F, 0x70, 0x74, 0x2F, 0x70, 0x31, 0x2F, 0x66, 0x77, 0x73,
+ 0x6D, 0x2F, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x2E, 0x70, 0x31, 0x27,
+ 0x20, 0x41, 0x53, 0x20, 0x70, 0x31, 0x3B, 0xA, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x5, 0x89, 0x13, 0xA2, 0x85, 0x51,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x38, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4,
+ 0x0, 0x0, 0x21, 0x6, 0x9D, 0x86, 0xE6, 0xD3, 0x41, 0x54, 0x54, 0x41,
+ 0x43, 0x48, 0x20, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x20,
+ 0x27, 0x2F, 0x6F, 0x70, 0x74, 0x2F, 0x70, 0x33, 0x2F, 0x66, 0x77, 0x73,
+ 0x6D, 0x2F, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x2E, 0x70, 0x33, 0x27,
+ 0x20, 0x41, 0x53, 0x20, 0x70, 0x33, 0x3B, 0xA, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x5, 0x89, 0x14, 0x5B, 0x84, 0x7B,
+ 0x0, 0x0, 0x23, 0x88, 0x0, 0x58, 0x43, 0x4F, 0x92, 0x41, 0x1B, 0xD4,
+ 0x0, 0x0, 0x21, 0x6, 0x9D, 0x86, 0xE6, 0xD3, 0x73, 0x65, 0x6C, 0x65,
+ 0x63, 0x74, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x5F, 0x65, 0x69, 0x64,
+ 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x63, 0x62, 0x6C, 0x76, 0x5F, 0x63,
+ 0x61, 0x62, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74,
+ 0x69, 0x6F, 0x6E, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5F, 0x70,
+ 0x75, 0x62, 0x6C, 0x69, 0x63, 0x5F, 0x76, 0x69, 0x65, 0x77, 0x20, 0x77,
+ 0x68, 0x65, 0x72, 0x65, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x5F, 0x65,
+ 0x69, 0x64, 0x20, 0x21, 0x3D, 0x20, 0x30, 0x0, 0x0, 0x0, 0x0, 0x74,
+ 0x55, 0x44, 0x0, 0xC, 0x1, 0xC, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x53, 0x53, 0x0, 0x50, 0x1, 0x1, 0xA8, 0x0, 0x2, 0x0, 0x0, 0x9,
+ 0x0, 0x0, 0x0, 0x48, 0x3, 0x1, 0x0, 0xF0, 0x2C, 0xC6, 0x67, 0x10,
+ 0xC1, 0x39, 0x20, 0x0, 0x40, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x3,
+ 0x9D, 0x86, 0xE6, 0xD3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x42, 0x31, 0x38, 0x31, 0x41, 0x38, 0x30, 0x35, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x55, 0x44, 0x1, 0xF4,
+ 0x1, 0xC, 0x31, 0x0, 0x1, 0x28, 0x4, 0x42, 0x46, 0x57, 0x44, 0x42,
+ 0x45, 0x52, 0x52, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x1, 0xEC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xEC,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x5, 0x38,
+ 0x26, 0x43, 0xFB, 0x66, 0x0, 0x0, 0xB, 0x7D, 0x0, 0x30, 0x43, 0x4F,
+ 0x27, 0x67, 0x44, 0xB7, 0x0, 0x0, 0x0, 0xB5, 0x66, 0x77, 0x64, 0x62,
+ 0x54, 0x72, 0x61, 0x63, 0x45, 0x72, 0x72, 0x49, 0x6E, 0x66, 0x6F, 0x0,
+ 0x73, 0x65, 0x74, 0x42, 0x75, 0x73, 0x79, 0x54, 0x69, 0x6D, 0x65, 0x6F,
+ 0x75, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0xA8, 0x9,
+ 0x50, 0x1, 0xF, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4C,
+ 0x0, 0x0, 0x5, 0x72, 0x13, 0xB8, 0x3D, 0x1C, 0x0, 0x0, 0x23, 0x33,
+ 0x0, 0x30, 0x43, 0x4F, 0x27, 0x67, 0x44, 0xB7, 0x0, 0x0, 0x0, 0xB5,
+ 0x66, 0x77, 0x64, 0x62, 0x54, 0x72, 0x61, 0x63, 0x45, 0x72, 0x72, 0x49,
+ 0x6E, 0x66, 0x6F, 0x0, 0x73, 0x65, 0x74, 0x42, 0x75, 0x73, 0x79, 0x54,
+ 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1A,
+ 0x0, 0x0, 0xA8, 0x9, 0x50, 0x1, 0xF, 0x5D, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x4C, 0x0, 0x0, 0x5, 0x88, 0x20, 0xE1, 0xE0, 0x7,
+ 0x0, 0x0, 0xD, 0x82, 0x0, 0x30, 0x43, 0x4F, 0x27, 0x67, 0x44, 0xB7,
+ 0x0, 0x0, 0x0, 0xB5, 0x66, 0x77, 0x64, 0x62, 0x54, 0x72, 0x61, 0x63,
+ 0x45, 0x72, 0x72, 0x49, 0x6E, 0x66, 0x6F, 0x0, 0x73, 0x65, 0x74, 0x42,
+ 0x75, 0x73, 0x79, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0xA8, 0x9, 0x50, 0x1, 0xF, 0x9B,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4C, 0x0, 0x0, 0x5, 0x89,
+ 0x12, 0xE, 0xFE, 0x6E, 0x0, 0x0, 0xB, 0x69, 0x0, 0x30, 0x43, 0x4F,
+ 0x27, 0x67, 0x44, 0xB7, 0x0, 0x0, 0x0, 0xB5, 0x66, 0x77, 0x64, 0x62,
+ 0x54, 0x72, 0x61, 0x63, 0x45, 0x72, 0x72, 0x49, 0x6E, 0x66, 0x6F, 0x0,
+ 0x73, 0x65, 0x74, 0x42, 0x75, 0x73, 0x79, 0x54, 0x69, 0x6D, 0x65, 0x6F,
+ 0x75, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1A, 0x0, 0x0, 0xA8, 0x9,
+ 0x50, 0x1, 0xF, 0xA4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4C,
+ 0x0, 0x0, 0x5, 0x89, 0x15, 0x7A, 0x13, 0xA4, 0x0, 0x0, 0x23, 0x88,
+ 0x0, 0x2C, 0x43, 0x4F, 0x27, 0x67, 0x44, 0xB7, 0x0, 0x0, 0x0, 0xB5,
+ 0x66, 0x77, 0x64, 0x62, 0x54, 0x72, 0x61, 0x63, 0x45, 0x72, 0x72, 0x49,
+ 0x6E, 0x66, 0x6F, 0x0, 0x73, 0x65, 0x72, 0x76, 0x45, 0x78, 0x65, 0x63,
+ 0x75, 0x74, 0x65, 0x0, 0x0, 0x0, 0x0, 0x1B, 0x0, 0x0, 0xA8, 0xE,
+ 0x50, 0x1, 0xF, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x48,
+ 0x0, 0x0, 0x5, 0x89, 0x16, 0xA5, 0xF7, 0xAC, 0x0, 0x0, 0xB, 0x69,
+ 0x0, 0x30, 0x43, 0x4F, 0x27, 0x67, 0x44, 0xB7, 0x0, 0x0, 0x0, 0xB5,
+ 0x66, 0x77, 0x64, 0x62, 0x54, 0x72, 0x61, 0x63, 0x45, 0x72, 0x72, 0x49,
+ 0x6E, 0x66, 0x6F, 0x0, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x4E,
+ 0x6F, 0x46, 0x6D, 0x74, 0x53, 0x74, 0x72, 0x0, 0x0, 0x0, 0x0, 0x1B,
+ 0x0, 0x0, 0xA8, 0xE, 0x50, 0x1, 0xF, 0xA5, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x4C, 0x55, 0x44, 0x1, 0xEC, 0x1, 0xC, 0x31, 0x0,
+ 0x1, 0x28, 0x4, 0x42, 0x46, 0x57, 0x44, 0x42, 0x43, 0x4D, 0x44, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xE4,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xE4, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xD, 0x0, 0x0, 0x5, 0x89, 0x5, 0xF9, 0x4B, 0x5C,
+ 0x0, 0x0, 0x23, 0x72, 0x0, 0x0, 0x43, 0x4F, 0x32, 0x5F, 0xF6, 0x1,
+ 0x0, 0x0, 0x1, 0x59, 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x5, 0x89,
+ 0x6, 0x36, 0xBE, 0xE8, 0x0, 0x0, 0x23, 0x72, 0x0, 0x0, 0x43, 0x4F,
+ 0xC9, 0x49, 0xE2, 0x7E, 0x0, 0x0, 0x1, 0xD6, 0x0, 0x0, 0x0, 0x1C,
+ 0x0, 0x0, 0x5, 0x89, 0x6, 0xAE, 0x6C, 0x5C, 0x0, 0x0, 0x23, 0x72,
+ 0x0, 0x0, 0x43, 0x4F, 0x32, 0x5F, 0xF6, 0x1, 0x0, 0x0, 0x1, 0x59,
+ 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x5, 0x89, 0x6, 0xC3, 0x48, 0x7A,
+ 0x0, 0x0, 0x23, 0x72, 0x0, 0x0, 0x43, 0x4F, 0xC9, 0x49, 0xE2, 0x7E,
+ 0x0, 0x0, 0x1, 0xD6, 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x5, 0x89,
+ 0x7, 0x12, 0x50, 0xB6, 0x0, 0x0, 0x23, 0x72, 0x0, 0x0, 0x43, 0x4F,
+ 0x32, 0x5F, 0xF6, 0x1, 0x0, 0x0, 0x1, 0x59, 0x0, 0x0, 0x0, 0x1C,
+ 0x0, 0x0, 0x5, 0x89, 0x7, 0x2C, 0x43, 0xFE, 0x0, 0x0, 0x23, 0x72,
+ 0x0, 0x0, 0x43, 0x4F, 0xC9, 0x49, 0xE2, 0x7E, 0x0, 0x0, 0x1, 0xD6,
+ 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x5, 0x89, 0x7, 0x97, 0x92, 0x5F,
+ 0x0, 0x0, 0x23, 0x72, 0x0, 0x0, 0x43, 0x4F, 0x32, 0x5F, 0xF6, 0x1,
+ 0x0, 0x0, 0x1, 0x59, 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x5, 0x89,
+ 0x7, 0xAF, 0x7C, 0xC6, 0x0, 0x0, 0x23, 0x72, 0x0, 0x0, 0x43, 0x4F,
+ 0xC9, 0x49, 0xE2, 0x7E, 0x0, 0x0, 0x1, 0xD6, 0x0, 0x0, 0x0, 0x1C,
+ 0x0, 0x0, 0x5, 0x89, 0x7, 0xD2, 0x84, 0xF, 0x0, 0x0, 0x23, 0x72,
+ 0x0, 0x0, 0x43, 0x4F, 0x32, 0x5F, 0xF6, 0x1, 0x0, 0x0, 0x1, 0x59,
+ 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x5, 0x89, 0x8, 0x6E, 0xBC, 0x17,
+ 0x0, 0x0, 0x23, 0x72, 0x0, 0x0, 0x43, 0x4F, 0xC9, 0x49, 0xE2, 0x7E,
+ 0x0, 0x0, 0x1, 0xD6, 0x0, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x5, 0x89,
+ 0x13, 0xA8, 0x5C, 0x6F, 0x0, 0x0, 0xB, 0x69, 0x0, 0x0, 0x43, 0x4F,
+ 0x32, 0x5F, 0xF6, 0x1, 0x0, 0x0, 0x1, 0x59, 0x0, 0x0, 0x0, 0x1C,
+ 0x0, 0x0, 0x5, 0x89, 0x16, 0xA5, 0x8F, 0x96, 0x0, 0x0, 0xB, 0x69,
+ 0x0, 0x6C, 0x43, 0x4F, 0x9A, 0x1E, 0xAD, 0xA5, 0x0, 0x0, 0x1, 0x9D,
+ 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x4E, 0x6F, 0x46, 0x6D, 0x74,
+ 0x53, 0x74, 0x72, 0x0, 0x0, 0x0, 0x0, 0x3, 0x73, 0x65, 0x6C, 0x65,
+ 0x63, 0x74, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x5F, 0x65, 0x69, 0x64,
+ 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x63, 0x62, 0x6C, 0x76, 0x5F, 0x63,
+ 0x61, 0x62, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74,
+ 0x69, 0x6F, 0x6E, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5F, 0x70,
+ 0x75, 0x62, 0x6C, 0x69, 0x63, 0x5F, 0x76, 0x69, 0x65, 0x77, 0x20, 0x77,
+ 0x68, 0x65, 0x72, 0x65, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x5F, 0x65,
+ 0x69, 0x64, 0x20, 0x21, 0x3D, 0x20, 0x30, 0x0, 0x9D, 0x86, 0xE6, 0xD3,
+ 0x0, 0x0, 0x0, 0x88, 0x55, 0x44, 0x1, 0xD0, 0x1, 0xC, 0x31, 0x0,
+ 0x1, 0x28, 0x4, 0x42, 0x46, 0x57, 0x44, 0x42, 0x55, 0x54, 0x49, 0x4C,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xC8,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xC8, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x5, 0x89, 0x13, 0xA7, 0xC0, 0xB6,
+ 0x0, 0x0, 0xB, 0x69, 0x0, 0x3C, 0x43, 0x4F, 0x4C, 0x53, 0x3A, 0xE0,
+ 0x0, 0x0, 0x1, 0xF7, 0x45, 0x58, 0x49, 0x54, 0x0, 0x0, 0x0, 0x0,
+ 0x41, 0x54, 0x54, 0x41, 0x43, 0x48, 0x0, 0x0, 0x9D, 0x86, 0xE6, 0xD3,
+ 0x0, 0x0, 0xB, 0x60, 0x0, 0x0, 0xB, 0x69, 0x2F, 0x6F, 0x70, 0x74,
+ 0x2F, 0x66, 0x69, 0x70, 0x73, 0x2F, 0x62, 0x69, 0x6E, 0x2F, 0x68, 0x65,
+ 0x61, 0x6C, 0x74, 0x68, 0x6D, 0x6F, 0x6E, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x43, 0x8F, 0x0, 0x0, 0x0, 0x58, 0x0, 0x0, 0x5, 0x89,
+ 0x13, 0xA8, 0xF8, 0x3A, 0x0, 0x0, 0xB, 0x69, 0x0, 0x88, 0x43, 0x4F,
+ 0x4C, 0x53, 0x3A, 0xE0, 0x0, 0x0, 0x1, 0xF7, 0x45, 0x4E, 0x54, 0x52,
+ 0x0, 0x0, 0x0, 0x0, 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x65,
+ 0x72, 0x72, 0x6F, 0x72, 0x5F, 0x65, 0x69, 0x64, 0x20, 0x66, 0x72, 0x6F,
+ 0x6D, 0x20, 0x63, 0x62, 0x6C, 0x76, 0x5F, 0x63, 0x61, 0x62, 0x6C, 0x65,
+ 0x5F, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x5F,
+ 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5F, 0x70, 0x75, 0x62, 0x6C, 0x69,
+ 0x63, 0x5F, 0x76, 0x69, 0x65, 0x77, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65,
+ 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x5F, 0x65, 0x69, 0x64, 0x20, 0x21,
+ 0x3D, 0x20, 0x30, 0x0, 0x9D, 0x86, 0xE6, 0xD3, 0x0, 0x0, 0xB, 0x60,
+ 0x0, 0x0, 0xB, 0x69, 0x2F, 0x6F, 0x70, 0x74, 0x2F, 0x66, 0x69, 0x70,
+ 0x73, 0x2F, 0x62, 0x69, 0x6E, 0x2F, 0x68, 0x65, 0x61, 0x6C, 0x74, 0x68,
+ 0x6D, 0x6F, 0x6E, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xA4, 0x0, 0x0, 0x5, 0x89, 0x16, 0xA5, 0x62, 0xFB,
+ 0x0, 0x0, 0xB, 0x69, 0x0, 0x88, 0x43, 0x4F, 0x4C, 0x53, 0x3A, 0xE0,
+ 0x0, 0x0, 0x1, 0xF7, 0x45, 0x58, 0x49, 0x54, 0x0, 0x0, 0x0, 0x0,
+ 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72,
+ 0x5F, 0x65, 0x69, 0x64, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x63, 0x62,
+ 0x6C, 0x76, 0x5F, 0x63, 0x61, 0x62, 0x6C, 0x65, 0x5F, 0x63, 0x6F, 0x6E,
+ 0x6E, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x5F, 0x73, 0x74, 0x61, 0x74,
+ 0x75, 0x73, 0x5F, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x5F, 0x76, 0x69,
+ 0x65, 0x77, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x65, 0x72, 0x72,
+ 0x6F, 0x72, 0x5F, 0x65, 0x69, 0x64, 0x20, 0x21, 0x3D, 0x20, 0x30, 0x0,
+ 0x9D, 0x86, 0xE6, 0xD3, 0x0, 0x0, 0xB, 0x60, 0x0, 0x0, 0xB, 0x69,
+ 0x2F, 0x6F, 0x70, 0x74, 0x2F, 0x66, 0x69, 0x70, 0x73, 0x2F, 0x62, 0x69,
+ 0x6E, 0x2F, 0x68, 0x65, 0x61, 0x6C, 0x74, 0x68, 0x6D, 0x6F, 0x6E, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC5, 0x63, 0x0, 0x0, 0x0, 0xA4};
+
+TEST_F(PELTest, RealPELTest)
+{
+ auto origData = realPELData;
+ PEL pel{origData};
+
+ EXPECT_TRUE(pel.valid());
+
+ // Check that the flat data is correct
+ auto flat = pel.data();
+ EXPECT_EQ(realPELData, flat);
+ EXPECT_EQ(realPELData.size(), pel.size());
+
+ // Check that the code can extract an object for every section.
+ //(The PrivateHeader and UserHeader account for the + 2 below.)
+ const auto& sections = pel.optionalSections();
+ EXPECT_EQ(pel.privateHeader().sectionCount(), sections.size() + 2);
+
+ auto src = pel.primarySRC();
+ EXPECT_EQ(src.value()->asciiString(), "B181A80E ");
+
+ // Check that the last section (a 'UD' section) is indeed the last
+ // section object by checking the ID and the last byte.
+ auto& last = pel.optionalSections().back();
+ EXPECT_EQ(last->header().id, 0x5544); // "UD"
+
+ std::vector<uint8_t> lastSectionData;
+ Stream stream{lastSectionData};
+ last->flatten(stream);
+ EXPECT_EQ(lastSectionData.back(), 0xA4);
+}
diff --git a/test/openpower-pels/registry_test.cpp b/test/openpower-pels/registry_test.cpp
new file mode 100644
index 0000000..2944ce0
--- /dev/null
+++ b/test/openpower-pels/registry_test.cpp
@@ -0,0 +1,336 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/registry.hpp"
+
+#include <filesystem>
+#include <fstream>
+#include <nlohmann/json.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels::message;
+namespace fs = std::filesystem;
+
+const auto registryData = R"(
+{
+ "PELs":
+ [
+ {
+ "Name": "xyz.openbmc_project.Power.Fault",
+ "Subsystem": "power_supply",
+
+ "SRC":
+ {
+ "ReasonCode": "0x2030"
+ },
+
+ "Documentation":
+ {
+ "Description": "A PGOOD Fault",
+ "Message": "PS had a PGOOD Fault"
+ }
+ },
+
+ {
+ "Name": "xyz.openbmc_project.Power.OverVoltage",
+ "Subsystem": "power_control_hw",
+ "Severity": "unrecoverable",
+ "MfgSeverity": "non_error",
+ "ActionFlags": ["service_action", "report", "call_home"],
+ "MfgActionFlags": ["hidden"],
+
+ "SRC":
+ {
+ "ReasonCode": "0x2333",
+ "Type": "BD",
+ "SymptomIDFields": ["SRCWord5", "SRCWord6", "SRCWord7"],
+ "PowerFault": true,
+ "Words6To9":
+ {
+ "6":
+ {
+ "description": "Failing unit number",
+ "AdditionalDataPropSource": "PS_NUM"
+ },
+
+ "7":
+ {
+ "description": "bad voltage",
+ "AdditionalDataPropSource": "VOLTAGE"
+ }
+ }
+ },
+
+ "Documentation":
+ {
+ "Description": "A PGOOD Fault",
+ "Message": "PS %1 had a PGOOD Fault",
+ "MessageArgSources":
+ [
+ "SRCWord6"
+ ],
+ "Notes": [
+ "In the UserData section there is a JSON",
+ "dump that provides debug information."
+ ]
+ }
+ }
+ ]
+}
+)";
+
+class RegistryTest : public ::testing::Test
+{
+ protected:
+ static void SetUpTestCase()
+ {
+ char path[] = "/tmp/regtestXXXXXX";
+ regDir = mkdtemp(path);
+ }
+
+ static void TearDownTestCase()
+ {
+ fs::remove_all(regDir);
+ }
+
+ static std::string writeData(const char* data)
+ {
+ fs::path path = regDir / "registry.json";
+ std::ofstream stream{path};
+ stream << data;
+ return path;
+ }
+
+ static fs::path regDir;
+};
+
+fs::path RegistryTest::regDir{};
+
+TEST_F(RegistryTest, TestNoEntry)
+{
+ auto path = RegistryTest::writeData(registryData);
+ Registry registry{path};
+
+ auto entry = registry.lookup("foo", LookupType::name);
+ EXPECT_FALSE(entry);
+}
+
+TEST_F(RegistryTest, TestFindEntry)
+{
+ auto path = RegistryTest::writeData(registryData);
+ Registry registry{path};
+
+ auto entry = registry.lookup("xyz.openbmc_project.Power.OverVoltage",
+ LookupType::name);
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage");
+ EXPECT_EQ(entry->subsystem, 0x62);
+ EXPECT_EQ(*(entry->severity), 0x40);
+ EXPECT_EQ(*(entry->mfgSeverity), 0x00);
+ EXPECT_EQ(*(entry->actionFlags), 0xA800);
+ EXPECT_EQ(*(entry->mfgActionFlags), 0x4000);
+ EXPECT_EQ(entry->componentID, 0x2300);
+ EXPECT_FALSE(entry->eventType);
+ EXPECT_FALSE(entry->eventScope);
+
+ EXPECT_EQ(entry->src.type, 0xBD);
+ EXPECT_EQ(entry->src.reasonCode, 0x2333);
+ EXPECT_EQ(*(entry->src.powerFault), true);
+
+ auto& hexwords = entry->src.hexwordADFields;
+ EXPECT_TRUE(hexwords);
+ EXPECT_EQ((*hexwords).size(), 2);
+
+ auto word = (*hexwords).find(6);
+ EXPECT_NE(word, (*hexwords).end());
+ EXPECT_EQ(word->second, "PS_NUM");
+
+ word = (*hexwords).find(7);
+ EXPECT_NE(word, (*hexwords).end());
+ EXPECT_EQ(word->second, "VOLTAGE");
+
+ auto& sid = entry->src.symptomID;
+ EXPECT_TRUE(sid);
+ EXPECT_EQ((*sid).size(), 3);
+ EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 5), (*sid).end());
+ EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 6), (*sid).end());
+ EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 7), (*sid).end());
+
+ EXPECT_EQ(entry->doc.description, "A PGOOD Fault");
+ EXPECT_EQ(entry->doc.message, "PS %1 had a PGOOD Fault");
+ auto& hexwordSource = entry->doc.messageArgSources;
+ EXPECT_TRUE(hexwordSource);
+ EXPECT_EQ((*hexwordSource).size(), 1);
+ EXPECT_EQ((*hexwordSource).front(), "SRCWord6");
+
+ entry = registry.lookup("0x2333", LookupType::reasonCode);
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage");
+}
+
+// Check the entry that mostly uses defaults
+TEST_F(RegistryTest, TestFindEntryMinimal)
+{
+ auto path = RegistryTest::writeData(registryData);
+ Registry registry{path};
+
+ auto entry =
+ registry.lookup("xyz.openbmc_project.Power.Fault", LookupType::name);
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.Fault");
+ EXPECT_EQ(entry->subsystem, 0x61);
+ EXPECT_FALSE(entry->severity);
+ EXPECT_FALSE(entry->mfgSeverity);
+ EXPECT_FALSE(entry->mfgActionFlags);
+ EXPECT_FALSE(entry->actionFlags);
+ EXPECT_EQ(entry->componentID, 0x2000);
+ EXPECT_FALSE(entry->eventType);
+ EXPECT_FALSE(entry->eventScope);
+
+ EXPECT_EQ(entry->src.reasonCode, 0x2030);
+ EXPECT_EQ(entry->src.type, 0xBD);
+ EXPECT_FALSE(entry->src.powerFault);
+ EXPECT_FALSE(entry->src.hexwordADFields);
+ EXPECT_FALSE(entry->src.symptomID);
+}
+
+TEST_F(RegistryTest, TestBadJSON)
+{
+ auto path = RegistryTest::writeData("bad {} json");
+
+ Registry registry{path};
+
+ EXPECT_FALSE(registry.lookup("foo", LookupType::name));
+}
+
+// Test the helper functions the use the pel_values data.
+TEST_F(RegistryTest, TestHelperFunctions)
+{
+ using namespace openpower::pels::message::helper;
+ EXPECT_EQ(getSubsystem("input_power_source"), 0xA1);
+ EXPECT_THROW(getSubsystem("foo"), std::runtime_error);
+
+ EXPECT_EQ(getSeverity("symptom_recovered"), 0x71);
+ EXPECT_THROW(getSeverity("foo"), std::runtime_error);
+
+ EXPECT_EQ(getEventType("dump_notification"), 0x08);
+ EXPECT_THROW(getEventType("foo"), std::runtime_error);
+
+ EXPECT_EQ(getEventScope("possibly_multiple_platforms"), 0x04);
+ EXPECT_THROW(getEventScope("foo"), std::runtime_error);
+
+ std::vector<std::string> flags{"service_action", "dont_report",
+ "termination"};
+ EXPECT_EQ(getActionFlags(flags), 0x9100);
+
+ flags.clear();
+ flags.push_back("foo");
+ EXPECT_THROW(getActionFlags(flags), std::runtime_error);
+}
+
+TEST_F(RegistryTest, TestGetSRCReasonCode)
+{
+ using namespace openpower::pels::message::helper;
+ EXPECT_EQ(getSRCReasonCode(R"({"ReasonCode": "0x5555"})"_json, "foo"),
+ 0x5555);
+
+ EXPECT_THROW(getSRCReasonCode(R"({"ReasonCode": "ZZZZ"})"_json, "foo"),
+ std::runtime_error);
+}
+
+TEST_F(RegistryTest, TestGetSRCType)
+{
+ using namespace openpower::pels::message::helper;
+ EXPECT_EQ(getSRCType(R"({"Type": "11"})"_json, "foo"), 0x11);
+ EXPECT_EQ(getSRCType(R"({"Type": "BF"})"_json, "foo"), 0xBF);
+
+ EXPECT_THROW(getSRCType(R"({"Type": "1"})"_json, "foo"),
+ std::runtime_error);
+
+ EXPECT_THROW(getSRCType(R"({"Type": "111"})"_json, "foo"),
+ std::runtime_error);
+}
+
+TEST_F(RegistryTest, TestGetSRCHexwordFields)
+{
+ using namespace openpower::pels::message::helper;
+ const auto hexwords = R"(
+ {"Words6To9":
+ {
+ "8":
+ {
+ "AdditionalDataPropSource": "TEST"
+ }
+ }
+ })"_json;
+
+ auto fields = getSRCHexwordFields(hexwords, "foo");
+ EXPECT_TRUE(fields);
+ auto word = fields->find(8);
+ EXPECT_NE(word, fields->end());
+
+ const auto theInvalidRWord = R"(
+ {"Words6To9":
+ {
+ "R":
+ {
+ "AdditionalDataPropSource": "TEST"
+ }
+ }
+ })"_json;
+
+ EXPECT_THROW(getSRCHexwordFields(theInvalidRWord, "foo"),
+ std::runtime_error);
+}
+
+TEST_F(RegistryTest, TestGetSRCSymptomIDFields)
+{
+ using namespace openpower::pels::message::helper;
+ const auto sID = R"(
+ {
+ "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord5"]
+ })"_json;
+
+ auto fields = getSRCSymptomIDFields(sID, "foo");
+ EXPECT_NE(std::find(fields->begin(), fields->end(), 3), fields->end());
+ EXPECT_NE(std::find(fields->begin(), fields->end(), 4), fields->end());
+ EXPECT_NE(std::find(fields->begin(), fields->end(), 5), fields->end());
+
+ const auto badField = R"(
+ {
+ "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord"]
+ })"_json;
+
+ EXPECT_THROW(getSRCSymptomIDFields(badField, "foo"), std::runtime_error);
+}
+
+TEST_F(RegistryTest, TestGetComponentID)
+{
+ using namespace openpower::pels::message::helper;
+
+ // Get it from the JSON
+ auto id =
+ getComponentID(0xBD, 0x4200, R"({"ComponentID":"0x4200"})"_json, "foo");
+ EXPECT_EQ(id, 0x4200);
+
+ // Get it from the reason code on a 0xBD SRC
+ id = getComponentID(0xBD, 0x6700, R"({})"_json, "foo");
+ EXPECT_EQ(id, 0x6700);
+
+ // Not present on a 0x11 SRC
+ EXPECT_THROW(getComponentID(0x11, 0x8800, R"({})"_json, "foo"),
+ std::runtime_error);
+}
diff --git a/test/openpower-pels/repository_test.cpp b/test/openpower-pels/repository_test.cpp
new file mode 100644
index 0000000..446e10e
--- /dev/null
+++ b/test/openpower-pels/repository_test.cpp
@@ -0,0 +1,436 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/paths.hpp"
+#include "extensions/openpower-pels/repository.hpp"
+#include "pel_utils.hpp"
+
+#include <ext/stdio_filebuf.h>
+
+#include <filesystem>
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+namespace fs = std::filesystem;
+
+/**
+ * Clean the Repo after every testcase.
+ * And because we have PEL object, also clean up
+ * the log ID.
+ */
+class RepositoryTest : public CleanLogID
+{
+ protected:
+ void SetUp() override
+ {
+ repoPath = getPELRepoPath();
+ }
+
+ void TearDown() override
+ {
+ fs::remove_all(repoPath);
+ }
+
+ fs::path repoPath;
+};
+
+TEST_F(RepositoryTest, FilenameTest)
+{
+ BCDTime date = {0x20, 0x30, 0x11, 0x28, 0x13, 0x6, 0x7, 0x8};
+
+ EXPECT_EQ(Repository::getPELFilename(0x12345678, date),
+ "2030112813060708_12345678");
+
+ EXPECT_EQ(Repository::getPELFilename(0xAABBCCDD, date),
+ "2030112813060708_AABBCCDD");
+
+ EXPECT_EQ(Repository::getPELFilename(0x3AFF1, date),
+ "2030112813060708_0003AFF1");
+
+ EXPECT_EQ(Repository::getPELFilename(100, date),
+ "2030112813060708_00000064");
+
+ EXPECT_EQ(Repository::getPELFilename(0, date), "2030112813060708_00000000");
+}
+
+TEST_F(RepositoryTest, AddTest)
+{
+ Repository repo{repoPath};
+ auto data = pelDataFactory(TestPELType::pelSimple);
+ auto pel = std::make_unique<PEL>(data);
+
+ repo.add(pel);
+
+ // Check that the PEL was stored where it was supposed to be,
+ // and that it wrote the PEL data.
+ const auto ts = pel->privateHeader().commitTimestamp();
+ auto name = Repository::getPELFilename(pel->id(), ts);
+
+ fs::path file = repoPath / "logs" / name;
+ EXPECT_TRUE(fs::exists(file));
+
+ auto newData = readPELFile(file);
+ auto pelData = pel->data();
+ EXPECT_EQ(*newData, pelData);
+}
+
+TEST_F(RepositoryTest, RestoreTest)
+{
+ using pelID = Repository::LogID::Pel;
+ using obmcID = Repository::LogID::Obmc;
+
+ std::vector<Repository::LogID> ids;
+
+ {
+ Repository repo{repoPath};
+
+ // Add some PELs to the repository
+ {
+ auto data = pelDataFactory(TestPELType::pelSimple);
+ auto pel = std::make_unique<PEL>(data, 1);
+ pel->assignID();
+ repo.add(pel);
+ ids.emplace_back(pelID(pel->id()), obmcID(1));
+ }
+ {
+ auto data = pelDataFactory(TestPELType::pelSimple);
+ auto pel = std::make_unique<PEL>(data, 2);
+ pel->assignID();
+ repo.add(pel);
+ ids.emplace_back(pelID(pel->id()), obmcID(2));
+ }
+
+ // Check they're there
+ EXPECT_TRUE(repo.hasPEL(ids[0]));
+ EXPECT_TRUE(repo.hasPEL(ids[1]));
+
+ // Do some other search tests while we're here.
+
+ // Search based on PEL ID
+ Repository::LogID id(pelID(ids[0].pelID));
+ EXPECT_TRUE(repo.hasPEL(id));
+
+ // Search based on OBMC log ID
+ id.pelID.id = 0;
+ id.obmcID = ids[0].obmcID;
+ EXPECT_TRUE(repo.hasPEL(id));
+
+ // ... based on the other PEL ID
+ id.pelID = ids[1].pelID;
+ id.obmcID.id = 0;
+ EXPECT_TRUE(repo.hasPEL(id));
+
+ // Not found
+ id.pelID.id = 99;
+ id.obmcID.id = 100;
+ EXPECT_FALSE(repo.hasPEL(id));
+ }
+
+ {
+ // Restore and check they're still there, then
+ // remove them.
+ Repository repo{repoPath};
+ EXPECT_TRUE(repo.hasPEL(ids[0]));
+ EXPECT_TRUE(repo.hasPEL(ids[1]));
+
+ repo.remove(ids[0]);
+ EXPECT_FALSE(repo.hasPEL(ids[0]));
+
+ repo.remove(ids[1]);
+ EXPECT_FALSE(repo.hasPEL(ids[1]));
+ }
+}
+
+TEST_F(RepositoryTest, TestGetPELData)
+{
+ using ID = Repository::LogID;
+ Repository repo{repoPath};
+
+ ID badID{ID::Pel(42)};
+ auto noData = repo.getPELData(badID);
+ EXPECT_FALSE(noData);
+
+ // Add a PEL to the repo, and get the data back with getPELData.
+ auto data = pelDataFactory(TestPELType::pelSimple);
+ auto dataCopy = data;
+ auto pel = std::make_unique<PEL>(data);
+ auto pelID = pel->id();
+ repo.add(pel);
+
+ ID id{ID::Pel(pelID)};
+ auto pelData = repo.getPELData(id);
+
+ ASSERT_TRUE(pelData);
+ EXPECT_EQ(dataCopy, *pelData);
+}
+
+TEST_F(RepositoryTest, TestForEach)
+{
+ Repository repo{repoPath};
+
+ // Add 2 PELs
+ auto data = pelDataFactory(TestPELType::pelSimple);
+ auto pel = std::make_unique<PEL>(data);
+ repo.add(pel);
+
+ pel = std::make_unique<PEL>(data);
+ pel->assignID();
+ pel->setCommitTime();
+ repo.add(pel);
+
+ // Make a function that saves the IDs
+ std::vector<uint32_t> ids;
+ Repository::ForEachFunc f1 = [&ids](const PEL& pel) {
+ ids.push_back(pel.id());
+ return false;
+ };
+
+ repo.for_each(f1);
+
+ EXPECT_EQ(ids.size(), 2);
+
+ // Stop after the first time in.
+ Repository::ForEachFunc f2 = [&ids](const PEL& pel) {
+ ids.push_back(pel.id());
+ return true;
+ };
+
+ ids.clear();
+ repo.for_each(f2);
+ EXPECT_EQ(ids.size(), 1);
+}
+
+TEST_F(RepositoryTest, TestSubscriptions)
+{
+ std::vector<uint32_t> added;
+ std::vector<uint32_t> removed;
+
+ Repository::AddCallback ac = [&added](const PEL& pel) {
+ added.push_back(pel.id());
+ };
+
+ Repository::DeleteCallback dc = [&removed](uint32_t id) {
+ removed.push_back(id);
+ };
+
+ Repository repo{repoPath};
+ repo.subscribeToAdds("test", ac);
+ repo.subscribeToDeletes("test", dc);
+
+ auto data = pelDataFactory(TestPELType::pelSimple);
+ auto pel = std::make_unique<PEL>(data);
+ auto pelID = pel->id();
+ repo.add(pel);
+
+ EXPECT_EQ(added.size(), 1);
+
+ using ID = Repository::LogID;
+ ID id{ID::Pel(pelID)};
+ repo.remove(id);
+
+ EXPECT_EQ(removed.size(), 1);
+
+ repo.unsubscribeFromAdds("test");
+ repo.unsubscribeFromDeletes("test");
+
+ added.clear();
+ removed.clear();
+
+ repo.add(pel);
+ EXPECT_EQ(added.size(), 0);
+
+ repo.remove(id);
+ EXPECT_EQ(removed.size(), 0);
+}
+
+TEST_F(RepositoryTest, TestGetAttributes)
+{
+ uint32_t pelID = 0;
+ std::bitset<16> actionFlags;
+
+ {
+ Repository repo{repoPath};
+
+ // Add a PEL to the repo
+ auto data = pelDataFactory(TestPELType::pelSimple);
+ auto pel = std::make_unique<PEL>(data);
+ repo.add(pel);
+
+ pelID = pel->id();
+ actionFlags = pel->userHeader().actionFlags();
+
+ using ID = Repository::LogID;
+ ID id{ID::Pel(pelID)};
+
+ auto a = repo.getPELAttributes(id);
+ EXPECT_TRUE(a);
+ EXPECT_EQ((*a).get().actionFlags, actionFlags);
+
+ id.pelID.id = 0;
+ a = repo.getPELAttributes(id);
+ EXPECT_FALSE(a);
+ }
+
+ {
+ // Restore the repository and check again
+ Repository repo{repoPath};
+
+ using ID = Repository::LogID;
+ ID id{ID::Pel(pelID)};
+
+ auto a = repo.getPELAttributes(id);
+ EXPECT_TRUE(a);
+ EXPECT_EQ((*a).get().actionFlags, actionFlags);
+
+ id.pelID.id = 0;
+ a = repo.getPELAttributes(id);
+ EXPECT_FALSE(a);
+ }
+}
+
+TEST_F(RepositoryTest, TestSetHostState)
+{
+ // Add a PEL to the repo
+ auto data = pelDataFactory(TestPELType::pelSimple);
+ auto pel = std::make_unique<PEL>(data);
+ using ID = Repository::LogID;
+ ID id{ID::Pel(pel->id())};
+
+ {
+ Repository repo{repoPath};
+
+ repo.add(pel);
+
+ auto a = repo.getPELAttributes(id);
+ EXPECT_EQ((*a).get().hostState, TransmissionState::newPEL);
+
+ repo.setPELHostTransState(pel->id(), TransmissionState::acked);
+
+ // First, check the attributes
+ a = repo.getPELAttributes(id);
+ EXPECT_EQ((*a).get().hostState, TransmissionState::acked);
+
+ // Next, check the PEL data itself
+ auto pelData = repo.getPELData(id);
+ PEL newPEL{*pelData};
+ EXPECT_EQ(newPEL.hostTransmissionState(), TransmissionState::acked);
+ }
+
+ {
+ // Now restore, and check again
+ Repository repo{repoPath};
+
+ // First, check the attributes
+ auto a = repo.getPELAttributes(id);
+ EXPECT_EQ((*a).get().hostState, TransmissionState::acked);
+
+ // Next, check the PEL data itself
+ auto pelData = repo.getPELData(id);
+ PEL newPEL{*pelData};
+ EXPECT_EQ(newPEL.hostTransmissionState(), TransmissionState::acked);
+ }
+}
+
+TEST_F(RepositoryTest, TestSetHMCState)
+{
+ // Add a PEL to the repo
+ auto data = pelDataFactory(TestPELType::pelSimple);
+ auto pel = std::make_unique<PEL>(data);
+ using ID = Repository::LogID;
+ ID id{ID::Pel(pel->id())};
+
+ {
+ Repository repo{repoPath};
+
+ repo.add(pel);
+
+ auto a = repo.getPELAttributes(id);
+ EXPECT_EQ((*a).get().hmcState, TransmissionState::newPEL);
+
+ repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
+
+ // First, check the attributes
+ a = repo.getPELAttributes(id);
+ EXPECT_EQ((*a).get().hmcState, TransmissionState::acked);
+
+ // Next, check the PEL data itself
+ auto pelData = repo.getPELData(id);
+ PEL newPEL{*pelData};
+ EXPECT_EQ(newPEL.hmcTransmissionState(), TransmissionState::acked);
+ }
+
+ {
+ // Now restore, and check again
+ Repository repo{repoPath};
+
+ // First, check the attributes
+ auto a = repo.getPELAttributes(id);
+ EXPECT_EQ((*a).get().hmcState, TransmissionState::acked);
+
+ // Next, check the PEL data itself
+ auto pelData = repo.getPELData(id);
+ PEL newPEL{*pelData};
+ EXPECT_EQ(newPEL.hmcTransmissionState(), TransmissionState::acked);
+ }
+}
+
+TEST_F(RepositoryTest, TestGetPELFD)
+{
+ Repository repo{repoPath};
+
+ auto data = pelDataFactory(TestPELType::pelSimple);
+ auto pel = std::make_unique<PEL>(data);
+ pel->setCommitTime();
+ pel->assignID();
+
+ repo.add(pel);
+
+ using ID = Repository::LogID;
+ ID id{ID::Pel(pel->id())};
+
+ auto fd = repo.getPELFD(id);
+
+ EXPECT_TRUE(fd);
+
+ // Get the size
+ struct stat s;
+ int r = fstat(*fd, &s);
+ ASSERT_EQ(r, 0);
+
+ auto size = s.st_size;
+
+ // Read the PEL data out of the FD
+ FILE* fp = fdopen(*fd, "r");
+ ASSERT_NE(fp, nullptr);
+
+ std::vector<uint8_t> newData;
+ newData.resize(size);
+ r = fread(newData.data(), 1, size, fp);
+ EXPECT_EQ(r, size);
+
+ PEL newPEL{newData};
+
+ EXPECT_TRUE(newPEL.valid());
+ EXPECT_EQ(newPEL.id(), pel->id());
+
+ fclose(fp);
+
+ // Call getPELFD again, this time with a bad ID
+ id.pelID.id = 42;
+ fd = repo.getPELFD(id);
+
+ EXPECT_FALSE(fd);
+}
diff --git a/test/openpower-pels/section_header_test.cpp b/test/openpower-pels/section_header_test.cpp
new file mode 100644
index 0000000..b8ff732
--- /dev/null
+++ b/test/openpower-pels/section_header_test.cpp
@@ -0,0 +1,55 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/section_header.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+
+TEST(SectionHeaderTest, SizeTest)
+{
+ EXPECT_EQ(SectionHeader::flattenedSize(), 8);
+}
+
+TEST(SectionHeaderTest, UnflattenTest)
+{
+ std::vector<uint8_t> data{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
+ Stream reader{data};
+ SectionHeader header;
+
+ reader >> header;
+
+ EXPECT_EQ(header.id, 0x1122);
+ EXPECT_EQ(header.size, 0x3344);
+ EXPECT_EQ(header.version, 0x55);
+ EXPECT_EQ(header.subType, 0x66);
+ EXPECT_EQ(header.componentID, 0x7788);
+}
+
+TEST(SectionHeaderTest, FlattenTest)
+{
+ SectionHeader header{0xAABB, 0xCCDD, 0xEE, 0xFF, 0xA0A0};
+
+ std::vector<uint8_t> data;
+ Stream writer{data};
+
+ writer << header;
+
+ std::vector<uint8_t> expected{0xAA, 0xBB, 0xCC, 0xDD,
+ 0xEE, 0xFF, 0xA0, 0xA0};
+ EXPECT_EQ(data, expected);
+}
diff --git a/test/openpower-pels/severity_test.cpp b/test/openpower-pels/severity_test.cpp
new file mode 100644
index 0000000..29c3bc3
--- /dev/null
+++ b/test/openpower-pels/severity_test.cpp
@@ -0,0 +1,33 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/severity.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+using LogSeverity = phosphor::logging::Entry::Level;
+
+TEST(SeverityTest, SeverityMapTest)
+{
+ ASSERT_EQ(convertOBMCSeverityToPEL(LogSeverity::Informational), 0x00);
+ ASSERT_EQ(convertOBMCSeverityToPEL(LogSeverity::Notice), 0x00);
+ ASSERT_EQ(convertOBMCSeverityToPEL(LogSeverity::Debug), 0x00);
+ ASSERT_EQ(convertOBMCSeverityToPEL(LogSeverity::Warning), 0x20);
+ ASSERT_EQ(convertOBMCSeverityToPEL(LogSeverity::Critical), 0x50);
+ ASSERT_EQ(convertOBMCSeverityToPEL(LogSeverity::Emergency), 0x40);
+ ASSERT_EQ(convertOBMCSeverityToPEL(LogSeverity::Alert), 0x40);
+ ASSERT_EQ(convertOBMCSeverityToPEL(LogSeverity::Error), 0x40);
+}
diff --git a/test/openpower-pels/src_callout_test.cpp b/test/openpower-pels/src_callout_test.cpp
new file mode 100644
index 0000000..3fd2a5c
--- /dev/null
+++ b/test/openpower-pels/src_callout_test.cpp
@@ -0,0 +1,161 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/callout.hpp"
+#include "pel_utils.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+using namespace openpower::pels::src;
+
+// Unflatten the callout section with all three substructures
+TEST(CalloutTest, TestUnflattenAllSubstructures)
+{
+ // The base data.
+ std::vector<uint8_t> data{
+ 0xFF, 0x2F, 'H', 8, // size, flags, priority, LC length
+ 'U', '1', '2', '-', 'P', '1', 0x00, 0x00 // LC
+ };
+
+ auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure);
+ auto pceIdentity = srcDataFactory(TestSRCType::pceIdentityStructure);
+ auto mrus = srcDataFactory(TestSRCType::mruStructure);
+
+ // Add all 3 substructures
+ data.insert(data.end(), fruIdentity.begin(), fruIdentity.end());
+ data.insert(data.end(), pceIdentity.begin(), pceIdentity.end());
+ data.insert(data.end(), mrus.begin(), mrus.end());
+
+ // The final size
+ data[0] = data.size();
+
+ Stream stream{data};
+ Callout callout{stream};
+
+ EXPECT_EQ(callout.flattenedSize(), data.size());
+ EXPECT_EQ(callout.priority(), 'H');
+ EXPECT_EQ(callout.locationCode(), "U12-P1");
+
+ // Spot check the 3 substructures
+ EXPECT_TRUE(callout.fruIdentity());
+ EXPECT_EQ(callout.fruIdentity()->getSN(), "123456789ABC");
+
+ EXPECT_TRUE(callout.pceIdentity());
+ EXPECT_EQ(callout.pceIdentity()->enclosureName(), "PCENAME12");
+
+ EXPECT_TRUE(callout.mru());
+ EXPECT_EQ(callout.mru()->mrus().size(), 4);
+ EXPECT_EQ(callout.mru()->mrus().at(3).id, 0x04040404);
+
+ // Now flatten
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+
+ callout.flatten(newStream);
+ EXPECT_EQ(data, newData);
+}
+
+TEST(CalloutTest, TestUnflattenOneSubstructure)
+{
+ std::vector<uint8_t> data{
+ 0xFF, 0x28, 'H', 0x08, // size, flags, priority, LC length
+ 'U', '1', '2', '-', 'P', '1', 0x00, 0x00 // LC
+ };
+
+ auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure);
+
+ data.insert(data.end(), fruIdentity.begin(), fruIdentity.end());
+
+ // The final size
+ data[0] = data.size();
+
+ Stream stream{data};
+ Callout callout{stream};
+
+ EXPECT_EQ(callout.flattenedSize(), data.size());
+
+ // Spot check the substructure
+ EXPECT_TRUE(callout.fruIdentity());
+ EXPECT_EQ(callout.fruIdentity()->getSN(), "123456789ABC");
+
+ // Not present
+ EXPECT_FALSE(callout.pceIdentity());
+ EXPECT_FALSE(callout.mru());
+
+ // Now flatten
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+
+ callout.flatten(newStream);
+ EXPECT_EQ(data, newData);
+}
+
+TEST(CalloutTest, TestUnflattenTwoSubstructures)
+{
+ std::vector<uint8_t> data{
+ 0xFF, 0x2B, 'H', 0x08, // size, flags, priority, LC length
+ 'U', '1', '2', '-', 'P', '1', 0x00, 0x00 // LC
+ };
+
+ auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure);
+ auto pceIdentity = srcDataFactory(TestSRCType::pceIdentityStructure);
+
+ data.insert(data.end(), fruIdentity.begin(), fruIdentity.end());
+ data.insert(data.end(), pceIdentity.begin(), pceIdentity.end());
+
+ // The final size
+ data[0] = data.size();
+
+ Stream stream{data};
+ Callout callout{stream};
+
+ EXPECT_EQ(callout.flattenedSize(), data.size());
+
+ // Spot check the 2 substructures
+ EXPECT_TRUE(callout.fruIdentity());
+ EXPECT_EQ(callout.fruIdentity()->getSN(), "123456789ABC");
+
+ EXPECT_TRUE(callout.pceIdentity());
+ EXPECT_EQ(callout.pceIdentity()->enclosureName(), "PCENAME12");
+
+ // Not present
+ EXPECT_FALSE(callout.mru());
+
+ // Now flatten
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+
+ callout.flatten(newStream);
+ EXPECT_EQ(data, newData);
+}
+
+TEST(CalloutTest, TestNoLocationCode)
+{
+ std::vector<uint8_t> data{
+ 0xFF, 0x2B, 'H', 0x00 // size, flags, priority, LC length
+ };
+
+ auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure);
+ data.insert(data.end(), fruIdentity.begin(), fruIdentity.end());
+
+ // The final size
+ data[0] = data.size();
+
+ Stream stream{data};
+ Callout callout{stream};
+
+ EXPECT_TRUE(callout.locationCode().empty());
+}
diff --git a/test/openpower-pels/src_callouts_test.cpp b/test/openpower-pels/src_callouts_test.cpp
new file mode 100644
index 0000000..f192c15
--- /dev/null
+++ b/test/openpower-pels/src_callouts_test.cpp
@@ -0,0 +1,91 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/callouts.hpp"
+#include "pel_utils.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+using namespace openpower::pels::src;
+
+TEST(CalloutsTest, UnflattenFlattenTest)
+{
+ std::vector<uint8_t> data{0xC0, 0x00, 0x00,
+ 0x00}; // ID, flags, length in words
+
+ // Add 2 callouts
+ auto callout = srcDataFactory(TestSRCType::calloutStructureA);
+ data.insert(data.end(), callout.begin(), callout.end());
+
+ callout = srcDataFactory(TestSRCType::calloutStructureB);
+ data.insert(data.end(), callout.begin(), callout.end());
+
+ Stream stream{data};
+
+ // Set the actual word length value at offset 2
+ uint16_t wordLength = data.size() / 4;
+ stream.offset(2);
+ stream << wordLength;
+ stream.offset(0);
+
+ Callouts callouts{stream};
+
+ EXPECT_EQ(callouts.flattenedSize(), data.size());
+ EXPECT_EQ(callouts.callouts().size(), 2);
+
+ // spot check that each callout has the right substructures
+ EXPECT_TRUE(callouts.callouts().front()->fruIdentity());
+ EXPECT_FALSE(callouts.callouts().front()->pceIdentity());
+ EXPECT_FALSE(callouts.callouts().front()->mru());
+
+ EXPECT_TRUE(callouts.callouts().back()->fruIdentity());
+ EXPECT_TRUE(callouts.callouts().back()->pceIdentity());
+ EXPECT_TRUE(callouts.callouts().back()->mru());
+
+ // Flatten
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+
+ callouts.flatten(newStream);
+ EXPECT_EQ(data, newData);
+}
+
+TEST(CalloutsTest, BadDataTest)
+{
+ // Start out with a valid 2 callout object, then truncate it.
+ std::vector<uint8_t> data{0xC0, 0x00, 0x00,
+ 0x00}; // ID, flags, length in words
+
+ // Add 2 callouts
+ auto callout = srcDataFactory(TestSRCType::calloutStructureA);
+ data.insert(data.end(), callout.begin(), callout.end());
+
+ callout = srcDataFactory(TestSRCType::calloutStructureB);
+ data.insert(data.end(), callout.begin(), callout.end());
+
+ Stream stream{data};
+
+ // Set the actual word length value at offset 2
+ uint16_t wordLength = data.size() / 4;
+ stream.offset(2);
+ stream << wordLength;
+ stream.offset(0);
+
+ // Shorten the data by an arbitrary amount so unflattening goes awry.
+ data.resize(data.size() - 37);
+
+ EXPECT_THROW(Callouts callouts{stream}, std::out_of_range);
+}
diff --git a/test/openpower-pels/src_test.cpp b/test/openpower-pels/src_test.cpp
new file mode 100644
index 0000000..b966344
--- /dev/null
+++ b/test/openpower-pels/src_test.cpp
@@ -0,0 +1,261 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/src.hpp"
+#include "pel_utils.hpp"
+
+#include <fstream>
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+namespace fs = std::filesystem;
+
+const auto testRegistry = R"(
+{
+"PELs":
+[
+ {
+ "Name": "xyz.openbmc_project.Error.Test",
+ "Subsystem": "bmc_firmware",
+ "SRC":
+ {
+ "ReasonCode": "0xABCD",
+ "Words6To9":
+ {
+ "6":
+ {
+ "Description": "Component ID",
+ "AdditionalDataPropSource": "COMPID"
+ },
+ "7":
+ {
+ "Description": "Failure count",
+ "AdditionalDataPropSource": "FREQUENCY"
+ },
+ "8":
+ {
+ "Description": "Time period",
+ "AdditionalDataPropSource": "DURATION"
+ },
+ "9":
+ {
+ "Description": "Error code",
+ "AdditionalDataPropSource": "ERRORCODE"
+ }
+ }
+ },
+ "Documentation":
+ {
+ "Description": "A Component Fault",
+ "Message": "Comp %1 failed %2 times over %3 secs with ErrorCode %4",
+ "MessageArgSources":
+ [
+ "SRCWord6", "SRCWord7", "SRCWord8", "SRCWord9"
+ ]
+ }
+ }
+]
+}
+)";
+
+class SRCTest : public ::testing::Test
+{
+ protected:
+ static void SetUpTestCase()
+ {
+ char path[] = "/tmp/srctestXXXXXX";
+ regDir = mkdtemp(path);
+ }
+
+ static void TearDownTestCase()
+ {
+ fs::remove_all(regDir);
+ }
+
+ static std::string writeData(const char* data)
+ {
+ fs::path path = regDir / "registry.json";
+ std::ofstream stream{path};
+ stream << data;
+ return path;
+ }
+
+ static fs::path regDir;
+};
+
+fs::path SRCTest::regDir{};
+
+TEST_F(SRCTest, UnflattenFlattenTestNoCallouts)
+{
+ auto data = pelDataFactory(TestPELType::primarySRCSection);
+
+ Stream stream{data};
+ SRC src{stream};
+
+ EXPECT_TRUE(src.valid());
+
+ EXPECT_EQ(src.header().id, 0x5053);
+ EXPECT_EQ(src.header().size, 0x50);
+ EXPECT_EQ(src.header().version, 0x01);
+ EXPECT_EQ(src.header().subType, 0x01);
+ EXPECT_EQ(src.header().componentID, 0x0202);
+
+ EXPECT_EQ(src.version(), 0x02);
+ EXPECT_EQ(src.flags(), 0x00);
+ EXPECT_EQ(src.hexWordCount(), 9);
+ EXPECT_EQ(src.size(), 0x48);
+
+ const auto& hexwords = src.hexwordData();
+ EXPECT_EQ(0x02020255, hexwords[0]);
+ EXPECT_EQ(0x03030310, hexwords[1]);
+ EXPECT_EQ(0x04040404, hexwords[2]);
+ EXPECT_EQ(0x05050505, hexwords[3]);
+ EXPECT_EQ(0x06060606, hexwords[4]);
+ EXPECT_EQ(0x07070707, hexwords[5]);
+ EXPECT_EQ(0x08080808, hexwords[6]);
+ EXPECT_EQ(0x09090909, hexwords[7]);
+
+ EXPECT_EQ(src.asciiString(), "BD8D5678 ");
+ EXPECT_FALSE(src.callouts());
+
+ // Flatten
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+
+ src.flatten(newStream);
+ EXPECT_EQ(data, newData);
+}
+
+TEST_F(SRCTest, UnflattenFlattenTest2Callouts)
+{
+ auto data = pelDataFactory(TestPELType::primarySRCSection2Callouts);
+
+ Stream stream{data};
+ SRC src{stream};
+
+ EXPECT_TRUE(src.valid());
+ EXPECT_EQ(src.flags(), 0x01); // Additional sections within the SRC.
+
+ // Spot check the SRC fields, but they're the same as above
+ EXPECT_EQ(src.asciiString(), "BD8D5678 ");
+
+ // There should be 2 callouts
+ const auto& calloutsSection = src.callouts();
+ ASSERT_TRUE(calloutsSection);
+ const auto& callouts = calloutsSection->callouts();
+ EXPECT_EQ(callouts.size(), 2);
+
+ // spot check that each callout has the right substructures
+ EXPECT_TRUE(callouts.front()->fruIdentity());
+ EXPECT_FALSE(callouts.front()->pceIdentity());
+ EXPECT_FALSE(callouts.front()->mru());
+
+ EXPECT_TRUE(callouts.back()->fruIdentity());
+ EXPECT_TRUE(callouts.back()->pceIdentity());
+ EXPECT_TRUE(callouts.back()->mru());
+
+ // Flatten
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+
+ src.flatten(newStream);
+ EXPECT_EQ(data, newData);
+}
+
+// Create an SRC from the message registry
+TEST_F(SRCTest, CreateTestNoCallouts)
+{
+ message::Entry entry;
+ entry.src.type = 0xBD;
+ entry.src.reasonCode = 0xABCD;
+ entry.subsystem = 0x42;
+ entry.src.powerFault = true;
+ entry.src.hexwordADFields = {{5, "TEST1"}, // Not a user defined word
+ {6, "TEST1"},
+ {7, "TEST2"},
+ {8, "TEST3"},
+ {9, "TEST4"}};
+
+ // Values for the SRC words pointed to above
+ std::vector<std::string> adData{"TEST1=0x12345678", "TEST2=12345678",
+ "TEST3=0XDEF", "TEST4=Z"};
+ AdditionalData ad{adData};
+ SRC src{entry, ad};
+
+ EXPECT_TRUE(src.valid());
+ EXPECT_TRUE(src.isPowerFaultEvent());
+ EXPECT_EQ(src.size(), baseSRCSize);
+
+ const auto& hexwords = src.hexwordData();
+
+ // The spec always refers to SRC words 2 - 9, and as the hexwordData()
+ // array index starts at 0 use the math in the [] below to make it easier
+ // to tell what is being accessed.
+ EXPECT_EQ(hexwords[2 - 2] & 0xF0000000, 0); // Partition dump status
+ EXPECT_EQ(hexwords[2 - 2] & 0x00F00000, 0); // Partition boot type
+ EXPECT_EQ(hexwords[2 - 2] & 0x000000FF, 0x55); // SRC format
+ EXPECT_EQ(hexwords[3 - 2] & 0x000000FF, 0x10); // BMC position
+
+ // Validate more fields here as the code starts filling them in.
+
+ // Ensure hex word 5 wasn't allowed to be set to TEST1's contents
+ EXPECT_EQ(hexwords[5 - 2], 0);
+
+ // The user defined hex word fields specifed in the additional data.
+ EXPECT_EQ(hexwords[6 - 2], 0x12345678); // TEST1
+ EXPECT_EQ(hexwords[7 - 2], 12345678); // TEST2
+ EXPECT_EQ(hexwords[8 - 2], 0xdef); // TEST3
+ EXPECT_EQ(hexwords[9 - 2], 0); // TEST4, but can't convert a 'Z'
+
+ EXPECT_EQ(src.asciiString(), "BD42ABCD ");
+
+ // No callouts
+ EXPECT_FALSE(src.callouts());
+
+ // May as well spot check the flatten/unflatten
+ std::vector<uint8_t> data;
+ Stream stream{data};
+ src.flatten(stream);
+
+ stream.offset(0);
+ SRC newSRC{stream};
+
+ EXPECT_TRUE(newSRC.valid());
+ EXPECT_EQ(newSRC.isPowerFaultEvent(), src.isPowerFaultEvent());
+ EXPECT_EQ(newSRC.asciiString(), src.asciiString());
+ EXPECT_FALSE(newSRC.callouts());
+}
+
+// Test the getErrorDetails function
+TEST_F(SRCTest, MessageSubstitutionTest)
+{
+ auto path = SRCTest::writeData(testRegistry);
+ message::Registry registry{path};
+ auto entry = registry.lookup("0xABCD", message::LookupType::reasonCode);
+
+ std::vector<std::string> adData{"COMPID=0x1", "FREQUENCY=0x4",
+ "DURATION=30", "ERRORCODE=0x01ABCDEF"};
+ AdditionalData ad{adData};
+
+ SRC src{*entry, ad};
+ EXPECT_TRUE(src.valid());
+
+ auto errorDetails = src.getErrorDetails(registry, DetailLevel::message);
+ ASSERT_TRUE(errorDetails);
+ EXPECT_EQ(
+ errorDetails.value(),
+ "Comp 0x1 failed 0x4 times over 0x1E secs with ErrorCode 0x1ABCDEF");
+}
diff --git a/test/openpower-pels/stream_test.cpp b/test/openpower-pels/stream_test.cpp
new file mode 100644
index 0000000..361cfc5
--- /dev/null
+++ b/test/openpower-pels/stream_test.cpp
@@ -0,0 +1,197 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/stream.hpp"
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+
+TEST(StreamTest, TestExtract)
+{
+ std::vector<uint8_t> data{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 'h', 'e', 'l', 'l', 'o'};
+ Stream stream{data};
+
+ {
+ uint8_t v;
+ stream >> v;
+ EXPECT_EQ(v, 0x11);
+ }
+ {
+ uint16_t v;
+ stream >> v;
+ EXPECT_EQ(v, 0x2233);
+ }
+ {
+ uint32_t v;
+ stream >> v;
+ EXPECT_EQ(v, 0x44556677);
+ }
+ {
+ uint64_t v;
+ stream >> v;
+ EXPECT_EQ(v, 0x0102030405060708);
+ }
+ {
+ char v[6] = {0};
+ stream.read(v, 5);
+ EXPECT_EQ(memcmp(v, "hello", 5), 0);
+ }
+
+ EXPECT_EQ(stream.remaining(), 0);
+
+ // At the end, so should throw.
+ uint8_t v;
+ EXPECT_THROW(stream >> v, std::out_of_range);
+}
+
+TEST(StreamTest, InputTestNoExpansion)
+{
+ std::vector<uint8_t> data(256, 0);
+ Stream stream(data);
+ uint8_t v1 = 0x11;
+ uint16_t v2 = 0x2233;
+ uint64_t v3 = 0x445566778899AABB;
+ uint32_t v4 = 0xCCDDEEFF;
+
+ stream << v3 << v2 << v4 << v1;
+
+ uint8_t e1;
+ uint16_t e2;
+ uint64_t e3;
+ uint32_t e4;
+
+ stream.offset(0);
+ stream >> e3 >> e2 >> e4 >> e1;
+
+ EXPECT_EQ(v1, e1);
+ EXPECT_EQ(v2, e2);
+ EXPECT_EQ(v3, e3);
+ EXPECT_EQ(v4, e4);
+}
+
+TEST(StreamTest, InputTestExpansion)
+{
+ // The stream will expand the underlying vector
+ std::vector<uint8_t> data;
+ Stream stream(data);
+
+ uint32_t v1 = 0xAABBCCDD;
+ stream << v1;
+
+ stream.offset(0);
+ uint32_t e1;
+ stream >> e1;
+ EXPECT_EQ(data.size(), 4);
+ EXPECT_EQ(v1, e1);
+
+ stream.offset(2);
+
+ uint64_t v2 = 0x0102030405060708;
+ stream << v2;
+
+ EXPECT_EQ(data.size(), 10);
+ uint64_t e2;
+ stream.offset(2);
+ stream >> e2;
+
+ EXPECT_EQ(v2, e2);
+
+ auto origSize = data.size();
+ uint8_t v3 = 0xCC;
+ stream << v3;
+
+ EXPECT_EQ(origSize + 1, data.size());
+ stream.offset(stream.offset() - 1);
+ uint8_t e3;
+ stream >> e3;
+ EXPECT_EQ(v3, e3);
+}
+
+TEST(StreamTest, ReadWriteTest)
+{
+ std::vector<uint8_t> data{0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc};
+ Stream stream{data};
+ uint8_t buf[data.size()];
+
+ stream.read(buf, data.size());
+
+ for (size_t i = 0; i < data.size(); i++)
+ {
+ EXPECT_EQ(buf[i], data[i]);
+
+ // for the next test
+ buf[i] = 0x20 + i;
+ }
+
+ stream.offset(6);
+ stream.write(buf, 6);
+ for (size_t i = 0; i < 6; i++)
+ {
+ EXPECT_EQ(buf[i], data[i + 6]);
+ }
+}
+
+TEST(StreamTest, TestOffsets)
+{
+ std::vector<uint8_t> data{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ Stream stream{data, 3};
+
+ {
+ uint8_t v;
+ stream >> v;
+ EXPECT_EQ(v, 0x44);
+ EXPECT_EQ(stream.offset(), 4);
+ }
+
+ stream.offset(6);
+
+ {
+ uint8_t v;
+ stream >> v;
+ EXPECT_EQ(v, 0x77);
+ EXPECT_EQ(stream.offset(), 7);
+ EXPECT_EQ(stream.remaining(), 0);
+ }
+
+ EXPECT_THROW(stream.offset(100), std::out_of_range);
+}
+
+TEST(StreamTest, TestVectorInsertExtract)
+{
+ std::vector<uint8_t> toInsert{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ std::vector<uint8_t> data;
+
+ // Insert
+ Stream stream{data};
+ stream << toInsert;
+ EXPECT_EQ(data, toInsert);
+
+ // Extract
+ std::vector<uint8_t> toExtract;
+ toExtract.resize(toInsert.size());
+ stream.offset(0);
+ stream >> toExtract;
+
+ EXPECT_EQ(data, toExtract);
+
+ // Go off the end
+ EXPECT_THROW(stream >> toExtract, std::out_of_range);
+}
diff --git a/test/openpower-pels/user_data_test.cpp b/test/openpower-pels/user_data_test.cpp
new file mode 100644
index 0000000..a957a4d
--- /dev/null
+++ b/test/openpower-pels/user_data_test.cpp
@@ -0,0 +1,106 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "extensions/openpower-pels/user_data.hpp"
+#include "pel_utils.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+
+std::vector<uint8_t> udSectionData{0x55, 0x44, // ID 'UD'
+ 0x00, 0x10, // Size
+ 0x01, 0x02, // version, subtype
+ 0x03, 0x04, // comp ID
+
+ // Data
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18};
+
+TEST(UserDataTest, UnflattenFlattenTest)
+{
+ Stream stream(udSectionData);
+ UserData ud(stream);
+
+ EXPECT_TRUE(ud.valid());
+ EXPECT_EQ(ud.header().id, 0x5544);
+ EXPECT_EQ(ud.header().size, udSectionData.size());
+ EXPECT_EQ(ud.header().version, 0x01);
+ EXPECT_EQ(ud.header().subType, 0x02);
+ EXPECT_EQ(ud.header().componentID, 0x0304);
+
+ const auto& data = ud.data();
+
+ // The data itself starts after the header
+ EXPECT_EQ(data.size(), udSectionData.size() - 8);
+
+ for (size_t i = 0; i < data.size(); i++)
+ {
+ EXPECT_EQ(data[i], udSectionData[i + 8]);
+ }
+
+ // Now flatten
+ std::vector<uint8_t> newData;
+ Stream newStream(newData);
+ ud.flatten(newStream);
+
+ EXPECT_EQ(udSectionData, newData);
+}
+
+TEST(UserDataTest, BadDataTest)
+{
+ auto data = udSectionData;
+ data.resize(4);
+
+ Stream stream(data);
+ UserData ud(stream);
+ EXPECT_FALSE(ud.valid());
+}
+
+TEST(UserDataTest, BadSizeFieldTest)
+{
+ auto data = udSectionData;
+
+ {
+ data[3] = 0xFF; // Set the size field too large
+ Stream stream(data);
+ UserData ud(stream);
+ EXPECT_FALSE(ud.valid());
+ }
+ {
+ data[3] = 0x7; // Set the size field too small
+ Stream stream(data);
+ UserData ud(stream);
+ EXPECT_FALSE(ud.valid());
+ }
+}
+
+TEST(UserDataTest, ConstructorTest)
+{
+ std::vector<uint8_t> data{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
+
+ UserData ud(0x1112, 0x42, 0x01, data);
+ EXPECT_TRUE(ud.valid());
+
+ EXPECT_EQ(ud.header().id, 0x5544);
+ EXPECT_EQ(ud.header().size, 16);
+ EXPECT_EQ(ud.header().version, 0x01);
+ EXPECT_EQ(ud.header().subType, 0x42);
+ EXPECT_EQ(ud.header().componentID, 0x1112);
+
+ const auto& d = ud.data();
+
+ EXPECT_EQ(d, data);
+}
diff --git a/test/openpower-pels/user_header_test.cpp b/test/openpower-pels/user_header_test.cpp
new file mode 100644
index 0000000..7b8842c
--- /dev/null
+++ b/test/openpower-pels/user_header_test.cpp
@@ -0,0 +1,163 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * 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 "elog_entry.hpp"
+#include "extensions/openpower-pels/pel_types.hpp"
+#include "extensions/openpower-pels/private_header.hpp"
+#include "extensions/openpower-pels/user_header.hpp"
+#include "pel_utils.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+
+TEST(UserHeaderTest, SizeTest)
+{
+ EXPECT_EQ(UserHeader::flattenedSize(), 24);
+}
+
+TEST(UserHeaderTest, UnflattenFlattenTest)
+{
+ auto data = pelDataFactory(TestPELType::userHeaderSection);
+
+ Stream stream(data);
+ UserHeader uh(stream);
+ EXPECT_EQ(uh.valid(), true);
+
+ EXPECT_EQ(uh.header().id, 0x5548);
+ EXPECT_EQ(uh.header().size, UserHeader::flattenedSize());
+ EXPECT_EQ(uh.header().version, 0x01);
+ EXPECT_EQ(uh.header().subType, 0x0A);
+ EXPECT_EQ(uh.header().componentID, 0x0B0C);
+
+ EXPECT_EQ(uh.subsystem(), 0x10);
+ EXPECT_EQ(uh.scope(), 0x04);
+ EXPECT_EQ(uh.severity(), 0x20);
+ EXPECT_EQ(uh.eventType(), 0x00);
+ EXPECT_EQ(uh.problemDomain(), 0x03);
+ EXPECT_EQ(uh.problemVector(), 0x04);
+ EXPECT_EQ(uh.actionFlags(), 0x80C0);
+
+ // Now flatten into a vector and check that this vector
+ // matches the original one.
+ std::vector<uint8_t> newData;
+ Stream newStream(newData);
+
+ uh.flatten(newStream);
+ EXPECT_EQ(data, newData);
+}
+
+TEST(UserHeaderTest, ShortDataTest)
+{
+ auto data = pelDataFactory(TestPELType::userHeaderSection);
+ data.resize(data.size() - 1);
+
+ Stream stream(data);
+ UserHeader uh(stream);
+
+ EXPECT_EQ(uh.valid(), false);
+}
+
+TEST(UserHeaderTest, CorruptDataTest1)
+{
+ auto data = pelDataFactory(TestPELType::userHeaderSection);
+ data.resize(data.size() - 1);
+
+ data.at(0) = 0; // corrupt the section ID
+
+ Stream stream(data);
+ UserHeader uh(stream);
+
+ EXPECT_EQ(uh.valid(), false);
+}
+
+TEST(UserHeaderTest, CorruptDataTest2)
+{
+ auto data = pelDataFactory(TestPELType::userHeaderSection);
+
+ data.at(4) = 0x22; // corrupt the version
+
+ Stream stream(data);
+ UserHeader uh(stream);
+
+ EXPECT_EQ(uh.valid(), false);
+}
+
+// Construct the User Header from the message registry
+TEST(UserHeaderTest, ConstructionTest)
+{
+ using namespace openpower::pels::message;
+ Entry regEntry;
+
+ regEntry.name = "test";
+ regEntry.subsystem = 5;
+ regEntry.severity = 0x40;
+ regEntry.actionFlags = 0xC000;
+ regEntry.eventType = 1;
+ regEntry.eventScope = 2;
+
+ UserHeader uh(regEntry, phosphor::logging::Entry::Level::Error);
+
+ ASSERT_TRUE(uh.valid());
+ EXPECT_EQ(uh.header().id, 0x5548);
+ EXPECT_EQ(uh.header().size, UserHeader::flattenedSize());
+ EXPECT_EQ(uh.header().version, 0x01);
+ EXPECT_EQ(uh.header().subType, 0x00);
+ EXPECT_EQ(uh.header().componentID,
+ static_cast<uint16_t>(ComponentID::phosphorLogging));
+
+ ASSERT_EQ(uh.subsystem(), 5);
+ ASSERT_EQ(uh.severity(), 0x40);
+ ASSERT_EQ(uh.eventType(), 1);
+ ASSERT_EQ(uh.scope(), 2);
+ ASSERT_EQ(uh.problemDomain(), 0);
+ ASSERT_EQ(uh.problemVector(), 0);
+ ASSERT_EQ(uh.actionFlags(), 0xC000);
+}
+
+// Test that the severity comes from the event log if not
+// in the message registry
+TEST(UserHeaderTest, UseEventLogSevTest)
+{
+ using namespace openpower::pels::message;
+ Entry regEntry;
+
+ regEntry.name = "test";
+ regEntry.subsystem = 5;
+ regEntry.actionFlags = 0xC000;
+ regEntry.eventType = 1;
+ regEntry.eventScope = 2;
+ // Leave off severity
+
+ UserHeader uh(regEntry, phosphor::logging::Entry::Level::Error);
+ ASSERT_EQ(uh.severity(), 0x40);
+}
+
+// Test that the optional event type & scope fields work
+TEST(UserHeaderTest, DefaultEventTypeScopeTest)
+{
+ using namespace openpower::pels::message;
+ Entry regEntry;
+
+ regEntry.name = "test";
+ regEntry.subsystem = 5;
+ regEntry.severity = 0x40;
+ regEntry.actionFlags = 0xC000;
+
+ UserHeader uh(regEntry, phosphor::logging::Entry::Level::Error);
+
+ ASSERT_EQ(uh.eventType(), 0);
+ ASSERT_EQ(uh.scope(), 0x03);
+}
diff --git a/test/remote_logging_test_config.cpp b/test/remote_logging_test_config.cpp
index b34a43f..cba54ea 100644
--- a/test/remote_logging_test_config.cpp
+++ b/test/remote_logging_test_config.cpp
@@ -3,19 +3,6 @@
#include <fstream>
#include <string>
-#if __has_include(<filesystem>)
-#include <filesystem>
-#elif __has_include(<experimental/filesystem>)
-#include <experimental/filesystem>
-namespace std
-{
-// splice experimental::filesystem into std
-namespace filesystem = std::experimental::filesystem;
-} // namespace std
-#else
-#error filesystem not available
-#endif
-
namespace phosphor
{
namespace logging
@@ -34,13 +21,13 @@ std::string getConfig(const char* filePath)
TEST_F(TestRemoteLogging, testOnlyAddress)
{
config->address("1.1.1.1");
- EXPECT_EQ(fs::exists(configFilePath.c_str()), false);
+ EXPECT_EQ(getConfig(configFilePath.c_str()), "*.* ~");
}
TEST_F(TestRemoteLogging, testOnlyPort)
{
config->port(100);
- EXPECT_EQ(fs::exists(configFilePath.c_str()), false);
+ EXPECT_EQ(getConfig(configFilePath.c_str()), "*.* ~");
}
TEST_F(TestRemoteLogging, testGoodConfig)
@@ -56,7 +43,7 @@ TEST_F(TestRemoteLogging, testClearAddress)
config->port(100);
EXPECT_EQ(getConfig(configFilePath.c_str()), "*.* @@1.1.1.1:100");
config->address("");
- EXPECT_EQ(fs::exists(configFilePath.c_str()), false);
+ EXPECT_EQ(getConfig(configFilePath.c_str()), "*.* ~");
}
TEST_F(TestRemoteLogging, testClearPort)
@@ -65,7 +52,7 @@ TEST_F(TestRemoteLogging, testClearPort)
config->port(100);
EXPECT_EQ(getConfig(configFilePath.c_str()), "*.* @@1.1.1.1:100");
config->port(0);
- EXPECT_EQ(fs::exists(configFilePath.c_str()), false);
+ EXPECT_EQ(getConfig(configFilePath.c_str()), "*.* ~");
}
} // namespace test
OpenPOWER on IntegriCloud