8 #include "xclox/ntp/client.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, Client::Status, Packet, steady_clock::duration> clientTracer;
31 Server server1, server2, server3, server4, server5;
34 TEST_CASE_FIXTURE(Context,
"query" * doctest::timeout(6))
36 Client client(clientTracer.callable());
38 client.query(
"255.255.255.255");
39 client.query(
"time.windows.com");
40 CHECK(clientTracer.wait(3, seconds(5)) == 3);
41 CHECK(clientTracer.find([&](
const std::string& name,
const std::string& address,
Client::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
42 return name ==
"x.y" && address ==
"" && status == Client::Status::ResolveError && packet.isNull() && rtt == seconds(0);
44 CHECK(clientTracer.find([&](
const std::string& name,
const std::string& address,
Client::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
45 return name ==
"255.255.255.255" && address ==
"255.255.255.255:123" && status == Client::Status::SendError && isClientPacket(packet) && rtt < seconds(1);
47 CHECK(clientTracer.find([&](
const std::string& name,
const std::string& address,
Client::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
48 return name ==
"time.windows.com" && !address.empty() && asio::ip::make_address(address.substr(0, address.size() - 4)).is_v4() && status == Client::Status::Succeeded && isServerPacket(packet) && rtt < seconds(5);
52 TEST_CASE_FIXTURE(Context,
"reset callback" * doctest::timeout(3))
54 Client client(clientTracer.callable());
56 CHECK(clientTracer.wait(1) == 1);
57 client.setCallback({});
59 CHECK(clientTracer.wait(2) == 1);
62 TEST_CASE_FIXTURE(Context,
"query concurrently" * doctest::timeout(5))
64 Client client(clientTracer.callable());
65 const std::string& host1 = stringify(server1.endpoint());
66 const std::string& host2 = stringify(server2.endpoint());
67 const std::string& host3 = stringify(server3.endpoint());
68 const int QueryCount = 99;
69 server1.loop(QueryCount);
70 server2.loop(QueryCount);
71 server3.loop(QueryCount);
72 asio::thread_pool pool;
73 for (
int i = 0; i < QueryCount; ++i) {
74 asio::post(pool, [&] { client.query(host1); });
75 asio::post(pool, [&] { client.query(host2); });
76 asio::post(pool, [&] { client.query(host3); });
79 CHECK(serverTracer1.wait(QueryCount * 2) == QueryCount * 2);
80 CHECK(serverTracer2.wait(QueryCount * 2) == QueryCount * 2);
81 CHECK(serverTracer3.wait(QueryCount * 2) == QueryCount * 2);
82 CHECK(clientTracer.wait(QueryCount * 3) == QueryCount * 3);
83 CHECK(clientTracer.find([&](
const std::string& name,
const std::string& address,
Client::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
84 return name == host1 && address == host1 && status == Client::Status::Succeeded && isClientPacket(packet) && rtt < seconds(1);
86 CHECK(clientTracer.find([&](
const std::string& name,
const std::string& address,
Client::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
87 return name == host2 && address == host2 && status == Client::Status::Succeeded && isClientPacket(packet) && rtt < seconds(1);
89 CHECK(clientTracer.find([&](
const std::string& name,
const std::string& address,
Client::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
90 return name == host3 && address == host3 && status == Client::Status::Succeeded && isClientPacket(packet) && rtt < seconds(1);
94 TEST_CASE_FIXTURE(Context,
"cancel queries" * doctest::timeout(4))
96 const std::string& host = stringify(server1.endpoint());
97 Client client(clientTracer.callable());
99 server1.replay(
nullptr, 0, milliseconds(100));
101 CHECK(serverTracer1.wait(1) == 1);
103 client.query(
"255.255.255.255");
104 CHECK(clientTracer.wait(2) == 2);
105 CHECK(clientTracer.find([&](
const std::string& name,
const std::string& address,
Client::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
106 return name == host && address ==
"" && status == Client::Status::Cancelled && packet.isNull() && rtt == seconds(0);
108 CHECK(clientTracer.find([&](
const std::string& name,
const std::string& address,
Client::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
109 return name ==
"255.255.255.255" && address ==
"255.255.255.255:123" && status == Client::Status::SendError && isClientPacket(packet) && rtt < seconds(1);
115 CHECK(clientTracer.wait(3) == 3);
116 CHECK(clientTracer.find([&](
const std::string& name,
const std::string& address,
Client::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
117 return name == host && address == host && status == Client::Status::Succeeded && isClientPacket(packet) && rtt < seconds(1);
121 TEST_CASE_FIXTURE(Context,
"cancel queries concurrently" * doctest::timeout(10))
123 std::vector<Server*> serverList { &server1, &server2, &server3, &server4, &server5 };
124 const size_t ServerCount = serverList.size();
125 const size_t CancelCount = 99;
126 Client client(clientTracer.callable());
127 asio::thread_pool pool;
128 for (
size_t j = 0; j < CancelCount; ++j) {
129 for (
size_t i = 0; i < ServerCount; ++i) {
130 serverList.at(i)->replay();
131 client.query(stringify(serverList.at(i)->endpoint()), milliseconds(i));
132 asio::post(pool, [&] { client.cancel(); });
134 CHECK(clientTracer.wait(ServerCount) == ServerCount);
135 clientTracer.reset();
139 TEST_CASE_FIXTURE(Context,
"wait all queries upon destruction" * doctest::timeout(11))
141 server1.replay(
nullptr, 0, milliseconds(50));
142 const std::string& host = stringify(server1.endpoint());
144 CHECK(clientTracer.wait() == 1);
145 CHECK(clientTracer.find([&](
const std::string& name,
const std::string& address,
Client::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
146 return name == host && address == host && status == Query::Status::Succeeded && isClientPacket(packet) && compare(rtt, milliseconds(50));
150 TEST_CASE_FIXTURE(Context,
"time out" * doctest::timeout(2))
152 const std::string& host = stringify(server1.endpoint());
153 for (
int i = 0; i < 3; ++i) {
154 const auto& start = steady_clock::now();
156 Client client(clientTracer.callable());
157 client.query(host, milliseconds(i * 100));
158 CHECK(clientTracer.wait() == 1);
159 CHECK(clientTracer.find([&](
const std::string& name,
const std::string& address,
Query::Status status,
const Packet& packet,
const steady_clock::duration& rtt) {
160 return name == host && address ==
"" && status == Query::Status::TimeoutError && packet.isNull() && rtt == seconds(0);
162 CHECK(compare(start, milliseconds(i * 100)));
163 clientTracer.reset();
Client is an asynchronous multi-query NTP client.
Definition: client.hpp:47
void query(const std::string &server, const std::chrono::milliseconds &timeout=std::chrono::milliseconds(DefaultTimeout::ms))
Place a NTP query [thread-safe].
Definition: client.hpp:80
Packet is an immutable raw NTP packet.
Definition: packet.hpp:54
Status
Type of query status.
Definition: query.hpp:55