|
|
|
@ -43,37 +43,11 @@ class MutableValue |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
# GreeterServer is simple server that implements the Helloworld Greeter server. |
|
|
|
|
# This service also has a mechanism to wait for a timeout until the first |
|
|
|
|
# RPC has been received, which is useful for synchronizing between parent |
|
|
|
|
# and child processes. |
|
|
|
|
class EchoServerImpl < Echo::EchoServer::Service |
|
|
|
|
def initialize |
|
|
|
|
@first_rpc_received_mu = Mutex.new |
|
|
|
|
@first_rpc_received_cv = ConditionVariable.new |
|
|
|
|
@first_rpc_received = MutableValue.new(false) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
# say_hello implements the SayHello rpc method. |
|
|
|
|
def echo(echo_req, _) |
|
|
|
|
@first_rpc_received_mu.synchronize do |
|
|
|
|
@first_rpc_received.value = true |
|
|
|
|
@first_rpc_received_cv.broadcast |
|
|
|
|
end |
|
|
|
|
Echo::EchoReply.new(response: echo_req.request) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def wait_for_first_rpc_received(timeout_seconds) |
|
|
|
|
Timeout.timeout(timeout_seconds) do |
|
|
|
|
@first_rpc_received_mu.synchronize do |
|
|
|
|
until @first_rpc_received.value |
|
|
|
|
@first_rpc_received_cv.wait(@first_rpc_received_mu) |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
rescue => e |
|
|
|
|
fail "Received error:|#{e}| while waiting for #{timeout_seconds} " \ |
|
|
|
|
'seconds to receive the first RPC' |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
# ServerRunner starts an "echo server" that test clients can make calls to |
|
|
|
@ -105,32 +79,64 @@ class ServerRunner |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def start_client(client_main, server_port) |
|
|
|
|
this_dir = File.expand_path(File.dirname(__FILE__)) |
|
|
|
|
|
|
|
|
|
tmp_server = TCPServer.new(0) |
|
|
|
|
client_control_port = tmp_server.local_address.ip_port |
|
|
|
|
tmp_server.close |
|
|
|
|
|
|
|
|
|
client_path = File.join(this_dir, client_main) |
|
|
|
|
client_pid = Process.spawn(RbConfig.ruby, |
|
|
|
|
client_path, |
|
|
|
|
"--client_control_port=#{client_control_port}", |
|
|
|
|
"--server_port=#{server_port}") |
|
|
|
|
control_stub = ClientControl::ClientController::Stub.new( |
|
|
|
|
"localhost:#{client_control_port}", :this_channel_is_insecure) |
|
|
|
|
[control_stub, client_pid] |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def cleanup(control_stub, client_pid, server_runner) |
|
|
|
|
control_stub.shutdown(ClientControl::Void.new) |
|
|
|
|
Process.wait(client_pid) |
|
|
|
|
|
|
|
|
|
client_exit_code = $CHILD_STATUS |
|
|
|
|
# ClientController is used to start a child process and communicate |
|
|
|
|
# with it for test orchestration purposes via RPCs. |
|
|
|
|
class ClientController < ClientControl::ParentController::Service |
|
|
|
|
attr_reader :stub, :client_pid |
|
|
|
|
|
|
|
|
|
def initialize(client_main, server_port) |
|
|
|
|
this_dir = File.expand_path(File.dirname(__FILE__)) |
|
|
|
|
client_path = File.join(this_dir, client_main) |
|
|
|
|
@server = new_rpc_server_for_testing(poll_period: 3) |
|
|
|
|
port = @server.add_http2_port('localhost:0', :this_port_is_insecure) |
|
|
|
|
server_thread = Thread.new do |
|
|
|
|
@server.handle(self) |
|
|
|
|
@server.run |
|
|
|
|
end |
|
|
|
|
@server.wait_till_running |
|
|
|
|
@client_controller_port_mu = Mutex.new |
|
|
|
|
@client_controller_port_cv = ConditionVariable.new |
|
|
|
|
@client_controller_port = nil |
|
|
|
|
@client_pid = Process.spawn(RbConfig.ruby, |
|
|
|
|
client_path, |
|
|
|
|
"--parent_controller_port=#{port}", |
|
|
|
|
"--server_port=#{server_port}") |
|
|
|
|
begin |
|
|
|
|
Timeout.timeout(10) do |
|
|
|
|
@client_controller_port_mu.synchronize do |
|
|
|
|
while @client_controller_port.nil? |
|
|
|
|
@client_controller_port_cv.wait(@client_controller_port_mu) |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
rescue => e |
|
|
|
|
fail "timeout waiting for child process to report port. error: #{e}" |
|
|
|
|
end |
|
|
|
|
@server.stop |
|
|
|
|
server_thread.join |
|
|
|
|
@stub = ClientControl::ClientController::Stub.new( |
|
|
|
|
"localhost:#{@client_controller_port}", :this_channel_is_insecure) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
if client_exit_code != 0 |
|
|
|
|
fail "term sig test failure: client exit code: #{client_exit_code}" |
|
|
|
|
def set_client_controller_port(req, _) |
|
|
|
|
@client_controller_port_mu.synchronize do |
|
|
|
|
unless @client_controller_port.nil? |
|
|
|
|
fail 'client controller port already set' |
|
|
|
|
end |
|
|
|
|
@client_controller_port = req.port |
|
|
|
|
@client_controller_port_cv.broadcast |
|
|
|
|
end |
|
|
|
|
ClientControl::Void.new |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
server_runner.stop |
|
|
|
|
def report_controller_port_to_parent(parent_controller_port, client_controller_port) |
|
|
|
|
unless parent_controller_port.to_i > 0 |
|
|
|
|
fail "bad parent control port: |#{parent_controller_port}|" |
|
|
|
|
end |
|
|
|
|
stub = ClientControl::ParentController::Stub.new( |
|
|
|
|
"localhost:#{parent_controller_port.to_i}", :this_channel_is_insecure) |
|
|
|
|
m = ClientControl::Port.new |
|
|
|
|
m.port = client_controller_port.to_i |
|
|
|
|
stub.set_client_controller_port(m, deadline: Time.now + 10) |
|
|
|
|
end |
|
|
|
|