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