18#include <boost/asio/executor_work_guard.hpp>
23using namespace std::chrono_literals;
48 boost::asio::executor_work_guard<boost::asio::io_context::executor_type>
_work_to_do;
53TEST(streaming, low_level_sending_strings) {
59 constexpr auto number_of_messages = 100u;
60 const std::string message_text =
"Hello client!";
62 std::atomic_size_t message_count{0u};
74 ASSERT_EQ(message.size(), message_text.size());
75 const std::string msg = as_string(message);
76 ASSERT_EQ(msg, message_text);
79 carla::Buffer Buf(boost::asio::buffer(message_text.c_str(), message_text.size()));
81 for (
auto i = 0u; i < number_of_messages; ++i) {
82 std::this_thread::sleep_for(2ms);
87 std::this_thread::sleep_for(2ms);
88 ASSERT_GE(message_count, number_of_messages - 3u);
93TEST(streaming, low_level_unsubscribing) {
99 constexpr auto number_of_messages = 50u;
100 const std::string message_text =
"Hello client!";
108 for (
auto n = 0u; n < 10u; ++n) {
110 std::atomic_size_t message_count{0u};
114 ASSERT_EQ(message.size(), message_text.size());
115 const std::string msg = as_string(message);
116 ASSERT_EQ(msg, message_text);
119 carla::Buffer Buf(boost::asio::buffer(message_text.c_str(), message_text.size()));
121 for (
auto i = 0u; i < number_of_messages; ++i) {
122 std::this_thread::sleep_for(4ms);
127 std::this_thread::sleep_for(4ms);
130 for (
auto i = 0u; i < number_of_messages; ++i) {
131 std::this_thread::sleep_for(2ms);
136 ASSERT_GE(message_count, number_of_messages - 3u);
142TEST(streaming, low_level_tcp_small_message) {
146 boost::asio::io_context io_context;
151 std::atomic_bool done{
false};
152 std::atomic_size_t message_count{0u};
154 const std::string msg =
"Hola!";
156 srv.
Listen([&](std::shared_ptr<tcp::ServerSession> session) {
157 ASSERT_EQ(session->get_stream_id(), 1u);
159 carla::Buffer Buf(boost::asio::buffer(msg.c_str(), msg.size()));
162 std::this_thread::sleep_for(1ns);
164 session->Write(View);
166 std::cout <<
"done!\n";
167 }, [](std::shared_ptr<tcp::ServerSession>) { std::cout <<
"session closed!\n"; });
170 Dispatcher dispatcher{make_endpoint<tcp::Client::protocol_type>(
173 auto c = std::make_shared<tcp::Client>(io_context, stream.token(), [&](
carla::Buffer message) {
175 ASSERT_FALSE(message.empty());
176 ASSERT_EQ(message.size(), 5u);
177 const std::string received = util::buffer::as_string(message);
178 ASSERT_EQ(received, msg);
185 std::max(2u, std::thread::hardware_concurrency()),
186 [&]() { io_context.run(); });
188 std::this_thread::sleep_for(2s);
191 std::cout <<
"client received " << message_count <<
" messages\n";
192 ASSERT_GT(message_count, 10u);
199TEST(streaming, low_level_tcp_callback_exception_does_not_terminate) {
203 boost::asio::io_context io_context;
208 std::atomic_bool done{
false};
209 std::atomic_size_t message_count{0u};
211 const std::string msg =
"Hola!";
213 srv.
Listen([&](std::shared_ptr<tcp::ServerSession> session) {
214 carla::Buffer Buf(boost::asio::buffer(msg.c_str(), msg.size()));
217 std::this_thread::sleep_for(1ns);
219 session->Write(View);
221 }, [](std::shared_ptr<tcp::ServerSession>) {});
224 Dispatcher dispatcher{make_endpoint<tcp::Client::protocol_type>(
227 auto c = std::make_shared<tcp::Client>(io_context, stream.token(), [&](
carla::Buffer) {
228 if (++message_count == 1u) {
229 throw std::runtime_error(
"callback failure on the first message");
237 std::max(2u, std::thread::hardware_concurrency()),
238 [&]() { io_context.run(); });
240 std::this_thread::sleep_for(2s);
243 std::cout <<
"client received " << message_count <<
" messages\n";
244 ASSERT_GT(message_count, 10u);
253TEST(streaming, stream_outlives_server) {
256 constexpr size_t iterations = 10u;
257 std::atomic_bool done{
false};
258 const std::string message =
"Hello client, how are you?";
259 std::shared_ptr<Stream> stream;
265 carla::Buffer Buf(boost::asio::buffer(message.c_str(), message.size()));
269 const auto next_write = std::chrono::steady_clock::now() + 1ms;
270 while (!done && std::chrono::steady_clock::now() < next_write) {
271 std::this_thread::yield();
273 auto s = std::atomic_load_explicit(&stream, std::memory_order_relaxed);
281 for (
auto i = 0u; i < iterations; ++i) {
285 auto s = std::make_shared<Stream>(srv.
MakeStream());
286 std::atomic_store_explicit(&stream, s, std::memory_order_relaxed);
288 std::atomic_size_t messages_received{0u};
292 c.
Subscribe(stream->token(), [&](
auto buffer) {
293 const std::string result = as_string(buffer);
294 ASSERT_EQ(result, message);
297 std::this_thread::sleep_for(20ms);
299 ASSERT_GT(messages_received, 0u);
304 std::atomic_store_explicit(&stream, std::shared_ptr<Stream>(), std::memory_order_relaxed);
305 std::this_thread::sleep_for(20ms);
313 constexpr size_t number_of_messages = 100u;
314 constexpr size_t number_of_clients = 6u;
315 constexpr size_t iterations = 10u;
316 const std::string message =
"Hi y'all!";
322 for (
auto i = 0u; i < iterations; ++i) {
323 std::vector<std::pair<std::atomic_size_t, std::unique_ptr<Client>>> v(number_of_clients);
325 for (
auto &pair : v) {
327 pair.second = std::make_unique<Client>();
328 pair.second->AsyncRun(1u);
329 pair.second->Subscribe(stream.token(), [&](
auto buffer) {
330 const std::string result = as_string(buffer);
331 ASSERT_EQ(result, message);
336 carla::Buffer Buf(boost::asio::buffer(message.c_str(), message.size()));
338 std::this_thread::sleep_for(6ms);
339 for (
auto j = 0u; j < number_of_messages; ++j) {
340 std::this_thread::sleep_for(6ms);
344 std::this_thread::sleep_for(6ms);
346 for (
auto &pair : v) {
347 ASSERT_GE(pair.first, number_of_messages - 3u);
355TEST(streaming, ros_topic_visibility_default_off) {
367TEST(streaming, ros_topic_visibility_default_on) {
380TEST(streaming, ros_topic_visibility_make_stream_path) {
393TEST(streaming, ros_topic_visibility_manual_toggle) {
static std::shared_ptr< BufferView > CreateFrom(Buffer &&buffer)
void CreateThreads(size_t count, F functor)
void CreateThread(F &&functor)
A client able to subscribe to multiple streams.
void AsyncRun(size_t worker_threads)
void Subscribe(const Token &token, Functor &&callback)
token_type GetToken(stream_id sensor_id)
bool IsEnabledForROS(stream_id sensor_id)
void EnableForROS(stream_id sensor_id)
void SetROS2TopicVisibilityDefaultEnabled(bool enabled)
void AsyncRun(size_t worker_threads)
void DisableForROS(stream_id sensor_id)
Keeps the mapping between streams and sessions.
carla::streaming::Stream MakeStream()
void SetTimeout(time_duration timeout)
Set session time-out.
boost::asio::ip::tcp::endpoint endpoint
endpoint GetLocalEndpoint() const
void Listen(FunctorT1 on_session_opened, FunctorT2 on_session_closed)
Start listening for connections.
Serializes a stream endpoint.
const auto & get_stream_id() const
A client able to subscribe to multiple streams.
void Subscribe(boost::asio::io_context &io_context, token_type token, Functor &&callback)
void UnSubscribe(token_type token)
A low-level streaming server.
void SetTimeout(time_duration timeout)
boost::asio::executor_work_guard< boost::asio::io_context::executor_type > _work_to_do
boost::asio::io_context service
carla::ThreadGroup _threads
io_context_running(size_t threads=2u)
std::shared_ptr< BufferView > SharedBufferView
constexpr uint16_t TESTING_PORT
TEST(streaming, low_level_sending_strings)