Skip to content

Commit 97dbcdd

Browse files
authored
Paranoia test TSP client (#1844)
## Description Added paranoia checks to satisfy @Gold872 ## Meta Merge checklist: - [x] Pull Request title is [short, imperative summary](https://cbea.ms/git-commit/) of proposed changes - [x] The description documents the _what_ and _why_ - [ ] If this PR changes behavior or adds a feature, user documentation is updated - [ ] If this PR touches photon-serde, all messages have been regenerated and hashes have not changed unexpectedly - [ ] If this PR touches configuration, this is backwards compatible with settings back to v2024.3.1 - [x] If this PR addresses a bug, a regression test for it is added
1 parent 0ef7c80 commit 97dbcdd

File tree

3 files changed

+143
-21
lines changed

3 files changed

+143
-21
lines changed

photon-targeting/src/main/native/cpp/net/TimeSyncClient.cpp

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,31 @@ static void ClientLoggerFunc(unsigned int level, const char* file,
5757
line);
5858
}
5959

60+
void wpi::tsp::TimeSyncClient::UpdateStatistics(uint64_t pong_local_time,
61+
wpi::tsp::TspPing ping,
62+
wpi::tsp::TspPong pong) {
63+
// when time = send_time+rtt2/2, server time = server time
64+
// server time = local time + offset
65+
// offset = (server time - local time) = (server time) - (send_time +
66+
// rtt2/2)
67+
auto rtt2 = pong_local_time - ping.client_time;
68+
int64_t serverTimeOffsetUs = pong.server_time - rtt2 / 2 - ping.client_time;
69+
70+
auto filtered = m_lastOffsets.Calculate(serverTimeOffsetUs);
71+
72+
// wpi::println("Ping-ponged! RTT2 {} uS, offset {}/filtered offset {} uS",
73+
// rtt2,
74+
// serverTimeOffsetUs, filtered);
75+
76+
{
77+
std::lock_guard lock{m_offsetMutex};
78+
m_metadata.offset = filtered;
79+
m_metadata.rtt2 = rtt2;
80+
m_metadata.pongsReceived++;
81+
m_metadata.lastPongTime = pong_local_time;
82+
}
83+
}
84+
6085
void wpi::tsp::TimeSyncClient::Tick() {
6186
// wpi::println("wpi::tsp::TimeSyncClient::Tick");
6287
// Regardless of if we've gotten a pong back yet, we'll ping again. this is
@@ -122,28 +147,9 @@ void wpi::tsp::TimeSyncClient::UdpCallback(uv::Buffer& buf, size_t nbytes,
122147
return;
123148
}
124149

125-
// when time = send_time+rtt2/2, server time = server time
126-
// server time = local time + offset
127-
// offset = (server time - local time) = (server time) - (send_time +
128-
// rtt2/2)
129-
auto rtt2 = pong_local_time - ping.client_time;
130-
int64_t serverTimeOffsetUs = pong.server_time - rtt2 / 2 - ping.client_time;
131-
132-
auto filtered = m_lastOffsets.Calculate(serverTimeOffsetUs);
133-
134-
// wpi::println("Ping-ponged! RTT2 {} uS, offset {}/filtered offset {} uS",
135-
// rtt2,
136-
// serverTimeOffsetUs, filtered);
137-
138-
{
139-
std::lock_guard lock{m_offsetMutex};
140-
m_metadata.offset = filtered;
141-
m_metadata.rtt2 = rtt2;
142-
m_metadata.pongsReceived++;
143-
m_metadata.lastPongTime = pong_local_time;
144-
}
150+
UpdateStatistics(pong_local_time, ping, pong);
145151

146-
using std::cout;
152+
// using std::cout;
147153
// wpi::println("Ping-ponged! RTT2 {} uS, offset {} uS", rtt2,
148154
// serverTimeOffsetUs);
149155
// wpi::println("Estimated server time {} s",

photon-targeting/src/main/native/include/net/TimeSyncClient.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ class TimeSyncClient {
9696
void Stop();
9797
int64_t GetOffset();
9898
Metadata GetMetadata();
99+
100+
// public for testability
101+
void UpdateStatistics(uint64_t pong_local_time, wpi::tsp::TspPing ping,
102+
wpi::tsp::TspPong pong);
99103
};
100104

101105
} // namespace tsp

photon-targeting/src/test/native/cpp/net/TimeSyncTest.cpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,115 @@ TEST(TimeSyncProtocolTest, Smoketest) {
3838

3939
server.Stop();
4040
}
41+
42+
TEST(TimeSyncClientTest, CalculateZero) {
43+
using namespace wpi::tsp;
44+
using namespace std::chrono_literals;
45+
46+
// GIVEN a fresh client
47+
TimeSyncClient client{"127.0.0.1", 5812, 100ms};
48+
49+
// AND a ping-pong sent with no delay
50+
// client -> server -> client
51+
uint64_t ping_client_time{100};
52+
uint64_t pong_server_time{100};
53+
uint64_t pong_client_time{100};
54+
55+
// setup our ping/pong packets
56+
TspPing ping{.version = 1, .message_id = 1, .client_time = ping_client_time};
57+
TspPong pong{ping, pong_server_time};
58+
59+
// WHEN we update statistics
60+
client.UpdateStatistics(pong_client_time, ping, pong);
61+
62+
// THEN the statistics will reflect no delay
63+
EXPECT_EQ(0, client.GetMetadata().offset);
64+
EXPECT_EQ(0, client.GetMetadata().rtt2);
65+
EXPECT_EQ(1u, client.GetMetadata().pongsReceived);
66+
EXPECT_EQ(pong_client_time, client.GetMetadata().lastPongTime);
67+
}
68+
69+
TEST(TimeSyncClientTest, CalculateZeroOffset) {
70+
using namespace wpi::tsp;
71+
using namespace std::chrono_literals;
72+
73+
// GIVEN a fresh client
74+
TimeSyncClient client{"127.0.0.1", 5812, 100ms};
75+
76+
// AND a ping-pong sent with 10ms delay each way
77+
// client -> server -> client
78+
uint64_t ping_client_time{100};
79+
uint64_t pong_server_time{110};
80+
uint64_t pong_client_time{120};
81+
82+
// setup our ping/pong packets
83+
TspPing ping{.version = 1, .message_id = 1, .client_time = ping_client_time};
84+
TspPong pong{ping, pong_server_time};
85+
86+
// WHEN we update statistics
87+
client.UpdateStatistics(pong_client_time, ping, pong);
88+
89+
// THEN the statistics will reflect no offset, and the expected rtt2
90+
// (client-to-client) latency
91+
EXPECT_EQ(0, client.GetMetadata().offset);
92+
EXPECT_EQ(20, client.GetMetadata().rtt2);
93+
EXPECT_EQ(1u, client.GetMetadata().pongsReceived);
94+
EXPECT_EQ(pong_client_time, client.GetMetadata().lastPongTime);
95+
}
96+
97+
TEST(TimeSyncClientTest, CalculateZeroRtt) {
98+
using namespace wpi::tsp;
99+
using namespace std::chrono_literals;
100+
101+
// GIVEN a fresh client
102+
TimeSyncClient client{"127.0.0.1", 5812, 100ms};
103+
104+
// AND a ping-pong sent with no delay
105+
// client -> server -> client
106+
uint64_t ping_client_time{100};
107+
uint64_t pong_server_time{123};
108+
uint64_t pong_client_time{100};
109+
110+
// setup our ping/pong packets
111+
TspPing ping{.version = 1, .message_id = 1, .client_time = ping_client_time};
112+
TspPong pong{ping, pong_server_time};
113+
114+
// WHEN we update statistics
115+
client.UpdateStatistics(pong_client_time, ping, pong);
116+
117+
// THEN the statistics will reflect the expected 23ms offset
118+
EXPECT_EQ(23, client.GetMetadata().offset);
119+
EXPECT_EQ(0, client.GetMetadata().rtt2);
120+
EXPECT_EQ(1u, client.GetMetadata().pongsReceived);
121+
EXPECT_EQ(pong_client_time, client.GetMetadata().lastPongTime);
122+
}
123+
124+
TEST(TimeSyncClientTest, CalculateBoth) {
125+
using namespace wpi::tsp;
126+
using namespace std::chrono_literals;
127+
128+
// GIVEN a fresh client
129+
TimeSyncClient client{"127.0.0.1", 5812, 100ms};
130+
131+
// AND a ping-pong sent with no delay
132+
// client -> server -> client
133+
int64_t offset{-234};
134+
int64_t network_latency{23};
135+
136+
uint64_t ping_client_time{100};
137+
uint64_t pong_server_time{ping_client_time + offset + network_latency};
138+
uint64_t pong_client_time{ping_client_time + 2 * network_latency};
139+
140+
// setup our ping/pong packets
141+
TspPing ping{.version = 1, .message_id = 1, .client_time = ping_client_time};
142+
TspPong pong{ping, pong_server_time};
143+
144+
// WHEN we update statistics
145+
client.UpdateStatistics(pong_client_time, ping, pong);
146+
147+
// THEN the statistics will reflect the expected latency and RTT
148+
EXPECT_EQ(offset, client.GetMetadata().offset);
149+
EXPECT_EQ(network_latency * 2, client.GetMetadata().rtt2);
150+
EXPECT_EQ(1u, client.GetMetadata().pongsReceived);
151+
EXPECT_EQ(pong_client_time, client.GetMetadata().lastPongTime);
152+
}

0 commit comments

Comments
 (0)