summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiroshi SHIBATA <hsbt@ruby-lang.org>2021-05-17 15:28:47 +0900
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2021-05-17 15:28:47 +0900
commite36da7ba186ed825a9fd1bfd993c504e95e021e3 (patch)
tree8984fcdb613efbf84b57532a2eb3d0be8d1befe4
parent31a757a4426f1ac8c479313e01542940386fc2fe (diff)
Revert an accidentally commits at df86a13cc150f573b1078779667a1573747d3dbd
-rw-r--r--test/lib/core_assertions.rb764
-rw-r--r--test/lib/envutil.rb367
-rw-r--r--test/lib/find_executable.rb22
-rw-r--r--test/lib/helper.rb4
4 files changed, 0 insertions, 1157 deletions
diff --git a/test/lib/core_assertions.rb b/test/lib/core_assertions.rb
deleted file mode 100644
index ff78c2bdfa..0000000000
--- a/test/lib/core_assertions.rb
+++ /dev/null
@@ -1,764 +0,0 @@
-# frozen_string_literal: true
-
-module Test
- module Unit
- module Assertions
- def _assertions= n # :nodoc:
- @_assertions = n
- end
-
- def _assertions # :nodoc:
- @_assertions ||= 0
- end
-
- ##
- # Returns a proc that will output +msg+ along with the default message.
-
- def message msg = nil, ending = nil, &default
- proc {
- msg = msg.call.chomp(".") if Proc === msg
- custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty?
- "#{custom_message}#{default.call}#{ending || "."}"
- }
- end
- end
-
- module CoreAssertions
- if defined?(MiniTest)
- require_relative '../../envutil'
- # for ruby core testing
- include MiniTest::Assertions
-
- # Compatibility hack for assert_raise
- Test::Unit::AssertionFailedError = MiniTest::Assertion
- else
- module MiniTest
- class Assertion < Exception; end
- class Skip < Assertion; end
- end
-
- require 'pp'
- require_relative 'envutil'
- include Test::Unit::Assertions
- end
-
- def mu_pp(obj) #:nodoc:
- obj.pretty_inspect.chomp
- end
-
- def assert_file
- AssertFile
- end
-
- FailDesc = proc do |status, message = "", out = ""|
- now = Time.now
- proc do
- EnvUtil.failure_description(status, now, message, out)
- end
- end
-
- def assert_in_out_err(args, test_stdin = "", test_stdout = [], test_stderr = [], message = nil,
- success: nil, **opt)
- args = Array(args).dup
- args.insert((Hash === args[0] ? 1 : 0), '--disable=gems')
- stdout, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, true, true, **opt)
- desc = FailDesc[status, message, stderr]
- if block_given?
- raise "test_stdout ignored, use block only or without block" if test_stdout != []
- raise "test_stderr ignored, use block only or without block" if test_stderr != []
- yield(stdout.lines.map {|l| l.chomp }, stderr.lines.map {|l| l.chomp }, status)
- else
- all_assertions(desc) do |a|
- [["stdout", test_stdout, stdout], ["stderr", test_stderr, stderr]].each do |key, exp, act|
- a.for(key) do
- if exp.is_a?(Regexp)
- assert_match(exp, act)
- elsif exp.all? {|e| String === e}
- assert_equal(exp, act.lines.map {|l| l.chomp })
- else
- assert_pattern_list(exp, act)
- end
- end
- end
- unless success.nil?
- a.for("success?") do
- if success
- assert_predicate(status, :success?)
- else
- assert_not_predicate(status, :success?)
- end
- end
- end
- end
- status
- end
- end
-
- if defined?(RubyVM::InstructionSequence)
- def syntax_check(code, fname, line)
- code = code.dup.force_encoding(Encoding::UTF_8)
- RubyVM::InstructionSequence.compile(code, fname, fname, line)
- :ok
- ensure
- raise if SyntaxError === $!
- end
- else
- def syntax_check(code, fname, line)
- code = code.b
- code.sub!(/\A(?:\xef\xbb\xbf)?(\s*\#.*$)*(\n)?/n) {
- "#$&#{"\n" if $1 && !$2}BEGIN{throw tag, :ok}\n"
- }
- code = code.force_encoding(Encoding::UTF_8)
- catch {|tag| eval(code, binding, fname, line - 1)}
- end
- end
-
- def assert_no_memory_leak(args, prepare, code, message=nil, limit: 2.0, rss: false, **opt)
- # TODO: consider choosing some appropriate limit for MJIT and stop skipping this once it does not randomly fail
- pend 'assert_no_memory_leak may consider MJIT memory usage as leak' if defined?(RubyVM::JIT) && RubyVM::JIT.enabled?
-
- require_relative '../../memory_status'
- raise MiniTest::Skip, "unsupported platform" unless defined?(Memory::Status)
-
- token = "\e[7;1m#{$$.to_s}:#{Time.now.strftime('%s.%L')}:#{rand(0x10000).to_s(16)}:\e[m"
- token_dump = token.dump
- token_re = Regexp.quote(token)
- envs = args.shift if Array === args and Hash === args.first
- args = [
- "--disable=gems",
- "-r", File.expand_path("../../../memory_status", __FILE__),
- *args,
- "-v", "-",
- ]
- if defined? Memory::NO_MEMORY_LEAK_ENVS then
- envs ||= {}
- newenvs = envs.merge(Memory::NO_MEMORY_LEAK_ENVS) { |_, _, _| break }
- envs = newenvs if newenvs
- end
- args.unshift(envs) if envs
- cmd = [
- 'END {STDERR.puts '"#{token_dump}"'"FINAL=#{Memory::Status.new}"}',
- prepare,
- 'STDERR.puts('"#{token_dump}"'"START=#{$initial_status = Memory::Status.new}")',
- '$initial_size = $initial_status.size',
- code,
- 'GC.start',
- ].join("\n")
- _, err, status = EnvUtil.invoke_ruby(args, cmd, true, true, **opt)
- before = err.sub!(/^#{token_re}START=(\{.*\})\n/, '') && Memory::Status.parse($1)
- after = err.sub!(/^#{token_re}FINAL=(\{.*\})\n/, '') && Memory::Status.parse($1)
- assert(status.success?, FailDesc[status, message, err])
- ([:size, (rss && :rss)] & after.members).each do |n|
- b = before[n]
- a = after[n]
- next unless a > 0 and b > 0
- assert_operator(a.fdiv(b), :<, limit, message(message) {"#{n}: #{b} => #{a}"})
- end
- rescue LoadError
- pend
- end
-
- # :call-seq:
- # assert_nothing_raised( *args, &block )
- #
- #If any exceptions are given as arguments, the assertion will
- #fail if one of those exceptions are raised. Otherwise, the test fails
- #if any exceptions are raised.
- #
- #The final argument may be a failure message.
- #
- # assert_nothing_raised RuntimeError do
- # raise Exception #Assertion passes, Exception is not a RuntimeError
- # end
- #
- # assert_nothing_raised do
- # raise Exception #Assertion fails
- # end
- def assert_nothing_raised(*args)
- self._assertions += 1
- if Module === args.last
- msg = nil
- else
- msg = args.pop
- end
- begin
- line = __LINE__; yield
- rescue MiniTest::Skip
- raise
- rescue Exception => e
- bt = e.backtrace
- as = e.instance_of?(MiniTest::Assertion)
- if as
- ans = /\A#{Regexp.quote(__FILE__)}:#{line}:in /o
- bt.reject! {|ln| ans =~ ln}
- end
- if ((args.empty? && !as) ||
- args.any? {|a| a.instance_of?(Module) ? e.is_a?(a) : e.class == a })
- msg = message(msg) {
- "Exception raised:\n<#{mu_pp(e)}>\n" +
- "Backtrace:\n" +
- e.backtrace.map{|frame| " #{frame}"}.join("\n")
- }
- raise MiniTest::Assertion, msg.call, bt
- else
- raise
- end
- end
- end
-
- def prepare_syntax_check(code, fname = nil, mesg = nil, verbose: nil)
- fname ||= caller_locations(2, 1)[0]
- mesg ||= fname.to_s
- verbose, $VERBOSE = $VERBOSE, verbose
- case
- when Array === fname
- fname, line = *fname
- when defined?(fname.path) && defined?(fname.lineno)
- fname, line = fname.path, fname.lineno
- else
- line = 1
- end
- yield(code, fname, line, message(mesg) {
- if code.end_with?("\n")
- "```\n#{code}```\n"
- else
- "```\n#{code}\n```\n""no-newline"
- end
- })
- ensure
- $VERBOSE = verbose
- end
-
- def assert_valid_syntax(code, *args, **opt)
- prepare_syntax_check(code, *args, **opt) do |src, fname, line, mesg|
- yield if defined?(yield)
- assert_nothing_raised(SyntaxError, mesg) do
- assert_equal(:ok, syntax_check(src, fname, line), mesg)
- end
- end
- end
-
- def assert_normal_exit(testsrc, message = '', child_env: nil, **opt)
- assert_valid_syntax(testsrc, caller_locations(1, 1)[0])
- if child_env
- child_env = [child_env]
- else
- child_env = []
- end
- out, _, status = EnvUtil.invoke_ruby(child_env + %W'-W0', testsrc, true, :merge_to_stdout, **opt)
- assert !status.signaled?, FailDesc[status, message, out]
- end
-
- def assert_ruby_status(args, test_stdin="", message=nil, **opt)
- out, _, status = EnvUtil.invoke_ruby(args, test_stdin, true, :merge_to_stdout, **opt)
- desc = FailDesc[status, message, out]
- assert(!status.signaled?, desc)
- message ||= "ruby exit status is not success:"
- assert(status.success?, desc)
- end
-
- ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV TERM")
-
- def separated_runner(out = nil)
- include(*Test::Unit::TestCase.ancestors.select {|c| !c.is_a?(Class) })
- out = out ? IO.new(out, 'w') : STDOUT
- at_exit {
- out.puts [Marshal.dump($!)].pack('m'), "assertions=\#{self._assertions}"
- }
- Test::Unit::Runner.class_variable_set(:@@stop_auto_run, true) if defined?(Test::Unit::Runner)
- end
-
- def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **opt)
- unless file and line
- loc, = caller_locations(1,1)
- file ||= loc.path
- line ||= loc.lineno
- end
- capture_stdout = true
- unless /mswin|mingw/ =~ RUBY_PLATFORM
- capture_stdout = false
- opt[:out] = MiniTest::Unit.output if defined?(MiniTest::Unit)
- res_p, res_c = IO.pipe
- opt[:ios] = [res_c]
- end
- src = <<eom
-# -*- coding: #{line += __LINE__; src.encoding}; -*-
-BEGIN {
- require "test/unit";include Test::Unit::Assertions;require #{(__dir__ + "/core_assertions").dump};include Test::Unit::CoreAssertions
- separated_runner #{res_c&.fileno}
-}
-#{line -= __LINE__; src}
-eom
- args = args.dup
- args.insert((Hash === args.first ? 1 : 0), "-w", "--disable=gems", *$:.map {|l| "-I#{l}"})
- stdout, stderr, status = EnvUtil.invoke_ruby(args, src, capture_stdout, true, **opt)
- ensure
- if res_c
- res_c.close
- res = res_p.read
- res_p.close
- else
- res = stdout
- end
- raise if $!
- abort = status.coredump? || (status.signaled? && ABORT_SIGNALS.include?(status.termsig))
- assert(!abort, FailDesc[status, nil, stderr])
- self._assertions += res[/^assertions=(\d+)/, 1].to_i
- begin
- res = Marshal.load(res.unpack1("m"))
- rescue => marshal_error
- ignore_stderr = nil
- res = nil
- end
- if res and !(SystemExit === res)
- if bt = res.backtrace
- bt.each do |l|
- l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"}
- end
- bt.concat(caller)
- else
- res.set_backtrace(caller)
- end
- raise res
- end
-
- # really is it succeed?
- unless ignore_stderr
- # the body of assert_separately must not output anything to detect error
- assert(stderr.empty?, FailDesc[status, "assert_separately failed with error message", stderr])
- end
- assert(status.success?, FailDesc[status, "assert_separately failed", stderr])
- raise marshal_error if marshal_error
- end
-
- # Run Ractor-related test without influencing the main test suite
- def assert_ractor(src, args: [], require: nil, require_relative: nil, file: nil, line: nil, ignore_stderr: nil, **opt)
- return unless defined?(Ractor)
-
- require = "require #{require.inspect}" if require
- if require_relative
- dir = File.dirname(caller_locations[0,1][0].absolute_path)
- full_path = File.expand_path(require_relative, dir)
- require = "#{require}; require #{full_path.inspect}"
- end
-
- assert_separately(args, file, line, <<~RUBY, ignore_stderr: ignore_stderr, **opt)
- #{require}
- previous_verbose = $VERBOSE
- $VERBOSE = nil
- Ractor.new {} # trigger initial warning
- $VERBOSE = previous_verbose
- #{src}
- RUBY
- end
-
- # :call-seq:
- # assert_throw( tag, failure_message = nil, &block )
- #
- #Fails unless the given block throws +tag+, returns the caught
- #value otherwise.
- #
- #An optional failure message may be provided as the final argument.
- #
- # tag = Object.new
- # assert_throw(tag, "#{tag} was not thrown!") do
- # throw tag
- # end
- def assert_throw(tag, msg = nil)
- ret = catch(tag) do
- begin
- yield(tag)
- rescue UncaughtThrowError => e
- thrown = e.tag
- end
- msg = message(msg) {
- "Expected #{mu_pp(tag)} to have been thrown"\
- "#{%Q[, not #{thrown}] if thrown}"
- }
- assert(false, msg)
- end
- assert(true)
- ret
- end
-
- # :call-seq:
- # assert_raise( *args, &block )
- #
- #Tests if the given block raises an exception. Acceptable exception
- #types may be given as optional arguments. If the last argument is a
- #String, it will be used as the error message.
- #
- # assert_raise do #Fails, no Exceptions are raised
- # end
- #
- # assert_raise NameError do
- # puts x #Raises NameError, so assertion succeeds
- # end
- def assert_raise(*exp, &b)
- case exp.last
- when String, Proc
- msg = exp.pop
- end
-
- begin
- yield
- rescue MiniTest::Skip => e
- return e if exp.include? MiniTest::Skip
- raise e
- rescue Exception => e
- expected = exp.any? { |ex|
- if ex.instance_of? Module then
- e.kind_of? ex
- else
- e.instance_of? ex
- end
- }
-
- assert expected, proc {
- flunk(message(msg) {"#{mu_pp(exp)} exception expected, not #{mu_pp(e)}"})
- }
-
- return e
- ensure
- unless e
- exp = exp.first if exp.size == 1
-
- flunk(message(msg) {"#{mu_pp(exp)} expected but nothing was raised"})
- end
- end
- end
-
- # :call-seq:
- # assert_raise_with_message(exception, expected, msg = nil, &block)
- #
- #Tests if the given block raises an exception with the expected
- #message.
- #
- # assert_raise_with_message(RuntimeError, "foo") do
- # nil #Fails, no Exceptions are raised
- # end
- #
- # assert_raise_with_message(RuntimeError, "foo") do
- # raise ArgumentError, "foo" #Fails, different Exception is raised
- # end
- #
- # assert_raise_with_message(RuntimeError, "foo") do
- # raise "bar" #Fails, RuntimeError is raised but the message differs
- # end
- #
- # assert_raise_with_message(RuntimeError, "foo") do
- # raise "foo" #Raises RuntimeError with the message, so assertion succeeds
- # end
- def assert_raise_with_message(exception, expected, msg = nil, &block)
- case expected
- when String
- assert = :assert_equal
- when Regexp
- assert = :assert_match
- else
- raise TypeError, "Expected #{expected.inspect} to be a kind of String or Regexp, not #{expected.class}"
- end
-
- ex = m = nil
- EnvUtil.with_default_internal(expected.encoding) do
- ex = assert_raise(exception, msg || proc {"Exception(#{exception}) with message matches to #{expected.inspect}"}) do
- yield
- end
- m = ex.message
- end
- msg = message(msg, "") {"Expected Exception(#{exception}) was raised, but the message doesn't match"}
-
- if assert == :assert_equal
- assert_equal(expected, m, msg)
- else
- msg = message(msg) { "Expected #{mu_pp expected} to match #{mu_pp m}" }
- assert expected =~ m, msg
- block.binding.eval("proc{|_|$~=_}").call($~)
- end
- ex
- end
-
- MINI_DIR = File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), "minitest") #:nodoc:
-
- # :call-seq:
- # assert(test, [failure_message])
- #
- #Tests if +test+ is true.
- #
- #+msg+ may be a String or a Proc. If +msg+ is a String, it will be used
- #as the failure message. Otherwise, the result of calling +msg+ will be
- #used as the message if the assertion fails.
- #
- #If no +msg+ is given, a default message will be used.
- #
- # assert(false, "This was expected to be true")
- def assert(test, *msgs)
- case msg = msgs.first
- when String, Proc
- when nil
- msgs.shift
- else
- bt = caller.reject { |s| s.start_with?(MINI_DIR) }
- raise ArgumentError, "assertion message must be String or Proc, but #{msg.class} was given.", bt
- end unless msgs.empty?
- super
- end
-
- # :call-seq:
- # assert_respond_to( object, method, failure_message = nil )
- #
- #Tests if the given Object responds to +method+.
- #
- #An optional failure message may be provided as the final argument.
- #
- # assert_respond_to("hello", :reverse) #Succeeds
- # assert_respond_to("hello", :does_not_exist) #Fails
- def assert_respond_to(obj, (meth, *priv), msg = nil)
- unless priv.empty?
- msg = message(msg) {
- "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}#{" privately" if priv[0]}"
- }
- return assert obj.respond_to?(meth, *priv), msg
- end
- #get rid of overcounting
- if caller_locations(1, 1)[0].path.start_with?(MINI_DIR)
- return if obj.respond_to?(meth)
- end
- super(obj, meth, msg)
- end
-
- # :call-seq:
- # assert_not_respond_to( object, method, failure_message = nil )
- #
- #Tests if the given Object does not respond to +method+.
- #
- #An optional failure message may be provided as the final argument.
- #
- # assert_not_respond_to("hello", :reverse) #Fails
- # assert_not_respond_to("hello", :does_not_exist) #Succeeds
- def assert_not_respond_to(obj, (meth, *priv), msg = nil)
- unless priv.empty?
- msg = message(msg) {
- "Expected #{mu_pp(obj)} (#{obj.class}) to not respond to ##{meth}#{" privately" if priv[0]}"
- }
- return assert !obj.respond_to?(meth, *priv), msg
- end
- #get rid of overcounting
- if caller_locations(1, 1)[0].path.start_with?(MINI_DIR)
- return unless obj.respond_to?(meth)
- end
- refute_respond_to(obj, meth, msg)
- end
-
- # pattern_list is an array which contains regexp and :*.
- # :* means any sequence.
- #
- # pattern_list is anchored.
- # Use [:*, regexp, :*] for non-anchored match.
- def assert_pattern_list(pattern_list, actual, message=nil)
- rest = actual
- anchored = true
- pattern_list.each_with_index {|pattern, i|
- if pattern == :*
- anchored = false
- else
- if anchored
- match = /\A#{pattern}/.match(rest)
- else
- match = pattern.match(rest)
- end
- unless match
- msg = message(msg) {
- expect_msg = "Expected #{mu_pp pattern}\n"
- if /\n[^\n]/ =~ rest
- actual_mesg = +"to match\n"
- rest.scan(/.*\n+/) {
- actual_mesg << ' ' << $&.inspect << "+\n"
- }
- actual_mesg.sub!(/\+\n\z/, '')
- else
- actual_mesg = "to match " + mu_pp(rest)
- end
- actual_mesg << "\nafter #{i} patterns with #{actual.length - rest.length} characters"
- expect_msg + actual_mesg
- }
- assert false, msg
- end
- rest = match.post_match
- anchored = true
- end
- }
- if anchored
- assert_equal("", rest)
- end
- end
-
- def assert_warning(pat, msg = nil)
- result = nil
- stderr = EnvUtil.with_default_internal(pat.encoding) {
- EnvUtil.verbose_warning {
- result = yield
- }
- }
- msg = message(msg) {diff pat, stderr}
- assert(pat === stderr, msg)
- result
- end
-
- def assert_warn(*args)
- assert_warning(*args) {$VERBOSE = false; yield}
- end
-
- def assert_deprecated_warning(mesg = /deprecated/)
- assert_warning(mesg) do
- Warning[:deprecated] = true
- yield
- end
- end
-
- def assert_deprecated_warn(mesg = /deprecated/)
- assert_warn(mesg) do
- Warning[:deprecated] = true
- yield
- end
- end
-
- class << (AssertFile = Struct.new(:failure_message).new)
- include CoreAssertions
- def assert_file_predicate(predicate, *args)
- if /\Anot_/ =~ predicate
- predicate = $'
- neg = " not"
- end
- result = File.__send__(predicate, *args)
- result = !result if neg
- mesg = "Expected file ".dup << args.shift.inspect
- mesg << "#{neg} to be #{predicate}"
- mesg << mu_pp(args).sub(/\A\[(.*)\]\z/m, '(\1)') unless args.empty?
- mesg << " #{failure_message}" if failure_message
- assert(result, mesg)
- end
- alias method_missing assert_file_predicate
-
- def for(message)
- clone.tap {|a| a.failure_message = message}
- end
- end
-
- class AllFailures
- attr_reader :failures
-
- def initialize
- @count = 0
- @failures = {}
- end
-
- def for(key)
- @count += 1
- yield
- rescue Exception => e
- @failures[key] = [@count, e]
- end
-
- def foreach(*keys)
- keys.each do |key|
- @count += 1
- begin
- yield key
- rescue Exception => e
- @failures[key] = [@count, e]
- end
- end
- end
-
- def message
- i = 0
- total = @count.to_s
- fmt = "%#{total.size}d"
- @failures.map {|k, (n, v)|
- v = v.message
- "\n#{i+=1}. [#{fmt%n}/#{total}] Assertion for #{k.inspect}\n#{v.b.gsub(/^/, ' | ').force_encoding(v.encoding)}"
- }.join("\n")
- end
-
- def pass?
- @failures.empty?
- end
- end
-
- # threads should respond to shift method.
- # Array can be used.
- def assert_join_threads(threads, message = nil)
- errs = []
- values = []
- while th = threads.shift
- begin
- values << th.value
- rescue Exception
- errs << [th, $!]
- th = nil
- end
- end
- values
- ensure
- if th&.alive?
- th.raise(Timeout::Error.new)
- th.join rescue errs << [th, $!]
- end
- if !errs.empty?
- msg = "exceptions on #{errs.length} threads:\n" +
- errs.map {|t, err|
- "#{t.inspect}:\n" +
- RUBY_VERSION >= "2.5.0" ? err.full_message(highlight: false, order: :top) : err.message
- }.join("\n---\n")
- if message
- msg = "#{message}\n#{msg}"
- end
- raise MiniTest::Assertion, msg
- end
- end
-
- def assert_all_assertions(msg = nil)
- all = AllFailures.new
- yield all
- ensure
- assert(all.pass?, message(msg) {all.message.chomp(".")})
- end
- alias all_assertions assert_all_assertions
-
- def message(msg = nil, *args, &default) # :nodoc:
- if Proc === msg
- super(nil, *args) do
- ary = [msg.call, (default.call if default)].compact.reject(&:empty?)
- if 1 < ary.length
- ary[0...-1] = ary[0...-1].map {|str| str.sub(/(?<!\.)\z/, '.') }
- end
- begin
- ary.join("\n")
- rescue Encoding::CompatibilityError
- ary.map(&:b).join("\n")
- end
- end
- else
- super
- end
- end
-
- def diff(exp, act)
- require 'pp'
- q = PP.new(+"")
- q.guard_inspect_key do
- q.group(2, "expected: ") do
- q.pp exp
- end
- q.text q.newline
- q.group(2, "actual: ") do
- q.pp act
- end
- q.flush
- end
- q.output
- end
- end
- end
-end
diff --git a/test/lib/envutil.rb b/test/lib/envutil.rb
deleted file mode 100644
index 0391b90c1c..0000000000
--- a/test/lib/envutil.rb
+++ /dev/null
@@ -1,367 +0,0 @@
-# -*- coding: us-ascii -*-
-# frozen_string_literal: true
-require "open3"
-require "timeout"
-require_relative "find_executable"
-begin
- require 'rbconfig'
-rescue LoadError
-end
-begin
- require "rbconfig/sizeof"
-rescue LoadError
-end
-
-module EnvUtil
- def rubybin
- if ruby = ENV["RUBY"]
- return ruby
- end
- ruby = "ruby"
- exeext = RbConfig::CONFIG["EXEEXT"]
- rubyexe = (ruby + exeext if exeext and !exeext.empty?)
- 3.times do
- if File.exist? ruby and File.executable? ruby and !File.directory? ruby
- return File.expand_path(ruby)
- end
- if rubyexe and File.exist? rubyexe and File.executable? rubyexe
- return File.expand_path(rubyexe)
- end
- ruby = File.join("..", ruby)
- end
- if defined?(RbConfig.ruby)
- RbConfig.ruby
- else
- "ruby"
- end
- end
- module_function :rubybin
-
- LANG_ENVS = %w"LANG LC_ALL LC_CTYPE"
-
- DEFAULT_SIGNALS = Signal.list
- DEFAULT_SIGNALS.delete("TERM") if /mswin|mingw/ =~ RUBY_PLATFORM
-
- RUBYLIB = ENV["RUBYLIB"]
-
- class << self
- attr_accessor :timeout_scale
- attr_reader :original_internal_encoding, :original_external_encoding,
- :original_verbose, :original_warning
-
- def capture_global_values
- @original_internal_encoding = Encoding.default_internal
- @original_external_encoding = Encoding.default_external
- @original_verbose = $VERBOSE
- @original_warning = defined?(Warning.[]) ? %i[deprecated experimental].to_h {|i| [i, Warning[i]]} : nil
- 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
-
- lldb = true if /darwin/ =~ RUBY_PLATFORM
-
- while signal = signals.shift
-
- if lldb and [:ABRT, :KILL].include?(signal)
- lldb = false
- # sudo -n: --non-interactive
- # lldb -p: attach
- # -o: run command
- system(*%W[sudo -n lldb -p #{pid} --batch -o bt\ all -o call\ rb_vmdebug_stack_dump_all_threads() -o quit])
- true
- end
-
- 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
- else
- break
- 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, ios: nil,
- signal: :TERM,
- rubybin: EnvUtil.rubybin, precommand: nil,
- **opt)
- 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
- opt[:in] = in_c
- opt[:out] = out_c if capture_stdout
- opt[:err] = capture_stderr == :merge_to_stdout ? out_c : err_c if capture_stderr
- if encoding
- out_p.set_encoding(encoding) if out_p
- err_p.set_encoding(encoding) if err_p
- end
- ios.each {|i, o = i|opt[i] = o} if ios
-
- c = "C"
- child_env = {}
- LANG_ENVS.each {|lc| child_env[lc] = c}
- 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
- child_env['ASAN_OPTIONS'] = ENV['ASAN_OPTIONS'] if ENV['ASAN_OPTIONS']
- args = [args] if args.kind_of?(String)
- pid = spawn(child_env, *precommand, rubybin, *args, opt)
- in_c.close
- out_c&.close
- out_c = nil
- err_c&.close
- err_c = nil
- if block_given?
- return yield in_p, out_p, err_p, pid
- else
- th_stdout = Thread.new { out_p.read } if capture_stdout
- th_stderr = Thread.new { err_p.read } if capture_stderr && capture_stderr != :merge_to_stdout
- in_p.write stdin_data.to_str unless stdin_data.empty?
- in_p.close
- if (!th_stdout || th_stdout.join(timeout)) && (!th_stderr || th_stderr.join(timeout))
- timeout_error = nil
- else
- 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
- out_p.close if capture_stdout
- err_p.close if capture_stderr && capture_stderr != :merge_to_stdout
- status ||= Process.wait2(pid)[1]
- stdout = stdout_filter.call(stdout) if stdout_filter
- stderr = stderr_filter.call(stderr) if stderr_filter
- if timeout_error
- bt = caller_locations
- 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
- end
- ensure
- [th_stdout, th_stderr].each do |th|
- th.kill if th
- end
- [in_c, in_p, out_c, out_p, err_c, err_p].each do |io|
- io&.close
- end
- [th_stdout, th_stderr].each do |th|
- th.join if th
- end
- end
- module_function :invoke_ruby
-
- def verbose_warning
- class << (stderr = "".dup)
- alias write concat
- def flush; end
- end
- stderr, $stderr = $stderr, stderr
- $VERBOSE = true
- yield stderr
- return $stderr
- ensure
- stderr, $stderr = $stderr, stderr
- $VERBOSE = EnvUtil.original_verbose
- EnvUtil.original_warning&.each {|i, v| Warning[i] = v}
- end
- module_function :verbose_warning
-
- def default_warning
- $VERBOSE = false
- yield
- ensure
- $VERBOSE = EnvUtil.original_verbose
- end
- module_function :default_warning
-
- def suppress_warning
- $VERBOSE = nil
- yield
- ensure
- $VERBOSE = EnvUtil.original_verbose
- end
- module_function :suppress_warning
-
- def under_gc_stress(stress = true)
- stress, GC.stress = GC.stress, stress
- yield
- ensure
- GC.stress = stress
- end
- module_function :under_gc_stress
-
- def with_default_external(enc)
- suppress_warning { Encoding.default_external = enc }
- yield
- ensure
- suppress_warning { Encoding.default_external = EnvUtil.original_external_encoding }
- end
- module_function :with_default_external
-
- def with_default_internal(enc)
- suppress_warning { Encoding.default_internal = enc }
- yield
- ensure
- suppress_warning { Encoding.default_internal = EnvUtil.original_internal_encoding }
- end
- module_function :with_default_internal
-
- def labeled_module(name, &block)
- Module.new do
- singleton_class.class_eval {
- define_method(:to_s) {name}
- alias inspect to_s
- alias name to_s
- }
- class_eval(&block) if block
- end
- end
- module_function :labeled_module
-
- def labeled_class(name, superclass = Object, &block)
- Class.new(superclass) do
- singleton_class.class_eval {
- define_method(:to_s) {name}
- alias inspect to_s
- alias name to_s
- }
- class_eval(&block) if block
- end
- end
- module_function :labeled_class
-
- if /darwin/ =~ RUBY_PLATFORM
- DIAGNOSTIC_REPORTS_PATH = File.expand_path("~/Library/Logs/DiagnosticReports")
- DIAGNOSTIC_REPORTS_TIMEFORMAT = '%Y-%m-%d-%H%M%S'
- @ruby_install_name = RbConfig::CONFIG['RUBY_INSTALL_NAME']
-
- def self.diagnostic_reports(signame, pid, now)
- return unless %w[ABRT QUIT SEGV ILL TRAP].include?(signame)
- cmd = File.basename(rubybin)
- cmd = @ruby_install_name if "ruby-runner#{RbConfig::CONFIG["EXEEXT"]}" == cmd
- path = DIAGNOSTIC_REPORTS_PATH
- timeformat = DIAGNOSTIC_REPORTS_TIMEFORMAT
- pat = "#{path}/#{cmd}_#{now.strftime(timeformat)}[-_]*.crash"
- first = true
- 30.times do
- first ? (first = false) : sleep(0.1)
- Dir.glob(pat) do |name|
- log = File.read(name) rescue next
- if /\AProcess:\s+#{cmd} \[#{pid}\]$/ =~ log
- File.unlink(name)
- File.unlink("#{path}/.#{File.basename(name)}.plist") rescue nil
- return log
- end
- end
- end
- nil
- end
- else
- 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)"])
- @gc_stress_to_class = status.success?
- end
- @gc_stress_to_class
- end
-end
-
-if defined?(RbConfig)
- module RbConfig
- @ruby = EnvUtil.rubybin
- class << self
- undef ruby if method_defined?(:ruby)
- attr_reader :ruby
- end
- dir = File.dirname(ruby)
- CONFIG['bindir'] = dir
- end
-end
-
-EnvUtil.capture_global_values
diff --git a/test/lib/find_executable.rb b/test/lib/find_executable.rb
deleted file mode 100644
index 89c6fb8f3b..0000000000
--- a/test/lib/find_executable.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-require "rbconfig"
-
-module EnvUtil
- def find_executable(cmd, *args)
- exts = RbConfig::CONFIG["EXECUTABLE_EXTS"].split | [RbConfig::CONFIG["EXEEXT"]]
- ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
- next if path.empty?
- path = File.join(path, cmd)
- exts.each do |ext|
- cmdline = [path + ext, *args]
- begin
- return cmdline if yield(IO.popen(cmdline, "r", err: [:child, :out], &:read))
- rescue
- next
- end
- end
- end
- nil
- end
- module_function :find_executable
-end
diff --git a/test/lib/helper.rb b/test/lib/helper.rb
deleted file mode 100644
index 909f8f98af..0000000000
--- a/test/lib/helper.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require "test/unit"
-require_relative "core_assertions"
-
-Test::Unit::TestCase.include Test::Unit::CoreAssertions