/* 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()); TEST_CHECK(span(b, sizeof(b) - 1) == e.data_section()); 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()); TEST_CHECK(span(b, sizeof(b) - 1) == e.data_section()); 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()); TEST_CHECK(span(test) == e.data_section()); 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()); TEST_CHECK(span(b, sizeof(b) - 1) == e.data_section()); 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); TEST_CHECK(span("3:aaa", 5) == e.list_at(1).data_section()); } // 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()); TEST_CHECK(span(b, sizeof(b) - 1) == e.data_section()); 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")); 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(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::ptrdiff_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) { string_view const b[] = { "d1:a1919191010:11111"_sv, "d2143289344:a4:aaaae"_sv, "d214328934114:a4:aaaae"_sv, "d9205357638345293824:a4:aaaae"_sv, "d1:a9205357638345293824:11111"_sv }; for (auto const& buf : b) { error_code ec; bdecode_node e = bdecode(buf, 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); }