| #include "dbus_environment.hpp" |
| #include "helpers.hpp" |
| #include "messages/collect_trigger_id.hpp" |
| #include "messages/trigger_presence_changed_ind.hpp" |
| #include "mocks/json_storage_mock.hpp" |
| #include "mocks/report_manager_mock.hpp" |
| #include "mocks/sensor_mock.hpp" |
| #include "mocks/threshold_mock.hpp" |
| #include "mocks/trigger_factory_mock.hpp" |
| #include "mocks/trigger_manager_mock.hpp" |
| #include "params/trigger_params.hpp" |
| #include "trigger.hpp" |
| #include "trigger_manager.hpp" |
| #include "utils/conversion_trigger.hpp" |
| #include "utils/dbus_path_utils.hpp" |
| #include "utils/messanger.hpp" |
| #include "utils/string_utils.hpp" |
| #include "utils/transform.hpp" |
| #include "utils/tstring.hpp" |
| |
| #include <boost/range/combine.hpp> |
| |
| using namespace testing; |
| using namespace std::literals::string_literals; |
| using sdbusplus::message::object_path; |
| |
| static constexpr size_t expectedTriggerVersion = 2; |
| |
| class TestTrigger : public Test |
| { |
| public: |
| TriggerParams triggerParams; |
| TriggerParams triggerDiscreteParams = |
| TriggerParams() |
| .id("DiscreteTrigger") |
| .name("My Discrete Trigger") |
| .thresholdParams(std::vector<discrete::LabeledThresholdParam>{ |
| discrete::LabeledThresholdParam{ |
| "userId", discrete::Severity::warning, |
| Milliseconds(10).count(), "15.2"}, |
| discrete::LabeledThresholdParam{ |
| "userId_2", discrete::Severity::critical, |
| Milliseconds(5).count(), "32.7"}, |
| }); |
| |
| std::unique_ptr<ReportManagerMock> reportManagerMockPtr = |
| std::make_unique<NiceMock<ReportManagerMock>>(); |
| std::unique_ptr<TriggerManagerMock> triggerManagerMockPtr = |
| std::make_unique<NiceMock<TriggerManagerMock>>(); |
| std::unique_ptr<TriggerFactoryMock> triggerFactoryMockPtr = |
| std::make_unique<NiceMock<TriggerFactoryMock>>(); |
| testing::NiceMock<StorageMock> storageMock; |
| NiceMock<MockFunction<void(const messages::TriggerPresenceChangedInd)>> |
| triggerPresenceChanged; |
| std::vector<std::shared_ptr<interfaces::Threshold>> thresholdMocks; |
| utils::Messanger messanger; |
| std::unique_ptr<Trigger> sut; |
| |
| TestTrigger() : messanger(DbusEnvironment::getIoc()) |
| { |
| messanger.on_receive<messages::TriggerPresenceChangedInd>( |
| [this](const auto& msg) { triggerPresenceChanged.Call(msg); }); |
| } |
| |
| void SetUp() override |
| { |
| sut = makeTrigger(triggerParams); |
| } |
| |
| static std::vector<LabeledSensorInfo> |
| convertToLabeledSensor(const SensorsInfo& sensorsInfo) |
| { |
| return utils::transform(sensorsInfo, [](const auto& sensorInfo) { |
| const auto& [sensorPath, sensorMetadata] = sensorInfo; |
| return LabeledSensorInfo("service1", sensorPath, sensorMetadata); |
| }); |
| } |
| |
| std::unique_ptr<Trigger> makeTrigger(const TriggerParams& params) |
| { |
| thresholdMocks = |
| ThresholdMock::makeThresholds(params.thresholdParams()); |
| |
| auto id = std::make_unique<const std::string>(params.id()); |
| |
| return std::make_unique<Trigger>( |
| DbusEnvironment::getIoc(), DbusEnvironment::getObjServer(), |
| std::move(id), params.name(), params.triggerActions(), |
| std::make_shared<std::vector<std::string>>( |
| params.reportIds().begin(), params.reportIds().end()), |
| std::vector<std::shared_ptr<interfaces::Threshold>>(thresholdMocks), |
| *triggerManagerMockPtr, storageMock, *triggerFactoryMockPtr, |
| SensorMock::makeSensorMocks(params.sensors())); |
| } |
| |
| static interfaces::JsonStorage::FilePath to_file_path(std::string name) |
| { |
| return interfaces::JsonStorage::FilePath( |
| std::to_string(std::hash<std::string>{}(name))); |
| } |
| |
| template <class T> |
| static T getProperty(const std::string& path, const std::string& property) |
| { |
| return DbusEnvironment::getProperty<T>(path, Trigger::triggerIfaceName, |
| property); |
| } |
| |
| template <class T> |
| static boost::system::error_code setProperty(const std::string& path, |
| const std::string& property, |
| const T& newValue) |
| { |
| return DbusEnvironment::setProperty<T>(path, Trigger::triggerIfaceName, |
| property, newValue); |
| } |
| |
| template <class T> |
| struct ChangePropertyParams |
| { |
| Matcher<T> valueBefore = _; |
| T newValue; |
| Matcher<boost::system::error_code> ec = |
| Eq(boost::system::errc::success); |
| Matcher<T> valueAfter = Eq(newValue); |
| }; |
| |
| template <class T> |
| static void changeProperty(const std::string& path, |
| const std::string& property, |
| ChangePropertyParams<T> p) |
| { |
| ASSERT_THAT(getProperty<T>(path, property), p.valueBefore); |
| ASSERT_THAT(setProperty<T>(path, property, p.newValue), p.ec); |
| EXPECT_THAT(getProperty<T>(path, property), p.valueAfter); |
| } |
| |
| boost::system::error_code deleteTrigger(const std::string& path) |
| { |
| std::promise<boost::system::error_code> methodPromise; |
| DbusEnvironment::getBus()->async_method_call( |
| [&methodPromise](boost::system::error_code ec) { |
| methodPromise.set_value(ec); |
| }, |
| DbusEnvironment::serviceName(), path, Trigger::deleteIfaceName, |
| "Delete"); |
| return DbusEnvironment::waitForFuture(methodPromise.get_future()); |
| } |
| }; |
| |
| TEST_F(TestTrigger, checkIfPropertiesAreSet) |
| { |
| EXPECT_THAT(getProperty<std::string>(sut->getPath(), "Name"), |
| Eq(triggerParams.name())); |
| EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistent"), Eq(true)); |
| EXPECT_THAT( |
| getProperty<std::vector<std::string>>(sut->getPath(), "TriggerActions"), |
| Eq(utils::transform(triggerParams.triggerActions(), |
| [](const auto& action) { |
| return actionToString(action); |
| }))); |
| EXPECT_THAT((getProperty<SensorsInfo>(sut->getPath(), "Sensors")), |
| Eq(utils::fromLabeledSensorsInfo(triggerParams.sensors()))); |
| EXPECT_THAT( |
| getProperty<std::vector<object_path>>(sut->getPath(), "Reports"), |
| Eq(triggerParams.reports())); |
| EXPECT_THAT( |
| getProperty<bool>(sut->getPath(), "Discrete"), |
| Eq(isTriggerThresholdDiscrete(triggerParams.thresholdParams()))); |
| EXPECT_THAT( |
| getProperty<TriggerThresholdParams>(sut->getPath(), "Thresholds"), |
| Eq(std::visit(utils::FromLabeledThresholdParamConversion(), |
| triggerParams.thresholdParams()))); |
| } |
| |
| TEST_F(TestTrigger, checkBasicGetters) |
| { |
| EXPECT_THAT(sut->getId(), Eq(triggerParams.id())); |
| EXPECT_THAT(sut->getPath(), |
| Eq(utils::constants::triggerDirPath.str + triggerParams.id())); |
| } |
| |
| TEST_F(TestTrigger, setPropertyNameToCorrectValue) |
| { |
| std::string name = "custom name 1234 %^#5"; |
| EXPECT_THAT(setProperty(sut->getPath(), "Name", name), |
| Eq(boost::system::errc::success)); |
| EXPECT_THAT(getProperty<std::string>(sut->getPath(), "Name"), Eq(name)); |
| } |
| |
| TEST_F(TestTrigger, setPropertyReportNames) |
| { |
| std::vector<object_path> newNames = { |
| utils::constants::reportDirPath / "abc", |
| utils::constants::reportDirPath / "one", |
| utils::constants::reportDirPath / "prefix" / "two"}; |
| EXPECT_THAT(setProperty(sut->getPath(), "Reports", newNames), |
| Eq(boost::system::errc::success)); |
| EXPECT_THAT( |
| getProperty<std::vector<object_path>>(sut->getPath(), "Reports"), |
| Eq(newNames)); |
| } |
| |
| TEST_F(TestTrigger, sendsUpdateWhenReportNamesChanges) |
| { |
| std::vector<object_path> newPropertyVal = { |
| utils::constants::reportDirPath / "abc", |
| utils::constants::reportDirPath / "one", |
| utils::constants::reportDirPath / "two"}; |
| |
| EXPECT_CALL(triggerPresenceChanged, |
| Call(FieldsAre(messages::Presence::Exist, triggerParams.id(), |
| UnorderedElementsAre("abc", "one", "two")))); |
| |
| EXPECT_THAT(setProperty(sut->getPath(), "Reports", newPropertyVal), |
| Eq(boost::system::errc::success)); |
| } |
| |
| TEST_F(TestTrigger, sendsUpdateWhenReportNamesChangesToSameValue) |
| { |
| const std::vector<object_path> newPropertyVal = triggerParams.reports(); |
| |
| EXPECT_CALL( |
| triggerPresenceChanged, |
| Call(FieldsAre(messages::Presence::Exist, triggerParams.id(), |
| UnorderedElementsAreArray(triggerParams.reportIds())))); |
| |
| EXPECT_THAT(setProperty(sut->getPath(), "Reports", newPropertyVal), |
| Eq(boost::system::errc::success)); |
| } |
| |
| TEST_F(TestTrigger, |
| DISABLED_settingPropertyReportNamesThrowsExceptionWhenDuplicateReportIds) |
| { |
| std::vector<object_path> newPropertyVal{ |
| utils::constants::reportDirPath / "report1", |
| utils::constants::reportDirPath / "report2", |
| utils::constants::reportDirPath / "report1"}; |
| |
| EXPECT_CALL(triggerPresenceChanged, Call(_)).Times(0); |
| |
| EXPECT_THAT(setProperty(sut->getPath(), "Reports", newPropertyVal), |
| Eq(boost::system::errc::invalid_argument)); |
| } |
| |
| TEST_F( |
| TestTrigger, |
| DISABLED_settingPropertyReportNamesThrowsExceptionWhenReportWithTooManyPrefixes) |
| { |
| std::vector<object_path> newPropertyVal{ |
| object_path("/xyz/openbmc_project/Telemetry/Reports/P1/P2/MyReport")}; |
| |
| EXPECT_CALL(triggerPresenceChanged, Call(_)).Times(0); |
| |
| EXPECT_THAT(setProperty(sut->getPath(), "Reports", newPropertyVal), |
| Eq(boost::system::errc::invalid_argument)); |
| } |
| |
| TEST_F( |
| TestTrigger, |
| DISABLED_settingPropertyReportNamesThrowsExceptionWhenReportWithTooLongPrefix) |
| { |
| std::vector<object_path> newPropertyVal{ |
| object_path("/xyz/openbmc_project/Telemetry/Reports/" + |
| utils::string_utils::getTooLongPrefix() + "/MyReport")}; |
| |
| EXPECT_CALL(triggerPresenceChanged, Call(_)).Times(0); |
| |
| EXPECT_THAT(setProperty(sut->getPath(), "Reports", newPropertyVal), |
| Eq(boost::system::errc::invalid_argument)); |
| } |
| |
| TEST_F( |
| TestTrigger, |
| DISABLED_settingPropertyReportNamesThrowsExceptionWhenReportWithTooLongId) |
| { |
| std::vector<object_path> newPropertyVal{ |
| object_path("/xyz/openbmc_project/Telemetry/Reports/Prefix/" + |
| utils::string_utils::getTooLongId())}; |
| |
| EXPECT_CALL(triggerPresenceChanged, Call(_)).Times(0); |
| |
| EXPECT_THAT(setProperty(sut->getPath(), "Reports", newPropertyVal), |
| Eq(boost::system::errc::invalid_argument)); |
| } |
| |
| TEST_F(TestTrigger, |
| DISABLED_settingPropertyReportNamesThrowsExceptionWhenReportWithBadPath) |
| { |
| std::vector<object_path> newPropertyVal{ |
| object_path("/xyz/openbmc_project/Telemetry/NotReports/MyReport")}; |
| |
| EXPECT_CALL(triggerPresenceChanged, Call(_)).Times(0); |
| |
| EXPECT_THAT(setProperty(sut->getPath(), "Reports", newPropertyVal), |
| Eq(boost::system::errc::invalid_argument)); |
| } |
| |
| TEST_F(TestTrigger, setPropertySensors) |
| { |
| EXPECT_CALL(*triggerFactoryMockPtr, updateSensors(_, _)); |
| for (const auto& threshold : thresholdMocks) |
| { |
| auto thresholdMockPtr = |
| std::dynamic_pointer_cast<NiceMock<ThresholdMock>>(threshold); |
| EXPECT_CALL(*thresholdMockPtr, updateSensors(_)); |
| } |
| SensorsInfo newSensors( |
| {std::make_pair(object_path("/abc/def"), "metadata")}); |
| EXPECT_THAT(setProperty(sut->getPath(), "Sensors", newSensors), |
| Eq(boost::system::errc::success)); |
| } |
| |
| TEST_F(TestTrigger, setPropertyThresholds) |
| { |
| EXPECT_CALL(*triggerFactoryMockPtr, updateThresholds(_, _, _, _, _, _)); |
| TriggerThresholdParams newThresholds = |
| std::vector<discrete::ThresholdParam>( |
| {std::make_tuple("discrete threshold", "OK", 10, "12.3")}); |
| EXPECT_THAT(setProperty(sut->getPath(), "Thresholds", newThresholds), |
| Eq(boost::system::errc::success)); |
| } |
| |
| TEST_F(TestTrigger, setThresholdParamsWithTooLongDiscreteName) |
| { |
| const TriggerThresholdParams currentValue = |
| std::visit(utils::FromLabeledThresholdParamConversion(), |
| triggerParams.thresholdParams()); |
| |
| TriggerThresholdParams newThresholds = |
| std::vector<discrete::ThresholdParam>({std::make_tuple( |
| utils::string_utils::getTooLongName(), "OK", 10, "12.3")}); |
| |
| changeProperty<TriggerThresholdParams>( |
| sut->getPath(), "Thresholds", |
| {.valueBefore = Eq(currentValue), |
| .newValue = newThresholds, |
| .ec = Eq(boost::system::errc::invalid_argument), |
| .valueAfter = Eq(currentValue)}); |
| } |
| |
| TEST_F(TestTrigger, setNameTooLong) |
| { |
| std::string currentValue = TriggerParams().name(); |
| |
| changeProperty<std::string>( |
| sut->getPath(), "Name", |
| {.valueBefore = Eq(currentValue), |
| .newValue = utils::string_utils::getTooLongName(), |
| .ec = Eq(boost::system::errc::invalid_argument), |
| .valueAfter = Eq(currentValue)}); |
| } |
| |
| TEST_F(TestTrigger, checkIfNumericCoversionsAreGood) |
| { |
| const auto& labeledParamsBase = |
| std::get<std::vector<numeric::LabeledThresholdParam>>( |
| triggerParams.thresholdParams()); |
| const auto paramsToCheck = |
| std::visit(utils::FromLabeledThresholdParamConversion(), |
| triggerParams.thresholdParams()); |
| const auto labeledParamsToCheck = |
| std::get<std::vector<numeric::LabeledThresholdParam>>(std::visit( |
| utils::ToLabeledThresholdParamConversion(), paramsToCheck)); |
| |
| for (const auto& [tocheck, base] : |
| boost::combine(labeledParamsToCheck, labeledParamsBase)) |
| { |
| EXPECT_THAT(tocheck.at_label<utils::tstring::Type>(), |
| Eq(base.at_label<utils::tstring::Type>())); |
| EXPECT_THAT(tocheck.at_label<utils::tstring::Direction>(), |
| Eq(base.at_label<utils::tstring::Direction>())); |
| EXPECT_THAT(tocheck.at_label<utils::tstring::DwellTime>(), |
| Eq(base.at_label<utils::tstring::DwellTime>())); |
| EXPECT_THAT(tocheck.at_label<utils::tstring::ThresholdValue>(), |
| Eq(base.at_label<utils::tstring::ThresholdValue>())); |
| } |
| } |
| |
| TEST_F(TestTrigger, checkIfDiscreteCoversionsAreGood) |
| { |
| const auto& labeledParamsBase = |
| std::get<std::vector<discrete::LabeledThresholdParam>>( |
| triggerDiscreteParams.thresholdParams()); |
| const auto paramsToCheck = |
| std::visit(utils::FromLabeledThresholdParamConversion(), |
| triggerDiscreteParams.thresholdParams()); |
| const auto labeledParamsToCheck = |
| std::get<std::vector<discrete::LabeledThresholdParam>>(std::visit( |
| utils::ToLabeledThresholdParamConversion(), paramsToCheck)); |
| |
| for (const auto& [tocheck, base] : |
| boost::combine(labeledParamsToCheck, labeledParamsBase)) |
| { |
| EXPECT_THAT(tocheck.at_label<utils::tstring::UserId>(), |
| Eq(base.at_label<utils::tstring::UserId>())); |
| EXPECT_THAT(tocheck.at_label<utils::tstring::Severity>(), |
| Eq(base.at_label<utils::tstring::Severity>())); |
| EXPECT_THAT(tocheck.at_label<utils::tstring::DwellTime>(), |
| Eq(base.at_label<utils::tstring::DwellTime>())); |
| EXPECT_THAT(tocheck.at_label<utils::tstring::ThresholdValue>(), |
| Eq(base.at_label<utils::tstring::ThresholdValue>())); |
| } |
| } |
| |
| TEST_F(TestTrigger, deleteTrigger) |
| { |
| EXPECT_CALL(storageMock, remove(to_file_path(sut->getId()))); |
| EXPECT_CALL(*triggerManagerMockPtr, removeTrigger(sut.get())); |
| |
| auto ec = deleteTrigger(sut->getPath()); |
| EXPECT_THAT(ec, Eq(boost::system::errc::success)); |
| } |
| |
| TEST_F(TestTrigger, sendUpdateWhenTriggerIsDeleted) |
| { |
| EXPECT_CALL(triggerPresenceChanged, |
| Call(FieldsAre(messages::Presence::Removed, triggerParams.id(), |
| UnorderedElementsAre()))); |
| |
| auto ec = deleteTrigger(sut->getPath()); |
| EXPECT_THAT(ec, Eq(boost::system::errc::success)); |
| } |
| |
| TEST_F(TestTrigger, deletingNonExistingTriggerReturnInvalidRequestDescriptor) |
| { |
| auto ec = |
| deleteTrigger(utils::constants::triggerDirPath.str + "NonExisting"s); |
| EXPECT_THAT(ec.value(), Eq(EBADR)); |
| } |
| |
| TEST_F(TestTrigger, settingPersistencyToFalseRemovesTriggerFromStorage) |
| { |
| EXPECT_CALL(storageMock, remove(to_file_path(sut->getId()))); |
| |
| bool persistent = false; |
| EXPECT_THAT(setProperty(sut->getPath(), "Persistent", persistent), |
| Eq(boost::system::errc::success)); |
| EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistent"), |
| Eq(persistent)); |
| } |
| |
| class TestTriggerInitialization : public TestTrigger |
| { |
| public: |
| void SetUp() override {} |
| |
| nlohmann::json storedConfiguration; |
| }; |
| |
| TEST_F(TestTriggerInitialization, |
| exceptionDuringTriggerStoreDisablesPersistency) |
| { |
| EXPECT_CALL(storageMock, store(_, _)) |
| .WillOnce(Throw(std::runtime_error("Generic error!"))); |
| |
| sut = makeTrigger(triggerParams); |
| |
| EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistent"), Eq(false)); |
| } |
| |
| TEST_F(TestTriggerInitialization, creatingTriggerThrowsExceptionWhenIdIsInvalid) |
| { |
| EXPECT_CALL(storageMock, store(_, _)).Times(0); |
| |
| EXPECT_THROW(makeTrigger(triggerParams.id("inv?lidId")), |
| sdbusplus::exception::SdBusError); |
| } |
| |
| TEST_F(TestTriggerInitialization, creatingTriggerUpdatesTriggersIdsInReports) |
| { |
| EXPECT_CALL( |
| triggerPresenceChanged, |
| Call(FieldsAre(messages::Presence::Exist, triggerParams.id(), |
| UnorderedElementsAreArray(triggerParams.reportIds())))); |
| |
| sut = makeTrigger(triggerParams); |
| } |
| |
| class TestTriggerStore : public TestTrigger |
| { |
| public: |
| nlohmann::json storedConfiguration; |
| nlohmann::json storedDiscreteConfiguration; |
| std::unique_ptr<Trigger> sutDiscrete; |
| |
| void SetUp() override |
| { |
| ON_CALL(storageMock, store(_, _)) |
| .WillByDefault(SaveArg<1>(&storedConfiguration)); |
| sut = makeTrigger(triggerParams); |
| |
| ON_CALL(storageMock, store(_, _)) |
| .WillByDefault(SaveArg<1>(&storedDiscreteConfiguration)); |
| sutDiscrete = makeTrigger(triggerDiscreteParams); |
| } |
| }; |
| |
| TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerVersion) |
| { |
| ASSERT_THAT(storedConfiguration.at("Version"), Eq(expectedTriggerVersion)); |
| } |
| |
| TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerId) |
| { |
| ASSERT_THAT(storedConfiguration.at("Id"), Eq(triggerParams.id())); |
| } |
| |
| TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerName) |
| { |
| ASSERT_THAT(storedConfiguration.at("Name"), Eq(triggerParams.name())); |
| } |
| |
| TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerTriggerActions) |
| { |
| ASSERT_THAT(storedConfiguration.at("TriggerActions"), |
| Eq(utils::transform(triggerParams.triggerActions(), |
| [](const auto& action) { |
| return actionToString(action); |
| }))); |
| } |
| |
| TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerReportIds) |
| { |
| ASSERT_THAT(storedConfiguration.at("ReportIds"), |
| Eq(triggerParams.reportIds())); |
| } |
| |
| TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerSensors) |
| { |
| nlohmann::json expectedItem; |
| expectedItem["service"] = "service1"; |
| expectedItem["path"] = "/xyz/openbmc_project/sensors/temperature/BMC_Temp"; |
| expectedItem["metadata"] = "metadata1"; |
| |
| ASSERT_THAT(storedConfiguration.at("Sensors"), ElementsAre(expectedItem)); |
| } |
| |
| TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerThresholdParams) |
| { |
| nlohmann::json expectedItem0; |
| expectedItem0["type"] = 0; |
| expectedItem0["dwellTime"] = 10; |
| expectedItem0["direction"] = 1; |
| expectedItem0["thresholdValue"] = 0.5; |
| |
| nlohmann::json expectedItem1; |
| expectedItem1["type"] = 3; |
| expectedItem1["dwellTime"] = 10; |
| expectedItem1["direction"] = 2; |
| expectedItem1["thresholdValue"] = 90.2; |
| |
| ASSERT_THAT(storedConfiguration.at("ThresholdParamsDiscriminator"), Eq(0)); |
| ASSERT_THAT(storedConfiguration.at("ThresholdParams"), |
| ElementsAre(expectedItem0, expectedItem1)); |
| } |
| |
| TEST_F(TestTriggerStore, |
| settingPersistencyToTrueStoresDiscreteTriggerThresholdParams) |
| { |
| nlohmann::json expectedItem0; |
| expectedItem0["userId"] = "userId"; |
| expectedItem0["severity"] = discrete::Severity::warning; |
| expectedItem0["dwellTime"] = 10; |
| expectedItem0["thresholdValue"] = "15.2"; |
| |
| nlohmann::json expectedItem1; |
| expectedItem1["userId"] = "userId_2"; |
| expectedItem1["severity"] = discrete::Severity::critical; |
| expectedItem1["dwellTime"] = 5; |
| expectedItem1["thresholdValue"] = "32.7"; |
| |
| ASSERT_THAT(storedDiscreteConfiguration.at("ThresholdParamsDiscriminator"), |
| Eq(1)); |
| ASSERT_THAT(storedDiscreteConfiguration.at("ThresholdParams"), |
| ElementsAre(expectedItem0, expectedItem1)); |
| } |