blob: c272f96f116229583ec13bd8e24af076a878243b [file] [log] [blame]
#include "modbus/modbus.hpp"
#include "modbus_server_tester.hpp"
#include <fcntl.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using namespace std::literals;
namespace RTUIntf = phosphor::modbus::rtu;
using ModbusIntf = RTUIntf::Modbus;
namespace TestIntf = phosphor::modbus::test;
class ModbusTest : public ::testing::Test
{
public:
static constexpr const char* clientDevicePath = "/tmp/ttyV0";
static constexpr const char* serverDevicePath = "/tmp/ttyV1";
static constexpr const auto defaultBaudeRate = "b115200";
int socat_pid = -1;
sdbusplus::async::context ctx;
std::unique_ptr<ModbusIntf> modbus;
int fdClient = -1;
std::unique_ptr<TestIntf::ServerTester> serverTester;
int fdServer = -1;
ModbusTest()
{
std::string socatCmd = std::format(
"socat -x -v -d -d pty,link={},rawer,echo=0,parenb,{} pty,link={},rawer,echo=0,parenb,{} & echo $!",
serverDevicePath, defaultBaudeRate, clientDevicePath,
defaultBaudeRate);
// Start socat in the background and capture its PID
FILE* fp = popen(socatCmd.c_str(), "r");
EXPECT_NE(fp, nullptr) << "Failed to start socat: " << strerror(errno);
EXPECT_GT(fscanf(fp, "%d", &socat_pid), 0);
pclose(fp);
// Wait for socat to start up
sleep(1);
fdClient = open(clientDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
EXPECT_NE(fdClient, -1)
<< "Failed to open serial port " << clientDevicePath
<< " with error: " << strerror(errno);
modbus = std::make_unique<ModbusIntf>(ctx, fdClient, 115200, 0);
fdServer = open(serverDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
EXPECT_NE(fdServer, -1)
<< "Failed to open serial port " << serverDevicePath
<< " with error: " << strerror(errno);
serverTester = std::make_unique<TestIntf::ServerTester>(ctx, fdServer);
}
~ModbusTest() noexcept override
{
if (fdClient != -1)
{
close(fdClient);
fdClient = -1;
}
if (fdServer != -1)
{
close(fdServer);
fdServer = -1;
}
kill(socat_pid, SIGTERM);
}
void SetUp() override
{
ctx.spawn(serverTester->processRequests());
}
auto TestHoldingRegisters(uint16_t registerOffset, bool res)
-> sdbusplus::async::task<void>
{
std::cout << "TestHoldingRegisters() start" << std::endl;
std::vector<uint16_t> registers(
TestIntf::testSuccessReadHoldingRegisterCount);
auto ret = co_await modbus->readHoldingRegisters(
TestIntf::testDeviceAddress, registerOffset, registers);
EXPECT_EQ(ret, res) << "Failed to read holding registers";
if (!res)
{
co_return;
}
for (auto i = 0; i < TestIntf::testSuccessReadHoldingRegisterCount; i++)
{
EXPECT_EQ(registers[i],
TestIntf::testSuccessReadHoldingRegisterResponse[i]);
}
co_return;
}
};
TEST_F(ModbusTest, TestReadHoldingRegisterSuccess)
{
ctx.spawn(TestHoldingRegisters(
TestIntf::testSuccessReadHoldingRegisterOffset, true));
ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
ctx.run();
}
TEST_F(ModbusTest, TestReadHoldingRegisterSegmentedSuccess)
{
ctx.spawn(TestHoldingRegisters(
TestIntf::testSuccessReadHoldingRegisterSegmentedOffset, true));
ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
ctx.run();
}
TEST_F(ModbusTest, TestReadHoldingRegisterFailure)
{
ctx.spawn(
TestHoldingRegisters(TestIntf::testFailureReadHoldingRegister, false));
ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
ctx.run();
}