blob: b0ef54eb165828aaa4113cbced5f49b9e85364cb [file] [log] [blame]
#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