Add IPMI interface for CustomAccel service

Change-Id: I28a8976e382b457233ac521e9ab71f75abe029d1
Signed-off-by: Steve Foreman <foremans@google.com>
diff --git a/test/google_accel_oob_unittest.cpp b/test/google_accel_oob_unittest.cpp
new file mode 100644
index 0000000..163b602
--- /dev/null
+++ b/test/google_accel_oob_unittest.cpp
@@ -0,0 +1,239 @@
+// Copyright 2022 Google LLC
+//
+// 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 "commands.hpp"
+#include "errors.hpp"
+#include "google_accel_oob.hpp"
+#include "handler_mock.hpp"
+
+#include <ipmid/api.h>
+
+#include <gtest/gtest.h>
+
+namespace google
+{
+namespace ipmi
+{
+
+using ::testing::Return;
+
+TEST(GoogleAccelOobTest, DeviceCount_Success)
+{
+    ::testing::StrictMock<HandlerMock> h;
+
+    uint8_t reqBuf[1]; // Could be 0, but zero-length arrays are an extension
+
+    struct Reply
+    {
+        uint32_t count;
+    } __attribute__((packed));
+
+    constexpr uint32_t kTestDeviceCount = 2;
+
+    EXPECT_CALL(h, accelOobDeviceCount()).WillOnce(Return(kTestDeviceCount));
+
+    Resp r = accelOobDeviceCount(reqBuf, &h);
+
+    const auto response = std::get<0>(r);
+    EXPECT_EQ(response, IPMI_CC_OK);
+
+    const auto payload = std::get<1>(r);
+    ASSERT_EQ(payload.has_value(), true);
+    const auto payload_tuple = payload.value();
+    const auto reply_cmd = std::get<0>(payload_tuple);
+    EXPECT_EQ(reply_cmd, SysAccelOobDeviceCount);
+    const auto reply_buff = std::get<1>(payload_tuple);
+    ASSERT_EQ(reply_buff.size(), sizeof(Reply));
+
+    auto* reply = reinterpret_cast<const Reply*>(reply_buff.data());
+    EXPECT_EQ(reply->count, kTestDeviceCount);
+}
+
+TEST(GoogleAccelOobTest, DeviceName_Success)
+{
+    ::testing::StrictMock<HandlerMock> h;
+
+    struct Request
+    {
+        uint32_t index;
+    } __attribute__((packed));
+
+    struct Reply
+    {
+        uint32_t index;
+        uint8_t length;
+        char name[1];
+    } __attribute__((packed));
+
+    constexpr uint32_t kTestDeviceIndex = 0;
+    const std::string kTestDeviceName("testDeviceName");
+
+    EXPECT_CALL(h, accelOobDeviceName(kTestDeviceIndex))
+        .WillOnce(Return(kTestDeviceName));
+
+    Request reqBuf{kTestDeviceIndex};
+    Resp r = accelOobDeviceName(
+        std::span(reinterpret_cast<const uint8_t*>(&reqBuf), sizeof(Request)),
+        &h);
+
+    const auto response = std::get<0>(r);
+    EXPECT_EQ(response, IPMI_CC_OK);
+
+    const auto payload = std::get<1>(r);
+    ASSERT_EQ(payload.has_value(), true);
+    const auto payload_tuple = payload.value();
+    const auto reply_cmd = std::get<0>(payload_tuple);
+    EXPECT_EQ(reply_cmd, SysAccelOobDeviceName);
+    const auto reply_buff = std::get<1>(payload_tuple);
+    ASSERT_GE(reply_buff.size(), sizeof(Reply));
+
+    auto* reply = reinterpret_cast<const Reply*>(reply_buff.data());
+    EXPECT_EQ(reply->index, kTestDeviceIndex);
+    EXPECT_EQ(reply->length, kTestDeviceName.length());
+    EXPECT_STREQ(reply->name, kTestDeviceName.c_str());
+}
+
+TEST(GoogleAccelOobTest, Read_Success)
+{
+    ::testing::StrictMock<HandlerMock> h;
+
+    constexpr char kTestDeviceName[] = "testDeviceName";
+    constexpr uint8_t kTestDeviceNameLength =
+        (sizeof(kTestDeviceName) / sizeof(*kTestDeviceName)) - 1;
+    constexpr uint8_t kTestToken = 0xAB;
+    constexpr uint64_t kTestAddress = 0;
+    constexpr uint8_t kTestReadSize = 8;
+    constexpr uint64_t kTestData = 0x12345678;
+
+    struct Request
+    {
+        uint8_t nameLength;
+        char name[kTestDeviceNameLength];
+        uint8_t token;
+        uint64_t address;
+        uint8_t num_bytes;
+    } __attribute__((packed));
+
+    struct Reply
+    {
+        uint8_t nameLength;
+        char name[kTestDeviceNameLength];
+        uint8_t token;
+        uint64_t address;
+        uint8_t num_bytes;
+        uint64_t data;
+    } __attribute__((packed));
+
+    const std::string_view kTestDeviceNameStr(kTestDeviceName,
+                                              kTestDeviceNameLength);
+    EXPECT_CALL(h,
+                accelOobRead(kTestDeviceNameStr, kTestAddress, kTestReadSize))
+        .WillOnce(Return(kTestData));
+
+    Request reqBuf{kTestDeviceNameLength, "", kTestToken, kTestAddress,
+                   kTestReadSize};
+    memcpy(reqBuf.name, kTestDeviceName, kTestDeviceNameLength);
+    Resp r = accelOobRead(
+        std::span(reinterpret_cast<const uint8_t*>(&reqBuf), sizeof(Request)),
+        &h);
+
+    const auto response = std::get<0>(r);
+    EXPECT_EQ(response, IPMI_CC_OK);
+
+    const auto payload = std::get<1>(r);
+    ASSERT_EQ(payload.has_value(), true);
+    const auto payload_tuple = payload.value();
+    const auto reply_cmd = std::get<0>(payload_tuple);
+    EXPECT_EQ(reply_cmd, SysAccelOobRead);
+    const auto reply_buff = std::get<1>(payload_tuple);
+    ASSERT_GE(reply_buff.size(), sizeof(Reply));
+
+    auto* reply = reinterpret_cast<const Reply*>(reply_buff.data());
+    EXPECT_EQ(reply->nameLength, kTestDeviceNameLength);
+    EXPECT_EQ(std::string_view(reply->name, reply->nameLength),
+              kTestDeviceNameStr);
+    EXPECT_EQ(reply->token, kTestToken);
+    EXPECT_EQ(reply->address, kTestAddress);
+    EXPECT_EQ(reply->num_bytes, kTestReadSize);
+    EXPECT_EQ(reply->data, kTestData);
+}
+
+TEST(GoogleAccelOobTest, Write_Success)
+{
+    ::testing::StrictMock<HandlerMock> h;
+
+    constexpr char kTestDeviceName[] = "testDeviceName";
+    constexpr uint8_t kTestDeviceNameLength =
+        (sizeof(kTestDeviceName) / sizeof(*kTestDeviceName)) - 1;
+    constexpr uint8_t kTestToken = 0xAB;
+    constexpr uint64_t kTestAddress = 0;
+    constexpr uint8_t kTestWriteSize = 8;
+    constexpr uint64_t kTestData = 0x12345678;
+
+    struct Request
+    {
+        uint8_t nameLength;
+        char name[kTestDeviceNameLength];
+        uint8_t token;
+        uint64_t address;
+        uint8_t num_bytes;
+        uint64_t data;
+    } __attribute__((packed));
+
+    struct Reply
+    {
+        uint8_t nameLength;
+        char name[kTestDeviceNameLength];
+        uint8_t token;
+        uint64_t address;
+        uint8_t num_bytes;
+        uint64_t data;
+    } __attribute__((packed));
+
+    const std::string_view kTestDeviceNameStr(kTestDeviceName,
+                                              kTestDeviceNameLength);
+    EXPECT_CALL(h, accelOobWrite(kTestDeviceNameStr, kTestAddress,
+                                 kTestWriteSize, kTestData))
+        .WillOnce(Return());
+
+    Request reqBuf{kTestDeviceNameLength, "",       kTestToken, kTestAddress,
+                   kTestWriteSize,        kTestData};
+    memcpy(reqBuf.name, kTestDeviceName, kTestDeviceNameLength);
+    Resp r = accelOobWrite(
+        std::span(reinterpret_cast<const uint8_t*>(&reqBuf), sizeof(Request)),
+        &h);
+
+    const auto response = std::get<0>(r);
+    EXPECT_EQ(response, IPMI_CC_OK);
+
+    const auto payload = std::get<1>(r);
+    ASSERT_EQ(payload.has_value(), true);
+    const auto payload_tuple = payload.value();
+    const auto reply_cmd = std::get<0>(payload_tuple);
+    EXPECT_EQ(reply_cmd, SysAccelOobWrite);
+    const auto reply_buff = std::get<1>(payload_tuple);
+    ASSERT_GE(reply_buff.size(), sizeof(Reply));
+
+    auto* reply = reinterpret_cast<const Reply*>(reply_buff.data());
+    EXPECT_EQ(reply->nameLength, kTestDeviceNameLength);
+    EXPECT_EQ(std::string_view(reply->name, reply->nameLength),
+              kTestDeviceNameStr);
+    EXPECT_EQ(reply->token, kTestToken);
+    EXPECT_EQ(reply->address, kTestAddress);
+    EXPECT_EQ(reply->num_bytes, kTestWriteSize);
+    EXPECT_EQ(reply->data, kTestData);
+}
+
+} // namespace ipmi
+} // namespace google