xref: /openbmc/telemetry/tests/src/test_discrete_threshold.cpp (revision 51f0fd501f4b772533271d15cb27d396186a7192)
1 #include "dbus_environment.hpp"
2 #include "discrete_threshold.hpp"
3 #include "helpers.hpp"
4 #include "mocks/sensor_mock.hpp"
5 #include "mocks/trigger_action_mock.hpp"
6 #include "types/duration_types.hpp"
7 #include "utils/conv_container.hpp"
8 
9 #include <gmock/gmock.h>
10 
11 using namespace testing;
12 using namespace std::chrono_literals;
13 
14 class TestDiscreteThreshold : public Test
15 {
16   public:
17     std::vector<std::shared_ptr<SensorMock>> sensorMocks = {
18         std::make_shared<NiceMock<SensorMock>>(),
19         std::make_shared<NiceMock<SensorMock>>()};
20     std::vector<std::string> sensorNames = {"Sensor1", "Sensor2"};
21     std::unique_ptr<TriggerActionMock> actionMockPtr =
22         std::make_unique<StrictMock<TriggerActionMock>>();
23     TriggerActionMock& actionMock = *actionMockPtr;
24     std::shared_ptr<DiscreteThreshold> sut;
25 
26     std::shared_ptr<DiscreteThreshold> makeThreshold(Milliseconds dwellTime,
27                                                      double thresholdValue)
28     {
29         std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
30         actions.push_back(std::move(actionMockPtr));
31 
32         return std::make_shared<DiscreteThreshold>(
33             DbusEnvironment::getIoc(),
34             utils::convContainer<std::shared_ptr<interfaces::Sensor>>(
35                 sensorMocks),
36             sensorNames, std::move(actions), dwellTime, thresholdValue,
37             "treshold_name");
38     }
39 
40     void SetUp() override
41     {
42         sut = makeThreshold(0ms, 90.0);
43     }
44 };
45 
46 TEST_F(TestDiscreteThreshold, initializeThresholdExpectAllSensorsAreRegistered)
47 {
48     for (auto& sensor : sensorMocks)
49     {
50         EXPECT_CALL(*sensor,
51                     registerForUpdates(Truly([sut = sut.get()](const auto& x) {
52                         return x.lock().get() == sut;
53                     })));
54     }
55 
56     sut->initialize();
57 }
58 
59 TEST_F(TestDiscreteThreshold, thresholdIsNotInitializeExpectNoActionCommit)
60 {
61     EXPECT_CALL(actionMock, commit(_, _, _)).Times(0);
62 }
63 
64 struct DiscreteParams
65 {
66     struct UpdateParams
67     {
68         size_t sensor;
69         Milliseconds timestamp;
70         double value;
71         Milliseconds sleepAfter;
72 
73         UpdateParams(size_t sensor, Milliseconds timestamp, double value,
74                      Milliseconds sleepAfter = 0ms) :
75             sensor(sensor),
76             timestamp(timestamp), value(value), sleepAfter(sleepAfter)
77         {}
78     };
79 
80     struct ExpectedParams
81     {
82         size_t sensor;
83         Milliseconds timestamp;
84         double value;
85         Milliseconds waitMin;
86 
87         ExpectedParams(size_t sensor, Milliseconds timestamp, double value,
88                        Milliseconds waitMin = 0ms) :
89             sensor(sensor),
90             timestamp(timestamp), value(value), waitMin(waitMin)
91         {}
92     };
93 
94     DiscreteParams& Updates(std::vector<UpdateParams> val)
95     {
96         updates = std::move(val);
97         return *this;
98     }
99 
100     DiscreteParams& Expected(std::vector<ExpectedParams> val)
101     {
102         expected = std::move(val);
103         return *this;
104     }
105 
106     DiscreteParams& ThresholdValue(double val)
107     {
108         thresholdValue = val;
109         return *this;
110     }
111 
112     DiscreteParams& DwellTime(Milliseconds val)
113     {
114         dwellTime = std::move(val);
115         return *this;
116     }
117 
118     friend void PrintTo(const DiscreteParams& o, std::ostream* os)
119     {
120         *os << "{ DwellTime: " << o.dwellTime.count() << "ms ";
121         *os << ", ThresholdValue: " << o.thresholdValue;
122         *os << ", Updates: ";
123         for (const auto& [index, timestamp, value, sleepAfter] : o.updates)
124         {
125             *os << "{ SensorIndex: " << index
126                 << ", Timestamp: " << timestamp.count() << ", Value: " << value
127                 << ", SleepAfter: " << sleepAfter.count() << "ms }, ";
128         }
129         *os << "Expected: ";
130         for (const auto& [index, timestamp, value, waitMin] : o.expected)
131         {
132             *os << "{ SensorIndex: " << index
133                 << ", Timestamp: " << timestamp.count() << ", Value: " << value
134                 << ", waitMin: " << waitMin.count() << "ms }, ";
135         }
136         *os << " }";
137     }
138 
139     std::vector<UpdateParams> updates;
140     std::vector<ExpectedParams> expected;
141     double thresholdValue = 0.0;
142     Milliseconds dwellTime = 0ms;
143 };
144 
145 class TestDiscreteThresholdCommon :
146     public TestDiscreteThreshold,
147     public WithParamInterface<DiscreteParams>
148 {
149   public:
150     void sleep(Milliseconds duration)
151     {
152         if (duration != 0ms)
153         {
154             DbusEnvironment::sleepFor(duration);
155         }
156     }
157 
158     void testBodySensorIsUpdatedMultipleTimes()
159     {
160         std::vector<std::chrono::time_point<std::chrono::high_resolution_clock>>
161             timestamps(sensorMocks.size());
162 
163         sut->initialize();
164 
165         InSequence seq;
166 
167         for (const auto& [index, timestamp, value, waitMin] :
168              GetParam().expected)
169         {
170             EXPECT_CALL(actionMock,
171                         commit(sensorNames[index], timestamp, value))
172                 .WillOnce(DoAll(
173                     InvokeWithoutArgs([idx = index, &timestamps] {
174                         timestamps[idx] =
175                             std::chrono::high_resolution_clock::now();
176                     }),
177                     InvokeWithoutArgs(DbusEnvironment::setPromise("commit"))));
178         }
179 
180         auto start = std::chrono::high_resolution_clock::now();
181 
182         for (const auto& [index, timestamp, value, sleepAfter] :
183              GetParam().updates)
184         {
185             sut->sensorUpdated(*sensorMocks[index], timestamp, value);
186             sleep(sleepAfter);
187         }
188 
189         EXPECT_THAT(DbusEnvironment::waitForFutures("commit"), true);
190         for (const auto& [index, timestamp, value, waitMin] :
191              GetParam().expected)
192         {
193             EXPECT_THAT(timestamps[index] - start, Ge(waitMin));
194         }
195     }
196 };
197 
198 class TestDiscreteThresholdNoDwellTime : public TestDiscreteThresholdCommon
199 {
200   public:
201     void SetUp() override
202     {
203         sut = makeThreshold(0ms, GetParam().thresholdValue);
204     }
205 };
206 
207 INSTANTIATE_TEST_SUITE_P(_, TestDiscreteThresholdNoDwellTime,
208                          Values(DiscreteParams()
209                                     .ThresholdValue(90.0)
210                                     .Updates({{0, 1ms, 80.0}, {0, 2ms, 89.0}})
211                                     .Expected({}),
212                                 DiscreteParams()
213                                     .ThresholdValue(90.0)
214                                     .Updates({{0, 1ms, 80.0},
215                                               {0, 2ms, 90.0},
216                                               {0, 3ms, 80.0},
217                                               {0, 4ms, 90.0}})
218                                     .Expected({{0, 2ms, 90.0}, {0, 4ms, 90.0}}),
219                                 DiscreteParams()
220                                     .ThresholdValue(90.0)
221                                     .Updates({{0, 1ms, 90.0},
222                                               {0, 2ms, 99.0},
223                                               {1, 3ms, 100.0},
224                                               {1, 4ms, 90.0}})
225                                     .Expected({{0, 1ms, 90.0},
226                                                {1, 4ms, 90.0}})));
227 
228 TEST_P(TestDiscreteThresholdNoDwellTime, senorsIsUpdatedMultipleTimes)
229 {
230     testBodySensorIsUpdatedMultipleTimes();
231 }
232 
233 class TestDiscreteThresholdWithDwellTime : public TestDiscreteThresholdCommon
234 {
235   public:
236     void SetUp() override
237     {
238         sut = makeThreshold(GetParam().dwellTime, GetParam().thresholdValue);
239     }
240 };
241 
242 INSTANTIATE_TEST_SUITE_P(
243     _, TestDiscreteThresholdWithDwellTime,
244     Values(DiscreteParams()
245                .DwellTime(200ms)
246                .ThresholdValue(90.0)
247                .Updates({{0, 1ms, 90.0, 100ms}, {0, 2ms, 91.0}, {0, 3ms, 90.0}})
248                .Expected({{0, 3ms, 90.0, 300ms}}),
249            DiscreteParams()
250                .DwellTime(100ms)
251                .ThresholdValue(90.0)
252                .Updates({{0, 1ms, 90.0, 100ms}})
253                .Expected({{0, 1ms, 90.0, 100ms}}),
254            DiscreteParams()
255                .DwellTime(1000ms)
256                .ThresholdValue(90.0)
257                .Updates({{0, 1ms, 90.0, 700ms},
258                          {0, 1ms, 91.0, 100ms},
259                          {0, 1ms, 90.0, 300ms},
260                          {0, 1ms, 91.0, 100ms}})
261                .Expected({}),
262            DiscreteParams()
263                .DwellTime(200ms)
264                .ThresholdValue(90.0)
265                .Updates({{0, 1ms, 90.0},
266                          {1, 2ms, 89.0, 100ms},
267                          {1, 3ms, 90.0, 100ms},
268                          {1, 4ms, 89.0, 100ms},
269                          {1, 5ms, 90.0, 300ms},
270                          {1, 6ms, 89.0, 100ms}})
271                .Expected({{0, 1ms, 90, 200ms}, {1, 5ms, 90, 500ms}})));
272 
273 TEST_P(TestDiscreteThresholdWithDwellTime, senorsIsUpdatedMultipleTimes)
274 {
275     testBodySensorIsUpdatedMultipleTimes();
276 }
277