8 #include "xclox/ntp/query_single.hpp"
10 #include "tools/server.hpp"
11 #include "tools/tracer.hpp"
13 #include "tools/helper.hpp"
15 using namespace xclox::ntp;
16 using namespace std::chrono;
18 TEST_SUITE(
"QuerySingle")
22 : server(32101, serverTracer.callable())
25 Tracer<asio::ip::udp::endpoint, asio::error_code, const uint8_t*, size_t> serverTracer;
26 Tracer<asio::ip::udp::endpoint, asio::error_code, Packet, steady_clock::duration> queryTracer;
31 TEST_CASE_FIXTURE(Context,
"no callback")
37 TEST_CASE_FIXTURE(Context,
"non-exising server")
41 CHECK(queryTracer.counter() == 1);
42 CHECK(queryTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const Packet& packet,
const steady_clock::duration& rtt) {
43 return endpoint == broadcastEndpoint && error == asio::error::access_denied && isClientPacket(packet) && compare(rtt, milliseconds(1));
47 TEST_CASE_FIXTURE(Context,
"bogus server")
49 std::array<uint8_t, 9> sendData {};
50 server.replay(sendData.data(), sendData.size());
53 CHECK(serverTracer.wait(2) == 2);
54 CHECK(serverTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const uint8_t* data,
size_t size) {
55 Packet::DataType buffer;
56 std::memcpy(buffer.data(), data, size);
57 return endpoint.address() == server.endpoint().address() && !error && isClientPacket(Packet(buffer)) && size == std::tuple_size<Packet::DataType> {};
59 CHECK(serverTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const uint8_t* data,
size_t size) {
60 return endpoint.address() == server.endpoint().address() && !error && std::memcmp(data, sendData.data(), sendData.size()) == 0 && size == sendData.size();
62 CHECK(queryTracer.counter() == 1);
63 CHECK(queryTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const Packet& packet,
const steady_clock::duration& rtt) {
64 return endpoint == server.endpoint() && error == asio::error::message_size && packet.isNull() && compare(rtt, milliseconds(1));
68 TEST_CASE_FIXTURE(Context,
"valid server")
73 CHECK(serverTracer.wait(2) == 2);
74 const uint8_t* recvData =
nullptr;
76 CHECK(serverTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const uint8_t* data,
size_t size) {
79 return endpoint.address() == server.endpoint().address() && !error && size == std::tuple_size<Packet::DataType> {};
81 CHECK(queryTracer.counter() == 1);
82 CHECK(queryTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const Packet& packet,
const steady_clock::duration& rtt) {
83 return endpoint == server.endpoint() && !error && std::memcmp(packet.data().data(), recvData, recvSize) == 0 && isClientPacket(packet) && compare(rtt, milliseconds(1));
87 TEST_CASE_FIXTURE(Context,
"round-trip time")
93 CHECK(queryTracer.counter() == 1);
94 CHECK(queryTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const Packet& packet,
const steady_clock::duration& rtt) {
95 return endpoint == broadcastEndpoint && error == asio::error::access_denied && isClientPacket(packet) && compare(rtt, milliseconds(1));
98 SUBCASE(
"receive error")
102 std::this_thread::sleep_for(milliseconds(100));
106 CHECK(serverTracer.wait() == 1);
107 asio::ip::udp::endpoint sender;
108 CHECK(serverTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const uint8_t*,
size_t) {
112 std::this_thread::sleep_for(milliseconds(100));
113 server.send(sender,
nullptr, 0);
114 CHECK(queryTracer.wait() == 1);
115 CHECK(queryTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const Packet& packet,
const steady_clock::duration& rtt) {
116 return endpoint == server.endpoint() && error == asio::error::message_size && packet.isNull() && compare(rtt, milliseconds(200));
119 SUBCASE(
"successful query")
123 std::this_thread::sleep_for(milliseconds(200));
127 CHECK(serverTracer.wait() == 1);
128 asio::ip::udp::endpoint sender;
130 CHECK(serverTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const uint8_t* data,
size_t size) {
132 std::memcpy(buffer.data(), data, size);
135 std::this_thread::sleep_for(milliseconds(200));
136 server.send(sender, buffer.data(), buffer.size());
137 CHECK(queryTracer.wait() == 1);
138 CHECK(queryTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const Packet& packet,
const steady_clock::duration& rtt) {
139 return endpoint == server.endpoint() && !error && isClientPacket(packet) && compare(rtt, milliseconds(400));
144 TEST_CASE_FIXTURE(Context,
"custom timeout")
146 for (
int i = 0; i < 3; ++i) {
147 const auto& start = steady_clock::now();
149 QuerySingle::start(io, server.endpoint(), queryTracer.callable(), milliseconds(i * 100));
151 CHECK(compare(start, milliseconds(i * 100)));
152 CHECK(queryTracer.counter() == 1);
153 CHECK(queryTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const Packet& packet,
const steady_clock::duration& rtt) {
154 return endpoint == server.endpoint() && error == asio::error::timed_out && packet.isNull() && compare(rtt, milliseconds(i * 100));
156 CHECK(serverTracer.wait() == 1);
158 serverTracer.reset();
163 TEST_CASE_FIXTURE(Context,
"default timeout")
165 const auto& start = steady_clock::now();
169 CHECK(compare(start, milliseconds(QuerySingle::DefaultTimeout::ms)));
170 CHECK(queryTracer.counter() == 1);
171 CHECK(queryTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const Packet& packet,
const steady_clock::duration& rtt) {
172 return endpoint == server.endpoint() && error == asio::error::timed_out && packet.isNull() && compare(rtt, milliseconds(QuerySingle::DefaultTimeout::ms));
174 CHECK(serverTracer.counter() == 1);
177 TEST_CASE_FIXTURE(Context,
"traceable")
182 CHECK(query.expired());
184 SUBCASE(
"unsuccessful query")
187 CHECK_FALSE(query.expired());
189 CHECK(queryTracer.counter() == 1);
190 CHECK(queryTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const Packet&,
const steady_clock::duration&) {
191 return endpoint == broadcastEndpoint && error == asio::error::access_denied;
193 CHECK(query.expired());
195 SUBCASE(
"successful query")
199 CHECK_FALSE(query.expired());
201 CHECK(queryTracer.counter() == 1);
202 CHECK(queryTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const Packet&,
const steady_clock::duration&) {
203 return endpoint == server.endpoint() && !error;
205 CHECK(query.expired());
209 TEST_CASE_FIXTURE(Context,
"cancellable")
214 query.lock()->cancel();
216 CHECK(query.expired());
217 CHECK(queryTracer.wait() == 1);
218 CHECK(queryTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const Packet& packet,
const steady_clock::duration& rtt) {
219 return endpoint == server.endpoint() && error == asio::error::operation_aborted && packet.isNull() && compare(rtt, milliseconds(1));
221 CHECK(serverTracer.counter() == 0);
223 SUBCASE(
"on receive")
230 CHECK(serverTracer.wait() == 1);
231 query.lock()->cancel();
232 CHECK(queryTracer.wait() == 1);
233 CHECK(queryTracer.find([&](
const asio::ip::udp::endpoint& endpoint,
const asio::error_code& error,
const Packet& packet,
const steady_clock::duration& rtt) {
234 return endpoint == server.endpoint() && error == asio::error::operation_aborted && packet.isNull() && compare(rtt, milliseconds(1));
236 CHECK(query.expired());
242 auto handle = query.lock();
246 CHECK(serverTracer.wait() == 1);
248 CHECK(queryTracer.wait() == 1);
251 CHECK(io.run() == 0);
Packet is an immutable raw NTP packet.
Definition: packet.hpp:54
internal::DataType DataType
Type of packet's underlying data.
Definition: packet.hpp:56
static std::weak_ptr< QuerySingle > start(asio::io_context &io, const asio::ip::udp::endpoint &server, Callback callback, const std::chrono::milliseconds &timeout=std::chrono::milliseconds(DefaultTimeout::ms))
Starts querying the given endpoint.
Definition: query_single.hpp:68