Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update test/lib #42

Merged
merged 1 commit into from
Oct 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions test/lib/colorize.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen-string-literal: true

class Colorize
def initialize(color = nil, opts = ((_, color = color, nil)[0] if Hash === color))
@colors = @reset = nil
if color or (color == nil && STDOUT.tty?)
if (/\A\e\[.*m\z/ =~ IO.popen("tput smso", "r", :err => IO::NULL, &:read) rescue nil)
@beg = "\e["
colors = (colors = ENV['TEST_COLORS']) ? Hash[colors.scan(/(\w+)=([^:\n]*)/)] : {}
if opts and colors_file = opts[:colors_file]
begin
File.read(colors_file).scan(/(\w+)=([^:\n]*)/) do |n, c|
colors[n] ||= c
end
rescue Errno::ENOENT
end
end
@colors = colors
@reset = "#{@beg}m"
end
end
self
end

DEFAULTS = {
"pass"=>"32", "fail"=>"31;1", "skip"=>"33;1",
"black"=>"30", "red"=>"31", "green"=>"32", "yellow"=>"33",
"blue"=>"34", "magenta"=>"35", "cyan"=>"36", "white"=>"37",
"bold"=>"1", "underline"=>"4", "reverse"=>"7",
}

def decorate(str, name)
if @colors and color = (@colors[name] || DEFAULTS[name])
"#{@beg}#{color}m#{str}#{@reset}"
else
str
end
end

DEFAULTS.each_key do |name|
define_method(name) {|str|
decorate(str, name)
}
end
end

if $0 == __FILE__
colorize = Colorize.new
col = ARGV.shift
ARGV.each {|str| puts colorize.decorate(str, col)}
end
185 changes: 122 additions & 63 deletions test/lib/envutil.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: us-ascii -*-
# frozen_string_literal: false
# frozen_string_literal: true
require "open3"
require "timeout"
require_relative "find_executable"
Expand Down Expand Up @@ -42,20 +42,79 @@ def rubybin
DEFAULT_SIGNALS = Signal.list
DEFAULT_SIGNALS.delete("TERM") if /mswin|mingw/ =~ RUBY_PLATFORM

RUBYLIB = ENV["RUBYLIB"]

class << self
attr_accessor :subprocess_timeout_scale
attr_accessor :timeout_scale
attr_reader :original_internal_encoding, :original_external_encoding,
:original_verbose

def capture_global_values
@original_internal_encoding = Encoding.default_internal
@original_external_encoding = Encoding.default_external
@original_verbose = $VERBOSE
end
end

def apply_timeout_scale(t)
if scale = EnvUtil.timeout_scale
t * scale
else
t
end
end
module_function :apply_timeout_scale

def timeout(sec, klass = nil, message = nil, &blk)
return yield(sec) if sec == nil or sec.zero?
sec = apply_timeout_scale(sec)
Timeout.timeout(sec, klass, message, &blk)
end
module_function :timeout

def terminate(pid, signal = :TERM, pgroup = nil, reprieve = 1)
reprieve = apply_timeout_scale(reprieve) if reprieve

signals = Array(signal).select do |sig|
DEFAULT_SIGNALS[sig.to_s] or
DEFAULT_SIGNALS[Signal.signame(sig)] rescue false
end
signals |= [:ABRT, :KILL]
case pgroup
when 0, true
pgroup = -pid
when nil, false
pgroup = pid
end
while signal = signals.shift
begin
Process.kill signal, pgroup
rescue Errno::EINVAL
next
rescue Errno::ESRCH
break
end
if signals.empty? or !reprieve
Process.wait(pid)
else
begin
Timeout.timeout(reprieve) {Process.wait(pid)}
rescue Timeout::Error
end
end
end
$?
end
module_function :terminate

def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr = false,
encoding: nil, timeout: 10, reprieve: 1, timeout_error: Timeout::Error,
stdout_filter: nil, stderr_filter: nil,
signal: :TERM,
rubybin: EnvUtil.rubybin,
rubybin: EnvUtil.rubybin, precommand: nil,
**opt)
if scale = EnvUtil.subprocess_timeout_scale
timeout *= scale if timeout
reprieve *= scale if reprieve
end
timeout = apply_timeout_scale(timeout)

in_c, in_p = IO.pipe
out_p, out_c = IO.pipe if capture_stdout
err_p, err_c = IO.pipe if capture_stderr && capture_stderr != :merge_to_stdout
Expand All @@ -72,8 +131,11 @@ def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr =
if Array === args and Hash === args.first
child_env.update(args.shift)
end
if RUBYLIB and lib = child_env["RUBYLIB"]
child_env["RUBYLIB"] = [lib, RUBYLIB].join(File::PATH_SEPARATOR)
end
args = [args] if args.kind_of?(String)
pid = spawn(child_env, rubybin, *args, **opt)
pid = spawn(child_env, *precommand, rubybin, *args, **opt)
in_c.close
out_c.close if capture_stdout
err_c.close if capture_stderr && capture_stderr != :merge_to_stdout
Expand All @@ -87,35 +149,8 @@ def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr =
if (!th_stdout || th_stdout.join(timeout)) && (!th_stderr || th_stderr.join(timeout))
timeout_error = nil
else
signals = Array(signal).select do |sig|
DEFAULT_SIGNALS[sig.to_s] or
DEFAULT_SIGNALS[Signal.signame(sig)] rescue false
end
signals |= [:ABRT, :KILL]
case pgroup = opt[:pgroup]
when 0, true
pgroup = -pid
when nil, false
pgroup = pid
end
while signal = signals.shift
begin
Process.kill signal, pgroup
rescue Errno::EINVAL
next
rescue Errno::ESRCH
break
end
if signals.empty? or !reprieve
Process.wait(pid)
else
begin
Timeout.timeout(reprieve) {Process.wait(pid)}
rescue Timeout::Error
end
end
end
status = $?
status = terminate(pid, signal, opt[:pgroup], reprieve)
terminated = Time.now
end
stdout = th_stdout.value if capture_stdout
stderr = th_stderr.value if capture_stderr && capture_stderr != :merge_to_stdout
Expand All @@ -126,8 +161,8 @@ def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr =
stderr = stderr_filter.call(stderr) if stderr_filter
if timeout_error
bt = caller_locations
msg = "execution of #{bt.shift.label} expired"
msg = Test::Unit::Assertions::FailDesc[status, msg, [stdout, stderr].join("\n")].()
msg = "execution of #{bt.shift.label} expired timeout (#{timeout} sec)"
msg = failure_description(status, terminated, msg, [stdout, stderr].join("\n"))
raise timeout_error, msg, bt.map(&:to_s)
end
return stdout, stderr, status
Expand All @@ -151,30 +186,33 @@ class << self
end

def verbose_warning
class << (stderr = "")
alias write <<
class << (stderr = "".dup)
alias write concat
def flush; end
end
stderr, $stderr, verbose, $VERBOSE = $stderr, stderr, $VERBOSE, true
stderr, $stderr = $stderr, stderr
$VERBOSE = true
yield stderr
return $stderr
ensure
stderr, $stderr, $VERBOSE = $stderr, stderr, verbose
stderr, $stderr = $stderr, stderr
$VERBOSE = EnvUtil.original_verbose
end
module_function :verbose_warning

def default_warning
verbose, $VERBOSE = $VERBOSE, false
$VERBOSE = false
yield
ensure
$VERBOSE = verbose
$VERBOSE = EnvUtil.original_verbose
end
module_function :default_warning

def suppress_warning
verbose, $VERBOSE = $VERBOSE, nil
$VERBOSE = nil
yield
ensure
$VERBOSE = verbose
$VERBOSE = EnvUtil.original_verbose
end
module_function :suppress_warning

Expand All @@ -187,26 +225,18 @@ def under_gc_stress(stress = true)
module_function :under_gc_stress

def with_default_external(enc)
verbose, $VERBOSE = $VERBOSE, nil
origenc, Encoding.default_external = Encoding.default_external, enc
$VERBOSE = verbose
suppress_warning { Encoding.default_external = enc }
yield
ensure
verbose, $VERBOSE = $VERBOSE, nil
Encoding.default_external = origenc
$VERBOSE = verbose
suppress_warning { Encoding.default_external = EnvUtil.original_external_encoding }
end
module_function :with_default_external

def with_default_internal(enc)
verbose, $VERBOSE = $VERBOSE, nil
origenc, Encoding.default_internal = Encoding.default_internal, enc
$VERBOSE = verbose
suppress_warning { Encoding.default_internal = enc }
yield
ensure
verbose, $VERBOSE = $VERBOSE, nil
Encoding.default_internal = origenc
$VERBOSE = verbose
suppress_warning { Encoding.default_internal = EnvUtil.original_internal_encoding }
end
module_function :with_default_internal

Expand Down Expand Up @@ -257,6 +287,37 @@ def self.diagnostic_reports(signame, pid, now)
end
end

def self.failure_description(status, now, message = "", out = "")
pid = status.pid
if signo = status.termsig
signame = Signal.signame(signo)
sigdesc = "signal #{signo}"
end
log = diagnostic_reports(signame, pid, now)
if signame
sigdesc = "SIG#{signame} (#{sigdesc})"
end
if status.coredump?
sigdesc = "#{sigdesc} (core dumped)"
end
full_message = ''.dup
message = message.call if Proc === message
if message and !message.empty?
full_message << message << "\n"
end
full_message << "pid #{pid}"
full_message << " exit #{status.exitstatus}" if status.exited?
full_message << " killed by #{sigdesc}" if sigdesc
if out and !out.empty?
full_message << "\n" << out.b.gsub(/^/, '| ')
full_message.sub!(/(?<!\n)\z/, "\n")
end
if log
full_message << "Diagnostic reports:\n" << log.b.gsub(/^/, '| ')
end
full_message
end

def self.gc_stress_to_class?
unless defined?(@gc_stress_to_class)
_, _, status = invoke_ruby(["-e""exit GC.respond_to?(:add_stress_to_class)"])
Expand All @@ -274,10 +335,8 @@ class << self
attr_reader :ruby
end
dir = File.dirname(ruby)
name = File.basename(ruby, CONFIG['EXEEXT'])
CONFIG['bindir'] = dir
CONFIG['ruby_install_name'] = name
CONFIG['RUBY_INSTALL_NAME'] = name
Gem::ConfigMap[:bindir] = dir if defined?(Gem::ConfigMap)
end
end

EnvUtil.capture_global_values
2 changes: 1 addition & 1 deletion test/lib/find_executable.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# frozen_string_literal: false
# frozen_string_literal: true
require "rbconfig"

module EnvUtil
Expand Down
Loading