34#include "dccl/codec.h"
42void check(
bool condition,
const std::string& what)
47 std::cerr <<
"FAILED: " << what << std::endl;
53template <
typename Exception,
typename Callable>
54void check_throws(Callable op,
const std::string& what)
61 catch (
const Exception& e)
63 std::cout <<
"ok (threw as expected): " << what <<
": " << e.what() << std::endl;
66 catch (
const std::exception& e)
68 std::cerr <<
"FAILED: " << what <<
" threw the wrong type: " << e.what() << std::endl;
71 std::cerr <<
"FAILED: " << what <<
" did not throw" << std::endl;
75void test_raw_buffer_encode()
78 codec.load<dccl::test::SimpleMsg>();
80 dccl::test::SimpleMsg msg;
84 std::string as_string;
85 codec.encode(&as_string, msg);
88 size_t written = codec.encode(buffer,
sizeof(buffer), msg);
90 check(written == as_string.size(),
"char* and std::string overloads agree on size");
91 check(std::string(buffer, written) == as_string,
92 "char* and std::string overloads agree on bytes");
93 check(written == codec.size(msg),
"size() matches the encoded length");
95 dccl::test::SimpleMsg decoded;
96 codec.decode(std::string(buffer, written), &decoded);
97 check(decoded.a() == 42 && decoded.b() == 7,
"raw buffer round trips");
100 char head_buffer[256];
101 size_t head_written = codec.encode(head_buffer,
sizeof(head_buffer), msg,
true);
102 check(head_written > 0,
"header-only encode writes something");
103 check(head_written < written,
"header-only encode is shorter than a full encode");
106 check_throws<std::length_error>([&]() { codec.encode(buffer, 0, msg); },
107 "encode into a zero-length buffer");
108 check_throws<std::length_error>([&]() { codec.encode(buffer, head_written, msg); },
109 "encode into a buffer that only fits the head");
112void test_load_and_unload()
116 std::size_t hash = codec.load<dccl::test::SimpleMsg>();
117 check(hash != 0,
"load returns a hash");
118 check(codec.load<dccl::test::SimpleMsg>() == hash,
"loading twice yields the same hash");
120 codec.load<dccl::test::OtherMsg>();
121 check(codec.id<dccl::test::SimpleMsg>() == 220,
"SimpleMsg keeps its declared id");
122 check(codec.id<dccl::test::OtherMsg>() == 221,
"OtherMsg keeps its declared id");
125 codec.unload<dccl::test::SimpleMsg>();
126 dccl::test::SimpleMsg msg;
128 check_throws<dccl::Exception>(
132 codec.encode(&s, msg);
134 "encoding an unloaded message");
137 dccl::test::OtherMsg other;
139 check_throws<dccl::Exception>(
143 codec.encode(&s, other);
145 "encoding a message unloaded by id");
148 codec.unload(dccl::test::SimpleMsg::descriptor());
150 check(
true,
"unloading an unloaded message is a no-op");
152 codec.load<dccl::test::SimpleMsg>();
153 codec.load<dccl::test::OtherMsg>();
155 check_throws<dccl::Exception>(
159 codec.encode(&s, msg);
161 "encoding after unload_all");
164void test_load_errors()
168 check_throws<dccl::Exception>([&]() { codec.load<dccl::test::NoMaxBytesMsg>(); },
169 "loading a message without max_bytes");
170 check_throws<dccl::Exception>([&]() { codec.load<dccl::test::NoCodecVersionMsg>(); },
171 "loading a message without codec_version");
172 check_throws<dccl::Exception>([&]() { codec.load<dccl::test::TooBigMsg>(); },
173 "loading a message that exceeds its own max_bytes");
176 codec.load<dccl::test::SimpleMsg>();
177 check_throws<dccl::Exception>([&]() { codec.load<dccl::test::DuplicateIdMsg>(); },
178 "loading a message with a duplicate DCCL id");
181 check_throws<dccl::Exception>([&]() { codec.load<dccl::test::OneofV2Msg>(); },
182 "loading a v2 message containing a oneof");
183 check_throws<dccl::Exception>([&]() { codec.load<dccl::test::OneofV3Msg>(); },
184 "loading a v3 message containing a oneof");
187void test_encode_errors()
190 codec.load<dccl::test::SimpleMsg>();
193 dccl::test::SimpleMsg incomplete;
194 check_throws<dccl::Exception>(
198 codec.encode(&s, incomplete);
200 "encoding a message with an unset required field");
204 codec.load<dccl::test::NestedMsg>();
205 dccl::test::NestedMsg nested;
206 nested.mutable_single();
207 nested.add_several();
208 nested.add_several();
210 bool named_the_field =
false;
214 codec.encode(&s, nested);
218 named_the_field = std::string(e.what()).find(
"inner_required") != std::string::npos;
220 check(named_the_field,
"the error names the missing field inside a sub-message");
223 dccl::test::SimpleMsg out_of_range;
224 out_of_range.set_a(999999);
226 codec.encode(&clamped, out_of_range);
227 check(!clamped.empty(),
"an out-of-range field encodes when not strict");
229 codec.set_strict(
true);
230 check_throws<dccl::OutOfRangeException>(
234 codec.encode(&s, out_of_range);
236 "an out-of-range field under strict mode");
237 codec.set_strict(
false);
240void test_size_queries()
243 codec.load<dccl::test::SimpleMsg>();
245 unsigned max = codec.max_size<dccl::test::SimpleMsg>();
246 unsigned min = codec.min_size<dccl::test::SimpleMsg>();
248 check(min <= max,
"min_size does not exceed max_size");
249 check(max <= 32,
"max_size respects the declared max_bytes");
251 dccl::test::SimpleMsg msg;
254 unsigned actual = codec.size(msg);
255 check(actual >= min && actual <= max,
"an actual encoding falls between min and max size");
258 check(codec.max_size(dccl::test::SimpleMsg::descriptor()) == max,
259 "max_size descriptor overload agrees");
260 check(codec.min_size(dccl::test::SimpleMsg::descriptor()) == min,
261 "min_size descriptor overload agrees");
264void test_info_output()
267 codec.load<dccl::test::SimpleMsg>();
268 codec.load<dccl::test::OtherMsg>();
270 std::stringstream single;
271 codec.info<dccl::test::SimpleMsg>(&single);
272 check(single.str().find(
"SimpleMsg") != std::string::npos,
"info() names the message");
273 check(single.str().find(
"a") != std::string::npos,
"info() lists fields");
275 std::stringstream all;
276 codec.info_all(&all);
277 check(all.str().find(
"SimpleMsg") != std::string::npos,
"info_all() covers SimpleMsg");
278 check(all.str().find(
"OtherMsg") != std::string::npos,
"info_all() covers OtherMsg");
279 check(all.str().find(
"2 messages loaded") != std::string::npos,
280 "info_all() reports how many messages are loaded");
284 codec.set_console_width(1);
285 std::stringstream narrow;
286 codec.info_all(&narrow);
287 check(!narrow.str().empty(),
"info_all() copes with a console narrower than its title");
289 codec.set_console_width(200);
290 std::stringstream wide;
291 codec.info_all(&wide);
292 check(!wide.str().empty(),
"info_all() copes with a wide console");
295void test_library_loading()
299 check_throws<dccl::Exception>([&]() { codec.load_library(
static_cast<void*
>(
nullptr)); },
300 "load_library with a null handle");
301 check_throws<dccl::Exception>([&]() { codec.unload_library(
nullptr); },
302 "unload_library with a null handle");
303 check_throws<dccl::Exception>([&]() { codec.load_library(
"/nonexistent/libnope.so"); },
304 "load_library with a path that does not resolve");
310 check(codec.get_id_codec() == dccl::Codec::default_id_codec_name(),
311 "a default-constructed Codec uses the default id codec");
313 check_throws<dccl::Exception>([&]() {
dccl::Codec bad(
"no.such.id.codec"); },
314 "constructing with an unknown id codec");
317 codec.load<dccl::test::SimpleMsg>();
318 check_throws<dccl::Exception>([&]() { codec.set_id_codec(
"no.such.id.codec"); },
319 "setting an unknown id codec");
322void test_id_from_bytes()
325 codec.load<dccl::test::SimpleMsg>();
327 dccl::test::SimpleMsg msg;
330 codec.encode(&bytes, msg);
332 check(codec.id(bytes) == 220,
"the id can be read back off the wire");
333 check(codec.id(bytes.begin(), bytes.end()) == 220,
"the iterator id() overload agrees");
334 check(codec.id(dccl::test::SimpleMsg::descriptor()) == 220,
335 "the descriptor id() overload agrees");
339int main(
int argc,
char* argv[])
341 bool verbose =
false;
342 for (
int i = 1; i < argc; ++i)
344 if (argv[i] && argv[i][0] ==
'-' && argv[i][1] ==
'v' && argv[i][2] ==
'\0')
348 dccl::dlog.
connect(verbose ? dccl::logger::ALL :
dccl::logger::WARN_PLUS, &std::cerr);
350 test_raw_buffer_encode();
351 test_load_and_unload();
353 test_encode_errors();
356 test_library_loading();
358 test_id_from_bytes();
360 std::cout << checks <<
" checks passed" << std::endl;
The Dynamic CCL enCODer/DECoder. This is the main class you will use to load, encode and decode DCCL ...
Exception class for DCCL.
void connect(int verbosity_mask, Slot slot)
Connect the output of one or more given verbosities to a slot (function pointer or similar)
Dynamic Compact Control Language namespace.