2020-08-11 18:11:55 +00:00
|
|
|
/*
|
|
|
|
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
|
|
|
*
|
|
|
|
* It may be used under the GNU GPL versions 2 or 3
|
|
|
|
* or any future license endorsed by Mnemosyne LLC.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define LIBTRANSMISSION_VARIANT_MODULE
|
|
|
|
|
|
|
|
#include "transmission.h"
|
|
|
|
#include "utils.h" /* tr_free */
|
|
|
|
#include "variant-common.h"
|
|
|
|
#include "variant.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <array>
|
|
|
|
#include <cmath> // lrint()
|
|
|
|
#include <cctype> // isspace()
|
|
|
|
#include <string>
|
2021-10-15 13:28:47 +00:00
|
|
|
#include <string_view>
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
|
2021-10-15 13:28:47 +00:00
|
|
|
using namespace std::literals;
|
|
|
|
|
2020-08-11 18:11:55 +00:00
|
|
|
class VariantTest : public ::testing::Test
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
std::string stripWhitespace(std::string const& in)
|
|
|
|
{
|
|
|
|
auto s = in;
|
|
|
|
s.erase(s.begin(), std::find_if_not(s.begin(), s.end(), ::isspace));
|
|
|
|
s.erase(std::find_if_not(s.rbegin(), s.rend(), ::isspace).base(), s.end());
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto bencParseInt(std::string const& in, uint8_t const** end, int64_t* val)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
return tr_bencParseInt(in.data(), in.data() + in.size(), end, val);
|
2020-08-11 18:11:55 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
2021-08-15 09:41:48 +00:00
|
|
|
#define STACK_SMASH_DEPTH (1 * 1000 * 1000)
|
2020-08-11 18:11:55 +00:00
|
|
|
#else
|
2021-08-15 09:41:48 +00:00
|
|
|
#define STACK_SMASH_DEPTH (100 * 1000)
|
2020-08-11 18:11:55 +00:00
|
|
|
#endif
|
|
|
|
|
2021-11-01 21:30:18 +00:00
|
|
|
TEST_F(VariantTest, getType)
|
|
|
|
{
|
|
|
|
auto i = int64_t{};
|
|
|
|
auto b = bool{};
|
|
|
|
auto d = double{};
|
|
|
|
auto sv = std::string_view{};
|
|
|
|
auto v = tr_variant{};
|
|
|
|
|
|
|
|
tr_variantInitInt(&v, 30);
|
|
|
|
EXPECT_TRUE(tr_variantGetInt(&v, &i));
|
|
|
|
EXPECT_EQ(30, i);
|
|
|
|
EXPECT_TRUE(tr_variantGetReal(&v, &d));
|
|
|
|
EXPECT_EQ(30, int(d));
|
|
|
|
EXPECT_FALSE(tr_variantGetBool(&v, &b));
|
|
|
|
EXPECT_FALSE(tr_variantGetStrView(&v, &sv));
|
|
|
|
|
|
|
|
auto strkey = "foo"sv;
|
|
|
|
tr_variantInitStr(&v, strkey);
|
|
|
|
EXPECT_FALSE(tr_variantGetBool(&v, &b));
|
|
|
|
EXPECT_TRUE(tr_variantGetStrView(&v, &sv));
|
|
|
|
EXPECT_EQ(strkey, sv);
|
|
|
|
|
|
|
|
strkey = "true"sv;
|
|
|
|
tr_variantInitStr(&v, strkey);
|
|
|
|
EXPECT_TRUE(tr_variantGetBool(&v, &b));
|
|
|
|
EXPECT_TRUE(b);
|
|
|
|
EXPECT_TRUE(tr_variantGetStrView(&v, &sv));
|
|
|
|
EXPECT_EQ(strkey, sv);
|
|
|
|
|
|
|
|
strkey = "false"sv;
|
|
|
|
tr_variantInitStr(&v, strkey);
|
|
|
|
EXPECT_TRUE(tr_variantGetBool(&v, &b));
|
|
|
|
EXPECT_FALSE(b);
|
|
|
|
EXPECT_TRUE(tr_variantGetStrView(&v, &sv));
|
|
|
|
EXPECT_EQ(strkey, sv);
|
|
|
|
}
|
|
|
|
|
2020-08-11 18:11:55 +00:00
|
|
|
TEST_F(VariantTest, parseInt)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
auto const in = std::string{ "i64e" };
|
|
|
|
auto constexpr InitVal = int64_t{ 888 };
|
|
|
|
auto constexpr ExpectVal = int64_t{ 64 };
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
uint8_t const* end = {};
|
2021-08-15 09:41:48 +00:00
|
|
|
auto val = int64_t{ InitVal };
|
2020-08-11 18:11:55 +00:00
|
|
|
auto const err = bencParseInt(in, &end, &val);
|
|
|
|
EXPECT_EQ(0, err);
|
|
|
|
EXPECT_EQ(ExpectVal, val);
|
|
|
|
EXPECT_EQ(reinterpret_cast<decltype(end)>(in.data() + in.size()), end);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, parseIntWithMissingEnd)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
auto const in = std::string{ "i64" };
|
|
|
|
auto constexpr InitVal = int64_t{ 888 };
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
uint8_t const* end = {};
|
2021-08-15 09:41:48 +00:00
|
|
|
auto val = int64_t{ InitVal };
|
2020-08-11 18:11:55 +00:00
|
|
|
auto const err = bencParseInt(in, &end, &val);
|
|
|
|
EXPECT_EQ(EILSEQ, err);
|
|
|
|
EXPECT_EQ(InitVal, val);
|
|
|
|
EXPECT_EQ(nullptr, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, parseIntEmptyBuffer)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
auto const in = std::string{};
|
|
|
|
auto constexpr InitVal = int64_t{ 888 };
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
uint8_t const* end = {};
|
2021-08-15 09:41:48 +00:00
|
|
|
auto val = int64_t{ InitVal };
|
2020-08-11 18:11:55 +00:00
|
|
|
auto const err = bencParseInt(in, &end, &val);
|
|
|
|
EXPECT_EQ(EILSEQ, err);
|
|
|
|
EXPECT_EQ(InitVal, val);
|
|
|
|
EXPECT_EQ(nullptr, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, parseIntWithBadDigits)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
auto const in = std::string{ "i6z4e" };
|
|
|
|
auto constexpr InitVal = int64_t{ 888 };
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
uint8_t const* end = {};
|
2021-08-15 09:41:48 +00:00
|
|
|
auto val = int64_t{ InitVal };
|
2020-08-11 18:11:55 +00:00
|
|
|
auto const err = bencParseInt(in, &end, &val);
|
|
|
|
EXPECT_EQ(EILSEQ, err);
|
|
|
|
EXPECT_EQ(InitVal, val);
|
|
|
|
EXPECT_EQ(nullptr, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, parseNegativeInt)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
auto const in = std::string{ "i-3e" };
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
uint8_t const* end = {};
|
2021-08-15 09:41:48 +00:00
|
|
|
auto val = int64_t{};
|
2020-08-11 18:11:55 +00:00
|
|
|
auto const err = bencParseInt(in, &end, &val);
|
|
|
|
EXPECT_EQ(0, err);
|
|
|
|
EXPECT_EQ(-3, val);
|
|
|
|
EXPECT_EQ(reinterpret_cast<decltype(end)>(in.data() + in.size()), end);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, parseIntZero)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
auto const in = std::string{ "i0e" };
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
uint8_t const* end = {};
|
2021-08-15 09:41:48 +00:00
|
|
|
auto val = int64_t{};
|
2020-08-11 18:11:55 +00:00
|
|
|
auto const err = bencParseInt(in, &end, &val);
|
|
|
|
EXPECT_EQ(0, err);
|
|
|
|
EXPECT_EQ(0, val);
|
|
|
|
EXPECT_EQ(reinterpret_cast<decltype(end)>(in.data() + in.size()), end);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, parseIntWithLeadingZero)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
auto const in = std::string{ "i04e" };
|
|
|
|
auto constexpr InitVal = int64_t{ 888 };
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
uint8_t const* end = {};
|
2021-08-15 09:41:48 +00:00
|
|
|
auto val = int64_t{ InitVal };
|
2020-08-11 18:11:55 +00:00
|
|
|
auto const err = bencParseInt(in, &end, &val);
|
|
|
|
EXPECT_EQ(EILSEQ, err); // no leading zeroes allowed
|
|
|
|
EXPECT_EQ(InitVal, val);
|
|
|
|
EXPECT_EQ(nullptr, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, str)
|
|
|
|
{
|
|
|
|
auto buf = std::array<uint8_t, 128>{};
|
|
|
|
int err;
|
|
|
|
int n;
|
|
|
|
uint8_t const* end;
|
|
|
|
uint8_t const* str;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
// string len is designed to overflow
|
|
|
|
n = tr_snprintf(buf.data(), buf.size(), "%zu:boat", size_t(SIZE_MAX - 2));
|
|
|
|
err = tr_bencParseStr(&buf[0], &buf[n], &end, &str, &len);
|
|
|
|
EXPECT_EQ(EILSEQ, err);
|
|
|
|
EXPECT_EQ(size_t{}, len);
|
|
|
|
EXPECT_EQ(nullptr, str);
|
|
|
|
EXPECT_EQ(nullptr, end);
|
|
|
|
|
|
|
|
// good string
|
|
|
|
n = tr_snprintf(buf.data(), buf.size(), "4:boat");
|
|
|
|
err = tr_bencParseStr(&buf[0], &buf[n], &end, &str, &len);
|
|
|
|
EXPECT_EQ(0, err);
|
|
|
|
EXPECT_EQ(size_t{ 4 }, len);
|
|
|
|
EXPECT_EQ(0, memcmp("boat", str, len));
|
|
|
|
EXPECT_EQ(buf.data() + n, end);
|
|
|
|
str = nullptr;
|
|
|
|
end = nullptr;
|
|
|
|
len = 0;
|
|
|
|
|
|
|
|
// string goes past end of buffer
|
|
|
|
err = tr_bencParseStr(&buf[0], &buf[n - 1], &end, &str, &len);
|
|
|
|
EXPECT_EQ(EILSEQ, err);
|
|
|
|
EXPECT_EQ(size_t{}, len);
|
|
|
|
EXPECT_EQ(nullptr, str);
|
|
|
|
EXPECT_EQ(nullptr, end);
|
|
|
|
|
|
|
|
// empty string
|
|
|
|
n = tr_snprintf(buf.data(), buf.size(), "0:");
|
|
|
|
err = tr_bencParseStr(&buf[0], &buf[n], &end, &str, &len);
|
|
|
|
EXPECT_EQ(0, err);
|
|
|
|
EXPECT_EQ(size_t{}, len);
|
|
|
|
EXPECT_EQ('\0', *str);
|
|
|
|
EXPECT_EQ(buf.data() + n, end);
|
|
|
|
str = nullptr;
|
|
|
|
end = nullptr;
|
|
|
|
len = 0;
|
|
|
|
|
|
|
|
// short string
|
|
|
|
n = tr_snprintf(buf.data(), buf.size(), "3:boat");
|
|
|
|
err = tr_bencParseStr(&buf[0], &buf[n], &end, &str, &len);
|
|
|
|
EXPECT_EQ(0, err);
|
|
|
|
EXPECT_EQ(size_t{ 3 }, len);
|
|
|
|
EXPECT_EQ(0, memcmp("boa", str, len));
|
|
|
|
EXPECT_EQ(buf.data() + 5, end);
|
|
|
|
str = nullptr;
|
|
|
|
end = nullptr;
|
|
|
|
len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, parse)
|
|
|
|
{
|
|
|
|
auto buf = std::array<uint8_t, 512>{};
|
|
|
|
int64_t i;
|
|
|
|
|
|
|
|
tr_variant val;
|
|
|
|
char const* end;
|
|
|
|
auto n = tr_snprintf(buf.data(), buf.size(), "i64e");
|
|
|
|
auto err = tr_variantFromBencFull(&val, buf.data(), n, nullptr, &end);
|
|
|
|
EXPECT_EQ(0, err);
|
|
|
|
EXPECT_TRUE(tr_variantGetInt(&val, &i));
|
|
|
|
EXPECT_EQ(int64_t(64), i);
|
|
|
|
EXPECT_EQ(reinterpret_cast<char const*>(buf.data()) + n, end);
|
|
|
|
tr_variantFree(&val);
|
|
|
|
|
|
|
|
n = tr_snprintf(buf.data(), buf.size(), "li64ei32ei16ee");
|
|
|
|
err = tr_variantFromBencFull(&val, buf.data(), n, nullptr, &end);
|
|
|
|
EXPECT_EQ(0, err);
|
|
|
|
EXPECT_EQ(reinterpret_cast<char const*>(&buf[n]), end);
|
|
|
|
EXPECT_EQ(size_t{ 3 }, tr_variantListSize(&val));
|
|
|
|
EXPECT_TRUE(tr_variantGetInt(tr_variantListChild(&val, 0), &i));
|
|
|
|
EXPECT_EQ(64, i);
|
|
|
|
EXPECT_TRUE(tr_variantGetInt(tr_variantListChild(&val, 1), &i));
|
|
|
|
EXPECT_EQ(32, i);
|
|
|
|
EXPECT_TRUE(tr_variantGetInt(tr_variantListChild(&val, 2), &i));
|
|
|
|
EXPECT_EQ(16, i);
|
|
|
|
|
|
|
|
size_t len;
|
|
|
|
auto* saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len);
|
|
|
|
EXPECT_EQ(static_cast<size_t>(n), len);
|
|
|
|
EXPECT_STREQ(reinterpret_cast<char const*>(buf.data()), saved);
|
|
|
|
tr_free(saved);
|
|
|
|
|
|
|
|
tr_variantFree(&val);
|
|
|
|
end = nullptr;
|
|
|
|
|
|
|
|
n = tr_snprintf(buf.data(), buf.size(), "lllee");
|
|
|
|
err = tr_variantFromBencFull(&val, buf.data(), n, nullptr, &end);
|
|
|
|
EXPECT_NE(0, err);
|
|
|
|
EXPECT_EQ(nullptr, end);
|
|
|
|
|
|
|
|
end = nullptr;
|
|
|
|
n = tr_snprintf(buf.data(), buf.size(), "le");
|
|
|
|
err = tr_variantFromBencFull(&val, buf.data(), n, nullptr, &end);
|
|
|
|
EXPECT_EQ(0, err);
|
|
|
|
EXPECT_EQ(reinterpret_cast<char const*>(&buf[n]), end);
|
|
|
|
|
|
|
|
saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len);
|
|
|
|
EXPECT_EQ(static_cast<size_t>(n), len);
|
|
|
|
EXPECT_STREQ("le", saved);
|
|
|
|
tr_free(saved);
|
|
|
|
tr_variantFree(&val);
|
|
|
|
}
|
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
TEST_F(VariantTest, bencParseAndReencode)
|
|
|
|
{
|
2020-09-07 21:19:10 +00:00
|
|
|
struct LocalTest
|
2020-08-11 18:11:55 +00:00
|
|
|
{
|
|
|
|
std::string benc;
|
|
|
|
bool is_good;
|
|
|
|
};
|
|
|
|
|
2020-09-07 21:19:10 +00:00
|
|
|
auto const tests = std::array<LocalTest, 9>{
|
|
|
|
LocalTest{ "llleee", true },
|
2020-08-11 18:11:55 +00:00
|
|
|
{ "d3:cow3:moo4:spam4:eggse", true },
|
|
|
|
{ "d4:spaml1:a1:bee", true },
|
|
|
|
{ "d5:greenli1ei2ei3ee4:spamd1:ai123e3:keyi214eee", true },
|
|
|
|
{ "d9:publisher3:bob17:publisher-webpage15:www.example.com18:publisher.location4:homee", true },
|
|
|
|
{ "d8:completei1e8:intervali1800e12:min intervali1800e5:peers0:e", true },
|
|
|
|
{ "d1:ai0e1:be", false }, // odd number of children
|
|
|
|
{ "", false },
|
2021-08-15 09:41:48 +00:00
|
|
|
{ " ", false },
|
2020-08-11 18:11:55 +00:00
|
|
|
};
|
|
|
|
|
2020-09-14 02:41:32 +00:00
|
|
|
for (auto const& test : tests)
|
2020-08-11 18:11:55 +00:00
|
|
|
{
|
|
|
|
tr_variant val;
|
|
|
|
char const* end = nullptr;
|
|
|
|
auto const err = tr_variantFromBencFull(&val, test.benc.data(), test.benc.size(), nullptr, &end);
|
|
|
|
if (!test.is_good)
|
|
|
|
{
|
|
|
|
EXPECT_NE(0, err);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EXPECT_EQ(0, err);
|
|
|
|
EXPECT_EQ(test.benc.data() + test.benc.size(), end);
|
|
|
|
auto saved_len = size_t{};
|
|
|
|
auto* saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &saved_len);
|
|
|
|
EXPECT_EQ(test.benc, std::string(saved, saved_len));
|
|
|
|
tr_free(saved);
|
|
|
|
tr_variantFree(&val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, bencSortWhenSerializing)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
auto const in = std::string{ "lld1:bi32e1:ai64eeee" };
|
|
|
|
auto const expected_out = std::string{ "lld1:ai64e1:bi32eeee" };
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
tr_variant val;
|
|
|
|
char const* end;
|
|
|
|
auto const err = tr_variantFromBencFull(&val, in.data(), in.size(), nullptr, &end);
|
|
|
|
EXPECT_EQ(0, err);
|
|
|
|
EXPECT_EQ(reinterpret_cast<decltype(end)>(in.data() + in.size()), end);
|
|
|
|
|
|
|
|
auto len = size_t{};
|
|
|
|
auto* saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len);
|
|
|
|
EXPECT_EQ(expected_out, std::string(saved, len));
|
|
|
|
tr_free(saved);
|
|
|
|
|
|
|
|
tr_variantFree(&val);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, bencMalformedTooManyEndings)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
auto const in = std::string{ "leee" };
|
|
|
|
auto const expected_out = std::string{ "le" };
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
tr_variant val;
|
|
|
|
char const* end;
|
|
|
|
auto const err = tr_variantFromBencFull(&val, in.data(), in.size(), nullptr, &end);
|
|
|
|
EXPECT_EQ(0, err);
|
|
|
|
EXPECT_EQ(in.data() + expected_out.size(), end);
|
|
|
|
|
|
|
|
auto len = size_t{};
|
|
|
|
auto* saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len);
|
|
|
|
EXPECT_EQ(expected_out, std::string(saved, len));
|
|
|
|
tr_free(saved);
|
|
|
|
|
|
|
|
tr_variantFree(&val);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, bencMalformedNoEnding)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
auto const in = std::string{ "l1:a1:b1:c" };
|
2020-08-11 18:11:55 +00:00
|
|
|
tr_variant val;
|
|
|
|
EXPECT_EQ(EILSEQ, tr_variantFromBenc(&val, in.data(), in.size()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, bencMalformedIncompleteString)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
auto const in = std::string{ "1:" };
|
2020-08-11 18:11:55 +00:00
|
|
|
tr_variant val;
|
|
|
|
EXPECT_EQ(EILSEQ, tr_variantFromBenc(&val, in.data(), in.size()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, bencToJson)
|
|
|
|
{
|
2020-09-07 21:19:10 +00:00
|
|
|
struct LocalTest
|
2020-08-11 18:11:55 +00:00
|
|
|
{
|
|
|
|
std::string benc;
|
|
|
|
std::string expected;
|
|
|
|
};
|
|
|
|
|
2020-09-07 21:19:10 +00:00
|
|
|
auto const tests = std::array<LocalTest, 5>{
|
|
|
|
LocalTest{ "i6e", "6" },
|
2020-08-11 18:11:55 +00:00
|
|
|
{ "d5:helloi1e5:worldi2ee", R"({"hello":1,"world":2})" },
|
|
|
|
{ "d5:helloi1e5:worldi2e3:fooli1ei2ei3eee", R"({"foo":[1,2,3],"hello":1,"world":2})" },
|
|
|
|
{ "d5:helloi1e5:worldi2e3:fooli1ei2ei3ed1:ai0eeee", R"({"foo":[1,2,3,{"a":0}],"hello":1,"world":2})" },
|
|
|
|
{ "d4:argsd6:statusle7:status2lee6:result7:successe", R"({"args":{"status":[],"status2":[]},"result":"success"})" }
|
|
|
|
};
|
|
|
|
|
|
|
|
for (auto const& test : tests)
|
|
|
|
{
|
|
|
|
tr_variant top;
|
|
|
|
tr_variantFromBenc(&top, test.benc.data(), test.benc.size());
|
|
|
|
|
|
|
|
auto len = size_t{};
|
|
|
|
auto* str = tr_variantToStr(&top, TR_VARIANT_FMT_JSON_LEAN, &len);
|
|
|
|
EXPECT_EQ(test.expected, stripWhitespace(std::string(str, len)));
|
|
|
|
tr_free(str);
|
|
|
|
tr_variantFree(&top);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, merge)
|
|
|
|
{
|
2021-10-15 13:28:47 +00:00
|
|
|
auto const i1 = tr_quark_new("i1"sv);
|
|
|
|
auto const i2 = tr_quark_new("i2"sv);
|
|
|
|
auto const i3 = tr_quark_new("i3"sv);
|
|
|
|
auto const i4 = tr_quark_new("i4"sv);
|
|
|
|
auto const s5 = tr_quark_new("s5"sv);
|
|
|
|
auto const s6 = tr_quark_new("s6"sv);
|
|
|
|
auto const s7 = tr_quark_new("s7"sv);
|
|
|
|
auto const s8 = tr_quark_new("s8"sv);
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
/* initial dictionary (default values) */
|
|
|
|
tr_variant dest;
|
|
|
|
tr_variantInitDict(&dest, 10);
|
|
|
|
tr_variantDictAddInt(&dest, i1, 1);
|
|
|
|
tr_variantDictAddInt(&dest, i2, 2);
|
|
|
|
tr_variantDictAddInt(&dest, i4, -35); /* remains untouched */
|
|
|
|
tr_variantDictAddStr(&dest, s5, "abc");
|
|
|
|
tr_variantDictAddStr(&dest, s6, "def");
|
|
|
|
tr_variantDictAddStr(&dest, s7, "127.0.0.1"); /* remains untouched */
|
|
|
|
|
|
|
|
/* new dictionary, will overwrite items in dest */
|
|
|
|
tr_variant src;
|
|
|
|
tr_variantInitDict(&src, 10);
|
|
|
|
tr_variantDictAddInt(&src, i1, 1); /* same value */
|
|
|
|
tr_variantDictAddInt(&src, i2, 4); /* new value */
|
|
|
|
tr_variantDictAddInt(&src, i3, 3); /* new key:value */
|
|
|
|
tr_variantDictAddStr(&src, s5, "abc"); /* same value */
|
|
|
|
tr_variantDictAddStr(&src, s6, "xyz"); /* new value */
|
|
|
|
tr_variantDictAddStr(&src, s8, "ghi"); /* new key:value */
|
|
|
|
|
|
|
|
tr_variantMergeDicts(&dest, /*const*/ &src);
|
|
|
|
|
|
|
|
int64_t i;
|
|
|
|
EXPECT_TRUE(tr_variantDictFindInt(&dest, i1, &i));
|
|
|
|
EXPECT_EQ(1, i);
|
|
|
|
EXPECT_TRUE(tr_variantDictFindInt(&dest, i2, &i));
|
|
|
|
EXPECT_EQ(4, i);
|
|
|
|
EXPECT_TRUE(tr_variantDictFindInt(&dest, i3, &i));
|
|
|
|
EXPECT_EQ(3, i);
|
|
|
|
EXPECT_TRUE(tr_variantDictFindInt(&dest, i4, &i));
|
|
|
|
EXPECT_EQ(-35, i);
|
|
|
|
size_t len;
|
|
|
|
char const* s;
|
|
|
|
EXPECT_TRUE(tr_variantDictFindStr(&dest, s5, &s, &len));
|
|
|
|
EXPECT_EQ(size_t{ 3 }, len);
|
|
|
|
EXPECT_STREQ("abc", s);
|
|
|
|
EXPECT_TRUE(tr_variantDictFindStr(&dest, s6, &s, &len));
|
|
|
|
EXPECT_EQ(size_t{ 3 }, len);
|
|
|
|
EXPECT_STREQ("xyz", s);
|
|
|
|
EXPECT_TRUE(tr_variantDictFindStr(&dest, s7, &s, &len));
|
|
|
|
EXPECT_EQ(size_t{ 9 }, len);
|
|
|
|
EXPECT_STREQ("127.0.0.1", s);
|
|
|
|
EXPECT_TRUE(tr_variantDictFindStr(&dest, s8, &s, &len));
|
|
|
|
EXPECT_EQ(size_t{ 3 }, len);
|
|
|
|
EXPECT_STREQ("ghi", s);
|
|
|
|
|
|
|
|
tr_variantFree(&dest);
|
|
|
|
tr_variantFree(&src);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, stackSmash)
|
|
|
|
{
|
|
|
|
// make a nested list of list of lists.
|
|
|
|
int constexpr Depth = STACK_SMASH_DEPTH;
|
|
|
|
std::string const in = std::string(Depth, 'l') + std::string(Depth, 'e');
|
|
|
|
|
|
|
|
// confirm that it parses
|
|
|
|
char const* end;
|
|
|
|
tr_variant val;
|
|
|
|
auto err = tr_variantFromBencFull(&val, in.data(), in.size(), nullptr, &end);
|
|
|
|
EXPECT_EQ(0, err);
|
|
|
|
EXPECT_EQ(in.data() + in.size(), end);
|
|
|
|
|
|
|
|
// confirm that we can serialize it back again
|
|
|
|
size_t len;
|
|
|
|
auto* saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len);
|
2021-08-16 01:45:50 +00:00
|
|
|
EXPECT_NE(nullptr, saved);
|
2020-08-11 18:11:55 +00:00
|
|
|
EXPECT_EQ(in, std::string(saved, len));
|
|
|
|
tr_free(saved);
|
|
|
|
|
|
|
|
tr_variantFree(&val);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, boolAndIntRecast)
|
|
|
|
{
|
2021-10-15 13:28:47 +00:00
|
|
|
auto const key1 = tr_quark_new("key1"sv);
|
|
|
|
auto const key2 = tr_quark_new("key2"sv);
|
|
|
|
auto const key3 = tr_quark_new("key3"sv);
|
|
|
|
auto const key4 = tr_quark_new("key4"sv);
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
tr_variant top;
|
|
|
|
tr_variantInitDict(&top, 10);
|
|
|
|
tr_variantDictAddBool(&top, key1, false);
|
|
|
|
tr_variantDictAddBool(&top, key2, 0); // NOLINT modernize-use-bool-literals
|
|
|
|
tr_variantDictAddInt(&top, key3, true);
|
|
|
|
tr_variantDictAddInt(&top, key4, 1);
|
|
|
|
|
|
|
|
// confirm we can read both bools and ints as bools
|
|
|
|
bool b;
|
|
|
|
EXPECT_TRUE(tr_variantDictFindBool(&top, key1, &b));
|
|
|
|
EXPECT_FALSE(b);
|
|
|
|
EXPECT_TRUE(tr_variantDictFindBool(&top, key2, &b));
|
|
|
|
EXPECT_FALSE(b);
|
|
|
|
EXPECT_TRUE(tr_variantDictFindBool(&top, key3, &b));
|
|
|
|
EXPECT_TRUE(b);
|
|
|
|
EXPECT_TRUE(tr_variantDictFindBool(&top, key4, &b));
|
|
|
|
EXPECT_TRUE(b);
|
|
|
|
|
|
|
|
// confirm we can read both bools and ints as ints
|
|
|
|
int64_t i;
|
|
|
|
EXPECT_TRUE(tr_variantDictFindInt(&top, key1, &i));
|
|
|
|
EXPECT_EQ(0, i);
|
|
|
|
EXPECT_TRUE(tr_variantDictFindInt(&top, key2, &i));
|
|
|
|
EXPECT_EQ(0, i);
|
|
|
|
EXPECT_TRUE(tr_variantDictFindInt(&top, key3, &i));
|
|
|
|
EXPECT_NE(0, i);
|
|
|
|
EXPECT_TRUE(tr_variantDictFindInt(&top, key4, &i));
|
|
|
|
EXPECT_NE(0, i);
|
|
|
|
|
|
|
|
tr_variantFree(&top);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VariantTest, dictFindType)
|
|
|
|
{
|
2021-11-01 21:30:18 +00:00
|
|
|
auto constexpr ExpectedStr = "this-is-a-string"sv;
|
|
|
|
auto constexpr ExpectedBool = bool{ true };
|
|
|
|
auto constexpr ExpectedInt = int{ 1234 };
|
|
|
|
auto constexpr ExpectedReal = double{ 0.3 };
|
2020-08-11 18:11:55 +00:00
|
|
|
|
2021-10-15 13:28:47 +00:00
|
|
|
auto const key_bool = tr_quark_new("this-is-a-bool"sv);
|
|
|
|
auto const key_real = tr_quark_new("this-is-a-real"sv);
|
|
|
|
auto const key_int = tr_quark_new("this-is-an-int"sv);
|
|
|
|
auto const key_str = tr_quark_new("this-is-a-string"sv);
|
2021-11-01 21:30:18 +00:00
|
|
|
auto const key_unknown = tr_quark_new("this-is-a-missing-entry"sv);
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
// populate a dict
|
|
|
|
tr_variant top;
|
|
|
|
tr_variantInitDict(&top, 0);
|
2021-11-01 21:30:18 +00:00
|
|
|
tr_variantDictAddBool(&top, key_bool, ExpectedBool);
|
|
|
|
tr_variantDictAddInt(&top, key_int, ExpectedInt);
|
|
|
|
tr_variantDictAddReal(&top, key_real, ExpectedReal);
|
|
|
|
tr_variantDictAddStr(&top, key_str, ExpectedStr.data());
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
// look up the keys as strings
|
|
|
|
char const* str = {};
|
|
|
|
auto len = size_t{};
|
2021-11-01 21:30:18 +00:00
|
|
|
auto sv = std::string_view{};
|
2020-08-11 18:11:55 +00:00
|
|
|
EXPECT_FALSE(tr_variantDictFindStr(&top, key_bool, &str, &len));
|
|
|
|
EXPECT_FALSE(tr_variantDictFindStr(&top, key_real, &str, &len));
|
|
|
|
EXPECT_FALSE(tr_variantDictFindStr(&top, key_int, &str, &len));
|
|
|
|
EXPECT_TRUE(tr_variantDictFindStr(&top, key_str, &str, &len));
|
2021-11-01 21:30:18 +00:00
|
|
|
EXPECT_EQ(ExpectedStr, std::string(str, len));
|
|
|
|
EXPECT_TRUE(tr_variantDictFindStrView(&top, key_str, &sv));
|
|
|
|
EXPECT_EQ(ExpectedStr, sv);
|
|
|
|
EXPECT_FALSE(tr_variantDictFindStrView(&top, key_unknown, &sv));
|
|
|
|
EXPECT_FALSE(tr_variantDictFindStr(&top, key_unknown, &str, &len));
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
// look up the keys as bools
|
|
|
|
auto b = bool{};
|
|
|
|
EXPECT_FALSE(tr_variantDictFindBool(&top, key_int, &b));
|
|
|
|
EXPECT_FALSE(tr_variantDictFindBool(&top, key_real, &b));
|
|
|
|
EXPECT_FALSE(tr_variantDictFindBool(&top, key_str, &b));
|
|
|
|
EXPECT_TRUE(tr_variantDictFindBool(&top, key_bool, &b));
|
2021-11-01 21:30:18 +00:00
|
|
|
EXPECT_EQ(ExpectedBool, b);
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
// look up the keys as doubles
|
|
|
|
auto d = double{};
|
|
|
|
EXPECT_FALSE(tr_variantDictFindReal(&top, key_bool, &d));
|
|
|
|
EXPECT_TRUE(tr_variantDictFindReal(&top, key_int, &d));
|
2021-11-01 21:30:18 +00:00
|
|
|
EXPECT_EQ(ExpectedInt, std::lrint(d));
|
2020-08-11 18:11:55 +00:00
|
|
|
EXPECT_FALSE(tr_variantDictFindReal(&top, key_str, &d));
|
|
|
|
EXPECT_TRUE(tr_variantDictFindReal(&top, key_real, &d));
|
2021-11-01 21:30:18 +00:00
|
|
|
EXPECT_EQ(std::lrint(ExpectedReal * 100), std::lrint(d * 100));
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
// look up the keys as ints
|
2021-08-15 09:41:48 +00:00
|
|
|
auto i = int64_t{};
|
2020-08-11 18:11:55 +00:00
|
|
|
EXPECT_TRUE(tr_variantDictFindInt(&top, key_bool, &i));
|
2021-11-01 21:30:18 +00:00
|
|
|
EXPECT_EQ(ExpectedBool ? 1 : 0, i);
|
2020-08-11 18:11:55 +00:00
|
|
|
EXPECT_FALSE(tr_variantDictFindInt(&top, key_real, &i));
|
|
|
|
EXPECT_FALSE(tr_variantDictFindInt(&top, key_str, &i));
|
|
|
|
EXPECT_TRUE(tr_variantDictFindInt(&top, key_int, &i));
|
2021-11-01 21:30:18 +00:00
|
|
|
EXPECT_EQ(ExpectedInt, i);
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
tr_variantFree(&top);
|
|
|
|
}
|