Files
rippled/src/test/protocol/TER_test.cpp

247 lines
8.8 KiB
C++

#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/protocol/TER.h>
#include <cstddef>
#include <string>
#include <tuple>
#include <type_traits>
namespace xrpl {
struct TER_test : public beast::unit_test::Suite
{
void
testTransResultInfo()
{
for (auto i = -400; i < 400; ++i)
{
TER const t = TER::fromInt(i);
auto inRange = isTelLocal(t) || isTemMalformed(t) || isTefFailure(t) || isTerRetry(t) ||
isTesSuccess(t) || isTecClaim(t);
std::string token, text;
auto good = transResultInfo(t, token, text);
BEAST_EXPECT(inRange || !good);
BEAST_EXPECT(transToken(t) == (good ? token : "-"));
BEAST_EXPECT(transHuman(t) == (good ? text : "-"));
auto code = transCode(token);
BEAST_EXPECT(good == !!code);
BEAST_EXPECT(!code || *code == t);
}
}
// Helper template that makes sure two types are not convertible or
// assignable if not the same.
// o I1 one tuple index.
// o I2 other tuple index.
// o Tup is expected to be a tuple.
// It's a functor, rather than a function template, since a class template
// can be a template argument without being full specified.
template <std::size_t I1, std::size_t I2>
class NotConvertible
{
public:
template <typename Tup>
void
operator()(Tup const& tup, beast::unit_test::Suite&) const
{
// Entries in the tuple should not be convertible or assignable
// unless they are the same types.
using To_t = std::decay_t<decltype(std::get<I1>(tup))>;
using From_t = std::decay_t<decltype(std::get<I2>(tup))>;
static_assert(
std::is_same_v<From_t, To_t> == std::is_convertible_v<From_t, To_t>, "Convert err");
static_assert(
std::is_same_v<To_t, From_t> == std::is_constructible_v<To_t, From_t>,
"Construct err");
static_assert(
std::is_same_v<To_t, From_t> == std::is_assignable_v<To_t&, From_t const&>,
"Assign err");
// Assignment or conversion from integer to type should never work.
static_assert(!std::is_convertible_v<int, To_t>, "Convert err");
static_assert(!std::is_constructible_v<To_t, int>, "Construct err");
static_assert(!std::is_assignable_v<To_t&, int const&>, "Assign err");
}
};
// Fast iteration over the tuple.
template <
std::size_t I1,
std::size_t I2,
template <std::size_t, std::size_t> class Func,
typename Tup>
std::enable_if_t<I1 != 0>
testIterate(Tup const& tup, beast::unit_test::Suite& s)
{
Func<I1, I2> const func;
func(tup, s);
testIterate<I1 - 1, I2, Func>(tup, s);
}
// Slow iteration over the tuple.
template <
std::size_t I1,
std::size_t I2,
template <std::size_t, std::size_t> class Func,
typename Tup>
std::enable_if_t<I1 == 0 && I2 != 0>
testIterate(Tup const& tup, beast::unit_test::Suite& s)
{
Func<I1, I2> const func;
func(tup, s);
testIterate<std::tuple_size_v<Tup> - 1, I2 - 1, Func>(tup, s);
}
// Finish iteration over the tuple.
template <
std::size_t I1,
std::size_t I2,
template <std::size_t, std::size_t> class Func,
typename Tup>
std::enable_if_t<I1 == 0 && I2 == 0>
testIterate(Tup const& tup, beast::unit_test::Suite& s)
{
Func<I1, I2> const func;
func(tup, s);
}
void
testConversion()
{
// Verify that valid conversions are valid and invalid conversions
// are not valid.
// Examples of each kind of enum.
static auto const kTER_ENUMS = std::make_tuple(
telLOCAL_ERROR, temMALFORMED, tefFAILURE, terRETRY, tesSUCCESS, tecCLAIM);
static int const kHI_INDEX{std::tuple_size_v<decltype(kTER_ENUMS)> - 1};
// Verify that enums cannot be converted to other enum types.
testIterate<kHI_INDEX, kHI_INDEX, NotConvertible>(kTER_ENUMS, *this);
// Lambda that verifies assignability and convertibility.
auto isConvertible = [](auto from, auto to) {
using From_t = std::decay_t<decltype(from)>;
using To_t = std::decay_t<decltype(to)>;
static_assert(std::is_convertible_v<From_t, To_t>, "Convert err");
static_assert(std::is_constructible_v<To_t, From_t>, "Construct err");
static_assert(std::is_assignable_v<To_t&, From_t const&>, "Assign err");
};
// Verify the right types convert to NotTEC.
NotTEC const notTec;
isConvertible(telLOCAL_ERROR, notTec);
isConvertible(temMALFORMED, notTec);
isConvertible(tefFAILURE, notTec);
isConvertible(terRETRY, notTec);
isConvertible(tesSUCCESS, notTec);
isConvertible(notTec, notTec);
// Lambda that verifies types and not assignable or convertible.
auto notConvertible = [](auto from, auto to) {
using To_t = std::decay_t<decltype(to)>;
using From_t = std::decay_t<decltype(from)>;
static_assert(!std::is_convertible_v<From_t, To_t>, "Convert err");
static_assert(!std::is_constructible_v<To_t, From_t>, "Construct err");
static_assert(!std::is_assignable_v<To_t&, From_t const&>, "Assign err");
};
// Verify types that shouldn't convert to NotTEC.
TER const ter;
notConvertible(tecCLAIM, notTec);
notConvertible(ter, notTec);
notConvertible(4, notTec);
// Verify the right types convert to TER.
isConvertible(telLOCAL_ERROR, ter);
isConvertible(temMALFORMED, ter);
isConvertible(tefFAILURE, ter);
isConvertible(terRETRY, ter);
isConvertible(tesSUCCESS, ter);
isConvertible(tecCLAIM, ter);
isConvertible(notTec, ter);
isConvertible(ter, ter);
// Verify that you can't convert from int to ter.
notConvertible(4, ter);
}
// Helper template that makes sure two types are comparable. Also
// verifies that one of the types does not compare to int.
// o I1 one tuple index.
// o I2 other tuple index.
// o Tup is expected to be a tuple.
// It's a functor, rather than a function template, since a class template
// can be a template argument without being full specified.
template <std::size_t I1, std::size_t I2>
class CheckComparable
{
public:
template <typename Tup>
void
operator()(Tup const& tup, beast::unit_test::Suite& s) const
{
// All entries in the tuple should be comparable one to the other.
auto const lhs = std::get<I1>(tup);
auto const rhs = std::get<I2>(tup);
static_assert(std::is_same_v<decltype(operator==(lhs, rhs)), bool>, "== err");
static_assert(std::is_same_v<decltype(operator!=(lhs, rhs)), bool>, "!= err");
static_assert(std::is_same_v<decltype(operator<(lhs, rhs)), bool>, "< err");
static_assert(std::is_same_v<decltype(operator<=(lhs, rhs)), bool>, "<= err");
static_assert(std::is_same_v<decltype(operator>(lhs, rhs)), bool>, "> err");
static_assert(std::is_same_v<decltype(operator>=(lhs, rhs)), bool>, ">= err");
// Make sure a sampling of TER types exhibit the expected behavior
// for all comparison operators.
s.expect((lhs == rhs) == (TERtoInt(lhs) == TERtoInt(rhs)));
s.expect((lhs != rhs) == (TERtoInt(lhs) != TERtoInt(rhs)));
s.expect((lhs < rhs) == (TERtoInt(lhs) < TERtoInt(rhs)));
s.expect((lhs <= rhs) == (TERtoInt(lhs) <= TERtoInt(rhs)));
s.expect((lhs > rhs) == (TERtoInt(lhs) > TERtoInt(rhs)));
s.expect((lhs >= rhs) == (TERtoInt(lhs) >= TERtoInt(rhs)));
}
};
void
testComparison()
{
// All of the TER-related types should be comparable.
// Examples of all the types we expect to successfully compare.
static auto const kTERS = std::make_tuple(
telLOCAL_ERROR,
temMALFORMED,
tefFAILURE,
terRETRY,
tesSUCCESS,
tecCLAIM,
NotTEC{telLOCAL_ERROR},
TER{tecCLAIM});
static int const kHI_INDEX{std::tuple_size_v<decltype(kTERS)> - 1};
// Verify that all types in the ters tuple can be compared with all
// the other types in ters.
testIterate<kHI_INDEX, kHI_INDEX, CheckComparable>(kTERS, *this);
}
void
run() override
{
testTransResultInfo();
testConversion();
testComparison();
}
};
BEAST_DEFINE_TESTSUITE(TER, protocol, xrpl);
} // namespace xrpl