blob: c1dd1ca8b6754e9e02bac66f0417a60163ed0e36 [file] [log] [blame]
Austin Schuhe84c3ed2019-12-14 15:29:48 -08001#include "gtest/gtest.h"
2
3#include <chrono>
4#include <thread>
5
6#include "aos/events/ping_generated.h"
7#include "aos/events/pong_generated.h"
8#include "aos/network/message_bridge_client_lib.h"
9#include "aos/network/message_bridge_server_lib.h"
10
11DECLARE_string(override_hostname);
12DECLARE_string(application_name);
13
14namespace aos {
15namespace message_bridge {
16namespace testing {
17
18namespace chrono = std::chrono;
19
20// Test that we can send a ping message over sctp and receive it.
21TEST(MessageBridgeTest, PingPong) {
22 // This is rather annoying to set up. We need to start up a client and
23 // server, on the same node, but get them to think that they are on different
24 // nodes.
25 //
26 // We then get to wait until they are connected.
27 //
28 // After they are connected, we send a Ping message.
29 //
30 // On the other end, we receive a Pong message.
31 //
32 // But, we need the client to not post directly to "/test" like it would in a
33 // real system, otherwise we will re-send the ping message... So, use an
34 // application specific map to have the client post somewhere else.
35 //
36 // To top this all off, each of these needs to be done with a ShmEventLoop,
37 // which needs to run in a separate thread... And it is really hard to get
38 // everything started up reliably. So just be super generous on timeouts and
39 // hope for the best. We can be more generous in the future if we need to.
40 //
41 // We are faking the application names by passing in --application_name=foo
42 aos::FlatbufferDetachedBuffer<aos::Configuration> server_config =
43 aos::configuration::ReadConfig(
44 "aos/network/message_bridge_test_server_config.json");
45 aos::FlatbufferDetachedBuffer<aos::Configuration> client_config =
46 aos::configuration::ReadConfig(
47 "aos/network/message_bridge_test_client_config.json");
48
49 FLAGS_application_name = "pi1_message_bridge_server";
50 // Force ourselves to be "raspberrypi" and allocate everything.
51 FLAGS_override_hostname = "raspberrypi";
Austin Schuh7bc59052020-02-16 23:48:33 -080052 aos::ShmEventLoop pi1_server_event_loop(&server_config.message());
53 MessageBridgeServer pi1_message_bridge_server(&pi1_server_event_loop);
54
55 FLAGS_application_name = "pi1_message_bridge_client";
56 aos::ShmEventLoop pi1_client_event_loop(&server_config.message());
57 MessageBridgeClient pi1_message_bridge_client(&pi1_client_event_loop);
Austin Schuhe84c3ed2019-12-14 15:29:48 -080058
59 // And build the app which sends the pings.
60 FLAGS_application_name = "ping";
61 aos::ShmEventLoop ping_event_loop(&server_config.message());
62 aos::Sender<examples::Ping> ping_sender =
63 ping_event_loop.MakeSender<examples::Ping>("/test");
64
65 // Now do it for "raspberrypi2", the client.
66 FLAGS_application_name = "pi2_message_bridge_client";
67 FLAGS_override_hostname = "raspberrypi2";
Austin Schuh7bc59052020-02-16 23:48:33 -080068 aos::ShmEventLoop pi2_client_event_loop(&client_config.message());
69 MessageBridgeClient pi2_message_bridge_client(&pi2_client_event_loop);
70
71 FLAGS_application_name = "pi2_message_bridge_server";
72 aos::ShmEventLoop pi2_server_event_loop(&client_config.message());
73 MessageBridgeServer pi2_message_bridge_server(&pi2_server_event_loop);
Austin Schuhe84c3ed2019-12-14 15:29:48 -080074
75 // And build the app which sends the pongs.
76 FLAGS_application_name = "pong";
77 aos::ShmEventLoop pong_event_loop(&client_config.message());
78
Austin Schuh7bc59052020-02-16 23:48:33 -080079 // And build the app for testing.
80 FLAGS_application_name = "test";
81 aos::ShmEventLoop test_event_loop(&client_config.message());
82
83 aos::Fetcher<ClientStatistics> client_statistics_fetcher =
84 test_event_loop.MakeFetcher<ClientStatistics>("/aos");
85
Austin Schuhe84c3ed2019-12-14 15:29:48 -080086 // Count the pongs.
87 int pong_count = 0;
88 pong_event_loop.MakeWatcher(
Austin Schuh7bc59052020-02-16 23:48:33 -080089 "/test2", [&pong_count](const examples::Ping &ping) {
Austin Schuhe84c3ed2019-12-14 15:29:48 -080090 ++pong_count;
91 LOG(INFO) << "Got ping back " << FlatbufferToJson(&ping);
Austin Schuhe84c3ed2019-12-14 15:29:48 -080092 });
93
94 FLAGS_override_hostname = "";
95
Austin Schuhe84c3ed2019-12-14 15:29:48 -080096 // Wait until we are connected, then send.
97 int ping_count = 0;
Austin Schuh7bc59052020-02-16 23:48:33 -080098 int pi1_server_statistics_count = 0;
Austin Schuhe84c3ed2019-12-14 15:29:48 -080099 ping_event_loop.MakeWatcher(
Austin Schuh7bc59052020-02-16 23:48:33 -0800100 "/aos/pi1",
101 [&ping_count, &pi2_client_event_loop, &ping_sender,
102 &pi1_server_statistics_count](const ServerStatistics &stats) {
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800103 LOG(INFO) << FlatbufferToJson(&stats);
104
105 ASSERT_TRUE(stats.has_connections());
106 EXPECT_EQ(stats.connections()->size(), 1);
107
108 bool connected = false;
109 for (const ServerConnection *connection : *stats.connections()) {
Austin Schuh7bc59052020-02-16 23:48:33 -0800110 // Confirm that we are estimating the server time offset correctly. It
111 // should be about 0 since we are on the same machine here.
112 if (connection->has_monotonic_offset()) {
113 EXPECT_LT(chrono::nanoseconds(connection->monotonic_offset()),
114 chrono::milliseconds(1));
115 EXPECT_GT(chrono::nanoseconds(connection->monotonic_offset()),
116 chrono::milliseconds(-1));
117 ++pi1_server_statistics_count;
118 }
119
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800120 if (connection->node()->name()->string_view() ==
Austin Schuh7bc59052020-02-16 23:48:33 -0800121 pi2_client_event_loop.node()->name()->string_view()) {
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800122 if (connection->state() == State::CONNECTED) {
123 connected = true;
124 }
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800125 }
126 }
127
128 if (connected) {
129 LOG(INFO) << "Connected! Sent ping.";
130 auto builder = ping_sender.MakeBuilder();
131 examples::Ping::Builder ping_builder =
132 builder.MakeBuilder<examples::Ping>();
133 ping_builder.add_value(ping_count + 971);
134 builder.Send(ping_builder.Finish());
135 ++ping_count;
136 }
137 });
138
Austin Schuh7bc59052020-02-16 23:48:33 -0800139 // Confirm both client and server statistics messages have decent offsets in
140 // them.
141 int pi2_server_statistics_count = 0;
142 pong_event_loop.MakeWatcher("/aos/pi2", [&pi2_server_statistics_count](
143 const ServerStatistics &stats) {
144 LOG(INFO) << FlatbufferToJson(&stats);
145 for (const ServerConnection *connection : *stats.connections()) {
146 if (connection->has_monotonic_offset()) {
147 ++pi2_server_statistics_count;
148 // Confirm that we are estimating the server time offset correctly. It
149 // should be about 0 since we are on the same machine here.
150 EXPECT_LT(chrono::nanoseconds(connection->monotonic_offset()),
151 chrono::milliseconds(1));
152 EXPECT_GT(chrono::nanoseconds(connection->monotonic_offset()),
153 chrono::milliseconds(-1));
154 }
155 }
156 });
157
158 int pi1_client_statistics_count = 0;
159 ping_event_loop.MakeWatcher(
160 "/aos/pi1", [&pi1_client_statistics_count](const ClientStatistics &stats) {
161 LOG(INFO) << FlatbufferToJson(&stats);
162
163 for (const ClientConnection *connection : *stats.connections()) {
164 if (connection->has_monotonic_offset()) {
165 ++pi1_client_statistics_count;
166 // It takes at least 10 microseconds to send a message between the
167 // client and server. The min (filtered) time shouldn't be over 10
168 // milliseconds on localhost. This might have to bump up if this is
169 // proving flaky.
170 EXPECT_LT(chrono::nanoseconds(connection->monotonic_offset()),
171 chrono::milliseconds(10));
172 EXPECT_GT(chrono::nanoseconds(connection->monotonic_offset()),
173 chrono::microseconds(10));
174 }
175 }
176 });
177
178 int pi2_client_statistics_count = 0;
179 pong_event_loop.MakeWatcher("/aos/pi2", [&pi2_client_statistics_count](
180 const ClientStatistics &stats) {
181 LOG(INFO) << FlatbufferToJson(&stats);
182
183 for (const ClientConnection *connection : *stats.connections()) {
184 if (connection->has_monotonic_offset()) {
185 ++pi2_client_statistics_count;
186 EXPECT_LT(chrono::nanoseconds(connection->monotonic_offset()),
187 chrono::milliseconds(10));
188 EXPECT_GT(chrono::nanoseconds(connection->monotonic_offset()),
189 chrono::microseconds(10));
190 }
191 }
192 });
193
194 ping_event_loop.MakeWatcher("/aos/pi1", [](const Timestamp &timestamp) {
195 EXPECT_TRUE(timestamp.has_offsets());
196 LOG(INFO) << FlatbufferToJson(&timestamp);
197 });
198 pong_event_loop.MakeWatcher("/aos/pi2", [](const Timestamp &timestamp) {
199 EXPECT_TRUE(timestamp.has_offsets());
200 LOG(INFO) << FlatbufferToJson(&timestamp);
201 });
202
203 // Run for 5 seconds to make sure we have time to estimate the offset.
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800204 aos::TimerHandler *quit = ping_event_loop.AddTimer(
205 [&ping_event_loop]() { ping_event_loop.Exit(); });
206 ping_event_loop.OnRun([quit, &ping_event_loop]() {
Austin Schuh7bc59052020-02-16 23:48:33 -0800207 // Stop between timestamps, not exactly on them.
208 quit->Setup(ping_event_loop.monotonic_now() + chrono::milliseconds(5050));
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800209 });
210
Austin Schuh7bc59052020-02-16 23:48:33 -0800211 // Start everything up. Pong is the only thing we don't know how to wait on,
212 // so start it first.
213 std::thread pong_thread([&pong_event_loop]() { pong_event_loop.Run(); });
214
215 std::thread pi1_server_thread(
216 [&pi1_server_event_loop]() { pi1_server_event_loop.Run(); });
217 std::thread pi1_client_thread(
218 [&pi1_client_event_loop]() { pi1_client_event_loop.Run(); });
219 std::thread pi2_client_thread(
220 [&pi2_client_event_loop]() { pi2_client_event_loop.Run(); });
221 std::thread pi2_server_thread(
222 [&pi2_server_event_loop]() { pi2_server_event_loop.Run(); });
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800223
224 // And go!
225 ping_event_loop.Run();
226
227 // Shut everyone else down
Austin Schuh7bc59052020-02-16 23:48:33 -0800228 pi1_server_event_loop.Exit();
229 pi1_client_event_loop.Exit();
230 pi2_client_event_loop.Exit();
231 pi2_server_event_loop.Exit();
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800232 pong_event_loop.Exit();
Austin Schuh7bc59052020-02-16 23:48:33 -0800233 pi1_server_thread.join();
234 pi1_client_thread.join();
235 pi2_client_thread.join();
236 pi2_server_thread.join();
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800237 pong_thread.join();
238
239 // Make sure we sent something.
240 EXPECT_GE(ping_count, 1);
241 // And got something back.
242 EXPECT_GE(pong_count, 1);
Austin Schuh7bc59052020-02-16 23:48:33 -0800243
244 // Confirm that we are estimating a monotonic offset on the client.
245 ASSERT_TRUE(client_statistics_fetcher.Fetch());
246
247 EXPECT_EQ(client_statistics_fetcher->connections()->size(), 1u);
248 EXPECT_EQ(client_statistics_fetcher->connections()
249 ->Get(0)
250 ->node()
251 ->name()
252 ->string_view(),
253 "pi1");
254
255 // Make sure the offset in one direction is less than a second.
256 EXPECT_GT(
257 client_statistics_fetcher->connections()->Get(0)->monotonic_offset(), 0);
258 EXPECT_LT(
259 client_statistics_fetcher->connections()->Get(0)->monotonic_offset(),
260 1000000000);
261
262 EXPECT_GE(pi1_server_statistics_count, 2);
263 EXPECT_GE(pi2_server_statistics_count, 2);
264 EXPECT_GE(pi1_client_statistics_count, 2);
265 EXPECT_GE(pi2_client_statistics_count, 2);
266
267 // TODO(austin): Need 2 servers going so we can do the round trip offset
268 // estimation.
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800269}
270
271} // namespace testing
272} // namespace message_bridge
273} // namespace aos