#include "dbus_environment.hpp" #include "discrete_threshold.hpp" #include "helpers.hpp" #include "mocks/clock_mock.hpp" #include "mocks/sensor_mock.hpp" #include "mocks/trigger_action_mock.hpp" #include "types/duration_types.hpp" #include "utils/conv_container.hpp" #include using namespace testing; using namespace std::chrono_literals; class TestDiscreteThreshold : public Test { public: std::vector> sensorMocks = { std::make_shared>(), std::make_shared>()}; std::vector sensorNames = {"Sensor1", "Sensor2"}; std::unique_ptr actionMockPtr = std::make_unique>(); TriggerActionMock& actionMock = *actionMockPtr; std::shared_ptr sut; std::string triggerId = "MyTrigger"; std::unique_ptr> clockMockPtr = std::make_unique>(); std::shared_ptr makeThreshold(Milliseconds dwellTime, std::string thresholdValue, discrete::Severity severity = discrete::Severity::ok, std::string thresholdName = "treshold name") { std::vector> actions; actions.push_back(std::move(actionMockPtr)); return std::make_shared( DbusEnvironment::getIoc(), triggerId, utils::convContainer>( sensorMocks), std::move(actions), dwellTime, thresholdValue, thresholdName, severity, std::move(clockMockPtr)); } void SetUp() override { for (size_t idx = 0; idx < sensorMocks.size(); idx++) { ON_CALL(*sensorMocks.at(idx), getName()) .WillByDefault(Return(sensorNames[idx])); } sut = makeThreshold(0ms, "90.0", discrete::Severity::critical); } }; TEST_F(TestDiscreteThreshold, initializeThresholdExpectAllSensorsAreRegistered) { for (auto& sensor : sensorMocks) { EXPECT_CALL(*sensor, registerForUpdates(Truly([sut = sut.get()](const auto& x) { return x.lock().get() == sut; }))); } sut->initialize(); } TEST_F(TestDiscreteThreshold, thresholdIsNotInitializeExpectNoActionCommit) { EXPECT_CALL(actionMock, commit(_, _, _, _, _)).Times(0); } class TestDiscreteThresholdValues : public TestDiscreteThreshold, public WithParamInterface {}; INSTANTIATE_TEST_SUITE_P(_, TestDiscreteThresholdValues, Values("90", ".90", "90.123", "0.0")); TEST_P(TestDiscreteThresholdValues, thresholdValueIsNumericAndStoredCorrectly) { sut = makeThreshold(0ms, GetParam(), discrete::Severity::critical); LabeledThresholdParam expected = discrete::LabeledThresholdParam( "treshold name", discrete::Severity::critical, 0, GetParam()); EXPECT_EQ(sut->getThresholdParam(), expected); } class TestBadDiscreteThresholdValues : public TestDiscreteThreshold, public WithParamInterface {}; INSTANTIATE_TEST_SUITE_P(_, TestBadDiscreteThresholdValues, Values("90ad", "ab.90", "x90", "On", "Off", "")); TEST_P(TestBadDiscreteThresholdValues, throwsWhenNotNumericValues) { EXPECT_THROW(makeThreshold(0ms, GetParam()), std::invalid_argument); } class TestDiscreteThresholdInit : public TestDiscreteThreshold { void SetUp() override {} }; TEST_F(TestDiscreteThresholdInit, nonEmptyNameIsNotChanged) { auto sut = makeThreshold(0ms, "12.3", discrete::Severity::ok, "non-empty"); EXPECT_THAT( std::get(sut->getThresholdParam()) .at_label(), Eq("non-empty")); } TEST_F(TestDiscreteThresholdInit, emptyNameIsChanged) { auto sut = makeThreshold(0ms, "12.3", discrete::Severity::ok, ""); EXPECT_THAT( std::get(sut->getThresholdParam()) .at_label(), Not(Eq(""))); } struct DiscreteParams { struct UpdateParams { size_t sensor; double value; Milliseconds sleepAfter; UpdateParams(size_t sensor, double value, Milliseconds sleepAfter = 0ms) : sensor(sensor), value(value), sleepAfter(sleepAfter) {} }; struct ExpectedParams { size_t sensor; double value; Milliseconds waitMin; ExpectedParams(size_t sensor, double value, Milliseconds waitMin = 0ms) : sensor(sensor), value(value), waitMin(waitMin) {} }; DiscreteParams& Updates(std::vector val) { updates = std::move(val); return *this; } DiscreteParams& Expected(std::vector val) { expected = std::move(val); return *this; } DiscreteParams& ThresholdValue(std::string val) { thresholdValue = std::move(val); return *this; } DiscreteParams& DwellTime(Milliseconds val) { dwellTime = std::move(val); return *this; } friend void PrintTo(const DiscreteParams& o, std::ostream* os) { *os << "{ DwellTime: " << o.dwellTime.count() << "ms "; *os << ", ThresholdValue: " << o.thresholdValue; *os << ", Updates: [ "; for (const auto& [index, value, sleepAfter] : o.updates) { *os << "{ SensorIndex: " << index << ", Value: " << value << ", SleepAfter: " << sleepAfter.count() << "ms }, "; } *os << " ] Expected: [ "; for (const auto& [index, value, waitMin] : o.expected) { *os << "{ SensorIndex: " << index << ", Value: " << value << ", waitMin: " << waitMin.count() << "ms }, "; } *os << " ] }"; } std::vector updates; std::vector expected; std::string thresholdValue = "0.0"; Milliseconds dwellTime = 0ms; }; class TestDiscreteThresholdCommon : public TestDiscreteThreshold, public WithParamInterface { public: void sleep(Milliseconds duration) { if (duration != 0ms) { DbusEnvironment::sleepFor(duration); } } void testBodySensorIsUpdatedMultipleTimes() { std::vector> timestamps(sensorMocks.size()); sut->initialize(); InSequence seq; for (const auto& [index, value, waitMin] : GetParam().expected) { EXPECT_CALL(actionMock, commit(triggerId, Optional(StrEq("treshold name")), sensorNames[index], _, TriggerValue(GetParam().thresholdValue))) .WillOnce(DoAll( InvokeWithoutArgs([idx = index, ×tamps] { timestamps[idx] = std::chrono::high_resolution_clock::now(); }), InvokeWithoutArgs(DbusEnvironment::setPromise("commit")))); } auto start = std::chrono::high_resolution_clock::now(); for (const auto& [index, value, sleepAfter] : GetParam().updates) { sut->sensorUpdated(*sensorMocks[index], 42ms, value); sleep(sleepAfter); } EXPECT_THAT(DbusEnvironment::waitForFutures("commit"), true); for (const auto& [index, value, waitMin] : GetParam().expected) { EXPECT_THAT(timestamps[index] - start, Ge(waitMin)); } } }; class TestDiscreteThresholdNoDwellTime : public TestDiscreteThresholdCommon { public: void SetUp() override { for (size_t idx = 0; idx < sensorMocks.size(); idx++) { ON_CALL(*sensorMocks.at(idx), getName()) .WillByDefault(Return(sensorNames[idx])); } sut = makeThreshold(0ms, GetParam().thresholdValue); } }; INSTANTIATE_TEST_SUITE_P( _, TestDiscreteThresholdNoDwellTime, Values(DiscreteParams() .ThresholdValue("90.0") .Updates({{0, 80.0}, {0, 89.0}}) .Expected({}), DiscreteParams() .ThresholdValue("90.0") .Updates({{0, 80.0}, {0, 90.0}, {0, 80.0}, {0, 90.0}}) .Expected({{0, 90.0}, {0, 90.0}}), DiscreteParams() .ThresholdValue("90.0") .Updates({{0, 90.0}, {0, 99.0}, {1, 100.0}, {1, 90.0}}) .Expected({{0, 90.0}, {1, 90.0}}))); TEST_P(TestDiscreteThresholdNoDwellTime, senorsIsUpdatedMultipleTimes) { testBodySensorIsUpdatedMultipleTimes(); } class TestDiscreteThresholdWithDwellTime : public TestDiscreteThresholdCommon { public: void SetUp() override { for (size_t idx = 0; idx < sensorMocks.size(); idx++) { ON_CALL(*sensorMocks.at(idx), getName()) .WillByDefault(Return(sensorNames[idx])); } sut = makeThreshold(GetParam().dwellTime, GetParam().thresholdValue); } }; INSTANTIATE_TEST_SUITE_P( _, TestDiscreteThresholdWithDwellTime, Values(DiscreteParams() .DwellTime(200ms) .ThresholdValue("90.0") .Updates({{0, 90.0, 100ms}, {0, 91.0}, {0, 90.0}}) .Expected({{0, 90.0, 300ms}}), DiscreteParams() .DwellTime(100ms) .ThresholdValue("90.0") .Updates({{0, 90.0, 100ms}}) .Expected({{0, 90.0, 100ms}}), DiscreteParams() .DwellTime(1000ms) .ThresholdValue("90.0") .Updates({{0, 90.0, 700ms}, {0, 91.0, 100ms}, {0, 90.0, 300ms}, {0, 91.0, 100ms}}) .Expected({}), DiscreteParams() .DwellTime(200ms) .ThresholdValue("90.0") .Updates({{0, 90.0}, {1, 89.0, 100ms}, {1, 90.0, 100ms}, {1, 89.0, 100ms}, {1, 90.0, 300ms}, {1, 89.0, 100ms}}) .Expected({{0, 90, 200ms}, {1, 90, 500ms}}))); TEST_P(TestDiscreteThresholdWithDwellTime, senorsIsUpdatedMultipleTimes) { testBodySensorIsUpdatedMultipleTimes(); }