xref: /openbmc/telemetry/tests/src/test_discrete_threshold.cpp (revision 583ba441654657bb4ba9d051b747144a7258c159)
1 #include "dbus_environment.hpp"
2 #include "discrete_threshold.hpp"
3 #include "helpers.hpp"
4 #include "mocks/clock_mock.hpp"
5 #include "mocks/sensor_mock.hpp"
6 #include "mocks/trigger_action_mock.hpp"
7 #include "types/duration_types.hpp"
8 #include "utils/conv_container.hpp"
9 
10 #include <gmock/gmock.h>
11 
12 using namespace testing;
13 using namespace std::chrono_literals;
14 
15 class TestDiscreteThreshold : public Test
16 {
17   public:
18     std::vector<std::shared_ptr<SensorMock>> sensorMocks = {
19         std::make_shared<NiceMock<SensorMock>>(),
20         std::make_shared<NiceMock<SensorMock>>()};
21     std::vector<std::string> sensorNames = {"Sensor1", "Sensor2"};
22     std::unique_ptr<TriggerActionMock> actionMockPtr =
23         std::make_unique<StrictMock<TriggerActionMock>>();
24     TriggerActionMock& actionMock = *actionMockPtr;
25     std::shared_ptr<DiscreteThreshold> sut;
26     std::string triggerId = "MyTrigger";
27     std::unique_ptr<NiceMock<ClockMock>> clockMockPtr =
28         std::make_unique<NiceMock<ClockMock>>();
29 
makeThreshold(Milliseconds dwellTime,std::string thresholdValue,discrete::Severity severity=discrete::Severity::ok,std::string thresholdName="treshold name")30     std::shared_ptr<DiscreteThreshold> makeThreshold(
31         Milliseconds dwellTime, std::string thresholdValue,
32         discrete::Severity severity = discrete::Severity::ok,
33         std::string thresholdName = "treshold name")
34     {
35         std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
36         actions.push_back(std::move(actionMockPtr));
37 
38         return std::make_shared<DiscreteThreshold>(
39             DbusEnvironment::getIoc(), triggerId,
40             utils::convContainer<std::shared_ptr<interfaces::Sensor>>(
41                 sensorMocks),
42             std::move(actions), dwellTime, thresholdValue, thresholdName,
43             severity, std::move(clockMockPtr));
44     }
45 
SetUp()46     void SetUp() override
47     {
48         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
49         {
50             ON_CALL(*sensorMocks.at(idx), getName())
51                 .WillByDefault(Return(sensorNames[idx]));
52         }
53 
54         sut = makeThreshold(0ms, "90.0", discrete::Severity::critical);
55     }
56 };
57 
TEST_F(TestDiscreteThreshold,initializeThresholdExpectAllSensorsAreRegistered)58 TEST_F(TestDiscreteThreshold, initializeThresholdExpectAllSensorsAreRegistered)
59 {
60     for (auto& sensor : sensorMocks)
61     {
62         EXPECT_CALL(*sensor,
63                     registerForUpdates(Truly([sut = sut.get()](const auto& x) {
64                         return x.lock().get() == sut;
65                     })));
66     }
67 
68     sut->initialize();
69 }
70 
TEST_F(TestDiscreteThreshold,thresholdIsNotInitializeExpectNoActionCommit)71 TEST_F(TestDiscreteThreshold, thresholdIsNotInitializeExpectNoActionCommit)
72 {
73     EXPECT_CALL(actionMock, commit(_, _, _, _, _)).Times(0);
74 }
75 
76 class TestDiscreteThresholdValues :
77     public TestDiscreteThreshold,
78     public WithParamInterface<std::string>
79 {};
80 
81 INSTANTIATE_TEST_SUITE_P(_, TestDiscreteThresholdValues,
82                          Values("90", ".90", "90.123", "0.0"));
83 
TEST_P(TestDiscreteThresholdValues,thresholdValueIsNumericAndStoredCorrectly)84 TEST_P(TestDiscreteThresholdValues, thresholdValueIsNumericAndStoredCorrectly)
85 {
86     sut = makeThreshold(0ms, GetParam(), discrete::Severity::critical);
87     LabeledThresholdParam expected = discrete::LabeledThresholdParam(
88         "treshold name", discrete::Severity::critical, 0, GetParam());
89     EXPECT_EQ(sut->getThresholdParam(), expected);
90 }
91 
92 class TestBadDiscreteThresholdValues :
93     public TestDiscreteThreshold,
94     public WithParamInterface<std::string>
95 {};
96 
97 INSTANTIATE_TEST_SUITE_P(_, TestBadDiscreteThresholdValues,
98                          Values("90ad", "ab.90", "x90", "On", "Off", ""));
99 
TEST_P(TestBadDiscreteThresholdValues,throwsWhenNotNumericValues)100 TEST_P(TestBadDiscreteThresholdValues, throwsWhenNotNumericValues)
101 {
102     EXPECT_THROW(makeThreshold(0ms, GetParam()), std::invalid_argument);
103 }
104 
105 class TestDiscreteThresholdInit : public TestDiscreteThreshold
106 {
SetUp()107     void SetUp() override {}
108 };
109 
TEST_F(TestDiscreteThresholdInit,nonEmptyNameIsNotChanged)110 TEST_F(TestDiscreteThresholdInit, nonEmptyNameIsNotChanged)
111 {
112     auto sut = makeThreshold(0ms, "12.3", discrete::Severity::ok, "non-empty");
113     EXPECT_THAT(
114         std::get<discrete::LabeledThresholdParam>(sut->getThresholdParam())
115             .at_label<utils::tstring::UserId>(),
116         Eq("non-empty"));
117 }
118 
TEST_F(TestDiscreteThresholdInit,emptyNameIsChanged)119 TEST_F(TestDiscreteThresholdInit, emptyNameIsChanged)
120 {
121     auto sut = makeThreshold(0ms, "12.3", discrete::Severity::ok, "");
122     EXPECT_THAT(
123         std::get<discrete::LabeledThresholdParam>(sut->getThresholdParam())
124             .at_label<utils::tstring::UserId>(),
125         Not(Eq("")));
126 }
127 
128 struct DiscreteParams
129 {
130     struct UpdateParams
131     {
132         size_t sensor;
133         double value;
134         Milliseconds sleepAfter;
135 
UpdateParamsDiscreteParams::UpdateParams136         UpdateParams(size_t sensor, double value,
137                      Milliseconds sleepAfter = 0ms) :
138             sensor(sensor), value(value), sleepAfter(sleepAfter)
139         {}
140     };
141 
142     struct ExpectedParams
143     {
144         size_t sensor;
145         double value;
146         Milliseconds waitMin;
147 
ExpectedParamsDiscreteParams::ExpectedParams148         ExpectedParams(size_t sensor, double value,
149                        Milliseconds waitMin = 0ms) :
150             sensor(sensor), value(value), waitMin(waitMin)
151         {}
152     };
153 
UpdatesDiscreteParams154     DiscreteParams& Updates(std::vector<UpdateParams> val)
155     {
156         updates = std::move(val);
157         return *this;
158     }
159 
ExpectedDiscreteParams160     DiscreteParams& Expected(std::vector<ExpectedParams> val)
161     {
162         expected = std::move(val);
163         return *this;
164     }
165 
ThresholdValueDiscreteParams166     DiscreteParams& ThresholdValue(std::string val)
167     {
168         thresholdValue = std::move(val);
169         return *this;
170     }
171 
DwellTimeDiscreteParams172     DiscreteParams& DwellTime(Milliseconds val)
173     {
174         dwellTime = std::move(val);
175         return *this;
176     }
177 
PrintTo(const DiscreteParams & o,std::ostream * os)178     friend void PrintTo(const DiscreteParams& o, std::ostream* os)
179     {
180         *os << "{ DwellTime: " << o.dwellTime.count() << "ms ";
181         *os << ", ThresholdValue: " << o.thresholdValue;
182         *os << ", Updates: [ ";
183         for (const auto& [index, value, sleepAfter] : o.updates)
184         {
185             *os << "{ SensorIndex: " << index << ", Value: " << value
186                 << ", SleepAfter: " << sleepAfter.count() << "ms }, ";
187         }
188         *os << " ] Expected: [ ";
189         for (const auto& [index, value, waitMin] : o.expected)
190         {
191             *os << "{ SensorIndex: " << index << ", Value: " << value
192                 << ", waitMin: " << waitMin.count() << "ms }, ";
193         }
194         *os << " ] }";
195     }
196 
197     std::vector<UpdateParams> updates;
198     std::vector<ExpectedParams> expected;
199     std::string thresholdValue = "0.0";
200     Milliseconds dwellTime = 0ms;
201 };
202 
203 class TestDiscreteThresholdCommon :
204     public TestDiscreteThreshold,
205     public WithParamInterface<DiscreteParams>
206 {
207   public:
sleep(Milliseconds duration)208     void sleep(Milliseconds duration)
209     {
210         if (duration != 0ms)
211         {
212             DbusEnvironment::sleepFor(duration);
213         }
214     }
215 
testBodySensorIsUpdatedMultipleTimes()216     void testBodySensorIsUpdatedMultipleTimes()
217     {
218         std::vector<std::chrono::time_point<std::chrono::high_resolution_clock>>
219             timestamps(sensorMocks.size());
220 
221         sut->initialize();
222 
223         InSequence seq;
224 
225         for (const auto& [index, value, waitMin] : GetParam().expected)
226         {
227             EXPECT_CALL(actionMock,
228                         commit(triggerId, Optional(StrEq("treshold name")),
229                                sensorNames[index], _,
230                                TriggerValue(GetParam().thresholdValue)))
231                 .WillOnce(DoAll(
232                     InvokeWithoutArgs([idx = index, &timestamps] {
233                         timestamps[idx] =
234                             std::chrono::high_resolution_clock::now();
235                     }),
236                     InvokeWithoutArgs(DbusEnvironment::setPromise("commit"))));
237         }
238 
239         auto start = std::chrono::high_resolution_clock::now();
240 
241         for (const auto& [index, value, sleepAfter] : GetParam().updates)
242         {
243             sut->sensorUpdated(*sensorMocks[index], 42ms, value);
244             sleep(sleepAfter);
245         }
246 
247         EXPECT_THAT(DbusEnvironment::waitForFutures("commit"), true);
248         for (const auto& [index, value, waitMin] : GetParam().expected)
249         {
250             EXPECT_THAT(timestamps[index] - start, Ge(waitMin));
251         }
252     }
253 };
254 
255 class TestDiscreteThresholdNoDwellTime : public TestDiscreteThresholdCommon
256 {
257   public:
SetUp()258     void SetUp() override
259     {
260         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
261         {
262             ON_CALL(*sensorMocks.at(idx), getName())
263                 .WillByDefault(Return(sensorNames[idx]));
264         }
265 
266         sut = makeThreshold(0ms, GetParam().thresholdValue);
267     }
268 };
269 
270 INSTANTIATE_TEST_SUITE_P(
271     _, TestDiscreteThresholdNoDwellTime,
272     Values(DiscreteParams()
273                .ThresholdValue("90.0")
274                .Updates({{0, 80.0}, {0, 89.0}})
275                .Expected({}),
276            DiscreteParams()
277                .ThresholdValue("90.0")
278                .Updates({{0, 80.0}, {0, 90.0}, {0, 80.0}, {0, 90.0}})
279                .Expected({{0, 90.0}, {0, 90.0}}),
280            DiscreteParams()
281                .ThresholdValue("90.0")
282                .Updates({{0, 90.0}, {0, 99.0}, {1, 100.0}, {1, 90.0}})
283                .Expected({{0, 90.0}, {1, 90.0}})));
284 
TEST_P(TestDiscreteThresholdNoDwellTime,senorsIsUpdatedMultipleTimes)285 TEST_P(TestDiscreteThresholdNoDwellTime, senorsIsUpdatedMultipleTimes)
286 {
287     testBodySensorIsUpdatedMultipleTimes();
288 }
289 
290 class TestDiscreteThresholdWithDwellTime : public TestDiscreteThresholdCommon
291 {
292   public:
SetUp()293     void SetUp() override
294     {
295         for (size_t idx = 0; idx < sensorMocks.size(); idx++)
296         {
297             ON_CALL(*sensorMocks.at(idx), getName())
298                 .WillByDefault(Return(sensorNames[idx]));
299         }
300 
301         sut = makeThreshold(GetParam().dwellTime, GetParam().thresholdValue);
302     }
303 };
304 
305 INSTANTIATE_TEST_SUITE_P(
306     _, TestDiscreteThresholdWithDwellTime,
307     Values(DiscreteParams()
308                .DwellTime(200ms)
309                .ThresholdValue("90.0")
310                .Updates({{0, 90.0, 100ms}, {0, 91.0}, {0, 90.0}})
311                .Expected({{0, 90.0, 300ms}}),
312            DiscreteParams()
313                .DwellTime(100ms)
314                .ThresholdValue("90.0")
315                .Updates({{0, 90.0, 100ms}})
316                .Expected({{0, 90.0, 100ms}}),
317            DiscreteParams()
318                .DwellTime(1000ms)
319                .ThresholdValue("90.0")
320                .Updates({{0, 90.0, 700ms},
321                          {0, 91.0, 100ms},
322                          {0, 90.0, 300ms},
323                          {0, 91.0, 100ms}})
324                .Expected({}),
325            DiscreteParams()
326                .DwellTime(200ms)
327                .ThresholdValue("90.0")
328                .Updates({{0, 90.0},
329                          {1, 89.0, 100ms},
330                          {1, 90.0, 100ms},
331                          {1, 89.0, 100ms},
332                          {1, 90.0, 300ms},
333                          {1, 89.0, 100ms}})
334                .Expected({{0, 90, 200ms}, {1, 90, 500ms}})));
335 
TEST_P(TestDiscreteThresholdWithDwellTime,senorsIsUpdatedMultipleTimes)336 TEST_P(TestDiscreteThresholdWithDwellTime, senorsIsUpdatedMultipleTimes)
337 {
338     testBodySensorIsUpdatedMultipleTimes();
339 }
340