| #include "propertywatchtest.hpp" |
| |
| #include "propertywatchimpl.hpp" |
| |
| #include <array> |
| #include <functional> |
| |
| using namespace std::string_literals; |
| using namespace phosphor::dbus::monitoring; |
| |
| const std::array<std::string, 4> paths = { |
| "/xyz/openbmc_project/testing/inst1"s, |
| "/xyz/openbmc_project/testing/inst2"s, |
| "/xyz/openbmc_project/testing/inst3"s, |
| "/xyz/openbmc_project/testing/inst4"s, |
| }; |
| |
| const std::array<std::string, 2> interfaces = { |
| "xyz.openbmc_project.Iface1"s, |
| "xyz.openbmc_project.Iface2"s, |
| }; |
| |
| const std::array<std::string, 2> properties = { |
| "Value1"s, |
| "Value2"s, |
| }; |
| |
| const std::string meta; |
| |
| std::array<std::tuple<std::any, std::any>, 8> storage = {}; |
| |
| const PropertyIndex watchIndex = { |
| { |
| {PropertyIndex::key_type{paths[0], interfaces[0], properties[0]}, |
| PropertyIndex::mapped_type{meta, meta, storage[0]}}, |
| {PropertyIndex::key_type{paths[0], interfaces[1], properties[1]}, |
| PropertyIndex::mapped_type{meta, meta, storage[1]}}, |
| {PropertyIndex::key_type{paths[1], interfaces[0], properties[0]}, |
| PropertyIndex::mapped_type{meta, meta, storage[2]}}, |
| {PropertyIndex::key_type{paths[1], interfaces[1], properties[1]}, |
| PropertyIndex::mapped_type{meta, meta, storage[3]}}, |
| {PropertyIndex::key_type{paths[2], interfaces[0], properties[0]}, |
| PropertyIndex::mapped_type{meta, meta, storage[4]}}, |
| {PropertyIndex::key_type{paths[2], interfaces[1], properties[1]}, |
| PropertyIndex::mapped_type{meta, meta, storage[5]}}, |
| {PropertyIndex::key_type{paths[3], interfaces[0], properties[0]}, |
| PropertyIndex::mapped_type{meta, meta, storage[6]}}, |
| {PropertyIndex::key_type{paths[3], interfaces[1], properties[1]}, |
| PropertyIndex::mapped_type{meta, meta, storage[7]}}, |
| }, |
| }; |
| |
| template <typename T> |
| struct Values |
| {}; |
| template <> |
| struct Values<uint8_t> |
| { |
| static auto& get(size_t i) |
| { |
| static const std::array<uint8_t, 8> values = { |
| {0, 1, 2, 3, 4, 5, 6, 7}, |
| }; |
| return values[i]; |
| } |
| }; |
| |
| template <> |
| struct Values<uint16_t> |
| { |
| static auto& get(size_t i) |
| { |
| static const std::array<uint16_t, 8> values = { |
| {88, 77, 66, 55, 44, 33, 22, 11}, |
| }; |
| return values[i]; |
| } |
| }; |
| |
| template <> |
| struct Values<uint32_t> |
| { |
| static auto& get(size_t i) |
| { |
| static const std::array<uint32_t, 8> values = { |
| {0xffffffff, 1, 3, 0, 5, 7, 9, 0xffffffff}, |
| }; |
| return values[i]; |
| } |
| }; |
| |
| template <> |
| struct Values<uint64_t> |
| { |
| static auto& get(size_t i) |
| { |
| static const std::array<uint64_t, 8> values = { |
| {0xffffffffffffffff, 3, 7, 12234, 0, 3, 9, 0xffffffff}, |
| }; |
| return values[i]; |
| } |
| }; |
| |
| template <> |
| struct Values<std::string> |
| { |
| static auto& get(size_t i) |
| { |
| static const std::array<std::string, 8> values = { |
| {""s, "foo"s, "bar"s, "baz"s, "hello"s, "string", "\x2\x3", "\\"}, |
| }; |
| return values[i]; |
| } |
| }; |
| |
| template <typename T> |
| void nonFilteredCheck(const std::any& value, const size_t ndx) |
| { |
| ASSERT_EQ(value.has_value(), true); |
| ASSERT_EQ(std::any_cast<T>(value), Values<T>::get(ndx)); |
| } |
| |
| template <typename T> |
| struct FilteredValues |
| {}; |
| |
| template <> |
| struct FilteredValues<uint8_t> |
| { |
| static auto& opFilters() |
| { |
| static std::unique_ptr<OperandFilters<uint8_t>> filters = |
| std::make_unique<OperandFilters<uint8_t>>( |
| std::vector<std::function<bool(uint8_t)>>{ |
| [](const auto& value) { return value < 4; }}); |
| return filters; |
| } |
| static auto& expected(size_t i) |
| { |
| static const std::array<std::any, 8> values = { |
| {std::any(uint8_t(0)), std::any(uint8_t(1)), std::any(uint8_t(2)), |
| std::any(uint8_t(3)), std::any(), std::any(), std::any(), |
| std::any()}}; |
| return values[i]; |
| } |
| }; |
| |
| template <> |
| struct FilteredValues<uint16_t> |
| { |
| static auto& opFilters() |
| { |
| static std::unique_ptr<OperandFilters<uint16_t>> filters = |
| std::make_unique<OperandFilters<uint16_t>>( |
| std::vector<std::function<bool(uint16_t)>>{ |
| [](const auto& value) { return value > 44; }, |
| [](const auto& value) { return value != 88; }}); |
| return filters; |
| } |
| static auto& expected(size_t i) |
| { |
| static const std::array<std::any, 8> values = { |
| {std::any(), std::any(uint16_t(77)), std::any(uint16_t(66)), |
| std::any(uint16_t(55)), std::any(), std::any(), std::any(), |
| std::any()}}; |
| return values[i]; |
| } |
| }; |
| |
| template <> |
| struct FilteredValues<uint32_t> |
| { |
| static auto& opFilters() |
| { |
| static std::unique_ptr<OperandFilters<uint32_t>> filters = |
| std::make_unique<OperandFilters<uint32_t>>( |
| std::vector<std::function<bool(uint32_t)>>{ |
| [](const auto& value) { return value != 0xffffffff; }, |
| [](const auto& value) { return value != 0; }}); |
| return filters; |
| } |
| static auto& expected(size_t i) |
| { |
| static const std::array<std::any, 8> values = { |
| {std::any(), std::any(uint32_t(1)), std::any(uint32_t(3)), |
| std::any(), std::any(uint32_t(5)), std::any(uint32_t(7)), |
| std::any(uint32_t(9)), std::any()}}; |
| return values[i]; |
| } |
| }; |
| |
| template <> |
| struct FilteredValues<uint64_t> |
| { |
| static auto& opFilters() |
| { |
| static std::unique_ptr<OperandFilters<uint64_t>> filters = |
| std::make_unique<OperandFilters<uint64_t>>( |
| std::vector<std::function<bool(uint64_t)>>{ |
| [](const auto& value) { return (value % 3) != 0; }}); |
| return filters; |
| } |
| static auto& expected(size_t i) |
| { |
| static const std::array<std::any, 8> values = { |
| {std::any(), std::any(), std::any(uint64_t(7)), std::any(), |
| std::any(), std::any(), std::any(), std::any()}}; |
| return values[i]; |
| } |
| }; |
| |
| template <> |
| struct FilteredValues<std::string> |
| { |
| static auto& opFilters() |
| { |
| static std::unique_ptr<OperandFilters<std::string>> filters = |
| std::make_unique<OperandFilters<std::string>>( |
| std::vector<std::function<bool(std::string)>>{ |
| [](const auto& value) { return value != ""s; }, |
| [](const auto& value) { return value != "string"s; }}); |
| return filters; |
| } |
| static auto& expected(size_t i) |
| { |
| static const std::array<std::any, 8> values = { |
| {std::any(), std::any("foo"s), std::any("bar"s), std::any("baz"s), |
| std::any("hello"s), std::any(), std::any("\x2\x3"s), |
| std::any("\\"s)}}; |
| return values[i]; |
| } |
| }; |
| |
| template <typename T> |
| void filteredCheck(const std::any& value, const size_t ndx) |
| { |
| ASSERT_EQ(value.has_value(), FilteredValues<T>::expected(ndx).has_value()); |
| if (value.has_value()) |
| { |
| ASSERT_EQ(std::any_cast<T>(value), |
| std::any_cast<T>(FilteredValues<T>::expected(ndx))); |
| } |
| } |
| |
| template <typename T> |
| void testStart(std::function<void(const std::any&, const size_t)>&& checkState, |
| OperandFilters<T>* opFilters = nullptr) |
| { |
| using ::testing::_; |
| using ::testing::Return; |
| |
| MockDBusInterface dbus; |
| MockDBusInterface::instance(dbus); |
| |
| const std::vector<std::string> expectedMapperInterfaces; |
| PropertyWatchOfType<T, MockDBusInterface> watch(watchIndex, false, |
| opFilters); |
| |
| auto ndx = static_cast<size_t>(0); |
| for (const auto& o : convert(watchIndex)) |
| { |
| const auto& path = o.first.get(); |
| const auto& interfaces = o.second; |
| std::vector<std::string> mapperResponse; |
| std::transform(interfaces.begin(), interfaces.end(), |
| std::back_inserter(mapperResponse), |
| // *INDENT-OFF* |
| [](const auto& item) { return item.first; }); |
| // *INDENT-ON* |
| EXPECT_CALL(dbus, mapperGetObject(MAPPER_BUSNAME, MAPPER_PATH, |
| MAPPER_INTERFACE, "GetObject", path, |
| expectedMapperInterfaces)) |
| .WillOnce(Return(GetObject({{"", mapperResponse}}))); |
| EXPECT_CALL( |
| dbus, fwdAddMatch( |
| sdbusplus::bus::match::rules::interfacesAdded(path), _)); |
| for (const auto& i : interfaces) |
| { |
| const auto& interface = i.first.get(); |
| const auto& properties = i.second; |
| EXPECT_CALL( |
| dbus, |
| fwdAddMatch(sdbusplus::bus::match::rules::propertiesChanged( |
| path, interface), |
| _)); |
| |
| PropertiesChanged<T> serviceResponse; |
| for (const auto& p : properties) |
| { |
| serviceResponse[p] = Values<T>::get(ndx); |
| ++ndx; |
| } |
| Expect<T>::getProperties(dbus, path, interface) |
| .WillOnce(Return(serviceResponse)); |
| } |
| } |
| |
| watch.start(); |
| |
| ndx = 0; |
| for (auto s : storage) |
| { |
| checkState(std::get<valueIndex>(s), ndx); |
| ++ndx; |
| } |
| |
| // Make sure start logic only runs the first time. |
| watch.start(); |
| } |
| |
| TEST(PropertyWatchTest, TestStart) |
| { |
| testStart<uint8_t>(nonFilteredCheck<uint8_t>); |
| testStart<uint16_t>(nonFilteredCheck<uint16_t>); |
| testStart<uint32_t>(nonFilteredCheck<uint32_t>); |
| testStart<uint64_t>(nonFilteredCheck<uint64_t>); |
| testStart<std::string>(nonFilteredCheck<std::string>); |
| } |
| |
| TEST(PropertyWatchTest, TestFilters) |
| { |
| testStart<uint8_t>(filteredCheck<uint8_t>, |
| FilteredValues<uint8_t>::opFilters().get()); |
| testStart<uint16_t>(filteredCheck<uint16_t>, |
| FilteredValues<uint16_t>::opFilters().get()); |
| testStart<uint32_t>(filteredCheck<uint32_t>, |
| FilteredValues<uint32_t>::opFilters().get()); |
| testStart<uint64_t>(filteredCheck<uint64_t>, |
| FilteredValues<uint64_t>::opFilters().get()); |
| testStart<std::string>(filteredCheck<std::string>, |
| FilteredValues<std::string>::opFilters().get()); |
| } |
| |
| MockDBusInterface* MockDBusInterface::ptr = nullptr; |