xref: /openbmc/phosphor-modbus/tests/test_port.cpp (revision b62e3dfe5505fdb8c2ab96156b62e3bfb878b7a6)
1*b62e3dfeSJagpal Singh Gill #include "common/entity_manager_interface.hpp"
27f9d41ddSJagpal Singh Gill #include "modbus_server_tester.hpp"
3*b62e3dfeSJagpal Singh Gill #include "port/port_factory.hpp"
47f9d41ddSJagpal Singh Gill 
57f9d41ddSJagpal Singh Gill #include <fcntl.h>
67f9d41ddSJagpal Singh Gill 
7*b62e3dfeSJagpal Singh Gill #include <xyz/openbmc_project/Configuration/USBPort/aserver.hpp>
8*b62e3dfeSJagpal Singh Gill #include <xyz/openbmc_project/Inventory/Item/client.hpp>
9*b62e3dfeSJagpal Singh Gill 
107f9d41ddSJagpal Singh Gill #include <gtest/gtest.h>
117f9d41ddSJagpal Singh Gill 
127f9d41ddSJagpal Singh Gill using namespace std::literals;
137f9d41ddSJagpal Singh Gill 
14*b62e3dfeSJagpal Singh Gill class PortTest;
15*b62e3dfeSJagpal Singh Gill 
167f9d41ddSJagpal Singh Gill namespace TestIntf = phosphor::modbus::test;
177f9d41ddSJagpal Singh Gill namespace PortIntf = phosphor::modbus::rtu::port;
187f9d41ddSJagpal Singh Gill namespace PortConfigIntf = PortIntf::config;
197f9d41ddSJagpal Singh Gill namespace RTUIntf = phosphor::modbus::rtu;
20*b62e3dfeSJagpal Singh Gill using PortFactoryIntf = PortIntf::PortFactory;
21*b62e3dfeSJagpal Singh Gill using USBPortConfigServerIntf =
22*b62e3dfeSJagpal Singh Gill     sdbusplus::aserver::xyz::openbmc_project::configuration::USBPort<PortTest>;
237f9d41ddSJagpal Singh Gill 
247f9d41ddSJagpal Singh Gill struct properties_t
257f9d41ddSJagpal Singh Gill {
267f9d41ddSJagpal Singh Gill     std::string name = {};
277f9d41ddSJagpal Singh Gill     std::string mode = {};
287f9d41ddSJagpal Singh Gill     uint64_t baud_rate = {};
297f9d41ddSJagpal Singh Gill     uint64_t rts_delay = {};
307f9d41ddSJagpal Singh Gill };
317f9d41ddSJagpal Singh Gill 
327f9d41ddSJagpal Singh Gill class MockPort : public PortIntf::BasePort
337f9d41ddSJagpal Singh Gill {
347f9d41ddSJagpal Singh Gill   public:
MockPort(sdbusplus::async::context & ctx,const PortConfigIntf::Config & config,const std::string & devicePath)357f9d41ddSJagpal Singh Gill     MockPort(sdbusplus::async::context& ctx,
367f9d41ddSJagpal Singh Gill              const PortConfigIntf::Config& config,
377f9d41ddSJagpal Singh Gill              const std::string& devicePath) : BasePort(ctx, config, devicePath)
387f9d41ddSJagpal Singh Gill     {}
397f9d41ddSJagpal Singh Gill };
407f9d41ddSJagpal Singh Gill 
417f9d41ddSJagpal Singh Gill class PortTest : public ::testing::Test
427f9d41ddSJagpal Singh Gill {
437f9d41ddSJagpal Singh Gill   public:
447f9d41ddSJagpal Singh Gill     static constexpr properties_t properties = {"TestPort", "RS485", 115200, 1};
457f9d41ddSJagpal Singh Gill     static constexpr const char* clientDevicePath = "/tmp/ttyPortV0";
467f9d41ddSJagpal Singh Gill     static constexpr const char* serverDevicePath = "/tmp/ttyPortV1";
477f9d41ddSJagpal Singh Gill     static constexpr const auto defaultBaudeRate = "b115200";
487f9d41ddSJagpal Singh Gill     int socat_pid = -1;
497f9d41ddSJagpal Singh Gill     sdbusplus::async::context ctx;
507f9d41ddSJagpal Singh Gill     int fdClient = -1;
517f9d41ddSJagpal Singh Gill     std::unique_ptr<TestIntf::ServerTester> serverTester;
527f9d41ddSJagpal Singh Gill     int fdServer = -1;
53*b62e3dfeSJagpal Singh Gill     bool getPortConfigPassed = false;
547f9d41ddSJagpal Singh Gill 
PortTest()557f9d41ddSJagpal Singh Gill     PortTest()
567f9d41ddSJagpal Singh Gill     {
577f9d41ddSJagpal Singh Gill         std::string socatCmd = std::format(
587f9d41ddSJagpal Singh Gill             "socat -x -v -d -d pty,link={},rawer,echo=0,parenb,{} pty,link={},rawer,echo=0,parenb,{} & echo $!",
597f9d41ddSJagpal Singh Gill             serverDevicePath, defaultBaudeRate, clientDevicePath,
607f9d41ddSJagpal Singh Gill             defaultBaudeRate);
617f9d41ddSJagpal Singh Gill 
627f9d41ddSJagpal Singh Gill         // Start socat in the background and capture its PID
637f9d41ddSJagpal Singh Gill         FILE* fp = popen(socatCmd.c_str(), "r");
647f9d41ddSJagpal Singh Gill         EXPECT_NE(fp, nullptr) << "Failed to start socat: " << strerror(errno);
657f9d41ddSJagpal Singh Gill         EXPECT_GT(fscanf(fp, "%d", &socat_pid), 0);
667f9d41ddSJagpal Singh Gill         pclose(fp);
677f9d41ddSJagpal Singh Gill 
687f9d41ddSJagpal Singh Gill         // Wait for socat to start up
697f9d41ddSJagpal Singh Gill         sleep(1);
707f9d41ddSJagpal Singh Gill 
717f9d41ddSJagpal Singh Gill         fdClient = open(clientDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
727f9d41ddSJagpal Singh Gill         EXPECT_NE(fdClient, -1)
737f9d41ddSJagpal Singh Gill             << "Failed to open serial port " << clientDevicePath
747f9d41ddSJagpal Singh Gill             << " with error: " << strerror(errno);
757f9d41ddSJagpal Singh Gill 
767f9d41ddSJagpal Singh Gill         fdServer = open(serverDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
777f9d41ddSJagpal Singh Gill         EXPECT_NE(fdServer, -1)
787f9d41ddSJagpal Singh Gill             << "Failed to open serial port " << serverDevicePath
797f9d41ddSJagpal Singh Gill             << " with error: " << strerror(errno);
807f9d41ddSJagpal Singh Gill 
817f9d41ddSJagpal Singh Gill         serverTester = std::make_unique<TestIntf::ServerTester>(ctx, fdServer);
827f9d41ddSJagpal Singh Gill     }
837f9d41ddSJagpal Singh Gill 
~PortTest()847f9d41ddSJagpal Singh Gill     ~PortTest() noexcept override
857f9d41ddSJagpal Singh Gill     {
867f9d41ddSJagpal Singh Gill         if (fdClient != -1)
877f9d41ddSJagpal Singh Gill         {
887f9d41ddSJagpal Singh Gill             close(fdClient);
897f9d41ddSJagpal Singh Gill             fdClient = -1;
907f9d41ddSJagpal Singh Gill         }
917f9d41ddSJagpal Singh Gill         if (fdServer != -1)
927f9d41ddSJagpal Singh Gill         {
937f9d41ddSJagpal Singh Gill             close(fdServer);
947f9d41ddSJagpal Singh Gill             fdServer = -1;
957f9d41ddSJagpal Singh Gill         }
967f9d41ddSJagpal Singh Gill         kill(socat_pid, SIGTERM);
977f9d41ddSJagpal Singh Gill     }
987f9d41ddSJagpal Singh Gill 
SetUp()99*b62e3dfeSJagpal Singh Gill     void SetUp() override
100*b62e3dfeSJagpal Singh Gill     {
101*b62e3dfeSJagpal Singh Gill         getPortConfigPassed = false;
102*b62e3dfeSJagpal Singh Gill     }
103*b62e3dfeSJagpal Singh Gill 
TestHoldingRegisters(PortConfigIntf::Config & config,MockPort & port,uint16_t registerOffset,bool res)1047f9d41ddSJagpal Singh Gill     auto TestHoldingRegisters(PortConfigIntf::Config& config, MockPort& port,
1057f9d41ddSJagpal Singh Gill                               uint16_t registerOffset, bool res)
1067f9d41ddSJagpal Singh Gill         -> sdbusplus::async::task<void>
1077f9d41ddSJagpal Singh Gill     {
1087f9d41ddSJagpal Singh Gill         std::vector<uint16_t> registers(
1097f9d41ddSJagpal Singh Gill             TestIntf::testSuccessReadHoldingRegisterCount);
1107f9d41ddSJagpal Singh Gill 
1117f9d41ddSJagpal Singh Gill         auto ret = co_await port.readHoldingRegisters(
1127f9d41ddSJagpal Singh Gill             TestIntf::testDeviceAddress, registerOffset, config.baudRate,
1137f9d41ddSJagpal Singh Gill             RTUIntf::Parity::none, registers);
1147f9d41ddSJagpal Singh Gill 
1157f9d41ddSJagpal Singh Gill         EXPECT_EQ(ret, res) << "Failed to read holding registers";
1167f9d41ddSJagpal Singh Gill 
1177f9d41ddSJagpal Singh Gill         if (!res)
1187f9d41ddSJagpal Singh Gill         {
1197f9d41ddSJagpal Singh Gill             co_return;
1207f9d41ddSJagpal Singh Gill         }
1217f9d41ddSJagpal Singh Gill 
1227f9d41ddSJagpal Singh Gill         for (auto i = 0; i < TestIntf::testSuccessReadHoldingRegisterCount; i++)
1237f9d41ddSJagpal Singh Gill         {
1247f9d41ddSJagpal Singh Gill             EXPECT_EQ(registers[i],
1257f9d41ddSJagpal Singh Gill                       TestIntf::testSuccessReadHoldingRegisterResponse[i]);
1267f9d41ddSJagpal Singh Gill         }
1277f9d41ddSJagpal Singh Gill 
1287f9d41ddSJagpal Singh Gill         co_return;
1297f9d41ddSJagpal Singh Gill     }
130*b62e3dfeSJagpal Singh Gill 
131*b62e3dfeSJagpal Singh Gill     template <typename Config, typename Properties>
VerifyConfig(const Config & config,const Properties & property)132*b62e3dfeSJagpal Singh Gill     static inline void VerifyConfig(const Config& config,
133*b62e3dfeSJagpal Singh Gill                                     const Properties& property)
134*b62e3dfeSJagpal Singh Gill     {
135*b62e3dfeSJagpal Singh Gill         EXPECT_EQ(config, property);
136*b62e3dfeSJagpal Singh Gill     }
137*b62e3dfeSJagpal Singh Gill 
TestGetUSBPortConfig(const USBPortConfigServerIntf::properties_t properties,bool shouldPass)138*b62e3dfeSJagpal Singh Gill     auto TestGetUSBPortConfig(
139*b62e3dfeSJagpal Singh Gill         const USBPortConfigServerIntf::properties_t properties, bool shouldPass)
140*b62e3dfeSJagpal Singh Gill         -> sdbusplus::async::task<void>
141*b62e3dfeSJagpal Singh Gill     {
142*b62e3dfeSJagpal Singh Gill         static constexpr auto objectPath =
143*b62e3dfeSJagpal Singh Gill             "/xyz/openbmc_project/inventory/system/board/Ventura_Modbus/DevTTYUSB0";
144*b62e3dfeSJagpal Singh Gill 
145*b62e3dfeSJagpal Singh Gill         auto configServer = std::make_unique<USBPortConfigServerIntf>(
146*b62e3dfeSJagpal Singh Gill             ctx, objectPath, properties);
147*b62e3dfeSJagpal Singh Gill 
148*b62e3dfeSJagpal Singh Gill         auto config = co_await PortFactoryIntf::getConfig(
149*b62e3dfeSJagpal Singh Gill             ctx, std::string(objectPath), USBPortConfigServerIntf::interface);
150*b62e3dfeSJagpal Singh Gill 
151*b62e3dfeSJagpal Singh Gill         if (!shouldPass)
152*b62e3dfeSJagpal Singh Gill         {
153*b62e3dfeSJagpal Singh Gill             VerifyConfig(config, nullptr);
154*b62e3dfeSJagpal Singh Gill             co_return;
155*b62e3dfeSJagpal Singh Gill         }
156*b62e3dfeSJagpal Singh Gill 
157*b62e3dfeSJagpal Singh Gill         VerifyConfig(config->name, properties.name);
158*b62e3dfeSJagpal Singh Gill         VerifyConfig(config->portMode, PortConfigIntf::PortMode::rs485);
159*b62e3dfeSJagpal Singh Gill         VerifyConfig(config->baudRate, properties.baud_rate);
160*b62e3dfeSJagpal Singh Gill         VerifyConfig(config->rtsDelay, properties.rts_delay);
161*b62e3dfeSJagpal Singh Gill 
162*b62e3dfeSJagpal Singh Gill         getPortConfigPassed = true;
163*b62e3dfeSJagpal Singh Gill 
164*b62e3dfeSJagpal Singh Gill         co_return;
165*b62e3dfeSJagpal Singh Gill     }
1667f9d41ddSJagpal Singh Gill };
1677f9d41ddSJagpal Singh Gill 
TEST_F(PortTest,TestUpdateConfig)1687f9d41ddSJagpal Singh Gill TEST_F(PortTest, TestUpdateConfig)
1697f9d41ddSJagpal Singh Gill {
1707f9d41ddSJagpal Singh Gill     PortConfigIntf::Config config = {};
1717f9d41ddSJagpal Singh Gill     auto res = PortConfigIntf::updateBaseConfig(config, properties);
1727f9d41ddSJagpal Singh Gill     EXPECT_TRUE(res) << "Failed to update config";
1737f9d41ddSJagpal Singh Gill 
1747f9d41ddSJagpal Singh Gill     EXPECT_EQ(config.name, properties.name);
1757f9d41ddSJagpal Singh Gill     EXPECT_EQ(config.portMode, PortConfigIntf::PortMode::rs485);
1767f9d41ddSJagpal Singh Gill     EXPECT_EQ(config.baudRate, properties.baud_rate);
1777f9d41ddSJagpal Singh Gill     EXPECT_EQ(config.rtsDelay, properties.rts_delay);
1787f9d41ddSJagpal Singh Gill }
1797f9d41ddSJagpal Singh Gill 
TEST_F(PortTest,TestGetUSBPortConfigSucess)180*b62e3dfeSJagpal Singh Gill TEST_F(PortTest, TestGetUSBPortConfigSucess)
181*b62e3dfeSJagpal Singh Gill {
182*b62e3dfeSJagpal Singh Gill     using InventoryIntf =
183*b62e3dfeSJagpal Singh Gill         sdbusplus::client::xyz::openbmc_project::inventory::Item<>;
184*b62e3dfeSJagpal Singh Gill     sdbusplus::server::manager_t manager{ctx, InventoryIntf::namespace_path};
185*b62e3dfeSJagpal Singh Gill 
186*b62e3dfeSJagpal Singh Gill     const USBPortConfigServerIntf::properties_t properties = {
187*b62e3dfeSJagpal Singh Gill         .type = "USBPort",
188*b62e3dfeSJagpal Singh Gill         .name = "USBPort1",
189*b62e3dfeSJagpal Singh Gill         .device_address = "0xa",
190*b62e3dfeSJagpal Singh Gill         .device_interface = 1,
191*b62e3dfeSJagpal Singh Gill         .port = 1,
192*b62e3dfeSJagpal Singh Gill         .mode = "RS485",
193*b62e3dfeSJagpal Singh Gill         .baud_rate = 115200,
194*b62e3dfeSJagpal Singh Gill         .rts_delay = 100};
195*b62e3dfeSJagpal Singh Gill 
196*b62e3dfeSJagpal Singh Gill     ctx.spawn(TestGetUSBPortConfig(properties, true));
197*b62e3dfeSJagpal Singh Gill 
198*b62e3dfeSJagpal Singh Gill     ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
199*b62e3dfeSJagpal Singh Gill               sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
200*b62e3dfeSJagpal Singh Gill 
201*b62e3dfeSJagpal Singh Gill     ctx.request_name(entity_manager::EntityManagerInterface::serviceName);
202*b62e3dfeSJagpal Singh Gill     ctx.run();
203*b62e3dfeSJagpal Singh Gill 
204*b62e3dfeSJagpal Singh Gill     EXPECT_EQ(getPortConfigPassed, true);
205*b62e3dfeSJagpal Singh Gill }
206*b62e3dfeSJagpal Singh Gill 
TEST_F(PortTest,TestGetUSBPortConfigFailureForInvalidPortMode)207*b62e3dfeSJagpal Singh Gill TEST_F(PortTest, TestGetUSBPortConfigFailureForInvalidPortMode)
208*b62e3dfeSJagpal Singh Gill {
209*b62e3dfeSJagpal Singh Gill     using InventoryIntf =
210*b62e3dfeSJagpal Singh Gill         sdbusplus::client::xyz::openbmc_project::inventory::Item<>;
211*b62e3dfeSJagpal Singh Gill     sdbusplus::server::manager_t manager{ctx, InventoryIntf::namespace_path};
212*b62e3dfeSJagpal Singh Gill 
213*b62e3dfeSJagpal Singh Gill     const USBPortConfigServerIntf::properties_t properties = {
214*b62e3dfeSJagpal Singh Gill         .type = "USBPort",
215*b62e3dfeSJagpal Singh Gill         .name = "USBPort1",
216*b62e3dfeSJagpal Singh Gill         .device_address = "0xa",
217*b62e3dfeSJagpal Singh Gill         .device_interface = 1,
218*b62e3dfeSJagpal Singh Gill         .port = 1,
219*b62e3dfeSJagpal Singh Gill         .mode = "RSXXX", // Invalid port mode
220*b62e3dfeSJagpal Singh Gill         .baud_rate = 115200,
221*b62e3dfeSJagpal Singh Gill         .rts_delay = 100};
222*b62e3dfeSJagpal Singh Gill 
223*b62e3dfeSJagpal Singh Gill     ctx.spawn(TestGetUSBPortConfig(properties, false));
224*b62e3dfeSJagpal Singh Gill 
225*b62e3dfeSJagpal Singh Gill     ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
226*b62e3dfeSJagpal Singh Gill               sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
227*b62e3dfeSJagpal Singh Gill 
228*b62e3dfeSJagpal Singh Gill     ctx.request_name(entity_manager::EntityManagerInterface::serviceName);
229*b62e3dfeSJagpal Singh Gill     ctx.run();
230*b62e3dfeSJagpal Singh Gill 
231*b62e3dfeSJagpal Singh Gill     EXPECT_EQ(getPortConfigPassed, false);
232*b62e3dfeSJagpal Singh Gill }
233*b62e3dfeSJagpal Singh Gill 
TEST_F(PortTest,TestReadHoldingRegisterSuccess)2347f9d41ddSJagpal Singh Gill TEST_F(PortTest, TestReadHoldingRegisterSuccess)
2357f9d41ddSJagpal Singh Gill {
2367f9d41ddSJagpal Singh Gill     PortConfigIntf::Config config = {};
2377f9d41ddSJagpal Singh Gill     auto res = PortConfigIntf::updateBaseConfig(config, properties);
2387f9d41ddSJagpal Singh Gill     EXPECT_TRUE(res) << "Failed to update config";
2397f9d41ddSJagpal Singh Gill 
2407f9d41ddSJagpal Singh Gill     MockPort port(ctx, config, clientDevicePath);
2417f9d41ddSJagpal Singh Gill 
2427f9d41ddSJagpal Singh Gill     ctx.spawn(serverTester->processRequests());
2437f9d41ddSJagpal Singh Gill 
2447f9d41ddSJagpal Singh Gill     ctx.spawn(TestHoldingRegisters(
2457f9d41ddSJagpal Singh Gill         config, port, TestIntf::testSuccessReadHoldingRegisterOffset, true));
2467f9d41ddSJagpal Singh Gill 
2477f9d41ddSJagpal Singh Gill     ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
2487f9d41ddSJagpal Singh Gill               sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
2497f9d41ddSJagpal Singh Gill 
2507f9d41ddSJagpal Singh Gill     ctx.run();
2517f9d41ddSJagpal Singh Gill }
2527f9d41ddSJagpal Singh Gill 
TEST_F(PortTest,TestReadHoldingRegisterFailure)2537f9d41ddSJagpal Singh Gill TEST_F(PortTest, TestReadHoldingRegisterFailure)
2547f9d41ddSJagpal Singh Gill {
2557f9d41ddSJagpal Singh Gill     PortConfigIntf::Config config = {};
2567f9d41ddSJagpal Singh Gill     auto res = PortConfigIntf::updateBaseConfig(config, properties);
2577f9d41ddSJagpal Singh Gill     EXPECT_TRUE(res) << "Failed to update config";
2587f9d41ddSJagpal Singh Gill 
2597f9d41ddSJagpal Singh Gill     MockPort port(ctx, config, clientDevicePath);
2607f9d41ddSJagpal Singh Gill 
2617f9d41ddSJagpal Singh Gill     ctx.spawn(serverTester->processRequests());
2627f9d41ddSJagpal Singh Gill 
2637f9d41ddSJagpal Singh Gill     ctx.spawn(TestHoldingRegisters(
2647f9d41ddSJagpal Singh Gill         config, port, TestIntf::testFailureReadHoldingRegister, false));
2657f9d41ddSJagpal Singh Gill 
2667f9d41ddSJagpal Singh Gill     ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
2677f9d41ddSJagpal Singh Gill               sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
2687f9d41ddSJagpal Singh Gill 
2697f9d41ddSJagpal Singh Gill     ctx.run();
2707f9d41ddSJagpal Singh Gill }
271