build: Some minor refactoring

Change-Id: I169d24356b883ba73327e1c6ab8d87a2398ced90
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/test/ethstats.cpp b/test/ethstats.cpp
new file mode 100644
index 0000000..db60319
--- /dev/null
+++ b/test/ethstats.cpp
@@ -0,0 +1,178 @@
+#include "ethstats.hpp"
+
+#include "handler_mock.hpp"
+
+#include <cstdint>
+#include <cstring>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#define MAX_IPMI_BUFFER 64
+
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::StrictMock;
+
+namespace ethstats
+{
+
+TEST(EthStatsTest, InvalidStatReturnsFailure)
+{
+    // Verify that the enum of valid statistic IDs is checked.
+
+    std::string ifName = "eth0";
+    struct EthStatRequest requestStruct;
+    requestStruct.statId = EthernetStatisticsIds::TX_WINDOW_ERRORS + 1;
+    requestStruct.if_name_len = ifName.length();
+
+    std::vector<std::uint8_t> request(sizeof(requestStruct) + ifName.length());
+    std::memcpy(request.data(), &requestStruct, sizeof(requestStruct));
+    std::memcpy(&request[sizeof(requestStruct)], ifName.c_str(),
+                ifName.length());
+
+    size_t dataLen = request.size();
+    std::uint8_t reply[MAX_IPMI_BUFFER];
+
+    // Using StrictMock to ensure it isn't called.
+    StrictMock<HandlerMock> hMock;
+
+    EXPECT_EQ(IPMI_CC_INVALID_FIELD_REQUEST,
+              handleEthStatCommand(request.data(), reply, &dataLen, &hMock));
+}
+
+TEST(EthStatsTest, InvalidIpmiPacketSize)
+{
+    // An IPMI packet for this command has a minimum length.
+
+    std::string ifName = "e";
+    struct EthStatRequest requestStruct;
+    requestStruct.statId = EthernetStatisticsIds::RX_BYTES;
+    requestStruct.if_name_len = ifName.length();
+
+    std::vector<std::uint8_t> request(sizeof(requestStruct) + ifName.length());
+    std::memcpy(request.data(), &requestStruct, sizeof(requestStruct));
+    std::memcpy(&request[sizeof(requestStruct)], ifName.c_str(),
+                ifName.length());
+
+    // The minimum length is a 1-byte ifname - this gives one, but dataLen is
+    // set to smaller.
+    size_t dataLen = request.size() - 1;
+    std::uint8_t reply[MAX_IPMI_BUFFER];
+
+    // Using StrictMock to ensure it isn't called.
+    StrictMock<HandlerMock> hMock;
+
+    EXPECT_EQ(IPMI_CC_REQ_DATA_LEN_INVALID,
+              handleEthStatCommand(request.data(), reply, &dataLen, &hMock));
+}
+
+TEST(EthStatsTest, InvalidIpmiPacketContents)
+{
+    // The packet has a name length and name contents, if the name length is
+    // longer than the packet size, it should fail.
+
+    std::string ifName = "eth0";
+    struct EthStatRequest requestStruct;
+    requestStruct.statId = EthernetStatisticsIds::RX_BYTES;
+    requestStruct.if_name_len = ifName.length() + 1;
+
+    std::vector<std::uint8_t> request(sizeof(requestStruct) + ifName.length());
+    std::memcpy(request.data(), &requestStruct, sizeof(requestStruct));
+    std::memcpy(&request[sizeof(requestStruct)], ifName.c_str(),
+                ifName.length());
+
+    size_t dataLen = request.size();
+    std::uint8_t reply[MAX_IPMI_BUFFER];
+
+    // Using StrictMock to ensure it isn't called.
+    StrictMock<HandlerMock> hMock;
+
+    EXPECT_EQ(IPMI_CC_REQ_DATA_LEN_INVALID,
+              handleEthStatCommand(request.data(), reply, &dataLen, &hMock));
+}
+
+TEST(EthStatsTest, NameHasIllegalCharacters)
+{
+    // The interface name cannot have slashes.
+    std::string ifName = "et/h0";
+    struct EthStatRequest requestStruct;
+    requestStruct.statId = EthernetStatisticsIds::RX_BYTES;
+    requestStruct.if_name_len = ifName.length();
+
+    std::vector<std::uint8_t> request(sizeof(requestStruct) + ifName.length());
+    std::memcpy(request.data(), &requestStruct, sizeof(requestStruct));
+    std::memcpy(&request[sizeof(requestStruct)], ifName.c_str(),
+                ifName.length());
+
+    size_t dataLen = request.size();
+    std::uint8_t reply[MAX_IPMI_BUFFER];
+
+    // Using StrictMock to ensure it isn't called.
+    StrictMock<HandlerMock> hMock;
+
+    EXPECT_EQ(IPMI_CC_INVALID_FIELD_REQUEST,
+              handleEthStatCommand(request.data(), reply, &dataLen, &hMock));
+}
+
+TEST(EthStatsTest, InvalidNameOrField)
+{
+    // The handler returns failure on the input validity check.
+    std::string ifName = "eth0";
+    struct EthStatRequest requestStruct;
+    requestStruct.statId = EthernetStatisticsIds::RX_BYTES;
+    requestStruct.if_name_len = ifName.length();
+
+    std::vector<std::uint8_t> request(sizeof(requestStruct) + ifName.length());
+    std::memcpy(request.data(), &requestStruct, sizeof(requestStruct));
+    std::memcpy(&request[sizeof(requestStruct)], ifName.c_str(),
+                ifName.length());
+
+    size_t dataLen = request.size();
+    std::uint8_t reply[MAX_IPMI_BUFFER];
+
+    std::string expectedPath = buildPath(ifName, "rx_bytes");
+
+    HandlerMock hMock;
+    EXPECT_CALL(hMock, validIfNameAndField(StrEq(expectedPath)))
+        .WillOnce(Return(false));
+
+    EXPECT_EQ(IPMI_CC_INVALID_FIELD_REQUEST,
+              handleEthStatCommand(request.data(), reply, &dataLen, &hMock));
+}
+
+TEST(EthStatsTest, EverythingHappy)
+{
+    std::string ifName = "eth0";
+    struct EthStatRequest requestStruct;
+    requestStruct.statId = EthernetStatisticsIds::RX_BYTES;
+    requestStruct.if_name_len = ifName.length();
+
+    std::vector<std::uint8_t> request(sizeof(requestStruct) + ifName.length());
+    std::memcpy(request.data(), &requestStruct, sizeof(requestStruct));
+    std::memcpy(&request[sizeof(requestStruct)], ifName.c_str(),
+                ifName.length());
+
+    size_t dataLen = request.size();
+    std::uint8_t reply[MAX_IPMI_BUFFER];
+
+    std::string expectedPath = buildPath(ifName, "rx_bytes");
+
+    HandlerMock hMock;
+    EXPECT_CALL(hMock, validIfNameAndField(StrEq(expectedPath)))
+        .WillOnce(Return(true));
+    EXPECT_CALL(hMock, readStatistic(StrEq(expectedPath))).WillOnce(Return(1));
+
+    EXPECT_EQ(IPMI_CC_OK,
+              handleEthStatCommand(request.data(), reply, &dataLen, &hMock));
+
+    struct EthStatReply expectedReply, realReply;
+    expectedReply.statId = EthernetStatisticsIds::RX_BYTES;
+    expectedReply.value = 1;
+
+    std::memcpy(&realReply, reply, sizeof(realReply));
+    EXPECT_EQ(0, std::memcmp(&expectedReply, &realReply, sizeof(realReply)));
+}
+
+} // namespace ethstats