1313 lines
34 KiB
C++
1313 lines
34 KiB
C++
/*
|
|
|
|
Copyright (c) 2015, Arvid Norberg
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in
|
|
the documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the author nor the names of its
|
|
contributors may be used to endorse or promote products derived
|
|
from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
#include "test.hpp"
|
|
#include "libtorrent/bdecode.hpp"
|
|
#include "libtorrent/entry.hpp"
|
|
|
|
using namespace lt;
|
|
|
|
// test integer
|
|
TORRENT_TEST(integer)
|
|
{
|
|
char b[] = "i12453e";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(!ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
span<const char> section = e.data_section();
|
|
TEST_CHECK(std::memcmp(b, section.data(), section.size()) == 0);
|
|
TEST_EQUAL(section.size(), sizeof(b) - 1);
|
|
TEST_EQUAL(e.type(), bdecode_node::int_t);
|
|
TEST_EQUAL(e.int_value(), 12453);
|
|
}
|
|
|
|
TORRENT_TEST(construct_string)
|
|
{
|
|
entry e(std::string("abc123"));
|
|
TEST_EQUAL(e.string(), "abc123");
|
|
}
|
|
|
|
TORRENT_TEST(construct_string_literal)
|
|
{
|
|
entry e("abc123");
|
|
TEST_EQUAL(e.string(), "abc123");
|
|
}
|
|
|
|
|
|
TORRENT_TEST(construct_string_view)
|
|
{
|
|
entry e(string_view("abc123"));
|
|
TEST_EQUAL(e.string(), "abc123");
|
|
}
|
|
|
|
TORRENT_TEST(construct_integer)
|
|
{
|
|
entry e(4);
|
|
TEST_EQUAL(e.integer(), 4);
|
|
}
|
|
|
|
// test string
|
|
TORRENT_TEST(string)
|
|
{
|
|
char b[] = "26:abcdefghijklmnopqrstuvwxyz";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(!ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
span<const char> section = e.data_section();
|
|
TEST_CHECK(std::memcmp(b, section.data(), section.size()) == 0);
|
|
TEST_EQUAL(section.size(), sizeof(b) - 1);
|
|
TEST_EQUAL(e.type(), bdecode_node::string_t);
|
|
TEST_EQUAL(e.string_value(), std::string("abcdefghijklmnopqrstuvwxyz"));
|
|
TEST_EQUAL(e.string_length(), 26);
|
|
}
|
|
|
|
// test string-prefix
|
|
TORRENT_TEST(string_prefix1)
|
|
{
|
|
// test edge-case of a string that's nearly too long
|
|
std::string test;
|
|
test.resize(1000000 + 8);
|
|
memcpy(&test[0], "1000000:", 8);
|
|
// test is a valid bencoded string, that's quite long
|
|
error_code ec;
|
|
bdecode_node e = bdecode(test, ec);
|
|
TEST_CHECK(!ec);
|
|
std::printf("%d bytes string\n", e.string_length());
|
|
span<const char> section = e.data_section();
|
|
TEST_CHECK(std::memcmp(test.c_str(), section.data(), section.size()) == 0);
|
|
TEST_EQUAL(section.size(), test.size());
|
|
TEST_EQUAL(e.type(), bdecode_node::string_t);
|
|
TEST_EQUAL(e.string_length(), 1000000);
|
|
TEST_EQUAL(e.string_ptr(), test.c_str() + 8);
|
|
}
|
|
|
|
// test list
|
|
TORRENT_TEST(list)
|
|
{
|
|
char b[] = "li12453e3:aaae";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(!ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
span<const char> section = e.data_section();
|
|
TEST_CHECK(std::memcmp(b, section.data(), section.size()) == 0);
|
|
TEST_EQUAL(section.size(), sizeof(b) - 1);
|
|
TEST_EQUAL(e.type(), bdecode_node::list_t);
|
|
TEST_EQUAL(e.list_size(), 2);
|
|
TEST_EQUAL(e.list_at(0).type(), bdecode_node::int_t);
|
|
TEST_EQUAL(e.list_at(1).type(), bdecode_node::string_t);
|
|
TEST_EQUAL(e.list_at(0).int_value(), 12453);
|
|
TEST_EQUAL(e.list_at(1).string_value(), std::string("aaa"));
|
|
TEST_EQUAL(e.list_at(1).string_length(), 3);
|
|
section = e.list_at(1).data_section();
|
|
TEST_CHECK(std::memcmp("3:aaa", section.data(), section.size()) == 0);
|
|
TEST_EQUAL(section.size(), 5);
|
|
}
|
|
|
|
// test dict
|
|
TORRENT_TEST(dict)
|
|
{
|
|
char b[] = "d1:ai12453e1:b3:aaa1:c3:bbb1:X10:0123456789e";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(!ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
span<const char> section = e.data_section();
|
|
TEST_CHECK(std::memcmp(b, section.data(), section.size()) == 0);
|
|
TEST_EQUAL(section.size(), sizeof(b) - 1);
|
|
TEST_EQUAL(e.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e.dict_size(), 4);
|
|
TEST_EQUAL(e.dict_find("a").type(), bdecode_node::int_t);
|
|
TEST_EQUAL(e.dict_find("a").int_value(), 12453);
|
|
TEST_EQUAL(e.dict_find("b").type(), bdecode_node::string_t);
|
|
TEST_EQUAL(e.dict_find("b").string_value(), std::string("aaa"));
|
|
TEST_EQUAL(e.dict_find("b").string_length(), 3);
|
|
TEST_EQUAL(e.dict_find("c").type(), bdecode_node::string_t);
|
|
TEST_EQUAL(e.dict_find("c").string_value(), std::string("bbb"));
|
|
TEST_EQUAL(e.dict_find("c").string_length(), 3);
|
|
TEST_EQUAL(e.dict_find_string_value("X"), "0123456789");
|
|
char error_string[200];
|
|
TEST_CHECK(e.has_soft_error(error_string));
|
|
TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key"));
|
|
}
|
|
|
|
// test dictionary with a key without a value
|
|
TORRENT_TEST(dict_key_novalue)
|
|
{
|
|
char b[] = "d1:ai1e1:be";
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 10);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::expected_value));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test dictionary with a key that's not a string
|
|
TORRENT_TEST(dict_nonstring_key)
|
|
{
|
|
char b[] = "di5e1:ae";
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 1);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// dictionary key with \0
|
|
TORRENT_TEST(dict_null_key)
|
|
{
|
|
char b[] = "d3:a\0bi1ee";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(e.dict_size() == 1);
|
|
bdecode_node d = e.dict_find(std::string("a\0b", 3));
|
|
TEST_EQUAL(d.type(), bdecode_node::int_t);
|
|
TEST_EQUAL(d.int_value(), 1);
|
|
}
|
|
|
|
// soft error reported for dictionary with unordered keys
|
|
TORRENT_TEST(dict_unordered_keys)
|
|
{
|
|
char error_string[200];
|
|
{
|
|
char b[] = "d2:abi1e2:aai2ee";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(e.has_soft_error(error_string));
|
|
TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key"));
|
|
}
|
|
{
|
|
char b[] = "d2:bai1e2:aai2ee";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(e.has_soft_error(error_string));
|
|
TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key"));
|
|
}
|
|
{
|
|
char b[] = "d2:aai1e1:ai2ee";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(e.has_soft_error(error_string));
|
|
TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key"));
|
|
}
|
|
{
|
|
char b[] = "d1:ai1e2:aai2ee";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(!e.has_soft_error(error_string));
|
|
TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key"));
|
|
}
|
|
{
|
|
char b[] = "d2:aai1e1:bi2ee";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(!e.has_soft_error(error_string));
|
|
TEST_EQUAL(std::string(error_string), std::string("unsorted dictionary key"));
|
|
}
|
|
}
|
|
|
|
TORRENT_TEST(dict_duplicate_key)
|
|
{
|
|
char b[] = "d2:aai1e2:aai2ee";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
char error_string[200];
|
|
TEST_CHECK(e.has_soft_error(error_string));
|
|
TEST_EQUAL(std::string(error_string), std::string("duplicate dictionary key"));
|
|
}
|
|
|
|
// premature e
|
|
TORRENT_TEST(premature_e)
|
|
{
|
|
char b[] = "e";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test strings with negative length-prefix
|
|
TORRENT_TEST(negative_length_prefix)
|
|
{
|
|
char b[] = "-10:foobar";
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 0);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::expected_value));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test strings with overflow length-prefix
|
|
TORRENT_TEST(overflow_length_prefix)
|
|
{
|
|
char b[] = "18446744073709551615:foobar";
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 19);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::overflow));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test strings with almost overflow (more than 8 digits)
|
|
TORRENT_TEST(close_overflow_length_prefix)
|
|
{
|
|
char b[] = "99999999:foobar";
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 8);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test strings with overflow (more than 8 digits)
|
|
TORRENT_TEST(overflow_length_prefix2)
|
|
{
|
|
char b[] = "199999999:foobar";
|
|
error_code ec;
|
|
int pos;
|
|
// pretend that we have a large buffer like that
|
|
bdecode_node e = bdecode({b, 999999999}, ec, &pos);
|
|
TEST_EQUAL(pos, 0);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
TORRENT_TEST(leading_zero_length_prefix)
|
|
{
|
|
{
|
|
char b[] = "06:foobar";
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
char error_string[200];
|
|
TEST_CHECK(e.has_soft_error(error_string));
|
|
TEST_EQUAL(std::string(error_string), std::string("leading zero in string length"));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
{
|
|
char b[] = "0:";
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
char error_string[200];
|
|
TEST_CHECK(!e.has_soft_error(error_string));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
}
|
|
|
|
// test integer without any digits
|
|
TORRENT_TEST(nodigit_int)
|
|
{
|
|
char b[] = "ie";
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 1);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test integer with just a minus
|
|
TORRENT_TEST(minus_int)
|
|
{
|
|
char b[] = "i-e";
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 2);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test integer with a minus inserted in it
|
|
TORRENT_TEST(interior_minus_int)
|
|
{
|
|
char b[] = "i35412-5633e";
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 6);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test integers that don't fit in 64 bits
|
|
TORRENT_TEST(int_overflow)
|
|
{
|
|
char b[] = "i18446744073709551615e";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
// the lazy aspect makes this overflow when asking for
|
|
// the value. turning it to zero.
|
|
TEST_EQUAL(e.int_value(), 0);
|
|
}
|
|
|
|
// test integers with more than 20 digits (overflow on parsing)
|
|
TORRENT_TEST(int_overflow2)
|
|
{
|
|
char b[] = "i184467440737095516154e";
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 22);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::overflow));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test truncated negative integer
|
|
TORRENT_TEST(int_truncated)
|
|
{
|
|
char b[] = "i-";
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode({b, 2}, ec, &pos);
|
|
TEST_EQUAL(pos, 2);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
TORRENT_TEST(int_leading_zero)
|
|
{
|
|
{
|
|
char b[] = "i01e";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
char error_string[200];
|
|
TEST_CHECK(e.has_soft_error(error_string));
|
|
TEST_EQUAL(std::string(error_string), std::string("leading zero in integer"));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
{
|
|
char b[] = "i0e";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
char error_string[200];
|
|
TEST_CHECK(!e.has_soft_error(error_string));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
}
|
|
|
|
// bdecode_error
|
|
TORRENT_TEST(bdecode_error)
|
|
{
|
|
error_code ec(bdecode_errors::overflow);
|
|
TEST_EQUAL(ec.message(), "integer overflow");
|
|
TEST_EQUAL(ec.category().name(), std::string("bdecode error"));
|
|
ec.assign(5434, bdecode_category());
|
|
TEST_EQUAL(ec.message(), "Unknown error");
|
|
}
|
|
|
|
// test integers that just exactly fit in 64 bits
|
|
TORRENT_TEST(64bit_int)
|
|
{
|
|
char b[] = "i9223372036854775807e";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
TEST_CHECK(e.int_value() == 9223372036854775807LL);
|
|
}
|
|
|
|
// test integers that just exactly fit in 64 bits
|
|
TORRENT_TEST(64bit_int_negative)
|
|
{
|
|
char b[] = "i-9223372036854775807e";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
TEST_CHECK(e.int_value() == -9223372036854775807LL);
|
|
}
|
|
|
|
// test integers that have invalid digits
|
|
TORRENT_TEST(int_invalid_digit)
|
|
{
|
|
char b[] = "i92337203t854775807e";
|
|
error_code ec;
|
|
int pos = 0;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 9);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test invalid encoding
|
|
TORRENT_TEST(invalid_encoding)
|
|
{
|
|
unsigned char buf[] =
|
|
{ 0x64, 0x31, 0x3a, 0x61, 0x64, 0x32, 0x3a, 0x69
|
|
, 0x64, 0x32, 0x30, 0x3a, 0x2a, 0x21, 0x19, 0x89
|
|
, 0x9f, 0xcd, 0x5f, 0xc9, 0xbc, 0x80, 0xc1, 0x76
|
|
, 0xfe, 0xe0, 0xc6, 0x84, 0x2d, 0xf6, 0xfc, 0xb8
|
|
, 0x39, 0x3a, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x68
|
|
, 0x61, 0xae, 0x68, 0x32, 0x30, 0x3a, 0x14, 0x78
|
|
, 0xd5, 0xb0, 0xdc, 0xf6, 0x82, 0x42, 0x32, 0xa0
|
|
, 0xd6, 0x88, 0xeb, 0x48, 0x57, 0x01, 0x89, 0x40
|
|
, 0x4e, 0xbc, 0x65, 0x31, 0x3a, 0x71, 0x39, 0x3a
|
|
, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x65, 0x65, 0x72
|
|
, 0x78, 0xff, 0x3a, 0x74, 0x38, 0x3a, 0xaa, 0xd4
|
|
, 0xa1, 0x88, 0x7a, 0x8d, 0xc3, 0xd6, 0x31, 0x3a
|
|
, 0x79, 0x31, 0xae, 0x71, 0x65, 0};
|
|
|
|
std::printf("%s\n", buf);
|
|
error_code ec;
|
|
bdecode_node e = bdecode({reinterpret_cast<char const*>(buf), sizeof(buf)}, ec);
|
|
TEST_CHECK(ec);
|
|
}
|
|
|
|
// test the depth limit
|
|
TORRENT_TEST(depth_limit)
|
|
{
|
|
char b[2048];
|
|
for (int i = 0; i < 1024; ++i)
|
|
b[i]= 'l';
|
|
|
|
for (int i = 1024; i < 2048; ++i)
|
|
b[i]= 'e';
|
|
|
|
// 1024 levels nested lists
|
|
|
|
error_code ec;
|
|
bdecode_node e = bdecode({b, sizeof(b)}, ec, nullptr, 100);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::depth_exceeded));
|
|
}
|
|
|
|
// test the item limit
|
|
TORRENT_TEST(item_limit)
|
|
{
|
|
char b[10240];
|
|
b[0] = 'l';
|
|
std::size_t i = 1;
|
|
for (i = 1; i < 10239; i += 2)
|
|
memcpy(&b[i], "0:", 2);
|
|
b[i] = 'e';
|
|
|
|
error_code ec;
|
|
bdecode_node e = bdecode({b, i + 1}, ec, nullptr, 1000, 1000);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded));
|
|
}
|
|
|
|
// test unexpected EOF
|
|
TORRENT_TEST(unepected_eof)
|
|
{
|
|
char b[] = "l2:.."; // expected terminating 'e'
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode({b, 5}, ec, &pos);
|
|
TEST_EQUAL(pos, 5);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test unexpected EOF in string length
|
|
TORRENT_TEST(unepected_eof2)
|
|
{
|
|
char b[] = "l2:..0"; // expected ':' delimiter instead of EOF
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode({b, 6}, ec, &pos);
|
|
TEST_EQUAL(pos, 6);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test expected string
|
|
TORRENT_TEST(expected_string)
|
|
{
|
|
char b[] = "di2ei0ee";
|
|
// expected string (dict keys must be strings)
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 1);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test unexpected EOF while parsing dict key
|
|
TORRENT_TEST(unexpected_eof_dict_key)
|
|
{
|
|
char b[] = "d1000:..e";
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 5);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test unexpected EOF while parsing dict key
|
|
TORRENT_TEST(unexpected_eof_dict_key2)
|
|
{
|
|
char b[] = "d1000:";
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 5);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test expected string while parsing dict key
|
|
TORRENT_TEST(expected_string_dict_key2)
|
|
{
|
|
char b[] = "df00:";
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 1);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::expected_digit));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test unexpected EOF while parsing int
|
|
TORRENT_TEST(unexpected_eof_int)
|
|
{
|
|
char b[] = "i";
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode({b, 1}, ec, &pos);
|
|
TEST_EQUAL(pos, 1);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test unexpected EOF while parsing int
|
|
TORRENT_TEST(unexpected_eof_int2)
|
|
{
|
|
char b[] = "i10";
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode({b, 3}, ec, &pos);
|
|
TEST_EQUAL(pos, 3);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
|
|
// test expected colon
|
|
TORRENT_TEST(expected_colon_dict)
|
|
{
|
|
char b[] = "d1000";
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode({b, 5}, ec, &pos);
|
|
TEST_EQUAL(pos, 5);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::expected_colon));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test empty string
|
|
TORRENT_TEST(empty_string)
|
|
{
|
|
error_code ec;
|
|
bdecode_node e = bdecode({}, ec, nullptr);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test partial string
|
|
TORRENT_TEST(partial_string)
|
|
{
|
|
char b[] = "100:..";
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 3);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
TORRENT_TEST(list_ints)
|
|
{
|
|
std::string buf;
|
|
buf += "l";
|
|
for (int i = 0; i < 1000; ++i)
|
|
{
|
|
char tmp[20];
|
|
std::snprintf(tmp, sizeof(tmp), "i%de", i);
|
|
buf += tmp;
|
|
}
|
|
buf += "e";
|
|
|
|
error_code ec;
|
|
bdecode_node e = bdecode(buf, ec);
|
|
TEST_CHECK(!ec);
|
|
TEST_EQUAL(e.type(), bdecode_node::list_t);
|
|
TEST_EQUAL(e.list_size(), 1000);
|
|
for (int i = 0; i < 1000; ++i)
|
|
{
|
|
TEST_EQUAL(e.list_int_value_at(i), i);
|
|
}
|
|
}
|
|
|
|
TORRENT_TEST(dict_ints)
|
|
{
|
|
std::string buf;
|
|
buf += "d";
|
|
for (int i = 0; i < 1000; ++i)
|
|
{
|
|
char tmp[30];
|
|
std::snprintf(tmp, sizeof(tmp), "4:%04di%de", i, i);
|
|
buf += tmp;
|
|
}
|
|
buf += "e";
|
|
|
|
std::printf("%s\n", buf.c_str());
|
|
error_code ec;
|
|
bdecode_node e = bdecode(buf, ec);
|
|
TEST_CHECK(!ec);
|
|
TEST_EQUAL(e.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e.dict_size(), 1000);
|
|
for (int i = 0; i < 1000; ++i)
|
|
{
|
|
char tmp[30];
|
|
std::snprintf(tmp, sizeof(tmp), "%04d", i);
|
|
TEST_EQUAL(e.dict_find_int_value(tmp), i);
|
|
}
|
|
}
|
|
|
|
// test dict_at
|
|
TORRENT_TEST(dict_at)
|
|
{
|
|
char b[] = "d3:fooi1e3:bari2ee";
|
|
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(!ec);
|
|
|
|
TEST_EQUAL(e.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e.dict_size(), 2);
|
|
TEST_EQUAL(e.dict_at(0).first, "foo");
|
|
TEST_EQUAL(e.dict_at(0).second.type(), bdecode_node::int_t);
|
|
TEST_EQUAL(e.dict_at(0).second.int_value(), 1);
|
|
TEST_EQUAL(e.dict_at(1).first, "bar");
|
|
TEST_EQUAL(e.dict_at(1).second.type(), bdecode_node::int_t);
|
|
TEST_EQUAL(e.dict_at(1).second.int_value(), 2);
|
|
}
|
|
|
|
// test string_ptr
|
|
TORRENT_TEST(string_ptr)
|
|
{
|
|
char b[] = "l3:fooe";
|
|
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(!ec);
|
|
|
|
TEST_EQUAL(e.type(), bdecode_node::list_t);
|
|
TEST_EQUAL(e.list_size(), 1);
|
|
TEST_EQUAL(e.list_at(0).type(), bdecode_node::string_t);
|
|
TEST_EQUAL(e.list_at(0).string_ptr(), b + 3);
|
|
TEST_EQUAL(e.list_at(0).string_length(), 3);
|
|
}
|
|
|
|
// test exceeding buffer size limit
|
|
TORRENT_TEST(exceed_buf_limit)
|
|
{
|
|
char b[] = "l3:fooe";
|
|
|
|
error_code ec;
|
|
bdecode_node e = bdecode({b, 0x3fffffff}, ec);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::limit_exceeded));
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
}
|
|
|
|
// test parse_int
|
|
TORRENT_TEST(parse_int)
|
|
{
|
|
char b[] = "1234567890e";
|
|
std::int64_t val = 0;
|
|
bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
|
|
char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec);
|
|
TEST_EQUAL(ec, bdecode_errors::no_error);
|
|
TEST_EQUAL(val, 1234567890);
|
|
TEST_EQUAL(e, b + sizeof(b) - 2);
|
|
}
|
|
|
|
// test invalid digit
|
|
TORRENT_TEST(invalid_digit)
|
|
{
|
|
char b[] = "0o";
|
|
std::int64_t val = 0;
|
|
bdecode_errors::error_code_enum ec;
|
|
char const* e = parse_int(b, b + sizeof(b)-1, 'e', val, ec);
|
|
TEST_EQUAL(ec, bdecode_errors::expected_digit);
|
|
TEST_EQUAL(e, b + 1);
|
|
}
|
|
|
|
// test parse_int overflow
|
|
TORRENT_TEST(parse_int_overflow)
|
|
{
|
|
char b[] = "9223372036854775808:";
|
|
std::int64_t val = 0;
|
|
bdecode_errors::error_code_enum ec;
|
|
char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec);
|
|
TEST_EQUAL(ec, bdecode_errors::overflow);
|
|
TEST_EQUAL(e, b + 18);
|
|
}
|
|
|
|
TORRENT_TEST(parse_length_overflow)
|
|
{
|
|
char const* b[] = {
|
|
"d1:a1919191010:11111",
|
|
"d2143289344:a4:aaaae",
|
|
"d214328934114:a4:aaaae",
|
|
"d9205357638345293824:a4:aaaae",
|
|
"d1:a9205357638345293824:11111",
|
|
};
|
|
|
|
for (int i = 0; i < int(sizeof(b)/sizeof(b[0])); ++i)
|
|
{
|
|
error_code ec;
|
|
bdecode_node e = bdecode({b[i], strlen(b[i])}, ec);
|
|
TEST_EQUAL(ec, error_code(bdecode_errors::unexpected_eof));
|
|
}
|
|
}
|
|
|
|
|
|
TORRENT_TEST(expected_colon_string)
|
|
{
|
|
char b[] = "928";
|
|
std::int64_t val = 0;
|
|
bdecode_errors::error_code_enum ec = bdecode_errors::no_error;
|
|
char const* e = parse_int(b, b + sizeof(b)-1, ':', val, ec);
|
|
TEST_EQUAL(ec, bdecode_errors::no_error);
|
|
TEST_EQUAL(e, b + 3);
|
|
}
|
|
|
|
// test dict_find_* functions
|
|
TORRENT_TEST(dict_find_funs)
|
|
{
|
|
// a: int
|
|
// b: string
|
|
// c: list
|
|
// d: dict
|
|
char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(!ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
|
|
TEST_EQUAL(e.type(), bdecode_node::dict_t);
|
|
|
|
// dict_find_int*
|
|
|
|
TEST_EQUAL(e.dict_find_int_value("a"), 1);
|
|
TEST_EQUAL(e.dict_find_int("a").type(), bdecode_node::int_t);
|
|
TEST_EQUAL(e.dict_find_int_value("b", -10), -10);
|
|
TEST_EQUAL(e.dict_find_int_value("x", -10), -10);
|
|
TEST_EQUAL(e.dict_find_int("b").type(), bdecode_node::none_t);
|
|
TEST_EQUAL(e.dict_find_int("x").type(), bdecode_node::none_t);
|
|
|
|
// dict_find_string*
|
|
|
|
TEST_EQUAL(e.dict_find_string_value("b"), "foo");
|
|
TEST_EQUAL(e.dict_find_string("b").type(), bdecode_node::string_t);
|
|
TEST_EQUAL(e.dict_find_string_value("c", "blah"), "blah");
|
|
TEST_EQUAL(e.dict_find_string_value("x", "blah"), "blah");
|
|
TEST_EQUAL(e.dict_find_string("c").type(), bdecode_node::none_t);
|
|
TEST_EQUAL(e.dict_find_string("x").type(), bdecode_node::none_t);
|
|
|
|
// dict_find_list
|
|
|
|
TEST_CHECK(e.dict_find_list("c"));
|
|
TEST_EQUAL(e.dict_find_list("c").list_size(), 2);
|
|
TEST_EQUAL(e.dict_find_list("c").list_int_value_at(0), 1);
|
|
TEST_EQUAL(e.dict_find_list("c").list_int_value_at(1), 2);
|
|
TEST_CHECK(!e.dict_find_list("d"));
|
|
|
|
// dict_find_dict
|
|
|
|
TEST_CHECK(e.dict_find_dict("d"));
|
|
TEST_EQUAL(e.dict_find_dict("d").dict_find_int_value("x"), 1);
|
|
TEST_EQUAL(e.dict_find_dict("d").dict_find_int_value("y", -10), -10);
|
|
TEST_CHECK(!e.dict_find_dict("c"));
|
|
|
|
// variants taking std::string
|
|
TEST_EQUAL(e.dict_find_dict(std::string("d")).dict_find_int_value("x"), 1);
|
|
TEST_CHECK(!e.dict_find_dict(std::string("c")));
|
|
TEST_CHECK(!e.dict_find_dict(std::string("x")));
|
|
|
|
TEST_EQUAL(e.dict_size(), 4);
|
|
TEST_EQUAL(e.dict_size(), 4);
|
|
|
|
// dict_at
|
|
|
|
TEST_EQUAL(e.dict_at(0).first, "a");
|
|
TEST_EQUAL(e.dict_at(0).second.int_value(), 1);
|
|
TEST_EQUAL(e.dict_at(1).first, "b");
|
|
TEST_EQUAL(e.dict_at(1).second.string_value(), "foo");
|
|
TEST_EQUAL(e.dict_at(2).first, "c");
|
|
TEST_EQUAL(e.dict_at(2).second.type(), bdecode_node::list_t);
|
|
TEST_EQUAL(e.dict_at(3).first, "d");
|
|
TEST_EQUAL(e.dict_at(3).second.type(), bdecode_node::dict_t);
|
|
}
|
|
|
|
// test list_*_at functions
|
|
TORRENT_TEST(list_at_funs)
|
|
{
|
|
// int
|
|
// string
|
|
// list
|
|
// dict
|
|
char b[] = "li1e3:fooli1ei2eed1:xi1eee";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(!ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
|
|
TEST_EQUAL(e.type(), bdecode_node::list_t);
|
|
|
|
TEST_EQUAL(e.list_int_value_at(0), 1);
|
|
// make sure default values work
|
|
TEST_EQUAL(e.list_int_value_at(1, -10), -10);
|
|
|
|
TEST_EQUAL(e.list_string_value_at(1), "foo");
|
|
// make sure default values work
|
|
TEST_EQUAL(e.list_string_value_at(2, "blah"), "blah");
|
|
|
|
TEST_EQUAL(e.list_at(2).type(), bdecode_node::list_t);
|
|
TEST_EQUAL(e.list_at(2).list_size(), 2);
|
|
TEST_EQUAL(e.list_at(2).list_int_value_at(0), 1);
|
|
TEST_EQUAL(e.list_at(2).list_int_value_at(1), 2);
|
|
|
|
TEST_EQUAL(e.list_at(3).type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e.list_at(3).dict_size(), 1);
|
|
TEST_EQUAL(e.list_at(3).dict_find_int_value("x"), 1);
|
|
TEST_EQUAL(e.list_at(3).dict_find_int_value("y", -10), -10);
|
|
|
|
TEST_EQUAL(e.list_size(), 4);
|
|
TEST_EQUAL(e.list_size(), 4);
|
|
}
|
|
|
|
// test list_at in reverse order
|
|
TORRENT_TEST(list_at_reverse)
|
|
{
|
|
// int
|
|
// string
|
|
// list
|
|
// dict
|
|
char b[] = "li1e3:fooli1ei2eed1:xi1eee";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(!ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
|
|
TEST_EQUAL(e.type(), bdecode_node::list_t);
|
|
|
|
TEST_EQUAL(e.list_at(3).type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e.list_at(2).type(), bdecode_node::list_t);
|
|
TEST_EQUAL(e.list_string_value_at(1), "foo");
|
|
TEST_EQUAL(e.list_int_value_at(0), 1);
|
|
|
|
TEST_EQUAL(e.list_size(), 4);
|
|
TEST_EQUAL(e.list_size(), 4);
|
|
}
|
|
|
|
|
|
// test dict_find_* functions
|
|
TORRENT_TEST(dict_find_funs2)
|
|
{
|
|
// a: int
|
|
// b: string
|
|
// c: list
|
|
// d: dict
|
|
char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(!ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
|
|
TEST_EQUAL(e.type(), bdecode_node::dict_t);
|
|
|
|
// try finding the last item in a dict (to skip all the other ones)
|
|
TEST_EQUAL(e.dict_find("d").type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e.dict_find(std::string("d")).type(), bdecode_node::dict_t);
|
|
}
|
|
|
|
// print_entry
|
|
|
|
TORRENT_TEST(print_entry)
|
|
{
|
|
char b[] = "li1e3:fooli1ei2eed1:xi1eee";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(!ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
|
|
TEST_EQUAL(print_entry(e), "[ 1, 'foo', [ 1, 2 ], { 'x': 1 } ]");
|
|
}
|
|
|
|
TORRENT_TEST(print_entry2)
|
|
{
|
|
char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
|
|
error_code ec;
|
|
bdecode_node e = bdecode(b, ec);
|
|
TEST_CHECK(!ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
|
|
TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ], 'd': { 'x': 1 } }");
|
|
}
|
|
|
|
// test swap()
|
|
TORRENT_TEST(swap)
|
|
{
|
|
char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
|
|
char b2[] = "i1e";
|
|
|
|
error_code ec;
|
|
|
|
bdecode_node e1 = bdecode({b1, sizeof(b1)-1}, ec);
|
|
TEST_CHECK(!ec);
|
|
bdecode_node e2 = bdecode({b2, sizeof(b2)-1}, ec);
|
|
TEST_CHECK(!ec);
|
|
|
|
std::string str1 = print_entry(e1);
|
|
std::string str2 = print_entry(e2);
|
|
TEST_EQUAL(e1.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e2.type(), bdecode_node::int_t);
|
|
std::printf("%s\n", print_entry(e1).c_str());
|
|
|
|
e1.swap(e2);
|
|
|
|
TEST_EQUAL(e1.type(), bdecode_node::int_t);
|
|
TEST_EQUAL(e2.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(print_entry(e1), str2);
|
|
TEST_EQUAL(print_entry(e2), str1);
|
|
std::printf("%s\n", print_entry(e1).c_str());
|
|
|
|
e1.swap(e2);
|
|
|
|
TEST_EQUAL(e1.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e2.type(), bdecode_node::int_t);
|
|
TEST_EQUAL(print_entry(e1), str1);
|
|
TEST_EQUAL(print_entry(e2), str2);
|
|
std::printf("%s\n", print_entry(e1).c_str());
|
|
}
|
|
|
|
// test swap() (one node is the root of the other node)
|
|
TORRENT_TEST(swap_root)
|
|
{
|
|
char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
|
|
|
|
error_code ec;
|
|
|
|
bdecode_node e1 = bdecode({b1, sizeof(b1)-1}, ec);
|
|
TEST_CHECK(!ec);
|
|
|
|
bdecode_node e2 = e1.dict_find("c").list_at(0);
|
|
|
|
std::string str1 = print_entry(e1);
|
|
std::string str2 = print_entry(e2);
|
|
TEST_EQUAL(e1.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e2.type(), bdecode_node::int_t);
|
|
std::printf("%s\n", print_entry(e1).c_str());
|
|
|
|
e1.swap(e2);
|
|
|
|
TEST_EQUAL(e1.type(), bdecode_node::int_t);
|
|
TEST_EQUAL(e2.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(print_entry(e1), str2);
|
|
TEST_EQUAL(print_entry(e2), str1);
|
|
std::printf("%s\n", print_entry(e1).c_str());
|
|
|
|
// swap back
|
|
e1.swap(e2);
|
|
|
|
TEST_EQUAL(e1.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e2.type(), bdecode_node::int_t);
|
|
TEST_EQUAL(print_entry(e1), str1);
|
|
TEST_EQUAL(print_entry(e2), str2);
|
|
std::printf("%s\n", print_entry(e1).c_str());
|
|
}
|
|
|
|
// test swap() (neither is a root and they don't share a root)
|
|
TORRENT_TEST(swap_disjoint)
|
|
{
|
|
char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
|
|
char b2[] = "li1e3:fooli1ei2eed1:xi1eee";
|
|
|
|
|
|
error_code ec;
|
|
|
|
bdecode_node e1_root = bdecode({b1, sizeof(b1)-1}, ec);
|
|
TEST_CHECK(!ec);
|
|
bdecode_node e2_root = bdecode({b2, sizeof(b2)-1}, ec);
|
|
TEST_CHECK(!ec);
|
|
|
|
bdecode_node e1 = e1_root.dict_find("c").list_at(0);
|
|
bdecode_node e2 = e2_root.list_at(1);
|
|
|
|
std::string str1 = print_entry(e1);
|
|
std::string str2 = print_entry(e2);
|
|
TEST_EQUAL(e1.type(), bdecode_node::int_t);
|
|
TEST_EQUAL(e2.type(), bdecode_node::string_t);
|
|
|
|
e1.swap(e2);
|
|
|
|
TEST_EQUAL(e1.type(), bdecode_node::string_t);
|
|
TEST_EQUAL(e2.type(), bdecode_node::int_t);
|
|
TEST_EQUAL(print_entry(e1), str2);
|
|
TEST_EQUAL(print_entry(e2), str1);
|
|
|
|
// swap back
|
|
e1.swap(e2);
|
|
|
|
TEST_EQUAL(e1.type(), bdecode_node::int_t);
|
|
TEST_EQUAL(e2.type(), bdecode_node::string_t);
|
|
TEST_EQUAL(print_entry(e1), str1);
|
|
TEST_EQUAL(print_entry(e2), str2);
|
|
}
|
|
|
|
// test swap() (one is a root and they don't share a root)
|
|
TORRENT_TEST(swap_root_disjoint)
|
|
{
|
|
char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
|
|
char b2[] = "li1e3:fooli1ei2eed1:xi1eee";
|
|
|
|
|
|
error_code ec;
|
|
|
|
bdecode_node e1_root = bdecode({b1, sizeof(b1)-1}, ec);
|
|
TEST_CHECK(!ec);
|
|
bdecode_node e2 = bdecode({b2, sizeof(b2)-1}, ec);
|
|
TEST_CHECK(!ec);
|
|
|
|
bdecode_node e1 = e1_root.dict_find("d");
|
|
|
|
std::string str1 = print_entry(e1);
|
|
std::string str2 = print_entry(e2);
|
|
TEST_EQUAL(e1.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e2.type(), bdecode_node::list_t);
|
|
|
|
e1.swap(e2);
|
|
|
|
TEST_EQUAL(e1.type(), bdecode_node::list_t);
|
|
TEST_EQUAL(e2.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(print_entry(e1), str2);
|
|
TEST_EQUAL(print_entry(e2), str1);
|
|
|
|
// swap back
|
|
e1.swap(e2);
|
|
|
|
TEST_EQUAL(e1.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e2.type(), bdecode_node::list_t);
|
|
TEST_EQUAL(print_entry(e1), str1);
|
|
TEST_EQUAL(print_entry(e2), str2);
|
|
}
|
|
|
|
// make sure it's safe to reuse bdecode_nodes after clear() is called
|
|
TORRENT_TEST(clear)
|
|
{
|
|
char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
|
|
char b2[] = "li1ei2ee";
|
|
|
|
error_code ec;
|
|
bdecode_node e = bdecode({b1, sizeof(b1)-1}, ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
TEST_CHECK(!ec);
|
|
TEST_EQUAL(e.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e.dict_size(), 4);
|
|
TEST_EQUAL(e.dict_at(1).first, "b");
|
|
|
|
e = bdecode({b2, sizeof(b2)-1}, ec);
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
TEST_CHECK(!ec);
|
|
TEST_EQUAL(e.type(), bdecode_node::list_t);
|
|
TEST_EQUAL(e.list_size(), 2);
|
|
TEST_EQUAL(e.list_int_value_at(1), 2);
|
|
}
|
|
|
|
// assignment/copy of root nodes
|
|
TORRENT_TEST(copy_root)
|
|
{
|
|
char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
|
|
|
|
error_code ec;
|
|
bdecode_node e1 = bdecode({b1, sizeof(b1)-1}, ec);
|
|
TEST_CHECK(!ec);
|
|
TEST_EQUAL(e1.type(), bdecode_node::dict_t);
|
|
std::printf("%s\n", print_entry(e1).c_str());
|
|
|
|
bdecode_node e2(e1);
|
|
bdecode_node e3;
|
|
e3 = e1;
|
|
|
|
e1.clear();
|
|
|
|
TEST_EQUAL(e2.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e2.dict_size(), 4);
|
|
TEST_EQUAL(e2.dict_at(1).first, "b");
|
|
|
|
TEST_EQUAL(e3.type(), bdecode_node::dict_t);
|
|
TEST_EQUAL(e3.dict_size(), 4);
|
|
TEST_EQUAL(e3.dict_at(1).first, "b");
|
|
}
|
|
|
|
// non-owning references
|
|
TORRENT_TEST(non_owning_refs)
|
|
{
|
|
char b1[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1eee";
|
|
|
|
error_code ec;
|
|
bdecode_node e1 = bdecode({b1, sizeof(b1)-1}, ec);
|
|
TEST_CHECK(!ec);
|
|
|
|
TEST_EQUAL(e1.type(), bdecode_node::dict_t);
|
|
std::printf("%s\n", print_entry(e1).c_str());
|
|
|
|
bdecode_node e2 = e1.non_owning();
|
|
|
|
TEST_EQUAL(e2.type(), bdecode_node::dict_t);
|
|
|
|
e1.clear();
|
|
|
|
// e2 is invalid now
|
|
}
|
|
|
|
// test that a partial parse can be still be printed up to the
|
|
// point where it faild
|
|
TORRENT_TEST(partial_parse)
|
|
{
|
|
char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:dd1:xi1-eee";
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 35);
|
|
TEST_EQUAL(e.type(), bdecode_node::dict_t);
|
|
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
|
|
TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ], 'd': { 'x': {} } }");
|
|
}
|
|
|
|
TORRENT_TEST(partial_parse2)
|
|
{
|
|
char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee1:d-d1:xi1eee";
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 29);
|
|
TEST_EQUAL(e.type(), bdecode_node::dict_t);
|
|
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
|
|
TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ], 'd': {} }");
|
|
}
|
|
|
|
TORRENT_TEST(partial_parse3)
|
|
{
|
|
char b[] = "d1:ai1e1:b3:foo1:cli1ei2ee-1:dd1:xi1eee";
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 26);
|
|
TEST_EQUAL(e.type(), bdecode_node::dict_t);
|
|
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
|
|
TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1, 2 ] }");
|
|
}
|
|
|
|
TORRENT_TEST(partial_parse4)
|
|
{
|
|
char b[] = "d1:ai1e1:b3:foo1:cli1e-i2ee1:dd1:xi1eee";
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode(b, ec, &pos);
|
|
TEST_EQUAL(pos, 22);
|
|
TEST_EQUAL(e.type(), bdecode_node::dict_t);
|
|
|
|
std::printf("%s\n", print_entry(e).c_str());
|
|
|
|
TEST_EQUAL(print_entry(e), "{ 'a': 1, 'b': 'foo', 'c': [ 1 ] }");
|
|
}
|
|
|
|
TORRENT_TEST(partial_parse_string)
|
|
{
|
|
// it's important to not have a null terminator here
|
|
// to allow address sanitizer to trigger in case the decoder reads past the
|
|
// end
|
|
char b[] = { '5', '5'};
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode({b, sizeof(b)}, ec, &pos);
|
|
TEST_CHECK(ec);
|
|
TEST_EQUAL(pos, 2);
|
|
}
|
|
|
|
// test switch_underlying_buffer
|
|
TORRENT_TEST(switch_buffer)
|
|
{
|
|
char b1[] = "d1:ai1e1:b3:foo1:cli1e-i2ee1:dd1:xi1eee";
|
|
char b2[] = "d1:ai1e1:b3:foo1:cli1e-i2ee1:dd1:xi1eee";
|
|
|
|
error_code ec;
|
|
int pos;
|
|
bdecode_node e = bdecode({b1, sizeof(b1)-1}, ec, &pos);
|
|
TEST_EQUAL(pos, 22);
|
|
TEST_EQUAL(e.type(), bdecode_node::dict_t);
|
|
|
|
std::string string1 = print_entry(e);
|
|
std::printf("%s\n", string1.c_str());
|
|
|
|
e.switch_underlying_buffer(b2);
|
|
|
|
std::string string2 = print_entry(e);
|
|
std::printf("%s\n", string2.c_str());
|
|
|
|
TEST_EQUAL(string1, string2);
|
|
}
|
|
|