-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
664 additions
and
624 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
#ifndef INC_BENCODE_TEST_DECODE_DECODE_HPP | ||
#define INC_BENCODE_TEST_DECODE_DECODE_HPP | ||
|
||
#include <sstream> | ||
#include <tuple> | ||
#include <utility> | ||
|
||
#include "bencode.hpp" | ||
|
||
using std::istringstream; | ||
using bistringstream = std::basic_istringstream<std::byte>; | ||
#if __cpp_char8_t | ||
using u8istringstream = std::basic_istringstream<char8_t>; | ||
#endif | ||
|
||
template<typename T> | ||
struct char_type { | ||
using type = typename T::value_type; | ||
}; | ||
|
||
template<typename Char> | ||
struct char_type<Char *> { using type = std::remove_cv_t<Char>; }; | ||
template<typename Char> | ||
struct char_type<std::basic_istringstream<Char>> { using type = Char; }; | ||
|
||
template<typename T> | ||
inline constexpr bool supports_view = | ||
!std::is_same_v<typename char_type<T>::type, std::byte>; | ||
template<typename Char> | ||
inline constexpr bool supports_view<std::basic_istringstream<Char>> = false; | ||
|
||
template<typename T> | ||
T make_data(const char *data) { | ||
using Char = typename char_type<T>::type; | ||
if constexpr(std::is_constructible_v<T, const Char *>) | ||
return T((Char*)(data)); | ||
else | ||
return T((Char*)data, (Char*)(data + std::strlen(data))); | ||
} | ||
|
||
template<typename DecodeArgs, typename DoDecode> | ||
auto mix_decoder(DecodeArgs &&decode_args, DoDecode &&do_decode) { | ||
return [decode_args, do_decode](auto &&data) { | ||
return std::apply( | ||
do_decode, decode_args(std::forward<decltype(data)>(data)) | ||
); | ||
}; | ||
} | ||
|
||
auto decode_args = [](auto &&data) { | ||
return std::tuple<decltype(data)&>(data); | ||
}; | ||
auto decode_iter_args = [](auto &&data) { | ||
return std::make_tuple(data.begin(), data.end()); | ||
}; | ||
auto decode_ptr_len_args = [](auto &&data) { | ||
return std::tuple<decltype(data)&, std::size_t>( | ||
data, bencode::detail::any_strlen(data) | ||
); | ||
}; | ||
|
||
template<typename InType, typename Builder, typename Callable> | ||
auto decode_tests(Builder &_, Callable &&decode) { | ||
using boost::get; | ||
using std::get; | ||
|
||
using OutType = decltype(decode(std::declval<InType>())); | ||
using Integer = typename OutType::integer; | ||
using String = typename OutType::string; | ||
using List = typename OutType::list; | ||
using Dict = typename OutType::dict; | ||
auto S = &make_data<String>; | ||
|
||
_.test("integer", [decode]() { | ||
auto pos = make_data<InType>("i42e"); | ||
auto pos_value = decode(pos); | ||
expect(get<Integer>(pos_value), equal_to(42)); | ||
|
||
auto neg = make_data<InType>("i-42e"); | ||
auto neg_value = decode(neg); | ||
expect(get<Integer>(neg_value), equal_to(-42)); | ||
}); | ||
|
||
_.test("string", [decode, S]() { | ||
auto data = make_data<InType>("4:spam"); | ||
// Silence GCC < 10. | ||
[[maybe_unused]] auto within_data_memory = within_memory(data); | ||
|
||
auto value = decode(data); | ||
auto str = get<String>(value); | ||
expect(str, equal_to(S("spam"))); | ||
if constexpr(bencode::detail::is_view_v<String>) { | ||
expect(&*str.begin(), within_data_memory); | ||
expect(&*str.end(), within_data_memory); | ||
} | ||
}); | ||
|
||
_.test("list", [decode]() { | ||
auto data = make_data<InType>("li42ee"); | ||
auto value = decode(data); | ||
auto list = get<List>(value); | ||
expect(get<Integer>(list[0]), equal_to(42)); | ||
}); | ||
|
||
_.test("dict", [decode, S]() { | ||
auto data = make_data<InType>("d4:spami42ee"); | ||
// Silence GCC < 10. | ||
[[maybe_unused]] auto within_data_memory = within_memory(data); | ||
|
||
auto value = decode(data); | ||
auto dict = get<Dict>(value); | ||
expect(get<Integer>(dict[S("spam")]), equal_to(42)); | ||
|
||
auto str = dict.find(S("spam"))->first; | ||
expect(str, equal_to(S("spam"))); | ||
if constexpr (bencode::detail::is_view_v<String>) { | ||
expect(&*str.begin(), within_data_memory); | ||
expect(&*str.end(), within_data_memory); | ||
} | ||
}); | ||
|
||
_.test("nested", [decode, S]() { | ||
auto data = make_data<InType>( | ||
"d" | ||
"3:one" "i1e" | ||
"5:three" "l" "d" "3:bar" "i0e" "3:foo" "i0e" "e" "e" | ||
"3:two" "l" "i3e" "3:foo" "i4e" "e" | ||
"e" | ||
); | ||
auto value = decode(data); | ||
auto dict = get<Dict>(value); | ||
expect(get<Integer>(dict[S("one")]), equal_to(1)); | ||
expect(get<String>(get<List>(dict[S("two")])[1]), equal_to(S("foo"))); | ||
expect(get<Integer>(get<Dict>(get<List>(dict[S("three")])[0])[S("foo")]), | ||
equal_to(0)); | ||
}); | ||
} | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
#include <mettle.hpp> | ||
using namespace mettle; | ||
|
||
#include "bencode.hpp" | ||
|
||
#include "../matchers.hpp" | ||
#include "decode.hpp" | ||
|
||
template<typename DecodeArgs> | ||
struct decode_suites { | ||
decode_suites(DecodeArgs decode_args) | ||
: decode_args(std::move(decode_args)) {} | ||
DecodeArgs decode_args; | ||
|
||
template<typename Builder> | ||
void operator ()(Builder &_) const { | ||
using InType = fixture_type_t<decltype(_)>; | ||
using Char = typename char_type<InType>::type; | ||
auto &decode_args = this->decode_args; | ||
|
||
subsuite<>(_, "decode", [decode_args](auto &_) { | ||
decode_tests<InType>(_, mix_decoder(decode_args, | ||
[](auto &&data, auto &&...rest) { | ||
auto value = bencode::decode(data, rest...); | ||
if constexpr(std::is_base_of_v<std::ios_base, InType>) | ||
expect(data, at_eof(data)); | ||
return value; | ||
})); | ||
}); | ||
|
||
if constexpr(supports_view<InType>) { | ||
subsuite<>(_, "decode_view", [decode_args](auto &_) { | ||
decode_tests<InType>(_, mix_decoder(decode_args, | ||
[](auto &&data, auto &&...rest) { | ||
return bencode::decode_view(data, rest...); | ||
})); | ||
}); | ||
} | ||
|
||
subsuite<>(_, "boost_decode", [decode_args](auto &_) { | ||
decode_tests<InType>(_, mix_decoder(decode_args, | ||
[](auto &&data, auto &&...rest) { | ||
auto value = bencode::boost_decode(data, rest...); | ||
if constexpr(std::is_base_of_v<std::ios_base, InType>) | ||
expect(data, at_eof(data)); | ||
return value; | ||
})); | ||
}); | ||
|
||
if constexpr(supports_view<InType>) { | ||
subsuite<>(_, "boost_decode_view", [decode_args](auto &_) { | ||
decode_tests<InType>(_, mix_decoder(decode_args, | ||
[](auto &&data, auto &&...rest) { | ||
return bencode::boost_decode_view(data, rest...); | ||
})); | ||
}); | ||
} | ||
|
||
subsuite< | ||
bencode::data_for_char_t<Char>, bencode::boost_data_for_char_t<Char> | ||
>(_, "basic_decode to", type_only, [decode_args](auto &_) { | ||
using OutType = fixture_type_t<decltype(_)>; | ||
decode_tests<InType>(_, mix_decoder(decode_args, | ||
[](auto &&data, auto &&...rest) { | ||
auto value = bencode::basic_decode<OutType>(data, rest...); | ||
if constexpr(std::is_base_of_v<std::ios_base, InType>) | ||
expect(data, at_eof(data)); | ||
return value; | ||
})); | ||
}); | ||
|
||
if constexpr(supports_view<InType>) { | ||
subsuite< | ||
bencode::data_view_for_char_t<Char>, | ||
bencode::boost_data_view_for_char_t<Char> | ||
>(_, "basic_decode to", type_only, [decode_args](auto &_) { | ||
using OutType = fixture_type_t<decltype(_)>; | ||
decode_tests<InType>(_, mix_decoder(decode_args, | ||
[](auto &&data, auto &&...rest) { | ||
auto value = bencode::basic_decode<OutType>(data, rest...); | ||
if constexpr(std::is_base_of_v<std::ios_base, InType>) | ||
expect(data, at_eof(data)); | ||
return value; | ||
})); | ||
}); | ||
} | ||
} | ||
}; | ||
|
||
suite<> test_decode("decode", [](auto &_) { | ||
subsuite< | ||
const char *, std::string, std::vector<char>, istringstream, | ||
#if __cpp_char8_t | ||
const char8_t *, std::u8string, std::vector<char8_t>, u8istringstream, | ||
#endif | ||
const std::byte *, std::vector<std::byte>, bistringstream | ||
>(_, "decode", type_only, decode_suites(decode_args)); | ||
|
||
subsuite< | ||
std::string, std::vector<char>, | ||
#if __cpp_char8_t | ||
std::u8string, std::vector<char8_t>, | ||
#endif | ||
std::vector<std::byte> | ||
>(_, "decode iterator pair", type_only, decode_suites(decode_iter_args)); | ||
|
||
subsuite< | ||
const char *, | ||
#if __cpp_char8_t | ||
const char8_t *, | ||
#endif | ||
const std::byte * | ||
>(_, "decode pointer/length", type_only, decode_suites(decode_ptr_len_args)); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
#include <mettle.hpp> | ||
using namespace mettle; | ||
|
||
#include "bencode.hpp" | ||
|
||
#include "../matchers.hpp" | ||
|
||
suite<> test_decode_errors("decode error handling", [](auto &_) { | ||
_.test("unexpected type token", []() { | ||
expect([]() { bencode::decode("x"); }, | ||
decode_error<bencode::syntax_error>("unexpected type token", 0)); | ||
}); | ||
|
||
_.test("unexpected end of input", []() { | ||
auto eos = [](std::size_t offset) { | ||
return decode_error<bencode::end_of_input_error>( | ||
"unexpected end of input", offset | ||
); | ||
}; | ||
|
||
expect([]() { bencode::decode(""); }, eos(0)); | ||
expect([]() { bencode::decode("i123"); }, eos(4)); | ||
expect([]() { bencode::decode("3"); }, eos(1)); | ||
expect([]() { bencode::decode("3:as"); }, eos(4)); | ||
expect([]() { bencode::decode("l"); }, eos(1)); | ||
expect([]() { bencode::decode("li1e"); }, eos(4)); | ||
expect([]() { bencode::decode("d"); }, eos(1)); | ||
expect([]() { bencode::decode("d1:a"); }, eos(4)); | ||
expect([]() { bencode::decode("d1:ai1e"); }, eos(7)); | ||
}); | ||
|
||
_.test("extraneous character", []() { | ||
expect([]() { bencode::decode("i123ei"); }, | ||
decode_error<bencode::syntax_error>("extraneous character", 5)); | ||
}); | ||
|
||
_.test("expected 'e' token", []() { | ||
expect([]() { bencode::decode("i123i"); }, | ||
decode_error<bencode::syntax_error>("expected 'e' token", 4)); | ||
}); | ||
|
||
_.test("unexpected 'e' token", []() { | ||
expect([]() { bencode::decode("e"); }, | ||
decode_error<bencode::syntax_error>("unexpected 'e' token", 0)); | ||
}); | ||
|
||
_.test("expected ':' token", []() { | ||
expect([]() { bencode::decode("1abc"); }, | ||
decode_error<bencode::syntax_error>("expected ':' token", 1)); | ||
}); | ||
|
||
_.test("expected string start token", []() { | ||
expect( | ||
[]() { bencode::decode("di123ee"); }, | ||
decode_error<bencode::syntax_error>( | ||
"expected string start token for dict key", 1 | ||
) | ||
); | ||
}); | ||
|
||
_.test("duplicated key", []() { | ||
expect([]() { bencode::decode("d3:fooi1e3:fooi1ee"); }, | ||
decode_error<bencode::syntax_error>( | ||
"duplicated key in dict: \"foo\"", 17 | ||
)); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
#include <mettle.hpp> | ||
using namespace mettle; | ||
|
||
#include "bencode.hpp" | ||
|
||
#include "../matchers.hpp" | ||
|
||
suite<> test_decode_integer("decode integers", [](auto &_) { | ||
using udata = bencode::basic_data< | ||
std::variant, unsigned long long, std::string, std::vector, | ||
bencode::map_proxy | ||
>; | ||
|
||
_.test("max value", []() { | ||
auto value = bencode::decode("i9223372036854775807e"); | ||
expect(std::get<bencode::integer>(value), | ||
equal_to(9223372036854775807LL)); | ||
}); | ||
|
||
_.test("overflow", []() { | ||
expect([]() { bencode::decode("i9223372036854775808e"); }, | ||
decode_error<std::overflow_error>("integer overflow", 20)); | ||
expect([]() { bencode::decode("i9323372036854775807e"); }, | ||
decode_error<std::overflow_error>("integer overflow", 20)); | ||
expect([]() { bencode::decode("i92233720368547758070e"); }, | ||
decode_error<std::overflow_error>("integer overflow", 20)); | ||
}); | ||
|
||
_.test("min value", []() { | ||
auto value = bencode::decode("i-9223372036854775808e"); | ||
expect(std::get<bencode::integer>(value), | ||
equal_to(-9223372036854775807LL - 1)); | ||
}); | ||
|
||
_.test("underflow", []() { | ||
expect([]() { bencode::decode("i-9223372036854775809e"); }, | ||
decode_error<std::underflow_error>("integer underflow", 21)); | ||
expect([]() { bencode::decode("i-9323372036854775808e"); }, | ||
decode_error<std::underflow_error>("integer underflow", 21)); | ||
expect([]() { bencode::decode("i-92233720368547758080e"); }, | ||
decode_error<std::underflow_error>("integer underflow", 21)); | ||
}); | ||
|
||
_.test("max value (unsigned)", []() { | ||
auto value = bencode::basic_decode<udata>("i18446744073709551615e"); | ||
expect(std::get<udata::integer>(value), | ||
equal_to(18446744073709551615ULL)); | ||
}); | ||
|
||
_.test("overflow (unsigned)", []() { | ||
expect([]() { | ||
bencode::basic_decode<udata>("i18446744073709551616e"); | ||
}, decode_error<std::overflow_error>("integer overflow", 21)); | ||
expect([]() { | ||
bencode::basic_decode<udata>("i19446744073709551615e"); | ||
}, decode_error<std::overflow_error>("integer overflow", 21)); | ||
expect([]() { | ||
bencode::basic_decode<udata>("i184467440737095516150e"); | ||
}, decode_error<std::overflow_error>("integer overflow", 21)); | ||
}); | ||
|
||
_.test("negative value (unsigned)", []() { | ||
expect( | ||
[]() { bencode::basic_decode<udata>("i-42e"); }, | ||
decode_error<std::underflow_error>("expected unsigned integer", 1) | ||
); | ||
}); | ||
}); |
Oops, something went wrong.