xref: /openbmc/phosphor-modbus/tests/test_device_events.cpp (revision acfdd55f5f8ca4db5cd41a9dcb15bce438136123)
1 #include "common/events.hpp"
2 #include "device/device_factory.hpp"
3 #include "modbus_server_tester.hpp"
4 #include "port/base_port.hpp"
5 #include "test_base.hpp"
6 
7 #include <xyz/openbmc_project/Logging/Create/aserver.hpp>
8 #include <xyz/openbmc_project/Logging/Entry/aserver.hpp>
9 #include <xyz/openbmc_project/Sensor/Threshold/Critical/client.hpp>
10 #include <xyz/openbmc_project/Sensor/Value/client.hpp>
11 #include <xyz/openbmc_project/State/Decorator/Availability/client.hpp>
12 #include <xyz/openbmc_project/State/Decorator/OperationalStatus/client.hpp>
13 
14 #include <cmath>
15 #include <string>
16 
17 #include <gtest/gtest.h>
18 
19 using namespace std::literals;
20 using namespace testing;
21 using SensorValueIntf =
22     sdbusplus::client::xyz::openbmc_project::sensor::Value<>;
23 using OperationalStatusIntf = sdbusplus::client::xyz::openbmc_project::state::
24     decorator::OperationalStatus<>;
25 using AvailabilityIntf =
26     sdbusplus::client::xyz::openbmc_project::state::decorator::Availability<>;
27 using ThresholdCriticalIntf =
28     sdbusplus::client::xyz::openbmc_project::sensor::threshold::Critical<>;
29 
30 class TestEventServer;
31 class TestEventEntry;
32 
33 using EventServerIntf =
34     sdbusplus::aserver::xyz::openbmc_project::logging::Create<TestEventServer>;
35 using EventEntryIntf =
36     sdbusplus::aserver::xyz::openbmc_project::logging::Entry<TestEventEntry>;
37 
38 namespace ModbusIntf = phosphor::modbus::rtu;
39 namespace PortIntf = phosphor::modbus::rtu::port;
40 namespace PortConfigIntf = PortIntf::config;
41 namespace DeviceIntf = phosphor::modbus::rtu::device;
42 namespace DeviceConfigIntf = DeviceIntf::config;
43 namespace EventIntf = phosphor::modbus::events;
44 
45 class MockPort : public PortIntf::BasePort
46 {
47   public:
MockPort(sdbusplus::async::context & ctx,const PortConfigIntf::Config & config,const std::string & devicePath)48     MockPort(sdbusplus::async::context& ctx,
49              const PortConfigIntf::Config& config,
50              const std::string& devicePath) : BasePort(ctx, config, devicePath)
51     {}
52 };
53 
54 // Test Event Class to mock the EventEntry
55 class TestEventEntry : public EventEntryIntf
56 {
57   public:
TestEventEntry(sdbusplus::async::context & ctx,const char * path)58     TestEventEntry(sdbusplus::async::context& ctx, const char* path) :
59         EventEntryIntf(ctx, path)
60     {}
61 
method_call(get_entry_t)62     auto method_call(get_entry_t)
63         -> sdbusplus::async::task<get_entry_t::return_type>
64     {
65         get_entry_t::return_type fd1 = 0;
66         co_return fd1;
67     }
68 };
69 
70 // Test Event Server Class to mock the EventServer
71 class TestEventServer : public EventServerIntf
72 {
73   public:
TestEventServer(sdbusplus::async::context & ctx,const char * path)74     TestEventServer(sdbusplus::async::context& ctx, const char* path) :
75         EventServerIntf(ctx, path), ctx(ctx)
76     {}
77 
method_call(create_t,auto message,auto,auto)78     auto method_call(create_t, auto message, auto, auto)
79         -> sdbusplus::async::task<create_t::return_type>
80 
81     {
82         static int cnt = 100;
83         cnt++;
84 
85         // Append the count to the object path to make it unique for each event
86         std::string objectPath =
87             "/xyz/openbmc_project/logging/entry/TestEvent" +
88             std::to_string(cnt);
89         EXPECT_EQ(message, expectedEvent) << "Event name mismatch";
90 
91         eventEntries.emplace_back(
92             std::make_unique<TestEventEntry>(ctx, objectPath.c_str()));
93 
94         co_return sdbusplus::message::object_path(objectPath);
95     }
96 
method_call(create_with_ffdc_files_t,auto,auto,auto,auto)97     auto method_call(create_with_ffdc_files_t, auto, auto, auto, auto)
98         -> sdbusplus::async::task<create_with_ffdc_files_t::return_type>
99 
100     {
101         co_return;
102     }
103 
104     std::string expectedEvent;
105 
106   private:
107     sdbusplus::async::context& ctx;
108     std::vector<std::unique_ptr<TestEventEntry>> eventEntries;
109 };
110 
111 class DeviceEventsTest : public BaseTest
112 {
113   public:
114     PortConfigIntf::Config portConfig;
115     static constexpr const char* clientDevicePath =
116         "/tmp/ttyDeviceEventsTestPort0";
117     static constexpr const char* serverDevicePath =
118         "/tmp/ttyDeviceEventsTestPort1";
119     static constexpr auto portName = "TestPort0";
120     std::string deviceName;
121     std::string fullSensorName;
122     std::string objectPath;
123     const char* loggingObjectPath = "/xyz/openbmc_project/logging";
124     static constexpr auto serviceName = "xyz.openbmc_project.Logging";
125     static constexpr auto sensorName = "OutletTemperature";
126     TestEventServer eventServer;
127     sdbusplus::server::manager_t manager;
128 
DeviceEventsTest()129     DeviceEventsTest() :
130         BaseTest(clientDevicePath, serverDevicePath, serviceName),
131         eventServer(ctx, loggingObjectPath), manager(ctx, loggingObjectPath)
132     {
133         portConfig.name = portName;
134         portConfig.portMode = PortConfigIntf::PortMode::rs485;
135         portConfig.baudRate = baudRate;
136         portConfig.rtsDelay = 1;
137 
138         deviceName = std::format("ResorviorPumpUnit_{}_{}",
139                                  TestIntf::testDeviceAddress, portName);
140 
141         fullSensorName = std::format("{}_{}", deviceName, sensorName);
142 
143         objectPath = std::format(
144             "{}/{}/{}", SensorValueIntf::namespace_path::value,
145             SensorValueIntf::namespace_path::temperature, fullSensorName);
146     }
147 
verifyValue(bool currentValue,bool expectedValue,const std::string & failureStr)148     auto verifyValue(bool currentValue, bool expectedValue,
149                      const std::string& failureStr) -> void
150     {
151         EXPECT_EQ(currentValue, expectedValue) << failureStr;
152     }
153 
verifyValue(double currentValue,double expectedValue,const std::string & failureStr)154     auto verifyValue(double currentValue, double expectedValue,
155                      const std::string& failureStr) -> void
156     {
157         EXPECT_EQ(currentValue, expectedValue) << failureStr;
158     }
159 
verifyResult(SensorValueIntf::properties_t & properties,OperationalStatusIntf::properties_t & operationalProperties,AvailabilityIntf::properties_t & availabilityProperties,ThresholdCriticalIntf::properties_t & thresholdProperties,double expectedValue,SensorValueIntf::Unit expectedUnit)160     auto verifyResult(
161         SensorValueIntf::properties_t& properties,
162         OperationalStatusIntf::properties_t& operationalProperties,
163         AvailabilityIntf::properties_t& availabilityProperties,
164         ThresholdCriticalIntf::properties_t& thresholdProperties,
165         double expectedValue, SensorValueIntf::Unit expectedUnit) -> void
166     {
167         if (std::isnan(expectedValue))
168         {
169             EXPECT_TRUE(std::isnan(properties.value))
170                 << "Sensor value should be Nan";
171             verifyValue(operationalProperties.functional, false,
172                         "Operational status mismatch");
173             verifyValue(availabilityProperties.available, false,
174                         "Availability mismatch");
175             verifyValue(thresholdProperties.critical_alarm_high, false,
176                         "Critical Alarm mismatch");
177         }
178         else
179         {
180             verifyValue(properties.value, expectedValue,
181                         "Sensor value mismatch");
182             verifyValue(operationalProperties.functional, true,
183                         "Operational status mismatch");
184             verifyValue(availabilityProperties.available, true,
185                         "Availability mismatch");
186             verifyValue(thresholdProperties.critical_alarm_high, true,
187                         "Critical Alarm mismatch");
188         }
189 
190         EXPECT_EQ(properties.unit, expectedUnit) << "Sensor unit mismatch";
191         EXPECT_TRUE(std::isnan(properties.min_value)) << "Min value mismatch";
192         EXPECT_TRUE(std::isnan(properties.max_value)) << "Max value mismatch";
193     }
194 
testSensorCreation(std::string objectPath,DeviceConfigIntf::StatusType statusType,double expectedValue)195     auto testSensorCreation(std::string objectPath,
196                             DeviceConfigIntf::StatusType statusType,
197                             double expectedValue)
198         -> sdbusplus::async::task<void>
199     {
200         DeviceConfigIntf::StatusBit statusBit = {
201             .name = sensorName,
202             .type = statusType,
203             .bitPosition = 0,
204             .value = true};
205         DeviceConfigIntf::Config::status_registers_t statusRegisters = {
206             {TestIntf::testReadHoldingRegisterEventOffset, {statusBit}}};
207         DeviceConfigIntf::Config::sensor_registers_t sensorRegisters = {{
208             .name = sensorName,
209             .pathSuffix = SensorValueIntf::namespace_path::temperature,
210             .unit = SensorValueIntf::Unit::DegreesC,
211             .offset = TestIntf::testReadHoldingRegisterTempUnsignedOffset,
212             .size = TestIntf::testReadHoldingRegisterTempCount,
213             .format = DeviceConfigIntf::SensorFormat::floatingPoint,
214         }};
215         DeviceConfigIntf::DeviceFactoryConfig deviceFactoryConfig = {
216             {
217                 .address = TestIntf::testDeviceAddress,
218                 .parity = ModbusIntf::Parity::none,
219                 .baudRate = baudRate,
220                 .name = deviceName,
221                 .portName = portConfig.name,
222                 .inventoryPath = sdbusplus::message::object_path(
223                     "xyz/openbmc_project/Inventory/ResorviorPumpUnit"),
224                 .sensorRegisters = sensorRegisters,
225                 .statusRegisters = statusRegisters,
226                 .firmwareRegisters = {},
227             },
228             DeviceConfigIntf::DeviceType::reservoirPumpUnit,
229             DeviceConfigIntf::DeviceModel::RDF040DSS5193E0,
230         };
231         EventIntf::Events events{ctx};
232         MockPort mockPort(ctx, portConfig, clientDevicePath);
233         auto device = DeviceIntf::DeviceFactory::create(
234             ctx, deviceFactoryConfig, mockPort, events);
235         co_await device->readSensorRegisters();
236         auto properties = co_await SensorValueIntf(ctx)
237                               .service(serviceName)
238                               .path(objectPath)
239                               .properties();
240         auto operationalProperties =
241             co_await OperationalStatusIntf(ctx)
242                 .service(serviceName)
243                 .path(objectPath)
244                 .properties();
245         auto availabilityProperties =
246             co_await AvailabilityIntf(ctx)
247                 .service(serviceName)
248                 .path(objectPath)
249                 .properties();
250         auto thresholdProperties =
251             co_await ThresholdCriticalIntf(ctx)
252                 .service(serviceName)
253                 .path(objectPath)
254                 .properties();
255         verifyResult(properties, operationalProperties, availabilityProperties,
256                      thresholdProperties, expectedValue,
257                      sensorRegisters[0].unit);
258         co_return;
259     }
260 };
261 
TEST_F(DeviceEventsTest,TestSensorReadingCritical)262 TEST_F(DeviceEventsTest, TestSensorReadingCritical)
263 {
264     eventServer.expectedEvent =
265         "xyz.openbmc_project.Sensor.Threshold.ReadingCritical";
266 
267     ctx.spawn(testSensorCreation(
268         objectPath, DeviceConfigIntf::StatusType::sensorReadingCritical,
269         TestIntf::testReadHoldingRegisterTempUnsigned[0]));
270 
271     ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
272               sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
273 
274     ctx.run();
275 }
276 
TEST_F(DeviceEventsTest,TestSensorFailure)277 TEST_F(DeviceEventsTest, TestSensorFailure)
278 {
279     eventServer.expectedEvent = "xyz.openbmc_project.Sensor.SensorFailure";
280 
281     ctx.spawn(testSensorCreation(objectPath,
282                                  DeviceConfigIntf::StatusType::sensorFailure,
283                                  std::numeric_limits<double>::quiet_NaN()));
284 
285     ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
286               sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
287 
288     ctx.run();
289 }
290