Skip to content

Commit ef695f6

Browse files
committed
Zenoh: add INTEGRATION_twoProcsSrvCallLateResponder test
Generated-by: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: Carlos Aguero <caguero@honurobotics.com>
1 parent 7635405 commit ef695f6

2 files changed

Lines changed: 112 additions & 0 deletions

File tree

test/integration/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ set(tests
88
twoProcsPubSub.cc
99
twoProcsPubSubStats.cc
1010
twoProcsSrvCall.cc
11+
twoProcsSrvCallLateResponder.cc
1112
twoProcsSrvCallStress.cc
1213
twoProcsSrvCallSync1.cc
1314
twoProcsSrvCallWithoutInput.cc
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright (C) 2026 Open Source Robotics Foundation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
#include <gz/msgs/int32.pb.h>
18+
19+
#include <chrono>
20+
#include <memory>
21+
#include <string>
22+
#include <thread>
23+
#include <vector>
24+
25+
#include "gz/transport/Node.hh"
26+
27+
#include <gz/utils/Environment.hh>
28+
#include "gz/utils/Subprocess.hh"
29+
30+
#include "gtest/gtest.h"
31+
#include "test_config.hh"
32+
#include "test_utils.hh"
33+
34+
using namespace gz;
35+
36+
static constexpr const char *g_topic = "/foo";
37+
static constexpr int g_data = 5;
38+
39+
//////////////////////////////////////////////////
40+
/// \brief Fixture that does NOT spawn the responder in SetUp.
41+
/// The test body opens a Node first and only then spawns the
42+
/// replier, exercising the cold-start path where a request is
43+
/// issued before the responder's queryable has been declared.
44+
class twoProcSrvCallLateResponder : public testing::Test {
45+
protected:
46+
void SetUp() override {
47+
gz::utils::env("GZ_PARTITION", this->prevPartition);
48+
this->partition = testing::getRandomNumber();
49+
gz::utils::setenv("GZ_PARTITION", this->partition);
50+
// Note: do NOT spawn the replier yet.
51+
}
52+
53+
void TearDown() override {
54+
if (this->replier)
55+
{
56+
this->replier->Terminate();
57+
this->replier->Join();
58+
}
59+
gz::utils::setenv("GZ_PARTITION", this->prevPartition);
60+
}
61+
62+
/// \brief Spawn the replier. Typically called from a worker
63+
/// thread after the requester has issued a blocking Request.
64+
void SpawnReplier() {
65+
this->replier = std::make_unique<gz::utils::Subprocess>(
66+
std::vector<std::string>(
67+
{test_executables::kTwoProcsSrvCallReplier, this->partition}));
68+
}
69+
70+
private:
71+
std::string prevPartition;
72+
std::string partition;
73+
std::unique_ptr<gz::utils::Subprocess> replier;
74+
};
75+
76+
//////////////////////////////////////////////////
77+
TEST_F(twoProcSrvCallLateResponder, RequestSucceedsWhenResponderAppearsLate)
78+
{
79+
// Open the requester Node before the responder process exists.
80+
// The persistent Querier declares interest on /foo immediately;
81+
// once the responder is spawned, it sees the interest and
82+
// propagates its queryable so the in-flight request finds it.
83+
transport::Node node;
84+
85+
// Spawn the replier from a worker thread 500 ms after we begin
86+
// the synchronous Request below. 500 ms is well inside the 5 s
87+
// request timeout, so a working request/reply path should
88+
// comfortably succeed within budget.
89+
std::thread spawner([this]()
90+
{
91+
std::this_thread::sleep_for(std::chrono::milliseconds(500));
92+
this->SpawnReplier();
93+
});
94+
95+
msgs::Int32 req;
96+
req.set_data(g_data);
97+
msgs::Int32 rep;
98+
bool result = false;
99+
const unsigned int timeoutMs = 5000;
100+
101+
const bool executed = node.Request(g_topic, req, timeoutMs, rep, result);
102+
103+
if (spawner.joinable())
104+
spawner.join();
105+
106+
EXPECT_TRUE(executed)
107+
<< "Request did not complete within " << timeoutMs << "ms after the "
108+
<< "late responder start";
109+
EXPECT_TRUE(result) << "responder returned a failure result";
110+
EXPECT_EQ(rep.data(), g_data);
111+
}

0 commit comments

Comments
 (0)