class Grntest::TestRunner

Constants

MAX_N_COLUMNS

Public Class Methods

new(tester, worker) click to toggle source
# File lib/grntest/test-runner.rb, line 101
def initialize(tester, worker)
  @tester = tester
  @worker = worker
  @max_n_columns = MAX_N_COLUMNS
  @id = nil
end

Public Instance Methods

run() click to toggle source
# File lib/grntest/test-runner.rb, line 108
def run
  succeeded = true

  @worker.on_test_start
  result = TestResult.new(@worker)
  result.measure do
    execute_groonga_script(result)
  end
  normalize_actual_result(result)
  result.expected = read_expected_result
  case result.status
  when :success
    @worker.on_test_success(result)
    remove_reject_file
  when :failure
    @worker.on_test_failure(result)
    output_reject_file(result.actual)
    succeeded = false
  when :leaked
    @worker.on_test_leak(result)
    output_actual_file(result.actual) unless result.checked?
    succeeded = false
  when :omitted
    @worker.on_test_omission(result)
  else
    @worker.on_test_no_check(result)
    output_actual_file(result.actual)
  end
  @worker.on_test_finish(result)

  succeeded
end

Private Instance Methods

check_memory_leak(context) click to toggle source
# File lib/grntest/test-runner.rb, line 982
def check_memory_leak(context)
  parser = GroongaLog::Parser.new
  parser.parse(context.log) do |entry|
    next unless /^grn_fin \((\d+)\)$/ =~ entry.message
    n_leaked_objects = $1.to_i
    next if n_leaked_objects.zero?
    context.result << [:n_leaked_objects, n_leaked_objects, {}]
  end
end
command_command_line(command, context, spawn_options) click to toggle source
# File lib/grntest/test-runner.rb, line 275
    def command_command_line(command, context, spawn_options)
      command_line = []
      if @tester.gdb
        if libtool_wrapper?(command)
          command_line << find_libtool(command)
          command_line << "--mode=execute"
        end
        command_line << @tester.gdb
        gdb_command_path = context.temporary_directory_path + "groonga.gdb"
        gdb_command_path.open("w") do |gdb_command|
          gdb_command.puts(<<-COMMANDS)
break main
run
call (int)chdir("#{context.temporary_directory_path}")
          COMMANDS
        end
        command_line << "--command=#{gdb_command_path}"
        command_line << "--quiet"
        command_line << "--args"
      elsif @tester.rr
        if libtool_wrapper?(command)
          command_line << find_libtool(command)
          command_line << "--mode=execute"
        end
        command_line << @tester.rr
        command_line << "record"
      elsif @tester.valgrind
        if libtool_wrapper?(command)
          command_line << find_libtool(command)
          command_line << "--mode=execute"
        end
        command_line << @tester.valgrind
        command_line << "--leak-check=full"
        command_line << "--show-reachable=yes"
        command_line << "--track-origins=yes"
        valgrind_suppressions_file_path =
          context.temporary_directory_path + "groonga.supp"
        valgrind_suppressions_file_path.open("w") do |suppressions|
          suppressions.puts(<<-SUPPRESSIONS)
{
  dlopen
  Memcheck:Leak
  match-leak-kinds: reachable
  ...
  fun:dlopen*
  ...
}
{
  _dl_catch_error
  Memcheck:Leak
  match-leak-kinds: reachable
  ...
  fun:_dl_catch_error
}
{
  _dl_init
  Memcheck:Leak
  match-leak-kinds: reachable
  ...
  fun:_dl_init
  ...
}
{
  _Z41__static_initialization_and_destruction_0ii
  Memcheck:Leak
  match-leak-kinds: reachable
  ...
  fun:_Z41__static_initialization_and_destruction_0ii
  ...
}
{
  je_arrow_private_je_background_thread_create
  Memcheck:Leak
  match-leak-kinds: possible
  ...
  fun:je_arrow_private_je_background_thread_create
  ...
}
          SUPPRESSIONS
        end
        command_line << "--suppressions=#{valgrind_suppressions_file_path}"
        if @tester.valgrind_gen_suppressions?
          command_line << "--gen-suppressions=all"
        end
        command_line << "--verbose"
      else
        spawn_options[:chdir] = context.temporary_directory_path.to_s
      end
      command_line << command
      command_line
    end
create_config_file(context, host, port, pid_file_path) click to toggle source
# File lib/grntest/test-runner.rb, line 539
    def create_config_file(context, host, port, pid_file_path)
      config_file_path =
        context.temporary_directory_path + "groonga-httpd.conf"
      config_file_path.open("w") do |config_file|
        if @tester.ngx_http_groonga_module_so
          config_file.puts(<<-LOAD_MODULE)
load_module #{@tester.ngx_http_groonga_module_so};
          LOAD_MODULE
        end
        config_file.puts(<<-GLOBAL)
daemon off;
master_process off;
worker_processes 1;
working_directory #{context.temporary_directory_path};
error_log groonga-httpd-error.log;
pid #{pid_file_path};
events {
     worker_connections 1024;
}
        GLOBAL

        env = ENV.to_hash.merge(extract_custom_env(context))
        env.each do |key, value|
          next unless key.start_with?("GRN_")
          config_file.puts(<<-ENV)
env #{key};
          ENV
        end
        config_file.puts(<<-ENV)
env LD_LIBRARY_PATH;
env DYLD_LIBRARY_PATH;
        ENV

        config_file.puts(<<-HTTP)
http {
     server {
             access_log groonga-httpd-access.log;
             listen #{port};
             server_name #{host};
             location /d/ {
                     groonga_database #{context.relative_db_path};
                     groonga_log_path #{context.log_path};
                     groonga_query_log_path #{context.query_log_path};
                     groonga on;
                     client_max_body_size 500m;
                     client_body_temp_path #{context.temporary_directory_path + "body"};
            }
            client_header_buffer_size 64k;
            large_client_header_buffers 4 64k;
     }
}
        HTTP
      end
      config_file_path
    end
create_empty_database(db_path) click to toggle source
# File lib/grntest/test-runner.rb, line 595
def create_empty_database(db_path)
  output_file = Tempfile.new("create-empty-database")
  env = {
    "GRN_FMALLOC_PROB" => nil,
  }
  options = {}
  create_database_command = [@tester.groonga]
  if Platform.windows?
    options[:out] = output_file
  else
    options[output_file.fileno] = output_file.fileno
    create_database_command += [
      "--output-fd", output_file.fileno.to_s,
    ]
  end
  create_database_command += [
    "-n", db_path,
    "shutdown",
  ]
  system(env, *create_database_command, options)
  output_file.close(true)
end
create_temporary_directory() { |Pathname(path).expand_path| ... } click to toggle source
# File lib/grntest/test-runner.rb, line 185
def create_temporary_directory
  path = "tmp/grntest"
  path << ".#{@worker.id}" if @tester.n_workers > 1
  FileUtils.rm_rf(path, :secure => true)
  FileUtils.mkdir_p(path)
  begin
    yield(Pathname(path).expand_path)
  ensure
    if @tester.keep_database? and File.exist?(path)
      FileUtils.rm_rf(keep_database_path, :secure => true)
      FileUtils.mv(path, keep_database_path)
    else
      FileUtils.rm_rf(path, :secure => true)
    end
  end
end
decide_groonga_server_port(host) click to toggle source
# File lib/grntest/test-runner.rb, line 395
def decide_groonga_server_port(host)
  static_port = 50041 + @worker.id
  10.times do
    begin
      TCPServer.open(host, static_port) do |server|
        return static_port
      end
    rescue SystemCallError
      sleep(0.1)
    end
  end

  dynamic_port = TCPServer.open(host, 0) do |server|
    server.addr[1]
  end
  dynamic_port
end
ensure_process_finished(pid) click to toggle source
# File lib/grntest/test-runner.rb, line 463
def ensure_process_finished(pid)
  return if pid.nil?

  if @tester.gdb
    Process.waitpid(pid)
    return
  end

  [:TERM, :KILL].each do |signal|
    n_retries = 0
    loop do
      finished_pid = Process.waitpid(pid, Process::WNOHANG)
      return if finished_pid
      n_retries += 1
      return if n_retries > 100
      begin
        Process.kill(signal, pid)
      rescue SystemCallError
        $stderr.puts("#{signal} -> #{pid}: #{$!.class}: #{$!}")
        return
      end
      sleep(0.1)
    end
  end
end
execute_groonga_script(result) click to toggle source
# File lib/grntest/test-runner.rb, line 142
def execute_groonga_script(result)
  create_temporary_directory do |directory_path|
    if @tester.database_path
      db_path = Pathname(@tester.database_path).expand_path
    else
      db_dir = directory_path + "db"
      FileUtils.mkdir_p(db_dir.to_s)
      db_path = db_dir + "db"
    end
    context = ExecutionContext.new
    context.temporary_directory_path = directory_path
    context.db_path = db_path
    context.base_directory = @tester.base_directory.expand_path
    context.plugins_directory = @tester.plugins_directory.expand_path
    context.groonga_suggest_create_dataset =
      @tester.groonga_suggest_create_dataset
    context.groonga_synonym_generate =
      @tester.groonga_synonym_generate
    context.testee = @tester.testee
    context.interface = @tester.interface
    context.use_http_post = @tester.use_http_post?
    context.use_http_chunked = @tester.use_http_chunked?
    context.input_type = @tester.input_type
    context.output_type = @tester.output_type
    context.timeout = @tester.timeout
    context.timeout = 0 if @tester.gdb
    context.read_timeout = @tester.read_timeout
    context.default_timeout = context.timeout
    context.default_read_timeout = context.read_timeout
    context.shutdown_wait_timeout = @tester.shutdown_wait_timeout
    context.suppress_backtrace = @tester.suppress_backtrace?
    context.debug = @tester.debug?
    run_groonga(context) do |executor|
      executor.execute(test_script_path)
    end
    check_memory_leak(context)
    result.omitted = context.omitted?
    result.actual = context.result
    result.benchmarks = context.benchmarks
    context.close_logs
  end
end
extract_custom_env(context) click to toggle source
# File lib/grntest/test-runner.rb, line 923
def extract_custom_env(context)
  return {} unless test_script_path.exist?

  env = {}
  test_script_path.open("r:ascii-8bit") do |script_file|
    expander = VariableExpander.new(context)
    script_file.each_line do |line|
      case line
      when /\A\#\$([a-zA-Z_\d]+)=(.*)/
        env[$1] = expander.expand($2.strip)
      end
    end
  end
  env
end
find_libtool(command) click to toggle source
# File lib/grntest/test-runner.rb, line 384
def find_libtool(command)
  command_path = Pathname.new(command)
  directory = command_path.dirname
  until directory.root?
    libtool = directory + "libtool"
    return libtool.to_s if libtool.executable?
    directory = directory.parent
  end
  "libtool"
end
groonga_command_line(context, spawn_options) click to toggle source
# File lib/grntest/test-runner.rb, line 367
def groonga_command_line(context, spawn_options)
  command_line = command_command_line(@tester.groonga, context,
                                      spawn_options)
  command_line << "--log-path=#{context.log_path}"
  command_line << "--query-log-path=#{context.query_log_path}"
  command_line << "--working-directory=#{context.temporary_directory_path}"
  command_line
end
groonga_http_command(host, port, pid_file_path, context, spawn_options) click to toggle source
# File lib/grntest/test-runner.rb, line 510
def groonga_http_command(host, port, pid_file_path, context, spawn_options)
  case @tester.testee
  when "groonga"
    command_line = groonga_command_line(context, spawn_options)
    command_line += [
      "--pid-path", pid_file_path.to_s,
      "--bind-address", host,
      "--port", port.to_s,
      "--protocol", "http",
      "-s",
      context.relative_db_path.to_s,
    ]
  when "groonga-httpd", "groonga-nginx"
    command_line = command_command_line(@tester.groonga_httpd,
                                        context,
                                        spawn_options)
    config_file_path = create_config_file(context,
                                          host,
                                          port,
                                          pid_file_path)
    FileUtils.mkdir_p(context.temporary_directory_path + "logs")
    command_line += [
      "-c", config_file_path.to_s,
      "-p", "#{context.temporary_directory_path}/",
    ]
  end
  command_line
end
have_extension?() click to toggle source
# File lib/grntest/test-runner.rb, line 939
def have_extension?
  not test_script_path.extname.empty?
end
keep_database_path() click to toggle source
# File lib/grntest/test-runner.rb, line 202
def keep_database_path
  test_script_path.to_s.gsub(/\//, ".")
end
libtool_wrapper?(command) click to toggle source
# File lib/grntest/test-runner.rb, line 376
def libtool_wrapper?(command)
  return false unless File.exist?(command)
  File.open(command, "r") do |command_file|
    first_line = command_file.gets
    first_line.start_with?("#!")
  end
end
normalize_actual_result(result) click to toggle source
# File lib/grntest/test-runner.rb, line 618
def normalize_actual_result(result)
  normalized_result = "".b
  result.actual.each do |tag, content, options|
    case tag
    when :input
      normalized_result << content.b
    when :output
      normalized_result << normalize_output(content, options)
    when :error
      normalized_result << normalize_error(content)
    when :query
      normalized_result << normalize_raw_content(content)
    when :n_leaked_objects
      result.n_leaked_objects = content
    end
  end
  result.actual = normalized_result
end
normalize_apache_arrow_content(content, options) click to toggle source
# File lib/grntest/test-runner.rb, line 711
def normalize_apache_arrow_content(content, options)
  normalized = ""
  buffer = Arrow::Buffer.new(content)
  Arrow::BufferInputStream.open(buffer) do |input|
    while input.tell < content.bytesize
      reader = Arrow::RecordBatchStreamReader.new(input)
      table = reader.read_all
      schema = table.schema
      unless normalized.empty?
        normalized << "=" * 40
        normalized << "\n"
      end
      if schema.respond_to?(:to_string_metadata)
        normalized << schema.to_string_metadata(true)
      else
        normalized << schema.to_s
      end
      normalized << "\n"
      case (schema.metadata || {})["GROONGA:data_type"]
      when "metadata"
        normalized_table = normalize_apache_arrow_content_metadata(table)
        normalized << normalized_table.to_s
      when "trace_log"
        normalized_table = normalize_apache_arrow_content_trace_log(table)
        normalized << normalized_table.to_s
      else
        normalized << table.to_s
      end
    end
  end
  normalize_raw_content(normalized.chomp)
end
normalize_apache_arrow_content_metadata(table) click to toggle source
# File lib/grntest/test-runner.rb, line 744
def normalize_apache_arrow_content_metadata(table)
  normalized_records = table.each_record.collect do |record|
    normalized_record = []
    record.to_h.each do |name, value|
      case name
      when "start_time", "elapsed_time"
        value = 0
      when "error_message"
        value = normalize_error_message(value) if value
      when "error_file"
        value = normalize_error_file_path(value) if value
      when "error_line"
        value = 0 if value
      when "error_function"
        value = normalize_error_function(value) if value
      end
      normalized_record << value
    end
    normalized_record
  end
  Arrow::Table.new(table.schema, normalized_records)
end
normalize_apache_arrow_content_trace_log(table) click to toggle source
# File lib/grntest/test-runner.rb, line 767
def normalize_apache_arrow_content_trace_log(table)
  normalized_columns = table.columns.collect do |column|
    case column.name
    when "elapsed_time"
      normalized_elapsed_times =
        column.data_type.build_array(column.n_rows.times.to_a)
      Arrow::Column.new(Arrow::Table.new(elapsed_time: normalized_elapsed_times),
                        0)
    else
      column
    end
  end
  Arrow::Table.new(table.schema, normalized_columns)
end
normalize_body(body) click to toggle source
# File lib/grntest/test-runner.rb, line 830
def normalize_body(body)
  case body
  when Hash
    if body["exception"]
      exception = Marshal.load(Marshal.dump(body["exception"]))
      message = exception["message"]
      exception["message"] = normalize_error_message(message)
      body.merge("exception" => exception)
    else
      normalize_body_recursive(body)
    end
  else
    body
  end
end
normalize_body_recursive(body) click to toggle source
# File lib/grntest/test-runner.rb, line 846
def normalize_body_recursive(body)
  case body
  when Array
    body.collect do |value|
      normalize_body_recursive(value)
    end
  when Hash
    normalized_body = {}
    body.each do |key, value|
      case key
      when "path"
        if value
          normalized_value = normalize_plugin_path(value)
        else
          normalized_value = value
        end
        normalized_body[key] = normalized_value
      when "disk_usage"
        normalized_body[key] = 0
      else
        normalized_body[key] = normalize_body_recursive(value)
      end
    end
    normalized_body
  else
    body
  end
end
normalize_error(content) click to toggle source
# File lib/grntest/test-runner.rb, line 889
def normalize_error(content)
  content = normalize_error_message(content)
  normalize_raw_content(content)
end
normalize_error_file_path(path) click to toggle source
# File lib/grntest/test-runner.rb, line 911
def normalize_error_file_path(path)
  File.basename(path)
end
normalize_error_function(function) click to toggle source
# File lib/grntest/test-runner.rb, line 907
def normalize_error_function(function)
  function.split("::").last
end
normalize_error_message(content) click to toggle source
# File lib/grntest/test-runner.rb, line 894
def normalize_error_message(content)
  case content
  when /\A(.*: fopen: failed to open mruby script file: )<(.+?)>?\z/
    pre = $1
    _path = $2
    "#{pre}<PATH>"
  when /\A(line \d+:\d+: syntax error), unexpected .*\z/
    $1
  else
    content
  end
end
normalize_header(header) click to toggle source
# File lib/grntest/test-runner.rb, line 788
def normalize_header(header)
  if header.is_a?(Hash)
    return_code = header["return_code"]
    if return_code.zero?
      header.merge({
                     "start_time"   => 0.0,
                     "elapsed_time" => 0.0,
                   })
    else
      error = header["error"]
      normalized_values = {}
      message = error["message"]
      normalized_values["message"] = normalize_error_message(message)
      function = error["function"]
      if function
        normalized_values["function"] = normalize_error_function(function)
      end
      file = error["file"]
      if file
        normalized_values["file"] = normalize_error_file_path(file)
      end
      normalized_values["line"] = 0 if error["line"]
      header.merge({
                     "start_time"   => 0.0,
                     "elapsed_time" => 0.0,
                     "error"        => error.merge(normalized_values),
                   })
    end
  else
    return_code, started_time, elapsed_time, *rest = header
    _ = started_time = elapsed_time # for suppress warnings
    if return_code.zero?
      [0, 0.0, 0.0]
    else
      message, backtrace = rest
      _ = backtrace # for suppress warnings
      message = normalize_error_message(message)
      [[return_code, 0.0, 0.0], message]
    end
  end
end
normalize_new_line(content) click to toggle source
# File lib/grntest/test-runner.rb, line 637
def normalize_new_line(content)
  content.gsub(/\r\n/, "\n")
end
normalize_output(content, options) click to toggle source
# File lib/grntest/test-runner.rb, line 646
def normalize_output(content, options)
  type = options[:type]
  case type
  when "json", "msgpack"
    normalize_output_structured(type, content, options)
  when "apache-arrow"
    normalize_apache_arrow_content(content, options)
  when "xml"
    normalized_xml = normalize_output_xml(content, options)
    normalize_raw_content(normalized_xml)
  when "groonga-command"
    normalize_raw_content(content.chomp)
  else
    normalize_raw_content(content)
  end
end
normalize_output_structured(type, content, options) click to toggle source
# File lib/grntest/test-runner.rb, line 663
def normalize_output_structured(type, content, options)
  response = nil
  content = content.chomp
  if type == "json" and /\A([^(]+\()(.+)(\);)\z/ =~ content
    jsonp = true
    jsonp_start = $1
    content = $2
    jsonp_end = $3
  else
    jsonp = false
  end
  begin
    response = ResponseParser.parse(content, type)
  rescue ParseError
    return $!.message
  end

  if response.is_a?(Hash)
    normalized_response =
      response.merge({
                       "header" => normalize_header(response["header"]),
                       "body"   => normalize_body(response["body"]),
                     })
    if normalized_response.key?("trace_log")
      normalized_response["trace_log"] =
        normalize_trace_log(normalized_response["trace_log"])
    end
  else
    header, *values = response
    normalized_header = normalize_header(header)
    normalized_values = values.collect do |value|
      normalize_body(value)
    end
    normalized_response = [normalized_header, *normalized_values]
  end
  normalized_output = JSON.generate(normalized_response)
  if normalized_output.bytesize > @max_n_columns
    normalized_output = JSON.pretty_generate(normalized_response)
  end
  normalized_raw_content = normalize_raw_content(normalized_output)

  if jsonp
    "#{jsonp_start}#{normalized_raw_content.chomp}#{jsonp_end}\n"
  else
    normalized_raw_content
  end
end
normalize_output_xml(content, options) click to toggle source
# File lib/grntest/test-runner.rb, line 782
def normalize_output_xml(content, options)
  content.sub(/^<RESULT .+?>/) do |result|
    result.gsub(/( (?:UP|ELAPSED))="-?\d+\.\d+(?:e[+-]?\d+)?"/, '\1="0.0"')
  end
end
normalize_plugin_path(path) click to toggle source
# File lib/grntest/test-runner.rb, line 915
def normalize_plugin_path(path)
  path.gsub(/\.libs\//, "").gsub(/\.dll\z/, ".so")
end
normalize_raw_content(content) click to toggle source
# File lib/grntest/test-runner.rb, line 641
def normalize_raw_content(content)
  content = normalize_new_line(content)
  "#{content}\n".force_encoding("ASCII-8BIT")
end
normalize_trace_log(trace_log) click to toggle source
# File lib/grntest/test-runner.rb, line 875
def normalize_trace_log(trace_log)
  if trace_log["logs"]
    elapsed_time_index = trace_log["columns"].index do |column|
      column["name"] == "elapsed_time"
    end
    normalized_elapsed_time = 0
    trace_log["logs"].each do |log|
      log[elapsed_time_index] = normalized_elapsed_time
      normalized_elapsed_time += 1
    end
  end
  trace_log
end
open_pipe() { |input_read, input_write, output_read, output_write| ... } click to toggle source
# File lib/grntest/test-runner.rb, line 267
def open_pipe
  IO.pipe("ASCII-8BIT") do |input_read, input_write|
    IO.pipe("ASCII-8BIT") do |output_read, output_write|
      yield(input_read, input_write, output_read, output_write)
    end
  end
end
output_actual_file(actual_result) click to toggle source
# File lib/grntest/test-runner.rb, line 970
def output_actual_file(actual_result)
  output_actual_result(actual_result, "actual")
end
output_actual_result(actual_result, suffix) click to toggle source
# File lib/grntest/test-runner.rb, line 974
def output_actual_result(actual_result, suffix)
  result_path = related_file_path(suffix)
  return if result_path.nil?
  result_path.open("w:ascii-8bit") do |result_file|
    result_file.print(actual_result)
  end
end
output_reject_file(actual_result) click to toggle source
# File lib/grntest/test-runner.rb, line 966
def output_reject_file(actual_result)
  output_actual_result(actual_result, "reject")
end
read_expected_result() click to toggle source
# File lib/grntest/test-runner.rb, line 949
def read_expected_result
  return nil unless have_extension?
  result_path = related_file_path("expected")
  return nil if result_path.nil?
  return nil unless result_path.exist?
  result_path.open("r:ascii-8bit") do |result_file|
    result_file.read
  end
end
remove_reject_file() click to toggle source
# File lib/grntest/test-runner.rb, line 959
def remove_reject_file
  return unless have_extension?
  reject_path = related_file_path("reject")
  return if reject_path.nil?
  FileUtils.rm_rf(reject_path.to_s, :secure => true)
end
run_groonga(context, &block) click to toggle source
# File lib/grntest/test-runner.rb, line 206
def run_groonga(context, &block)
  unless @tester.database_path
    create_empty_database(context.db_path.to_s)
  end

  catch do |tag|
    context.abort_tag = tag
    case @tester.interface
    when "stdio"
      run_groonga_stdio(context, &block)
    when "http"
      run_groonga_http(context, &block)
    end
  end
end
run_groonga_http(context) { |executor| ... } click to toggle source
# File lib/grntest/test-runner.rb, line 413
def run_groonga_http(context)
  host = "127.0.0.1"
  port = decide_groonga_server_port(host)
  pid_file_path = context.temporary_directory_path + "groonga.pid"

  env = extract_custom_env(context)
  spawn_options = {}
  command_line = groonga_http_command(host, port, pid_file_path, context,
                                      spawn_options)
  pid = nil
  begin
    pid = Process.spawn(env, *command_line, spawn_options)
    executor = nil
    begin
      executor = Executors::HTTPExecutor.new(host, port, context)
      begin
        executor.ensure_groonga_ready
      rescue
        if Process.waitpid(pid, Process::WNOHANG)
          pid = nil
          raise
        end
        raise unless @tester.gdb
        retry
      end
      yield(executor)
    ensure
      if pid
        if executor.shutdown(pid)
          pid = nil
        else
          wait_groonga_http_shutdown(pid_file_path)
          pid = nil if wait_pid(pid)
        end
      end
    end
  ensure
    if pid
      begin
        Process.kill(:TERM, pid)
      rescue SystemCallError => error
        $stderr.puts("TERM -> #{pid}: #{error.class}: #{error}")
      else
        wait_groonga_http_shutdown(pid_file_path)
        ensure_process_finished(pid)
      end
    end
  end
end
run_groonga_stdio(context) { |executor| ... } click to toggle source
# File lib/grntest/test-runner.rb, line 222
def run_groonga_stdio(context)
  pid = nil
  begin
    open_pipe do |input_read, input_write, output_read, output_write|
      groonga_input = input_write
      groonga_output = output_read

      env = extract_custom_env(context)
      spawn_options = {}
      command_line = groonga_command_line(context, spawn_options)
      if Platform.windows?
        spawn_options[:in] = input_read
        spawn_options[:out] = output_write
      else
        input_fd = input_read.to_i
        output_fd = output_write.to_i
        spawn_options[input_fd] = input_fd
        spawn_options[output_fd] = output_fd
        command_line += [
          "--input-fd", input_fd.to_s,
          "--output-fd", output_fd.to_s,
        ]
      end
      command_line += [
        context.relative_db_path.to_s,
      ]
      pid = Process.spawn(env, *command_line, spawn_options)
      executor = Executors::StandardIOExecutor.new(groonga_input,
                                                   groonga_output,
                                                   context)
      executor.ensure_groonga_ready
      begin
        yield(executor)
      ensure
        pid = nil if executor.shutdown(pid)
      end
    end
  ensure
    if pid
      pid = nil if wait_pid(pid)
      ensure_process_finished(pid)
    end
  end
end
test_script_path() click to toggle source
# File lib/grntest/test-runner.rb, line 919
def test_script_path
  @worker.test_script_path
end
wait_groonga_http_shutdown(pid_file_path) click to toggle source
# File lib/grntest/test-runner.rb, line 500
def wait_groonga_http_shutdown(pid_file_path)
  total_sleep_time = 0
  sleep_time = 0.1
  while pid_file_path.exist?
    sleep(sleep_time)
    total_sleep_time += sleep_time
    break if total_sleep_time > @tester.shutdown_wait_timeout
  end
end
wait_pid(pid) click to toggle source
# File lib/grntest/test-runner.rb, line 489
def wait_pid(pid)
  total_sleep_time = 0
  sleep_time = 0.1
  loop do
    return true if Process.waitpid(pid, Process::WNOHANG)
    sleep(sleep_time)
    total_sleep_time += sleep_time
    return false if total_sleep_time > @tester.shutdown_wait_timeout
  end
end