5 #include <boost/filesystem.hpp>
6 #include <gtest/gtest.h>
9 #include <std_srvs/SetBool.h>
10 #include <websocketpp/config/asio_client.hpp>
15 constexpr
char URI[] =
"ws://localhost:9876";
19 111, 32, 119, 111, 114, 108, 100};
27 inline static const std::string
PARAM_1_NAME =
"/node_1/string_param";
31 inline static const std::string
PARAM_2_NAME =
"/node_2/int_array_param";
40 _wsClient = std::make_shared<foxglove::Client<websocketpp::config::asio_client>>();
45 std::shared_ptr<foxglove::Client<websocketpp::config::asio_client>>
_wsClient;
57 res.message =
"hello";
58 res.success = req.data;
68 TEST(SmokeTest, testConnection) {
73 TEST(SmokeTest, testSubscription) {
75 const std::string topic_name =
"/pub_topic";
77 auto pub = nh.
advertise<std_msgs::String>(topic_name, 10,
true);
78 pub.
publish(std::string(
"hello world"));
81 const auto clientCount = 3;
82 for (
auto i = 0; i < clientCount; ++i) {
84 auto client = std::make_shared<foxglove::Client<websocketpp::config::asio_client>>();
86 ASSERT_EQ(std::future_status::ready, client->connect(
URI).wait_for(
ONE_SECOND));
87 ASSERT_EQ(std::future_status::ready, channelFuture.wait_for(
DEFAULT_TIMEOUT));
93 client->subscribe({{subscriptionId, channel.
id}});
94 ASSERT_EQ(std::future_status::ready, msgFuture.wait_for(
ONE_SECOND));
95 const auto msgData = msgFuture.get();
100 client->unsubscribe({subscriptionId});
104 TEST(SmokeTest, testSubscriptionParallel) {
106 const std::string topic_name =
"/pub_topic";
108 auto pub = nh.
advertise<std_msgs::String>(topic_name, 10,
true);
109 pub.
publish(std::string(
"hello world"));
114 std::make_shared<foxglove::Client<websocketpp::config::asio_client>>(),
116 std::make_shared<foxglove::Client<websocketpp::config::asio_client>>(),
119 std::vector<std::future<std::vector<uint8_t>>> futures;
120 for (
auto client : clients) {
124 for (
auto client : clients) {
126 ASSERT_EQ(std::future_status::ready, client->connect(
URI).wait_for(
ONE_SECOND));
127 ASSERT_EQ(std::future_status::ready, channelFuture.wait_for(
DEFAULT_TIMEOUT));
129 client->subscribe({{subscriptionId, channel.
id}});
132 for (
auto& future : futures) {
133 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
134 auto msgData = future.get();
139 for (
auto client : clients) {
140 client->unsubscribe({subscriptionId});
144 TEST(SmokeTest, testPublishing) {
149 advertisement.
topic =
"/foo";
155 std::promise<std::string> msgPromise;
156 auto msgFuture = msgPromise.get_future();
157 auto subscriber = nh.
subscribe<std_msgs::String>(
158 advertisement.
topic, 10, [&msgPromise](
const std_msgs::String::ConstPtr& msg) {
159 msgPromise.set_value(msg->data);
170 const auto msgResult = msgFuture.wait_for(
ONE_SECOND);
171 ASSERT_EQ(std::future_status::ready, msgResult);
172 EXPECT_EQ(
"hello world", msgFuture.get());
176 const std::string requestId =
"req-testGetAllParams";
178 _wsClient->getParameters({}, requestId);
179 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
180 std::vector<foxglove::Parameter> params = future.get();
182 EXPECT_GE(params.size(), 2UL);
186 const std::string requestId =
"req-testGetNonExistingParameters";
188 _wsClient->getParameters(
189 {
"/foo_1/non_existing_parameter",
"/foo_2/non_existing/nested_parameter"}, requestId);
190 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
191 std::vector<foxglove::Parameter> params = future.get();
193 EXPECT_TRUE(params.empty());
197 const std::string requestId =
"req-testGetParameters";
199 _wsClient->getParameters({PARAM_1_NAME, PARAM_2_NAME}, requestId);
200 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
201 std::vector<foxglove::Parameter> params = future.get();
203 EXPECT_EQ(2UL, params.size());
204 auto p1Iter = std::find_if(params.begin(), params.end(), [](
const auto&
param) {
205 return param.getName() == PARAM_1_NAME;
207 auto p2Iter = std::find_if(params.begin(), params.end(), [](
const auto&
param) {
208 return param.getName() == PARAM_2_NAME;
210 ASSERT_NE(p1Iter, params.end());
211 EXPECT_EQ(PARAM_1_DEFAULT_VALUE, p1Iter->getValue().getValue<PARAM_1_TYPE>());
212 ASSERT_NE(p2Iter, params.end());
214 std::vector<double> double_array_val;
215 const auto array_params = p2Iter->getValue().getValue<std::vector<foxglove::ParameterValue>>();
216 for (
const auto& paramValue : array_params) {
217 double_array_val.push_back(paramValue.getValue<
double>());
219 EXPECT_EQ(double_array_val, PARAM_2_DEFAULT_VALUE);
223 const PARAM_1_TYPE newP1value =
"world";
224 const std::vector<foxglove::ParameterValue> newP2value = {4.1, 5.5, 6.6};
226 const std::vector<foxglove::Parameter> parameters = {
231 _wsClient->setParameters(parameters);
232 const std::string requestId =
"req-testSetParameters";
234 _wsClient->getParameters({PARAM_1_NAME, PARAM_2_NAME}, requestId);
235 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
236 std::vector<foxglove::Parameter> params = future.get();
238 EXPECT_EQ(2UL, params.size());
239 auto p1Iter = std::find_if(params.begin(), params.end(), [](
const auto&
param) {
240 return param.getName() == PARAM_1_NAME;
242 auto p2Iter = std::find_if(params.begin(), params.end(), [](
const auto&
param) {
243 return param.getName() == PARAM_2_NAME;
245 ASSERT_NE(p1Iter, params.end());
246 EXPECT_EQ(newP1value, p1Iter->getValue().getValue<PARAM_1_TYPE>());
247 ASSERT_NE(p2Iter, params.end());
249 std::vector<double> double_array_val;
250 const auto array_params = p2Iter->getValue().getValue<std::vector<foxglove::ParameterValue>>();
251 for (
const auto& paramValue : array_params) {
252 double_array_val.push_back(paramValue.getValue<
double>());
254 const std::vector<double> expected_value = {4.1, 5.5, 6.6};
255 EXPECT_EQ(double_array_val, expected_value);
259 const PARAM_1_TYPE newP1value =
"world";
260 const std::vector<foxglove::Parameter> parameters = {
264 const std::string requestId =
"req-testSetParameters";
266 _wsClient->setParameters(parameters, requestId);
267 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
268 std::vector<foxglove::Parameter> params = future.get();
270 EXPECT_EQ(1UL, params.size());
274 const std::vector<foxglove::Parameter> parameters = {
278 const std::string requestId =
"req-testUnsetParameter";
280 _wsClient->setParameters(parameters, requestId);
281 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
282 std::vector<foxglove::Parameter> params = future.get();
284 EXPECT_EQ(0UL, params.size());
290 _wsClient->subscribeParameterUpdates({PARAM_1_NAME});
292 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
293 std::vector<foxglove::Parameter> params = future.get();
295 ASSERT_EQ(1UL, params.size());
296 EXPECT_EQ(params.front().getName(), PARAM_1_NAME);
298 _wsClient->unsubscribeParameterUpdates({PARAM_1_NAME});
302 ASSERT_EQ(std::future_status::timeout, future.wait_for(
ONE_SECOND));
308 std::make_shared<foxglove::Client<websocketpp::config::asio_client>>(),
310 std::make_shared<foxglove::Client<websocketpp::config::asio_client>>(),
313 std::vector<std::future<std::vector<foxglove::Parameter>>> futures;
314 for (
auto client : clients) {
316 std::async(std::launch::async, [client]() -> std::vector<foxglove::Parameter> {
318 const std::string requestId =
"req-123";
320 client->getParameters({}, requestId);
322 if (future.valid()) {
330 for (
auto& future : futures) {
331 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
332 std::vector<foxglove::Parameter> parameters;
333 EXPECT_NO_THROW(parameters = future.get());
334 EXPECT_GE(parameters.size(), 2UL);
341 std::make_shared<foxglove::Client<websocketpp::config::asio_client>>(),
343 std::make_shared<foxglove::Client<websocketpp::config::asio_client>>(),
347 for (
auto client : clients) {
348 ASSERT_EQ(std::future_status::ready, client->connect(
URI).wait_for(std::chrono::seconds(5)));
350 ASSERT_EQ(std::future_status::ready, serviceFuture.wait_for(std::chrono::seconds(5)));
359 const std::vector<uint8_t> expectedSerializedResponse = {1, 5, 0, 0, 0, 104, 101, 108, 108, 111};
361 std::vector<std::future<foxglove::ServiceResponse>> futures;
362 for (
auto client : clients) {
364 client->sendServiceRequest(request);
367 for (
auto& future : futures) {
368 ASSERT_EQ(std::future_status::ready, future.wait_for(std::chrono::seconds(5)));
370 EXPECT_NO_THROW(
response = future.get());
374 EXPECT_EQ(
response.data, expectedSerializedResponse);
378 TEST(FetchAssetTest, fetchExistingAsset) {
379 auto wsClient = std::make_shared<foxglove::Client<websocketpp::config::asio_client>>();
380 EXPECT_EQ(std::future_status::ready, wsClient->connect(
URI).wait_for(
DEFAULT_TIMEOUT));
382 const auto millisSinceEpoch = std::chrono::duration_cast<std::chrono::milliseconds>(
384 const auto tmpFilePath =
385 boost::filesystem::temp_directory_path() / std::to_string(millisSinceEpoch.count());
386 constexpr
char content[] =
"Hello, world";
387 FILE* tmpAssetFile = std::fopen(tmpFilePath.c_str(),
"w");
388 std::fputs(content, tmpAssetFile);
389 std::fclose(tmpAssetFile);
391 const std::string uri = std::string(
"file://") + tmpFilePath.string();
392 const uint32_t requestId = 123;
395 wsClient->fetchAsset(uri, requestId);
396 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
399 EXPECT_EQ(
response.requestId, requestId);
402 ASSERT_EQ(
response.data.size() + 1ul,
sizeof(content));
403 EXPECT_EQ(0, std::memcmp(content,
response.data.data(),
response.data.size()));
404 std::remove(tmpFilePath.c_str());
407 TEST(FetchAssetTest, fetchNonExistingAsset) {
408 auto wsClient = std::make_shared<foxglove::Client<websocketpp::config::asio_client>>();
409 EXPECT_EQ(std::future_status::ready, wsClient->connect(
URI).wait_for(
DEFAULT_TIMEOUT));
411 const std::string assetId =
"file:///foo/bar";
412 const uint32_t requestId = 456;
415 wsClient->fetchAsset(assetId, requestId);
416 ASSERT_EQ(std::future_status::ready, future.wait_for(
DEFAULT_TIMEOUT));
419 EXPECT_EQ(
response.requestId, requestId);
421 EXPECT_FALSE(
response.errorMessage.empty());
425 int main(
int argc,
char** argv) {
426 testing::InitGoogleTest(&argc, argv);
431 std::this_thread::sleep_for(std::chrono::seconds(2));
435 const auto testResult = RUN_ALL_TESTS();