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