blob: 383c1c4a678b6575cd8b9696a234cc4668268be9 [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";
52 aos::ShmEventLoop server_event_loop(&server_config.message());
53 MessageBridgeServer message_bridge_server(&server_event_loop);
54
55 // And build the app which sends the pings.
56 FLAGS_application_name = "ping";
57 aos::ShmEventLoop ping_event_loop(&server_config.message());
58 aos::Sender<examples::Ping> ping_sender =
59 ping_event_loop.MakeSender<examples::Ping>("/test");
60
61 // Now do it for "raspberrypi2", the client.
62 FLAGS_application_name = "pi2_message_bridge_client";
63 FLAGS_override_hostname = "raspberrypi2";
64 aos::ShmEventLoop client_event_loop(&client_config.message());
65 MessageBridgeClient message_bridge_client(&client_event_loop);
66
67 // And build the app which sends the pongs.
68 FLAGS_application_name = "pong";
69 aos::ShmEventLoop pong_event_loop(&client_config.message());
70
71 // Count the pongs.
72 int pong_count = 0;
73 pong_event_loop.MakeWatcher(
74 "/test2", [&pong_count, &ping_event_loop](const examples::Ping &ping) {
75 ++pong_count;
76 LOG(INFO) << "Got ping back " << FlatbufferToJson(&ping);
77 if (pong_count >= 2) {
78 LOG(INFO) << "That's enough bailing early.";
79 // And Exit is async safe, so thread safe is easy.
80 ping_event_loop.Exit();
81 }
82 });
83
84 FLAGS_override_hostname = "";
85
86 // Start everything up. Pong is the only thing we don't know how to wait on,
87 // so start it first.
88 std::thread pong_thread([&pong_event_loop]() { pong_event_loop.Run(); });
89
90 std::thread server_thread(
91 [&server_event_loop]() { server_event_loop.Run(); });
92 std::thread client_thread(
93 [&client_event_loop]() { client_event_loop.Run(); });
94
95 // Wait until we are connected, then send.
96 int ping_count = 0;
97 ping_event_loop.MakeWatcher(
98 "/aos/pi1", [&ping_count, &client_event_loop,
99 &ping_sender](const ServerStatistics &stats) {
100 LOG(INFO) << FlatbufferToJson(&stats);
101
102 ASSERT_TRUE(stats.has_connections());
103 EXPECT_EQ(stats.connections()->size(), 1);
104
105 bool connected = false;
106 for (const ServerConnection *connection : *stats.connections()) {
107 if (connection->node()->name()->string_view() ==
108 client_event_loop.node()->name()->string_view()) {
109 if (connection->state() == State::CONNECTED) {
110 connected = true;
111 }
112 break;
113 }
114 }
115
116 if (connected) {
117 LOG(INFO) << "Connected! Sent ping.";
118 auto builder = ping_sender.MakeBuilder();
119 examples::Ping::Builder ping_builder =
120 builder.MakeBuilder<examples::Ping>();
121 ping_builder.add_value(ping_count + 971);
122 builder.Send(ping_builder.Finish());
123 ++ping_count;
124 }
125 });
126
127 // Time ourselves out after a while if Pong doesn't do it for us.
128 aos::TimerHandler *quit = ping_event_loop.AddTimer(
129 [&ping_event_loop]() { ping_event_loop.Exit(); });
130 ping_event_loop.OnRun([quit, &ping_event_loop]() {
131 quit->Setup(ping_event_loop.monotonic_now() + chrono::seconds(10));
132 });
133
134
135 // And go!
136 ping_event_loop.Run();
137
138 // Shut everyone else down
139 server_event_loop.Exit();
140 client_event_loop.Exit();
141 pong_event_loop.Exit();
142 server_thread.join();
143 client_thread.join();
144 pong_thread.join();
145
146 // Make sure we sent something.
147 EXPECT_GE(ping_count, 1);
148 // And got something back.
149 EXPECT_GE(pong_count, 1);
150}
151
152} // namespace testing
153} // namespace message_bridge
154} // namespace aos