xref: /openbmc/phosphor-modbus/tests/test_inventory.cpp (revision cad9ecf69472f03f9ece64eff5d2d94bc51bcf90)
1*cad9ecf6SJagpal Singh Gill #include "inventory/modbus_inventory.hpp"
2*cad9ecf6SJagpal Singh Gill #include "modbus_server_tester.hpp"
3*cad9ecf6SJagpal Singh Gill #include "port/base_port.hpp"
4*cad9ecf6SJagpal Singh Gill 
5*cad9ecf6SJagpal Singh Gill #include <fcntl.h>
6*cad9ecf6SJagpal Singh Gill 
7*cad9ecf6SJagpal Singh Gill #include <xyz/openbmc_project/Inventory/Source/Modbus/FRU/client.hpp>
8*cad9ecf6SJagpal Singh Gill 
9*cad9ecf6SJagpal Singh Gill #include <gtest/gtest.h>
10*cad9ecf6SJagpal Singh Gill 
11*cad9ecf6SJagpal Singh Gill using namespace std::literals;
12*cad9ecf6SJagpal Singh Gill using namespace testing;
13*cad9ecf6SJagpal Singh Gill using InventorySourceIntf =
14*cad9ecf6SJagpal Singh Gill     sdbusplus::client::xyz::openbmc_project::inventory::source::modbus::FRU<>;
15*cad9ecf6SJagpal Singh Gill 
16*cad9ecf6SJagpal Singh Gill namespace TestIntf = phosphor::modbus::test;
17*cad9ecf6SJagpal Singh Gill namespace ModbusIntf = phosphor::modbus::rtu;
18*cad9ecf6SJagpal Singh Gill namespace PortIntf = phosphor::modbus::rtu::port;
19*cad9ecf6SJagpal Singh Gill namespace PortConfigIntf = PortIntf::config;
20*cad9ecf6SJagpal Singh Gill namespace InventoryIntf = phosphor::modbus::rtu::inventory;
21*cad9ecf6SJagpal Singh Gill namespace InventoryConfigIntf = InventoryIntf::config;
22*cad9ecf6SJagpal Singh Gill 
23*cad9ecf6SJagpal Singh Gill class MockPort : public PortIntf::BasePort
24*cad9ecf6SJagpal Singh Gill {
25*cad9ecf6SJagpal Singh Gill   public:
MockPort(sdbusplus::async::context & ctx,const PortConfigIntf::Config & config,const std::string & devicePath)26*cad9ecf6SJagpal Singh Gill     MockPort(sdbusplus::async::context& ctx,
27*cad9ecf6SJagpal Singh Gill              const PortConfigIntf::Config& config,
28*cad9ecf6SJagpal Singh Gill              const std::string& devicePath) : BasePort(ctx, config, devicePath)
29*cad9ecf6SJagpal Singh Gill     {}
30*cad9ecf6SJagpal Singh Gill };
31*cad9ecf6SJagpal Singh Gill 
32*cad9ecf6SJagpal Singh Gill class InventoryTest : public ::testing::Test
33*cad9ecf6SJagpal Singh Gill {
34*cad9ecf6SJagpal Singh Gill   public:
35*cad9ecf6SJagpal Singh Gill     PortConfigIntf::Config portConfig;
36*cad9ecf6SJagpal Singh Gill     static constexpr const char* clientDevicePath =
37*cad9ecf6SJagpal Singh Gill         "/tmp/ttyInventoryTestPort0";
38*cad9ecf6SJagpal Singh Gill     static constexpr const char* serverDevicePath =
39*cad9ecf6SJagpal Singh Gill         "/tmp/ttyInventoryTestPort1";
40*cad9ecf6SJagpal Singh Gill     static constexpr const auto defaultBaudeRate = "b115200";
41*cad9ecf6SJagpal Singh Gill     static constexpr const auto deviceName = "Test1";
42*cad9ecf6SJagpal Singh Gill     static constexpr auto serviceName = "xyz.openbmc_project.TestModbusRTU";
43*cad9ecf6SJagpal Singh Gill     int socat_pid = -1;
44*cad9ecf6SJagpal Singh Gill     sdbusplus::async::context ctx;
45*cad9ecf6SJagpal Singh Gill     int fdClient = -1;
46*cad9ecf6SJagpal Singh Gill     std::unique_ptr<TestIntf::ServerTester> serverTester;
47*cad9ecf6SJagpal Singh Gill     int fdServer = -1;
48*cad9ecf6SJagpal Singh Gill 
InventoryTest()49*cad9ecf6SJagpal Singh Gill     InventoryTest()
50*cad9ecf6SJagpal Singh Gill     {
51*cad9ecf6SJagpal Singh Gill         portConfig.name = "TestPort1";
52*cad9ecf6SJagpal Singh Gill         portConfig.portMode = PortConfigIntf::PortMode::rs485;
53*cad9ecf6SJagpal Singh Gill         portConfig.baudRate = 115200;
54*cad9ecf6SJagpal Singh Gill         portConfig.rtsDelay = 1;
55*cad9ecf6SJagpal Singh Gill 
56*cad9ecf6SJagpal Singh Gill         std::string socatCmd = std::format(
57*cad9ecf6SJagpal Singh Gill             "socat -x -v -d -d pty,link={},rawer,echo=0,parenb,{} pty,link={},rawer,echo=0,parenb,{} & echo $!",
58*cad9ecf6SJagpal Singh Gill             serverDevicePath, defaultBaudeRate, clientDevicePath,
59*cad9ecf6SJagpal Singh Gill             defaultBaudeRate);
60*cad9ecf6SJagpal Singh Gill 
61*cad9ecf6SJagpal Singh Gill         // Start socat in the background and capture its PID
62*cad9ecf6SJagpal Singh Gill         FILE* fp = popen(socatCmd.c_str(), "r");
63*cad9ecf6SJagpal Singh Gill         EXPECT_NE(fp, nullptr) << "Failed to start socat: " << strerror(errno);
64*cad9ecf6SJagpal Singh Gill         EXPECT_GT(fscanf(fp, "%d", &socat_pid), 0);
65*cad9ecf6SJagpal Singh Gill         pclose(fp);
66*cad9ecf6SJagpal Singh Gill 
67*cad9ecf6SJagpal Singh Gill         // Wait for socat to start up
68*cad9ecf6SJagpal Singh Gill         sleep(1);
69*cad9ecf6SJagpal Singh Gill 
70*cad9ecf6SJagpal Singh Gill         fdClient = open(clientDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
71*cad9ecf6SJagpal Singh Gill         EXPECT_NE(fdClient, -1)
72*cad9ecf6SJagpal Singh Gill             << "Failed to open serial port " << clientDevicePath
73*cad9ecf6SJagpal Singh Gill             << " with error: " << strerror(errno);
74*cad9ecf6SJagpal Singh Gill 
75*cad9ecf6SJagpal Singh Gill         fdServer = open(serverDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
76*cad9ecf6SJagpal Singh Gill         EXPECT_NE(fdServer, -1)
77*cad9ecf6SJagpal Singh Gill             << "Failed to open serial port " << serverDevicePath
78*cad9ecf6SJagpal Singh Gill             << " with error: " << strerror(errno);
79*cad9ecf6SJagpal Singh Gill 
80*cad9ecf6SJagpal Singh Gill         ctx.request_name(serviceName);
81*cad9ecf6SJagpal Singh Gill 
82*cad9ecf6SJagpal Singh Gill         serverTester = std::make_unique<TestIntf::ServerTester>(ctx, fdServer);
83*cad9ecf6SJagpal Singh Gill     }
84*cad9ecf6SJagpal Singh Gill 
~InventoryTest()85*cad9ecf6SJagpal Singh Gill     ~InventoryTest() noexcept override
86*cad9ecf6SJagpal Singh Gill     {
87*cad9ecf6SJagpal Singh Gill         if (fdClient != -1)
88*cad9ecf6SJagpal Singh Gill         {
89*cad9ecf6SJagpal Singh Gill             close(fdClient);
90*cad9ecf6SJagpal Singh Gill             fdClient = -1;
91*cad9ecf6SJagpal Singh Gill         }
92*cad9ecf6SJagpal Singh Gill         if (fdServer != -1)
93*cad9ecf6SJagpal Singh Gill         {
94*cad9ecf6SJagpal Singh Gill             close(fdServer);
95*cad9ecf6SJagpal Singh Gill             fdServer = -1;
96*cad9ecf6SJagpal Singh Gill         }
97*cad9ecf6SJagpal Singh Gill         kill(socat_pid, SIGTERM);
98*cad9ecf6SJagpal Singh Gill     }
99*cad9ecf6SJagpal Singh Gill 
testInventorySourceCreation(std::string objPath)100*cad9ecf6SJagpal Singh Gill     auto testInventorySourceCreation(std::string objPath)
101*cad9ecf6SJagpal Singh Gill         -> sdbusplus::async::task<void>
102*cad9ecf6SJagpal Singh Gill     {
103*cad9ecf6SJagpal Singh Gill         InventoryConfigIntf::Config::port_address_map_t addressMap;
104*cad9ecf6SJagpal Singh Gill         addressMap[portConfig.name] = {{.start = TestIntf::testDeviceAddress,
105*cad9ecf6SJagpal Singh Gill                                         .end = TestIntf::testDeviceAddress}};
106*cad9ecf6SJagpal Singh Gill         InventoryConfigIntf::Config deviceConfig = {
107*cad9ecf6SJagpal Singh Gill             .name = deviceName,
108*cad9ecf6SJagpal Singh Gill             .addressMap = addressMap,
109*cad9ecf6SJagpal Singh Gill             .registers = {{"Model",
110*cad9ecf6SJagpal Singh Gill                            TestIntf::testReadHoldingRegisterModelOffset,
111*cad9ecf6SJagpal Singh Gill                            TestIntf::testReadHoldingRegisterModelCount}},
112*cad9ecf6SJagpal Singh Gill             .parity = ModbusIntf::Parity::none,
113*cad9ecf6SJagpal Singh Gill             .baudRate = 115200};
114*cad9ecf6SJagpal Singh Gill         InventoryIntf::Device::serial_port_map_t ports;
115*cad9ecf6SJagpal Singh Gill         ports[portConfig.name] =
116*cad9ecf6SJagpal Singh Gill             std::make_unique<MockPort>(ctx, portConfig, clientDevicePath);
117*cad9ecf6SJagpal Singh Gill 
118*cad9ecf6SJagpal Singh Gill         auto inventoryDevice =
119*cad9ecf6SJagpal Singh Gill             std::make_unique<InventoryIntf::Device>(ctx, deviceConfig, ports);
120*cad9ecf6SJagpal Singh Gill 
121*cad9ecf6SJagpal Singh Gill         co_await inventoryDevice->probePorts();
122*cad9ecf6SJagpal Singh Gill 
123*cad9ecf6SJagpal Singh Gill         // Create InventorySource client interface to read back D-Bus properties
124*cad9ecf6SJagpal Singh Gill         auto properties = co_await InventorySourceIntf(ctx)
125*cad9ecf6SJagpal Singh Gill                               .service(serviceName)
126*cad9ecf6SJagpal Singh Gill                               .path(objPath)
127*cad9ecf6SJagpal Singh Gill                               .properties();
128*cad9ecf6SJagpal Singh Gill 
129*cad9ecf6SJagpal Singh Gill         constexpr auto defaultInventoryValue = "Unknown";
130*cad9ecf6SJagpal Singh Gill 
131*cad9ecf6SJagpal Singh Gill         EXPECT_EQ(properties.name,
132*cad9ecf6SJagpal Singh Gill                   std::format("{} {} {}", deviceName,
133*cad9ecf6SJagpal Singh Gill                               TestIntf::testDeviceAddress, portConfig.name))
134*cad9ecf6SJagpal Singh Gill             << "Name mismatch";
135*cad9ecf6SJagpal Singh Gill         EXPECT_EQ(properties.address, TestIntf::testDeviceAddress)
136*cad9ecf6SJagpal Singh Gill             << "Address mismatch";
137*cad9ecf6SJagpal Singh Gill         EXPECT_EQ(properties.link_tty, portConfig.name) << "Link TTY mismatch";
138*cad9ecf6SJagpal Singh Gill         EXPECT_EQ(properties.model, TestIntf::testReadHoldingRegisterModelStr)
139*cad9ecf6SJagpal Singh Gill             << "Model mismatch";
140*cad9ecf6SJagpal Singh Gill         EXPECT_EQ(properties.serial_number, defaultInventoryValue)
141*cad9ecf6SJagpal Singh Gill             << "Part Number mismatch";
142*cad9ecf6SJagpal Singh Gill 
143*cad9ecf6SJagpal Singh Gill         co_return;
144*cad9ecf6SJagpal Singh Gill     }
145*cad9ecf6SJagpal Singh Gill 
SetUp()146*cad9ecf6SJagpal Singh Gill     void SetUp() override
147*cad9ecf6SJagpal Singh Gill     {
148*cad9ecf6SJagpal Singh Gill         // Process request for probe device call
149*cad9ecf6SJagpal Singh Gill         ctx.spawn(serverTester->processRequests());
150*cad9ecf6SJagpal Singh Gill 
151*cad9ecf6SJagpal Singh Gill         // Process request to read `Model` holding register call
152*cad9ecf6SJagpal Singh Gill         ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
153*cad9ecf6SJagpal Singh Gill                   sdbusplus::async::execution::then([&]() {
154*cad9ecf6SJagpal Singh Gill                       ctx.spawn(serverTester->processRequests());
155*cad9ecf6SJagpal Singh Gill                   }));
156*cad9ecf6SJagpal Singh Gill     }
157*cad9ecf6SJagpal Singh Gill };
158*cad9ecf6SJagpal Singh Gill 
TEST_F(InventoryTest,TestAddInventorySource)159*cad9ecf6SJagpal Singh Gill TEST_F(InventoryTest, TestAddInventorySource)
160*cad9ecf6SJagpal Singh Gill {
161*cad9ecf6SJagpal Singh Gill     auto objPath =
162*cad9ecf6SJagpal Singh Gill         std::format("{}/{}_{}_{}", InventorySourceIntf::namespace_path,
163*cad9ecf6SJagpal Singh Gill                     deviceName, TestIntf::testDeviceAddress, portConfig.name);
164*cad9ecf6SJagpal Singh Gill 
165*cad9ecf6SJagpal Singh Gill     ctx.spawn(testInventorySourceCreation(objPath));
166*cad9ecf6SJagpal Singh Gill 
167*cad9ecf6SJagpal Singh Gill     ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
168*cad9ecf6SJagpal Singh Gill               sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
169*cad9ecf6SJagpal Singh Gill 
170*cad9ecf6SJagpal Singh Gill     ctx.run();
171*cad9ecf6SJagpal Singh Gill }
172