Merge pull request #23827 from apolcyn/bump_to_1_31_1
Bump 1.31.x to 1.31.1
diff --git a/src/compiler/ruby_generator_string-inl.h b/src/compiler/ruby_generator_string-inl.h
index b3e1938..968f795 100644
--- a/src/compiler/ruby_generator_string-inl.h
+++ b/src/compiler/ruby_generator_string-inl.h
@@ -124,7 +124,7 @@
ReplacePrefix(&proto_type, ".", ""); // remove the leading . (no package)
proto_type = RubyPackage(descriptor->file()) + "." + proto_type;
}
- std::string res(proto_type);
+ std::string res("." + proto_type);
if (res.find('.') == std::string::npos) {
return res;
} else {
diff --git a/src/ruby/bin/math_services_pb.rb b/src/ruby/bin/math_services_pb.rb
index e6f32e3..961117c 100644
--- a/src/ruby/bin/math_services_pb.rb
+++ b/src/ruby/bin/math_services_pb.rb
@@ -31,19 +31,19 @@
# Div divides DivArgs.dividend by DivArgs.divisor and returns the quotient
# and remainder.
- rpc :Div, DivArgs, DivReply
+ rpc :Div, ::Math::DivArgs, ::Math::DivReply
# DivMany accepts an arbitrary number of division args from the client stream
# and sends back the results in the reply stream. The stream continues until
# the client closes its end; the server does the same after sending all the
# replies. The stream ends immediately if either end aborts.
- rpc :DivMany, stream(DivArgs), stream(DivReply)
+ rpc :DivMany, stream(::Math::DivArgs), stream(::Math::DivReply)
# Fib generates numbers in the Fibonacci sequence. If FibArgs.limit > 0, Fib
# generates up to limit numbers; otherwise it continues until the call is
# canceled. Unlike Fib above, Fib has no final FibReply.
- rpc :Fib, FibArgs, stream(Num)
+ rpc :Fib, ::Math::FibArgs, stream(::Math::Num)
# Sum sums a stream of numbers, returning the final result once the stream
# is closed.
- rpc :Sum, stream(Num), Num
+ rpc :Sum, stream(::Math::Num), ::Math::Num
end
Stub = Service.rpc_stub_class
diff --git a/src/ruby/end2end/package_with_underscore_checker.rb b/src/ruby/end2end/package_with_underscore_checker.rb
index 27ea00f..043e621 100644
--- a/src/ruby/end2end/package_with_underscore_checker.rb
+++ b/src/ruby/end2end/package_with_underscore_checker.rb
@@ -43,8 +43,8 @@
end
correct_modularized_rpc = 'rpc :TestOne, ' \
- 'Grpc::Testing::PackageWithUnderscore::Data::Request, ' \
- 'Grpc::Testing::PackageWithUnderscore::Data::Response'
+ '::Grpc::Testing::PackageWithUnderscore::Data::Request, ' \
+ '::Grpc::Testing::PackageWithUnderscore::Data::Response'
return if got.include?(correct_modularized_rpc)
diff --git a/src/ruby/lib/grpc/generic/client_stub.rb b/src/ruby/lib/grpc/generic/client_stub.rb
index b193f5c..1884dcb 100644
--- a/src/ruby/lib/grpc/generic/client_stub.rb
+++ b/src/ruby/lib/grpc/generic/client_stub.rb
@@ -100,7 +100,7 @@
channel_args: {},
interceptors: [])
@ch = ClientStub.setup_channel(channel_override, host, creds,
- channel_args)
+ channel_args.dup)
alt_host = channel_args[Core::Channel::SSL_TARGET]
@host = alt_host.nil? ? host : alt_host
@propagate_mask = propagate_mask
diff --git a/src/ruby/pb/grpc/health/v1/health_services_pb.rb b/src/ruby/pb/grpc/health/v1/health_services_pb.rb
index 5992f1c..351e7e1 100644
--- a/src/ruby/pb/grpc/health/v1/health_services_pb.rb
+++ b/src/ruby/pb/grpc/health/v1/health_services_pb.rb
@@ -36,7 +36,7 @@
# If the requested service is unknown, the call will fail with status
# NOT_FOUND.
- rpc :Check, HealthCheckRequest, HealthCheckResponse
+ rpc :Check, ::Grpc::Health::V1::HealthCheckRequest, ::Grpc::Health::V1::HealthCheckResponse
# Performs a watch for the serving status of the requested service.
# The server will immediately send back a message indicating the current
# serving status. It will then subsequently send a new message whenever
@@ -52,7 +52,7 @@
# should assume this method is not supported and should not retry the
# call. If the call terminates with any other status (including OK),
# clients should retry the call with appropriate exponential backoff.
- rpc :Watch, HealthCheckRequest, stream(HealthCheckResponse)
+ rpc :Watch, ::Grpc::Health::V1::HealthCheckRequest, stream(::Grpc::Health::V1::HealthCheckResponse)
end
Stub = Service.rpc_stub_class
diff --git a/src/ruby/pb/src/proto/grpc/testing/messages_pb.rb b/src/ruby/pb/src/proto/grpc/testing/messages_pb.rb
index f492ccf..d902ae0 100644
--- a/src/ruby/pb/src/proto/grpc/testing/messages_pb.rb
+++ b/src/ruby/pb/src/proto/grpc/testing/messages_pb.rb
@@ -71,6 +71,10 @@
add_message "grpc.testing.LoadBalancerStatsResponse" do
map :rpcs_by_peer, :string, :int32, 1
optional :num_failures, :int32, 2
+ map :rpcs_by_method, :string, :message, 3, "grpc.testing.LoadBalancerStatsResponse.RpcsByPeer"
+ end
+ add_message "grpc.testing.LoadBalancerStatsResponse.RpcsByPeer" do
+ map :rpcs_by_peer, :string, :int32, 1
end
add_enum "grpc.testing.PayloadType" do
value :COMPRESSABLE, 0
@@ -99,6 +103,7 @@
ReconnectInfo = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.ReconnectInfo").msgclass
LoadBalancerStatsRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.LoadBalancerStatsRequest").msgclass
LoadBalancerStatsResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.LoadBalancerStatsResponse").msgclass
+ LoadBalancerStatsResponse::RpcsByPeer = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.LoadBalancerStatsResponse.RpcsByPeer").msgclass
PayloadType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.PayloadType").enummodule
GrpclbRouteType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.GrpclbRouteType").enummodule
end
diff --git a/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb b/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb
index 8138bd0..115acd0 100644
--- a/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb
+++ b/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb
@@ -36,31 +36,31 @@
self.service_name = 'grpc.testing.TestService'
# One empty request followed by one empty response.
- rpc :EmptyCall, Empty, Empty
+ rpc :EmptyCall, ::Grpc::Testing::Empty, ::Grpc::Testing::Empty
# One request followed by one response.
- rpc :UnaryCall, SimpleRequest, SimpleResponse
+ rpc :UnaryCall, ::Grpc::Testing::SimpleRequest, ::Grpc::Testing::SimpleResponse
# One request followed by one response. Response has cache control
# headers set such that a caching HTTP proxy (such as GFE) can
# satisfy subsequent requests.
- rpc :CacheableUnaryCall, SimpleRequest, SimpleResponse
+ rpc :CacheableUnaryCall, ::Grpc::Testing::SimpleRequest, ::Grpc::Testing::SimpleResponse
# One request followed by a sequence of responses (streamed download).
# The server returns the payload with client desired type and sizes.
- rpc :StreamingOutputCall, StreamingOutputCallRequest, stream(StreamingOutputCallResponse)
+ rpc :StreamingOutputCall, ::Grpc::Testing::StreamingOutputCallRequest, stream(::Grpc::Testing::StreamingOutputCallResponse)
# A sequence of requests followed by one response (streamed upload).
# The server returns the aggregated size of client payload as the result.
- rpc :StreamingInputCall, stream(StreamingInputCallRequest), StreamingInputCallResponse
+ rpc :StreamingInputCall, stream(::Grpc::Testing::StreamingInputCallRequest), ::Grpc::Testing::StreamingInputCallResponse
# A sequence of requests with each request served by the server immediately.
# As one request could lead to multiple responses, this interface
# demonstrates the idea of full duplexing.
- rpc :FullDuplexCall, stream(StreamingOutputCallRequest), stream(StreamingOutputCallResponse)
+ rpc :FullDuplexCall, stream(::Grpc::Testing::StreamingOutputCallRequest), stream(::Grpc::Testing::StreamingOutputCallResponse)
# A sequence of requests followed by a sequence of responses.
# The server buffers all the client requests and then serves them in order. A
# stream of responses are returned to the client when the server starts with
# first request.
- rpc :HalfDuplexCall, stream(StreamingOutputCallRequest), stream(StreamingOutputCallResponse)
+ rpc :HalfDuplexCall, stream(::Grpc::Testing::StreamingOutputCallRequest), stream(::Grpc::Testing::StreamingOutputCallResponse)
# The test server will not implement this method. It will be used
# to test the behavior when clients call unimplemented methods.
- rpc :UnimplementedCall, Empty, Empty
+ rpc :UnimplementedCall, ::Grpc::Testing::Empty, ::Grpc::Testing::Empty
end
Stub = Service.rpc_stub_class
@@ -77,7 +77,7 @@
self.service_name = 'grpc.testing.UnimplementedService'
# A call that no server should implement
- rpc :UnimplementedCall, Empty, Empty
+ rpc :UnimplementedCall, ::Grpc::Testing::Empty, ::Grpc::Testing::Empty
end
Stub = Service.rpc_stub_class
@@ -92,8 +92,8 @@
self.unmarshal_class_method = :decode
self.service_name = 'grpc.testing.ReconnectService'
- rpc :Start, ReconnectParams, Empty
- rpc :Stop, Empty, ReconnectInfo
+ rpc :Start, ::Grpc::Testing::ReconnectParams, ::Grpc::Testing::Empty
+ rpc :Stop, ::Grpc::Testing::Empty, ::Grpc::Testing::ReconnectInfo
end
Stub = Service.rpc_stub_class
@@ -109,7 +109,23 @@
self.service_name = 'grpc.testing.LoadBalancerStatsService'
# Gets the backend distribution for RPCs sent by a test client.
- rpc :GetClientStats, LoadBalancerStatsRequest, LoadBalancerStatsResponse
+ rpc :GetClientStats, ::Grpc::Testing::LoadBalancerStatsRequest, ::Grpc::Testing::LoadBalancerStatsResponse
+ end
+
+ Stub = Service.rpc_stub_class
+ end
+ module XdsUpdateHealthService
+ # A service to remotely control health status of an xDS test server.
+ class Service
+
+ include GRPC::GenericService
+
+ self.marshal_class_method = :encode
+ self.unmarshal_class_method = :decode
+ self.service_name = 'grpc.testing.XdsUpdateHealthService'
+
+ rpc :SetServing, ::Grpc::Testing::Empty, ::Grpc::Testing::Empty
+ rpc :SetNotServing, ::Grpc::Testing::Empty, ::Grpc::Testing::Empty
end
Stub = Service.rpc_stub_class
diff --git a/src/ruby/qps/src/proto/grpc/testing/benchmark_service_services_pb.rb b/src/ruby/qps/src/proto/grpc/testing/benchmark_service_services_pb.rb
index 65e5a75..63e2d5d 100644
--- a/src/ruby/qps/src/proto/grpc/testing/benchmark_service_services_pb.rb
+++ b/src/ruby/qps/src/proto/grpc/testing/benchmark_service_services_pb.rb
@@ -34,20 +34,20 @@
# One request followed by one response.
# The server returns the client payload as-is.
- rpc :UnaryCall, SimpleRequest, SimpleResponse
+ rpc :UnaryCall, ::Grpc::Testing::SimpleRequest, ::Grpc::Testing::SimpleResponse
# Repeated sequence of one request followed by one response.
# Should be called streaming ping-pong
# The server returns the client payload as-is on each response
- rpc :StreamingCall, stream(SimpleRequest), stream(SimpleResponse)
+ rpc :StreamingCall, stream(::Grpc::Testing::SimpleRequest), stream(::Grpc::Testing::SimpleResponse)
# Single-sided unbounded streaming from client to server
# The server returns the client payload as-is once the client does WritesDone
- rpc :StreamingFromClient, stream(SimpleRequest), SimpleResponse
+ rpc :StreamingFromClient, stream(::Grpc::Testing::SimpleRequest), ::Grpc::Testing::SimpleResponse
# Single-sided unbounded streaming from server to client
# The server repeatedly returns the client payload as-is
- rpc :StreamingFromServer, SimpleRequest, stream(SimpleResponse)
+ rpc :StreamingFromServer, ::Grpc::Testing::SimpleRequest, stream(::Grpc::Testing::SimpleResponse)
# Two-sided unbounded streaming between server to client
# Both sides send the content of their own choice to the other
- rpc :StreamingBothWays, stream(SimpleRequest), stream(SimpleResponse)
+ rpc :StreamingBothWays, stream(::Grpc::Testing::SimpleRequest), stream(::Grpc::Testing::SimpleResponse)
end
Stub = Service.rpc_stub_class
diff --git a/src/ruby/qps/src/proto/grpc/testing/messages_pb.rb b/src/ruby/qps/src/proto/grpc/testing/messages_pb.rb
index f492ccf..d902ae0 100644
--- a/src/ruby/qps/src/proto/grpc/testing/messages_pb.rb
+++ b/src/ruby/qps/src/proto/grpc/testing/messages_pb.rb
@@ -71,6 +71,10 @@
add_message "grpc.testing.LoadBalancerStatsResponse" do
map :rpcs_by_peer, :string, :int32, 1
optional :num_failures, :int32, 2
+ map :rpcs_by_method, :string, :message, 3, "grpc.testing.LoadBalancerStatsResponse.RpcsByPeer"
+ end
+ add_message "grpc.testing.LoadBalancerStatsResponse.RpcsByPeer" do
+ map :rpcs_by_peer, :string, :int32, 1
end
add_enum "grpc.testing.PayloadType" do
value :COMPRESSABLE, 0
@@ -99,6 +103,7 @@
ReconnectInfo = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.ReconnectInfo").msgclass
LoadBalancerStatsRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.LoadBalancerStatsRequest").msgclass
LoadBalancerStatsResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.LoadBalancerStatsResponse").msgclass
+ LoadBalancerStatsResponse::RpcsByPeer = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.LoadBalancerStatsResponse.RpcsByPeer").msgclass
PayloadType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.PayloadType").enummodule
GrpclbRouteType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.GrpclbRouteType").enummodule
end
diff --git a/src/ruby/qps/src/proto/grpc/testing/report_qps_scenario_service_services_pb.rb b/src/ruby/qps/src/proto/grpc/testing/report_qps_scenario_service_services_pb.rb
index ddc81be..5e41cfe 100644
--- a/src/ruby/qps/src/proto/grpc/testing/report_qps_scenario_service_services_pb.rb
+++ b/src/ruby/qps/src/proto/grpc/testing/report_qps_scenario_service_services_pb.rb
@@ -33,7 +33,7 @@
self.service_name = 'grpc.testing.ReportQpsScenarioService'
# Report results of a QPS test benchmark scenario.
- rpc :ReportScenario, ScenarioResult, Void
+ rpc :ReportScenario, ::Grpc::Testing::ScenarioResult, ::Grpc::Testing::Void
end
Stub = Service.rpc_stub_class
diff --git a/src/ruby/qps/src/proto/grpc/testing/worker_service_services_pb.rb b/src/ruby/qps/src/proto/grpc/testing/worker_service_services_pb.rb
index a7ecc95..049db47 100644
--- a/src/ruby/qps/src/proto/grpc/testing/worker_service_services_pb.rb
+++ b/src/ruby/qps/src/proto/grpc/testing/worker_service_services_pb.rb
@@ -38,18 +38,18 @@
# stats. Closing the stream will initiate shutdown of the test server
# and once the shutdown has finished, the OK status is sent to terminate
# this RPC.
- rpc :RunServer, stream(ServerArgs), stream(ServerStatus)
+ rpc :RunServer, stream(::Grpc::Testing::ServerArgs), stream(::Grpc::Testing::ServerStatus)
# Start client with specified workload.
# First request sent specifies the ClientConfig followed by ClientStatus
# response. After that, a "Mark" can be sent anytime to request the latest
# stats. Closing the stream will initiate shutdown of the test client
# and once the shutdown has finished, the OK status is sent to terminate
# this RPC.
- rpc :RunClient, stream(ClientArgs), stream(ClientStatus)
+ rpc :RunClient, stream(::Grpc::Testing::ClientArgs), stream(::Grpc::Testing::ClientStatus)
# Just return the core count - unary call
- rpc :CoreCount, CoreRequest, CoreResponse
+ rpc :CoreCount, ::Grpc::Testing::CoreRequest, ::Grpc::Testing::CoreResponse
# Quit this worker
- rpc :QuitWorker, Void, Void
+ rpc :QuitWorker, ::Grpc::Testing::Void, ::Grpc::Testing::Void
end
Stub = Service.rpc_stub_class
diff --git a/src/ruby/spec/pb/codegen/grpc/testing/same_package_service_name.proto b/src/ruby/spec/pb/codegen/grpc/testing/same_package_service_name.proto
new file mode 100644
index 0000000..15dd8f0
--- /dev/null
+++ b/src/ruby/spec/pb/codegen/grpc/testing/same_package_service_name.proto
@@ -0,0 +1,27 @@
+// Copyright 2020 gRPC authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package same_name;
+
+service SameName {
+ rpc Health(Request) returns (Status);
+}
+
+message Status {
+ string msg = 1;
+}
+
+message Request {}
diff --git a/src/ruby/spec/pb/codegen/grpc/testing/same_ruby_package_service_name.proto b/src/ruby/spec/pb/codegen/grpc/testing/same_ruby_package_service_name.proto
new file mode 100644
index 0000000..9ce631c
--- /dev/null
+++ b/src/ruby/spec/pb/codegen/grpc/testing/same_ruby_package_service_name.proto
@@ -0,0 +1,29 @@
+// Copyright 2020 gRPC authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package other_name;
+
+option ruby_package = "SameName2";
+
+service SameName2 {
+ rpc Health(Request) returns (Status);
+}
+
+message Status {
+ string msg = 1;
+}
+
+message Request {}
diff --git a/src/ruby/spec/pb/codegen/package_option_spec.rb b/src/ruby/spec/pb/codegen/package_option_spec.rb
index a4668ac..f64b4f6 100644
--- a/src/ruby/spec/pb/codegen/package_option_spec.rb
+++ b/src/ruby/spec/pb/codegen/package_option_spec.rb
@@ -48,6 +48,26 @@
expect(services[:NestedMessageTest].output).to eq(RPC::Test::New::Package::Options::Bar::Baz)
end
end
+
+ it 'should generate when package and service has same name' do
+ with_protos(['grpc/testing/same_package_service_name.proto']) do
+ expect { SameName::SameName::Service }.to raise_error(NameError)
+ expect(require('grpc/testing/same_package_service_name_services_pb')).to be_truthy
+ expect { SameName::SameName::Service }.to_not raise_error
+ expect { SameName::Request }.to_not raise_error
+ expect { SameName::Status }.to_not raise_error
+ end
+ end
+
+ it 'should generate when ruby_package and service has same name' do
+ with_protos(['grpc/testing/same_ruby_package_service_name.proto']) do
+ expect { SameName2::SameName2::Service }.to raise_error(NameError)
+ expect(require('grpc/testing/same_ruby_package_service_name_services_pb')).to be_truthy
+ expect { SameName2::SameName2::Service }.to_not raise_error
+ expect { SameName2::Request }.to_not raise_error
+ expect { SameName2::Status }.to_not raise_error
+ end
+ end
end
def with_protos(file_paths)
diff --git a/src/ruby/spec/user_agent_spec.rb b/src/ruby/spec/user_agent_spec.rb
new file mode 100644
index 0000000..a5aa206
--- /dev/null
+++ b/src/ruby/spec/user_agent_spec.rb
@@ -0,0 +1,74 @@
+# Copyright 2020 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'spec_helper'
+
+# a test service that checks the cert of its peer
+class UserAgentEchoService
+ include GRPC::GenericService
+ rpc :an_rpc, EchoMsg, EchoMsg
+
+ def an_rpc(_req, call)
+ EchoMsg.new(msg: call.metadata['user-agent'])
+ end
+end
+
+UserAgentEchoServiceStub = UserAgentEchoService.rpc_stub_class
+
+describe 'user agent' do
+ RpcServer = GRPC::RpcServer
+
+ before(:all) do
+ server_opts = {
+ poll_period: 1
+ }
+ @srv = new_rpc_server_for_testing(**server_opts)
+ @port = @srv.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
+ @srv.handle(UserAgentEchoService)
+ @srv_thd = Thread.new { @srv.run }
+ @srv.wait_till_running
+ end
+
+ after(:all) do
+ expect(@srv.stopped?).to be(false)
+ @srv.stop
+ @srv_thd.join
+ end
+
+ it 'client sends expected user agent' do
+ stub = UserAgentEchoServiceStub.new("localhost:#{@port}",
+ :this_channel_is_insecure,
+ {})
+ response = stub.an_rpc(EchoMsg.new)
+ expected_user_agent_prefix = "grpc-ruby/#{GRPC::VERSION}"
+ expect(response.msg.start_with?(expected_user_agent_prefix)).to be true
+ # check that the expected user agent prefix occurs in the real user agent exactly once
+ expect(response.msg.split(expected_user_agent_prefix).size).to eq 2
+ end
+
+ it 'user agent header does not grow when the same channel args hash is used across multiple stubs' do
+ shared_channel_args_hash = {}
+ 10.times do
+ stub = UserAgentEchoServiceStub.new("localhost:#{@port}",
+ :this_channel_is_insecure,
+ channel_args: shared_channel_args_hash)
+ response = stub.an_rpc(EchoMsg.new)
+ puts "got echo response: #{response.msg}"
+ expected_user_agent_prefix = "grpc-ruby/#{GRPC::VERSION}"
+ expect(response.msg.start_with?(expected_user_agent_prefix)).to be true
+ # check that the expected user agent prefix occurs in the real user agent exactly once
+ expect(response.msg.split(expected_user_agent_prefix).size).to eq 2
+ end
+ end
+end