mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-30 18:40:28 +00:00
chore: Move lexical cast tests to gtest (#7873)
This commit is contained in:
@@ -58,7 +58,7 @@ struct LexicalCast<Out, std::string_view>
|
||||
"beast::LexicalCast can only be used with integral types");
|
||||
|
||||
template <class Integral = Out>
|
||||
bool
|
||||
constexpr bool
|
||||
operator()(Integral& out, std::string_view in) const
|
||||
requires(std::is_integral_v<Integral> && !std::is_same_v<Integral, bool>)
|
||||
{
|
||||
@@ -110,7 +110,7 @@ struct LexicalCast<Out, boost::core::basic_string_view<char>>
|
||||
{
|
||||
explicit LexicalCast() = default;
|
||||
|
||||
bool
|
||||
constexpr bool
|
||||
operator()(Out& out, boost::core::basic_string_view<char> in) const
|
||||
{
|
||||
return LexicalCast<Out, std::string_view>()(out, in);
|
||||
@@ -123,7 +123,7 @@ struct LexicalCast<Out, std::string>
|
||||
{
|
||||
explicit LexicalCast() = default;
|
||||
|
||||
bool
|
||||
constexpr bool
|
||||
operator()(Out& out, std::string in) const
|
||||
{
|
||||
return LexicalCast<Out, std::string_view>()(out, in);
|
||||
@@ -136,7 +136,7 @@ struct LexicalCast<Out, char const*>
|
||||
{
|
||||
explicit LexicalCast() = default;
|
||||
|
||||
bool
|
||||
constexpr bool
|
||||
operator()(Out& out, char const* in) const
|
||||
{
|
||||
XRPL_ASSERT(in, "beast::detail::LexicalCast(char const*) : non-null input");
|
||||
@@ -151,7 +151,7 @@ struct LexicalCast<Out, char*>
|
||||
{
|
||||
explicit LexicalCast() = default;
|
||||
|
||||
bool
|
||||
constexpr bool
|
||||
operator()(Out& out, char* in) const
|
||||
{
|
||||
XRPL_ASSERT(in, "beast::detail::LexicalCast(char*) : non-null input");
|
||||
@@ -177,7 +177,7 @@ struct BadLexicalCast : public std::bad_cast
|
||||
* @return `false` if there was a parsing or range error
|
||||
*/
|
||||
template <class Out, class In>
|
||||
bool
|
||||
constexpr bool
|
||||
lexicalCastChecked(Out& out, In in)
|
||||
{
|
||||
return detail::LexicalCast<Out, In>()(out, in);
|
||||
@@ -191,7 +191,7 @@ lexicalCastChecked(Out& out, In in)
|
||||
* @return The new type.
|
||||
*/
|
||||
template <class Out, class In>
|
||||
Out
|
||||
constexpr Out
|
||||
lexicalCastThrow(In in)
|
||||
{
|
||||
if (Out out; lexicalCastChecked(out, in))
|
||||
@@ -207,7 +207,7 @@ lexicalCastThrow(In in)
|
||||
* @return The new type.
|
||||
*/
|
||||
template <class Out, class In>
|
||||
Out
|
||||
constexpr Out
|
||||
lexicalCast(In in, Out defaultValue = Out())
|
||||
{
|
||||
if (Out out; lexicalCastChecked(out, in))
|
||||
|
||||
@@ -1,280 +0,0 @@
|
||||
#include <xrpl/beast/core/LexicalCast.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/beast/xor_shift_engine.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
|
||||
class LexicalCast_test : public unit_test::Suite
|
||||
{
|
||||
public:
|
||||
template <class IntType>
|
||||
static IntType
|
||||
nextRandomInt(xor_shift_engine& r)
|
||||
{
|
||||
return static_cast<IntType>(r());
|
||||
}
|
||||
|
||||
template <class IntType>
|
||||
void
|
||||
testInteger(IntType in)
|
||||
{
|
||||
std::string s;
|
||||
auto out = static_cast<IntType>(~in); // Ensure out != in
|
||||
|
||||
expect(lexicalCastChecked(s, in));
|
||||
expect(lexicalCastChecked(out, s));
|
||||
expect(out == in);
|
||||
}
|
||||
|
||||
template <class IntType>
|
||||
void
|
||||
testIntegers(xor_shift_engine& r)
|
||||
{
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "random " << typeid(IntType).name();
|
||||
testcase(ss.str());
|
||||
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
auto const value = nextRandomInt<IntType>(r);
|
||||
testInteger(value);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "numeric_limits <" << typeid(IntType).name() << ">";
|
||||
testcase(ss.str());
|
||||
|
||||
testInteger(std::numeric_limits<IntType>::min());
|
||||
testInteger(std::numeric_limits<IntType>::max());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testPathologies()
|
||||
{
|
||||
testcase("pathologies");
|
||||
try
|
||||
{
|
||||
lexicalCastThrow<int>("\xef\xbc\x91\xef\xbc\x90"); // utf-8 encoded
|
||||
}
|
||||
catch (BadLexicalCast const&)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
tryBadConvert(std::string const& s)
|
||||
{
|
||||
T out;
|
||||
expect(!lexicalCastChecked(out, s), s);
|
||||
}
|
||||
|
||||
void
|
||||
testConversionOverflows()
|
||||
{
|
||||
testcase("conversion overflows");
|
||||
|
||||
tryBadConvert<std::uint64_t>("99999999999999999999");
|
||||
tryBadConvert<std::uint32_t>("4294967300");
|
||||
tryBadConvert<std::uint16_t>("75821");
|
||||
}
|
||||
|
||||
void
|
||||
testConversionUnderflows()
|
||||
{
|
||||
testcase("conversion underflows");
|
||||
|
||||
tryBadConvert<std::uint32_t>("-1");
|
||||
|
||||
tryBadConvert<std::int64_t>("-99999999999999999999");
|
||||
tryBadConvert<std::int32_t>("-4294967300");
|
||||
tryBadConvert<std::int16_t>("-75821");
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool
|
||||
tryEdgeCase(std::string const& s)
|
||||
{
|
||||
T ret;
|
||||
|
||||
bool const result = lexicalCastChecked(ret, s);
|
||||
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
return s == std::to_string(ret);
|
||||
}
|
||||
|
||||
void
|
||||
testEdgeCases()
|
||||
{
|
||||
testcase("conversion edge cases");
|
||||
|
||||
expect(tryEdgeCase<std::uint64_t>("18446744073709551614"));
|
||||
expect(tryEdgeCase<std::uint64_t>("18446744073709551615"));
|
||||
expect(!tryEdgeCase<std::uint64_t>("18446744073709551616"));
|
||||
|
||||
expect(tryEdgeCase<std::int64_t>("9223372036854775806"));
|
||||
expect(tryEdgeCase<std::int64_t>("9223372036854775807"));
|
||||
expect(!tryEdgeCase<std::int64_t>("9223372036854775808"));
|
||||
|
||||
expect(tryEdgeCase<std::int64_t>("-9223372036854775807"));
|
||||
expect(tryEdgeCase<std::int64_t>("-9223372036854775808"));
|
||||
expect(!tryEdgeCase<std::int64_t>("-9223372036854775809"));
|
||||
|
||||
expect(tryEdgeCase<std::uint32_t>("4294967294"));
|
||||
expect(tryEdgeCase<std::uint32_t>("4294967295"));
|
||||
expect(!tryEdgeCase<std::uint32_t>("4294967296"));
|
||||
|
||||
expect(tryEdgeCase<std::int32_t>("2147483646"));
|
||||
expect(tryEdgeCase<std::int32_t>("2147483647"));
|
||||
expect(!tryEdgeCase<std::int32_t>("2147483648"));
|
||||
|
||||
expect(tryEdgeCase<std::int32_t>("-2147483647"));
|
||||
expect(tryEdgeCase<std::int32_t>("-2147483648"));
|
||||
expect(!tryEdgeCase<std::int32_t>("-2147483649"));
|
||||
|
||||
expect(tryEdgeCase<std::uint16_t>("65534"));
|
||||
expect(tryEdgeCase<std::uint16_t>("65535"));
|
||||
expect(!tryEdgeCase<std::uint16_t>("65536"));
|
||||
|
||||
expect(tryEdgeCase<std::int16_t>("32766"));
|
||||
expect(tryEdgeCase<std::int16_t>("32767"));
|
||||
expect(!tryEdgeCase<std::int16_t>("32768"));
|
||||
|
||||
expect(tryEdgeCase<std::int16_t>("-32767"));
|
||||
expect(tryEdgeCase<std::int16_t>("-32768"));
|
||||
expect(!tryEdgeCase<std::int16_t>("-32769"));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
testThrowConvert(std::string const& s, bool success)
|
||||
{
|
||||
bool result = !success;
|
||||
T out;
|
||||
|
||||
try
|
||||
{
|
||||
out = lexicalCastThrow<T>(s);
|
||||
result = true;
|
||||
}
|
||||
catch (BadLexicalCast const&)
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
|
||||
expect(result == success, s);
|
||||
}
|
||||
|
||||
void
|
||||
testThrowingConversions()
|
||||
{
|
||||
testcase("throwing conversion");
|
||||
|
||||
testThrowConvert<std::uint64_t>("99999999999999999999", false);
|
||||
testThrowConvert<std::uint64_t>("9223372036854775806", true);
|
||||
|
||||
testThrowConvert<std::uint32_t>("4294967290", true);
|
||||
testThrowConvert<std::uint32_t>("42949672900", false);
|
||||
testThrowConvert<std::uint32_t>("429496729000", false);
|
||||
testThrowConvert<std::uint32_t>("4294967290000", false);
|
||||
|
||||
testThrowConvert<std::int32_t>("5294967295", false);
|
||||
testThrowConvert<std::int32_t>("-2147483644", true);
|
||||
|
||||
testThrowConvert<std::int16_t>("66666", false);
|
||||
testThrowConvert<std::int16_t>("-5711", true);
|
||||
}
|
||||
|
||||
void
|
||||
testZero()
|
||||
{
|
||||
testcase("zero conversion");
|
||||
|
||||
{
|
||||
std::int32_t out = 0;
|
||||
|
||||
expect(lexicalCastChecked(out, "-0"), "0");
|
||||
expect(lexicalCastChecked(out, "0"), "0");
|
||||
expect(lexicalCastChecked(out, "+0"), "0");
|
||||
}
|
||||
|
||||
{
|
||||
std::uint32_t out = 0;
|
||||
|
||||
expect(!lexicalCastChecked(out, "-0"), "0");
|
||||
expect(lexicalCastChecked(out, "0"), "0");
|
||||
expect(lexicalCastChecked(out, "+0"), "0");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testEntireRange()
|
||||
{
|
||||
testcase("entire range");
|
||||
|
||||
std::int32_t i = std::numeric_limits<std::int16_t>::min();
|
||||
std::string const empty;
|
||||
|
||||
while (i <= std::numeric_limits<std::int16_t>::max())
|
||||
{
|
||||
auto const j = static_cast<std::int16_t>(i);
|
||||
|
||||
auto actual = std::to_string(j);
|
||||
|
||||
auto result = lexicalCast(j, empty);
|
||||
|
||||
expect(result == actual, actual + " (string to integer)");
|
||||
|
||||
if (result == actual)
|
||||
{
|
||||
auto number = lexicalCast<std::int16_t>(result);
|
||||
|
||||
if (number != j)
|
||||
expect(false, actual + " (integer to string)");
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
std::int64_t const seedValue = 50;
|
||||
|
||||
xor_shift_engine r(seedValue);
|
||||
|
||||
testIntegers<int>(r);
|
||||
testIntegers<unsigned int>(r);
|
||||
testIntegers<short>(r);
|
||||
testIntegers<unsigned short>(r);
|
||||
testIntegers<std::int32_t>(r);
|
||||
testIntegers<std::uint32_t>(r);
|
||||
testIntegers<std::int64_t>(r);
|
||||
testIntegers<std::uint64_t>(r);
|
||||
|
||||
testPathologies();
|
||||
testConversionOverflows();
|
||||
testConversionUnderflows();
|
||||
testThrowingConversions();
|
||||
testZero();
|
||||
testEdgeCases();
|
||||
testEntireRange();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(LexicalCast, beast, beast);
|
||||
|
||||
} // namespace beast
|
||||
339
src/tests/libxrpl/beast/LexicalCast.cpp
Normal file
339
src/tests/libxrpl/beast/LexicalCast.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
#include <xrpl/beast/core/LexicalCast.h>
|
||||
|
||||
#include <xrpl/beast/xor_shift_engine.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
#include <charconv>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace {
|
||||
|
||||
template <class T>
|
||||
[[nodiscard]] constexpr bool
|
||||
parses(std::string_view text)
|
||||
{
|
||||
T out{};
|
||||
return lexicalCastChecked(out, text);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
[[nodiscard]] constexpr T
|
||||
parsed(std::string_view text)
|
||||
{
|
||||
T out{};
|
||||
return lexicalCastChecked(out, text) ? out : T{};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
constexpr T kMax = std::numeric_limits<T>::max();
|
||||
|
||||
template <class T>
|
||||
constexpr T kMin = std::numeric_limits<T>::min();
|
||||
|
||||
template <class T>
|
||||
constexpr T kUnderMax = kMax<T> - 1;
|
||||
|
||||
template <class T>
|
||||
constexpr T kOverMin = kMin<T> + 1;
|
||||
|
||||
// Comfortably inside the range, not boundary values.
|
||||
constexpr auto kNearMax32 = kMax<uint32_t> - 5;
|
||||
constexpr auto kNearMin32 = kMin<int32_t> + 4;
|
||||
constexpr auto kUnderInt64Max = uint64_t{kMax<int64_t>} - 1;
|
||||
constexpr auto kInRangeInt16 = int16_t{-5711};
|
||||
|
||||
// No wider integer type can hold these, so ToString cannot produce them.
|
||||
constexpr auto kAboveUint64Max = "18446744073709551616";
|
||||
constexpr auto kBelowInt64Min = "-9223372036854775809";
|
||||
|
||||
// Out of range for every integer type we test.
|
||||
constexpr auto kTwentyNines = "99999999999999999999";
|
||||
constexpr auto kNegativeTwentyNines = "-99999999999999999999";
|
||||
|
||||
// Arbitrary values chosen to sit well outside a type's range, not just over it.
|
||||
constexpr auto kAboveUint16Max = "75821";
|
||||
constexpr auto kBelowInt16Min = "-75821";
|
||||
constexpr auto kAboveInt32Max = "5294967295";
|
||||
constexpr auto kAboveInt16Max = "66666";
|
||||
|
||||
constexpr auto kPositiveInt32 = int32_t{42};
|
||||
constexpr auto kNegativeInt32 = int32_t{-42};
|
||||
|
||||
constexpr auto kPositiveInt32Text = "+42";
|
||||
constexpr auto kNegativeInt32Text = "-42";
|
||||
|
||||
constexpr auto kNegativeOne = "-1";
|
||||
constexpr auto kNegativeZero = "-0";
|
||||
constexpr auto kBareZero = "0";
|
||||
constexpr auto kPositiveZero = "+0";
|
||||
|
||||
// Full-width digits one and zero, not ASCII ones.
|
||||
constexpr std::string_view kFullWidthDigits = "\xef\xbc\x91\xef\xbc\x90";
|
||||
|
||||
// The decimal text of a value, usable in a constant expression.
|
||||
template <class T>
|
||||
struct ToString
|
||||
{
|
||||
std::array<char, 24> buffer{};
|
||||
std::size_t length{};
|
||||
|
||||
constexpr explicit ToString(T value)
|
||||
{
|
||||
auto const result = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value);
|
||||
length = static_cast<std::size_t>(result.ptr - buffer.data());
|
||||
}
|
||||
|
||||
constexpr
|
||||
operator std::string_view() const
|
||||
{
|
||||
return {buffer.data(), length};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
constexpr auto kMaxText = ToString{kMax<T>};
|
||||
|
||||
template <class T>
|
||||
constexpr auto kUnderMaxText = ToString{kUnderMax<T>};
|
||||
|
||||
template <class T, class Wider>
|
||||
constexpr auto kOverMaxText = ToString{Wider{kMax<T>} + 1};
|
||||
|
||||
template <class T>
|
||||
constexpr auto kMinText = ToString{kMin<T>};
|
||||
|
||||
template <class T>
|
||||
constexpr auto kOverMinText = ToString{kOverMin<T>};
|
||||
|
||||
template <class T, class Wider>
|
||||
constexpr auto kUnderMinText = ToString{Wider{kMin<T>} - 1};
|
||||
|
||||
constexpr auto kOverUint32MaxText = ToString{uint64_t{kMax<uint32_t>} + 5};
|
||||
constexpr auto kNegatedOverUint32MaxText = ToString{-(int64_t{kMax<uint32_t>} + 5)};
|
||||
|
||||
// lexicalCastThrow deduces its input type, so the text has to be an explicit
|
||||
// string_view rather than a ToString.
|
||||
template <class T, class Value>
|
||||
[[nodiscard]] constexpr T
|
||||
castThrow(Value value)
|
||||
{
|
||||
return lexicalCastThrow<T>(std::string_view{ToString{value}});
|
||||
}
|
||||
|
||||
template <class T>
|
||||
[[nodiscard]] bool
|
||||
roundTrips(std::string_view text)
|
||||
{
|
||||
T out{};
|
||||
return lexicalCastChecked(out, text) && std::to_string(out) == text;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
expectRoundTrip(T value)
|
||||
{
|
||||
SCOPED_TRACE(::testing::Message() << "value: " << value);
|
||||
|
||||
auto const text = lexicalCast<std::string>(value);
|
||||
EXPECT_EQ(text, std::to_string(value));
|
||||
|
||||
auto decoded = static_cast<T>(~value); // ensure decoded != value
|
||||
EXPECT_TRUE(lexicalCastChecked(decoded, text));
|
||||
EXPECT_EQ(decoded, value);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// int/unsigned/short/unsigned short are covered by the list below — they are
|
||||
// these exact types everywhere we build.
|
||||
static_assert(std::is_same_v<int, int32_t>);
|
||||
static_assert(std::is_same_v<unsigned int, uint32_t>);
|
||||
static_assert(std::is_same_v<short, int16_t>);
|
||||
static_assert(std::is_same_v<unsigned short, uint16_t>);
|
||||
|
||||
using IntegerTypes = ::testing::Types< //
|
||||
int16_t,
|
||||
uint16_t,
|
||||
int32_t,
|
||||
uint32_t,
|
||||
int64_t,
|
||||
uint64_t>;
|
||||
|
||||
struct IntegerTypeNames
|
||||
{
|
||||
template <class T>
|
||||
static std::string
|
||||
// NOLINTNEXTLINE(readability-identifier-naming) - required by gtest
|
||||
GetName(int)
|
||||
{
|
||||
return (std::is_signed_v<T> ? "int" : "uint") + std::to_string(sizeof(T) * 8) + "_t";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class LexicalCastIntegers : public ::testing::Test
|
||||
{
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE(LexicalCastIntegers, IntegerTypes, IntegerTypeNames);
|
||||
|
||||
TYPED_TEST(LexicalCastIntegers, round_trips_random_values)
|
||||
{
|
||||
static constexpr auto kSampleCount = 1000uz;
|
||||
|
||||
xor_shift_engine r{50}; // seeded per test so a failure reproduces on its own
|
||||
|
||||
for (auto i = 0uz; i < kSampleCount; ++i)
|
||||
expectRoundTrip(static_cast<TypeParam>(r()));
|
||||
}
|
||||
|
||||
TYPED_TEST(LexicalCastIntegers, round_trips_numeric_limits)
|
||||
{
|
||||
expectRoundTrip(std::numeric_limits<TypeParam>::min());
|
||||
expectRoundTrip(std::numeric_limits<TypeParam>::max());
|
||||
}
|
||||
|
||||
TEST(LexicalCast, round_trips_every_int16_value)
|
||||
{
|
||||
for (int32_t i = kMin<int16_t>; i <= kMax<int16_t>; ++i)
|
||||
{
|
||||
auto const value = static_cast<int16_t>(i);
|
||||
|
||||
// ASSERT, or a broken cast reports all 65536 iterations.
|
||||
auto const text = lexicalCast<std::string>(value);
|
||||
ASSERT_EQ(text, std::to_string(value));
|
||||
ASSERT_EQ(lexicalCast<int16_t>(text), value);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(LexicalCast, rejects_overflow)
|
||||
{
|
||||
static_assert(not parses<uint32_t>(kOverUint32MaxText));
|
||||
static_assert(not parses<uint64_t>(kTwentyNines));
|
||||
static_assert(not parses<uint16_t>(kAboveUint16Max));
|
||||
}
|
||||
|
||||
TEST(LexicalCast, rejects_underflow)
|
||||
{
|
||||
static_assert(not parses<uint32_t>(kNegativeOne));
|
||||
static_assert(not parses<int32_t>(kNegatedOverUint32MaxText));
|
||||
static_assert(not parses<int64_t>(kNegativeTwentyNines));
|
||||
static_assert(not parses<int16_t>(kBelowInt16Min));
|
||||
}
|
||||
|
||||
TEST(LexicalCast, accepts_up_to_the_maximum)
|
||||
{
|
||||
static_assert(parsed<uint16_t>(kUnderMaxText<uint16_t>) == kUnderMax<uint16_t>);
|
||||
static_assert(parsed<uint16_t>(kMaxText<uint16_t>) == kMax<uint16_t>);
|
||||
static_assert(not parses<uint16_t>(kOverMaxText<uint16_t, uint32_t>));
|
||||
|
||||
static_assert(parsed<int16_t>(kUnderMaxText<int16_t>) == kUnderMax<int16_t>);
|
||||
static_assert(parsed<int16_t>(kMaxText<int16_t>) == kMax<int16_t>);
|
||||
static_assert(not parses<int16_t>(kOverMaxText<int16_t, int32_t>));
|
||||
|
||||
static_assert(parsed<uint32_t>(kUnderMaxText<uint32_t>) == kUnderMax<uint32_t>);
|
||||
static_assert(parsed<uint32_t>(kMaxText<uint32_t>) == kMax<uint32_t>);
|
||||
static_assert(not parses<uint32_t>(kOverMaxText<uint32_t, uint64_t>));
|
||||
|
||||
static_assert(parsed<int32_t>(kUnderMaxText<int32_t>) == kUnderMax<int32_t>);
|
||||
static_assert(parsed<int32_t>(kMaxText<int32_t>) == kMax<int32_t>);
|
||||
static_assert(not parses<int32_t>(kOverMaxText<int32_t, int64_t>));
|
||||
|
||||
static_assert(parsed<int64_t>(kUnderMaxText<int64_t>) == kUnderMax<int64_t>);
|
||||
static_assert(parsed<int64_t>(kMaxText<int64_t>) == kMax<int64_t>);
|
||||
static_assert(not parses<int64_t>(kOverMaxText<int64_t, uint64_t>));
|
||||
|
||||
static_assert(parsed<uint64_t>(kUnderMaxText<uint64_t>) == kUnderMax<uint64_t>);
|
||||
static_assert(parsed<uint64_t>(kMaxText<uint64_t>) == kMax<uint64_t>);
|
||||
static_assert(not parses<uint64_t>(kAboveUint64Max));
|
||||
}
|
||||
|
||||
TEST(LexicalCast, accepts_down_to_the_minimum)
|
||||
{
|
||||
static_assert(parsed<int16_t>(kOverMinText<int16_t>) == kOverMin<int16_t>);
|
||||
static_assert(parsed<int16_t>(kMinText<int16_t>) == kMin<int16_t>);
|
||||
static_assert(not parses<int16_t>(kUnderMinText<int16_t, int32_t>));
|
||||
|
||||
static_assert(parsed<int32_t>(kOverMinText<int32_t>) == kOverMin<int32_t>);
|
||||
static_assert(parsed<int32_t>(kMinText<int32_t>) == kMin<int32_t>);
|
||||
static_assert(not parses<int32_t>(kUnderMinText<int32_t, int64_t>));
|
||||
|
||||
static_assert(parsed<int64_t>(kOverMinText<int64_t>) == kOverMin<int64_t>);
|
||||
static_assert(parsed<int64_t>(kMinText<int64_t>) == kMin<int64_t>);
|
||||
static_assert(not parses<int64_t>(kBelowInt64Min));
|
||||
}
|
||||
|
||||
TEST(LexicalCast, limits_round_trip_through_to_string)
|
||||
{
|
||||
EXPECT_TRUE(roundTrips<uint64_t>(kMaxText<uint64_t>));
|
||||
EXPECT_TRUE(roundTrips<int64_t>(kMaxText<int64_t>));
|
||||
EXPECT_TRUE(roundTrips<int64_t>(kMinText<int64_t>));
|
||||
EXPECT_TRUE(roundTrips<uint32_t>(kMaxText<uint32_t>));
|
||||
EXPECT_TRUE(roundTrips<int32_t>(kMinText<int32_t>));
|
||||
EXPECT_TRUE(roundTrips<uint16_t>(kMaxText<uint16_t>));
|
||||
EXPECT_TRUE(roundTrips<int16_t>(kMinText<int16_t>));
|
||||
}
|
||||
|
||||
TEST(LexicalCast, accepts_signed_zero_in_every_form)
|
||||
{
|
||||
static_assert(parsed<int32_t>(kNegativeZero) == 0);
|
||||
static_assert(parsed<int32_t>(kBareZero) == 0);
|
||||
static_assert(parsed<int32_t>(kPositiveZero) == 0);
|
||||
}
|
||||
|
||||
TEST(LexicalCast, rejects_negative_zero_when_unsigned)
|
||||
{
|
||||
static_assert(not parses<uint32_t>(kNegativeZero));
|
||||
static_assert(parsed<uint32_t>(kBareZero) == 0);
|
||||
static_assert(parsed<uint32_t>(kPositiveZero) == 0);
|
||||
}
|
||||
|
||||
TEST(LexicalCast, accepts_char_pointer_and_std_string_input)
|
||||
{
|
||||
int32_t fromLiteral = 0;
|
||||
EXPECT_TRUE(lexicalCastChecked(fromLiteral, kPositiveInt32Text));
|
||||
EXPECT_EQ(fromLiteral, kPositiveInt32);
|
||||
|
||||
int32_t fromString = 0;
|
||||
EXPECT_TRUE(lexicalCastChecked(fromString, std::string{kNegativeInt32Text}));
|
||||
EXPECT_EQ(fromString, kNegativeInt32);
|
||||
}
|
||||
|
||||
TEST(LexicalCast, throwing_cast_returns_in_range_values)
|
||||
{
|
||||
static_assert(castThrow<uint64_t>(kUnderInt64Max) == kUnderInt64Max);
|
||||
static_assert(castThrow<uint32_t>(kNearMax32) == kNearMax32);
|
||||
static_assert(castThrow<int32_t>(kNearMin32) == kNearMin32);
|
||||
static_assert(castThrow<int16_t>(kInRangeInt16) == kInRangeInt16);
|
||||
}
|
||||
|
||||
TEST(LexicalCast, throwing_cast_throws_on_out_of_range)
|
||||
{
|
||||
EXPECT_THROW(lexicalCastThrow<uint64_t>(kTwentyNines), BadLexicalCast);
|
||||
|
||||
// kNearMax32 with digits appended, so each is further past uint32_t's range.
|
||||
for (auto const scale : {10, 100, 1000})
|
||||
{
|
||||
auto const tooBig = ToString{uint64_t{kNearMax32} * scale};
|
||||
EXPECT_THROW(lexicalCastThrow<uint32_t>(std::string_view{tooBig}), BadLexicalCast);
|
||||
}
|
||||
|
||||
EXPECT_THROW(lexicalCastThrow<int32_t>(kAboveInt32Max), BadLexicalCast);
|
||||
EXPECT_THROW(lexicalCastThrow<int16_t>(kAboveInt16Max), BadLexicalCast);
|
||||
}
|
||||
|
||||
// Full-width digits, not ASCII ones.
|
||||
TEST(LexicalCast, throwing_cast_throws_on_utf8_digits)
|
||||
{
|
||||
EXPECT_THROW(lexicalCastThrow<int>(kFullWidthDigits), BadLexicalCast);
|
||||
}
|
||||
|
||||
} // namespace beast
|
||||
Reference in New Issue
Block a user