1 | // |
2 | // Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com) |
3 | // |
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying |
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
6 | // |
7 | |
8 | #include <boost/mysql/client_errc.hpp> |
9 | #include <boost/mysql/error_code.hpp> |
10 | |
11 | #include <boost/mysql/impl/internal/sansio/message_reader.hpp> |
12 | #include <boost/mysql/impl/internal/sansio/read_buffer.hpp> |
13 | |
14 | #include <boost/test/tools/interface.hpp> |
15 | #include <boost/test/unit_test.hpp> |
16 | |
17 | #include <algorithm> |
18 | #include <cstddef> |
19 | #include <cstdint> |
20 | #include <cstring> |
21 | #include <vector> |
22 | |
23 | #include "test_common/assert_buffer_equals.hpp" |
24 | #include "test_common/buffer_concat.hpp" |
25 | #include "test_unit/create_frame.hpp" |
26 | |
27 | using namespace boost::mysql::detail; |
28 | using namespace boost::mysql::test; |
29 | using boost::span; |
30 | using boost::mysql::client_errc; |
31 | using boost::mysql::error_code; |
32 | |
33 | using u8vec = std::vector<std::uint8_t>; |
34 | |
35 | BOOST_AUTO_TEST_SUITE(test_message_reader) |
36 | |
37 | class reader_fixture |
38 | { |
39 | public: |
40 | message_reader reader; |
41 | std::uint8_t seqnum{42}; |
42 | |
43 | reader_fixture(std::vector<std::uint8_t> contents, std::size_t buffsize = 512) |
44 | : reader(buffsize, 64), // max frame size is 64 |
45 | contents_(std::move(contents)), |
46 | buffer_first_(reader.internal_buffer().first()) |
47 | { |
48 | } |
49 | |
50 | void set_contents(u8vec value) |
51 | { |
52 | contents_ = std::move(value); |
53 | bytes_written_ = 0u; |
54 | } |
55 | |
56 | // Reads bytes until reader.done() or all bytes in contents have been read. |
57 | // Resizes the buffer as required |
58 | void read_until_completion() |
59 | { |
60 | while (!reader.done() && remaining_bytes()) |
61 | { |
62 | reader.prepare_buffer(); |
63 | std::size_t bytes_to_copy = (std::min)(a: reader.buffer().size(), b: contents_.size() - bytes_written_); |
64 | read_bytes(num_bytes: bytes_to_copy); |
65 | } |
66 | BOOST_TEST(reader.done()); |
67 | BOOST_TEST(remaining_bytes() == 0u); |
68 | } |
69 | |
70 | // Simulates a read of num_bytes against the read buffer, then processes the result. |
71 | // Doesn't resize the buffer |
72 | void read_bytes(std::size_t num_bytes) |
73 | { |
74 | // Simulate a write against the buffer |
75 | if (num_bytes) |
76 | { |
77 | assert(num_bytes <= reader.buffer().size()); |
78 | std::memcpy(dest: reader.buffer().data(), src: contents_.data() + bytes_written_, n: num_bytes); |
79 | bytes_written_ += num_bytes; |
80 | } |
81 | |
82 | // Trigger the op |
83 | reader.resume(bytes_read: num_bytes); |
84 | } |
85 | |
86 | span<const std::uint8_t> check_message(const std::vector<std::uint8_t>& expected) |
87 | { |
88 | BOOST_TEST_REQUIRE(reader.done()); |
89 | BOOST_TEST_REQUIRE(reader.error() == error_code()); |
90 | auto msg = reader.message(); |
91 | BOOST_MYSQL_ASSERT_BUFFER_EQUALS(msg, expected); |
92 | return msg; |
93 | } |
94 | |
95 | void record_buffer_first() noexcept { buffer_first_ = reader.internal_buffer().first(); } |
96 | void check_buffer_stability() { BOOST_TEST(reader.internal_buffer().first() == buffer_first_); } |
97 | std::size_t buffsize() const noexcept { return reader.internal_buffer().size(); } |
98 | |
99 | private: |
100 | std::vector<std::uint8_t> contents_; |
101 | std::size_t bytes_written_{0}; |
102 | const std::uint8_t* buffer_first_; |
103 | |
104 | std::size_t remaining_bytes() const noexcept { return contents_.size() - bytes_written_; } |
105 | }; |
106 | |
107 | // Parsing algorithm. Without buffer relocations or short reads |
108 | BOOST_AUTO_TEST_CASE(parsing_algorithm_success) |
109 | { |
110 | struct |
111 | { |
112 | const char* name; |
113 | u8vec input; |
114 | u8vec expected_msg; |
115 | std::uint8_t expected_seqnum; |
116 | } test_cases[] = { |
117 | {.name: "empty_message" , .input: create_empty_frame(seqnum: 42), .expected_msg: {}, .expected_seqnum: 43}, |
118 | {.name: "one_frame" , .input: create_frame(seqnum: 42, body: {0x01, 0x02, 0x03}), .expected_msg: {0x01, 0x02, 0x03}, .expected_seqnum: 43}, |
119 | {.name: "one_frame_max_size" , |
120 | .input: buffer_builder().add(value: create_frame(seqnum: 42, body: u8vec(64, 0x04))).add(value: create_empty_frame(seqnum: 43)).build(), |
121 | .expected_msg: u8vec(64, 0x04), |
122 | .expected_seqnum: 44}, |
123 | {.name: "two_frames" , |
124 | .input: buffer_builder().add(value: create_frame(seqnum: 42, body: u8vec(64, 0x04))).add(value: create_frame(seqnum: 43, body: {0x05, 0x06})).build(), |
125 | .expected_msg: concat_copy(lhs: u8vec(64, 0x04), rhs: {0x05, 0x06}), |
126 | .expected_seqnum: 44}, |
127 | {.name: "two_frames_max_size" , |
128 | .input: buffer_builder() |
129 | .add(value: create_frame(seqnum: 42, body: u8vec(64, 0x04))) |
130 | .add(value: create_frame(seqnum: 43, body: u8vec(64, 0x05))) |
131 | .add(value: create_empty_frame(seqnum: 44)) |
132 | .build(), |
133 | .expected_msg: concat_copy(lhs: u8vec(64, 0x04), rhs: u8vec(64, 0x05)), |
134 | .expected_seqnum: 45}, |
135 | {.name: "three_frames" , |
136 | .input: buffer_builder() |
137 | .add(value: create_frame(seqnum: 42, body: u8vec(64, 0x04))) |
138 | .add(value: create_frame(seqnum: 43, body: u8vec(64, 0x05))) |
139 | .add(value: create_frame(seqnum: 44, body: {0x0a})) |
140 | .build(), |
141 | .expected_msg: buffer_builder().add(value: u8vec(64, 0x04)).add(value: u8vec(64, 0x05)).add(value: {0x0a}).build(), |
142 | .expected_seqnum: 45}, |
143 | }; |
144 | |
145 | for (auto& tc : test_cases) |
146 | { |
147 | BOOST_TEST_CONTEXT(tc.name) |
148 | { |
149 | // Setup |
150 | reader_fixture fix(tc.input); |
151 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
152 | BOOST_TEST(!fix.reader.done()); |
153 | |
154 | // Receive the message |
155 | fix.read_bytes(num_bytes: tc.input.size()); |
156 | |
157 | // Check |
158 | fix.check_message(expected: tc.expected_msg); |
159 | BOOST_TEST(fix.seqnum == tc.expected_seqnum); |
160 | |
161 | // Buffer didn't reallocate |
162 | fix.check_buffer_stability(); |
163 | } |
164 | } |
165 | } |
166 | |
167 | BOOST_AUTO_TEST_CASE(seqnum_overflow) |
168 | { |
169 | // message to be parsed |
170 | reader_fixture fix( |
171 | buffer_builder() |
172 | .add(value: create_frame(seqnum: 255, body: std::vector<std::uint8_t>(64, 0x04))) |
173 | .add(value: create_frame(seqnum: 0, body: {0x05, 0x06, 0x07})) |
174 | .build(), |
175 | 64 + 16 |
176 | ); |
177 | fix.seqnum = 255; |
178 | |
179 | // Setup |
180 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
181 | BOOST_TEST(!fix.reader.done()); |
182 | |
183 | // all in one |
184 | fix.read_bytes(num_bytes: 64 + 4 * 2 + 3); |
185 | fix.check_message(expected: concat_copy(lhs: std::vector<std::uint8_t>(64, 0x04), rhs: {0x05, 0x06, 0x07})); |
186 | BOOST_TEST(fix.seqnum == 1u); |
187 | |
188 | // Buffer didn't reallocate |
189 | fix.check_buffer_stability(); |
190 | } |
191 | |
192 | BOOST_AUTO_TEST_CASE(seqnum_mismatch) |
193 | { |
194 | struct |
195 | { |
196 | const char* name; |
197 | u8vec input; |
198 | } test_cases[] = { |
199 | {.name: "1st_frame" , .input: create_frame(seqnum: 1, body: {0x01, 0x02})}, |
200 | {.name: "2nd_frame" , .input: concat_copy(lhs: create_frame(seqnum: 42, body: u8vec(64, 0x04)), rhs: create_frame(seqnum: 44, body: {0x01}))}, |
201 | }; |
202 | |
203 | for (const auto& tc : test_cases) |
204 | { |
205 | BOOST_TEST_CONTEXT(tc.name) |
206 | { |
207 | reader_fixture fix(tc.input); |
208 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
209 | fix.read_bytes(num_bytes: tc.input.size()); |
210 | BOOST_TEST(fix.reader.done()); |
211 | BOOST_TEST(fix.reader.error() == error_code(client_errc::sequence_number_mismatch)); |
212 | } |
213 | } |
214 | } |
215 | |
216 | // Long read: we received two messages at once. |
217 | // We don't consume the next message while parsing the first one |
218 | // We don't get rid of the first message while there's space for the second one |
219 | BOOST_AUTO_TEST_CASE(long_read) |
220 | { |
221 | // message to be parsed |
222 | std::vector<std::uint8_t> first_msg_body{0x01, 0x02, 0x03}; |
223 | std::vector<std::uint8_t> second_msg_body{0x04, 0x05, 0x06, 0x07}; |
224 | reader_fixture fix(concat_copy(lhs: create_frame(seqnum: 42, body: first_msg_body), rhs: create_frame(seqnum: 2, body: second_msg_body))); |
225 | |
226 | // Prepare first read |
227 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
228 | BOOST_TEST(!fix.reader.done()); |
229 | |
230 | // The read yields two messages at once |
231 | fix.read_bytes(num_bytes: 15); |
232 | fix.check_message(expected: first_msg_body); |
233 | BOOST_TEST(fix.seqnum == 43u); |
234 | |
235 | // We can read the 2nd message, too |
236 | std::uint8_t seqnum2 = 2u; |
237 | fix.reader.prepare_read(sequence_number&: seqnum2); |
238 | fix.check_message(expected: second_msg_body); |
239 | BOOST_TEST(fix.seqnum == 43u); // old seqnum not updated |
240 | BOOST_TEST(seqnum2 == 3u); // new seqnum updated |
241 | |
242 | // Buffer shouldn't reallocate |
243 | fix.check_buffer_stability(); |
244 | } |
245 | |
246 | // Short reads |
247 | BOOST_AUTO_TEST_CASE(short_reads_multiple) |
248 | { |
249 | // message to be parsed |
250 | reader_fixture fix(create_frame(seqnum: 42, body: {0x01, 0x02, 0x03})); |
251 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
252 | BOOST_TEST(!fix.reader.done()); |
253 | |
254 | // 1 byte in the header received |
255 | fix.read_bytes(num_bytes: 1); |
256 | BOOST_TEST(!fix.reader.done()); |
257 | |
258 | // Another 2 bytes received |
259 | fix.read_bytes(num_bytes: 2); |
260 | BOOST_TEST(!fix.reader.done()); |
261 | |
262 | // Header fully received |
263 | fix.read_bytes(num_bytes: 1); |
264 | BOOST_TEST(!fix.reader.done()); |
265 | |
266 | // 1 byte in body received |
267 | fix.read_bytes(num_bytes: 1); |
268 | BOOST_TEST(!fix.reader.done()); |
269 | |
270 | // body fully received |
271 | fix.read_bytes(num_bytes: 2); |
272 | fix.check_message(expected: {0x01, 0x02, 0x03}); |
273 | BOOST_TEST(fix.seqnum == 43u); |
274 | |
275 | // Buffer shouldn't reallocate |
276 | fix.check_buffer_stability(); |
277 | } |
278 | |
279 | BOOST_AUTO_TEST_CASE() |
280 | { |
281 | // message to be parsed |
282 | reader_fixture fix(create_frame(seqnum: 42, body: {0x01, 0x02, 0x03})); |
283 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
284 | BOOST_TEST(!fix.reader.done()); |
285 | |
286 | // Full header received |
287 | fix.read_bytes(num_bytes: 4); |
288 | BOOST_TEST(!fix.reader.done()); |
289 | |
290 | // Full body received |
291 | fix.read_bytes(num_bytes: 3); |
292 | fix.check_message(expected: {0x01, 0x02, 0x03}); |
293 | BOOST_TEST(fix.seqnum == 43u); |
294 | |
295 | // Buffer didn't reallocate |
296 | fix.check_buffer_stability(); |
297 | } |
298 | |
299 | BOOST_AUTO_TEST_CASE(short_reads_two_frames) |
300 | { |
301 | // message to be parsed |
302 | reader_fixture fix( |
303 | buffer_builder() |
304 | .add(value: create_frame(seqnum: 42, body: std::vector<std::uint8_t>(64, 0x04))) |
305 | .add(value: create_frame(seqnum: 43, body: {0x05, 0x06, 0x07})) |
306 | .build(), |
307 | 64 + 16 |
308 | ); |
309 | auto expected_message = concat_copy(lhs: std::vector<std::uint8_t>(64, 0x04), rhs: {0x05, 0x06, 0x07}); |
310 | |
311 | // Setup |
312 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
313 | |
314 | // part of header 1 |
315 | fix.read_bytes(num_bytes: 3); |
316 | BOOST_TEST(!fix.reader.done()); |
317 | |
318 | // header 1 full |
319 | fix.read_bytes(num_bytes: 1); |
320 | BOOST_TEST(!fix.reader.done()); |
321 | |
322 | // part of body 1 |
323 | fix.read_bytes(num_bytes: 64 - 8); |
324 | BOOST_TEST(!fix.reader.done()); |
325 | |
326 | // rest of body 1 |
327 | fix.read_bytes(num_bytes: 8); |
328 | BOOST_TEST(!fix.reader.done()); |
329 | |
330 | // part of header 2 |
331 | fix.read_bytes(num_bytes: 1); |
332 | BOOST_TEST(!fix.reader.done()); |
333 | |
334 | // another part of header 2 |
335 | fix.read_bytes(num_bytes: 2); |
336 | BOOST_TEST(!fix.reader.done()); |
337 | |
338 | // rest of header 2 and part of body 1 |
339 | fix.read_bytes(num_bytes: 2); |
340 | BOOST_TEST(!fix.reader.done()); |
341 | |
342 | // another part of body 2 |
343 | fix.read_bytes(num_bytes: 1); |
344 | BOOST_TEST(!fix.reader.done()); |
345 | |
346 | // remaining of body 2 |
347 | fix.read_bytes(num_bytes: 1); |
348 | fix.check_message(expected: expected_message); |
349 | BOOST_TEST(fix.seqnum == 44u); |
350 | |
351 | // Buffer shouldn't reallocate |
352 | fix.check_buffer_stability(); |
353 | } |
354 | |
355 | // Buffer resizing |
356 | BOOST_AUTO_TEST_CASE(buffer_resizing_not_enough_space) |
357 | { |
358 | // Setup |
359 | reader_fixture fix(create_frame(seqnum: 42, body: u8vec(50, 0x04)), 0); |
360 | BOOST_TEST(fix.buffsize() == 0u); |
361 | |
362 | // Prepare read. The buffer hasn't resized. |
363 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
364 | BOOST_TEST(!fix.reader.done()); |
365 | BOOST_TEST(fix.buffsize() == 0u); |
366 | |
367 | // Resize the buffer |
368 | fix.reader.prepare_buffer(); |
369 | fix.record_buffer_first(); |
370 | BOOST_TEST(fix.buffsize() >= 4u); |
371 | BOOST_TEST(fix.buffsize() < 50u); |
372 | |
373 | // Read the header. The buffer didn't reallocate |
374 | fix.read_bytes(num_bytes: 4); |
375 | BOOST_TEST(!fix.reader.done()); |
376 | fix.check_buffer_stability(); |
377 | |
378 | // Resize the buffer again |
379 | fix.reader.prepare_buffer(); |
380 | fix.record_buffer_first(); |
381 | BOOST_TEST(fix.buffsize() >= 50u); |
382 | |
383 | // Finish reading |
384 | fix.read_bytes(num_bytes: 50); |
385 | fix.check_message(expected: u8vec(50, 0x04)); |
386 | BOOST_TEST(fix.seqnum == 43u); |
387 | } |
388 | |
389 | BOOST_AUTO_TEST_CASE(buffer_resizing_old_messages_removed) |
390 | { |
391 | // prepare_buffer removes old messages |
392 | // so the buffer doesn't grow indefinitely |
393 | |
394 | // Setup |
395 | reader_fixture fix(create_frame(seqnum: 42, body: u8vec(60, 0x04)), 0); |
396 | |
397 | // Parse an entire message, to make space in the buffer |
398 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
399 | fix.read_until_completion(); |
400 | fix.check_message(expected: u8vec(60, 0x04)); |
401 | |
402 | // Record size, as this should not increase |
403 | const std::size_t old_size = fix.buffsize(); |
404 | BOOST_TEST(old_size >= 60u); |
405 | |
406 | // Parse new messages |
407 | for (std::uint8_t i = 0u; i < 100u; ++i) |
408 | { |
409 | // Setup |
410 | u8vec msg_body(50, i); |
411 | fix.seqnum = i; |
412 | fix.set_contents(create_frame(seqnum: i, body: msg_body)); |
413 | |
414 | // Prepare read |
415 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
416 | |
417 | // Read the message into the buffer and trigger the op until completion. |
418 | // This will call prepare_buffer() internally |
419 | fix.read_until_completion(); |
420 | |
421 | // Check results |
422 | BOOST_TEST_REQUIRE(fix.reader.error() == error_code()); |
423 | BOOST_MYSQL_ASSERT_BUFFER_EQUALS(fix.reader.message(), msg_body); |
424 | } |
425 | |
426 | // Buffer size should be the same |
427 | BOOST_TEST(fix.buffsize() == old_size); |
428 | } |
429 | |
430 | // Keep parsing state |
431 | BOOST_AUTO_TEST_CASE(keep_state_continuation) |
432 | { |
433 | // Setup. We use a multiframe message to verify that we update the sequence number reference correctly |
434 | u8vec msg1_body{0x01, 0x02, 0x03}; |
435 | u8vec msg2_body(65, 0x04); |
436 | reader_fixture fix( |
437 | buffer_builder() |
438 | .add(value: create_frame(seqnum: 42, body: msg1_body)) |
439 | .add(value: create_frame(seqnum: 43, body: u8vec(64, 0x04))) |
440 | .add(value: create_frame(seqnum: 44, body: {0x04})) |
441 | .build(), |
442 | 16 |
443 | ); |
444 | |
445 | // Read the first message and part of the second |
446 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
447 | fix.read_bytes(num_bytes: 16); |
448 | auto msg = fix.check_message(expected: {0x01, 0x02, 0x03}); |
449 | BOOST_TEST(fix.seqnum == 43u); |
450 | |
451 | // Prepare the second read. We don't have enough bytes or buffer space |
452 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
453 | BOOST_TEST(!fix.reader.done()); |
454 | fix.check_buffer_stability(); // Didn't reallocate |
455 | BOOST_MYSQL_ASSERT_BUFFER_EQUALS(msg, msg1_body); // Old message still valid |
456 | BOOST_TEST(fix.seqnum == 44u); // Updated to the last received seqnum |
457 | |
458 | // Prepare a read as a continuation. This will not throw away the parsing state |
459 | std::uint8_t new_seqnum{fix.seqnum}; |
460 | fix.reader.prepare_read(sequence_number&: new_seqnum, keep_state: true); |
461 | fix.read_until_completion(); |
462 | fix.check_message(expected: msg2_body); |
463 | BOOST_TEST(fix.seqnum == 44u); // Old seqnum not updated |
464 | BOOST_TEST(new_seqnum == 45u); // New seqnum updated |
465 | } |
466 | |
467 | BOOST_AUTO_TEST_CASE(keep_state_done) |
468 | { |
469 | // Passing keep_state=true won't have effect if the operation is already done |
470 | reader_fixture fix( |
471 | buffer_builder().add(value: create_frame(seqnum: 42, body: {0x01, 0x02, 0x03})).add(value: create_frame(seqnum: 43, body: {0x04, 0x05})).build() |
472 | ); |
473 | |
474 | // Read the first message |
475 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
476 | fix.read_bytes(num_bytes: 7); |
477 | fix.check_message(expected: {0x01, 0x02, 0x03}); |
478 | BOOST_TEST(fix.seqnum == 43u); |
479 | |
480 | // Prepare a read as a continuation. As the operation is done, this will reset parsing state |
481 | fix.reader.prepare_read(sequence_number&: fix.seqnum, keep_state: true); |
482 | BOOST_TEST(!fix.reader.done()); |
483 | fix.read_bytes(num_bytes: 6); |
484 | fix.check_message(expected: {0x04, 0x05}); |
485 | BOOST_TEST(fix.seqnum == 44u); |
486 | } |
487 | |
488 | BOOST_AUTO_TEST_CASE(keep_state_initial) |
489 | { |
490 | // Passing keep_state=true with a reader that hasn't been used works |
491 | reader_fixture fix(create_frame(seqnum: 42, body: {0x01, 0x02, 0x03})); |
492 | fix.reader.prepare_read(sequence_number&: fix.seqnum, keep_state: true); |
493 | fix.read_bytes(num_bytes: 7); |
494 | fix.check_message(expected: {0x01, 0x02, 0x03}); |
495 | BOOST_TEST(fix.seqnum == 43u); |
496 | } |
497 | |
498 | // Resetting |
499 | BOOST_AUTO_TEST_CASE(reset_done) |
500 | { |
501 | // Read a message until completion |
502 | reader_fixture fix(create_frame(seqnum: 42, body: {0x01, 0x02, 0x03})); |
503 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
504 | fix.read_until_completion(); |
505 | |
506 | // Reset |
507 | fix.reader.reset(); |
508 | |
509 | // A new message can be read now |
510 | fix.set_contents(create_frame(seqnum: 20, body: {0x09, 0x0a})); |
511 | fix.seqnum = 20; |
512 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
513 | fix.read_until_completion(); |
514 | fix.check_message(expected: {0x09, 0x0a}); |
515 | fix.check_buffer_stability(); // No reallocation happened |
516 | BOOST_TEST(fix.seqnum == 21u); |
517 | } |
518 | |
519 | BOOST_AUTO_TEST_CASE(reset_message_half_read) |
520 | { |
521 | // Read part of a message |
522 | reader_fixture fix(create_frame(seqnum: 42, body: {0x01, 0x02, 0x03})); |
523 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
524 | fix.read_bytes(num_bytes: 3); |
525 | |
526 | // Reset |
527 | fix.reader.reset(); |
528 | |
529 | // A new message can be read now |
530 | fix.set_contents(create_frame(seqnum: 20, body: {0x09, 0x0a})); |
531 | fix.seqnum = 20; |
532 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
533 | fix.read_until_completion(); |
534 | fix.check_message(expected: {0x09, 0x0a}); |
535 | fix.check_buffer_stability(); // No reallocation happened |
536 | BOOST_TEST(fix.seqnum == 21u); |
537 | } |
538 | |
539 | BOOST_AUTO_TEST_CASE(reset_keep_state_true) |
540 | { |
541 | // Read part of a message |
542 | reader_fixture fix(create_frame(seqnum: 42, body: {0x01, 0x02, 0x03})); |
543 | fix.reader.prepare_read(sequence_number&: fix.seqnum, keep_state: true); |
544 | fix.read_bytes(num_bytes: 3); |
545 | |
546 | // Reset |
547 | fix.reader.reset(); |
548 | |
549 | // A new message can be read now |
550 | fix.set_contents(create_frame(seqnum: 20, body: {0x09, 0x0a})); |
551 | fix.seqnum = 20; |
552 | fix.reader.prepare_read(sequence_number&: fix.seqnum); |
553 | fix.read_until_completion(); |
554 | fix.check_message(expected: {0x09, 0x0a}); |
555 | fix.check_buffer_stability(); // No reallocation happened |
556 | BOOST_TEST(fix.seqnum == 21u); |
557 | } |
558 | |
559 | BOOST_AUTO_TEST_SUITE_END() |
560 | |