xref: /openbmc/phosphor-modbus/tests/test_modbus.cpp (revision a32d241b08af236b5ea1d4e18a390bdc561b435b)
1 #include "modbus/modbus.hpp"
2 #include "modbus_server_tester.hpp"
3 
4 #include <fcntl.h>
5 
6 #include <gmock/gmock.h>
7 #include <gtest/gtest.h>
8 
9 using namespace std::literals;
10 
11 namespace RTUIntf = phosphor::modbus::rtu;
12 using ModbusIntf = RTUIntf::Modbus;
13 namespace TestIntf = phosphor::modbus::test;
14 
15 class ModbusTest : public ::testing::Test
16 {
17   public:
18     static constexpr const char* clientDevicePath = "/tmp/ttyV0";
19     static constexpr const char* serverDevicePath = "/tmp/ttyV1";
20     static constexpr const auto defaultBaudeRate = "b115200";
21     int socat_pid = -1;
22     sdbusplus::async::context ctx;
23     std::unique_ptr<ModbusIntf> modbus;
24     int fdClient = -1;
25     std::unique_ptr<TestIntf::ServerTester> serverTester;
26     int fdServer = -1;
27 
ModbusTest()28     ModbusTest()
29     {
30         std::string socatCmd = std::format(
31             "socat -x -v -d -d pty,link={},rawer,echo=0,parenb,{} pty,link={},rawer,echo=0,parenb,{} & echo $!",
32             serverDevicePath, defaultBaudeRate, clientDevicePath,
33             defaultBaudeRate);
34 
35         // Start socat in the background and capture its PID
36         FILE* fp = popen(socatCmd.c_str(), "r");
37         EXPECT_NE(fp, nullptr) << "Failed to start socat: " << strerror(errno);
38         EXPECT_GT(fscanf(fp, "%d", &socat_pid), 0);
39         pclose(fp);
40 
41         // Wait for socat to start up
42         sleep(1);
43 
44         fdClient = open(clientDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
45         EXPECT_NE(fdClient, -1)
46             << "Failed to open serial port " << clientDevicePath
47             << " with error: " << strerror(errno);
48 
49         modbus = std::make_unique<ModbusIntf>(ctx, fdClient, 115200, 0);
50 
51         fdServer = open(serverDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
52         EXPECT_NE(fdServer, -1)
53             << "Failed to open serial port " << serverDevicePath
54             << " with error: " << strerror(errno);
55 
56         serverTester = std::make_unique<TestIntf::ServerTester>(ctx, fdServer);
57     }
58 
~ModbusTest()59     ~ModbusTest() noexcept override
60     {
61         if (fdClient != -1)
62         {
63             close(fdClient);
64             fdClient = -1;
65         }
66         if (fdServer != -1)
67         {
68             close(fdServer);
69             fdServer = -1;
70         }
71         kill(socat_pid, SIGTERM);
72     }
73 
SetUp()74     void SetUp() override
75     {
76         ctx.spawn(serverTester->processRequests());
77     }
78 
TestHoldingRegisters(uint16_t registerOffset,bool res)79     auto TestHoldingRegisters(uint16_t registerOffset, bool res)
80         -> sdbusplus::async::task<void>
81     {
82         std::cout << "TestHoldingRegisters() start" << std::endl;
83 
84         std::vector<uint16_t> registers(
85             TestIntf::testSuccessReadHoldingRegisterCount);
86 
87         auto ret = co_await modbus->readHoldingRegisters(
88             TestIntf::testDeviceAddress, registerOffset, registers);
89 
90         EXPECT_EQ(ret, res) << "Failed to read holding registers";
91 
92         if (!res)
93         {
94             co_return;
95         }
96 
97         for (auto i = 0; i < TestIntf::testSuccessReadHoldingRegisterCount; i++)
98         {
99             EXPECT_EQ(registers[i],
100                       TestIntf::testSuccessReadHoldingRegisterResponse[i]);
101         }
102 
103         co_return;
104     }
105 };
106 
TEST_F(ModbusTest,TestReadHoldingRegisterSuccess)107 TEST_F(ModbusTest, TestReadHoldingRegisterSuccess)
108 {
109     ctx.spawn(TestHoldingRegisters(
110         TestIntf::testSuccessReadHoldingRegisterOffset, true));
111 
112     ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
113               sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
114 
115     ctx.run();
116 }
117 
TEST_F(ModbusTest,TestReadHoldingRegisterSegmentedSuccess)118 TEST_F(ModbusTest, TestReadHoldingRegisterSegmentedSuccess)
119 {
120     ctx.spawn(TestHoldingRegisters(
121         TestIntf::testSuccessReadHoldingRegisterSegmentedOffset, true));
122 
123     ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
124               sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
125 
126     ctx.run();
127 }
128 
TEST_F(ModbusTest,TestReadHoldingRegisterFailure)129 TEST_F(ModbusTest, TestReadHoldingRegisterFailure)
130 {
131     ctx.spawn(
132         TestHoldingRegisters(TestIntf::testFailureReadHoldingRegister, false));
133 
134     ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
135               sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
136 
137     ctx.run();
138 }
139