Skip to content

Commit e2c15a9

Browse files
committed
Split up decoding tests
1 parent 047b3a5 commit e2c15a9

File tree

7 files changed

+664
-624
lines changed

7 files changed

+664
-624
lines changed

test/decode/decode.hpp

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#ifndef INC_BENCODE_TEST_DECODE_DECODE_HPP
2+
#define INC_BENCODE_TEST_DECODE_DECODE_HPP
3+
4+
#include <sstream>
5+
#include <tuple>
6+
#include <utility>
7+
8+
#include "bencode.hpp"
9+
10+
using std::istringstream;
11+
#if __cpp_char8_t
12+
using u8istringstream = std::basic_istringstream<char8_t>;
13+
#endif
14+
15+
template<typename T>
16+
struct char_type {
17+
using type = typename T::value_type;
18+
};
19+
20+
template<typename Char>
21+
struct char_type<Char *> { using type = std::remove_cv_t<Char>; };
22+
template<typename Char>
23+
struct char_type<std::basic_istringstream<Char>> { using type = Char; };
24+
25+
template<typename T>
26+
inline constexpr bool supports_view =
27+
!std::is_same_v<typename char_type<T>::type, std::byte>;
28+
template<typename Char>
29+
inline constexpr bool supports_view<std::basic_istringstream<Char>> = false;
30+
31+
template<typename T>
32+
T make_data(const char *data) {
33+
using Char = typename char_type<T>::type;
34+
if constexpr(std::is_constructible_v<T, const Char *>)
35+
return T((Char*)(data));
36+
else
37+
return T((Char*)data, (Char*)(data + std::strlen(data)));
38+
}
39+
40+
template<typename DecodeArgs, typename DoDecode>
41+
auto mix_decoder(DecodeArgs &&decode_args, DoDecode &&do_decode) {
42+
return [decode_args, do_decode](auto &&data) {
43+
return std::apply(
44+
do_decode, decode_args(std::forward<decltype(data)>(data))
45+
);
46+
};
47+
}
48+
49+
auto decode_args = [](auto &&data) {
50+
return std::tuple<decltype(data)&>(data);
51+
};
52+
auto decode_iter_args = [](auto &&data) {
53+
return std::make_tuple(data.begin(), data.end());
54+
};
55+
auto decode_ptr_len_args = [](auto &&data) {
56+
return std::tuple<decltype(data)&, std::size_t>(
57+
data, bencode::detail::any_strlen(data)
58+
);
59+
};
60+
61+
template<typename InType, typename Builder, typename Callable>
62+
auto decode_tests(Builder &_, Callable &&decode) {
63+
using boost::get;
64+
using std::get;
65+
66+
using OutType = decltype(decode(std::declval<InType>()));
67+
using Integer = typename OutType::integer;
68+
using String = typename OutType::string;
69+
using List = typename OutType::list;
70+
using Dict = typename OutType::dict;
71+
auto S = &make_data<String>;
72+
73+
_.test("integer", [decode]() {
74+
auto pos = make_data<InType>("i42e");
75+
auto pos_value = decode(pos);
76+
expect(get<Integer>(pos_value), equal_to(42));
77+
78+
auto neg = make_data<InType>("i-42e");
79+
auto neg_value = decode(neg);
80+
expect(get<Integer>(neg_value), equal_to(-42));
81+
});
82+
83+
_.test("string", [decode, S]() {
84+
auto data = make_data<InType>("4:spam");
85+
// Silence GCC < 10.
86+
[[maybe_unused]] auto within_data_memory = within_memory(data);
87+
88+
auto value = decode(data);
89+
auto str = get<String>(value);
90+
expect(str, equal_to(S("spam")));
91+
if constexpr(std::ranges::view<String>) {
92+
expect(&*str.begin(), within_data_memory);
93+
expect(&*str.end(), within_data_memory);
94+
}
95+
});
96+
97+
_.test("list", [decode]() {
98+
auto data = make_data<InType>("li42ee");
99+
auto value = decode(data);
100+
auto list = get<List>(value);
101+
expect(get<Integer>(list[0]), equal_to(42));
102+
});
103+
104+
_.test("dict", [decode, S]() {
105+
auto data = make_data<InType>("d4:spami42ee");
106+
// Silence GCC < 10.
107+
[[maybe_unused]] auto within_data_memory = within_memory(data);
108+
109+
auto value = decode(data);
110+
auto dict = get<Dict>(value);
111+
expect(get<Integer>(dict[S("spam")]), equal_to(42));
112+
113+
auto str = dict.find(S("spam"))->first;
114+
expect(str, equal_to(S("spam")));
115+
if constexpr(std::ranges::view<String>) {
116+
expect(&*str.begin(), within_data_memory);
117+
expect(&*str.end(), within_data_memory);
118+
}
119+
});
120+
121+
_.test("nested", [decode, S]() {
122+
auto data = make_data<InType>(
123+
"d"
124+
"3:one" "i1e"
125+
"5:three" "l" "d" "3:bar" "i0e" "3:foo" "i0e" "e" "e"
126+
"3:two" "l" "i3e" "3:foo" "i4e" "e"
127+
"e"
128+
);
129+
auto value = decode(data);
130+
auto dict = get<Dict>(value);
131+
expect(get<Integer>(dict[S("one")]), equal_to(1));
132+
expect(get<String>(get<List>(dict[S("two")])[1]), equal_to(S("foo")));
133+
expect(get<Integer>(get<Dict>(get<List>(dict[S("three")])[0])[S("foo")]),
134+
equal_to(0));
135+
});
136+
}
137+
138+
#endif

test/decode/test_decode.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#include <mettle.hpp>
2+
using namespace mettle;
3+
4+
#include "bencode.hpp"
5+
6+
#include "../matchers.hpp"
7+
#include "decode.hpp"
8+
9+
template<typename DecodeArgs>
10+
struct decode_suites {
11+
decode_suites(DecodeArgs decode_args)
12+
: decode_args(std::move(decode_args)) {}
13+
DecodeArgs decode_args;
14+
15+
template<typename Builder>
16+
void operator ()(Builder &_) const {
17+
using InType = fixture_type_t<decltype(_)>;
18+
using Char = typename char_type<InType>::type;
19+
auto &decode_args = this->decode_args;
20+
21+
subsuite<>(_, "decode", [decode_args](auto &_) {
22+
decode_tests<InType>(_, mix_decoder(decode_args,
23+
[](auto &&data, auto &&...rest) {
24+
auto value = bencode::decode(data, rest...);
25+
if constexpr(std::is_base_of_v<std::ios_base, InType>)
26+
expect(data, at_eof(data));
27+
return value;
28+
}));
29+
});
30+
31+
if constexpr(supports_view<InType>) {
32+
subsuite<>(_, "decode_view", [decode_args](auto &_) {
33+
decode_tests<InType>(_, mix_decoder(decode_args,
34+
[](auto &&data, auto &&...rest) {
35+
return bencode::decode_view(data, rest...);
36+
}));
37+
});
38+
}
39+
40+
subsuite<>(_, "boost_decode", [decode_args](auto &_) {
41+
decode_tests<InType>(_, mix_decoder(decode_args,
42+
[](auto &&data, auto &&...rest) {
43+
auto value = bencode::boost_decode(data, rest...);
44+
if constexpr(std::is_base_of_v<std::ios_base, InType>)
45+
expect(data, at_eof(data));
46+
return value;
47+
}));
48+
});
49+
50+
if constexpr(supports_view<InType>) {
51+
subsuite<>(_, "boost_decode_view", [decode_args](auto &_) {
52+
decode_tests<InType>(_, mix_decoder(decode_args,
53+
[](auto &&data, auto &&...rest) {
54+
return bencode::boost_decode_view(data, rest...);
55+
}));
56+
});
57+
}
58+
59+
subsuite<
60+
bencode::data_for_char_t<Char>, bencode::boost_data_for_char_t<Char>
61+
>(_, "basic_decode to", type_only, [decode_args](auto &_) {
62+
using OutType = fixture_type_t<decltype(_)>;
63+
decode_tests<InType>(_, mix_decoder(decode_args,
64+
[](auto &&data, auto &&...rest) {
65+
auto value = bencode::basic_decode<OutType>(data, rest...);
66+
if constexpr(std::is_base_of_v<std::ios_base, InType>)
67+
expect(data, at_eof(data));
68+
return value;
69+
}));
70+
});
71+
72+
if constexpr(supports_view<InType>) {
73+
subsuite<
74+
bencode::data_view_for_char_t<Char>,
75+
bencode::boost_data_view_for_char_t<Char>
76+
>(_, "basic_decode to", type_only, [decode_args](auto &_) {
77+
using OutType = fixture_type_t<decltype(_)>;
78+
decode_tests<InType>(_, mix_decoder(decode_args,
79+
[](auto &&data, auto &&...rest) {
80+
auto value = bencode::basic_decode<OutType>(data, rest...);
81+
if constexpr(std::is_base_of_v<std::ios_base, InType>)
82+
expect(data, at_eof(data));
83+
return value;
84+
}));
85+
});
86+
}
87+
}
88+
};
89+
90+
suite<> test_decode("decode", [](auto &_) {
91+
subsuite<
92+
const char *, std::string, std::vector<char>, istringstream,
93+
#if __cpp_char8_t
94+
const char8_t *, std::u8string, std::vector<char8_t>, u8istringstream,
95+
#endif
96+
const std::byte *, std::vector<std::byte>
97+
>(_, "decode", type_only, decode_suites(decode_args));
98+
99+
subsuite<
100+
std::string, std::vector<char>,
101+
#if __cpp_char8_t
102+
std::u8string, std::vector<char8_t>,
103+
#endif
104+
std::vector<std::byte>
105+
>(_, "decode iterator pair", type_only, decode_suites(decode_iter_args));
106+
107+
subsuite<
108+
const char *,
109+
#if __cpp_char8_t
110+
const char8_t *,
111+
#endif
112+
const std::byte *
113+
>(_, "decode pointer/length", type_only, decode_suites(decode_ptr_len_args));
114+
});

test/decode/test_decode_errors.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#include <mettle.hpp>
2+
using namespace mettle;
3+
4+
#include "bencode.hpp"
5+
6+
#include "../matchers.hpp"
7+
8+
suite<> test_decode_errors("decode error handling", [](auto &_) {
9+
_.test("unexpected type token", []() {
10+
expect([]() { bencode::decode("x"); },
11+
decode_error<bencode::syntax_error>("unexpected type token", 0));
12+
});
13+
14+
_.test("unexpected end of input", []() {
15+
auto eos = [](std::size_t offset) {
16+
return decode_error<bencode::end_of_input_error>(
17+
"unexpected end of input", offset
18+
);
19+
};
20+
21+
expect([]() { bencode::decode(""); }, eos(0));
22+
expect([]() { bencode::decode("i123"); }, eos(4));
23+
expect([]() { bencode::decode("3"); }, eos(1));
24+
expect([]() { bencode::decode("3:as"); }, eos(4));
25+
expect([]() { bencode::decode("l"); }, eos(1));
26+
expect([]() { bencode::decode("li1e"); }, eos(4));
27+
expect([]() { bencode::decode("d"); }, eos(1));
28+
expect([]() { bencode::decode("d1:a"); }, eos(4));
29+
expect([]() { bencode::decode("d1:ai1e"); }, eos(7));
30+
});
31+
32+
_.test("extraneous character", []() {
33+
expect([]() { bencode::decode("i123ei"); },
34+
decode_error<bencode::syntax_error>("extraneous character", 5));
35+
});
36+
37+
_.test("expected 'e' token", []() {
38+
expect([]() { bencode::decode("i123i"); },
39+
decode_error<bencode::syntax_error>("expected 'e' token", 4));
40+
});
41+
42+
_.test("unexpected 'e' token", []() {
43+
expect([]() { bencode::decode("e"); },
44+
decode_error<bencode::syntax_error>("unexpected 'e' token", 0));
45+
});
46+
47+
_.test("expected ':' token", []() {
48+
expect([]() { bencode::decode("1abc"); },
49+
decode_error<bencode::syntax_error>("expected ':' token", 1));
50+
});
51+
52+
_.test("expected string start token", []() {
53+
expect(
54+
[]() { bencode::decode("di123ee"); },
55+
decode_error<bencode::syntax_error>(
56+
"expected string start token for dict key", 1
57+
)
58+
);
59+
});
60+
61+
_.test("duplicated key", []() {
62+
expect([]() { bencode::decode("d3:fooi1e3:fooi1ee"); },
63+
decode_error<bencode::syntax_error>(
64+
"duplicated key in dict: \"foo\"", 17
65+
));
66+
});
67+
});
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include <mettle.hpp>
2+
using namespace mettle;
3+
4+
#include "bencode.hpp"
5+
6+
#include "../matchers.hpp"
7+
8+
suite<> test_decode_integer("decode integers", [](auto &_) {
9+
using udata = bencode::basic_data<
10+
std::variant, unsigned long long, std::string, std::vector,
11+
bencode::map_proxy
12+
>;
13+
14+
_.test("max value", []() {
15+
auto value = bencode::decode("i9223372036854775807e");
16+
expect(std::get<bencode::integer>(value),
17+
equal_to(9223372036854775807LL));
18+
});
19+
20+
_.test("overflow", []() {
21+
expect([]() { bencode::decode("i9223372036854775808e"); },
22+
decode_error<std::overflow_error>("integer overflow", 20));
23+
expect([]() { bencode::decode("i9323372036854775807e"); },
24+
decode_error<std::overflow_error>("integer overflow", 20));
25+
expect([]() { bencode::decode("i92233720368547758070e"); },
26+
decode_error<std::overflow_error>("integer overflow", 20));
27+
});
28+
29+
_.test("min value", []() {
30+
auto value = bencode::decode("i-9223372036854775808e");
31+
expect(std::get<bencode::integer>(value),
32+
equal_to(-9223372036854775807LL - 1));
33+
});
34+
35+
_.test("underflow", []() {
36+
expect([]() { bencode::decode("i-9223372036854775809e"); },
37+
decode_error<std::underflow_error>("integer underflow", 21));
38+
expect([]() { bencode::decode("i-9323372036854775808e"); },
39+
decode_error<std::underflow_error>("integer underflow", 21));
40+
expect([]() { bencode::decode("i-92233720368547758080e"); },
41+
decode_error<std::underflow_error>("integer underflow", 21));
42+
});
43+
44+
_.test("max value (unsigned)", []() {
45+
auto value = bencode::basic_decode<udata>("i18446744073709551615e");
46+
expect(std::get<udata::integer>(value),
47+
equal_to(18446744073709551615ULL));
48+
});
49+
50+
_.test("overflow (unsigned)", []() {
51+
expect([]() {
52+
bencode::basic_decode<udata>("i18446744073709551616e");
53+
}, decode_error<std::overflow_error>("integer overflow", 21));
54+
expect([]() {
55+
bencode::basic_decode<udata>("i19446744073709551615e");
56+
}, decode_error<std::overflow_error>("integer overflow", 21));
57+
expect([]() {
58+
bencode::basic_decode<udata>("i184467440737095516150e");
59+
}, decode_error<std::overflow_error>("integer overflow", 21));
60+
});
61+
62+
_.test("negative value (unsigned)", []() {
63+
expect(
64+
[]() { bencode::basic_decode<udata>("i-42e"); },
65+
decode_error<std::underflow_error>("expected unsigned integer", 1)
66+
);
67+
});
68+
});

0 commit comments

Comments
 (0)