#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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(arg->iov_base); return std::string(start, arg->iov_len) == match_string; } class AppendTest : public testing::Test { protected: testing::StrictMock 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 void expect_basic(char type, T val) { EXPECT_CALL(mock, sd_bus_message_append_basic( nullptr, type, MatcherCast( SafeMatcherCast(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( SafeMatcherCast(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); expect_basic(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); expect_basic(SD_BUS_TYPE_INT32, a); new_message().append(a); } TEST_F(AppendTest, XValueInt) { int a = 1; expect_basic(SD_BUS_TYPE_INT32, a); new_message().append(std::move(a)); } TEST_F(AppendTest, RValueBool) { expect_basic(SD_BUS_TYPE_BOOLEAN, true); new_message().append(true); } TEST_F(AppendTest, LValueBool) { const bool a = false; expect_basic(SD_BUS_TYPE_BOOLEAN, false); new_message().append(a); } TEST_F(AppendTest, XValueBool) { bool a = false; expect_basic(SD_BUS_TYPE_BOOLEAN, false); new_message().append(std::move(a)); } TEST_F(AppendTest, RValueDouble) { expect_basic(SD_BUS_TYPE_DOUBLE, 1.1); new_message().append(1.1); } TEST_F(AppendTest, LValueDouble) { const double a = 1.1; expect_basic(SD_BUS_TYPE_DOUBLE, a); new_message().append(a); } TEST_F(AppendTest, XValueDouble) { double a = 1.1; expect_basic(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); 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); 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(SD_BUS_TYPE_INT32, 1); expect_basic(SD_BUS_TYPE_DOUBLE, 2.2); expect_basic(SD_BUS_TYPE_INT32, c); expect_basic_string(SD_BUS_TYPE_STRING, s1.c_str()); expect_basic(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 a{1.1, 2.2, 3.3, 4.4}; static_assert( !sdbusplus::message::details::can_append_multiple_v); { testing::InSequence seq; expect_open_container(SD_BUS_TYPE_ARRAY, "d"); for (const auto& i : a) { expect_basic(SD_BUS_TYPE_DOUBLE, i); } expect_close_container(); } new_message().append(a); } TEST_F(AppendTest, Span) { const std::array a{1.1, 2.2, 3.3, 4.4}; auto s = std::span{a}; static_assert( !sdbusplus::message::details::can_append_multiple_v); { testing::InSequence seq; expect_open_container(SD_BUS_TYPE_ARRAY, "d"); for (const auto& i : s) { expect_basic(SD_BUS_TYPE_DOUBLE, i); } expect_close_container(); } new_message().append(s); } TEST_F(AppendTest, Vector) { const std::vector v{1, 2, 3, 4}; { testing::InSequence seq; expect_open_container(SD_BUS_TYPE_ARRAY, "i"); for (const auto& i : v) { expect_basic(SD_BUS_TYPE_INT32, i); } expect_close_container(); } new_message().append(v); } TEST_F(AppendTest, Set) { const std::set 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 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 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(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 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(SD_BUS_TYPE_INT32, i.first); expect_basic(SD_BUS_TYPE_BOOLEAN, i.second); expect_close_container(); } expect_close_container(); } new_message().append(m); } TEST_F(AppendTest, Tuple) { const std::tuple t{5, "asdf", false}; { testing::InSequence seq; expect_open_container(SD_BUS_TYPE_STRUCT, "isb"); expect_basic(SD_BUS_TYPE_INT32, std::get<0>(t)); expect_basic_string(SD_BUS_TYPE_STRING, std::get<1>(t).c_str()); expect_basic(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 v1{b1}, v2{s2}; { testing::InSequence seq; expect_open_container(SD_BUS_TYPE_VARIANT, "b"); expect_basic(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> vas{{"a", "b", "c"}, {"d", "", "e"}}; std::map> 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(sv.second)) { expect_open_container(SD_BUS_TYPE_VARIANT, "i"); expect_basic(SD_BUS_TYPE_INT32, std::get(sv.second)); expect_close_container(); } else { expect_open_container(SD_BUS_TYPE_VARIANT, "d"); expect_basic(SD_BUS_TYPE_DOUBLE, std::get(sv.second)); expect_close_container(); } expect_close_container(); } expect_close_container(); } new_message().append(vas, msv); } } // namespace