| #include <systemd/sd-bus-protocol.h> |
| |
| #include <sdbusplus/message.hpp> |
| #include <sdbusplus/test/sdbus_mock.hpp> |
| |
| #include <array> |
| #include <map> |
| #include <set> |
| #include <span> |
| #include <string> |
| #include <tuple> |
| #include <unordered_map> |
| #include <unordered_set> |
| #include <variant> |
| #include <vector> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| namespace |
| { |
| |
| using testing::Eq; |
| using testing::MatcherCast; |
| using testing::Pointee; |
| using testing::Return; |
| using testing::SafeMatcherCast; |
| using testing::StrEq; |
| |
| MATCHER_P(iovec_equal, match_string, "") |
| { |
| const char* start = std::bit_cast<char*>(arg->iov_base); |
| return std::string(start, arg->iov_len) == match_string; |
| } |
| |
| class AppendTest : public testing::Test |
| { |
| protected: |
| testing::StrictMock<sdbusplus::SdBusMock> mock; |
| |
| void SetUp() override |
| { |
| EXPECT_CALL(mock, sd_bus_message_new_method_call(testing::_, testing::_, |
| nullptr, nullptr, |
| nullptr, nullptr)) |
| .WillRepeatedly(Return(0)); |
| }; |
| |
| sdbusplus::message_t new_message() |
| { |
| return sdbusplus::get_mocked_new(&mock).new_method_call( |
| nullptr, nullptr, nullptr, nullptr); |
| } |
| |
| template <typename T> |
| void expect_basic(char type, T val) |
| { |
| EXPECT_CALL(mock, sd_bus_message_append_basic( |
| nullptr, type, |
| MatcherCast<const void*>( |
| SafeMatcherCast<const T*>(Pointee(Eq(val)))))) |
| .WillOnce(Return(0)); |
| } |
| |
| void expect_basic_string(char type, const char* str) |
| { |
| EXPECT_CALL(mock, sd_bus_message_append_basic( |
| nullptr, type, |
| MatcherCast<const void*>( |
| SafeMatcherCast<const char*>(StrEq(str))))) |
| .WillOnce(Return(0)); |
| } |
| void expect_basic_string_iovec(const char* str, size_t size) |
| { |
| std::string tmp = {str, size}; |
| EXPECT_CALL(mock, sd_bus_message_append_string_iovec( |
| nullptr, iovec_equal(tmp), 1)) |
| .WillOnce(Return(0)); |
| } |
| |
| void expect_open_container(char type, const char* contents) |
| { |
| EXPECT_CALL( |
| mock, sd_bus_message_open_container(nullptr, type, StrEq(contents))) |
| .WillOnce(Return(0)); |
| } |
| |
| void expect_close_container() |
| { |
| EXPECT_CALL(mock, sd_bus_message_close_container(nullptr)) |
| .WillOnce(Return(0)); |
| } |
| }; |
| |
| TEST_F(AppendTest, RValueInt) |
| { |
| static_assert( |
| sdbusplus::message::details::can_append_multiple_v<decltype(1)>); |
| expect_basic<int>(SD_BUS_TYPE_INT32, 1); |
| new_message().append(1); |
| } |
| |
| TEST_F(AppendTest, LValueInt) |
| { |
| const int a = 1; |
| static_assert( |
| sdbusplus::message::details::can_append_multiple_v<decltype(a)>); |
| expect_basic<int>(SD_BUS_TYPE_INT32, a); |
| new_message().append(a); |
| } |
| |
| TEST_F(AppendTest, XValueInt) |
| { |
| int a = 1; |
| expect_basic<int>(SD_BUS_TYPE_INT32, a); |
| new_message().append(std::move(a)); |
| } |
| |
| TEST_F(AppendTest, RValueBool) |
| { |
| expect_basic<int>(SD_BUS_TYPE_BOOLEAN, true); |
| new_message().append(true); |
| } |
| |
| TEST_F(AppendTest, LValueBool) |
| { |
| const bool a = false; |
| expect_basic<int>(SD_BUS_TYPE_BOOLEAN, false); |
| new_message().append(a); |
| } |
| |
| TEST_F(AppendTest, XValueBool) |
| { |
| bool a = false; |
| expect_basic<int>(SD_BUS_TYPE_BOOLEAN, false); |
| new_message().append(std::move(a)); |
| } |
| |
| TEST_F(AppendTest, RValueDouble) |
| { |
| expect_basic<double>(SD_BUS_TYPE_DOUBLE, 1.1); |
| new_message().append(1.1); |
| } |
| |
| TEST_F(AppendTest, LValueDouble) |
| { |
| const double a = 1.1; |
| expect_basic<double>(SD_BUS_TYPE_DOUBLE, a); |
| new_message().append(a); |
| } |
| |
| TEST_F(AppendTest, XValueDouble) |
| { |
| double a = 1.1; |
| expect_basic<double>(SD_BUS_TYPE_DOUBLE, a); |
| new_message().append(std::move(a)); |
| } |
| |
| TEST_F(AppendTest, RValueCString) |
| { |
| expect_basic_string(SD_BUS_TYPE_STRING, "asdf"); |
| new_message().append("asdf"); |
| } |
| |
| TEST_F(AppendTest, LValueCString) |
| { |
| const char* const s = "asdf"; |
| expect_basic_string(SD_BUS_TYPE_STRING, s); |
| new_message().append(s); |
| } |
| |
| TEST_F(AppendTest, XValueCString) |
| { |
| const char* s = "asdf"; |
| static_assert( |
| sdbusplus::message::details::can_append_multiple_v<decltype(s)>); |
| expect_basic_string(SD_BUS_TYPE_STRING, s); |
| new_message().append(std::move(s)); |
| } |
| |
| TEST_F(AppendTest, RValueString) |
| { |
| expect_basic_string(SD_BUS_TYPE_STRING, "asdf"); |
| new_message().append(std::string{"asdf"}); |
| } |
| |
| TEST_F(AppendTest, LValueString) |
| { |
| std::string s{"asdf"}; |
| static_assert( |
| !sdbusplus::message::details::can_append_multiple_v<decltype(s)>); |
| expect_basic_string(SD_BUS_TYPE_STRING, s.c_str()); |
| new_message().append(s); |
| } |
| |
| TEST_F(AppendTest, XValueString) |
| { |
| std::string s{"asdf"}; |
| expect_basic_string(SD_BUS_TYPE_STRING, s.c_str()); |
| new_message().append(std::move(s)); |
| } |
| |
| TEST_F(AppendTest, LValueStringView) |
| { |
| std::string_view s{"asdf"}; |
| expect_basic_string_iovec(s.data(), s.size()); |
| new_message().append(s); |
| } |
| |
| TEST_F(AppendTest, RValueStringView) |
| { |
| std::string_view s{"asdf"}; |
| expect_basic_string_iovec(s.data(), s.size()); |
| new_message().append(std::string_view{"asdf"}); |
| } |
| |
| TEST_F(AppendTest, ObjectPath) |
| { |
| sdbusplus::message::object_path o{"/asdf"}; |
| expect_basic_string(SD_BUS_TYPE_OBJECT_PATH, o.str.c_str()); |
| new_message().append(o); |
| } |
| |
| TEST_F(AppendTest, Signature) |
| { |
| sdbusplus::message::signature g{"ii"}; |
| expect_basic_string(SD_BUS_TYPE_SIGNATURE, g.str.c_str()); |
| new_message().append(g); |
| } |
| |
| TEST_F(AppendTest, CombinedBasic) |
| { |
| const int c = 3; |
| const std::string s1{"fdsa"}; |
| const char* const s2 = "asdf"; |
| |
| { |
| testing::InSequence seq; |
| expect_basic<int>(SD_BUS_TYPE_INT32, 1); |
| expect_basic<double>(SD_BUS_TYPE_DOUBLE, 2.2); |
| expect_basic<int>(SD_BUS_TYPE_INT32, c); |
| expect_basic_string(SD_BUS_TYPE_STRING, s1.c_str()); |
| expect_basic<int>(SD_BUS_TYPE_BOOLEAN, false); |
| expect_basic_string(SD_BUS_TYPE_STRING, s2); |
| } |
| new_message().append(1, 2.2, c, s1, false, s2); |
| } |
| |
| TEST_F(AppendTest, Array) |
| { |
| const std::array<double, 4> a{1.1, 2.2, 3.3, 4.4}; |
| static_assert( |
| !sdbusplus::message::details::can_append_multiple_v<decltype(a)>); |
| |
| { |
| testing::InSequence seq; |
| expect_open_container(SD_BUS_TYPE_ARRAY, "d"); |
| for (const auto& i : a) |
| { |
| expect_basic<double>(SD_BUS_TYPE_DOUBLE, i); |
| } |
| expect_close_container(); |
| } |
| new_message().append(a); |
| } |
| |
| TEST_F(AppendTest, Span) |
| { |
| const std::array<double, 4> a{1.1, 2.2, 3.3, 4.4}; |
| auto s = std::span{a}; |
| static_assert( |
| !sdbusplus::message::details::can_append_multiple_v<decltype(s)>); |
| |
| { |
| testing::InSequence seq; |
| expect_open_container(SD_BUS_TYPE_ARRAY, "d"); |
| for (const auto& i : s) |
| { |
| expect_basic<double>(SD_BUS_TYPE_DOUBLE, i); |
| } |
| expect_close_container(); |
| } |
| new_message().append(s); |
| } |
| |
| TEST_F(AppendTest, Vector) |
| { |
| const std::vector<int> v{1, 2, 3, 4}; |
| |
| { |
| testing::InSequence seq; |
| expect_open_container(SD_BUS_TYPE_ARRAY, "i"); |
| for (const auto& i : v) |
| { |
| expect_basic<int>(SD_BUS_TYPE_INT32, i); |
| } |
| expect_close_container(); |
| } |
| new_message().append(v); |
| } |
| |
| TEST_F(AppendTest, Set) |
| { |
| const std::set<std::string> s{"one", "two", "eight"}; |
| |
| { |
| testing::InSequence seq; |
| expect_open_container(SD_BUS_TYPE_ARRAY, "s"); |
| for (const auto& i : s) |
| { |
| expect_basic_string(SD_BUS_TYPE_STRING, i.c_str()); |
| } |
| expect_close_container(); |
| } |
| new_message().append(s); |
| } |
| |
| TEST_F(AppendTest, UnorderedSet) |
| { |
| const std::unordered_set<std::string> s{"one", "two", "eight"}; |
| |
| { |
| testing::InSequence seq; |
| expect_open_container(SD_BUS_TYPE_ARRAY, "s"); |
| for (const auto& i : s) |
| { |
| expect_basic_string(SD_BUS_TYPE_STRING, i.c_str()); |
| } |
| expect_close_container(); |
| } |
| new_message().append(s); |
| } |
| |
| TEST_F(AppendTest, Map) |
| { |
| const std::map<int, std::string> m{ |
| {1, "a"}, |
| {2, "bc"}, |
| {3, "def"}, |
| {4, "ghij"}, |
| }; |
| |
| { |
| testing::InSequence seq; |
| expect_open_container(SD_BUS_TYPE_ARRAY, "{is}"); |
| for (const auto& i : m) |
| { |
| expect_open_container(SD_BUS_TYPE_DICT_ENTRY, "is"); |
| expect_basic<int>(SD_BUS_TYPE_INT32, i.first); |
| expect_basic_string(SD_BUS_TYPE_STRING, i.second.c_str()); |
| expect_close_container(); |
| } |
| expect_close_container(); |
| } |
| new_message().append(m); |
| } |
| |
| TEST_F(AppendTest, UnorderedMap) |
| { |
| const std::unordered_map<int, bool> m{ |
| {1, false}, |
| {2, true}, |
| {3, true}, |
| {4, false}, |
| }; |
| |
| { |
| testing::InSequence seq; |
| expect_open_container(SD_BUS_TYPE_ARRAY, "{ib}"); |
| for (const auto& i : m) |
| { |
| expect_open_container(SD_BUS_TYPE_DICT_ENTRY, "ib"); |
| expect_basic<int>(SD_BUS_TYPE_INT32, i.first); |
| expect_basic<int>(SD_BUS_TYPE_BOOLEAN, i.second); |
| expect_close_container(); |
| } |
| expect_close_container(); |
| } |
| new_message().append(m); |
| } |
| |
| TEST_F(AppendTest, Tuple) |
| { |
| const std::tuple<int, std::string, bool> t{5, "asdf", false}; |
| |
| { |
| testing::InSequence seq; |
| expect_open_container(SD_BUS_TYPE_STRUCT, "isb"); |
| expect_basic<int>(SD_BUS_TYPE_INT32, std::get<0>(t)); |
| expect_basic_string(SD_BUS_TYPE_STRING, std::get<1>(t).c_str()); |
| expect_basic<int>(SD_BUS_TYPE_BOOLEAN, std::get<2>(t)); |
| expect_close_container(); |
| } |
| new_message().append(t); |
| } |
| |
| TEST_F(AppendTest, Variant) |
| { |
| const bool b1 = false; |
| const std::string s2{"asdf"}; |
| const std::variant<int, std::string, bool> v1{b1}, v2{s2}; |
| |
| { |
| testing::InSequence seq; |
| expect_open_container(SD_BUS_TYPE_VARIANT, "b"); |
| expect_basic<int>(SD_BUS_TYPE_BOOLEAN, b1); |
| expect_close_container(); |
| expect_open_container(SD_BUS_TYPE_VARIANT, "s"); |
| expect_basic_string(SD_BUS_TYPE_STRING, s2.c_str()); |
| expect_close_container(); |
| } |
| new_message().append(v1, v2); |
| } |
| |
| TEST_F(AppendTest, LargeCombo) |
| { |
| std::vector<std::array<std::string, 3>> vas{{"a", "b", "c"}, |
| {"d", "", "e"}}; |
| std::map<std::string, std::variant<int, double>> msv = { |
| {"a", 3.3}, {"b", 1}, {"c", 4.4}}; |
| |
| { |
| testing::InSequence seq; |
| |
| expect_open_container(SD_BUS_TYPE_ARRAY, "as"); |
| for (const auto& as : vas) |
| { |
| expect_open_container(SD_BUS_TYPE_ARRAY, "s"); |
| for (const auto& s : as) |
| { |
| expect_basic_string(SD_BUS_TYPE_STRING, s.c_str()); |
| } |
| expect_close_container(); |
| } |
| expect_close_container(); |
| |
| expect_open_container(SD_BUS_TYPE_ARRAY, "{sv}"); |
| for (const auto& sv : msv) |
| { |
| expect_open_container(SD_BUS_TYPE_DICT_ENTRY, "sv"); |
| expect_basic_string(SD_BUS_TYPE_STRING, sv.first.c_str()); |
| if (std::holds_alternative<int>(sv.second)) |
| { |
| expect_open_container(SD_BUS_TYPE_VARIANT, "i"); |
| expect_basic<int>(SD_BUS_TYPE_INT32, std::get<int>(sv.second)); |
| expect_close_container(); |
| } |
| else |
| { |
| expect_open_container(SD_BUS_TYPE_VARIANT, "d"); |
| expect_basic<double>(SD_BUS_TYPE_DOUBLE, |
| std::get<double>(sv.second)); |
| expect_close_container(); |
| } |
| expect_close_container(); |
| } |
| expect_close_container(); |
| } |
| new_message().append(vas, msv); |
| } |
| |
| } // namespace |