tomlplusplus/tests/formatters.cpp
2022-04-18 12:27:54 +03:00

333 lines
8.0 KiB
C++

// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
namespace
{
template <typename Formatter, typename T>
static auto format_to_string(const T& obj,
format_flags flags = Formatter::default_flags,
format_flags exclude_flags = format_flags::none)
{
std::stringstream ss;
ss << "*****\n" << Formatter{ obj, flags & ~(exclude_flags) } << "\n*****";
return ss.str();
}
struct char32_printer
{
char32_t value;
friend std::ostream& operator<<(std::ostream& os, const char32_printer& p)
{
if (p.value <= U'\x1F')
return os << '\'' << impl::control_char_escapes[static_cast<size_t>(p.value)] << '\'';
else if (p.value == U'\x7F')
return os << "'\\u007F'"sv;
else if (p.value < 127u)
return os << '\'' << static_cast<char>(static_cast<uint8_t>(p.value)) << '\'';
else
return os << static_cast<uint_least32_t>(p.value);
}
};
struct string_difference
{
source_position position;
size_t index;
char32_t a, b;
friend std::ostream& operator<<(std::ostream& os, const string_difference& diff)
{
if (diff.a && diff.b && diff.a != diff.b)
os << char32_printer{ diff.a } << " vs "sv << char32_printer{ diff.b } << " at "sv;
return os << diff.position << ", index "sv << diff.index;
}
};
static optional<string_difference> find_first_difference(std::string_view str_a, std::string_view str_b) noexcept
{
string_difference diff{ { 1u, 1u } };
impl::utf8_decoder a, b;
for (size_t i = 0, e = std::min(str_a.length(), str_b.length()); i < e; i++, diff.index++)
{
a(static_cast<uint8_t>(str_a[i]));
b(static_cast<uint8_t>(str_b[i]));
if (a.has_code_point() != b.has_code_point() || a.error() != b.error())
return diff;
if (a.error())
{
a.reset();
b.reset();
continue;
}
if (!a.has_code_point())
continue;
if (a.codepoint != b.codepoint)
{
diff.a = a.codepoint;
diff.b = b.codepoint;
return diff;
}
if (a.codepoint == U'\n')
{
diff.position.line++;
diff.position.column = 1u;
}
else
diff.position.column++;
}
if (str_a.length() != str_b.length())
return diff;
return {};
}
}
#define CHECK_FORMATTER(formatter, data, expected) \
do \
{ \
const auto str = format_to_string<formatter>(data); \
const auto diff = find_first_difference(str, expected); \
if (diff) \
FORCE_FAIL("string mismatch: "sv << *diff); \
} \
while (false)
TEST_CASE("formatters")
{
const auto data_date = toml::date{ 2021, 11, 2 };
const auto data_time = toml::time{ 20, 33, 0 };
const auto data = toml::table{
{ "integers"sv,
toml::table{ { "zero"sv, 0 },
{ "one"sv, 1 },
{ "dec"sv, 10 },
{ "bin"sv, 10, toml::value_flags::format_as_binary },
{ "oct"sv, 10, toml::value_flags::format_as_octal },
{ "hex"sv, 10, toml::value_flags::format_as_hexadecimal } } },
{ "floats"sv,
toml::table{ { "pos_zero"sv, +0.0 },
{ "neg_zero"sv, -0.0 },
{ "one"sv, 1.0 },
{ "pos_inf"sv, +std::numeric_limits<double>::infinity() },
{ "neg_inf"sv, -std::numeric_limits<double>::infinity() },
{ "pos_nan"sv, +std::numeric_limits<double>::quiet_NaN() },
{ "neg_nan"sv, -std::numeric_limits<double>::quiet_NaN() }
} },
{ "dates and times"sv,
toml::table{
{ "dates"sv, toml::table{ { "val"sv, data_date } } },
{ "times"sv, toml::table{ { "val"sv, data_time } } },
{ "date-times"sv,
toml::table{
{ "local"sv, toml::table{ { "val"sv, toml::date_time{ data_date, data_time } } } },
{ "offset"sv,
toml::table{
{ "val"sv, toml::date_time{ data_date, data_time, toml::time_offset{} } } } } } } } },
{ "bools"sv,
toml::table{ { "true"sv, true }, //
{ "false"sv, false } } },
{
"strings"sv,
toml::array{ R"()"sv,
R"(string)"sv,
R"(string with a single quote in it: ')"sv,
R"(string with a double quote in it: ")"sv,
"string with a tab: \t"sv,
R"(a long string to force the array over multiple lines)"sv },
},
{ "a"sv,
toml::table{ { "val", true },
{ "b"sv, toml::table{ { "val", true }, { "c"sv, toml::table{ { "val", true } } } } } } }
};
SECTION("toml_formatter")
{
static constexpr auto expected = R"(*****
strings = [
'',
'string',
"string with a single quote in it: '",
'string with a double quote in it: "',
'string with a tab: ',
'a long string to force the array over multiple lines'
]
[a]
val = true
[a.b]
val = true
[a.b.c]
val = true
[bools]
false = false
true = true
['dates and times'.date-times.local]
val = 2021-11-02T20:33:00
['dates and times'.date-times.offset]
val = 2021-11-02T20:33:00Z
['dates and times'.dates]
val = 2021-11-02
['dates and times'.times]
val = 20:33:00
[floats]
neg_inf = -inf
neg_nan = nan
neg_zero = -0.0
one = 1.0
pos_inf = inf
pos_nan = nan
pos_zero = 0.0
[integers]
bin = 0b1010
dec = 10
hex = 0xA
oct = 0o12
one = 1
zero = 0
*****)"sv;
CHECK_FORMATTER(toml_formatter, data, expected);
}
SECTION("json_formatter")
{
static constexpr auto expected = R"(*****
{
"a" : {
"b" : {
"c" : {
"val" : true
},
"val" : true
},
"val" : true
},
"bools" : {
"false" : false,
"true" : true
},
"dates and times" : {
"date-times" : {
"local" : {
"val" : "2021-11-02T20:33:00"
},
"offset" : {
"val" : "2021-11-02T20:33:00Z"
}
},
"dates" : {
"val" : "2021-11-02"
},
"times" : {
"val" : "20:33:00"
}
},
"floats" : {
"neg_inf" : "-Infinity",
"neg_nan" : "NaN",
"neg_zero" : -0.0,
"one" : 1.0,
"pos_inf" : "Infinity",
"pos_nan" : "NaN",
"pos_zero" : 0.0
},
"integers" : {
"bin" : 10,
"dec" : 10,
"hex" : 10,
"oct" : 10,
"one" : 1,
"zero" : 0
},
"strings" : [
"",
"string",
"string with a single quote in it: '",
"string with a double quote in it: \"",
"string with a tab: \t",
"a long string to force the array over multiple lines"
]
}
*****)"sv;
CHECK_FORMATTER(json_formatter, data, expected);
}
SECTION("yaml_formatter")
{
static constexpr auto expected = R"(*****
a:
b:
c:
val: true
val: true
val: true
bools:
false: false
true: true
'dates and times':
date-times:
local:
val: '2021-11-02T20:33:00'
offset:
val: '2021-11-02T20:33:00Z'
dates:
val: '2021-11-02'
times:
val: '20:33:00'
floats:
neg_inf: -.inf
neg_nan: .NAN
neg_zero: -0.0
one: 1.0
pos_inf: .inf
pos_nan: .NAN
pos_zero: 0.0
integers:
bin: 10
dec: 10
hex: 0xA
oct: 0o12
one: 1
zero: 0
strings:
- ''
- string
- "string with a single quote in it: '"
- 'string with a double quote in it: "'
- "string with a tab: \t"
- 'a long string to force the array over multiple lines'
*****)"sv;
CHECK_FORMATTER(yaml_formatter, data, expected);
}
}