PDR: implement generation framework

This commit implements a framework that allows describing
platform/system specific PDR information in JSON files, which the PLDM
responder library parses and builds a PDR out of.

Separate JSON files are expected per PDR type. This commit also adds the
code to build a state effecter PDR. PDR record handles are implemented
as incrementing indices, starting at 1, but this commit enables
implementing other type of record handles (for eg offset based).

This commit doesn't handle merging of PDRs received from other terminii,
so the PDR that's generated now is for the BMC. Merging of PDRs will be
implemented in future commits.

Change-Id: I005b08c5d29d12fc2459ca8d6e18547107a3da7d
Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
diff --git a/README.md b/README.md
index e1347a7..e9cfc14 100644
--- a/README.md
+++ b/README.md
@@ -115,3 +115,18 @@
 
 d) The requester app has to work with the response field(s). It can make use of
    a decode_foo_resp() API to deserialize the response message.
+
+# PDR Implementation
+While PLDM Platform Descriptor Records (PDRs) are mostly static information,
+they can vary across platforms and systems. For this reason, platform specific
+PDR information is encoded in platform specific JSON files. JSON files must be
+named based on the PDR type number. For example a state effecter PDR JSON file
+will be named 11.json. The JSON files may also include information to enable
+additional processing (apart from PDR creation) for specific PDR types, for eg
+mapping an effecter id to a D-Bus object.
+
+The PLDM responder implementation finds and parses PDR JSON files to create the
+PDR repository. Platform specific PDR modifications would likely just result in
+JSON updates. New PDR type support would require JSON updates as well as PDR
+generation code. The PDR generator is a map of PDR Type -> C++ lambda to create
+PDR entries for that type based on the JSON, and to update the central PDR repo.
diff --git a/configure.ac b/configure.ac
index 845a324..b1e7fc1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,8 @@
 AX_ADD_AM_MACRO_STATIC([])
 
 AX_PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus])
+AX_PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging])
+PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces])
 
 # Check/set gtest specific functions.
 AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"])
diff --git a/libpldm/Makefile.am b/libpldm/Makefile.am
index 7dddf02..dc03711 100644
--- a/libpldm/Makefile.am
+++ b/libpldm/Makefile.am
@@ -3,7 +3,8 @@
 	base.h \
 	pldm_types.h \
 	platform.h \
-	bios.h
+	bios.h \
+	states.h
 
 libpldm_LTLIBRARIES = libpldm.la
 libpldmdir = ${libdir}
diff --git a/libpldm/platform.h b/libpldm/platform.h
index 790f1f5..7d25eb3 100644
--- a/libpldm/platform.h
+++ b/libpldm/platform.h
@@ -23,6 +23,61 @@
 	PLDM_SET_STATE_EFFECTER_STATES = 0x39,
 };
 
+/** @brief PLDM PDR types
+ */
+enum pldm_pdr_types {
+	PLDM_STATE_EFFECTER_PDR = 11,
+};
+
+/** @brief PLDM effecter initialization schemes
+ */
+enum pldm_effecter_init {
+	PLDM_NO_INIT,
+	PLDM_USE_INIT_PDR,
+	PLDM_ENABLE_EFFECTER,
+	PLDM_DISABLE_EFECTER
+};
+
+/** @struct pldm_pdr_hdr
+ *
+ *  Structure representing PLDM common PDR header
+ */
+struct pldm_pdr_hdr {
+	uint32_t record_handle;
+	uint8_t version;
+	uint8_t type;
+	uint16_t record_change_num;
+	uint16_t length;
+} __attribute__((packed));
+
+/** @struct pldm_state_effecter_pdr
+ *
+ *  Structure representing PLDM state effecter PDR
+ */
+struct pldm_state_effecter_pdr {
+	struct pldm_pdr_hdr hdr;
+	uint16_t terminus_handle;
+	uint16_t effecter_id;
+	uint16_t entity_type;
+	uint16_t entity_instance;
+	uint16_t container_id;
+	uint16_t effecter_semantic_id;
+	uint8_t effecter_init;
+	bool8_t has_description_pdr;
+	uint8_t composite_effecter_count;
+	uint8_t possible_states[1];
+} __attribute__((packed));
+
+/** @struct state_effecter_possible_states
+ *
+ *  Structure representing state enums for state effecter
+ */
+struct state_effecter_possible_states {
+	uint16_t state_set_id;
+	uint8_t possible_states_size;
+	bitfield8_t states[1];
+} __attribute__((packed));
+
 /** @struct set_effecter_state_field
  *
  *  Structure representing a stateField in SetStateEffecterStates command */
diff --git a/libpldm/pldm_types.h b/libpldm/pldm_types.h
index 5ec53ff..3470413 100644
--- a/libpldm/pldm_types.h
+++ b/libpldm/pldm_types.h
@@ -28,4 +28,6 @@
 	uint8_t alpha;
 } __attribute__((packed)) ver32_t;
 
+typedef uint8_t bool8_t;
+
 #endif /* PLDM_TYPES_H */
diff --git a/libpldm/states.h b/libpldm/states.h
new file mode 100644
index 0000000..a64a7ae
--- /dev/null
+++ b/libpldm/states.h
@@ -0,0 +1,27 @@
+#ifndef STATES_H
+#define STATES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "pldm_types.h"
+
+/** @brief PLDM state set ids
+ */
+enum pldm_state_set_ids {
+	PLDM_BOOT_PROGRESS_STATE = 196,
+};
+
+/** @brief PLDM enums for the boot progress state set
+ */
+enum pldm_boot_progress_states {
+	PLDM_BOOT_NOT_ACTIVE = 1,
+	PLDM_BOOT_COMPLETED = 2,
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* STATES_H */
diff --git a/libpldmresponder/Makefile.am b/libpldmresponder/Makefile.am
index 21cfdc8..0bd1ea5 100644
--- a/libpldmresponder/Makefile.am
+++ b/libpldmresponder/Makefile.am
@@ -3,16 +3,24 @@
 libpldmresponder_la_SOURCES = \
 	base.cpp \
 	utils.cpp \
-	bios.cpp
+	bios.cpp \
+	effecters.cpp \
+	pdr.cpp
 libpldmresponder_la_LIBADD = \
 	../libpldm/libpldm.la \
 	$(CODE_COVERAGE_LIBS)
 libpldmresponder_la_LDFLAGS = \
 	$(SDBUSPLUS_LIBS) \
+	$(PHOSPHOR_LOGGING_LIBS) \
+	$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+	-lstdc++fs \
 	-version-info 1:0:0 -shared
 libpldmresponder_la_CXXFLAGS = \
 	$(CODE_COVERAGE_CXXFLAGS) \
-	$(SDBUSPLUS_CFLAGS)
+	$(SDBUSPLUS_CFLAGS) \
+	$(PHOSPHOR_LOGGING_CFLAGS) \
+	$(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
+
 libpldmresponder_la_CPPFLAGS = \
 	$(CODE_COVERAGE_CPPFLAGS) \
    	-I$(top_builddir)/libpldm/ \
diff --git a/libpldmresponder/effecters.cpp b/libpldmresponder/effecters.cpp
new file mode 100644
index 0000000..ecc63bc
--- /dev/null
+++ b/libpldmresponder/effecters.cpp
@@ -0,0 +1,20 @@
+#include "effecters.hpp"
+
+namespace pldm
+{
+
+namespace responder
+{
+
+namespace effecter
+{
+
+Id nextId()
+{
+    static Id id = 0;
+    return ++id;
+}
+
+} // namespace effecter
+} // namespace responder
+} // namespace pldm
diff --git a/libpldmresponder/effecters.hpp b/libpldmresponder/effecters.hpp
new file mode 100644
index 0000000..a0704df
--- /dev/null
+++ b/libpldmresponder/effecters.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <stdint.h>
+
+namespace pldm
+{
+
+namespace responder
+{
+
+namespace effecter
+{
+
+using Id = uint16_t;
+
+/** @brief Get next available id to assign to an effecter
+ *
+ *  @return  uint16_t - effecter id
+ */
+Id nextId();
+
+} // namespace effecter
+} // namespace responder
+} // namespace pldm
diff --git a/libpldmresponder/examples/pdr/11.json b/libpldmresponder/examples/pdr/11.json
new file mode 100644
index 0000000..7410ab9
--- /dev/null
+++ b/libpldmresponder/examples/pdr/11.json
@@ -0,0 +1,33 @@
+{
+    "entries" : [{
+        "type" : 33,
+        "instance" : 0,
+        "container" : 0,
+        "effecters" : [{
+            "set" : {
+                "id" : 196,
+                "size" : 1,
+                "states" : [1]
+            }
+        }]
+    },
+    {
+        "type" : 100,
+        "instance" : 0,
+        "container" : 0,
+        "effecters" : [{
+            "set" : {
+                "id" : 197,
+                "size" : 1,
+                "states" : [1]
+            }
+        },
+        {
+            "set" : {
+                "id" : 198,
+                "size" : 2,
+                "states" : [1,2,5,15]
+            }
+        }]
+    }]
+}
diff --git a/libpldmresponder/pdr.cpp b/libpldmresponder/pdr.cpp
new file mode 100644
index 0000000..e252cb3
--- /dev/null
+++ b/libpldmresponder/pdr.cpp
@@ -0,0 +1,26 @@
+#include "pdr.hpp"
+
+namespace pldm
+{
+
+namespace responder
+{
+
+namespace pdr
+{
+
+Repo& get(const std::string& dir)
+{
+    using namespace internal;
+    static IndexedRepo repo;
+    if (repo.empty())
+    {
+        generate(dir, repo);
+    }
+
+    return repo;
+}
+
+} // namespace pdr
+} // namespace responder
+} // namespace pldm
diff --git a/libpldmresponder/pdr.hpp b/libpldmresponder/pdr.hpp
new file mode 100644
index 0000000..9cbfbac
--- /dev/null
+++ b/libpldmresponder/pdr.hpp
@@ -0,0 +1,312 @@
+#pragma once
+
+#include "effecters.hpp"
+
+#include <stdint.h>
+
+#include <filesystem>
+#include <fstream>
+#include <functional>
+#include <map>
+#include <nlohmann/json.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <string>
+#include <vector>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+#include "libpldm/platform.h"
+
+using namespace phosphor::logging;
+using InternalFailure =
+    sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+namespace fs = std::filesystem;
+
+namespace pldm
+{
+
+namespace responder
+{
+
+namespace pdr
+{
+
+using Type = uint8_t;
+using Json = nlohmann::json;
+using RecordHandle = uint32_t;
+using Entry = std::vector<uint8_t>;
+using Pdr = std::vector<Entry>;
+
+/** @class Repo
+ *
+ *  @brief Abstract class describing the interface API to the PDR repository
+ *
+ *  Concrete implementations of this must handle storing and addressing the
+ *  PDR entries by a "record handle", which can be indices, offsets, etc.
+ */
+class Repo
+{
+  public:
+    /** @brief Add a new entry to the PDR
+     *
+     *  @param[in] entry - new PDR entry
+     */
+    virtual void add(Entry&& entry) = 0;
+
+    /** @brief Access PDR entry at inout record handle
+     *
+     *  @param[in] handle - record handle
+     *
+     *  @return Entry - PDR entry
+     */
+    virtual Entry at(RecordHandle handle) const = 0;
+
+    /** @brief Get next available record handle for assignment
+     *
+     *  @return RecordHandle - PDR record handle
+     */
+    virtual RecordHandle getNextRecordHandle() const = 0;
+
+    /** @brief Get record handle immediately suceeding the input record
+     *         handle
+     *
+     *  @param[in] current - input record handle
+     *
+     *  @return RecordHandle - PDR record handle
+     */
+    virtual RecordHandle getNextRecordHandle(RecordHandle current) const = 0;
+
+    /** @brief Get number of entries in the PDR
+     *
+     *  @return size_t - number of entries
+     */
+    virtual size_t numEntries() const = 0;
+
+    /** @brief Check if PDR is empty
+     *
+     *  @return bool - true if PDR is empty, false otherwise
+     */
+    virtual bool empty() const = 0;
+
+    /** @brief Empty the PDR
+     */
+    virtual void makeEmpty() = 0;
+};
+
+namespace internal
+{
+
+/** @brief Parse PDR JSON file and output Json object
+ *
+ *  @param[in] path - path of PDR JSON file
+ *
+ *  @return Json - Json object
+ */
+inline Json readJson(const std::string& path)
+{
+    std::ifstream jsonFile(path);
+    if (!jsonFile.is_open())
+    {
+        log<level::INFO>("Error opening PDR JSON file",
+                         entry("PATH=%s", path.c_str()));
+        return {};
+    }
+
+    return Json::parse(jsonFile);
+}
+
+/** @class IndexedRepo
+ *
+ *  @brief Inherits and implements Repo
+ *
+ *  Stores the PDR as a vector of entries, and addresses PDR entries based on an
+ *  incrementing record handle, starting at 1.
+ */
+class IndexedRepo : public Repo
+{
+  public:
+    void add(Entry&& entry)
+    {
+        repo.emplace_back(std::move(entry));
+    }
+
+    Entry at(RecordHandle handle) const
+    {
+        if (!handle)
+        {
+            handle = 1;
+        }
+        return repo.at(handle - 1);
+    }
+
+    RecordHandle getNextRecordHandle() const
+    {
+        return repo.size() + 1;
+    }
+
+    RecordHandle getNextRecordHandle(RecordHandle current) const
+    {
+        if (current >= repo.size())
+        {
+            return 0;
+        }
+        if (!current)
+        {
+            current = 1;
+        }
+        return current + 1;
+    }
+
+    size_t numEntries() const
+    {
+        return repo.size();
+    }
+
+    bool empty() const
+    {
+        return repo.empty();
+    }
+
+    void makeEmpty()
+    {
+        repo.clear();
+    }
+
+  private:
+    Pdr repo{};
+};
+
+/** @brief Parse PDR JSONs and build PDR repository
+ *
+ *  @param[in] dir - directory housing platform specific PDR JSON files
+ *  @tparam[in] repo - instance of concrete implementation of Repo
+ */
+template <typename T>
+void generate(const std::string& dir, T& repo)
+{
+    using namespace internal;
+    // A map of PDR type to a lambda that handles creation of that PDR type.
+    // The lambda essentially would parse the platform specific PDR JSONs to
+    // generate the PDR structures. This function iterates through the map to
+    // invoke all lambdas, so that all PDR types can be created.
+    std::map<Type, std::function<void(const Json& json, T& repo)>> generators =
+        {{PLDM_STATE_EFFECTER_PDR, [](const auto& json, T& repo) {
+              static const std::vector<Json> emptyList{};
+              static const Json empty{};
+              auto entries = json.value("entries", emptyList);
+              for (const auto& e : entries)
+              {
+                  size_t pdrSize = 0;
+                  auto effecters = e.value("effecters", emptyList);
+                  static const Json empty{};
+                  for (const auto& effecter : effecters)
+                  {
+                      auto set = effecter.value("set", empty);
+                      auto statesSize = set.value("size", 0);
+                      if (!statesSize)
+                      {
+                          log<level::ERR>(
+                              "Malformed PDR JSON - no state set info",
+                              entry("TYPE=%d", PLDM_STATE_EFFECTER_PDR));
+                          elog<InternalFailure>();
+                      }
+                      pdrSize += sizeof(state_effecter_possible_states) -
+                                 sizeof(bitfield8_t) +
+                                 (sizeof(bitfield8_t) * statesSize);
+                  }
+                  pdrSize += sizeof(pldm_state_effecter_pdr) - sizeof(uint8_t);
+
+                  Entry pdrEntry{};
+                  pdrEntry.resize(pdrSize);
+
+                  pldm_state_effecter_pdr* pdr =
+                      reinterpret_cast<pldm_state_effecter_pdr*>(
+                          pdrEntry.data());
+                  pdr->hdr.record_handle = repo.getNextRecordHandle();
+                  pdr->hdr.version = 1;
+                  pdr->hdr.type = PLDM_STATE_EFFECTER_PDR;
+                  pdr->hdr.record_change_num = 0;
+                  pdr->hdr.length = pdrSize - sizeof(pldm_pdr_hdr);
+
+                  pdr->terminus_handle = 0;
+                  pdr->effecter_id = effecter::nextId();
+                  pdr->entity_type = e.value("type", 0);
+                  pdr->entity_instance = e.value("instance", 0);
+                  pdr->container_id = e.value("container", 0);
+                  pdr->effecter_semantic_id = 0;
+                  pdr->effecter_init = PLDM_NO_INIT;
+                  pdr->has_description_pdr = false;
+                  pdr->composite_effecter_count = effecters.size();
+
+                  uint8_t* start = pdrEntry.data() +
+                                   sizeof(pldm_state_effecter_pdr) -
+                                   sizeof(uint8_t);
+                  for (const auto& effecter : effecters)
+                  {
+                      auto set = effecter.value("set", empty);
+                      state_effecter_possible_states* possibleStates =
+                          reinterpret_cast<state_effecter_possible_states*>(
+                              start);
+                      possibleStates->state_set_id = set.value("id", 0);
+                      possibleStates->possible_states_size =
+                          set.value("size", 0);
+
+                      start += sizeof(possibleStates->state_set_id) +
+                               sizeof(possibleStates->possible_states_size);
+                      static const std::vector<uint8_t> emptyStates{};
+                      auto states = set.value("states", emptyStates);
+                      for (const auto& state : states)
+                      {
+                          auto index = state / 8;
+                          auto bit = state - (index * 8);
+                          bitfield8_t* bf =
+                              reinterpret_cast<bitfield8_t*>(start + index);
+                          bf->byte |= 1 << bit;
+                      }
+                      start += possibleStates->possible_states_size;
+                  }
+                  repo.add(std::move(pdrEntry));
+              }
+          }}};
+
+    auto eraseLen = strlen(".json");
+    Type pdrType{};
+    for (const auto& dirEntry : fs::directory_iterator(dir))
+    {
+        try
+        {
+            auto json = readJson(dirEntry.path().string());
+            if (!json.empty())
+            {
+                auto fileName = dirEntry.path().filename().string();
+                fileName.erase(fileName.end() - eraseLen);
+                pdrType = stoi(fileName);
+                generators.at(pdrType)(json, repo);
+            }
+        }
+        catch (const InternalFailure& e)
+        {
+        }
+        catch (const std::exception& e)
+        {
+            log<level::ERR>("Failed parsing PDR JSON file",
+                            entry("TYPE=%d", pdrType),
+                            entry("ERROR=%s", e.what()));
+            report<InternalFailure>();
+        }
+    }
+}
+
+} // namespace internal
+
+/** @brief Build (if not built already) and retrieve PDR
+ *
+ *  @param[in] dir - directory housing platform specific PDR JSON files
+ *
+ *  @return Repo& - Reference to instance of pdr::Repo
+ */
+Repo& get(const std::string& dir);
+
+} // namespace pdr
+} // namespace responder
+} // namespace pldm
diff --git a/test/Makefile.am b/test/Makefile.am
index ca75b47..42663a1 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -7,7 +7,8 @@
 	libpldm_platform_test \
 	libpldmresponder_base_test \
 	libpldm_bios_test \
-	libpldmresponder_bios_test
+	libpldmresponder_bios_test \
+	libpldmresponder_pdr_state_effecter_test
 
 if OEM_IBM
 check_PROGRAMS += \
@@ -110,3 +111,15 @@
 libpldmoemresponder_fileio_test_SOURCES = $(top_builddir)/oem/ibm/test/libpldmresponder_fileio_test.cpp
 endif
 
+libpldmresponder_pdr_state_effecter_test_CPPFLAGS = $(test_cppflags)
+libpldmresponder_pdr_state_effecter_test_CXXFLAGS = $(test_cxxflags)
+libpldmresponder_pdr_state_effecter_test_LDFLAGS = $(test_ldflags)
+libpldmresponder_pdr_state_effecter_test_LDADD = \
+	$(top_builddir)/libpldmresponder/libpldmresponder_la-pdr.o \
+	$(top_builddir)/libpldmresponder/libpldmresponder_la-effecters.o \
+	$(CODE_COVERAGE_LIBS) \
+	$(SDBUSPLUS_LIBS) \
+	$(PHOSPHOR_LOGGING_LIBS) \
+	$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+	-lstdc++fs
+libpldmresponder_pdr_state_effecter_test_SOURCES = libpldmresponder_pdr_state_effecter_test.cpp
diff --git a/test/libpldmresponder_pdr_state_effecter_test.cpp b/test/libpldmresponder_pdr_state_effecter_test.cpp
new file mode 100644
index 0000000..fb8af01
--- /dev/null
+++ b/test/libpldmresponder_pdr_state_effecter_test.cpp
@@ -0,0 +1,96 @@
+#include "libpldmresponder/pdr.hpp"
+
+#include "libpldm/platform.h"
+
+#include <gtest/gtest.h>
+
+using namespace pldm::responder;
+
+TEST(GeneratePDR, testGoodJson)
+{
+    using namespace pdr;
+    Repo& pdrRepo = get("./pdr_jsons/good");
+
+    // 2 entries
+    ASSERT_EQ(pdrRepo.numEntries(), 2);
+
+    // Check first PDR
+    pdr::Entry e = pdrRepo.at(1);
+    pldm_state_effecter_pdr* pdr =
+        reinterpret_cast<pldm_state_effecter_pdr*>(e.data());
+
+    ASSERT_EQ(pdr->hdr.record_handle, 1);
+    ASSERT_EQ(pdr->hdr.version, 1);
+    ASSERT_EQ(pdr->hdr.type, PLDM_STATE_EFFECTER_PDR);
+    ASSERT_EQ(pdr->hdr.record_change_num, 0);
+    ASSERT_EQ(pdr->hdr.length, 19);
+
+    ASSERT_EQ(pdr->terminus_handle, 0);
+    ASSERT_EQ(pdr->effecter_id, 1);
+    ASSERT_EQ(pdr->entity_type, 33);
+    ASSERT_EQ(pdr->entity_instance, 0);
+    ASSERT_EQ(pdr->container_id, 0);
+    ASSERT_EQ(pdr->effecter_semantic_id, 0);
+    ASSERT_EQ(pdr->effecter_init, PLDM_NO_INIT);
+    ASSERT_EQ(pdr->has_description_pdr, false);
+    ASSERT_EQ(pdr->composite_effecter_count, 1);
+    state_effecter_possible_states* states =
+        reinterpret_cast<state_effecter_possible_states*>(pdr->possible_states);
+    ASSERT_EQ(states->state_set_id, 196);
+    ASSERT_EQ(states->possible_states_size, 1);
+    bitfield8_t bf1{};
+    bf1.byte = 2;
+    ASSERT_EQ(states->states[0].byte, bf1.byte);
+
+    // Check second PDR
+    e = pdrRepo.at(2);
+    pdr = reinterpret_cast<pldm_state_effecter_pdr*>(e.data());
+
+    ASSERT_EQ(pdr->hdr.record_handle, 2);
+    ASSERT_EQ(pdr->hdr.version, 1);
+    ASSERT_EQ(pdr->hdr.type, PLDM_STATE_EFFECTER_PDR);
+    ASSERT_EQ(pdr->hdr.record_change_num, 0);
+    ASSERT_EQ(pdr->hdr.length, 24);
+
+    ASSERT_EQ(pdr->terminus_handle, 0);
+    ASSERT_EQ(pdr->effecter_id, 2);
+    ASSERT_EQ(pdr->entity_type, 100);
+    ASSERT_EQ(pdr->entity_instance, 0);
+    ASSERT_EQ(pdr->container_id, 0);
+    ASSERT_EQ(pdr->effecter_semantic_id, 0);
+    ASSERT_EQ(pdr->effecter_init, PLDM_NO_INIT);
+    ASSERT_EQ(pdr->has_description_pdr, false);
+    ASSERT_EQ(pdr->composite_effecter_count, 2);
+    states =
+        reinterpret_cast<state_effecter_possible_states*>(pdr->possible_states);
+    ASSERT_EQ(states->state_set_id, 197);
+    ASSERT_EQ(states->possible_states_size, 1);
+    bf1.byte = 2;
+    ASSERT_EQ(states->states[0].byte, bf1.byte);
+    states = reinterpret_cast<state_effecter_possible_states*>(
+        pdr->possible_states + sizeof(state_effecter_possible_states));
+    ASSERT_EQ(states->state_set_id, 198);
+    ASSERT_EQ(states->possible_states_size, 2);
+    bitfield8_t bf2[2];
+    bf2[0].byte = 38;
+    bf2[1].byte = 128;
+    ASSERT_EQ(states->states[0].byte, bf2[0].byte);
+    ASSERT_EQ(states->states[1].byte, bf2[1].byte);
+}
+
+TEST(GeneratePDR, testNoJson)
+{
+    using namespace pdr;
+    Repo& pdrRepo = get("./pdr_jsons/not_there");
+
+    ASSERT_EQ(pdrRepo.numEntries(), 2);
+}
+
+TEST(GeneratePDR, testMalformedJson)
+{
+    using namespace pdr;
+    Repo& pdrRepo = get("./pdr_jsons/good");
+    ASSERT_EQ(pdrRepo.numEntries(), 2);
+    pdrRepo.makeEmpty();
+    ASSERT_THROW(get("./pdr_jsons/malformed"), std::exception);
+}
diff --git a/test/pdr_jsons/good/11.json b/test/pdr_jsons/good/11.json
new file mode 100644
index 0000000..7410ab9
--- /dev/null
+++ b/test/pdr_jsons/good/11.json
@@ -0,0 +1,33 @@
+{
+    "entries" : [{
+        "type" : 33,
+        "instance" : 0,
+        "container" : 0,
+        "effecters" : [{
+            "set" : {
+                "id" : 196,
+                "size" : 1,
+                "states" : [1]
+            }
+        }]
+    },
+    {
+        "type" : 100,
+        "instance" : 0,
+        "container" : 0,
+        "effecters" : [{
+            "set" : {
+                "id" : 197,
+                "size" : 1,
+                "states" : [1]
+            }
+        },
+        {
+            "set" : {
+                "id" : 198,
+                "size" : 2,
+                "states" : [1,2,5,15]
+            }
+        }]
+    }]
+}
diff --git a/test/pdr_jsons/malformed/11.json b/test/pdr_jsons/malformed/11.json
new file mode 100644
index 0000000..541fdd4
--- /dev/null
+++ b/test/pdr_jsons/malformed/11.json
@@ -0,0 +1,33 @@
+{
+    "entries" : [{
+        "type" : 33
+        "instance" : 0,
+        "container" : 0,
+        "effecters" : [{
+            "set" : {
+                "id" : 196,
+                "size" : 1,
+                "states" : [1]
+            }
+        }]
+    },
+    {
+        "type" : 100,
+        "instance" : 0,
+        "container" : 0,
+        "effecters" : [{
+            "set" : {
+                "id" : 197,
+                "size" : 1,
+                "states" : [1]
+            }
+        },
+        {
+            "set" : {
+                "id" : 198,
+                "size" : 2,
+                "states" : [1,2,5,15]
+            }
+        }]
+    }]
+}