xref: /openbmc/phosphor-modbus/tests/test_port.cpp (revision 7f9d41ddfad8c74d51fa1cf4a591b7f085254396)
1*7f9d41ddSJagpal Singh Gill #include "modbus_server_tester.hpp"
2*7f9d41ddSJagpal Singh Gill #include "port/base_port.hpp"
3*7f9d41ddSJagpal Singh Gill 
4*7f9d41ddSJagpal Singh Gill #include <fcntl.h>
5*7f9d41ddSJagpal Singh Gill 
6*7f9d41ddSJagpal Singh Gill #include <gtest/gtest.h>
7*7f9d41ddSJagpal Singh Gill 
8*7f9d41ddSJagpal Singh Gill using namespace std::literals;
9*7f9d41ddSJagpal Singh Gill 
10*7f9d41ddSJagpal Singh Gill namespace TestIntf = phosphor::modbus::test;
11*7f9d41ddSJagpal Singh Gill namespace PortIntf = phosphor::modbus::rtu::port;
12*7f9d41ddSJagpal Singh Gill namespace PortConfigIntf = PortIntf::config;
13*7f9d41ddSJagpal Singh Gill namespace RTUIntf = phosphor::modbus::rtu;
14*7f9d41ddSJagpal Singh Gill 
15*7f9d41ddSJagpal Singh Gill struct properties_t
16*7f9d41ddSJagpal Singh Gill {
17*7f9d41ddSJagpal Singh Gill     std::string name = {};
18*7f9d41ddSJagpal Singh Gill     std::string mode = {};
19*7f9d41ddSJagpal Singh Gill     uint64_t baud_rate = {};
20*7f9d41ddSJagpal Singh Gill     uint64_t rts_delay = {};
21*7f9d41ddSJagpal Singh Gill };
22*7f9d41ddSJagpal Singh Gill 
23*7f9d41ddSJagpal Singh Gill class MockPort : public PortIntf::BasePort
24*7f9d41ddSJagpal Singh Gill {
25*7f9d41ddSJagpal Singh Gill   public:
26*7f9d41ddSJagpal Singh Gill     MockPort(sdbusplus::async::context& ctx,
27*7f9d41ddSJagpal Singh Gill              const PortConfigIntf::Config& config,
28*7f9d41ddSJagpal Singh Gill              const std::string& devicePath) : BasePort(ctx, config, devicePath)
29*7f9d41ddSJagpal Singh Gill     {}
30*7f9d41ddSJagpal Singh Gill };
31*7f9d41ddSJagpal Singh Gill 
32*7f9d41ddSJagpal Singh Gill class PortTest : public ::testing::Test
33*7f9d41ddSJagpal Singh Gill {
34*7f9d41ddSJagpal Singh Gill   public:
35*7f9d41ddSJagpal Singh Gill     static constexpr properties_t properties = {"TestPort", "RS485", 115200, 1};
36*7f9d41ddSJagpal Singh Gill     static constexpr const char* clientDevicePath = "/tmp/ttyPortV0";
37*7f9d41ddSJagpal Singh Gill     static constexpr const char* serverDevicePath = "/tmp/ttyPortV1";
38*7f9d41ddSJagpal Singh Gill     static constexpr const auto defaultBaudeRate = "b115200";
39*7f9d41ddSJagpal Singh Gill     int socat_pid = -1;
40*7f9d41ddSJagpal Singh Gill     sdbusplus::async::context ctx;
41*7f9d41ddSJagpal Singh Gill     int fdClient = -1;
42*7f9d41ddSJagpal Singh Gill     std::unique_ptr<TestIntf::ServerTester> serverTester;
43*7f9d41ddSJagpal Singh Gill     int fdServer = -1;
44*7f9d41ddSJagpal Singh Gill 
45*7f9d41ddSJagpal Singh Gill     PortTest()
46*7f9d41ddSJagpal Singh Gill     {
47*7f9d41ddSJagpal Singh Gill         std::string socatCmd = std::format(
48*7f9d41ddSJagpal Singh Gill             "socat -x -v -d -d pty,link={},rawer,echo=0,parenb,{} pty,link={},rawer,echo=0,parenb,{} & echo $!",
49*7f9d41ddSJagpal Singh Gill             serverDevicePath, defaultBaudeRate, clientDevicePath,
50*7f9d41ddSJagpal Singh Gill             defaultBaudeRate);
51*7f9d41ddSJagpal Singh Gill 
52*7f9d41ddSJagpal Singh Gill         // Start socat in the background and capture its PID
53*7f9d41ddSJagpal Singh Gill         FILE* fp = popen(socatCmd.c_str(), "r");
54*7f9d41ddSJagpal Singh Gill         EXPECT_NE(fp, nullptr) << "Failed to start socat: " << strerror(errno);
55*7f9d41ddSJagpal Singh Gill         EXPECT_GT(fscanf(fp, "%d", &socat_pid), 0);
56*7f9d41ddSJagpal Singh Gill         pclose(fp);
57*7f9d41ddSJagpal Singh Gill 
58*7f9d41ddSJagpal Singh Gill         // Wait for socat to start up
59*7f9d41ddSJagpal Singh Gill         sleep(1);
60*7f9d41ddSJagpal Singh Gill 
61*7f9d41ddSJagpal Singh Gill         fdClient = open(clientDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
62*7f9d41ddSJagpal Singh Gill         EXPECT_NE(fdClient, -1)
63*7f9d41ddSJagpal Singh Gill             << "Failed to open serial port " << clientDevicePath
64*7f9d41ddSJagpal Singh Gill             << " with error: " << strerror(errno);
65*7f9d41ddSJagpal Singh Gill 
66*7f9d41ddSJagpal Singh Gill         fdServer = open(serverDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
67*7f9d41ddSJagpal Singh Gill         EXPECT_NE(fdServer, -1)
68*7f9d41ddSJagpal Singh Gill             << "Failed to open serial port " << serverDevicePath
69*7f9d41ddSJagpal Singh Gill             << " with error: " << strerror(errno);
70*7f9d41ddSJagpal Singh Gill 
71*7f9d41ddSJagpal Singh Gill         serverTester = std::make_unique<TestIntf::ServerTester>(ctx, fdServer);
72*7f9d41ddSJagpal Singh Gill     }
73*7f9d41ddSJagpal Singh Gill 
74*7f9d41ddSJagpal Singh Gill     ~PortTest() noexcept override
75*7f9d41ddSJagpal Singh Gill     {
76*7f9d41ddSJagpal Singh Gill         if (fdClient != -1)
77*7f9d41ddSJagpal Singh Gill         {
78*7f9d41ddSJagpal Singh Gill             close(fdClient);
79*7f9d41ddSJagpal Singh Gill             fdClient = -1;
80*7f9d41ddSJagpal Singh Gill         }
81*7f9d41ddSJagpal Singh Gill         if (fdServer != -1)
82*7f9d41ddSJagpal Singh Gill         {
83*7f9d41ddSJagpal Singh Gill             close(fdServer);
84*7f9d41ddSJagpal Singh Gill             fdServer = -1;
85*7f9d41ddSJagpal Singh Gill         }
86*7f9d41ddSJagpal Singh Gill         kill(socat_pid, SIGTERM);
87*7f9d41ddSJagpal Singh Gill     }
88*7f9d41ddSJagpal Singh Gill 
89*7f9d41ddSJagpal Singh Gill     auto TestHoldingRegisters(PortConfigIntf::Config& config, MockPort& port,
90*7f9d41ddSJagpal Singh Gill                               uint16_t registerOffset, bool res)
91*7f9d41ddSJagpal Singh Gill         -> sdbusplus::async::task<void>
92*7f9d41ddSJagpal Singh Gill     {
93*7f9d41ddSJagpal Singh Gill         std::vector<uint16_t> registers(
94*7f9d41ddSJagpal Singh Gill             TestIntf::testSuccessReadHoldingRegisterCount);
95*7f9d41ddSJagpal Singh Gill 
96*7f9d41ddSJagpal Singh Gill         auto ret = co_await port.readHoldingRegisters(
97*7f9d41ddSJagpal Singh Gill             TestIntf::testDeviceAddress, registerOffset, config.baudRate,
98*7f9d41ddSJagpal Singh Gill             RTUIntf::Parity::none, registers);
99*7f9d41ddSJagpal Singh Gill 
100*7f9d41ddSJagpal Singh Gill         EXPECT_EQ(ret, res) << "Failed to read holding registers";
101*7f9d41ddSJagpal Singh Gill 
102*7f9d41ddSJagpal Singh Gill         if (!res)
103*7f9d41ddSJagpal Singh Gill         {
104*7f9d41ddSJagpal Singh Gill             co_return;
105*7f9d41ddSJagpal Singh Gill         }
106*7f9d41ddSJagpal Singh Gill 
107*7f9d41ddSJagpal Singh Gill         for (auto i = 0; i < TestIntf::testSuccessReadHoldingRegisterCount; i++)
108*7f9d41ddSJagpal Singh Gill         {
109*7f9d41ddSJagpal Singh Gill             EXPECT_EQ(registers[i],
110*7f9d41ddSJagpal Singh Gill                       TestIntf::testSuccessReadHoldingRegisterResponse[i]);
111*7f9d41ddSJagpal Singh Gill         }
112*7f9d41ddSJagpal Singh Gill 
113*7f9d41ddSJagpal Singh Gill         co_return;
114*7f9d41ddSJagpal Singh Gill     }
115*7f9d41ddSJagpal Singh Gill };
116*7f9d41ddSJagpal Singh Gill 
117*7f9d41ddSJagpal Singh Gill TEST_F(PortTest, TestUpdateConfig)
118*7f9d41ddSJagpal Singh Gill {
119*7f9d41ddSJagpal Singh Gill     PortConfigIntf::Config config = {};
120*7f9d41ddSJagpal Singh Gill     auto res = PortConfigIntf::updateBaseConfig(config, properties);
121*7f9d41ddSJagpal Singh Gill     EXPECT_TRUE(res) << "Failed to update config";
122*7f9d41ddSJagpal Singh Gill 
123*7f9d41ddSJagpal Singh Gill     EXPECT_EQ(config.name, properties.name);
124*7f9d41ddSJagpal Singh Gill     EXPECT_EQ(config.portMode, PortConfigIntf::PortMode::rs485);
125*7f9d41ddSJagpal Singh Gill     EXPECT_EQ(config.baudRate, properties.baud_rate);
126*7f9d41ddSJagpal Singh Gill     EXPECT_EQ(config.rtsDelay, properties.rts_delay);
127*7f9d41ddSJagpal Singh Gill }
128*7f9d41ddSJagpal Singh Gill 
129*7f9d41ddSJagpal Singh Gill TEST_F(PortTest, TestReadHoldingRegisterSuccess)
130*7f9d41ddSJagpal Singh Gill {
131*7f9d41ddSJagpal Singh Gill     PortConfigIntf::Config config = {};
132*7f9d41ddSJagpal Singh Gill     auto res = PortConfigIntf::updateBaseConfig(config, properties);
133*7f9d41ddSJagpal Singh Gill     EXPECT_TRUE(res) << "Failed to update config";
134*7f9d41ddSJagpal Singh Gill 
135*7f9d41ddSJagpal Singh Gill     MockPort port(ctx, config, clientDevicePath);
136*7f9d41ddSJagpal Singh Gill 
137*7f9d41ddSJagpal Singh Gill     ctx.spawn(serverTester->processRequests());
138*7f9d41ddSJagpal Singh Gill 
139*7f9d41ddSJagpal Singh Gill     ctx.spawn(TestHoldingRegisters(
140*7f9d41ddSJagpal Singh Gill         config, port, TestIntf::testSuccessReadHoldingRegisterOffset, true));
141*7f9d41ddSJagpal Singh Gill 
142*7f9d41ddSJagpal Singh Gill     ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
143*7f9d41ddSJagpal Singh Gill               sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
144*7f9d41ddSJagpal Singh Gill 
145*7f9d41ddSJagpal Singh Gill     ctx.run();
146*7f9d41ddSJagpal Singh Gill }
147*7f9d41ddSJagpal Singh Gill 
148*7f9d41ddSJagpal Singh Gill TEST_F(PortTest, TestReadHoldingRegisterFailure)
149*7f9d41ddSJagpal Singh Gill {
150*7f9d41ddSJagpal Singh Gill     PortConfigIntf::Config config = {};
151*7f9d41ddSJagpal Singh Gill     auto res = PortConfigIntf::updateBaseConfig(config, properties);
152*7f9d41ddSJagpal Singh Gill     EXPECT_TRUE(res) << "Failed to update config";
153*7f9d41ddSJagpal Singh Gill 
154*7f9d41ddSJagpal Singh Gill     MockPort port(ctx, config, clientDevicePath);
155*7f9d41ddSJagpal Singh Gill 
156*7f9d41ddSJagpal Singh Gill     ctx.spawn(serverTester->processRequests());
157*7f9d41ddSJagpal Singh Gill 
158*7f9d41ddSJagpal Singh Gill     ctx.spawn(TestHoldingRegisters(
159*7f9d41ddSJagpal Singh Gill         config, port, TestIntf::testFailureReadHoldingRegister, false));
160*7f9d41ddSJagpal Singh Gill 
161*7f9d41ddSJagpal Singh Gill     ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
162*7f9d41ddSJagpal Singh Gill               sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
163*7f9d41ddSJagpal Singh Gill 
164*7f9d41ddSJagpal Singh Gill     ctx.run();
165*7f9d41ddSJagpal Singh Gill }
166