|  | @@ -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
 |