1 #define ASIO_STANDALONE 7 #include <gtest/gtest.h> 8 #include <rclcpp_components/component_manager.hpp> 9 #include <std_msgs/msg/string.hpp> 10 #include <std_srvs/srv/set_bool.hpp> 11 #include <websocketpp/config/asio_client.hpp> 16 constexpr
char URI[] =
"ws://localhost:8765";
19 constexpr uint8_t
HELLO_WORLD_BINARY[] = {0, 1, 0, 0, 12, 0, 0, 0, 104, 101,
20 108, 108, 111, 32, 119, 111, 114, 108, 100, 0};
29 inline static const std::string
PARAM_1_NAME =
"string_param";
35 inline static const std::string
PARAM_2_NAME =
"int_array_param";
40 auto nodeOptions = rclcpp::NodeOptions();
41 nodeOptions.allow_undeclared_parameters(
true);
42 _paramNode1 = rclcpp::Node::make_shared(NODE_1_NAME, nodeOptions);
43 auto p1Param = rcl_interfaces::msg::ParameterDescriptor{};
45 p1Param.type = rcl_interfaces::msg::ParameterType::PARAMETER_STRING;
46 p1Param.read_only =
false;
47 _paramNode1->declare_parameter(p1Param.name, PARAM_1_DEFAULT_VALUE, p1Param);
48 _paramNode1->set_parameter(rclcpp::Parameter(DELETABLE_PARAM_NAME,
true));
50 _paramNode2 = rclcpp::Node::make_shared(NODE_2_NAME);
51 auto p2Param = rcl_interfaces::msg::ParameterDescriptor{};
53 p2Param.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY;
54 p2Param.read_only =
false;
55 _paramNode2->declare_parameter(p2Param.name, PARAM_2_DEFAULT_VALUE, p2Param);
63 _wsClient = std::make_shared<foxglove::Client<websocketpp::config::asio_client>>();
64 ASSERT_EQ(std::future_status::ready, _wsClient->connect(
URI).wait_for(
DEFAULT_TIMEOUT));
72 rclcpp::executors::SingleThreadedExecutor
_executor;
76 std::shared_ptr<foxglove::Client<websocketpp::config::asio_client>>
_wsClient;
81 inline static const std::string SERVICE_NAME =
"/foo_service";
85 _node = rclcpp::Node::make_shared(
"node");
86 _service = _node->create_service<std_srvs::srv::SetBool>(
87 SERVICE_NAME, [&](std::shared_ptr<std_srvs::srv::SetBool::Request> req,
88 std::shared_ptr<std_srvs::srv::SetBool::Response> res) {
89 res->message =
"hello";
90 res->success = req->data;
108 std::shared_ptr<foxglove::Client<websocketpp::config::asio_client>>
_wsClient;
113 inline static const std::string TOPIC_NAME =
"/some_topic";
117 _node = rclcpp::Node::make_shared(
"node");
119 _node->create_publisher<std_msgs::msg::String>(TOPIC_NAME, rclcpp::SystemDefaultsQoS());
138 std::shared_ptr<rclcpp::SerializedMessage>
serializeMsg(
const T* msg) {
139 using rosidl_typesupport_cpp::get_message_type_support_handle;
140 auto typeSupportHdl = get_message_type_support_handle<T>();
141 auto result = std::make_shared<rclcpp::SerializedMessage>();
142 rmw_ret_t ret = rmw_serialize(msg, typeSupportHdl, &result->get_rcl_serialized_message());
143 EXPECT_EQ(ret, RMW_RET_OK);
149 using rosidl_typesupport_cpp::get_message_type_support_handle;
150 auto typeSupportHdl = get_message_type_support_handle<T>();
151 auto result = std::make_shared<T>();
152 rmw_ret_t ret = rmw_deserialize(msg, typeSupportHdl, result.get());
153 EXPECT_EQ(ret, RMW_RET_OK);
157 TEST(SmokeTest, testConnection) {
162 TEST(SmokeTest, testSubscription) {
164 const std::string topic_name =
"/pub_topic";
165 std_msgs::msg::String rosMsg;
166 rosMsg.data =
"hello world";
168 auto node = rclcpp::Node::make_shared(
"tester");
169 rclcpp::QoS qos = rclcpp::QoS{rclcpp::KeepLast(1lu)};
170 qos.transient_local();
171 auto pub = node->create_publisher<std_msgs::msg::String>(topic_name, qos);
172 pub->publish(rosMsg);
175 const auto clientCount = 3;
176 for (
auto i = 0; i < clientCount; ++i) {
177 std::vector<uint8_t> msgData;
184 TEST(SmokeTest, testSubscriptionParallel) {
186 const std::string topic_name =
"/pub_topic";
187 std_msgs::msg::String rosMsg;
188 rosMsg.data =
"hello world";
190 auto node = rclcpp::Node::make_shared(
"tester");
191 rclcpp::QoS qos = rclcpp::QoS{rclcpp::KeepLast(1lu)};
192 qos.transient_local();
193 auto pub = node->create_publisher<std_msgs::msg::String>(topic_name, qos);
194 pub->publish(rosMsg);
197 std::vector<std::future<std::vector<uint8_t>>> futures;
198 const auto clientCount = 3;
199 for (
auto i = 0; i < clientCount; ++i) {
204 for (
auto& future : futures) {
205 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
206 auto msgData = future.get();
212 TEST(SmokeTest, testPublishing) {
215 advertisement.
topic =
"/foo";
220 std::promise<std::string> msgPromise;
221 auto msgFuture = msgPromise.get_future();
222 auto node = rclcpp::Node::make_shared(
"tester");
223 auto sub = node->create_subscription<std_msgs::msg::String>(
224 advertisement.
topic, 10, [&msgPromise](
const std_msgs::msg::String::SharedPtr msg) {
225 msgPromise.set_value(msg->data);
227 rclcpp::executors::SingleThreadedExecutor executor;
228 executor.add_node(node);
239 const auto ret = executor.spin_until_future_complete(msgFuture,
ONE_SECOND);
240 ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
241 EXPECT_EQ(
"hello world", msgFuture.get());
247 advertisement.
topic = TOPIC_NAME;
252 std::promise<std::string> msgPromise;
253 auto msgFuture = msgPromise.get_future();
254 auto node = rclcpp::Node::make_shared(
"tester");
255 auto sub = node->create_subscription<std_msgs::msg::String>(
256 advertisement.
topic, 10, [&msgPromise](
const std_msgs::msg::String::SharedPtr msg) {
257 msgPromise.set_value(msg->data);
259 rclcpp::executors::SingleThreadedExecutor executor;
260 executor.add_node(node);
271 const auto ret = executor.spin_until_future_complete(msgFuture,
ONE_SECOND);
272 ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
273 EXPECT_EQ(
"hello world", msgFuture.get());
277 const std::string requestId =
"req-testGetAllParams";
280 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
281 std::vector<foxglove::Parameter> params = future.get();
283 EXPECT_GE(params.size(), 2UL);
287 const std::string requestId =
"req-testGetNonExistingParameters";
290 {
"/foo_1.non_existing_parameter",
"/foo_2.non_existing.nested_parameter"}, requestId);
291 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
292 std::vector<foxglove::Parameter> params = future.get();
294 EXPECT_TRUE(params.empty());
301 const std::string requestId =
"req-testGetParameters";
303 _wsClient->getParameters({p1, p2}, requestId);
304 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
305 std::vector<foxglove::Parameter> params = future.get();
307 EXPECT_EQ(2UL, params.size());
308 auto p1Iter = std::find_if(params.begin(), params.end(), [&p1](
const auto&
param) {
309 return param.getName() == p1;
311 auto p2Iter = std::find_if(params.begin(), params.end(), [&p2](
const auto&
param) {
312 return param.getName() == p2;
314 ASSERT_NE(p1Iter, params.end());
316 ASSERT_NE(p2Iter, params.end());
318 std::vector<int64_t> int_array_val;
319 const auto array_params = p2Iter->getValue().getValue<std::vector<foxglove::ParameterValue>>();
320 for (
const auto& paramValue : array_params) {
321 int_array_val.push_back(paramValue.getValue<int64_t>());
330 const std::vector<foxglove::ParameterValue> newP2value = {4, 5, 6};
332 const std::vector<foxglove::Parameter> parameters = {
338 const std::string requestId =
"req-testSetParameters";
340 _wsClient->getParameters({p1, p2}, requestId);
341 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
342 std::vector<foxglove::Parameter> params = future.get();
344 EXPECT_EQ(2UL, params.size());
345 auto p1Iter = std::find_if(params.begin(), params.end(), [&p1](
const auto&
param) {
346 return param.getName() == p1;
348 auto p2Iter = std::find_if(params.begin(), params.end(), [&p2](
const auto&
param) {
349 return param.getName() == p2;
351 ASSERT_NE(p1Iter, params.end());
352 EXPECT_EQ(newP1value, p1Iter->getValue().getValue<
PARAM_1_TYPE>());
353 ASSERT_NE(p2Iter, params.end());
355 std::vector<int64_t> int_array_val;
356 const auto array_params = p2Iter->getValue().getValue<std::vector<foxglove::ParameterValue>>();
357 for (
const auto& paramValue : array_params) {
358 int_array_val.push_back(paramValue.getValue<int64_t>());
360 const std::vector<int64_t> expected_value = {4, 5, 6};
361 EXPECT_EQ(int_array_val, expected_value);
367 const std::vector<foxglove::Parameter> parameters = {
371 const std::string requestId =
"req-testSetParameters";
373 _wsClient->setParameters(parameters, requestId);
374 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
375 std::vector<foxglove::Parameter> params = future.get();
377 EXPECT_EQ(1UL, params.size());
382 const std::vector<foxglove::Parameter> parameters = {
386 const std::string requestId =
"req-testUnsetParameter";
388 _wsClient->setParameters(parameters, requestId);
389 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
390 std::vector<foxglove::Parameter> params = future.get();
392 EXPECT_EQ(0UL, params.size());
398 _wsClient->subscribeParameterUpdates({p1});
401 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
402 std::vector<foxglove::Parameter> params = future.get();
404 ASSERT_EQ(1UL, params.size());
405 EXPECT_EQ(params.front().getName(), p1);
407 _wsClient->unsubscribeParameterUpdates({p1});
411 ASSERT_EQ(std::future_status::timeout, future.wait_for(
ONE_SECOND));
417 std::make_shared<foxglove::Client<websocketpp::config::asio_client>>(),
419 std::make_shared<foxglove::Client<websocketpp::config::asio_client>>(),
422 std::vector<std::future<std::vector<foxglove::Parameter>>> futures;
423 for (
auto client : clients) {
425 std::async(std::launch::async, [client]() -> std::vector<foxglove::Parameter> {
427 const std::string requestId =
"req-123";
429 client->getParameters({}, requestId);
431 if (future.valid()) {
439 for (
auto& future : futures) {
440 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
441 std::vector<foxglove::Parameter> parameters;
442 EXPECT_NO_THROW(parameters = future.get());
443 EXPECT_GE(parameters.size(), 2UL);
450 std::make_shared<foxglove::Client<websocketpp::config::asio_client>>(),
452 std::make_shared<foxglove::Client<websocketpp::config::asio_client>>(),
456 for (
auto client : clients) {
457 ASSERT_EQ(std::future_status::ready, client->connect(
URI).wait_for(std::chrono::seconds(5)));
459 ASSERT_EQ(std::future_status::ready, serviceFuture.wait_for(std::chrono::seconds(5)));
462 std_srvs::srv::SetBool::Request requestMsg;
463 requestMsg.data =
true;
464 const auto serializedRequest =
serializeMsg(&requestMsg);
465 const auto& serRequestMsg = serializedRequest->get_rcl_serialized_message();
471 request.
data.resize(serRequestMsg.buffer_length);
472 std::memcpy(request.
data.data(), serRequestMsg.buffer, serRequestMsg.buffer_length);
474 std::vector<std::future<foxglove::ServiceResponse>> futures;
475 for (
auto client : clients) {
477 client->sendServiceRequest(request);
480 for (
auto& future : futures) {
481 ASSERT_EQ(std::future_status::ready, future.wait_for(std::chrono::seconds(5)));
483 EXPECT_NO_THROW(response = future.get());
484 EXPECT_EQ(response.serviceId, request.
serviceId);
485 EXPECT_EQ(response.callId, request.
callId);
486 EXPECT_EQ(response.encoding, request.
encoding);
488 rclcpp::SerializedMessage serializedResponseMsg(response.data.size());
489 auto& serMsg = serializedResponseMsg.get_rcl_serialized_message();
490 std::memcpy(serMsg.buffer, response.data.data(), response.data.size());
491 serMsg.buffer_length = response.data.size();
492 const auto resMsg = deserializeMsg<std_srvs::srv::SetBool::Response>(&serMsg);
494 EXPECT_EQ(resMsg->message,
"hello");
495 EXPECT_EQ(resMsg->success, requestMsg.data);
500 int main(
int argc,
char** argv) {
501 testing::InitGoogleTest(&argc, argv);
502 rclcpp::init(argc, argv);
504 const size_t numThreads = 2;
506 rclcpp::executors::MultiThreadedExecutor::make_shared(rclcpp::ExecutorOptions{}, numThreads);
508 rclcpp_components::ComponentManager componentManager(executor,
"ComponentManager");
509 const auto componentResources = componentManager.get_component_resources(
"foxglove_bridge");
511 if (componentResources.empty()) {
512 RCLCPP_INFO(componentManager.get_logger(),
"No loadable resources found");
516 auto componentFactory = componentManager.create_component_factory(componentResources.front());
517 auto node = componentFactory->create_node_instance(rclcpp::NodeOptions());
518 executor->add_node(node.get_node_base_interface());
520 std::thread spinnerThread([&executor]() {
524 const auto testResult = RUN_ALL_TESTS();
526 spinnerThread.join();
constexpr uint8_t HELLO_WORLD_BINARY[]
rclcpp::PublisherBase::SharedPtr _publisher
rclcpp::Node::SharedPtr _paramNode2
bool param(const std::string ¶m_name, T ¶m_val, const T &default_val)
rclcpp::executors::SingleThreadedExecutor _executor
static const std::string DELETABLE_PARAM_NAME
static const std::string NODE_2_NAME
std::shared_ptr< foxglove::Client< websocketpp::config::asio_client > > _wsClient
constexpr auto ONE_SECOND
static const PARAM_1_TYPE PARAM_1_DEFAULT_VALUE
std::future< Service > waitForService(std::shared_ptr< ClientInterface > client, const std::string &serviceName)
rclcpp::Node::SharedPtr _node
rclcpp::executors::SingleThreadedExecutor _executor
void advertise(const std::vector< ClientAdvertisement > &channels) override
std::thread _executorThread
std::vector< uint8_t > data
std::shared_ptr< T > deserializeMsg(const rcl_serialized_message_t *msg)
TEST(SmokeTest, testConnection)
TEST_F(ExistingPublisherTest, testPublishingWithExistingPublisher)
std::thread _executorThread
rclcpp::executors::SingleThreadedExecutor _executor
void connect(const std::string &uri, std::function< void(websocketpp::connection_hdl)> onOpenHandler, std::function< void(websocketpp::connection_hdl)> onCloseHandler=nullptr) override
static const std::string PARAM_2_NAME
constexpr auto DEFAULT_TIMEOUT
void publish(ClientChannelId channelId, const uint8_t *buffer, size_t size) override
void unadvertise(const std::vector< ClientChannelId > &channelIds) override
rclcpp::Node::SharedPtr _paramNode1
std::vector< uint8_t > connectClientAndReceiveMsg(const std::string &uri, const std::string &topic_name)
std::shared_ptr< foxglove::Client< websocketpp::config::asio_client > > _wsClient
rclcpp::Node::SharedPtr _node
int main(int argc, char **argv)
static const std::string NODE_1_NAME
static const PARAM_2_TYPE PARAM_2_DEFAULT_VALUE
static const std::string PARAM_1_NAME
std::vector< double > PARAM_2_TYPE
ClientChannelId channelId
std::thread _executorThread
std::future< ServiceResponse > waitForServiceResponse(std::shared_ptr< ClientInterface > client)
std::shared_ptr< rclcpp::SerializedMessage > serializeMsg(const T *msg)
const std::string response
rclcpp::ServiceBase::SharedPtr _service
std::future< std::vector< Parameter > > waitForParameters(std::shared_ptr< ClientInterface > client, const std::string &requestId=std::string())