blob: a0ce5ec12222ebbe96f0e84045281330c595279c [file] [log] [blame]
Jagpal Singh Gillcf77ef52025-09-02 15:19:29 -07001#include "device/device_factory.hpp"
2#include "modbus_server_tester.hpp"
3#include "port/base_port.hpp"
4
5#include <fcntl.h>
6
7#include <xyz/openbmc_project/Software/Version/client.hpp>
8
9#include <gtest/gtest.h>
10
11using namespace std::literals;
12using namespace testing;
13using SoftwareIntf =
14 sdbusplus::client::xyz::openbmc_project::software::Version<>;
15
16namespace TestIntf = phosphor::modbus::test;
17namespace ModbusIntf = phosphor::modbus::rtu;
18namespace PortIntf = phosphor::modbus::rtu::port;
19namespace PortConfigIntf = PortIntf::config;
20namespace DeviceIntf = phosphor::modbus::rtu::device;
21namespace DeviceConfigIntf = DeviceIntf::config;
22
23class MockPort : public PortIntf::BasePort
24{
25 public:
26 MockPort(sdbusplus::async::context& ctx,
27 const PortConfigIntf::Config& config,
28 const std::string& devicePath) : BasePort(ctx, config, devicePath)
29 {}
30};
31
32class TestFirmware : public DeviceIntf::DeviceFirmware
33{
34 public:
35 TestFirmware(sdbusplus::async::context& ctx,
36 const DeviceConfigIntf::Config& config,
37 PortIntf::BasePort& serialPort) :
38 DeviceIntf::DeviceFirmware(ctx, config, serialPort)
39 {}
40
41 auto getObjectPath() -> sdbusplus::message::object_path
42 {
43 return objectPath;
44 }
45};
46
47class FirmwareTest : public ::testing::Test
48{
49 public:
50 PortConfigIntf::Config portConfig;
51 static constexpr const char* clientDevicePath = "/tmp/ttyFirmwareTestPort0";
52 static constexpr const char* serverDevicePath = "/tmp/ttyFirmwareTestPort1";
53 static constexpr auto portName = "TestPort0";
54 static constexpr auto baudRate = 115200;
55 static constexpr const auto strBaudeRate = "b115200";
56 std::string deviceName;
57 std::string objectPath;
58 static constexpr auto serviceName =
59 "xyz.openbmc_project.TestModbusRTUFirmware";
60 static constexpr auto firmwareName = "TestVersion";
61 int socat_pid = -1;
62 sdbusplus::async::context ctx;
63 int fdClient = -1;
64 std::unique_ptr<TestIntf::ServerTester> serverTester;
65 int fdServer = -1;
66 std::unique_ptr<MockPort> mockPort;
67
68 FirmwareTest()
69 {
70 portConfig.name = portName;
71 portConfig.portMode = PortConfigIntf::PortMode::rs485;
72 portConfig.baudRate = baudRate;
73 portConfig.rtsDelay = 1;
74
75 deviceName = std::format("ResorviorPumpUnit_{}_{}",
76 TestIntf::testDeviceAddress, portName);
77 objectPath =
78 std::format("{}/{}", SoftwareIntf::namespace_path, deviceName);
79
80 std::string socatCmd = std::format(
81 "socat -x -v -d -d pty,link={},rawer,echo=0,parenb,{} pty,link={},rawer,echo=0,parenb,{} & echo $!",
82 serverDevicePath, strBaudeRate, clientDevicePath, strBaudeRate);
83
84 // Start socat in the background and capture its PID
85 FILE* fp = popen(socatCmd.c_str(), "r");
86 EXPECT_NE(fp, nullptr) << "Failed to start socat: " << strerror(errno);
87 EXPECT_GT(fscanf(fp, "%d", &socat_pid), 0);
88 pclose(fp);
89
90 // Wait for socat to start up
91 sleep(1);
92
93 fdClient = open(clientDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
94 EXPECT_NE(fdClient, -1)
95 << "Failed to open serial port " << clientDevicePath
96 << " with error: " << strerror(errno);
97
98 fdServer = open(serverDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
99 EXPECT_NE(fdServer, -1)
100 << "Failed to open serial port " << serverDevicePath
101 << " with error: " << strerror(errno);
102
103 ctx.request_name(serviceName);
104
105 mockPort =
106 std::make_unique<MockPort>(ctx, portConfig, clientDevicePath);
107
108 serverTester = std::make_unique<TestIntf::ServerTester>(ctx, fdServer);
109 }
110
111 ~FirmwareTest() noexcept override
112 {
113 if (fdClient != -1)
114 {
115 close(fdClient);
116 fdClient = -1;
117 }
118 if (fdServer != -1)
119 {
120 close(fdServer);
121 fdServer = -1;
122 }
123 kill(socat_pid, SIGTERM);
124 }
125
126 auto testFirmwareVersion(
127 std::string objectPath,
128 DeviceConfigIntf::FirmwareRegister firmwareRegister,
129 std::string expectedVersion) -> sdbusplus::async::task<void>
130 {
131 DeviceConfigIntf::DeviceFactoryConfig deviceFactoryConfig = {
132 {
133 .address = TestIntf::testDeviceAddress,
134 .parity = ModbusIntf::Parity::none,
135 .baudRate = baudRate,
136 .name = deviceName,
137 .portName = portConfig.name,
138 .inventoryPath = sdbusplus::message::object_path(
139 "xyz/openbmc_project/Inventory/ResorviorPumpUnit"),
140 .sensorRegisters = {},
141 .statusRegisters = {},
142 .firmwareRegisters = {firmwareRegister},
143 },
144 DeviceConfigIntf::DeviceType::reservoirPumpUnit,
145 DeviceConfigIntf::DeviceModel::RDF040DSS5193E0,
146 };
147
148 auto deviceFirmware =
149 std::make_unique<TestFirmware>(ctx, deviceFactoryConfig, *mockPort);
150
151 co_await deviceFirmware->readVersionRegister();
152
153 EXPECT_TRUE(deviceFirmware->getObjectPath().str.starts_with(objectPath))
154 << "Invalid ObjectPath";
155
156 auto softwarePath = deviceFirmware->getObjectPath().str;
157
158 auto properties = co_await SoftwareIntf(ctx)
159 .service(serviceName)
160 .path(softwarePath)
161 .properties();
162
163 EXPECT_EQ(properties.version, expectedVersion)
164 << "Firmware version mismatch";
165
166 co_return;
167 }
168
169 void SetUp() override
170 {
171 // Process request to read firmware version
172 ctx.spawn(serverTester->processRequests());
173 }
174};
175
176TEST_F(FirmwareTest, TestFirmwareVersion)
177{
178 const DeviceConfigIntf::FirmwareRegister firmwareRegister = {
179 .name = "",
180 .type = DeviceConfigIntf::FirmwareRegisterType::version,
181 .offset = TestIntf::testReadHoldingRegisterFirmwareVersionOffset,
182 .size = TestIntf::testReadHoldingRegisterFirmwareVersionCount};
183
184 ctx.spawn(testFirmwareVersion(
185 objectPath, firmwareRegister,
186 TestIntf::testReadHoldingRegisterFirmwareVersionStr));
187
188 ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
189 sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
190
191 ctx.run();
192}