pldmd: implement a new handler registration scheme

Implement a PLDM request handler registration scheme that requires
handlers to be C++ objects rather than plain functions. This was needed
for a couple of reasons:

- Perform specific actions at the PLDM daemon startup (that's when the
  handlers are loaded).
- Share data across PLDM request messages (for eg FRU/BIOS tables),
  without having to resort to globals and statics.

Tested:
- existing unit tests still pass
- added tests for the new registration scheme

Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Change-Id: I1cf1c0a6fccd15da54f08120e61a5f256df6bc36
diff --git a/test/libpldmresponder_base_test.cpp b/test/libpldmresponder_base_test.cpp
index bbd986a..885ef24 100644
--- a/test/libpldmresponder_base_test.cpp
+++ b/test/libpldmresponder_base_test.cpp
@@ -16,7 +16,8 @@
     auto request = reinterpret_cast<pldm_msg*>(requestPayload.data());
     // payload length will be 0 in this case
     size_t requestPayloadLength = 0;
-    auto response = getPLDMTypes(request, requestPayloadLength);
+    base::Handler handler;
+    auto response = handler.getPLDMTypes(request, requestPayloadLength);
     // Only base type supported at the moment
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
     uint8_t* payload_ptr = responsePtr->payload;
@@ -33,7 +34,8 @@
         requestPayload{};
     auto request = reinterpret_cast<pldm_msg*>(requestPayload.data());
     size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr);
-    auto response = getPLDMCommands(request, requestPayloadLength);
+    base::Handler handler;
+    auto response = handler.getPLDMCommands(request, requestPayloadLength);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
     uint8_t* payload_ptr = responsePtr->payload;
     ASSERT_EQ(payload_ptr[0], 0);
@@ -49,7 +51,8 @@
 
     request->payload[0] = 0xFF;
     size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr);
-    auto response = getPLDMCommands(request, requestPayloadLength);
+    base::Handler handler;
+    auto response = handler.getPLDMCommands(request, requestPayloadLength);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
     uint8_t* payload_ptr = responsePtr->payload;
     ASSERT_EQ(payload_ptr[0], PLDM_ERROR_INVALID_PLDM_TYPE);
@@ -72,7 +75,8 @@
 
     ASSERT_EQ(0, rc);
 
-    auto response = getPLDMVersion(request, requestPayloadLength);
+    base::Handler handler;
+    auto response = handler.getPLDMVersion(request, requestPayloadLength);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
 
     ASSERT_EQ(responsePtr->payload[0], 0);
@@ -101,7 +105,8 @@
 
     ASSERT_EQ(0, rc);
 
-    auto response = getPLDMVersion(request, requestPayloadLength - 1);
+    base::Handler handler;
+    auto response = handler.getPLDMVersion(request, requestPayloadLength - 1);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
 
     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
@@ -113,7 +118,7 @@
 
     ASSERT_EQ(0, rc);
 
-    response = getPLDMVersion(request, requestPayloadLength);
+    response = handler.getPLDMVersion(request, requestPayloadLength);
     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
 
     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_PLDM_TYPE);
@@ -125,7 +130,8 @@
     auto request = reinterpret_cast<pldm_msg*>(requestPayload.data());
     size_t requestPayloadLength = 0;
 
-    auto response = getTID(request, requestPayloadLength);
+    base::Handler handler;
+    auto response = handler.getTID(request, requestPayloadLength);
 
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
     uint8_t* payload = responsePtr->payload;
diff --git a/test/libpldmresponder_bios_test.cpp b/test/libpldmresponder_bios_test.cpp
index 7271567..1271dda 100644
--- a/test/libpldmresponder_bios_test.cpp
+++ b/test/libpldmresponder_bios_test.cpp
@@ -177,7 +177,7 @@
     table.insert(table.end(), padSize, 0);
     table.insert(table.end(), sizeof(uint32_t) /*checksum*/, 0);
 
-    pldm::responder::traverseBIOSAttrTable(
+    pldm::responder::bios::traverseBIOSAttrTable(
         table, [&](const struct pldm_bios_attr_table_entry* entry) {
             int rc;
             switch (entry->attr_type)
diff --git a/test/libpldmresponder_platform_test.cpp b/test/libpldmresponder_platform_test.cpp
index 6b89b2e..88a6eb4 100644
--- a/test/libpldmresponder_platform_test.cpp
+++ b/test/libpldmresponder_platform_test.cpp
@@ -25,7 +25,8 @@
     using namespace pdr;
     Repo& pdrRepo = get("./pdr_jsons/state_effecter/good");
     ASSERT_EQ(pdrRepo.empty(), false);
-    auto response = getPDR(request, requestPayloadLength);
+    platform::Handler handler;
+    auto response = handler.getPDR(request, requestPayloadLength);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
 
     ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
@@ -59,7 +60,8 @@
     using namespace pdr;
     Repo& pdrRepo = get("./pdr_jsons/state_effecter/good");
     ASSERT_EQ(pdrRepo.empty(), false);
-    auto response = getPDR(request, requestPayloadLength);
+    platform::Handler handler;
+    auto response = handler.getPDR(request, requestPayloadLength);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
 
     ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
@@ -86,7 +88,8 @@
     using namespace pdr;
     Repo& pdrRepo = get("./pdr_jsons/state_effecter/good");
     ASSERT_EQ(pdrRepo.empty(), false);
-    auto response = getPDR(request, requestPayloadLength);
+    platform::Handler handler;
+    auto response = handler.getPDR(request, requestPayloadLength);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
 
     ASSERT_EQ(responsePtr->payload[0], PLDM_PLATFORM_INVALID_RECORD_HANDLE);
@@ -105,7 +108,8 @@
     using namespace pdr;
     Repo& pdrRepo = get("./pdr_jsons/state_effecter/good");
     ASSERT_EQ(pdrRepo.empty(), false);
-    auto response = getPDR(request, requestPayloadLength);
+    platform::Handler handler;
+    auto response = handler.getPDR(request, requestPayloadLength);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
 
     ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
@@ -129,7 +133,8 @@
     using namespace pdr;
     Repo& pdrRepo = get("./pdr_jsons/state_effecter/good");
     ASSERT_EQ(pdrRepo.empty(), false);
-    auto response = getPDR(request, requestPayloadLength);
+    platform::Handler handler;
+    auto response = handler.getPDR(request, requestPayloadLength);
 
     // Let's try to find a PDR of type stateEffecter (= 11) and entity type =
     // 100
@@ -141,7 +146,8 @@
         start = request->payload;
         recordHandle = reinterpret_cast<uint32_t*>(start);
         *recordHandle = handle;
-        auto response = getPDR(request, requestPayloadLength);
+        platform::Handler handler;
+        auto response = handler.getPDR(request, requestPayloadLength);
         auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
 
         ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
@@ -218,8 +224,9 @@
     EXPECT_CALL(handlerObj, setDbusProperty(objPath, bootProgressProp,
                                             bootProgressInf, value))
         .Times(2);
-    auto rc = setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x1,
-                                                             stateField);
+    platform::Handler handler;
+    auto rc = handler.setStateEffecterStatesHandler<MockdBusHandler>(
+        handlerObj, 0x1, stateField);
     ASSERT_EQ(rc, 0);
 }
 
@@ -236,23 +243,24 @@
     stateField.push_back({PLDM_REQUEST_SET, 4});
 
     MockdBusHandler handlerObj;
-    auto rc = setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x1,
-                                                             stateField);
+    platform::Handler handler;
+    auto rc = handler.setStateEffecterStatesHandler<MockdBusHandler>(
+        handlerObj, 0x1, stateField);
     ASSERT_EQ(rc, PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE);
 
-    rc = setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x9,
-                                                        stateField);
+    rc = handler.setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x9,
+                                                                stateField);
     ASSERT_EQ(rc, PLDM_PLATFORM_INVALID_EFFECTER_ID);
 
     stateField.push_back({PLDM_REQUEST_SET, 4});
-    rc = setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x1,
-                                                        stateField);
+    rc = handler.setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x1,
+                                                                stateField);
     ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
 
     std::vector<set_effecter_state_field> newStateField;
     newStateField.push_back({PLDM_REQUEST_SET, 1});
 
-    rc = setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x2,
-                                                        newStateField);
+    rc = handler.setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x2,
+                                                                newStateField);
     ASSERT_EQ(rc, PLDM_PLATFORM_INVALID_STATE_VALUE);
 }
diff --git a/test/meson.build b/test/meson.build
index ee509db..4019cde 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -29,6 +29,7 @@
   'libpldm_fru_test',
   'libpldm_utils_test',
   'pldmd_instanceid_test',
+  'pldmd_registration_test',
 ]
 
 if get_option('oem-ibm').enabled()
diff --git a/test/pldmd_registration_test.cpp b/test/pldmd_registration_test.cpp
new file mode 100644
index 0000000..d51efdd
--- /dev/null
+++ b/test/pldmd_registration_test.cpp
@@ -0,0 +1,49 @@
+#include "invoker.hpp"
+
+#include <stdexcept>
+
+#include "libpldm/base.h"
+
+#include <gtest/gtest.h>
+
+using namespace pldm;
+using namespace pldm::responder;
+constexpr Command testCmd = 0xFF;
+constexpr Type testType = 0xFF;
+
+class TestHandler : public CmdHandler
+{
+  public:
+    TestHandler()
+    {
+        handlers.emplace(testCmd,
+                         [this](const pldm_msg* request, size_t payloadLength) {
+                             return this->handle(request, payloadLength);
+                         });
+    }
+
+    Response handle(const pldm_msg* /*request*/, size_t /*payloadLength*/)
+    {
+        return {100, 200};
+    }
+};
+
+TEST(Registration, testSuccess)
+{
+    Invoker invoker{};
+    invoker.registerHandler(testType, std::make_unique<TestHandler>());
+    auto result = invoker.handle(testType, testCmd, nullptr, 0);
+    ASSERT_EQ(result[0], 100);
+    ASSERT_EQ(result[1], 200);
+}
+
+TEST(Registration, testFailure)
+{
+    Invoker invoker{};
+    ASSERT_THROW(invoker.handle(testType, testCmd, nullptr, 0),
+                 std::out_of_range);
+    invoker.registerHandler(testType, std::make_unique<TestHandler>());
+    uint8_t badCmd = 0xFE;
+    ASSERT_THROW(invoker.handle(testType, badCmd, nullptr, 0),
+                 std::out_of_range);
+}