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