blob: 5ba9b4605ad4cae5eb10fcaed47602926f0ba2ae [file] [log] [blame]
#include <boost/container/flat_map.hpp>
#include <sdbusplus/unpack_properties.hpp>
#include <gmock/gmock.h>
namespace sdbusplus
{
struct ThrowingUnpack
{
template <typename... Args>
bool operator()(Args&&... args) const
{
unpackProperties(std::forward<Args>(args)...);
return false;
}
};
struct NonThrowingUnpack
{
struct UnpackError
{
UnpackError(sdbusplus::UnpackErrorReason r, const std::string& p) :
reason(r), property(p)
{}
sdbusplus::UnpackErrorReason reason;
std::string property;
};
template <typename... Args>
std::optional<UnpackError> operator()(Args&&... args) const
{
std::optional<UnpackError> error;
unpackPropertiesNoThrow(
[&error](sdbusplus::UnpackErrorReason reason,
const std::string& property) {
error.emplace(reason, property);
},
std::forward<Args>(args)...);
return error;
}
};
template <typename A, typename B>
struct TestingTypes
{
using SystemUnderTest = A;
using Container = B;
};
using VariantType = std::variant<std::string, uint32_t, float, double>;
using ContainerTypes = testing::Types<
TestingTypes<NonThrowingUnpack,
std::vector<std::pair<std::string, VariantType>>>,
TestingTypes<ThrowingUnpack,
std::vector<std::pair<std::string, VariantType>>>>;
template <typename Exception, typename F>
std::optional<Exception> captureException(F&& code)
{
try
{
code();
}
catch (const Exception& e)
{
return e;
}
return std::nullopt;
}
template <typename Params>
struct UnpackPropertiesTest : public testing::Test
{
void SetUp() override
{
using namespace std::string_literals;
data.insert(data.end(),
std::make_pair("Key-1"s, VariantType("string"s)));
data.insert(data.end(), std::make_pair("Key-2"s, VariantType(42.f)));
data.insert(data.end(), std::make_pair("Key-3"s, VariantType(15.)));
}
typename Params::Container data;
typename Params::SystemUnderTest unpackPropertiesCall;
};
TYPED_TEST_SUITE(UnpackPropertiesTest, ContainerTypes);
TYPED_TEST(UnpackPropertiesTest, returnsValueWhenKeyIsPresentAndTypeMatches)
{
using namespace testing;
std::string val1;
float val2 = 0.f;
double val3 = 0.;
EXPECT_FALSE(this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-2",
val2, "Key-3", val3));
ASSERT_THAT(val1, Eq("string"));
ASSERT_THAT(val2, FloatEq(42.f));
ASSERT_THAT(val3, DoubleEq(15.));
}
TYPED_TEST(UnpackPropertiesTest,
unpackDoesntChangeOriginalDataWhenPassedAsNonConstReference)
{
using namespace testing;
std::string val1, val2;
EXPECT_FALSE(this->unpackPropertiesCall(this->data, "Key-1", val1));
EXPECT_FALSE(this->unpackPropertiesCall(this->data, "Key-1", val2));
ASSERT_THAT(val1, Eq("string"));
ASSERT_THAT(val2, Eq("string"));
}
TYPED_TEST(UnpackPropertiesTest, doesntReportMissingPropertyForOptional)
{
using namespace testing;
using namespace std::string_literals;
std::optional<std::string> val1;
std::optional<std::string> val4;
EXPECT_FALSE(
this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-4", val4));
ASSERT_THAT(val1, Eq("string"));
ASSERT_THAT(val4, Eq(std::nullopt));
}
TYPED_TEST(UnpackPropertiesTest, setPresentPointersOnSuccess)
{
using namespace testing;
using namespace std::string_literals;
const std::string* val1 = nullptr;
const float* val2 = nullptr;
const double* val3 = nullptr;
const std::string* val4 = nullptr;
EXPECT_FALSE(this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-2",
val2, "Key-3", val3, "Key-4",
val4));
ASSERT_TRUE(val1 && val2 && val3);
ASSERT_TRUE(!val4);
ASSERT_THAT(*val1, Eq("string"));
ASSERT_THAT(*val2, FloatEq(42.f));
ASSERT_THAT(*val3, DoubleEq(15.));
}
template <typename Params>
struct UnpackPropertiesThrowingTest : public UnpackPropertiesTest<Params>
{};
using ContainerTypesThrowing = testing::Types<TestingTypes<
ThrowingUnpack, std::vector<std::pair<std::string, VariantType>>>>;
TYPED_TEST_SUITE(UnpackPropertiesThrowingTest, ContainerTypesThrowing);
TYPED_TEST(UnpackPropertiesThrowingTest, throwsErrorWhenKeyIsMissing)
{
using namespace testing;
std::string val1;
float val2 = 0.f;
double val3 = 0.;
auto error = captureException<exception::UnpackPropertyError>([&] {
this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-4", val2,
"Key-3", val3);
});
ASSERT_TRUE(error);
ASSERT_THAT(error->reason, Eq(UnpackErrorReason::missingProperty));
ASSERT_THAT(error->propertyName, Eq("Key-4"));
}
TYPED_TEST(UnpackPropertiesThrowingTest, throwsErrorWhenTypeDoesntMatch)
{
using namespace testing;
std::string val1;
std::string val2;
double val3 = 0.;
auto error = captureException<exception::UnpackPropertyError>([&] {
this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-2", val2,
"Key-3", val3);
});
ASSERT_TRUE(error);
ASSERT_THAT(error->reason, Eq(UnpackErrorReason::wrongType));
ASSERT_THAT(error->propertyName, Eq("Key-2"));
}
TYPED_TEST(UnpackPropertiesThrowingTest, throwsErrorWhenOptionalTypeDoesntMatch)
{
using namespace testing;
std::optional<std::string> val1;
std::optional<std::string> val2;
auto error = captureException<exception::UnpackPropertyError>([&] {
this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-2", val2);
});
ASSERT_TRUE(error);
ASSERT_THAT(error->reason, Eq(UnpackErrorReason::wrongType));
ASSERT_THAT(error->propertyName, Eq("Key-2"));
}
template <typename Params>
struct UnpackPropertiesNonThrowingTest : public UnpackPropertiesTest<Params>
{};
using ContainerTypesNonThrowing = testing::Types<TestingTypes<
NonThrowingUnpack, std::vector<std::pair<std::string, VariantType>>>>;
TYPED_TEST_SUITE(UnpackPropertiesNonThrowingTest, ContainerTypesNonThrowing);
TYPED_TEST(UnpackPropertiesNonThrowingTest, ErrorWhenKeyIsMissing)
{
using namespace testing;
std::string val1;
float val2 = 0.f;
double val3 = 0.;
auto badProperty = this->unpackPropertiesCall(this->data, "Key-1", val1,
"Key-4", val2, "Key-3", val3);
ASSERT_TRUE(badProperty);
EXPECT_THAT(badProperty->reason, Eq(UnpackErrorReason::missingProperty));
EXPECT_THAT(badProperty->property, Eq("Key-4"));
}
TYPED_TEST(UnpackPropertiesNonThrowingTest, ErrorWhenTypeDoesntMatch)
{
using namespace testing;
std::string val1;
std::string val2;
double val3 = 0.;
auto badProperty = this->unpackPropertiesCall(this->data, "Key-1", val1,
"Key-2", val2, "Key-3", val3);
ASSERT_TRUE(badProperty);
EXPECT_THAT(badProperty->reason, Eq(UnpackErrorReason::wrongType));
EXPECT_THAT(badProperty->property, Eq("Key-2"));
}
TYPED_TEST(UnpackPropertiesNonThrowingTest, ErrorWhenOptionalTypeDoesntMatch)
{
using namespace testing;
std::optional<std::string> val1;
std::optional<std::string> val2;
auto badProperty = this->unpackPropertiesCall(this->data, "Key-1", val1,
"Key-2", val2);
ASSERT_TRUE(badProperty);
EXPECT_THAT(badProperty->reason, Eq(UnpackErrorReason::wrongType));
EXPECT_THAT(badProperty->property, Eq("Key-2"));
}
template <typename Params>
struct UnpackPropertiesTest_ForVector : public UnpackPropertiesTest<Params>
{};
using ContainerTypesVector = testing::Types<
TestingTypes<NonThrowingUnpack,
std::vector<std::pair<std::string, VariantType>>>,
TestingTypes<ThrowingUnpack,
std::vector<std::pair<std::string, VariantType>>>>;
TYPED_TEST_SUITE(UnpackPropertiesTest_ForVector, ContainerTypesVector);
TYPED_TEST(UnpackPropertiesTest_ForVector, silentlyDiscardsDuplicatedKeyInData)
{
using namespace testing;
using namespace std::string_literals;
std::string val1;
float val2 = 0.f;
double val3 = 0.;
this->data.insert(this->data.end(),
std::make_pair("Key-1"s, VariantType("string2"s)));
EXPECT_FALSE(this->unpackPropertiesCall(this->data, "Key-1", val1, "Key-2",
val2, "Key-3", val3));
ASSERT_THAT(val1, Eq("string"));
ASSERT_THAT(val2, FloatEq(42.f));
ASSERT_THAT(val3, DoubleEq(15.));
}
} // namespace sdbusplus