add handler logic to handle SysGetEthDevice

Add a handler to handle code logic outside of the actual IPMI
processing.

Tested: Only ran unit-tests (added new ones).
Change-Id: Iadd8c4f2d9b3e2cfba24ae32cda2ef66177b1177
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/Makefile.am b/Makefile.am
index 868cf6a..4218de9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -40,7 +40,7 @@
 endif
 
 noinst_LTLIBRARIES = libsyscmds_common.la
-libsyscmds_common_la_SOURCES = cable.cpp cpld.cpp eth.cpp psu.cpp pcie_i2c.cpp entity_name.cpp
+libsyscmds_common_la_SOURCES = cable.cpp cpld.cpp eth.cpp psu.cpp pcie_i2c.cpp entity_name.cpp handler.cpp
 libsyscmds_common_la_CXXFLAGS = \
 	$(SDBUSPLUS_CFLAGS) \
 	$(PHOSPHOR_LOGGING_CFLAGS) \
diff --git a/eth.cpp b/eth.cpp
index 45ce91b..080dd9c 100644
--- a/eth.cpp
+++ b/eth.cpp
@@ -16,11 +16,13 @@
 
 #include "eth.hpp"
 
+#include "handler.hpp"
 #include "main.hpp"
 
 #include <cstdint>
 #include <cstring>
 #include <string>
+#include <tuple>
 
 namespace google
 {
@@ -32,30 +34,14 @@
     uint8_t subcommand;
 } __attribute__((packed));
 
-// The phosphor-host-ipmi daemon requires a configuration that maps
-// the if_name to the IPMI LAN channel.  However, that doesn't strictly
-// define which is meant to be used for NCSI.
-#ifndef NCSI_IPMI_CHANNEL
-#define NCSI_IPMI_CHANNEL 1
-#endif
-
-#ifndef NCSI_IF_NAME
-#define NCSI_IF_NAME eth0
-#endif
-
 // TOOD(venture): The ipmid.h has this macro, which is a header we
 // can't normally access.
 #ifndef MAX_IPMI_BUFFER
 #define MAX_IPMI_BUFFER 64
 #endif
 
-// To deal with receiving a string without quotes.
-#define QUOTE(name) #name
-#define STR(macro) QUOTE(macro)
-#define NCSI_IF_NAME_STR STR(NCSI_IF_NAME)
-
 ipmi_ret_t GetEthDevice(const uint8_t* reqBuf, uint8_t* replyBuf,
-                        size_t* dataLen)
+                        size_t* dataLen, const HandlerInterface* handler)
 {
     if ((*dataLen) < sizeof(struct EthDeviceRequest))
     {
@@ -64,7 +50,9 @@
         return IPMI_CC_REQ_DATA_LEN_INVALID;
     }
 
-    std::string device = NCSI_IF_NAME_STR;
+    std::tuple<std::uint8_t, std::string> details = handler->getEthDetails();
+
+    std::string device = std::get<1>(details);
     if (device.length() == 0)
     {
         std::fprintf(stderr, "Invalid eth string\n");
@@ -80,7 +68,7 @@
     // Fill in the response buffer.
     auto reply = reinterpret_cast<struct EthDeviceReply*>(&replyBuf[0]);
     reply->subcommand = SysGetEthDevice;
-    reply->channel = NCSI_IPMI_CHANNEL;
+    reply->channel = std::get<0>(details);
     reply->if_name_len = device.length();
     std::memcpy(reply->if_name, device.c_str(), device.length());
 
diff --git a/eth.hpp b/eth.hpp
index 60a98b5..e5b8079 100644
--- a/eth.hpp
+++ b/eth.hpp
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "handler.hpp"
+
 #include <ipmid/api.h>
 
 namespace google
@@ -23,7 +25,8 @@
 // Sys can query the if_name and IPMI channel of the BMC's NCSI ethernet
 // device.
 ipmi_ret_t GetEthDevice(const uint8_t* reqBuf, uint8_t* replyBuf,
-                        size_t* dataLen);
+                        size_t* dataLen,
+                        const HandlerInterface* handler = &handlerImpl);
 
 } // namespace ipmi
 } // namespace google
diff --git a/handler.cpp b/handler.cpp
new file mode 100644
index 0000000..6a346ba
--- /dev/null
+++ b/handler.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * 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 "handler.hpp"
+
+// The phosphor-host-ipmi daemon requires a configuration that maps
+// the if_name to the IPMI LAN channel.  However, that doesn't strictly
+// define which is meant to be used for NCSI.
+#ifndef NCSI_IPMI_CHANNEL
+#define NCSI_IPMI_CHANNEL 1
+#endif
+
+#ifndef NCSI_IF_NAME
+#define NCSI_IF_NAME eth0
+#endif
+
+// To deal with receiving a string without quotes.
+#define QUOTE(name) #name
+#define STR(macro) QUOTE(macro)
+#define NCSI_IF_NAME_STR STR(NCSI_IF_NAME)
+
+namespace google
+{
+namespace ipmi
+{
+
+std::tuple<std::uint8_t, std::string> Handler::getEthDetails() const
+{
+    return std::make_tuple(NCSI_IPMI_CHANNEL, NCSI_IF_NAME_STR);
+}
+
+Handler handlerImpl;
+
+} // namespace ipmi
+} // namespace google
diff --git a/handler.hpp b/handler.hpp
new file mode 100644
index 0000000..cad2916
--- /dev/null
+++ b/handler.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <cstdint>
+#include <string>
+#include <tuple>
+
+namespace google
+{
+namespace ipmi
+{
+
+class HandlerInterface
+{
+  public:
+    virtual ~HandlerInterface() = default;
+
+    /**
+     * Return ethernet details (hard-coded).
+     *
+     * @return tuple of ethernet details (channel, if name).
+     */
+    virtual std::tuple<std::uint8_t, std::string> getEthDetails() const = 0;
+};
+
+class Handler : public HandlerInterface
+{
+  public:
+    Handler() = default;
+    ~Handler() = default;
+
+    std::tuple<std::uint8_t, std::string> getEthDetails() const override;
+};
+
+extern Handler handlerImpl;
+
+} // namespace ipmi
+} // namespace google
diff --git a/test/Makefile.am b/test/Makefile.am
index a039e01..fea204b 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -15,6 +15,10 @@
 check_PROGRAMS =
 TESTS = $(check_PROGRAMS)
 
+check_PROGRAMS += handler_unittest
+handler_unittest_SOURCES = handler_unittest.cpp
+handler_unittest_LDADD = $(top_builddir)/libsyscmds_common.la
+
 check_PROGRAMS += eth_unittest
 eth_unittest_SOURCES = eth_unittest.cpp
 eth_unittest_LDADD = $(top_builddir)/libsyscmds_common.la
diff --git a/test/eth_unittest.cpp b/test/eth_unittest.cpp
index f0e4d23..dd3c73f 100644
--- a/test/eth_unittest.cpp
+++ b/test/eth_unittest.cpp
@@ -1,14 +1,19 @@
 #include "eth.hpp"
+#include "handler_mock.hpp"
 #include "main.hpp"
 
 #include <cstdint>
 #include <cstring>
+#include <string>
+#include <tuple>
 #include <vector>
 
 #include <gtest/gtest.h>
 
 #define MAX_IPMI_BUFFER 64
 
+using ::testing::Return;
+
 namespace google
 {
 namespace ipmi
@@ -22,12 +27,21 @@
     size_t dataLen = request.size();
     std::uint8_t reply[MAX_IPMI_BUFFER];
     const std::uint8_t expectedAnswer[4] = {'e', 't', 'h', '0'};
+    const std::uint8_t expectedChannel = 1;
 
-    EXPECT_EQ(IPMI_CC_OK, GetEthDevice(request.data(), &reply[0], &dataLen));
+    HandlerMock hMock;
+    EXPECT_CALL(hMock, getEthDetails())
+        .WillOnce(Return(std::make_tuple(
+            expectedChannel,
+            std::string(expectedAnswer,
+                        expectedAnswer + sizeof(expectedAnswer)))));
+
+    EXPECT_EQ(IPMI_CC_OK,
+              GetEthDevice(request.data(), &reply[0], &dataLen, &hMock));
     struct EthDeviceReply check;
     std::memcpy(&check, &reply[0], sizeof(check));
     EXPECT_EQ(check.subcommand, SysOEMCommands::SysGetEthDevice);
-    EXPECT_EQ(check.channel, 1);
+    EXPECT_EQ(check.channel, expectedChannel);
     EXPECT_EQ(check.if_name_len, sizeof(expectedAnswer));
     EXPECT_EQ(0, std::memcmp(expectedAnswer, &reply[sizeof(check)],
                              sizeof(expectedAnswer)));
diff --git a/test/handler_mock.hpp b/test/handler_mock.hpp
new file mode 100644
index 0000000..8507f50
--- /dev/null
+++ b/test/handler_mock.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "handler.hpp"
+
+#include <gmock/gmock.h>
+
+namespace google
+{
+namespace ipmi
+{
+
+class HandlerMock : public HandlerInterface
+{
+
+  public:
+    ~HandlerMock() = default;
+
+    MOCK_CONST_METHOD0(getEthDetails, std::tuple<std::uint8_t, std::string>());
+};
+
+} // namespace ipmi
+} // namespace google
diff --git a/test/handler_unittest.cpp b/test/handler_unittest.cpp
new file mode 100644
index 0000000..05edc0a
--- /dev/null
+++ b/test/handler_unittest.cpp
@@ -0,0 +1,26 @@
+#include "handler.hpp"
+
+#include <string>
+#include <tuple>
+
+#include <gtest/gtest.h>
+
+namespace google
+{
+namespace ipmi
+{
+
+TEST(HandlerTest, EthCheckValidHappy)
+{
+    // The code returns compiled-in information, and therefore cannot really
+    // fail.
+    Handler h;
+    std::tuple<std::uint8_t, std::string> result = h.getEthDetails();
+    EXPECT_EQ(1, std::get<0>(result));
+    EXPECT_STREQ("eth0", std::get<1>(result).c_str());
+}
+
+// TODO: Add checks for other functions of handler.
+
+} // namespace ipmi
+} // namespace google