Line data Source code
1 : // Copyright (c) 2011-2025 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snapdev
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 : /** \file
20 : * \brief Verify the BRS functions.
21 : *
22 : * This file implements tests to verify that the BRS functions do what
23 : * they are expected to do.
24 : */
25 :
26 : // snapdev
27 : //
28 : #include <snapdev/brs.h>
29 :
30 : #include <snapdev/timespec_ex.h>
31 :
32 :
33 : // self
34 : //
35 : #include "catch_main.h"
36 :
37 :
38 : // C++
39 : //
40 : #include <fstream>
41 :
42 :
43 :
44 :
45 1 : CATCH_TEST_CASE("brs_bitfield_size", "[serialization][math]")
46 : {
47 1 : CATCH_START_SECTION("brs_bitfield_size: bitfield_size")
48 : {
49 1 : constexpr std::size_t const sizeof_type(SIZEOF_BITFIELD(snapdev::hunk_sizes_t, f_type));
50 1 : CATCH_REQUIRE(sizeof_type == 2);
51 :
52 1 : constexpr std::size_t const sizeof_name(SIZEOF_BITFIELD(snapdev::hunk_sizes_t, f_name));
53 1 : CATCH_REQUIRE(sizeof_name == 7);
54 :
55 1 : constexpr std::size_t const sizeof_hunk(SIZEOF_BITFIELD(snapdev::hunk_sizes_t, f_hunk));
56 1 : CATCH_REQUIRE(sizeof_hunk == 23);
57 : }
58 1 : CATCH_END_SECTION()
59 1 : }
60 :
61 :
62 14 : CATCH_TEST_CASE("brs_basic_types", "[serialization]")
63 : {
64 14 : CATCH_START_SECTION("brs_basic_types: push/restore char")
65 : {
66 1 : std::stringstream buffer;
67 1 : snapdev::serializer out(buffer);
68 :
69 1 : std::string data(buffer.str());
70 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
71 :
72 1 : CATCH_REQUIRE(data[0] == 'B');
73 1 : CATCH_REQUIRE(data[1] == 'R');
74 1 : CATCH_REQUIRE(data[2] == 'L');
75 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
76 :
77 1 : char value = 33;
78 3 : out.add_value("orange", value);
79 :
80 5 : CATCH_REQUIRE_THROWS_MATCHES(
81 : out.add_value(std::string(), &value, sizeof(value))
82 : , snapdev::brs_cannot_be_empty
83 : , Catch::Matchers::ExceptionMessage(
84 : "brs_error: name cannot be an empty string."));
85 :
86 : // make sure it did not get smashed
87 1 : data = buffer.str();
88 1 : CATCH_REQUIRE(data[0] == 'B');
89 1 : CATCH_REQUIRE(data[1] == 'R');
90 1 : CATCH_REQUIRE(data[2] == 'L');
91 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
92 :
93 1 : CATCH_REQUIRE(data[4] == 6 << 2); // hunk_sizes_t
94 1 : CATCH_REQUIRE(data[5] == 1 << 1);
95 1 : CATCH_REQUIRE(data[6] == 0);
96 1 : CATCH_REQUIRE(data[7] == 0);
97 :
98 1 : CATCH_REQUIRE(data[8] == 'o'); // name
99 1 : CATCH_REQUIRE(data[9] == 'r');
100 1 : CATCH_REQUIRE(data[10] == 'a');
101 1 : CATCH_REQUIRE(data[11] == 'n');
102 1 : CATCH_REQUIRE(data[12] == 'g');
103 1 : CATCH_REQUIRE(data[13] == 'e');
104 :
105 1 : CATCH_REQUIRE(data[14] == 33); // value
106 :
107 : struct processor
108 : {
109 1 : static bool process_hunk(
110 : snapdev::deserializer<std::stringstream> & in
111 : , snapdev::field_t const & field)
112 : {
113 1 : CATCH_REQUIRE(field.f_name == "orange");
114 1 : CATCH_REQUIRE(field.f_sub_name.empty());
115 1 : CATCH_REQUIRE(field.f_index == -1);
116 1 : CATCH_REQUIRE(field.f_size == 1);
117 1 : char c;
118 1 : in.read_data(c);
119 1 : CATCH_REQUIRE(c == 33);
120 1 : return true;
121 : }
122 : };
123 :
124 : // Note: you don't usually end up re-using the serializer buffer
125 : // but here it's practical
126 : //
127 1 : buffer.clear();
128 :
129 1 : snapdev::deserializer in(buffer);
130 :
131 : // WARNING: we want to use CATCH_...() macros inside the callback
132 : // so make sure not to use one around unserialize_buffer().
133 : //
134 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
135 2 : &processor::process_hunk
136 : , std::placeholders::_1
137 2 : , std::placeholders::_2));
138 1 : bool const r(in.deserialize(func));
139 1 : CATCH_REQUIRE(r);
140 1 : }
141 14 : CATCH_END_SECTION()
142 :
143 14 : CATCH_START_SECTION("brs_basic_types: push/restore signed char")
144 : {
145 1 : std::stringstream buffer;
146 1 : snapdev::serializer out(buffer);
147 :
148 1 : std::string data(buffer.str());
149 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
150 :
151 1 : CATCH_REQUIRE(data[0] == 'B');
152 1 : CATCH_REQUIRE(data[1] == 'R');
153 1 : CATCH_REQUIRE(data[2] == 'L');
154 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
155 :
156 1 : signed char value = -43;
157 3 : out.add_value("orange", value);
158 :
159 : // make sure it did not get smashed
160 1 : data = buffer.str();
161 1 : CATCH_REQUIRE(data[0] == 'B');
162 1 : CATCH_REQUIRE(data[1] == 'R');
163 1 : CATCH_REQUIRE(data[2] == 'L');
164 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
165 :
166 1 : CATCH_REQUIRE(data[4] == 6 << 2); // hunk_sizes_t
167 1 : CATCH_REQUIRE(data[5] == 1 << 1);
168 1 : CATCH_REQUIRE(data[6] == 0);
169 1 : CATCH_REQUIRE(data[7] == 0);
170 :
171 1 : CATCH_REQUIRE(data[8] == 'o'); // name
172 1 : CATCH_REQUIRE(data[9] == 'r');
173 1 : CATCH_REQUIRE(data[10] == 'a');
174 1 : CATCH_REQUIRE(data[11] == 'n');
175 1 : CATCH_REQUIRE(data[12] == 'g');
176 1 : CATCH_REQUIRE(data[13] == 'e');
177 :
178 1 : CATCH_REQUIRE(static_cast<signed char>(data[14]) == -43); // value
179 :
180 : struct processor
181 : {
182 1 : static bool process_hunk(
183 : snapdev::deserializer<std::stringstream> & in
184 : , snapdev::field_t const & field)
185 : {
186 1 : CATCH_REQUIRE(field.f_name == "orange");
187 1 : CATCH_REQUIRE(field.f_sub_name.empty());
188 1 : CATCH_REQUIRE(field.f_index == -1);
189 1 : CATCH_REQUIRE(field.f_size == 1);
190 1 : signed char c;
191 1 : in.read_data(c);
192 1 : CATCH_REQUIRE(c == -43);
193 1 : return true;
194 : }
195 : };
196 :
197 : // Note: you don't usually end up re-using the serializer buffer
198 : // but here it's practical
199 : //
200 1 : buffer.clear();
201 :
202 1 : snapdev::deserializer in(buffer);
203 :
204 : // WARNING: we want to use CATCH_...() macros inside the callback
205 : // so make sure not to use one around unserialize_buffer().
206 : //
207 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
208 2 : &processor::process_hunk
209 : , std::placeholders::_1
210 2 : , std::placeholders::_2));
211 1 : bool const r(in.deserialize(func));
212 1 : CATCH_REQUIRE(r);
213 1 : }
214 14 : CATCH_END_SECTION()
215 :
216 14 : CATCH_START_SECTION("brs_basic_types: push/restore unsigned char")
217 : {
218 1 : std::stringstream buffer;
219 1 : snapdev::serializer out(buffer);
220 :
221 1 : std::string data(buffer.str());
222 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
223 :
224 1 : CATCH_REQUIRE(data[0] == 'B');
225 1 : CATCH_REQUIRE(data[1] == 'R');
226 1 : CATCH_REQUIRE(data[2] == 'L');
227 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
228 :
229 1 : unsigned char value = 200;
230 3 : out.add_value("orange", value);
231 :
232 : // make sure it did not get smashed
233 1 : data = buffer.str();
234 1 : CATCH_REQUIRE(data[0] == 'B');
235 1 : CATCH_REQUIRE(data[1] == 'R');
236 1 : CATCH_REQUIRE(data[2] == 'L');
237 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
238 :
239 1 : CATCH_REQUIRE(data[4] == 6 << 2); // hunk_sizes_t
240 1 : CATCH_REQUIRE(data[5] == 1 << 1);
241 1 : CATCH_REQUIRE(data[6] == 0);
242 1 : CATCH_REQUIRE(data[7] == 0);
243 :
244 1 : CATCH_REQUIRE(data[8] == 'o'); // name
245 1 : CATCH_REQUIRE(data[9] == 'r');
246 1 : CATCH_REQUIRE(data[10] == 'a');
247 1 : CATCH_REQUIRE(data[11] == 'n');
248 1 : CATCH_REQUIRE(data[12] == 'g');
249 1 : CATCH_REQUIRE(data[13] == 'e');
250 :
251 1 : CATCH_REQUIRE(static_cast<unsigned char>(data[14]) == 200); // value
252 :
253 : struct processor
254 : {
255 1 : static bool process_hunk(
256 : snapdev::deserializer<std::stringstream> & in
257 : , snapdev::field_t const & field)
258 : {
259 1 : CATCH_REQUIRE(field.f_name == "orange");
260 1 : CATCH_REQUIRE(field.f_sub_name.empty());
261 1 : CATCH_REQUIRE(field.f_index == -1);
262 1 : CATCH_REQUIRE(field.f_size == 1);
263 1 : unsigned char c;
264 1 : in.read_data(c);
265 1 : CATCH_REQUIRE(c == 200);
266 1 : return true;
267 : }
268 : };
269 :
270 : // Note: you don't usually end up re-using the serializer buffer
271 : // but here it's practical
272 : //
273 1 : buffer.clear();
274 :
275 1 : snapdev::deserializer in(buffer);
276 :
277 : // WARNING: we want to use CATCH_...() macros inside the callback
278 : // so make sure not to use one around unserialize_buffer().
279 : //
280 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
281 2 : &processor::process_hunk
282 : , std::placeholders::_1
283 2 : , std::placeholders::_2));
284 1 : bool const r(in.deserialize(func));
285 1 : CATCH_REQUIRE(r);
286 1 : }
287 14 : CATCH_END_SECTION()
288 :
289 14 : CATCH_START_SECTION("brs_basic_types: push/restore shorts (16 bits)")
290 : {
291 1 : std::stringstream buffer;
292 1 : snapdev::serializer out(buffer);
293 :
294 1 : std::string data(buffer.str());
295 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
296 :
297 1 : CATCH_REQUIRE(data[0] == 'B');
298 1 : CATCH_REQUIRE(data[1] == 'R');
299 1 : CATCH_REQUIRE(data[2] == 'L');
300 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
301 :
302 1 : std::int16_t const purple = 3003;
303 3 : out.add_value("purple", purple);
304 :
305 1 : std::uint16_t const black = 65001;
306 3 : out.add_value("black", black);
307 :
308 : // make sure it did not get smashed
309 1 : data = buffer.str();
310 1 : CATCH_REQUIRE(data[0] == 'B');
311 1 : CATCH_REQUIRE(data[1] == 'R');
312 1 : CATCH_REQUIRE(data[2] == 'L');
313 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
314 :
315 1 : CATCH_REQUIRE(data[4] == 6 << 2); // hunk_sizes_t
316 1 : CATCH_REQUIRE(data[5] == 2 << 1);
317 1 : CATCH_REQUIRE(data[6] == 0);
318 1 : CATCH_REQUIRE(data[7] == 0);
319 :
320 1 : CATCH_REQUIRE(data[8] == 'p'); // name
321 1 : CATCH_REQUIRE(data[9] == 'u');
322 1 : CATCH_REQUIRE(data[10] == 'r');
323 1 : CATCH_REQUIRE(data[11] == 'p');
324 1 : CATCH_REQUIRE(data[12] == 'l');
325 1 : CATCH_REQUIRE(data[13] == 'e');
326 :
327 : #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
328 : CATCH_REQUIRE(static_cast<std::uint8_t>(data[14]) * 256 + static_cast<std::uint8_t>(data[15]) == 3003); // value
329 : #else
330 1 : CATCH_REQUIRE(static_cast<std::uint8_t>(data[14]) + static_cast<std::uint8_t>(data[15]) * 256 == 3003); // value
331 : #endif
332 :
333 : struct processor
334 : {
335 2 : static bool process_hunk(
336 : snapdev::deserializer<std::stringstream> & in
337 : , snapdev::field_t const & field
338 : , std::int16_t purple
339 : , std::uint16_t black)
340 : {
341 2 : CATCH_REQUIRE(field.f_sub_name.empty());
342 2 : CATCH_REQUIRE(field.f_index == -1);
343 2 : CATCH_REQUIRE(field.f_size == 2);
344 2 : if(field.f_name == "purple")
345 : {
346 1 : std::int16_t value;
347 1 : in.read_data(value);
348 1 : CATCH_REQUIRE(value == purple);
349 : }
350 1 : else if(field.f_name == "black")
351 : {
352 1 : std::uint16_t value;
353 1 : in.read_data(value);
354 1 : CATCH_REQUIRE(value == black);
355 : }
356 : else
357 : {
358 0 : CATCH_REQUIRE(field.f_name == "?unknown?");
359 : }
360 2 : return true;
361 : }
362 : };
363 :
364 : // Note: you don't usually end up re-using the serializer buffer
365 : // but here it's practical
366 : //
367 1 : buffer.clear();
368 :
369 1 : snapdev::deserializer in(buffer);
370 :
371 : // WARNING: we want to use CATCH_...() macros inside the callback
372 : // so make sure not to use one around unserialize_buffer().
373 : //
374 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
375 2 : &processor::process_hunk
376 : , std::placeholders::_1
377 : , std::placeholders::_2
378 : , purple
379 2 : , black));
380 1 : bool const r(in.deserialize(func));
381 1 : CATCH_REQUIRE(r);
382 1 : }
383 14 : CATCH_END_SECTION()
384 :
385 14 : CATCH_START_SECTION("brs_basic_types: push/restore ints (32 bits)")
386 : {
387 1 : std::stringstream buffer;
388 1 : snapdev::serializer out(buffer);
389 :
390 1 : std::string data(buffer.str());
391 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
392 :
393 1 : CATCH_REQUIRE(data[0] == 'B');
394 1 : CATCH_REQUIRE(data[1] == 'R');
395 1 : CATCH_REQUIRE(data[2] == 'L');
396 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
397 :
398 1 : std::int32_t const red = static_cast<std::int32_t>(SNAP_CATCH2_NAMESPACE::rand_int64());
399 3 : out.add_value("red", red);
400 :
401 1 : std::uint32_t const blue = static_cast<std::int32_t>(SNAP_CATCH2_NAMESPACE::rand_int64());
402 3 : out.add_value("blue", blue);
403 :
404 : // make sure it did not get smashed
405 1 : data = buffer.str();
406 1 : CATCH_REQUIRE(data[0] == 'B');
407 1 : CATCH_REQUIRE(data[1] == 'R');
408 1 : CATCH_REQUIRE(data[2] == 'L');
409 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
410 :
411 1 : CATCH_REQUIRE(data[4] == 3 << 2); // hunk_sizes_t
412 1 : CATCH_REQUIRE(data[5] == 4 << 1);
413 1 : CATCH_REQUIRE(data[6] == 0);
414 1 : CATCH_REQUIRE(data[7] == 0);
415 :
416 1 : CATCH_REQUIRE(data[8] == 'r'); // name
417 1 : CATCH_REQUIRE(data[9] == 'e');
418 1 : CATCH_REQUIRE(data[10] == 'd');
419 :
420 : #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
421 : CATCH_REQUIRE(
422 : static_cast<std::uint8_t>(data[11]) * 0x1000000
423 : + static_cast<std::uint8_t>(data[12]) * 0x10000
424 : + static_cast<std::uint8_t>(data[13]) * 0x100
425 : + static_cast<std::uint8_t>(data[14]) * 0x1 == red); // value
426 : #else
427 1 : CATCH_REQUIRE(
428 : static_cast<std::uint8_t>(data[14]) * 0x1000000
429 : + static_cast<std::uint8_t>(data[13]) * 0x10000
430 : + static_cast<std::uint8_t>(data[12]) * 0x100
431 : + static_cast<std::uint8_t>(data[11]) * 0x1 == red); // value
432 : #endif
433 :
434 : struct processor
435 : {
436 2 : static bool process_hunk(
437 : snapdev::deserializer<std::stringstream> & in
438 : , snapdev::field_t const & field
439 : , std::int32_t red
440 : , std::uint32_t blue)
441 : {
442 2 : CATCH_REQUIRE(field.f_sub_name.empty());
443 2 : CATCH_REQUIRE(field.f_index == -1);
444 2 : CATCH_REQUIRE(field.f_size == 4);
445 2 : if(field.f_name == "red")
446 : {
447 1 : std::int32_t value;
448 1 : in.read_data(value);
449 1 : CATCH_REQUIRE(value == red);
450 : }
451 1 : else if(field.f_name == "blue")
452 : {
453 1 : std::uint32_t value;
454 1 : in.read_data(value);
455 1 : CATCH_REQUIRE(value == blue);
456 : }
457 : else
458 : {
459 0 : CATCH_REQUIRE(field.f_name == "?unknown?");
460 : }
461 2 : return true;
462 : }
463 : };
464 :
465 : // Note: you don't usually end up re-using the serializer buffer
466 : // but here it's practical
467 : //
468 1 : buffer.clear();
469 :
470 1 : snapdev::deserializer in(buffer);
471 :
472 : // WARNING: we want to use CATCH_...() macros inside the callback
473 : // so make sure not to use one around unserialize_buffer().
474 : //
475 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
476 2 : &processor::process_hunk
477 : , std::placeholders::_1
478 : , std::placeholders::_2
479 : , red
480 2 : , blue));
481 1 : bool const r(in.deserialize(func));
482 1 : CATCH_REQUIRE(r);
483 1 : }
484 14 : CATCH_END_SECTION()
485 :
486 14 : CATCH_START_SECTION("brs_basic_types: push/restore ints (64 bits)")
487 : {
488 1 : std::stringstream buffer;
489 1 : snapdev::serializer out(buffer);
490 :
491 1 : std::string data(buffer.str());
492 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
493 :
494 1 : CATCH_REQUIRE(data[0] == 'B');
495 1 : CATCH_REQUIRE(data[1] == 'R');
496 1 : CATCH_REQUIRE(data[2] == 'L');
497 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
498 :
499 1 : std::int64_t const white = static_cast<std::int32_t>(SNAP_CATCH2_NAMESPACE::rand_int64());
500 3 : out.add_value("white", white);
501 :
502 1 : std::uint64_t const gray = static_cast<std::int32_t>(SNAP_CATCH2_NAMESPACE::rand_int64());
503 3 : out.add_value("gray", gray);
504 :
505 : // make sure it did not get smashed
506 1 : data = buffer.str();
507 1 : CATCH_REQUIRE(data[0] == 'B');
508 1 : CATCH_REQUIRE(data[1] == 'R');
509 1 : CATCH_REQUIRE(data[2] == 'L');
510 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
511 :
512 1 : CATCH_REQUIRE(data[4] == 5 << 2); // hunk_sizes_t
513 1 : CATCH_REQUIRE(data[5] == 8 << 1);
514 1 : CATCH_REQUIRE(data[6] == 0);
515 1 : CATCH_REQUIRE(data[7] == 0);
516 :
517 1 : CATCH_REQUIRE(data[8] == 'w'); // name
518 1 : CATCH_REQUIRE(data[9] == 'h');
519 1 : CATCH_REQUIRE(data[10] == 'i');
520 1 : CATCH_REQUIRE(data[11] == 't');
521 1 : CATCH_REQUIRE(data[12] == 'e');
522 :
523 : struct processor
524 : {
525 2 : static bool process_hunk(
526 : snapdev::deserializer<std::stringstream> & in
527 : , snapdev::field_t const & field
528 : , std::int64_t white
529 : , std::uint64_t gray)
530 : {
531 2 : CATCH_REQUIRE(field.f_sub_name.empty());
532 2 : CATCH_REQUIRE(field.f_index == -1);
533 2 : CATCH_REQUIRE(field.f_size == 8);
534 2 : if(field.f_name == "white")
535 : {
536 1 : std::int64_t value;
537 1 : in.read_data(value);
538 1 : CATCH_REQUIRE(value == white);
539 : }
540 1 : else if(field.f_name == "gray")
541 : {
542 1 : std::uint64_t value;
543 1 : in.read_data(value);
544 1 : CATCH_REQUIRE(value == gray);
545 : }
546 : else
547 : {
548 0 : CATCH_REQUIRE(field.f_name == "?unknown?");
549 : }
550 2 : return true;
551 : }
552 : };
553 :
554 : // Note: you don't usually end up re-using the serializer buffer
555 : // but here it's practical
556 : //
557 1 : buffer.clear();
558 :
559 1 : snapdev::deserializer in(buffer);
560 :
561 : // WARNING: we want to use CATCH_...() macros inside the callback
562 : // so make sure not to use one around unserialize_buffer().
563 : //
564 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
565 2 : &processor::process_hunk
566 : , std::placeholders::_1
567 : , std::placeholders::_2
568 : , white
569 2 : , gray));
570 1 : bool const r(in.deserialize(func));
571 1 : CATCH_REQUIRE(r);
572 1 : }
573 14 : CATCH_END_SECTION()
574 :
575 14 : CATCH_START_SECTION("brs_basic_types: push/restore floats")
576 : {
577 1 : std::stringstream buffer;
578 1 : snapdev::serializer out(buffer);
579 :
580 1 : std::string data(buffer.str());
581 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
582 :
583 1 : CATCH_REQUIRE(data[0] == 'B');
584 1 : CATCH_REQUIRE(data[1] == 'R');
585 1 : CATCH_REQUIRE(data[2] == 'L');
586 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
587 :
588 1 : float const green = static_cast<float>(SNAP_CATCH2_NAMESPACE::rand_int64())
589 1 : / static_cast<float>(SNAP_CATCH2_NAMESPACE::rand_int64());
590 3 : out.add_value("green", green);
591 :
592 1 : double const yellow = static_cast<float>(SNAP_CATCH2_NAMESPACE::rand_int64())
593 1 : / static_cast<float>(SNAP_CATCH2_NAMESPACE::rand_int64());
594 3 : out.add_value("yellow", yellow);
595 :
596 1 : long double const fushia = static_cast<float>(SNAP_CATCH2_NAMESPACE::rand_int64())
597 1 : / static_cast<float>(SNAP_CATCH2_NAMESPACE::rand_int64());
598 3 : out.add_value("fushia", fushia);
599 :
600 : // make sure it did not get smashed
601 1 : data = buffer.str();
602 1 : CATCH_REQUIRE(data[0] == 'B');
603 1 : CATCH_REQUIRE(data[1] == 'R');
604 1 : CATCH_REQUIRE(data[2] == 'L');
605 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
606 :
607 1 : CATCH_REQUIRE(data[4] == 5 << 2); // hunk_sizes_t
608 1 : CATCH_REQUIRE(data[5] == 4 << 1);
609 1 : CATCH_REQUIRE(data[6] == 0);
610 1 : CATCH_REQUIRE(data[7] == 0);
611 :
612 1 : CATCH_REQUIRE(data[8] == 'g'); // name
613 1 : CATCH_REQUIRE(data[9] == 'r');
614 1 : CATCH_REQUIRE(data[10] == 'e');
615 1 : CATCH_REQUIRE(data[11] == 'e');
616 1 : CATCH_REQUIRE(data[12] == 'n');
617 :
618 : struct processor
619 : {
620 3 : static bool process_hunk(
621 : snapdev::deserializer<std::stringstream> & in
622 : , snapdev::field_t const & field
623 : , float green
624 : , double yellow
625 : , long double fushia)
626 : {
627 3 : CATCH_REQUIRE(field.f_sub_name.empty());
628 3 : CATCH_REQUIRE(field.f_index == -1);
629 3 : if(field.f_name == "green")
630 : {
631 1 : CATCH_REQUIRE(field.f_size == 4);
632 :
633 1 : float value;
634 1 : in.read_data(value);
635 1 : CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(value, green, 0.0f));
636 : }
637 2 : else if(field.f_name == "yellow")
638 : {
639 1 : CATCH_REQUIRE(field.f_size == 8);
640 :
641 1 : double value;
642 1 : in.read_data(value);
643 1 : CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(value, yellow, 0.0));
644 : }
645 1 : else if(field.f_name == "fushia")
646 : {
647 1 : CATCH_REQUIRE(field.f_size == 16);
648 :
649 1 : long double value;
650 1 : in.read_data(value);
651 1 : CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(value, fushia, 0.0L));
652 : }
653 : else
654 : {
655 0 : CATCH_REQUIRE(field.f_name == "?unknown?");
656 : }
657 3 : return true;
658 : }
659 : };
660 :
661 : // Note: you don't usually end up re-using the serializer buffer
662 : // but here it's practical
663 : //
664 1 : buffer.clear();
665 :
666 1 : snapdev::deserializer in(buffer);
667 :
668 : // WARNING: we want to use CATCH_...() macros inside the callback
669 : // so make sure not to use one around unserialize_buffer().
670 : //
671 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
672 2 : &processor::process_hunk
673 : , std::placeholders::_1
674 : , std::placeholders::_2
675 : , green
676 : , yellow
677 2 : , fushia));
678 1 : bool const r(in.deserialize(func));
679 1 : CATCH_REQUIRE(r);
680 1 : }
681 14 : CATCH_END_SECTION()
682 :
683 14 : CATCH_START_SECTION("brs_basic_types: push/restore string")
684 : {
685 1 : std::stringstream buffer;
686 1 : snapdev::serializer out(buffer);
687 :
688 1 : std::string data(buffer.str());
689 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
690 :
691 1 : CATCH_REQUIRE(data[0] == 'B');
692 1 : CATCH_REQUIRE(data[1] == 'R');
693 1 : CATCH_REQUIRE(data[2] == 'L');
694 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
695 :
696 3 : std::string const message("this is the message we are going to serialize");
697 3 : out.add_value("message", message);
698 :
699 : // make sure it did not get smashed
700 1 : data = buffer.str();
701 1 : CATCH_REQUIRE(data[0] == 'B');
702 1 : CATCH_REQUIRE(data[1] == 'R');
703 1 : CATCH_REQUIRE(data[2] == 'L');
704 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
705 :
706 1 : CATCH_REQUIRE(data[4] == 7 << 2); // hunk_sizes_t
707 1 : CATCH_REQUIRE(data[5] == 45 << 1);
708 1 : CATCH_REQUIRE(data[6] == 0);
709 1 : CATCH_REQUIRE(data[7] == 0);
710 :
711 1 : CATCH_REQUIRE(data[8] == 'm'); // name
712 1 : CATCH_REQUIRE(data[9] == 'e');
713 1 : CATCH_REQUIRE(data[10] == 's');
714 1 : CATCH_REQUIRE(data[11] == 's');
715 1 : CATCH_REQUIRE(data[12] == 'a');
716 1 : CATCH_REQUIRE(data[13] == 'g');
717 1 : CATCH_REQUIRE(data[14] == 'e');
718 :
719 : struct processor
720 : {
721 1 : static bool process_hunk(
722 : snapdev::deserializer<std::stringstream> & in
723 : , snapdev::field_t const & field
724 : , std::string message)
725 : {
726 1 : CATCH_REQUIRE(field.f_name == "message");
727 1 : CATCH_REQUIRE(field.f_sub_name.empty());
728 1 : CATCH_REQUIRE(field.f_index == -1);
729 1 : CATCH_REQUIRE(field.f_size == 45);
730 :
731 1 : std::string value;
732 1 : in.read_data(value);
733 1 : CATCH_REQUIRE(value == message);
734 :
735 1 : return true;
736 1 : }
737 : };
738 :
739 : // Note: you don't usually end up re-using the serializer buffer
740 : // but here it's practical
741 : //
742 1 : buffer.clear();
743 :
744 1 : snapdev::deserializer in(buffer);
745 :
746 : // WARNING: we want to use CATCH_...() macros inside the callback
747 : // so make sure not to use one around unserialize_buffer().
748 : //
749 4 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
750 2 : &processor::process_hunk
751 : , std::placeholders::_1
752 : , std::placeholders::_2
753 2 : , message));
754 1 : bool const r(in.deserialize(func));
755 1 : CATCH_REQUIRE(r);
756 1 : }
757 14 : CATCH_END_SECTION()
758 :
759 14 : CATCH_START_SECTION("brs_basic_types: push/restore timespec")
760 : {
761 1 : std::stringstream buffer;
762 1 : snapdev::serializer out(buffer);
763 :
764 1 : timespec const now(snapdev::now());
765 1 : std::string data(buffer.str());
766 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
767 :
768 1 : CATCH_REQUIRE(data[0] == 'B');
769 1 : CATCH_REQUIRE(data[1] == 'R');
770 1 : CATCH_REQUIRE(data[2] == 'L');
771 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
772 :
773 1 : timespec bad_time(now);
774 4 : while(bad_time.tv_nsec < 1'000'000'000)
775 : {
776 3 : SNAP_CATCH2_NAMESPACE::random(bad_time.tv_nsec);
777 : }
778 5 : CATCH_REQUIRE_THROWS_MATCHES(
779 : out.add_value("now", bad_time)
780 : , snapdev::brs_invalid_value
781 : , Catch::Matchers::ExceptionMessage(
782 : "brs_error: nanoseconds are out of range: "
783 : + std::to_string(bad_time.tv_nsec)
784 : + "."));
785 :
786 3 : out.add_value("now", now);
787 :
788 : // make sure the header did not get smashed
789 : //
790 1 : data = buffer.str();
791 1 : CATCH_REQUIRE(data[0] == 'B');
792 1 : CATCH_REQUIRE(data[1] == 'R');
793 1 : CATCH_REQUIRE(data[2] == 'L');
794 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
795 :
796 1 : CATCH_REQUIRE(data[4] == 3 << 2); // hunk_sizes_t
797 1 : CATCH_REQUIRE(data[5] == (sizeof(now.tv_sec) + sizeof(std::uint32_t)) << 1);
798 1 : CATCH_REQUIRE(data[6] == 0);
799 1 : CATCH_REQUIRE(data[7] == 0);
800 :
801 1 : CATCH_REQUIRE(data[8] == 'n'); // name
802 1 : CATCH_REQUIRE(data[9] == 'o');
803 1 : CATCH_REQUIRE(data[10] == 'w');
804 :
805 : struct processor
806 : {
807 5 : static bool process_hunk(
808 : snapdev::deserializer<std::stringstream> & in
809 : , snapdev::field_t const & field
810 : , timespec const & now
811 : , bool & called_flag)
812 : {
813 5 : CATCH_REQUIRE(field.f_name == "now");
814 5 : CATCH_REQUIRE(field.f_sub_name.empty());
815 5 : CATCH_REQUIRE(field.f_index == -1);
816 5 : CATCH_REQUIRE(field.f_size == sizeof(now.tv_sec) + sizeof(std::uint32_t));
817 :
818 5 : timespec value;
819 5 : CATCH_REQUIRE(in.read_data(value));
820 5 : bool const result(memcmp(&value, &now, sizeof(now)) == 0);
821 5 : if(!result)
822 : {
823 : std::cerr << "timespecs not equal: "
824 : << value
825 : << " (read back) != "
826 : << now
827 0 : << " (expected)\n";
828 : }
829 5 : CATCH_REQUIRE(result);
830 :
831 5 : called_flag = true;
832 :
833 5 : return true;
834 : }
835 : };
836 :
837 6 : for(int count(0); count < 5; ++count)
838 : {
839 : // Note: you don't usually end up re-using the serializer buffer
840 : // but here it's practical; warning: this works because in
841 : // a C++ stream the seek position is distinct between the
842 : // write() and read() functions (tellp() vs tellg())
843 : //
844 5 : buffer.clear();
845 5 : if(count != 0)
846 : {
847 : // the second time and further need a seek
848 : //
849 4 : buffer.seekg(0, std::ios_base::beg);
850 : }
851 :
852 5 : snapdev::deserializer in(buffer);
853 :
854 : // WARNING: we want to use CATCH_...() macros inside the callback
855 : // so make sure not to use one around unserialize_buffer().
856 : //
857 5 : bool process_hunk_called(false);
858 20 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
859 10 : &processor::process_hunk
860 : , std::placeholders::_1
861 : , std::placeholders::_2
862 : , now
863 20 : , std::ref(process_hunk_called)));
864 5 : bool const r(in.deserialize(func));
865 5 : CATCH_REQUIRE(r);
866 5 : CATCH_REQUIRE(process_hunk_called);
867 5 : }
868 :
869 : {
870 : struct any_size_processor
871 : {
872 1 : static bool any_size_process_hunk(
873 : snapdev::deserializer<std::stringstream> & in
874 : , snapdev::field_t const & field
875 : , timespec const & now
876 : , bool & called_flag)
877 : {
878 1 : CATCH_REQUIRE(field.f_name == "now");
879 1 : CATCH_REQUIRE(field.f_sub_name.empty());
880 1 : CATCH_REQUIRE(field.f_index == -1);
881 : //CATCH_REQUIRE(field.f_size == sizeof(now.tv_sec) + sizeof(std::uint32_t));
882 :
883 1 : timespec value;
884 1 : in.read_data(value); // no CATCH_...() here we're expecting a throw;
885 0 : bool const result(memcmp(&value, &now, sizeof(now)) == 0);
886 0 : if(!result)
887 : {
888 : std::cerr << "timespecs not equal: "
889 : << value
890 : << " (read back) != "
891 : << now
892 0 : << " (expected)\n";
893 : }
894 0 : CATCH_REQUIRE(result);
895 :
896 0 : called_flag = true;
897 :
898 0 : return true;
899 : }
900 : };
901 :
902 : // re-re-using
903 : //
904 1 : buffer.clear();
905 1 : buffer.seekg(0, std::ios_base::beg);
906 :
907 1 : snapdev::deserializer in(buffer);
908 :
909 : // the buffer should not be modified while deserializing,
910 : // but this is a test and we want to see that destroying
911 : // something in the buffer makes the deserialization fail
912 : //
913 1 : buffer.seekp(5, std::ios_base::beg);
914 1 : char const v((sizeof(now.tv_sec) + sizeof(std::uint32_t) - 1) << 1);
915 1 : buffer.write(&v, 1);
916 :
917 : // WARNING: we want to use CATCH_...() macros inside the callback
918 : // so make sure not to use one around deserialize().
919 : //
920 1 : bool process_hunk_called(false);
921 4 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
922 2 : &any_size_processor::any_size_process_hunk
923 : , std::placeholders::_1
924 : , std::placeholders::_2
925 : , now
926 4 : , std::ref(process_hunk_called)));
927 :
928 3 : CATCH_REQUIRE_THROWS_MATCHES(
929 : in.deserialize(func)
930 : , snapdev::brs_logic_error
931 : , Catch::Matchers::ExceptionMessage(
932 : "brs_logic_error: hunk size is 11, but you are trying to read 12."));
933 :
934 : // the read must happened before we set this variable to
935 : // true so if it all works as expected, this is still false
936 : //
937 1 : CATCH_REQUIRE_FALSE(process_hunk_called);
938 :
939 : // test passed, fix the size for the next tests
940 : //
941 1 : buffer.seekp(5, std::ios_base::beg);
942 1 : char const c((sizeof(now.tv_sec) + sizeof(std::uint32_t)) << 1);
943 1 : buffer.write(&c, 1);
944 1 : }
945 :
946 : {
947 : struct any_size_processor
948 : {
949 1 : static bool any_size_process_hunk(
950 : snapdev::deserializer<std::stringstream> & in
951 : , snapdev::field_t const & field
952 : , timespec const & now
953 : , bool & called_flag)
954 : {
955 1 : CATCH_REQUIRE(field.f_name == "now");
956 1 : CATCH_REQUIRE(field.f_sub_name.empty());
957 1 : CATCH_REQUIRE(field.f_index == -1);
958 : //CATCH_REQUIRE(field.f_size == sizeof(now.tv_sec) + sizeof(std::uint32_t));
959 :
960 1 : timespec value;
961 1 : in.read_data(value); // no CATCH_...() here we're expecting a throw;
962 0 : bool const result(memcmp(&value, &now, sizeof(now)) == 0);
963 0 : if(!result)
964 : {
965 : std::cerr << "timespecs not equal: "
966 : << value
967 : << " (read back) != "
968 : << now
969 0 : << " (expected)\n";
970 : }
971 0 : CATCH_REQUIRE(result);
972 :
973 0 : called_flag = true;
974 :
975 0 : return true;
976 : }
977 : };
978 :
979 : // re-re-using
980 : //
981 1 : buffer.clear();
982 1 : buffer.seekg(0, std::ios_base::beg);
983 :
984 1 : snapdev::deserializer in(buffer);
985 :
986 : // the buffer should not be modified while deserializing,
987 : // but this is a test and we want to see that destroying
988 : // something in the buffer makes the deserialization fail
989 : //
990 1 : std::uint32_t bad_nsec(0);
991 2 : while(bad_nsec < 1'000'000'000)
992 : {
993 1 : SNAP_CATCH2_NAMESPACE::random(bad_nsec);
994 : }
995 1 : buffer.seekp(19, std::ios_base::beg);
996 1 : buffer.write(reinterpret_cast<char const *>(&bad_nsec), sizeof(std::uint32_t));
997 :
998 : // WARNING: we want to use CATCH_...() macros inside the callback
999 : // so make sure not to use one around deserialize().
1000 : //
1001 1 : bool process_hunk_called(false);
1002 4 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
1003 2 : &any_size_processor::any_size_process_hunk
1004 : , std::placeholders::_1
1005 : , std::placeholders::_2
1006 : , now
1007 4 : , std::ref(process_hunk_called)));
1008 :
1009 1 : CATCH_REQUIRE_THROWS_MATCHES(
1010 : in.deserialize(func)
1011 : , snapdev::brs_invalid_value
1012 : , Catch::Matchers::ExceptionMessage(
1013 : "brs_error: nanoseconds are out of range: "
1014 : + std::to_string(bad_nsec)
1015 : + "."));
1016 :
1017 : // the read must happened before we set this variable to
1018 : // true so if it all works as expected, this is still false
1019 : //
1020 1 : CATCH_REQUIRE_FALSE(process_hunk_called);
1021 :
1022 : // test passed, fix tv_nsec for the next tests
1023 : //
1024 1 : buffer.seekp(19, std::ios_base::beg);
1025 1 : std::uint32_t const good_nsec(now.tv_nsec);
1026 1 : buffer.write(reinterpret_cast<char const *>(&good_nsec), sizeof(std::uint32_t));
1027 1 : }
1028 :
1029 1 : std::string short_value(data);
1030 13 : for(std::size_t size(data.length() - 1); size >= data.length() - sizeof(now.tv_sec) - sizeof(std::uint32_t); --size)
1031 : {
1032 : struct short_processor
1033 : {
1034 12 : static bool short_process_hunk(
1035 : snapdev::deserializer<std::stringstream> & in
1036 : , snapdev::field_t const & field
1037 : , bool & called_flag)
1038 : {
1039 12 : CATCH_REQUIRE(field.f_name == "now");
1040 12 : CATCH_REQUIRE(field.f_sub_name.empty());
1041 12 : CATCH_REQUIRE(field.f_index == -1);
1042 : //CATCH_REQUIRE(field.f_size == sizeof(now.tv_sec) + sizeof(std::uint32_t));
1043 :
1044 12 : timespec value;
1045 12 : CATCH_REQUIRE_FALSE(in.read_data(value));
1046 :
1047 12 : called_flag = true;
1048 :
1049 12 : return false;
1050 : }
1051 : };
1052 :
1053 : // re-re-using
1054 : //
1055 12 : short_value.resize(size);
1056 12 : buffer.str(short_value);
1057 12 : buffer.clear();
1058 12 : buffer.seekg(0, std::ios_base::beg);
1059 :
1060 12 : snapdev::deserializer in(buffer);
1061 :
1062 : // WARNING: we want to use CATCH_...() macros inside the callback
1063 : // so make sure not to use one around deserialize().
1064 : //
1065 12 : bool process_hunk_called(false);
1066 48 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
1067 24 : &short_processor::short_process_hunk
1068 : , std::placeholders::_1
1069 : , std::placeholders::_2
1070 48 : , std::ref(process_hunk_called)));
1071 :
1072 12 : CATCH_REQUIRE_FALSE(in.deserialize(func));
1073 :
1074 : // even if we return false, we processed the hunk
1075 : //
1076 12 : CATCH_REQUIRE(process_hunk_called);
1077 12 : }
1078 1 : }
1079 14 : CATCH_END_SECTION()
1080 :
1081 14 : CATCH_START_SECTION("brs_basic_types: push/restore array (varying name)")
1082 : {
1083 1 : std::stringstream buffer;
1084 1 : snapdev::serializer out(buffer);
1085 :
1086 1 : std::string data(buffer.str());
1087 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
1088 :
1089 1 : CATCH_REQUIRE(data[0] == 'B');
1090 1 : CATCH_REQUIRE(data[1] == 'R');
1091 1 : CATCH_REQUIRE(data[2] == 'L');
1092 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
1093 :
1094 1 : std::vector<int> order;
1095 : typedef std::map<int, std::string> value_t;
1096 1 : value_t values;
1097 1 : int index(-1);
1098 26 : for(int i(0); i < 25; ++i)
1099 : {
1100 : for(;;)
1101 : {
1102 26 : index = rand() % 256;
1103 26 : if(values.find(index) == values.end())
1104 : {
1105 25 : break;
1106 : }
1107 : }
1108 25 : std::string str;
1109 25 : int max(rand() % 25 + 1);
1110 373 : for(int j(0); j < max; ++j)
1111 : {
1112 348 : str += ' ' + rand() % ('~' + 1 - ' '); // ASCII except controls
1113 : }
1114 25 : values[index] = str;
1115 25 : order.push_back(index);
1116 :
1117 25 : out.add_value("str" + std::to_string(index), index, str);
1118 25 : }
1119 :
1120 : // make sure it did not get smashed
1121 1 : data = buffer.str();
1122 1 : CATCH_REQUIRE(data[0] == 'B');
1123 1 : CATCH_REQUIRE(data[1] == 'R');
1124 1 : CATCH_REQUIRE(data[2] == 'L');
1125 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
1126 :
1127 : struct processor
1128 : {
1129 25 : static bool process_hunk(
1130 : snapdev::deserializer<std::stringstream> & in
1131 : , snapdev::field_t const & field
1132 : , value_t & values)
1133 : {
1134 25 : CATCH_REQUIRE(field.f_sub_name.empty());
1135 25 : CATCH_REQUIRE(field.f_index != -1);
1136 :
1137 25 : std::string expected_name("str" + std::to_string(field.f_index));
1138 25 : CATCH_REQUIRE(field.f_name == expected_name);
1139 :
1140 25 : CATCH_REQUIRE(field.f_size == values[field.f_index].length());
1141 :
1142 25 : std::string value;
1143 25 : in.read_data(value);
1144 25 : CATCH_REQUIRE(value == values[field.f_index]);
1145 :
1146 25 : return true;
1147 25 : }
1148 : };
1149 :
1150 : // Note: you don't usually end up re-using the serializer buffer
1151 : // but here it's practical; warning: this works because in
1152 : // a C++ stream the seek position is distinct between the
1153 : // write() and read() functions (tellp() vs tellg())
1154 : //
1155 1 : buffer.clear();
1156 :
1157 1 : snapdev::deserializer in(buffer);
1158 :
1159 : // WARNING: we want to use CATCH_...() macros inside the callback
1160 : // so make sure not to use one around unserialize_buffer().
1161 : //
1162 4 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
1163 2 : &processor::process_hunk
1164 : , std::placeholders::_1
1165 : , std::placeholders::_2
1166 2 : , values));
1167 1 : bool const r(in.deserialize(func));
1168 1 : CATCH_REQUIRE(r);
1169 1 : }
1170 14 : CATCH_END_SECTION()
1171 :
1172 14 : CATCH_START_SECTION("brs_basic_types: push/restore array (same name)")
1173 : {
1174 1 : std::stringstream buffer;
1175 1 : snapdev::serializer out(buffer);
1176 :
1177 1 : std::string data(buffer.str());
1178 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
1179 :
1180 1 : CATCH_REQUIRE(data[0] == 'B');
1181 1 : CATCH_REQUIRE(data[1] == 'R');
1182 1 : CATCH_REQUIRE(data[2] == 'L');
1183 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
1184 :
1185 : // order does not matter and we can have gaps, to test that, create
1186 : // a map of a few values and corresponding strings
1187 : //
1188 1 : std::vector<int> order;
1189 : typedef std::map<int, std::string> value_t;
1190 1 : value_t values;
1191 1 : int index(-1);
1192 1 : int const size(25);
1193 26 : for(int i(0); i < size; ++i)
1194 : {
1195 : for(;;)
1196 : {
1197 26 : index = rand() % 256;
1198 26 : if(values.find(index) == values.end())
1199 : {
1200 25 : break;
1201 : }
1202 : }
1203 25 : std::string str;
1204 25 : int max(rand() % 25 + 1);
1205 377 : for(int j(0); j < max; ++j)
1206 : {
1207 352 : str += ' ' + rand() % ('~' + 1 - ' '); // ASCII except controls
1208 : }
1209 25 : values[index] = str;
1210 25 : order.push_back(index);
1211 :
1212 25 : if((index & 1) != 0)
1213 : {
1214 : // pass as a 'char const *'
1215 51 : out.add_value("unique", index, str.c_str());
1216 : }
1217 : else
1218 : {
1219 : // pass as an std::string
1220 24 : out.add_value("unique", index, str);
1221 : }
1222 :
1223 : // nullptr does nothing
1224 75 : out.add_value("unique", index + size, nullptr);
1225 25 : }
1226 :
1227 : // make sure it did not get smashed
1228 1 : data = buffer.str();
1229 1 : CATCH_REQUIRE(data[0] == 'B');
1230 1 : CATCH_REQUIRE(data[1] == 'R');
1231 1 : CATCH_REQUIRE(data[2] == 'L');
1232 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
1233 :
1234 : struct processor
1235 : {
1236 25 : static bool process_hunk(
1237 : snapdev::deserializer<std::stringstream> & in
1238 : , snapdev::field_t const & field
1239 : , value_t & values)
1240 : {
1241 25 : CATCH_REQUIRE(field.f_name == "unique");
1242 25 : CATCH_REQUIRE(field.f_sub_name.empty());
1243 25 : CATCH_REQUIRE(field.f_index != -1);
1244 :
1245 25 : CATCH_REQUIRE(field.f_size == values[field.f_index].length());
1246 :
1247 25 : std::string value;
1248 25 : in.read_data(value);
1249 25 : CATCH_REQUIRE(value == values[field.f_index]);
1250 :
1251 25 : return true;
1252 25 : }
1253 : };
1254 :
1255 : // Note: you don't usually end up re-using the serializer buffer
1256 : // but here it's practical; warning: this works because in
1257 : // a C++ stream the seek position is distinct between the
1258 : // write() and read() functions (tellp() vs tellg())
1259 : //
1260 1 : buffer.clear();
1261 :
1262 1 : snapdev::deserializer in(buffer);
1263 :
1264 : // WARNING: we want to use CATCH_...() macros inside the callback
1265 : // so make sure not to use one around unserialize_buffer().
1266 : //
1267 4 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
1268 2 : &processor::process_hunk
1269 : , std::placeholders::_1
1270 : , std::placeholders::_2
1271 2 : , values));
1272 1 : bool const r(in.deserialize(func));
1273 1 : CATCH_REQUIRE(r);
1274 1 : }
1275 14 : CATCH_END_SECTION()
1276 :
1277 14 : CATCH_START_SECTION("brs_basic_types: push/restore map of strings")
1278 : {
1279 1 : std::stringstream buffer;
1280 1 : snapdev::serializer out(buffer);
1281 :
1282 1 : std::string data(buffer.str());
1283 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
1284 :
1285 1 : CATCH_REQUIRE(data[0] == 'B');
1286 1 : CATCH_REQUIRE(data[1] == 'R');
1287 1 : CATCH_REQUIRE(data[2] == 'L');
1288 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
1289 :
1290 : // order does not matter and we can have gaps, to test that, create
1291 : // a map of a few values and corresponding strings
1292 : //
1293 1 : std::vector<std::string> order;
1294 : typedef std::map<std::string, std::string> value_t;
1295 1 : value_t values;
1296 1 : int empty(rand() % 25);
1297 26 : for(int i(0); i < 25; ++i)
1298 : {
1299 25 : int max(0);
1300 25 : std::string index;
1301 : for(;;)
1302 : {
1303 25 : max = rand() % 25 + 1;
1304 356 : for(int j(0); j < max; ++j)
1305 : {
1306 331 : index += ' ' + rand() % ('~' + 1 - ' '); // ASCII except controls
1307 : }
1308 25 : if(values.find(index) == values.end())
1309 : {
1310 : // index is unique, use it
1311 25 : break;
1312 : }
1313 0 : }
1314 :
1315 25 : std::string str;
1316 25 : if(i != empty)
1317 : {
1318 24 : max = rand() % 25;
1319 293 : for(int j(0); j < max; ++j)
1320 : {
1321 269 : str += ' ' + rand() % ('~' + 1 - ' '); // ASCII except controls
1322 : }
1323 : }
1324 :
1325 25 : values[index] = str;
1326 25 : order.push_back(index);
1327 :
1328 25 : if((i & 1) == 0)
1329 : {
1330 13 : if(str.empty()
1331 13 : || (rand() & 1) != 0)
1332 : {
1333 18 : out.add_value("mapping", index, str);
1334 : }
1335 : else
1336 : {
1337 21 : out.add_value_if_not_empty("mapping", index, str);
1338 : }
1339 : }
1340 : else
1341 : {
1342 : // bare string
1343 : //
1344 36 : out.add_value("mapping", index, str.c_str());
1345 : }
1346 :
1347 75 : out.add_value("mapping", index + "a", nullptr);
1348 25 : }
1349 5 : out.add_value_if_not_empty("mapping", "last-index", std::string());
1350 :
1351 : //{
1352 : //std::ofstream p("t1.txt");
1353 : //std::string s(buffer.str());
1354 : //p << s;
1355 : //}
1356 :
1357 : // make sure it did not get smashed
1358 1 : data = buffer.str();
1359 1 : CATCH_REQUIRE(data[0] == 'B');
1360 1 : CATCH_REQUIRE(data[1] == 'R');
1361 1 : CATCH_REQUIRE(data[2] == 'L');
1362 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
1363 :
1364 : struct processor
1365 : {
1366 25 : static bool process_hunk(
1367 : snapdev::deserializer<std::stringstream> & in
1368 : , snapdev::field_t const & field
1369 : , value_t & values)
1370 : {
1371 25 : CATCH_REQUIRE(field.f_name == "mapping");
1372 25 : CATCH_REQUIRE_FALSE(field.f_sub_name.empty());
1373 25 : CATCH_REQUIRE(field.f_index == -1);
1374 25 : CATCH_REQUIRE(values.find(field.f_sub_name) != values.end());
1375 :
1376 25 : CATCH_REQUIRE(field.f_size == values[field.f_sub_name].length());
1377 :
1378 25 : std::string value;
1379 25 : in.read_data(value);
1380 25 : CATCH_REQUIRE(value == values[field.f_sub_name]);
1381 :
1382 25 : return true;
1383 25 : }
1384 : };
1385 :
1386 : // Note: you don't usually end up re-using the serializer buffer
1387 : // but here it's practical; warning: this works because in
1388 : // a C++ stream the seek position is distinct between the
1389 : // write() and read() functions (tellp() vs tellg())
1390 : //
1391 1 : buffer.clear();
1392 :
1393 1 : snapdev::deserializer in(buffer);
1394 :
1395 : // WARNING: we want to use CATCH_...() macros inside the callback
1396 : // so make sure not to use one around unserialize_buffer().
1397 : //
1398 4 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
1399 2 : &processor::process_hunk
1400 : , std::placeholders::_1
1401 : , std::placeholders::_2
1402 2 : , values));
1403 1 : bool const r(in.deserialize(func));
1404 1 : CATCH_REQUIRE(r);
1405 1 : }
1406 14 : CATCH_END_SECTION()
1407 :
1408 14 : CATCH_START_SECTION("brs_basic_types: push/restore map of struct")
1409 : {
1410 : struct data_t
1411 : {
1412 : std::uint32_t f_color = 0xaa648b;
1413 : float f_height = 1.3f;
1414 : std::uint8_t f_status = 3;
1415 : double f_width = 3.7;
1416 :
1417 69 : bool operator == (data_t const & rhs) const
1418 : {
1419 : #pragma GCC diagnostic push
1420 : #pragma GCC diagnostic ignored "-Wfloat-equal"
1421 69 : return f_color == rhs.f_color
1422 69 : && f_height == rhs.f_height
1423 69 : && f_status == rhs.f_status
1424 138 : && f_width == rhs.f_width;
1425 : #pragma GCC diagnostic pop
1426 : }
1427 : };
1428 :
1429 1 : std::stringstream buffer;
1430 1 : snapdev::serializer out(buffer);
1431 :
1432 1 : std::string data(buffer.str());
1433 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
1434 :
1435 1 : CATCH_REQUIRE(data[0] == 'B');
1436 1 : CATCH_REQUIRE(data[1] == 'R');
1437 1 : CATCH_REQUIRE(data[2] == 'L');
1438 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
1439 :
1440 : // order does not matter and we can have gaps, to test that, create
1441 : // a map of a few values and corresponding strings
1442 : //
1443 1 : std::vector<std::string> order;
1444 : typedef std::map<std::string, data_t> value_t;
1445 1 : value_t values;
1446 1 : std::size_t const count(rand() % 100 + 25);
1447 70 : for(std::size_t i(0); i < count; ++i)
1448 : {
1449 69 : std::string index;
1450 : for(;;)
1451 : {
1452 69 : int const max(rand() % 25 + 1);
1453 954 : for(int j(0); j < max; ++j)
1454 : {
1455 885 : index += ' ' + rand() % ('~' + 1 - ' '); // ASCII except controls
1456 : }
1457 69 : if(values.find(index) == values.end())
1458 : {
1459 : // index is unique, use it
1460 69 : break;
1461 : }
1462 0 : }
1463 :
1464 69 : data_t my_data;
1465 69 : my_data.f_color = rand(),
1466 69 : my_data.f_height = rand(),
1467 138 : my_data.f_status = rand(),
1468 69 : my_data.f_width = rand(),
1469 :
1470 69 : values[index] = my_data;
1471 69 : order.push_back(index);
1472 :
1473 207 : out.add_value("set", index, my_data);
1474 69 : }
1475 : //{
1476 : //std::ofstream p("t1.txt");
1477 : //std::string s(buffer.str());
1478 : //p << s;
1479 : //}
1480 :
1481 : // make sure it did not get smashed
1482 1 : data = buffer.str();
1483 1 : CATCH_REQUIRE(data[0] == 'B');
1484 1 : CATCH_REQUIRE(data[1] == 'R');
1485 1 : CATCH_REQUIRE(data[2] == 'L');
1486 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
1487 :
1488 : struct processor
1489 : {
1490 69 : static bool process_hunk(
1491 : snapdev::deserializer<std::stringstream> & in
1492 : , snapdev::field_t const & field
1493 : , value_t & values)
1494 : {
1495 69 : CATCH_REQUIRE(field.f_name == "set");
1496 69 : CATCH_REQUIRE_FALSE(field.f_sub_name.empty());
1497 69 : CATCH_REQUIRE(field.f_index == -1);
1498 69 : CATCH_REQUIRE(values.find(field.f_sub_name) != values.end());
1499 :
1500 69 : CATCH_REQUIRE(field.f_size == sizeof(data_t));
1501 :
1502 69 : data_t value;
1503 69 : in.read_data(value);
1504 69 : data_t expected(values[field.f_sub_name]);
1505 69 : CATCH_REQUIRE(value == expected);
1506 :
1507 138 : return true;
1508 : }
1509 : };
1510 :
1511 : // Note: you don't usually end up re-using the serializer buffer
1512 : // but here it's practical; warning: this works because in
1513 : // a C++ stream the seek position is distinct between the
1514 : // write() and read() functions (tellp() vs tellg())
1515 : //
1516 1 : buffer.clear();
1517 :
1518 1 : snapdev::deserializer in(buffer);
1519 :
1520 : // WARNING: we want to use CATCH_...() macros inside the callback
1521 : // so make sure not to use one around unserialize_buffer().
1522 : //
1523 4 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
1524 2 : &processor::process_hunk
1525 : , std::placeholders::_1
1526 : , std::placeholders::_2
1527 2 : , values));
1528 1 : bool const r(in.deserialize(func));
1529 1 : CATCH_REQUIRE(r);
1530 1 : }
1531 14 : CATCH_END_SECTION()
1532 :
1533 14 : CATCH_START_SECTION("brs_basic_types: push/restore recursive")
1534 : {
1535 : class t1
1536 : {
1537 : public:
1538 3 : void serialize(snapdev::serializer<std::stringstream> & out) const
1539 : {
1540 9 : out.add_value("name", f_name);
1541 3 : }
1542 :
1543 3 : bool process_hunk(
1544 : snapdev::deserializer<std::stringstream> & in
1545 : , snapdev::field_t const & field)
1546 : {
1547 3 : CATCH_REQUIRE(field.f_sub_name.empty());
1548 3 : CATCH_REQUIRE(field.f_index == -1);
1549 :
1550 3 : if(field.f_name == "name")
1551 : {
1552 3 : std::string value;
1553 3 : in.read_data(value);
1554 3 : CATCH_REQUIRE(f_name == value);
1555 3 : }
1556 : else
1557 : {
1558 0 : CATCH_REQUIRE(field.f_name == "?unknown?");
1559 : }
1560 :
1561 3 : return true;
1562 : }
1563 :
1564 3 : void set_name(std::string const & name)
1565 : {
1566 3 : f_name = name;
1567 3 : }
1568 :
1569 : private:
1570 : std::string f_name = "--undefined--";
1571 : };
1572 :
1573 : class t2
1574 : {
1575 : public:
1576 1 : void serialize(snapdev::serializer<std::stringstream> & out) const
1577 : {
1578 22 : for(std::size_t idx(0); idx < std::size(f_sizes); ++idx)
1579 : {
1580 30 : out.add_value("size", idx, f_sizes[idx]);
1581 : }
1582 1 : }
1583 :
1584 10 : bool process_hunk(
1585 : snapdev::deserializer<std::stringstream> & in
1586 : , snapdev::field_t const & field)
1587 : {
1588 10 : if(field.f_name == "size")
1589 : {
1590 20 : CATCH_REQUIRE(static_cast<std::size_t>(field.f_index) < std::size(f_sizes));
1591 :
1592 10 : std::int32_t value;
1593 10 : in.read_data(value);
1594 10 : CATCH_REQUIRE(value == f_sizes[field.f_index]);
1595 : }
1596 : else
1597 : {
1598 0 : CATCH_REQUIRE(field.f_name == "?unknown?");
1599 : }
1600 :
1601 10 : return true;
1602 : }
1603 :
1604 : private:
1605 : std::int32_t f_sizes[10] = {
1606 : rand(), rand(), rand(), rand(), rand(),
1607 : rand(), rand(), rand(), rand(), rand(),
1608 : };
1609 : };
1610 :
1611 : // c includes an array of t1's and one t2
1612 : class c
1613 : {
1614 : public:
1615 1 : c()
1616 1 : {
1617 1 : int const max(rand() % 5 + 3);
1618 4 : for(int idx(0); idx < max; ++idx)
1619 : {
1620 3 : std::string name;
1621 3 : int len(rand() % 25 + 10);
1622 61 : for(int j(0); j < len; ++j)
1623 : {
1624 58 : name += 'a' + rand() % 26;
1625 : }
1626 3 : t1 t;
1627 3 : t.set_name(name);
1628 3 : f_t1.push_back(t);
1629 3 : }
1630 :
1631 1 : int const array_max(rand() % 90 + 10);
1632 64 : for(int idx(0); idx < array_max; ++idx)
1633 : {
1634 63 : float nominator(static_cast<float>(rand()));
1635 63 : float denominator(0.0f);
1636 : #pragma GCC diagnostic push
1637 : #pragma GCC diagnostic ignored "-Wfloat-equal"
1638 126 : while(denominator == 0.0f)
1639 : {
1640 63 : denominator = static_cast<float>(rand());
1641 : }
1642 : #pragma GCC diagnostic pop
1643 63 : f_t3.push_back(nominator / denominator);
1644 : }
1645 1 : }
1646 :
1647 1 : void serialize(snapdev::serializer<std::stringstream> & out) const
1648 : {
1649 3 : out.add_value("count", f_count);
1650 3 : out.add_value("age", f_age);
1651 :
1652 : {
1653 1 : int const max(static_cast<int>(f_t1.size()));
1654 4 : for(int idx(0); idx < max; ++idx)
1655 : {
1656 9 : snapdev::recursive r(out, "t1_array");
1657 3 : f_t1[idx].serialize(out);
1658 3 : }
1659 : }
1660 :
1661 3 : out.add_value("float-array", f_t3);
1662 :
1663 : {
1664 3 : snapdev::recursive r(out, "t2");
1665 1 : f_t2.serialize(out);
1666 1 : }
1667 :
1668 5 : out.add_value("flower-color", std::string("bluish"));
1669 3 : out.add_value("flower-height", std::string()); // unknown
1670 3 : out.add_value_if_not_empty("flower-name", std::string()); // unknown -- not added
1671 :
1672 3 : out.add_value("dog-color", "red");
1673 3 : out.add_value("dog-height", ""); // unknown
1674 5 : out.add_value_if_not_empty("dog-name", ""); // unknown -- not added
1675 3 : out.add_value("dog-nothing", nullptr); // unknown -- not added
1676 5 : out.add_value_if_not_empty("dog-eye-color", "green");
1677 1 : }
1678 :
1679 12 : bool process_hunk(
1680 : snapdev::deserializer<std::stringstream> & in
1681 : , snapdev::field_t const & field)
1682 : {
1683 12 : if(field.f_name == "count")
1684 : {
1685 1 : int value;
1686 1 : in.read_data(value);
1687 1 : CATCH_REQUIRE(f_count == value);
1688 : }
1689 11 : else if(field.f_name == "age")
1690 : {
1691 1 : int value;
1692 1 : in.read_data(value);
1693 1 : CATCH_REQUIRE(f_age == value);
1694 : }
1695 10 : else if(field.f_name == "t1_array")
1696 : {
1697 : // this is a set of sub-classes so we do not have an index
1698 : //
1699 3 : CATCH_REQUIRE(field.f_index == -1);
1700 3 : CATCH_REQUIRE(field.f_sub_name == std::string());
1701 :
1702 : // in a normal implementation, here we would create a new
1703 : // item and append it if the load succeeds; the test will
1704 : // instead verify that the f_t1_index is still valid and
1705 : // test load that next f_t1 object
1706 : //
1707 3 : CATCH_REQUIRE(f_t1_index < f_t1.size());
1708 :
1709 9 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
1710 6 : &t1::process_hunk
1711 6 : , &f_t1[f_t1_index]
1712 : , std::placeholders::_1
1713 6 : , std::placeholders::_2));
1714 3 : bool const r(in.deserialize(func));
1715 3 : CATCH_REQUIRE(r);
1716 :
1717 3 : ++f_t1_index;
1718 3 : }
1719 7 : else if(field.f_name == "float-array")
1720 : {
1721 1 : CATCH_REQUIRE(field.f_index == -1);
1722 1 : CATCH_REQUIRE(field.f_sub_name == std::string());
1723 :
1724 1 : std::vector<float> v;
1725 1 : in.read_data(v);
1726 1 : CATCH_REQUIRE(v.size() == f_t3.size());
1727 1 : CATCH_REQUIRE(v == f_t3);
1728 1 : }
1729 6 : else if(field.f_name == "t2")
1730 : {
1731 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
1732 2 : &t2::process_hunk
1733 2 : , &f_t2
1734 : , std::placeholders::_1
1735 2 : , std::placeholders::_2));
1736 1 : bool const r(in.deserialize(func));
1737 1 : CATCH_REQUIRE(r);
1738 1 : }
1739 5 : else if(field.f_name == "flower-color")
1740 : {
1741 1 : std::string value;
1742 1 : in.read_data(value);
1743 3 : CATCH_REQUIRE(value.length() == std::string("bluish").length());
1744 3 : CATCH_REQUIRE(value == std::string("bluish"));
1745 1 : }
1746 4 : else if(field.f_name == "flower-height")
1747 : {
1748 1 : std::string value;
1749 1 : in.read_data(value);
1750 1 : CATCH_REQUIRE(value == std::string());
1751 1 : }
1752 3 : else if(field.f_name == "flower-name")
1753 : {
1754 : // this field should not be added since the value is
1755 : // the empty string and we on purpose avoid adding
1756 : // it (i.e. "add value (only) if not empty")
1757 : //
1758 0 : CATCH_REQUIRE(field.f_name == "not-expected");
1759 : }
1760 3 : else if(field.f_name == "dog-color")
1761 : {
1762 1 : std::string value;
1763 1 : in.read_data(value);
1764 3 : CATCH_REQUIRE(value.length() == std::string("red").length());
1765 3 : CATCH_REQUIRE(value == std::string("red"));
1766 1 : }
1767 2 : else if(field.f_name == "dog-height")
1768 : {
1769 1 : std::string value;
1770 1 : in.read_data(value);
1771 1 : CATCH_REQUIRE(value == std::string());
1772 1 : }
1773 1 : else if(field.f_name == "dog-name"
1774 1 : || field.f_name == "dog-nothing")
1775 : {
1776 : // this field should not be added since the value is
1777 : // the empty string and we on purpose avoid adding
1778 : // it (i.e. "add value (only) if not empty")
1779 : //
1780 0 : CATCH_REQUIRE(field.f_name == "not-expected");
1781 : }
1782 1 : else if(field.f_name == "dog-eye-color")
1783 : {
1784 1 : std::string value;
1785 1 : in.read_data(value);
1786 3 : CATCH_REQUIRE(value == std::string("green"));
1787 1 : }
1788 : else
1789 : {
1790 0 : CATCH_REQUIRE(field.f_name == "?unknown?");
1791 : }
1792 :
1793 12 : return true;
1794 : }
1795 :
1796 : private:
1797 : int f_count = rand();
1798 : int f_age = rand() % 100;
1799 : std::vector<t1> f_t1 = std::vector<t1>();
1800 : t2 f_t2 = t2();
1801 : std::vector<float> f_t3 = {};
1802 : std::size_t f_t1_index = 0;
1803 : };
1804 :
1805 1 : c o;
1806 :
1807 1 : std::stringstream buffer;
1808 1 : snapdev::serializer out(buffer);
1809 :
1810 1 : std::string data(buffer.str());
1811 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
1812 :
1813 1 : CATCH_REQUIRE(data[0] == 'B');
1814 1 : CATCH_REQUIRE(data[1] == 'R');
1815 1 : CATCH_REQUIRE(data[2] == 'L');
1816 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
1817 :
1818 1 : o.serialize(out);
1819 :
1820 : // Note: you don't usually end up re-using the serializer buffer
1821 : // but here it's practical; warning: this works because in
1822 : // a C++ stream the seek position is distinct between the
1823 : // write() and read() functions (tellp() vs tellg())
1824 : //
1825 1 : buffer.clear();
1826 :
1827 1 : snapdev::deserializer in(buffer);
1828 :
1829 : // WARNING: we want to use CATCH_...() macros inside the callback
1830 : // so make sure not to use one around unserialize_buffer().
1831 : //
1832 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
1833 2 : &c::process_hunk
1834 2 : , &o
1835 : , std::placeholders::_1
1836 2 : , std::placeholders::_2));
1837 1 : bool const r(in.deserialize(func));
1838 1 : CATCH_REQUIRE(r);
1839 1 : }
1840 14 : CATCH_END_SECTION()
1841 14 : }
1842 :
1843 :
1844 14 : CATCH_TEST_CASE("brs_invalid", "[serialization][error]")
1845 : {
1846 14 : CATCH_START_SECTION("brs_invalid: name missing")
1847 : {
1848 1 : std::stringstream buffer;
1849 1 : snapdev::serializer out(buffer);
1850 :
1851 1 : std::string data(buffer.str());
1852 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
1853 :
1854 1 : CATCH_REQUIRE(data[0] == 'B');
1855 1 : CATCH_REQUIRE(data[1] == 'R');
1856 1 : CATCH_REQUIRE(data[2] == 'L');
1857 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
1858 :
1859 1 : char value = 33;
1860 :
1861 : // FIELD
1862 5 : CATCH_REQUIRE_THROWS_MATCHES(
1863 : out.add_value(std::string(), &value, sizeof(value))
1864 : , snapdev::brs_cannot_be_empty
1865 : , Catch::Matchers::ExceptionMessage(
1866 : "brs_error: name cannot be an empty string."));
1867 :
1868 : // FIELD: timespec
1869 1 : struct timespec ts;
1870 5 : CATCH_REQUIRE_THROWS_MATCHES(
1871 : out.add_value(std::string(), ts)
1872 : , snapdev::brs_cannot_be_empty
1873 : , Catch::Matchers::ExceptionMessage(
1874 : "brs_error: name cannot be an empty string."));
1875 :
1876 : // ARRAY
1877 5 : CATCH_REQUIRE_THROWS_MATCHES(
1878 : out.add_value(std::string(), 5, &value, sizeof(value))
1879 : , snapdev::brs_cannot_be_empty
1880 : , Catch::Matchers::ExceptionMessage(
1881 : "brs_error: name cannot be an empty string."));
1882 :
1883 : // MAP (main name)
1884 7 : CATCH_REQUIRE_THROWS_MATCHES(
1885 : out.add_value(std::string(), std::string(), &value, sizeof(value))
1886 : , snapdev::brs_cannot_be_empty
1887 : , Catch::Matchers::ExceptionMessage(
1888 : "brs_error: name cannot be an empty string."));
1889 :
1890 : // MAP (sub-name)
1891 9 : CATCH_REQUIRE_THROWS_MATCHES(
1892 : out.add_value("good-name", std::string(), &value, sizeof(value))
1893 : , snapdev::brs_cannot_be_empty
1894 : , Catch::Matchers::ExceptionMessage(
1895 : "brs_error: sub-name cannot be an empty string."));
1896 :
1897 : // SUB-FIELD
1898 5 : CATCH_REQUIRE_THROWS_MATCHES(
1899 : out.start_subfield(std::string())
1900 : , snapdev::brs_cannot_be_empty
1901 : , Catch::Matchers::ExceptionMessage(
1902 : "brs_error: name cannot be an empty string."));
1903 1 : }
1904 14 : CATCH_END_SECTION()
1905 :
1906 14 : CATCH_START_SECTION("brs_invalid: name too long")
1907 : {
1908 1 : std::stringstream buffer;
1909 1 : snapdev::serializer out(buffer);
1910 :
1911 1 : std::string data(buffer.str());
1912 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
1913 :
1914 1 : CATCH_REQUIRE(data[0] == 'B');
1915 1 : CATCH_REQUIRE(data[1] == 'R');
1916 1 : CATCH_REQUIRE(data[2] == 'L');
1917 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
1918 :
1919 1 : std::string name;
1920 1 : std::size_t count(1 << SIZEOF_BITFIELD(snapdev::hunk_sizes_t, f_name));
1921 1 : std::size_t const max(count + rand() % 200);
1922 286 : for(std::size_t idx(0); idx < max; ++idx)
1923 : {
1924 285 : name += rand() % 26 + 'a';
1925 : }
1926 :
1927 1 : char value = 33;
1928 :
1929 : // FIELD
1930 3 : CATCH_REQUIRE_THROWS_MATCHES(
1931 : out.add_value(name, &value, sizeof(value))
1932 : , snapdev::brs_out_of_range
1933 : , Catch::Matchers::ExceptionMessage(
1934 : "brs_out_of_range: name or hunk too large."));
1935 :
1936 : // FIELD: timespec
1937 1 : struct timespec ts;
1938 3 : CATCH_REQUIRE_THROWS_MATCHES(
1939 : out.add_value(name, ts)
1940 : , snapdev::brs_out_of_range
1941 : , Catch::Matchers::ExceptionMessage(
1942 : "brs_out_of_range: name too large."));
1943 :
1944 : // ARRAY
1945 3 : CATCH_REQUIRE_THROWS_MATCHES(
1946 : out.add_value(name, 17, &value, sizeof(value))
1947 : , snapdev::brs_out_of_range
1948 : , Catch::Matchers::ExceptionMessage(
1949 : "brs_out_of_range: name, index, or hunk too large."));
1950 :
1951 : // MAP (main name)
1952 7 : CATCH_REQUIRE_THROWS_MATCHES(
1953 : out.add_value(name, "name-too-long", &value, sizeof(value))
1954 : , snapdev::brs_out_of_range
1955 : , Catch::Matchers::ExceptionMessage(
1956 : "brs_out_of_range: name, sub-name, or hunk too large."));
1957 :
1958 : // MAP (sub-name)
1959 : //
1960 : // sub-names make use of an std::uint8_t for their size
1961 : //
1962 1 : std::string sub_name;
1963 1 : std::size_t const sub_max(std::numeric_limits<std::uint8_t>::max() + 1 + rand() % 200);
1964 364 : for(std::size_t idx(0); idx < sub_max; ++idx)
1965 : {
1966 363 : sub_name += rand() % 26 + 'a';
1967 : }
1968 :
1969 7 : CATCH_REQUIRE_THROWS_MATCHES(
1970 : out.add_value("sub-name-too-long", sub_name, &value, sizeof(value))
1971 : , snapdev::brs_out_of_range
1972 : , Catch::Matchers::ExceptionMessage(
1973 : "brs_out_of_range: name, sub-name, or hunk too large."));
1974 :
1975 : // SUB-FIELD
1976 3 : CATCH_REQUIRE_THROWS_MATCHES(
1977 : out.start_subfield(name)
1978 : , snapdev::brs_out_of_range
1979 : , Catch::Matchers::ExceptionMessage(
1980 : "brs_out_of_range: name too large."));
1981 1 : }
1982 14 : CATCH_END_SECTION()
1983 :
1984 14 : CATCH_START_SECTION("brs_invalid: hunk too long")
1985 : {
1986 1 : std::stringstream buffer;
1987 1 : snapdev::serializer out(buffer);
1988 :
1989 1 : std::string data(buffer.str());
1990 1 : CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
1991 :
1992 1 : CATCH_REQUIRE(data[0] == 'B');
1993 1 : CATCH_REQUIRE(data[1] == 'R');
1994 1 : CATCH_REQUIRE(data[2] == 'L');
1995 1 : CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
1996 :
1997 1 : char value = 33;
1998 11 : for(std::size_t extra(0); extra < 10; ++extra)
1999 : {
2000 320 : CATCH_REQUIRE_THROWS_MATCHES(
2001 : out.add_value("large-hunk", &value, (1 << SIZEOF_BITFIELD(snapdev::hunk_sizes_t, f_hunk)) + extra)
2002 : , snapdev::brs_out_of_range
2003 : , Catch::Matchers::ExceptionMessage(
2004 : "brs_out_of_range: name or hunk too large."));
2005 :
2006 320 : CATCH_REQUIRE_THROWS_MATCHES(
2007 : out.add_value("large-hunk", 33, &value, (1 << SIZEOF_BITFIELD(snapdev::hunk_sizes_t, f_hunk)) + extra)
2008 : , snapdev::brs_out_of_range
2009 : , Catch::Matchers::ExceptionMessage(
2010 : "brs_out_of_range: name, index, or hunk too large."));
2011 :
2012 360 : CATCH_REQUIRE_THROWS_MATCHES(
2013 : out.add_value("large-hunk", "sub-name", &value, (1 << SIZEOF_BITFIELD(snapdev::hunk_sizes_t, f_hunk)) + extra)
2014 : , snapdev::brs_out_of_range
2015 : , Catch::Matchers::ExceptionMessage(
2016 : "brs_out_of_range: name, sub-name, or hunk too large."));
2017 : }
2018 1 : }
2019 14 : CATCH_END_SECTION()
2020 :
2021 14 : CATCH_START_SECTION("brs_invalid: empty input")
2022 : {
2023 1 : std::stringstream buffer;
2024 :
2025 4 : CATCH_REQUIRE_THROWS_MATCHES(
2026 : snapdev::deserializer<std::stringstream>(buffer)
2027 : , snapdev::brs_magic_missing
2028 : , Catch::Matchers::ExceptionMessage(
2029 : "brs_error: magic missing at the start of the buffer."));
2030 1 : }
2031 14 : CATCH_END_SECTION()
2032 :
2033 14 : CATCH_START_SECTION("brs_invalid: magic unrecognized")
2034 : {
2035 1 : std::stringstream buffer;
2036 :
2037 1 : buffer << 'G'; // pretend it's a GIF
2038 1 : buffer << 'I';
2039 1 : buffer << 'F';
2040 1 : buffer << '8';
2041 1 : buffer << '9';
2042 1 : buffer << 'a';
2043 :
2044 4 : CATCH_REQUIRE_THROWS_MATCHES(
2045 : snapdev::deserializer<std::stringstream>(buffer)
2046 : , snapdev::brs_magic_unsupported
2047 : , Catch::Matchers::ExceptionMessage(
2048 : "brs_error: magic unsupported."));
2049 1 : }
2050 14 : CATCH_END_SECTION()
2051 :
2052 14 : CATCH_START_SECTION("brs_invalid: unknown hunk type")
2053 : {
2054 1 : std::stringstream buffer;
2055 :
2056 : // magic
2057 1 : buffer << 'B';
2058 1 : buffer << 'R';
2059 1 : buffer << 'L';
2060 1 : buffer << snapdev::BRS_VERSION;
2061 :
2062 : // size hunk
2063 : // type 3 is not recognized at the moment
2064 1 : std::uint32_t hunk(static_cast<std::uint32_t>((0x03 << 0) | (3 << 2) | (11 << 9)));
2065 1 : buffer.write(reinterpret_cast<char const *>(&hunk), 4);
2066 :
2067 : struct processor
2068 : {
2069 0 : static bool process_hunk(
2070 : snapdev::deserializer<std::stringstream> & in
2071 : , snapdev::field_t const & field)
2072 : {
2073 0 : snapdev::NOT_USED(in, field);
2074 0 : throw std::logic_error("process_hunk() was called!");
2075 : }
2076 : };
2077 :
2078 1 : snapdev::deserializer<std::stringstream> in(buffer);
2079 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
2080 2 : &processor::process_hunk
2081 : , std::placeholders::_1
2082 2 : , std::placeholders::_2));
2083 :
2084 3 : CATCH_REQUIRE_THROWS_MATCHES(
2085 : in.deserialize(func)
2086 : , snapdev::brs_unknown_type
2087 : , Catch::Matchers::ExceptionMessage(
2088 : "brs_error: read a field with an unknown type."));
2089 1 : }
2090 14 : CATCH_END_SECTION()
2091 :
2092 14 : CATCH_START_SECTION("brs_invalid: field missing name")
2093 : {
2094 1 : std::stringstream buffer;
2095 :
2096 : // magic
2097 1 : buffer << 'B';
2098 1 : buffer << 'R';
2099 1 : buffer << 'L';
2100 1 : buffer << snapdev::BRS_VERSION;
2101 :
2102 : // size hunk
2103 : // type 3 is not recognized at the moment
2104 1 : std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_FIELD << 0) | (3 << 2) | (11 << 9)));
2105 1 : buffer.write(reinterpret_cast<char const *>(&hunk), 4);
2106 :
2107 : struct processor
2108 : {
2109 0 : static bool process_hunk(
2110 : snapdev::deserializer<std::stringstream> & in
2111 : , snapdev::field_t const & field)
2112 : {
2113 0 : snapdev::NOT_USED(in, field);
2114 0 : throw std::logic_error("process_hunk() was called!");
2115 : }
2116 : };
2117 :
2118 1 : snapdev::deserializer<std::stringstream> in(buffer);
2119 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
2120 2 : &processor::process_hunk
2121 : , std::placeholders::_1
2122 2 : , std::placeholders::_2));
2123 :
2124 1 : bool r(in.deserialize(func));
2125 1 : CATCH_REQUIRE_FALSE(r);
2126 1 : }
2127 14 : CATCH_END_SECTION()
2128 :
2129 14 : CATCH_START_SECTION("brs_invalid: field data mismatch reading")
2130 : {
2131 1 : std::stringstream buffer;
2132 :
2133 : // magic
2134 1 : buffer << 'B';
2135 1 : buffer << 'R';
2136 1 : buffer << 'L';
2137 1 : buffer << snapdev::BRS_VERSION;
2138 :
2139 : // size hunk
2140 : // type 3 is not recognized at the moment
2141 1 : std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_FIELD << 0) | (3 << 2) | (1 << 9)));
2142 1 : buffer.write(reinterpret_cast<char const *>(&hunk), 4);
2143 1 : char const * name("ABC");
2144 1 : buffer.write(name, 3);
2145 1 : std::uint8_t value(5);
2146 1 : buffer.write(reinterpret_cast<char const *>(&value), 1);
2147 :
2148 : struct processor
2149 : {
2150 1 : static bool process_hunk(
2151 : snapdev::deserializer<std::stringstream> & in
2152 : , snapdev::field_t const & field)
2153 : {
2154 1 : CATCH_REQUIRE(field.f_name == "ABC");
2155 1 : CATCH_REQUIRE(field.f_sub_name == std::string());
2156 1 : CATCH_REQUIRE(field.f_index == -1);
2157 1 : CATCH_REQUIRE(field.f_size == 1);
2158 :
2159 : // reading the data other than the byte creates errors
2160 : // (incorrect size--so you may be calling the read_data()
2161 : // function with the wrong type)
2162 : //
2163 :
2164 1 : std::int16_t s;
2165 3 : CATCH_REQUIRE_THROWS_MATCHES(
2166 : in.read_data(s)
2167 : , snapdev::brs_logic_error
2168 : , Catch::Matchers::ExceptionMessage(
2169 : "brs_logic_error: hunk size is 1, but you are trying to read 2."));
2170 :
2171 1 : std::uint16_t us;
2172 3 : CATCH_REQUIRE_THROWS_MATCHES(
2173 : in.read_data(us)
2174 : , snapdev::brs_logic_error
2175 : , Catch::Matchers::ExceptionMessage(
2176 : "brs_logic_error: hunk size is 1, but you are trying to read 2."));
2177 :
2178 1 : std::int32_t i;
2179 3 : CATCH_REQUIRE_THROWS_MATCHES(
2180 : in.read_data(i)
2181 : , snapdev::brs_logic_error
2182 : , Catch::Matchers::ExceptionMessage(
2183 : "brs_logic_error: hunk size is 1, but you are trying to read 4."));
2184 :
2185 1 : std::uint32_t ui;
2186 3 : CATCH_REQUIRE_THROWS_MATCHES(
2187 : in.read_data(ui)
2188 : , snapdev::brs_logic_error
2189 : , Catch::Matchers::ExceptionMessage(
2190 : "brs_logic_error: hunk size is 1, but you are trying to read 4."));
2191 :
2192 1 : std::int64_t l;
2193 3 : CATCH_REQUIRE_THROWS_MATCHES(
2194 : in.read_data(l)
2195 : , snapdev::brs_logic_error
2196 : , Catch::Matchers::ExceptionMessage(
2197 : "brs_logic_error: hunk size is 1, but you are trying to read 8."));
2198 :
2199 1 : std::uint64_t ul;
2200 3 : CATCH_REQUIRE_THROWS_MATCHES(
2201 : in.read_data(ul)
2202 : , snapdev::brs_logic_error
2203 : , Catch::Matchers::ExceptionMessage(
2204 : "brs_logic_error: hunk size is 1, but you are trying to read 8."));
2205 :
2206 1 : float f;
2207 3 : CATCH_REQUIRE_THROWS_MATCHES(
2208 : in.read_data(f)
2209 : , snapdev::brs_logic_error
2210 : , Catch::Matchers::ExceptionMessage(
2211 : "brs_logic_error: hunk size is 1, but you are trying to read 4."));
2212 :
2213 1 : double d;
2214 3 : CATCH_REQUIRE_THROWS_MATCHES(
2215 : in.read_data(d)
2216 : , snapdev::brs_logic_error
2217 : , Catch::Matchers::ExceptionMessage(
2218 : "brs_logic_error: hunk size is 1, but you are trying to read 8."));
2219 :
2220 1 : long double ld;
2221 3 : CATCH_REQUIRE_THROWS_MATCHES(
2222 : in.read_data(ld)
2223 : , snapdev::brs_logic_error
2224 : , Catch::Matchers::ExceptionMessage(
2225 : "brs_logic_error: hunk size is 1, but you are trying to read 16."));
2226 :
2227 1 : std::vector<std::int64_t> vl;
2228 3 : CATCH_REQUIRE_THROWS_MATCHES(
2229 : in.read_data(vl)
2230 : , snapdev::brs_logic_error
2231 : , Catch::Matchers::ExceptionMessage(
2232 : "brs_logic_error: hunk size (1) is not a multiple of the vector item size: 8."));
2233 :
2234 1 : std::uint8_t b(0);
2235 1 : CATCH_REQUIRE(in.read_data(b));
2236 1 : CATCH_REQUIRE(b == 5);
2237 :
2238 1 : return true;
2239 1 : }
2240 : };
2241 :
2242 1 : snapdev::deserializer<std::stringstream> in(buffer);
2243 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
2244 2 : &processor::process_hunk
2245 : , std::placeholders::_1
2246 2 : , std::placeholders::_2));
2247 :
2248 1 : bool r(in.deserialize(func));
2249 1 : CATCH_REQUIRE(r);
2250 1 : }
2251 14 : CATCH_END_SECTION()
2252 :
2253 14 : CATCH_START_SECTION("brs_invalid: missing array index")
2254 : {
2255 1 : std::stringstream buffer;
2256 :
2257 : // magic
2258 1 : buffer << 'B';
2259 1 : buffer << 'R';
2260 1 : buffer << 'L';
2261 1 : buffer << snapdev::BRS_VERSION;
2262 :
2263 : // size hunk
2264 : // type 3 is not recognized at the moment
2265 1 : std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_ARRAY << 0) | (3 << 2) | (11 << 9)));
2266 1 : buffer.write(reinterpret_cast<char const *>(&hunk), 4);
2267 :
2268 : struct processor
2269 : {
2270 0 : static bool process_hunk(
2271 : snapdev::deserializer<std::stringstream> & in
2272 : , snapdev::field_t const & field)
2273 : {
2274 0 : snapdev::NOT_USED(in, field);
2275 0 : throw std::logic_error("process_hunk() was called!");
2276 : }
2277 : };
2278 :
2279 1 : snapdev::deserializer<std::stringstream> in(buffer);
2280 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
2281 2 : &processor::process_hunk
2282 : , std::placeholders::_1
2283 2 : , std::placeholders::_2));
2284 :
2285 1 : bool r(in.deserialize(func));
2286 1 : CATCH_REQUIRE_FALSE(r);
2287 1 : }
2288 14 : CATCH_END_SECTION()
2289 :
2290 14 : CATCH_START_SECTION("brs_invalid: missing array field name")
2291 : {
2292 1 : std::stringstream buffer;
2293 :
2294 : // magic
2295 1 : buffer << 'B';
2296 1 : buffer << 'R';
2297 1 : buffer << 'L';
2298 1 : buffer << snapdev::BRS_VERSION;
2299 :
2300 : // size hunk
2301 : // type 3 is not recognized at the moment
2302 1 : std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_ARRAY << 0) | (3 << 2) | (11 << 9)));
2303 1 : buffer.write(reinterpret_cast<char const *>(&hunk), 4);
2304 1 : std::uint16_t size(10);
2305 1 : buffer.write(reinterpret_cast<char const *>(&size), 2);
2306 :
2307 : struct processor
2308 : {
2309 0 : static bool process_hunk(
2310 : snapdev::deserializer<std::stringstream> & in
2311 : , snapdev::field_t const & field)
2312 : {
2313 0 : snapdev::NOT_USED(in, field);
2314 0 : throw std::logic_error("process_hunk() was called!");
2315 : }
2316 : };
2317 :
2318 1 : snapdev::deserializer<std::stringstream> in(buffer);
2319 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
2320 2 : &processor::process_hunk
2321 : , std::placeholders::_1
2322 2 : , std::placeholders::_2));
2323 :
2324 1 : bool r(in.deserialize(func));
2325 1 : CATCH_REQUIRE_FALSE(r);
2326 1 : }
2327 14 : CATCH_END_SECTION()
2328 :
2329 14 : CATCH_START_SECTION("brs_invalid: missing map sub-name length")
2330 : {
2331 1 : std::stringstream buffer;
2332 :
2333 : // magic
2334 1 : buffer << 'B';
2335 1 : buffer << 'R';
2336 1 : buffer << 'L';
2337 1 : buffer << snapdev::BRS_VERSION;
2338 :
2339 : // size hunk
2340 : // type 3 is not recognized at the moment
2341 1 : std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_MAP << 0) | (3 << 2) | (11 << 9)));
2342 1 : buffer.write(reinterpret_cast<char const *>(&hunk), 4);
2343 :
2344 : struct processor
2345 : {
2346 0 : static bool process_hunk(
2347 : snapdev::deserializer<std::stringstream> & in
2348 : , snapdev::field_t const & field)
2349 : {
2350 0 : snapdev::NOT_USED(in, field);
2351 0 : throw std::logic_error("process_hunk() was called!");
2352 : }
2353 : };
2354 :
2355 1 : snapdev::deserializer<std::stringstream> in(buffer);
2356 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
2357 2 : &processor::process_hunk
2358 : , std::placeholders::_1
2359 2 : , std::placeholders::_2));
2360 :
2361 1 : bool r(in.deserialize(func));
2362 1 : CATCH_REQUIRE_FALSE(r);
2363 1 : }
2364 14 : CATCH_END_SECTION()
2365 :
2366 14 : CATCH_START_SECTION("brs_invalid: map sub-name length is zero")
2367 : {
2368 1 : std::stringstream buffer;
2369 :
2370 : // magic
2371 1 : buffer << 'B';
2372 1 : buffer << 'R';
2373 1 : buffer << 'L';
2374 1 : buffer << snapdev::BRS_VERSION;
2375 :
2376 : // size hunk
2377 : // type 3 is not recognized at the moment
2378 1 : std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_MAP << 0) | (3 << 2) | (11 << 9)));
2379 1 : buffer.write(reinterpret_cast<char const *>(&hunk), 4);
2380 1 : std::uint8_t len(0);
2381 1 : buffer.write(reinterpret_cast<char const *>(&len), 1);
2382 :
2383 : struct processor
2384 : {
2385 0 : static bool process_hunk(
2386 : snapdev::deserializer<std::stringstream> & in
2387 : , snapdev::field_t const & field)
2388 : {
2389 0 : snapdev::NOT_USED(in, field);
2390 0 : throw std::logic_error("process_hunk() was called!");
2391 : }
2392 : };
2393 :
2394 1 : snapdev::deserializer<std::stringstream> in(buffer);
2395 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
2396 2 : &processor::process_hunk
2397 : , std::placeholders::_1
2398 2 : , std::placeholders::_2));
2399 :
2400 3 : CATCH_REQUIRE_THROWS_MATCHES(
2401 : in.deserialize(func)
2402 : , snapdev::brs_map_name_cannot_be_empty
2403 : , Catch::Matchers::ExceptionMessage(
2404 : "brs_error: the length of a map's field name cannot be zero."));
2405 1 : }
2406 14 : CATCH_END_SECTION()
2407 :
2408 14 : CATCH_START_SECTION("brs_invalid: missing map sub-name")
2409 : {
2410 1 : std::stringstream buffer;
2411 :
2412 : // magic
2413 1 : buffer << 'B';
2414 1 : buffer << 'R';
2415 1 : buffer << 'L';
2416 1 : buffer << snapdev::BRS_VERSION;
2417 :
2418 : // size hunk
2419 : // type 3 is not recognized at the moment
2420 1 : std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_MAP << 0) | (3 << 2) | (11 << 9)));
2421 1 : buffer.write(reinterpret_cast<char const *>(&hunk), 4);
2422 1 : std::uint8_t len(10);
2423 1 : buffer.write(reinterpret_cast<char const *>(&len), 1);
2424 :
2425 : struct processor
2426 : {
2427 0 : static bool process_hunk(
2428 : snapdev::deserializer<std::stringstream> & in
2429 : , snapdev::field_t const & field)
2430 : {
2431 0 : snapdev::NOT_USED(in, field);
2432 0 : throw std::logic_error("process_hunk() was called!");
2433 : }
2434 : };
2435 :
2436 1 : snapdev::deserializer<std::stringstream> in(buffer);
2437 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
2438 2 : &processor::process_hunk
2439 : , std::placeholders::_1
2440 2 : , std::placeholders::_2));
2441 :
2442 1 : bool r(in.deserialize(func));
2443 1 : CATCH_REQUIRE_FALSE(r);
2444 1 : }
2445 14 : CATCH_END_SECTION()
2446 :
2447 14 : CATCH_START_SECTION("brs_invalid: missing map name")
2448 : {
2449 1 : std::stringstream buffer;
2450 :
2451 : // magic
2452 1 : buffer << 'B';
2453 1 : buffer << 'R';
2454 1 : buffer << 'L';
2455 1 : buffer << snapdev::BRS_VERSION;
2456 :
2457 : // size hunk
2458 : // type 3 is not recognized at the moment
2459 1 : std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_MAP << 0) | (3 << 2) | (11 << 9)));
2460 1 : buffer.write(reinterpret_cast<char const *>(&hunk), 4);
2461 1 : std::uint8_t len(3);
2462 1 : buffer.write(reinterpret_cast<char const *>(&len), 1);
2463 1 : char const * name = "SUB";
2464 1 : buffer.write(name, 3);
2465 :
2466 : struct processor
2467 : {
2468 0 : static bool process_hunk(
2469 : snapdev::deserializer<std::stringstream> & in
2470 : , snapdev::field_t const & field)
2471 : {
2472 0 : snapdev::NOT_USED(in, field);
2473 0 : throw std::logic_error("process_hunk() was called!");
2474 : }
2475 : };
2476 :
2477 1 : snapdev::deserializer<std::stringstream> in(buffer);
2478 3 : snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
2479 2 : &processor::process_hunk
2480 : , std::placeholders::_1
2481 2 : , std::placeholders::_2));
2482 :
2483 1 : bool r(in.deserialize(func));
2484 1 : CATCH_REQUIRE_FALSE(r);
2485 1 : }
2486 14 : CATCH_END_SECTION()
2487 14 : }
2488 :
2489 :
2490 : // vim: ts=4 sw=4 et
|