JayBeams  0.1
Another project to have fun coding.
ut_mold_udp_channel.cpp
Go to the documentation of this file.
5 #include <jb/itch5/timestamp.hpp>
7 
8 #include <jb/gmock/init.hpp>
9 #include <boost/test/unit_test.hpp>
10 
11 /**
12  * Helper types and functions to test jb::itch5::mold_udp_channel
13  */
14 namespace {
15 std::vector<char>
16 create_mold_udp_packet(std::uint64_t sequence_number, int message_count) {
17  std::size_t const max_packet_size = 1 << 16;
18  char packet[max_packet_size] = {0};
19 
21  max_packet_size, packet,
24  max_packet_size, packet, jb::itch5::mold_udp_protocol::block_count_offset,
25  message_count);
26 
27  std::size_t packet_size = jb::itch5::mold_udp_protocol::header_size;
28  boost::asio::mutable_buffer dst(packet, packet_size);
29 
30  char msg_type = 'A';
31  int ts = 5;
32  for (int i = 0; i != message_count; ++i) {
34  msg_type, jb::itch5::timestamp{std::chrono::microseconds(ts)}, 64);
35  ts += 5;
36  msg_type++;
38  max_packet_size, packet, packet_size, message.size());
39  packet_size += 2;
40  boost::asio::buffer_copy(dst + packet_size, boost::asio::buffer(message));
41  packet_size += message.size();
42  }
43  return std::vector<char>(packet, packet + packet_size);
44 }
45 
46 /**
47  * Pick a localhost address that is valid on the testing host.
48  *
49  * In some testing hosts (notably travis-ci.org) the host does not
50  * support IPv6 addresses. We need to determine, at run-time, a valid
51  * address to test the code. A separate test
52  * (ut_make_socket_upd_recv) validates that the library works with any
53  * address and fails gracefully. In this test we just want to move
54  * forward.
55  *
56  * @param io the Boost.ASIO io service
57  * @returns a string with the valid localhost address, typically ::1,
58  * but can be 127.0.0.1 if IPv6 is not functional
59  * @throws std::exception if no valid localhost address is found
60  */
61 std::string select_localhost_address(boost::asio::io_service& io) {
62  for (auto const& addr : {"::1", "127.0.0.1"}) {
63  try {
64  BOOST_TEST_CHECKPOINT("Checking " << addr << " as the localhost address");
65  auto socket = jb::itch5::make_socket_udp_recv(
66  io, jb::itch5::udp_receiver_config().address(addr).port(40000));
67  return addr;
68  } catch (...) {
69  }
70  }
71  BOOST_TEST_MESSAGE("No valid localhost address found, aborting");
72  throw std::runtime_error("Cannot find valid IPv6 or IPv4 address");
73 }
74 } // anonymous namespace
75 
76 namespace jb {
77 namespace itch5 {
78 /**
79  * Break encapsulation in jb::itch5::mold_udp_channel for testing purposes.
80  */
83  tested.handle_received(boost::system::error_code(), 0);
84  }
85  static void call_with_error_code(mold_udp_channel& tested) {
86  tested.handle_received(
87  boost::asio::error::make_error_code(boost::asio::error::network_down),
88  16);
89  }
90 };
91 
92 } // namespace itch5
93 } // namespace jb
94 
95 /**
96  * @test Verify that jb::itch5::mold_udp_channel works.
97  */
98 BOOST_AUTO_TEST_CASE(itch5_mold_udp_channel_basic) {
99  struct mock_function {
100  MOCK_METHOD5(
101  method, void(
103  std::size_t, std::string, std::size_t));
104  };
105  mock_function mock;
106  auto adapter = [&mock](
107  std::chrono::steady_clock::time_point ts, std::uint64_t seqno,
108  std::size_t offset, char const* msg, std::size_t msgsize) {
109  mock.method(ts, seqno, offset, std::string(msg, msgsize), msgsize);
110  };
111 
112  using boost::asio::ip::udp;
113 
114  boost::asio::io_service io;
115  auto local = select_localhost_address(io);
116  BOOST_TEST_MESSAGE("Running test on " << local);
117 
119  io, adapter, jb::itch5::udp_receiver_config().port(50000).address(local));
120 
121  udp::resolver resolver(io);
122  udp::endpoint send_to;
123  udp::endpoint send_from;
124  auto d_address = boost::asio::ip::address::from_string(local);
125  if (d_address.is_v6()) {
126  BOOST_TEST_CHECKPOINT(
127  "Resolving dst/src IPv6 addresses for " << local << ":50000");
128  send_to = *resolver.resolve({udp::v6(), local, "50000"});
129  send_from = udp::endpoint(udp::v6(), 0);
130  } else {
131  BOOST_TEST_CHECKPOINT(
132  "Resolving dst/src IPv4 addresses for " << local << ":50000");
133  send_to = *resolver.resolve({udp::v4(), local, "50000"});
134  send_from = udp::endpoint(udp::v4(), 0);
135  }
136  udp::socket socket(io, send_from);
137 
138  using namespace ::testing;
139  EXPECT_CALL(mock, method(_, _, _, _, _)).Times(3);
140  auto packet = create_mold_udp_packet(0, 3);
141  socket.send_to(boost::asio::buffer(packet), send_to);
142  io.run_one();
143 
144  EXPECT_CALL(mock, method(_, _, _, _, _)).Times(3);
145  packet = create_mold_udp_packet(0, 3);
146  socket.send_to(boost::asio::buffer(packet), send_to);
147  io.run_one();
148 
149  EXPECT_CALL(mock, method(_, _, _, _, _)).Times(2);
150  packet = create_mold_udp_packet(9, 2);
151  socket.send_to(boost::asio::buffer(packet), send_to);
152  io.run_one();
153 
154  EXPECT_CALL(mock, method(_, _, _, _, _)).Times(1);
155  packet = create_mold_udp_packet(12, 1);
156  socket.send_to(boost::asio::buffer(packet), send_to);
157  io.run_one();
158 
159  EXPECT_CALL(mock, method(_, _, _, _, _)).Times(0);
160  packet = create_mold_udp_packet(13, 0);
161  socket.send_to(boost::asio::buffer(packet), send_to);
162  io.run_one();
163 }
164 
165 /**
166  * @test Comlete code coverage for jb::itch5::mold_udp_channel.
167  */
168 BOOST_AUTO_TEST_CASE(itch5_mold_udp_channel_coverage) {
169  auto adapter =
170  [](std::chrono::steady_clock::time_point ts, std::uint64_t seqno,
171  std::size_t offset, char const* msg, std::size_t msgsize) {};
172 
173  using boost::asio::ip::udp;
174 
175  boost::asio::io_service io;
176  auto local = select_localhost_address(io);
178  io, adapter, jb::itch5::udp_receiver_config().port(50000).address(local));
179 
182 
183  jb::itch5::mold_udp_channel::buffer_handler const handler(adapter);
185  io, handler, jb::itch5::udp_receiver_config().port(50000).address(local));
186 
189 }
clock_type::time_point time_point
A convenience alias for clock_type::time_point.
static void w(std::size_t size, void *msg, std::size_t offset, T const &x)
Write a single message or field to a buffer.
socket_t make_socket_udp_recv(boost::asio::io_service &io, udp_receiver_config const &cfg)
Create a socket given the configuration parameters.
std::function< void(std::chrono::steady_clock::time_point, std::uint64_t, std::size_t, char const *, std::size_t)> buffer_handler
A callback function type to process any received ITCH-5.0 messages.
static void call_with_error_code(mold_udp_channel &tested)
constexpr std::size_t sequence_number_offset
The location of the sequence number field within the header.
BOOST_AUTO_TEST_CASE(itch5_mold_udp_channel_basic)
void handle_received(boost::system::error_code const &ec, size_t bytes_received)
The Boost.ASIO callback for I/O events.
Break encapsulation in jb::itch5::mold_udp_channel for testing purposes.
Represent a ITCH-5.0 timestamp.
Definition: timestamp.hpp:17
std::vector< char > create_message(int message_type, jb::itch5::timestamp ts, std::size_t total_size)
Generate test messages with a more-or-less valid header.
Definition: data.cpp:287
Initialize GMock to work with Boost.Test.
static void call_with_empty_packet(mold_udp_channel &tested)
constexpr std::size_t header_size
The total size of the MoldUDP64 header.
constexpr std::size_t block_count_offset
The location of the block count field within the header.
A configuration object for UDP receivers.
Create and manage a socket to receive MoldUDP64 packets.
The top-level namespace for the JayBeams library.
Definition: as_hhmmss.hpp:7