| Jagpal Singh Gill | b62e3df | 2025-10-22 18:10:40 -0700 | [diff] [blame] | 1 | #include "common/entity_manager_interface.hpp" |
| Jagpal Singh Gill | 7f9d41d | 2025-10-16 09:42:18 -0700 | [diff] [blame] | 2 | #include "modbus_server_tester.hpp" |
| Jagpal Singh Gill | b62e3df | 2025-10-22 18:10:40 -0700 | [diff] [blame] | 3 | #include "port/port_factory.hpp" |
| Jagpal Singh Gill | 2fa10f4 | 2025-12-08 10:35:14 -0800 | [diff] [blame] | 4 | #include "test_base.hpp" |
| Jagpal Singh Gill | 7f9d41d | 2025-10-16 09:42:18 -0700 | [diff] [blame] | 5 | |
| Jagpal Singh Gill | b62e3df | 2025-10-22 18:10:40 -0700 | [diff] [blame] | 6 | #include <xyz/openbmc_project/Configuration/USBPort/aserver.hpp> |
| 7 | #include <xyz/openbmc_project/Inventory/Item/client.hpp> |
| 8 | |
| Jagpal Singh Gill | 7f9d41d | 2025-10-16 09:42:18 -0700 | [diff] [blame] | 9 | #include <gtest/gtest.h> |
| 10 | |
| 11 | using namespace std::literals; |
| 12 | |
| Jagpal Singh Gill | b62e3df | 2025-10-22 18:10:40 -0700 | [diff] [blame] | 13 | class PortTest; |
| 14 | |
| Jagpal Singh Gill | 7f9d41d | 2025-10-16 09:42:18 -0700 | [diff] [blame] | 15 | namespace PortIntf = phosphor::modbus::rtu::port; |
| 16 | namespace PortConfigIntf = PortIntf::config; |
| 17 | namespace RTUIntf = phosphor::modbus::rtu; |
| Jagpal Singh Gill | b62e3df | 2025-10-22 18:10:40 -0700 | [diff] [blame] | 18 | using PortFactoryIntf = PortIntf::PortFactory; |
| 19 | using USBPortConfigServerIntf = |
| 20 | sdbusplus::aserver::xyz::openbmc_project::configuration::USBPort<PortTest>; |
| Jagpal Singh Gill | 7f9d41d | 2025-10-16 09:42:18 -0700 | [diff] [blame] | 21 | |
| 22 | struct properties_t |
| 23 | { |
| 24 | std::string name = {}; |
| 25 | std::string mode = {}; |
| 26 | uint64_t baud_rate = {}; |
| 27 | uint64_t rts_delay = {}; |
| 28 | }; |
| 29 | |
| 30 | class MockPort : public PortIntf::BasePort |
| 31 | { |
| 32 | public: |
| 33 | MockPort(sdbusplus::async::context& ctx, |
| 34 | const PortConfigIntf::Config& config, |
| 35 | const std::string& devicePath) : BasePort(ctx, config, devicePath) |
| 36 | {} |
| 37 | }; |
| 38 | |
| Jagpal Singh Gill | 2fa10f4 | 2025-12-08 10:35:14 -0800 | [diff] [blame] | 39 | class PortTest : public BaseTest |
| Jagpal Singh Gill | 7f9d41d | 2025-10-16 09:42:18 -0700 | [diff] [blame] | 40 | { |
| 41 | public: |
| 42 | static constexpr properties_t properties = {"TestPort", "RS485", 115200, 1}; |
| Jagpal Singh Gill | 2fa10f4 | 2025-12-08 10:35:14 -0800 | [diff] [blame] | 43 | static constexpr auto clientDevicePath = "/tmp/ttyPortV0"; |
| 44 | static constexpr auto serverDevicePath = "/tmp/ttyPortV1"; |
| 45 | static constexpr auto serviceName = "xyz.openbmc_project.TestModbusPort"; |
| Jagpal Singh Gill | b62e3df | 2025-10-22 18:10:40 -0700 | [diff] [blame] | 46 | bool getPortConfigPassed = false; |
| Jagpal Singh Gill | 7f9d41d | 2025-10-16 09:42:18 -0700 | [diff] [blame] | 47 | |
| Jagpal Singh Gill | 2fa10f4 | 2025-12-08 10:35:14 -0800 | [diff] [blame] | 48 | PortTest() : BaseTest(clientDevicePath, serverDevicePath, serviceName) {} |
| Jagpal Singh Gill | 7f9d41d | 2025-10-16 09:42:18 -0700 | [diff] [blame] | 49 | |
| Jagpal Singh Gill | b62e3df | 2025-10-22 18:10:40 -0700 | [diff] [blame] | 50 | void SetUp() override |
| 51 | { |
| 52 | getPortConfigPassed = false; |
| Jagpal Singh Gill | 2fa10f4 | 2025-12-08 10:35:14 -0800 | [diff] [blame] | 53 | BaseTest::SetUp(); |
| Jagpal Singh Gill | b62e3df | 2025-10-22 18:10:40 -0700 | [diff] [blame] | 54 | } |
| 55 | |
| Jagpal Singh Gill | 7f9d41d | 2025-10-16 09:42:18 -0700 | [diff] [blame] | 56 | auto TestHoldingRegisters(PortConfigIntf::Config& config, MockPort& port, |
| 57 | uint16_t registerOffset, bool res) |
| 58 | -> sdbusplus::async::task<void> |
| 59 | { |
| 60 | std::vector<uint16_t> registers( |
| 61 | TestIntf::testSuccessReadHoldingRegisterCount); |
| 62 | |
| 63 | auto ret = co_await port.readHoldingRegisters( |
| 64 | TestIntf::testDeviceAddress, registerOffset, config.baudRate, |
| 65 | RTUIntf::Parity::none, registers); |
| 66 | |
| 67 | EXPECT_EQ(ret, res) << "Failed to read holding registers"; |
| 68 | |
| 69 | if (!res) |
| 70 | { |
| 71 | co_return; |
| 72 | } |
| 73 | |
| 74 | for (auto i = 0; i < TestIntf::testSuccessReadHoldingRegisterCount; i++) |
| 75 | { |
| 76 | EXPECT_EQ(registers[i], |
| 77 | TestIntf::testSuccessReadHoldingRegisterResponse[i]); |
| 78 | } |
| 79 | |
| 80 | co_return; |
| 81 | } |
| Jagpal Singh Gill | b62e3df | 2025-10-22 18:10:40 -0700 | [diff] [blame] | 82 | |
| 83 | template <typename Config, typename Properties> |
| 84 | static inline void VerifyConfig(const Config& config, |
| 85 | const Properties& property) |
| 86 | { |
| 87 | EXPECT_EQ(config, property); |
| 88 | } |
| 89 | |
| 90 | auto TestGetUSBPortConfig( |
| 91 | const USBPortConfigServerIntf::properties_t properties, bool shouldPass) |
| 92 | -> sdbusplus::async::task<void> |
| 93 | { |
| 94 | static constexpr auto objectPath = |
| 95 | "/xyz/openbmc_project/inventory/system/board/Ventura_Modbus/DevTTYUSB0"; |
| 96 | |
| 97 | auto configServer = std::make_unique<USBPortConfigServerIntf>( |
| 98 | ctx, objectPath, properties); |
| 99 | |
| 100 | auto config = co_await PortFactoryIntf::getConfig( |
| 101 | ctx, std::string(objectPath), USBPortConfigServerIntf::interface); |
| 102 | |
| 103 | if (!shouldPass) |
| 104 | { |
| 105 | VerifyConfig(config, nullptr); |
| 106 | co_return; |
| 107 | } |
| 108 | |
| 109 | VerifyConfig(config->name, properties.name); |
| 110 | VerifyConfig(config->portMode, PortConfigIntf::PortMode::rs485); |
| 111 | VerifyConfig(config->baudRate, properties.baud_rate); |
| 112 | VerifyConfig(config->rtsDelay, properties.rts_delay); |
| 113 | |
| 114 | getPortConfigPassed = true; |
| 115 | |
| 116 | co_return; |
| 117 | } |
| Jagpal Singh Gill | 7f9d41d | 2025-10-16 09:42:18 -0700 | [diff] [blame] | 118 | }; |
| 119 | |
| 120 | TEST_F(PortTest, TestUpdateConfig) |
| 121 | { |
| 122 | PortConfigIntf::Config config = {}; |
| 123 | auto res = PortConfigIntf::updateBaseConfig(config, properties); |
| 124 | EXPECT_TRUE(res) << "Failed to update config"; |
| 125 | |
| 126 | EXPECT_EQ(config.name, properties.name); |
| 127 | EXPECT_EQ(config.portMode, PortConfigIntf::PortMode::rs485); |
| 128 | EXPECT_EQ(config.baudRate, properties.baud_rate); |
| 129 | EXPECT_EQ(config.rtsDelay, properties.rts_delay); |
| 130 | } |
| 131 | |
| Jagpal Singh Gill | b62e3df | 2025-10-22 18:10:40 -0700 | [diff] [blame] | 132 | TEST_F(PortTest, TestGetUSBPortConfigSucess) |
| 133 | { |
| 134 | using InventoryIntf = |
| 135 | sdbusplus::client::xyz::openbmc_project::inventory::Item<>; |
| 136 | sdbusplus::server::manager_t manager{ctx, InventoryIntf::namespace_path}; |
| 137 | |
| 138 | const USBPortConfigServerIntf::properties_t properties = { |
| 139 | .type = "USBPort", |
| 140 | .name = "USBPort1", |
| 141 | .device_address = "0xa", |
| 142 | .device_interface = 1, |
| 143 | .port = 1, |
| 144 | .mode = "RS485", |
| 145 | .baud_rate = 115200, |
| 146 | .rts_delay = 100}; |
| 147 | |
| 148 | ctx.spawn(TestGetUSBPortConfig(properties, true)); |
| 149 | |
| 150 | ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) | |
| 151 | sdbusplus::async::execution::then([&]() { ctx.request_stop(); })); |
| 152 | |
| 153 | ctx.request_name(entity_manager::EntityManagerInterface::serviceName); |
| 154 | ctx.run(); |
| 155 | |
| 156 | EXPECT_EQ(getPortConfigPassed, true); |
| 157 | } |
| 158 | |
| 159 | TEST_F(PortTest, TestGetUSBPortConfigFailureForInvalidPortMode) |
| 160 | { |
| 161 | using InventoryIntf = |
| 162 | sdbusplus::client::xyz::openbmc_project::inventory::Item<>; |
| 163 | sdbusplus::server::manager_t manager{ctx, InventoryIntf::namespace_path}; |
| 164 | |
| 165 | const USBPortConfigServerIntf::properties_t properties = { |
| 166 | .type = "USBPort", |
| 167 | .name = "USBPort1", |
| 168 | .device_address = "0xa", |
| 169 | .device_interface = 1, |
| 170 | .port = 1, |
| 171 | .mode = "RSXXX", // Invalid port mode |
| 172 | .baud_rate = 115200, |
| 173 | .rts_delay = 100}; |
| 174 | |
| 175 | ctx.spawn(TestGetUSBPortConfig(properties, false)); |
| 176 | |
| 177 | ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) | |
| 178 | sdbusplus::async::execution::then([&]() { ctx.request_stop(); })); |
| 179 | |
| 180 | ctx.request_name(entity_manager::EntityManagerInterface::serviceName); |
| 181 | ctx.run(); |
| 182 | |
| 183 | EXPECT_EQ(getPortConfigPassed, false); |
| 184 | } |
| 185 | |
| Jagpal Singh Gill | 7f9d41d | 2025-10-16 09:42:18 -0700 | [diff] [blame] | 186 | TEST_F(PortTest, TestReadHoldingRegisterSuccess) |
| 187 | { |
| 188 | PortConfigIntf::Config config = {}; |
| 189 | auto res = PortConfigIntf::updateBaseConfig(config, properties); |
| 190 | EXPECT_TRUE(res) << "Failed to update config"; |
| 191 | |
| 192 | MockPort port(ctx, config, clientDevicePath); |
| 193 | |
| Jagpal Singh Gill | 7f9d41d | 2025-10-16 09:42:18 -0700 | [diff] [blame] | 194 | ctx.spawn(TestHoldingRegisters( |
| 195 | config, port, TestIntf::testSuccessReadHoldingRegisterOffset, true)); |
| 196 | |
| 197 | ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) | |
| 198 | sdbusplus::async::execution::then([&]() { ctx.request_stop(); })); |
| 199 | |
| 200 | ctx.run(); |
| 201 | } |
| 202 | |
| 203 | TEST_F(PortTest, TestReadHoldingRegisterFailure) |
| 204 | { |
| 205 | PortConfigIntf::Config config = {}; |
| 206 | auto res = PortConfigIntf::updateBaseConfig(config, properties); |
| 207 | EXPECT_TRUE(res) << "Failed to update config"; |
| 208 | |
| 209 | MockPort port(ctx, config, clientDevicePath); |
| 210 | |
| Jagpal Singh Gill | 7f9d41d | 2025-10-16 09:42:18 -0700 | [diff] [blame] | 211 | ctx.spawn(TestHoldingRegisters( |
| 212 | config, port, TestIntf::testFailureReadHoldingRegister, false)); |
| 213 | |
| 214 | ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) | |
| 215 | sdbusplus::async::execution::then([&]() { ctx.request_stop(); })); |
| 216 | |
| 217 | ctx.run(); |
| 218 | } |