8 #include "xclox/ntp/query.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;
22 : server1(32101, serverTracer1.callable())
23 , server2(32102, serverTracer2.callable())
24 , server3(32103, serverTracer3.callable())
25 , server4(32104, serverTracer4.callable())
26 , server5(32105, serverTracer5.callable())
29 Tracer<asio::ip::udp::endpoint, asio::error_code, const uint8_t*, size_t> serverTracer1, serverTracer2, serverTracer3, serverTracer4, serverTracer5;
30 Tracer<std::string, std::string, Query::Status, Packet, steady_clock::duration> queryTracer;
31 Server server1, server2, server3, server4, server5;
32 asio::thread_pool pool;
35 TEST_CASE_FIXTURE(Context,
"non-existing domain" * doctest::timeout(2))
37 const std::string& host =
"x.y";
39 CHECK(queryTracer.wait() == 1);
40 CHECK(queryTracer.find([&](
const std::string& name,
const std::string& address,
Query::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
41 return name == host && address.empty() && status == Query::Status::ResolveError && packet.isNull() && rtt == seconds(0);
45 TEST_CASE_FIXTURE(Context,
"non-existing server" * doctest::timeout(2))
47 const std::string& host =
"255.255.255.255";
49 CHECK(queryTracer.wait() == 1);
50 CHECK(queryTracer.find([&](
const std::string& name,
const std::string& address,
Query::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
51 return name == host && address == host +
":123" && status == Query::Status::SendError && !packet.isNull() && rtt > nanoseconds(0) && compare(rtt, milliseconds(1));
55 TEST_CASE_FIXTURE(Context,
"custom port" * doctest::timeout(2))
59 const std::string& host =
"255.255.255.255:ntp";
61 CHECK(queryTracer.wait() == 1);
62 CHECK(queryTracer.find([&](
const std::string& name,
const std::string& address,
Query::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
63 return name == host && address ==
"255.255.255.255:123" && status == Query::Status::SendError && !packet.isNull() && rtt > nanoseconds(0) && compare(rtt, milliseconds(1));
68 const std::string& host =
"255.255.255.255:123";
70 CHECK(queryTracer.wait() == 1);
71 CHECK(queryTracer.find([&](
const std::string& name,
const std::string& address,
Query::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
72 return name == host && address == host && status == Query::Status::SendError && !packet.isNull() && rtt > nanoseconds(0) && compare(rtt, milliseconds(1));
77 TEST_CASE_FIXTURE(Context,
"bogus server" * doctest::timeout(2))
80 server1.replay(&data, 1);
81 const std::string& host = stringify(server1.endpoint());
83 CHECK(queryTracer.wait() == 1);
84 CHECK(queryTracer.find([&](
const std::string& name,
const std::string& address,
Query::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
85 return name == host && address == host && status == Query::Status::ReceiveError && packet.isNull() && rtt > nanoseconds(0) && compare(rtt, milliseconds(1));
89 TEST_CASE_FIXTURE(Context,
"success" * doctest::timeout(2))
91 server1.replay(
nullptr, 0, milliseconds(100));
92 const std::string& host = stringify(server1.endpoint());
94 CHECK(queryTracer.wait() == 1);
95 CHECK(queryTracer.find([&](
const std::string& name,
const std::string& address,
Query::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
96 return name == host && address == host && status == Query::Status::Succeeded && !packet.isNull() && compare(rtt, milliseconds(100));
100 TEST_CASE_FIXTURE(Context,
"non-blocking" * doctest::timeout(2))
102 server1.replay(
nullptr, 0, milliseconds(200));
103 const std::string& host = stringify(server1.endpoint());
104 const auto& start = steady_clock::now();
106 CHECK(compare(start, milliseconds(1)));
107 CHECK(queryTracer.wait() == 1);
108 CHECK(queryTracer.find([&](
const std::string& name,
const std::string& address,
Query::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
109 return name == host && address == host && status == Query::Status::Succeeded && !packet.isNull() && compare(rtt, milliseconds(200));
113 TEST_CASE_FIXTURE(Context,
"traceable" * doctest::timeout(2))
115 const std::string host =
"x.y";
116 auto query =
Query::start(pool, host, queryTracer.callable());
117 CHECK_FALSE(query.expired());
118 CHECK(queryTracer.wait() == 1);
119 CHECK(query.expired());
122 TEST_CASE_FIXTURE(Context,
"no callback" * doctest::timeout(1))
124 auto query =
Query::start(pool,
"254.254.254.254:1234", {});
125 CHECK(query.expired());
128 TEST_CASE_FIXTURE(Context,
"timeout - lookup" * doctest::timeout(11))
130 const auto& start = steady_clock::now();
131 const std::string& host =
"1234567890";
132 const auto& timeoutMs = 100;
133 auto query =
Query::start(pool, host, queryTracer.callable(), milliseconds(timeoutMs));
134 CHECK_FALSE(query.expired());
135 CHECK(queryTracer.wait() == 1);
136 CHECK(queryTracer.find([&](
const std::string& name,
const std::string& address,
Query::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
137 return name == host && address ==
"" && status == Query::Status::TimeoutError && packet.isNull() && rtt == seconds(0);
139 CHECK(query.expired());
140 CHECK(compare(start, milliseconds(timeoutMs)));
143 TEST_CASE_FIXTURE(Context,
"timeout - query" * doctest::timeout(2))
145 for (
int i = 0; i < 3; ++i) {
147 const auto& start = steady_clock::now();
148 const std::string& host = stringify(server1.endpoint());
149 auto query =
Query::start(pool, host, queryTracer.callable(), milliseconds(i * 100));
150 CHECK_FALSE(query.expired());
151 CHECK(queryTracer.wait() == 1);
152 CHECK(queryTracer.find([&](
const std::string& name,
const std::string& address,
Query::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
153 return name == host && address ==
"" && status == Query::Status::TimeoutError && packet.isNull() && rtt == seconds(0);
155 CHECK(compare(start, milliseconds(i * 100)));
156 CHECK(query.expired());
161 TEST_CASE_FIXTURE(Context,
"cancellable during lookup" * doctest::timeout(11))
163 const auto& start = steady_clock::now();
164 const std::string& host =
"1234567890";
165 auto query =
Query::start(pool, host, queryTracer.callable());
166 CHECK_FALSE(query.expired());
167 query.lock()->cancel();
168 CHECK(queryTracer.wait() == 1);
169 CHECK(queryTracer.find([&](
const std::string& name,
const std::string& address,
Query::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
170 return name == host && address ==
"" && status == Query::Status::Cancelled && packet.isNull() && rtt == seconds(0);
172 CHECK(query.expired());
173 CHECK(compare(start, milliseconds(1)));
176 TEST_CASE_FIXTURE(Context,
"cancellable during query - multiple times" * doctest::timeout(4))
178 const auto& start = steady_clock::now();
179 server1.replay(
nullptr, 0, milliseconds(400));
180 const std::string& host = stringify(server1.endpoint());
181 auto query =
Query::start(pool, host, queryTracer.callable());
182 CHECK(serverTracer1.wait() == 1);
183 CHECK_FALSE(query.expired());
184 for (
int i = 0; i < 10; ++i) {
185 asio::post(pool, [query] {
186 if (
auto handle = query.lock()) {
187 std::this_thread::sleep_for(milliseconds(10));
192 CHECK(queryTracer.wait() == 1);
193 CHECK(queryTracer.find([&](
const std::string& name,
const std::string& address,
Query::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
194 return name == host && address ==
"" && status == Query::Status::Cancelled && packet.isNull() && rtt == seconds(0);
197 std::this_thread::sleep_for(milliseconds(100));
198 CHECK(query.expired());
199 CHECK(serverTracer1.counter() == 1);
200 CHECK(serverTracer1.wait(2) == 2);
201 CHECK(compare(start, milliseconds(400)));
204 TEST_CASE_FIXTURE(Context,
"cancellable concurrently" * doctest::timeout(2))
206 std::vector<Server*> serverList { &server1, &server2, &server3, &server4, &server5 };
207 const size_t QueryCount = serverList.size();
208 for (
size_t j = 0; j < QueryCount; ++j) {
209 for (
size_t i = 0; i < QueryCount; ++i) {
210 serverList.at(i)->replay(
nullptr, 0, milliseconds(1));
211 auto query =
Query::start(pool, stringify(serverList.at(i)->endpoint()), queryTracer.callable(), milliseconds(0));
212 asio::post(pool, [query] {
213 if (
auto shared = query.lock()) {
218 CHECK(queryTracer.wait(QueryCount) == QueryCount);
223 TEST_CASE_FIXTURE(Context,
"domain name" * doctest::timeout(6))
225 const std::string& host =
"time.windows.com";
227 CHECK(queryTracer.wait() == 1);
228 CHECK(queryTracer.find([&](
const std::string& name,
const std::string& address,
Query::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
229 return name == host && !address.empty() && asio::ip::make_address(address.substr(0, address.size() - 4)).is_v4() && status == Query::Status::Succeeded && isServerPacket(packet) && rtt < seconds(1);
Packet is an immutable raw NTP packet.
Definition: packet.hpp:54
static std::weak_ptr< Query > start(asio::thread_pool &pool, const std::string &server, Callback callback, const std::chrono::milliseconds &timeout=std::chrono::milliseconds(DefaultTimeout::ms))
Starts querying all resolved addresses of server one at a time until success.
Definition: query.hpp:87
Status
Type of query status.
Definition: query.hpp:55