mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
support concurrent btest execution
* `-j` option for concurrent test with threads * `-jN` uses N threads * `-j` uses nproc/2 threads * Introduce `BT` struct to manage configurations * Introduce `Assertion` to manage all assertions * Remove all toplevel instance variables * Show elapsed seconds at last ``` $ time make btest ... real 0m37.319s user 0m26.221s sys 0m16.534s $ time make btest TESTOPTS=-j ... real 0m11.812s user 0m36.667s sys 0m21.872s ```
This commit is contained in:
parent
2a76440fac
commit
603ab70961
Notes:
git
2022-02-06 03:06:23 +09:00
5 changed files with 488 additions and 333 deletions
|
@ -8,6 +8,8 @@
|
||||||
# Never use Ruby extensions in this file.
|
# Never use Ruby extensions in this file.
|
||||||
# Maintain Ruby 1.8 compatibility for now
|
# Maintain Ruby 1.8 compatibility for now
|
||||||
|
|
||||||
|
$start_time = Time.now
|
||||||
|
|
||||||
begin
|
begin
|
||||||
require 'fileutils'
|
require 'fileutils'
|
||||||
require 'tmpdir'
|
require 'tmpdir'
|
||||||
|
@ -58,24 +60,44 @@ if !Dir.respond_to?(:mktmpdir)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
BT = Struct.new(:ruby,
|
||||||
|
:verbose,
|
||||||
|
:color,
|
||||||
|
:tty,
|
||||||
|
:quiet,
|
||||||
|
:wn,
|
||||||
|
:progress,
|
||||||
|
:progress_bs,
|
||||||
|
:passed,
|
||||||
|
:failed,
|
||||||
|
:reset,
|
||||||
|
:columns,
|
||||||
|
:width,
|
||||||
|
).new
|
||||||
|
|
||||||
|
BT_STATE = Struct.new(:count, :error).new
|
||||||
|
|
||||||
def main
|
def main
|
||||||
@ruby = File.expand_path('miniruby')
|
BT.ruby = File.expand_path('miniruby')
|
||||||
@verbose = false
|
BT.verbose = false
|
||||||
$VERBOSE = false
|
$VERBOSE = false
|
||||||
$stress = false
|
$stress = false
|
||||||
@color = nil
|
BT.color = nil
|
||||||
@tty = nil
|
BT.tty = nil
|
||||||
@quiet = false
|
BT.quiet = false
|
||||||
|
BT.wn = 1
|
||||||
dir = nil
|
dir = nil
|
||||||
quiet = false
|
quiet = false
|
||||||
tests = nil
|
tests = nil
|
||||||
ARGV.delete_if {|arg|
|
ARGV.delete_if {|arg|
|
||||||
case arg
|
case arg
|
||||||
when /\A--ruby=(.*)/
|
when /\A--ruby=(.*)/
|
||||||
@ruby = $1
|
ruby = $1
|
||||||
@ruby.gsub!(/^([^ ]*)/){File.expand_path($1)}
|
ruby.gsub!(/^([^ ]*)/){File.expand_path($1)}
|
||||||
@ruby.gsub!(/(\s+-I\s*)((?!(?:\.\/)*-(?:\s|\z))\S+)/){$1+File.expand_path($2)}
|
ruby.gsub!(/(\s+-I\s*)((?!(?:\.\/)*-(?:\s|\z))\S+)/){$1+File.expand_path($2)}
|
||||||
@ruby.gsub!(/(\s+-r\s*)(\.\.?\/\S+)/){$1+File.expand_path($2)}
|
ruby.gsub!(/(\s+-r\s*)(\.\.?\/\S+)/){$1+File.expand_path($2)}
|
||||||
|
BT.ruby = ruby
|
||||||
true
|
true
|
||||||
when /\A--sets=(.*)/
|
when /\A--sets=(.*)/
|
||||||
tests = Dir.glob("#{File.dirname($0)}/test_{#{$1}}*.rb").sort
|
tests = Dir.glob("#{File.dirname($0)}/test_{#{$1}}*.rb").sort
|
||||||
|
@ -88,18 +110,26 @@ def main
|
||||||
$stress = true
|
$stress = true
|
||||||
when /\A--color(?:=(?:always|(auto)|(never)|(.*)))?\z/
|
when /\A--color(?:=(?:always|(auto)|(never)|(.*)))?\z/
|
||||||
warn "unknown --color argument: #$3" if $3
|
warn "unknown --color argument: #$3" if $3
|
||||||
@color = $1 ? nil : !$2
|
BT.color = color = $1 ? nil : !$2
|
||||||
true
|
true
|
||||||
when /\A--tty(=(?:yes|(no)|(.*)))?\z/
|
when /\A--tty(=(?:yes|(no)|(.*)))?\z/
|
||||||
warn "unknown --tty argument: #$3" if $3
|
warn "unknown --tty argument: #$3" if $3
|
||||||
@tty = !$1 || !$2
|
BT.tty = !$1 || !$2
|
||||||
true
|
true
|
||||||
when /\A(-q|--q(uiet))\z/
|
when /\A(-q|--q(uiet))\z/
|
||||||
quiet = true
|
quiet = true
|
||||||
@quiet = true
|
BT.quiet = true
|
||||||
|
true
|
||||||
|
when /\A-j(\d+)?/
|
||||||
|
wn = $1.to_i
|
||||||
|
if wn <= 0
|
||||||
|
require 'etc'
|
||||||
|
wn = [Etc.nprocessors / 2, 1].max
|
||||||
|
end
|
||||||
|
BT.wn = wn
|
||||||
true
|
true
|
||||||
when /\A(-v|--v(erbose))\z/
|
when /\A(-v|--v(erbose))\z/
|
||||||
@verbose = true
|
BT.verbose = true
|
||||||
when /\A(-h|--h(elp)?)\z/
|
when /\A(-h|--h(elp)?)\z/
|
||||||
puts(<<-End)
|
puts(<<-End)
|
||||||
Usage: #{File.basename($0, '.*')} --ruby=PATH [--sets=NAME,NAME,...]
|
Usage: #{File.basename($0, '.*')} --ruby=PATH [--sets=NAME,NAME,...]
|
||||||
|
@ -128,15 +158,16 @@ End
|
||||||
tests = Dir.glob("#{File.dirname($0)}/test_*.rb").sort if tests.empty?
|
tests = Dir.glob("#{File.dirname($0)}/test_*.rb").sort if tests.empty?
|
||||||
pathes = tests.map {|path| File.expand_path(path) }
|
pathes = tests.map {|path| File.expand_path(path) }
|
||||||
|
|
||||||
@progress = %w[- \\ | /]
|
BT.progress = %w[- \\ | /]
|
||||||
@progress_bs = "\b" * @progress[0].size
|
BT.progress_bs = "\b" * BT.progress[0].size
|
||||||
@tty = $stderr.tty? if @tty.nil?
|
BT.tty = $stderr.tty? if BT.tty.nil?
|
||||||
case @color
|
|
||||||
|
case BT.color
|
||||||
when nil
|
when nil
|
||||||
@color = @tty && /dumb/ !~ ENV["TERM"]
|
BT.color = BT.tty && /dumb/ !~ ENV["TERM"]
|
||||||
end
|
end
|
||||||
@tty &&= !@verbose
|
BT.tty &&= !BT.verbose
|
||||||
if @color
|
if BT.color
|
||||||
# dircolors-like style
|
# dircolors-like style
|
||||||
colors = (colors = ENV['TEST_COLORS']) ? Hash[colors.scan(/(\w+)=([^:\n]*)/)] : {}
|
colors = (colors = ENV['TEST_COLORS']) ? Hash[colors.scan(/(\w+)=([^:\n]*)/)] : {}
|
||||||
begin
|
begin
|
||||||
|
@ -145,14 +176,14 @@ End
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
end
|
end
|
||||||
@passed = "\e[;#{colors["pass"] || "32"}m"
|
BT.passed = "\e[;#{colors["pass"] || "32"}m"
|
||||||
@failed = "\e[;#{colors["fail"] || "31"}m"
|
BT.failed = "\e[;#{colors["fail"] || "31"}m"
|
||||||
@reset = "\e[m"
|
BT.reset = "\e[m"
|
||||||
else
|
else
|
||||||
@passed = @failed = @reset = ""
|
BT.passed = BT.failed = BT.reset = ""
|
||||||
end
|
end
|
||||||
unless quiet
|
unless quiet
|
||||||
puts Time.now
|
puts $start_time
|
||||||
if defined?(RUBY_DESCRIPTION)
|
if defined?(RUBY_DESCRIPTION)
|
||||||
puts "Driver is #{RUBY_DESCRIPTION}"
|
puts "Driver is #{RUBY_DESCRIPTION}"
|
||||||
elsif defined?(RUBY_PATCHLEVEL)
|
elsif defined?(RUBY_PATCHLEVEL)
|
||||||
|
@ -160,289 +191,473 @@ End
|
||||||
else
|
else
|
||||||
puts "Driver is ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
|
puts "Driver is ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
|
||||||
end
|
end
|
||||||
puts "Target is #{`#{@ruby} -v`.chomp}"
|
puts "Target is #{`#{BT.ruby} -v`.chomp}"
|
||||||
puts
|
puts
|
||||||
$stdout.flush
|
$stdout.flush
|
||||||
end
|
end
|
||||||
|
|
||||||
in_temporary_working_directory(dir) {
|
in_temporary_working_directory(dir) do
|
||||||
exec_test pathes
|
exec_test pathes
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def erase(e = true)
|
def erase(e = true)
|
||||||
if e and @columns > 0 and @tty and !@verbose
|
if e and BT.columns > 0 and BT.tty and !BT.verbose
|
||||||
"\e[1K\r"
|
"\e[1K\r"
|
||||||
else
|
else
|
||||||
""
|
""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def exec_test(pathes)
|
def load_test pathes
|
||||||
@count = 0
|
|
||||||
@error = 0
|
|
||||||
@errbuf = []
|
|
||||||
@location = nil
|
|
||||||
@columns = 0
|
|
||||||
@width = pathes.map {|path| File.basename(path).size}.max + 2
|
|
||||||
pathes.each do |path|
|
pathes.each do |path|
|
||||||
@basename = File.basename(path)
|
|
||||||
unless @quiet
|
|
||||||
$stderr.printf("%s%-*s ", erase(@quiet), @width, @basename)
|
|
||||||
$stderr.flush
|
|
||||||
end
|
|
||||||
@columns = @width + 1
|
|
||||||
$stderr.puts if @verbose
|
|
||||||
count = @count
|
|
||||||
error = @error
|
|
||||||
load File.expand_path(path)
|
load File.expand_path(path)
|
||||||
if @tty
|
|
||||||
if @error == error
|
|
||||||
msg = "PASS #{@count-count}"
|
|
||||||
@columns += msg.size - 1
|
|
||||||
$stderr.print "#{@progress_bs}#{@passed}#{msg}#{@reset}" unless @quiet
|
|
||||||
else
|
|
||||||
msg = "FAIL #{@error-error}/#{@count-count}"
|
|
||||||
$stderr.print "#{@progress_bs}#{@failed}#{msg}#{@reset}"
|
|
||||||
@columns = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
$stderr.puts if !@quiet and (@tty or @error == error)
|
|
||||||
end
|
|
||||||
$stderr.print(erase) if @quiet
|
|
||||||
@errbuf.each do |msg|
|
|
||||||
$stderr.puts msg
|
|
||||||
end
|
|
||||||
|
|
||||||
out = @quiet ? $stdout : $stderr
|
|
||||||
|
|
||||||
if @error == 0
|
|
||||||
if @count == 0
|
|
||||||
out.puts "No tests, no problem" unless @quiet
|
|
||||||
else
|
|
||||||
out.puts "#{@passed}PASS#{@reset} all #{@count} tests"
|
|
||||||
end
|
|
||||||
exit true
|
|
||||||
else
|
|
||||||
$stderr.puts "#{@failed}FAIL#{@reset} #{@error}/#{@count} tests failed"
|
|
||||||
exit false
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_progress(message = '')
|
def concurrent_exec_test
|
||||||
if @quiet
|
aq = Queue.new
|
||||||
# do nothing
|
rq = Queue.new
|
||||||
elsif @verbose
|
|
||||||
$stderr.print "\##{@count} #{@location} "
|
ts = BT.wn.times.map do
|
||||||
elsif @tty
|
Thread.new do
|
||||||
$stderr.print "#{@progress_bs}#{@progress[@count % @progress.size]}"
|
while as = aq.pop
|
||||||
end
|
as.call
|
||||||
t = Time.now if @verbose
|
rq << as
|
||||||
faildesc, errout = with_stderr {yield}
|
end
|
||||||
t = Time.now - t if @verbose
|
ensure
|
||||||
if !faildesc
|
rq << nil
|
||||||
if @quiet
|
|
||||||
# do nothing
|
|
||||||
elsif @tty
|
|
||||||
$stderr.print "#{@progress_bs}#{@progress[@count % @progress.size]}"
|
|
||||||
elsif @verbose
|
|
||||||
$stderr.printf(". %.3f\n", t)
|
|
||||||
else
|
|
||||||
$stderr.print '.'
|
|
||||||
end
|
|
||||||
else
|
|
||||||
$stderr.print "#{@failed}F"
|
|
||||||
$stderr.printf(" %.3f", t) if @verbose
|
|
||||||
$stderr.print @reset
|
|
||||||
$stderr.puts if @verbose
|
|
||||||
error faildesc, message
|
|
||||||
unless errout.empty?
|
|
||||||
$stderr.print "#{@failed}stderr output is not empty#{@reset}\n", adjust_indent(errout)
|
|
||||||
end
|
|
||||||
if @tty and !@verbose
|
|
||||||
$stderr.printf("%-*s%s", @width, @basename, @progress[@count % @progress.size])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue Interrupt
|
|
||||||
$stderr.puts "\##{@count} #{@location}"
|
Assertion.all.to_a.shuffle.each do |path, assertions|
|
||||||
raise
|
assertions.each do |as|
|
||||||
rescue Exception => err
|
aq << as
|
||||||
$stderr.print 'E'
|
end
|
||||||
$stderr.puts if @verbose
|
end
|
||||||
error err.message, message
|
|
||||||
ensure
|
$stderr.print ' ' unless BT.quiet
|
||||||
|
aq.close
|
||||||
|
i = 1
|
||||||
|
term_wn = 0
|
||||||
begin
|
begin
|
||||||
check_coredump
|
while BT.wn != term_wn
|
||||||
rescue CoreDumpError => err
|
if r = rq.pop
|
||||||
$stderr.print 'E'
|
case
|
||||||
$stderr.puts if @verbose
|
when BT.quiet
|
||||||
error err.message, message
|
when BT.tty
|
||||||
|
$stderr.print "#{BT.progress_bs}#{BT.progress[(i+=1) % BT.progress.size]}"
|
||||||
|
else
|
||||||
|
$stderr.print '.'
|
||||||
|
end
|
||||||
|
else
|
||||||
|
term_wn += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
ts.each(&:kill)
|
||||||
|
ts.each(&:join)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def exec_test(pathes)
|
||||||
|
# setup
|
||||||
|
load_test pathes
|
||||||
|
BT_STATE.count = 0
|
||||||
|
BT_STATE.error = 0
|
||||||
|
BT.columns = 0
|
||||||
|
BT.width = pathes.map {|path| File.basename(path).size}.max + 2
|
||||||
|
|
||||||
|
# execute tests
|
||||||
|
if BT.wn > 1
|
||||||
|
concurrent_exec_test if BT.wn > 1
|
||||||
|
else
|
||||||
|
prev_basename = nil
|
||||||
|
Assertion.all.each do |basename, assertions|
|
||||||
|
if !BT.quiet && basename != prev_basename
|
||||||
|
prev_basename = basename
|
||||||
|
$stderr.printf("%s%-*s ", erase(BT.quiet), BT.width, basename)
|
||||||
|
$stderr.flush
|
||||||
|
end
|
||||||
|
BT.columns = BT.width + 1
|
||||||
|
$stderr.puts if BT.verbose
|
||||||
|
count = BT_STATE.count
|
||||||
|
error = BT_STATE.error
|
||||||
|
|
||||||
|
assertions.each do |assertion|
|
||||||
|
BT_STATE.count += 1
|
||||||
|
assertion.call
|
||||||
|
end
|
||||||
|
|
||||||
|
if BT.tty
|
||||||
|
if BT_STATE.error == error
|
||||||
|
msg = "PASS #{BT_STATE.count-count}"
|
||||||
|
BT.columns += msg.size - 1
|
||||||
|
$stderr.print "#{BT.progress_bs}#{BT.passed}#{msg}#{BT.reset}" unless BT.quiet
|
||||||
|
else
|
||||||
|
msg = "FAIL #{BT_STATE.error-error}/#{BT_STATE.count-count}"
|
||||||
|
$stderr.print "#{BT.progress_bs}#{BT.failed}#{msg}#{BT.reset}"
|
||||||
|
BT.columns = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
$stderr.puts if !BT.quiet and (BT.tty or BT_STATE.error == error)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# show results
|
||||||
|
unless BT.quiet
|
||||||
|
$stderr.puts(erase)
|
||||||
|
|
||||||
|
sec = Time.now - $start_time
|
||||||
|
$stderr.puts "Finished in #{'%.2f' % sec} sec\n\n" if Assertion.count > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
Assertion.errbuf.each do |msg|
|
||||||
|
$stderr.puts msg
|
||||||
|
end
|
||||||
|
|
||||||
|
out = BT.quiet ? $stdout : $stderr
|
||||||
|
|
||||||
|
if BT_STATE.error == 0
|
||||||
|
if Assertion.count == 0
|
||||||
|
out.puts "No tests, no problem" unless BT.quiet
|
||||||
|
else
|
||||||
|
out.puts "#{BT.passed}PASS#{BT.reset} all #{Assertion.count} tests"
|
||||||
|
end
|
||||||
|
true
|
||||||
|
else
|
||||||
|
$stderr.puts "#{BT.failed}FAIL#{BT.reset} #{BT_STATE.error}/#{BT_STATE.count} tests failed"
|
||||||
|
false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def target_platform
|
def target_platform
|
||||||
if @ruby
|
if BT.ruby
|
||||||
`#{@ruby} --disable-gems -e 'print RUBY_PLATFORM'`
|
`#{BT.ruby} --disable-gems -e 'print RUBY_PLATFORM'`
|
||||||
else
|
else
|
||||||
RUBY_PLATFORM
|
RUBY_PLATFORM
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_limit(testsrc, opt = '', **argh)
|
class Assertion < Struct.new(:src, :path, :lineno, :proc)
|
||||||
result = get_result_string(testsrc, opt, **argh)
|
@count = 0
|
||||||
if @tty and @verbose
|
@all = Hash.new{|h, k| h[k] = []}
|
||||||
$stderr.puts ".{#@reset}\n#{erase}#{result}"
|
@errbuf = []
|
||||||
else
|
|
||||||
@errbuf.push result
|
class << self
|
||||||
|
attr_reader :count, :errbuf
|
||||||
|
|
||||||
|
def all
|
||||||
|
@all
|
||||||
|
end
|
||||||
|
|
||||||
|
def add as
|
||||||
|
@all[as.path] << as
|
||||||
|
as.id = (@count += 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_accessor :id
|
||||||
|
attr_reader :err, :category
|
||||||
|
|
||||||
|
def initialize(*args)
|
||||||
|
super
|
||||||
|
self.class.add self
|
||||||
|
@category = self.path.match(/test_(.+)\.rb/)[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
self.proc.call self
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_check(message = '', opt = '', **argh)
|
||||||
|
show_progress(message) {
|
||||||
|
result = get_result_string(opt, **argh)
|
||||||
|
yield(result)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_stderr
|
||||||
|
out = err = nil
|
||||||
|
r, w = IO.pipe
|
||||||
|
@err = w
|
||||||
|
err_reader = Thread.new{ r.read }
|
||||||
|
|
||||||
|
begin
|
||||||
|
out = yield
|
||||||
|
ensure
|
||||||
|
w.close
|
||||||
|
err = err_reader.value
|
||||||
|
r.close rescue nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return out, err
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_error(msg, additional_message)
|
||||||
|
msg = "#{BT.failed}\##{self.id} #{self.path}:#{self.lineno}#{BT.reset}: #{msg} #{additional_message}"
|
||||||
|
if BT.tty
|
||||||
|
$stderr.puts "#{erase}#{msg}"
|
||||||
|
else
|
||||||
|
Assertion.errbuf << msg
|
||||||
|
end
|
||||||
|
BT_STATE.error += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def show_progress(message = '')
|
||||||
|
if BT.quiet || BT.wn > 1
|
||||||
|
# do nothing
|
||||||
|
elsif BT.verbose
|
||||||
|
$stderr.print "\##{@id} #{self.path}:#{self.lineno} "
|
||||||
|
elsif BT.tty
|
||||||
|
$stderr.print "#{BT.progress_bs}#{BT.progress[BT_STATE.count % BT.progress.size]}"
|
||||||
|
end
|
||||||
|
|
||||||
|
t = Time.now if BT.verbose
|
||||||
|
faildesc, errout = with_stderr {yield}
|
||||||
|
t = Time.now - t if BT.verbose
|
||||||
|
|
||||||
|
if !faildesc
|
||||||
|
# success
|
||||||
|
if BT.quiet || BT.wn > 1
|
||||||
|
# do nothing
|
||||||
|
elsif BT.tty
|
||||||
|
$stderr.print "#{BT.progress_bs}#{BT.progress[BT_STATE.count % BT.progress.size]}"
|
||||||
|
elsif BT.verbose
|
||||||
|
$stderr.printf(". %.3f\n", t)
|
||||||
|
else
|
||||||
|
$stderr.print '.'
|
||||||
|
end
|
||||||
|
else
|
||||||
|
$stderr.print "#{BT.failed}F"
|
||||||
|
$stderr.printf(" %.3f", t) if BT.verbose
|
||||||
|
$stderr.print BT.reset
|
||||||
|
$stderr.puts if BT.verbose
|
||||||
|
show_error faildesc, message
|
||||||
|
unless errout.empty?
|
||||||
|
$stderr.print "#{BT.failed}stderr output is not empty#{BT.reset}\n", adjust_indent(errout)
|
||||||
|
end
|
||||||
|
|
||||||
|
if BT.tty and !BT.verbose and BT.wn == 1
|
||||||
|
$stderr.printf("%-*s%s", BT.width, path, BT.progress[BT_STATE.count % BT.progress.size])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue Interrupt
|
||||||
|
$stderr.puts "\##{@id} #{path}:#{lineno}"
|
||||||
|
raise
|
||||||
|
rescue Exception => err
|
||||||
|
$stderr.print 'E'
|
||||||
|
$stderr.puts if BT.verbose
|
||||||
|
show_error err.message, message
|
||||||
|
ensure
|
||||||
|
begin
|
||||||
|
check_coredump
|
||||||
|
rescue CoreDumpError => err
|
||||||
|
$stderr.print 'E'
|
||||||
|
$stderr.puts if BT.verbose
|
||||||
|
show_error err.message, message
|
||||||
|
cleanup_coredump
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_result_string(opt = '', **argh)
|
||||||
|
if BT.ruby
|
||||||
|
filename = make_srcfile(**argh)
|
||||||
|
begin
|
||||||
|
kw = self.err ? {err: self.err} : {}
|
||||||
|
out = IO.popen("#{BT.ruby} -W0 #{opt} #{filename}", **kw)
|
||||||
|
pid = out.pid
|
||||||
|
out.read.tap{ Process.waitpid(pid); out.close }
|
||||||
|
ensure
|
||||||
|
raise Interrupt if $? and $?.signaled? && $?.termsig == Signal.list["INT"]
|
||||||
|
|
||||||
|
begin
|
||||||
|
Process.kill :KILL, pid
|
||||||
|
rescue Errno::ESRCH
|
||||||
|
# OK
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
eval(src).to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_srcfile(frozen_string_literal: nil)
|
||||||
|
filename = "bootstraptest.#{self.path}_#{self.lineno}_#{self.id}.rb"
|
||||||
|
File.open(filename, 'w') {|f|
|
||||||
|
f.puts "#frozen_string_literal:true" if frozen_string_literal
|
||||||
|
f.puts "GC.stress = true" if $stress
|
||||||
|
f.puts "print(begin; #{self.src}; end)"
|
||||||
|
}
|
||||||
|
filename
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_check(testsrc, message = '', opt = '', **argh)
|
def add_assertion src, pr
|
||||||
show_progress(message) {
|
loc = caller_locations(2, 1).first
|
||||||
result = get_result_string(testsrc, opt, **argh)
|
lineno = loc.lineno
|
||||||
yield(result)
|
path = File.basename(loc.path)
|
||||||
}
|
|
||||||
|
Assertion.new(src, path, lineno, pr)
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_equal(expected, testsrc, message = '', opt = '', **argh)
|
def assert_equal(expected, testsrc, message = '', opt = '', **argh)
|
||||||
newtest
|
add_assertion testsrc, -> as do
|
||||||
assert_check(testsrc, message, opt, **argh) {|result|
|
as.assert_check(message, opt, **argh) {|result|
|
||||||
if expected == result
|
if expected == result
|
||||||
nil
|
nil
|
||||||
else
|
else
|
||||||
desc = "#{result.inspect} (expected #{expected.inspect})"
|
desc = "#{result.inspect} (expected #{expected.inspect})"
|
||||||
pretty(testsrc, desc, result)
|
pretty(testsrc, desc, result)
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_match(expected_pattern, testsrc, message = '')
|
def assert_match(expected_pattern, testsrc, message = '')
|
||||||
newtest
|
add_assertion testsrc, -> as do
|
||||||
assert_check(testsrc, message) {|result|
|
as.assert_check(message) {|result|
|
||||||
if expected_pattern =~ result
|
if expected_pattern =~ result
|
||||||
nil
|
nil
|
||||||
else
|
else
|
||||||
desc = "#{expected_pattern.inspect} expected to be =~\n#{result.inspect}"
|
desc = "#{expected_pattern.inspect} expected to be =~\n#{result.inspect}"
|
||||||
pretty(testsrc, desc, result)
|
pretty(testsrc, desc, result)
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_not_match(unexpected_pattern, testsrc, message = '')
|
def assert_not_match(unexpected_pattern, testsrc, message = '')
|
||||||
newtest
|
add_assertion testsrc, -> as do
|
||||||
assert_check(testsrc, message) {|result|
|
as.assert_check(message) {|result|
|
||||||
if unexpected_pattern !~ result
|
if unexpected_pattern !~ result
|
||||||
nil
|
nil
|
||||||
else
|
else
|
||||||
desc = "#{unexpected_pattern.inspect} expected to be !~\n#{result.inspect}"
|
desc = "#{unexpected_pattern.inspect} expected to be !~\n#{result.inspect}"
|
||||||
pretty(testsrc, desc, result)
|
pretty(testsrc, desc, result)
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_valid_syntax(testsrc, message = '')
|
def assert_valid_syntax(testsrc, message = '')
|
||||||
newtest
|
add_assertion testsrc, -> as do
|
||||||
assert_check(testsrc, message, '-c') {|result|
|
as.assert_check(message, '-c') {|result|
|
||||||
result if /Syntax OK/ !~ result
|
result if /Syntax OK/ !~ result
|
||||||
}
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_normal_exit(testsrc, *rest, timeout: nil, **opt)
|
def assert_normal_exit(testsrc, *rest, timeout: nil, **opt)
|
||||||
newtest
|
add_assertion testsrc, -> as do
|
||||||
message, ignore_signals = rest
|
message, ignore_signals = rest
|
||||||
message ||= ''
|
message ||= ''
|
||||||
show_progress(message) {
|
as.show_progress(message) {
|
||||||
faildesc = nil
|
faildesc = nil
|
||||||
filename = make_srcfile(testsrc)
|
filename = as.make_srcfile
|
||||||
old_stderr = $stderr.dup
|
timeout_signaled = false
|
||||||
timeout_signaled = false
|
logfile = "assert_normal_exit.#{as.path}.#{as.lineno}.log"
|
||||||
begin
|
|
||||||
$stderr.reopen("assert_normal_exit.log", "w")
|
begin
|
||||||
io = IO.popen("#{@ruby} -W0 #{filename}")
|
err = open(logfile, "w")
|
||||||
pid = io.pid
|
io = IO.popen("#{BT.ruby} -W0 #{filename}", err: err)
|
||||||
th = Thread.new {
|
pid = io.pid
|
||||||
io.read
|
th = Thread.new {
|
||||||
io.close
|
io.read
|
||||||
$?
|
io.close
|
||||||
}
|
$?
|
||||||
if !th.join(timeout)
|
}
|
||||||
Process.kill :KILL, pid
|
if !th.join(timeout)
|
||||||
timeout_signaled = true
|
Process.kill :KILL, pid
|
||||||
|
timeout_signaled = true
|
||||||
|
end
|
||||||
|
status = th.value
|
||||||
|
ensure
|
||||||
|
err.close
|
||||||
end
|
end
|
||||||
status = th.value
|
if status && status.signaled?
|
||||||
ensure
|
signo = status.termsig
|
||||||
$stderr.reopen(old_stderr)
|
signame = Signal.list.invert[signo]
|
||||||
old_stderr.close
|
unless ignore_signals and ignore_signals.include?(signame)
|
||||||
end
|
sigdesc = "signal #{signo}"
|
||||||
if status && status.signaled?
|
if signame
|
||||||
signo = status.termsig
|
sigdesc = "SIG#{signame} (#{sigdesc})"
|
||||||
signame = Signal.list.invert[signo]
|
end
|
||||||
unless ignore_signals and ignore_signals.include?(signame)
|
if timeout_signaled
|
||||||
sigdesc = "signal #{signo}"
|
sigdesc << " (timeout)"
|
||||||
if signame
|
end
|
||||||
sigdesc = "SIG#{signame} (#{sigdesc})"
|
faildesc = pretty(testsrc, "killed by #{sigdesc}", nil)
|
||||||
end
|
stderr_log = File.read(logfile)
|
||||||
if timeout_signaled
|
if !stderr_log.empty?
|
||||||
sigdesc << " (timeout)"
|
faildesc << "\n" if /\n\z/ !~ faildesc
|
||||||
end
|
stderr_log << "\n" if /\n\z/ !~ stderr_log
|
||||||
faildesc = pretty(testsrc, "killed by #{sigdesc}", nil)
|
stderr_log.gsub!(/^.*\n/) { '| ' + $& }
|
||||||
stderr_log = File.read("assert_normal_exit.log")
|
faildesc << stderr_log
|
||||||
if !stderr_log.empty?
|
end
|
||||||
faildesc << "\n" if /\n\z/ !~ faildesc
|
|
||||||
stderr_log << "\n" if /\n\z/ !~ stderr_log
|
|
||||||
stderr_log.gsub!(/^.*\n/) { '| ' + $& }
|
|
||||||
faildesc << stderr_log
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
faildesc
|
||||||
faildesc
|
}
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_finish(timeout_seconds, testsrc, message = '')
|
def assert_finish(timeout_seconds, testsrc, message = '')
|
||||||
if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
|
add_assertion testsrc, -> as do
|
||||||
timeout_seconds *= 3
|
if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
|
||||||
end
|
timeout_seconds *= 3
|
||||||
newtest
|
end
|
||||||
show_progress(message) {
|
|
||||||
faildesc = nil
|
as.show_progress(message) {
|
||||||
filename = make_srcfile(testsrc)
|
faildesc = nil
|
||||||
io = IO.popen("#{@ruby} -W0 #{filename}")
|
filename = as.make_srcfile
|
||||||
pid = io.pid
|
io = IO.popen("#{BT.ruby} -W0 #{filename}", err: as.err)
|
||||||
waited = false
|
pid = io.pid
|
||||||
tlimit = Time.now + timeout_seconds
|
waited = false
|
||||||
diff = timeout_seconds
|
tlimit = Time.now + timeout_seconds
|
||||||
while diff > 0
|
diff = timeout_seconds
|
||||||
if Process.waitpid pid, Process::WNOHANG
|
while diff > 0
|
||||||
waited = true
|
if Process.waitpid pid, Process::WNOHANG
|
||||||
break
|
waited = true
|
||||||
end
|
break
|
||||||
if io.respond_to?(:read_nonblock)
|
|
||||||
if IO.select([io], nil, nil, diff)
|
|
||||||
begin
|
|
||||||
io.read_nonblock(1024)
|
|
||||||
rescue Errno::EAGAIN, IO::WaitReadable, EOFError
|
|
||||||
break
|
|
||||||
end while true
|
|
||||||
end
|
end
|
||||||
else
|
if io.respond_to?(:read_nonblock)
|
||||||
sleep 0.1
|
if IO.select([io], nil, nil, diff)
|
||||||
|
begin
|
||||||
|
io.read_nonblock(1024)
|
||||||
|
rescue Errno::EAGAIN, IO::WaitReadable, EOFError
|
||||||
|
break
|
||||||
|
end while true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
sleep 0.1
|
||||||
|
end
|
||||||
|
diff = tlimit - Time.now
|
||||||
end
|
end
|
||||||
diff = tlimit - Time.now
|
if !waited
|
||||||
end
|
Process.kill(:KILL, pid)
|
||||||
if !waited
|
Process.waitpid pid
|
||||||
Process.kill(:KILL, pid)
|
faildesc = pretty(testsrc, "not finished in #{timeout_seconds} seconds", nil)
|
||||||
Process.waitpid pid
|
end
|
||||||
faildesc = pretty(testsrc, "not finished in #{timeout_seconds} seconds", nil)
|
io.close
|
||||||
end
|
faildesc
|
||||||
io.close
|
}
|
||||||
faildesc
|
end
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def flunk(message = '')
|
def flunk(message = '')
|
||||||
newtest
|
add_assertion '', -> as do
|
||||||
show_progress('') { message }
|
as.show_progress('') { message }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_limit(testsrc, opt = '', **argh)
|
||||||
|
return if BT.quiet
|
||||||
|
|
||||||
|
add_assertion testsrc, -> as do
|
||||||
|
result = as.get_result_string(opt, **argh)
|
||||||
|
Assertion.errbuf << result
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def pretty(src, desc, result)
|
def pretty(src, desc, result)
|
||||||
|
@ -460,66 +675,6 @@ def untabify(str)
|
||||||
str.gsub(/^\t+/) {' ' * (8 * $&.size) }
|
str.gsub(/^\t+/) {' ' * (8 * $&.size) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_srcfile(src, frozen_string_literal: nil)
|
|
||||||
filename = 'bootstraptest.tmp.rb'
|
|
||||||
File.open(filename, 'w') {|f|
|
|
||||||
f.puts "#frozen_string_literal:true" if frozen_string_literal
|
|
||||||
f.puts "GC.stress = true" if $stress
|
|
||||||
f.puts "print(begin; #{src}; end)"
|
|
||||||
}
|
|
||||||
filename
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_result_string(src, opt = '', **argh)
|
|
||||||
if @ruby
|
|
||||||
filename = make_srcfile(src, **argh)
|
|
||||||
begin
|
|
||||||
`#{@ruby} -W0 #{opt} #{filename}`
|
|
||||||
ensure
|
|
||||||
raise Interrupt if $? and $?.signaled? && $?.termsig == Signal.list["INT"]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
eval(src).to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def with_stderr
|
|
||||||
out = err = nil
|
|
||||||
begin
|
|
||||||
r, w = IO.pipe
|
|
||||||
stderr = $stderr.dup
|
|
||||||
$stderr.reopen(w)
|
|
||||||
w.close
|
|
||||||
reader = Thread.start {r.read}
|
|
||||||
begin
|
|
||||||
out = yield
|
|
||||||
ensure
|
|
||||||
$stderr.reopen(stderr)
|
|
||||||
err = reader.value
|
|
||||||
end
|
|
||||||
ensure
|
|
||||||
w.close rescue nil
|
|
||||||
r.close rescue nil
|
|
||||||
end
|
|
||||||
return out, err
|
|
||||||
end
|
|
||||||
|
|
||||||
def newtest
|
|
||||||
@location = File.basename(caller(2).first)
|
|
||||||
@count += 1
|
|
||||||
cleanup_coredump
|
|
||||||
end
|
|
||||||
|
|
||||||
def error(msg, additional_message)
|
|
||||||
msg = "#{@failed}\##{@count} #{@location}#{@reset}: #{msg} #{additional_message}"
|
|
||||||
if @tty
|
|
||||||
$stderr.puts "#{erase}#{msg}"
|
|
||||||
else
|
|
||||||
@errbuf.push msg
|
|
||||||
end
|
|
||||||
@error += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def in_temporary_working_directory(dir)
|
def in_temporary_working_directory(dir)
|
||||||
if dir
|
if dir
|
||||||
Dir.mkdir dir
|
Dir.mkdir dir
|
||||||
|
@ -547,21 +702,21 @@ def cleanup_coredump
|
||||||
core_path = "/tmp/bootstraptest-core.#{Time.now.utc.iso8601}"
|
core_path = "/tmp/bootstraptest-core.#{Time.now.utc.iso8601}"
|
||||||
warn "A core file is found. Saving it at: #{core_path.dump}"
|
warn "A core file is found. Saving it at: #{core_path.dump}"
|
||||||
FileUtils.mv('core', core_path)
|
FileUtils.mv('core', core_path)
|
||||||
cmd = ['gdb', @ruby, '-c', core_path, '-ex', 'bt', '-batch']
|
cmd = ['gdb', BT.ruby, '-c', core_path, '-ex', 'bt', '-batch']
|
||||||
p cmd # debugging why it's not working
|
p cmd # debugging why it's not working
|
||||||
system(*cmd)
|
system(*cmd)
|
||||||
end
|
end
|
||||||
FileUtils.rm_f Dir.glob('core.*')
|
FileUtils.rm_f Dir.glob('core.*')
|
||||||
FileUtils.rm_f @ruby+'.stackdump' if @ruby
|
FileUtils.rm_f BT.ruby+'.stackdump' if BT.ruby
|
||||||
end
|
end
|
||||||
|
|
||||||
class CoreDumpError < StandardError; end
|
class CoreDumpError < StandardError; end
|
||||||
|
|
||||||
def check_coredump
|
def check_coredump
|
||||||
if File.file?('core') or not Dir.glob('core.*').empty? or
|
if File.file?('core') or not Dir.glob('core.*').empty? or
|
||||||
(@ruby and File.exist?(@ruby+'.stackdump'))
|
(BT.ruby and File.exist?(BT.ruby+'.stackdump'))
|
||||||
raise CoreDumpError, "core dumped"
|
raise CoreDumpError, "core dumped"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
main
|
exit main
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
assert_equal 'ok', %q{
|
assert_equal 'ok', %q{
|
||||||
File.unlink('zzz.rb') if File.file?('zzz.rb')
|
File.unlink('zzz1.rb') if File.file?('zzz1.rb')
|
||||||
instance_eval do
|
instance_eval do
|
||||||
autoload :ZZZ, './zzz.rb'
|
autoload :ZZZ, './zzz1.rb'
|
||||||
begin
|
begin
|
||||||
ZZZ
|
ZZZ
|
||||||
rescue LoadError
|
rescue LoadError
|
||||||
|
@ -11,9 +11,9 @@ assert_equal 'ok', %q{
|
||||||
}, '[ruby-dev:43816]'
|
}, '[ruby-dev:43816]'
|
||||||
|
|
||||||
assert_equal 'ok', %q{
|
assert_equal 'ok', %q{
|
||||||
open('zzz.rb', 'w') {|f| f.puts '' }
|
open('zzz2.rb', 'w') {|f| f.puts '' }
|
||||||
instance_eval do
|
instance_eval do
|
||||||
autoload :ZZZ, './zzz.rb'
|
autoload :ZZZ, './zzz2.rb'
|
||||||
begin
|
begin
|
||||||
ZZZ
|
ZZZ
|
||||||
rescue NameError
|
rescue NameError
|
||||||
|
@ -23,29 +23,29 @@ assert_equal 'ok', %q{
|
||||||
}, '[ruby-dev:43816]'
|
}, '[ruby-dev:43816]'
|
||||||
|
|
||||||
assert_equal 'ok', %q{
|
assert_equal 'ok', %q{
|
||||||
open('zzz.rb', 'w') {|f| f.puts 'class ZZZ; def self.ok;:ok;end;end'}
|
open('zzz3.rb', 'w') {|f| f.puts 'class ZZZ; def self.ok;:ok;end;end'}
|
||||||
instance_eval do
|
instance_eval do
|
||||||
autoload :ZZZ, './zzz.rb'
|
autoload :ZZZ, './zzz3.rb'
|
||||||
ZZZ.ok
|
ZZZ.ok
|
||||||
end
|
end
|
||||||
}, '[ruby-dev:43816]'
|
}, '[ruby-dev:43816]'
|
||||||
|
|
||||||
assert_equal 'ok', %q{
|
assert_equal 'ok', %q{
|
||||||
open("zzz.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"}
|
open("zzz4.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"}
|
||||||
autoload :ZZZ, "./zzz.rb"
|
autoload :ZZZ, "./zzz4.rb"
|
||||||
ZZZ.ok
|
ZZZ.ok
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_equal 'ok', %q{
|
assert_equal 'ok', %q{
|
||||||
open("zzz.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"}
|
open("zzz5.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"}
|
||||||
autoload :ZZZ, "./zzz.rb"
|
autoload :ZZZ, "./zzz5.rb"
|
||||||
require "./zzz.rb"
|
require "./zzz5.rb"
|
||||||
ZZZ.ok
|
ZZZ.ok
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_equal 'okok', %q{
|
assert_equal 'okok', %q{
|
||||||
open("zzz.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"}
|
open("zzz6.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"}
|
||||||
autoload :ZZZ, "./zzz.rb"
|
autoload :ZZZ, "./zzz6.rb"
|
||||||
t1 = Thread.new {ZZZ.ok}
|
t1 = Thread.new {ZZZ.ok}
|
||||||
t2 = Thread.new {ZZZ.ok}
|
t2 = Thread.new {ZZZ.ok}
|
||||||
[t1.value, t2.value].join
|
[t1.value, t2.value].join
|
||||||
|
@ -60,9 +60,9 @@ assert_finish 5, %q{
|
||||||
}, '[ruby-core:21696]'
|
}, '[ruby-core:21696]'
|
||||||
|
|
||||||
assert_equal 'A::C', %q{
|
assert_equal 'A::C', %q{
|
||||||
open("zzz.rb", "w") {}
|
open("zzz7.rb", "w") {}
|
||||||
class A
|
class A
|
||||||
autoload :C, "./zzz"
|
autoload :C, "./zzz7"
|
||||||
class C
|
class C
|
||||||
end
|
end
|
||||||
C
|
C
|
||||||
|
|
|
@ -10,7 +10,7 @@ show_limit %q{
|
||||||
puts "Fiber count: #{fibers.count} (#{error})"
|
puts "Fiber count: #{fibers.count} (#{error})"
|
||||||
break
|
break
|
||||||
end while true
|
end while true
|
||||||
} unless @quiet
|
}
|
||||||
|
|
||||||
assert_equal %q{ok}, %q{
|
assert_equal %q{ok}, %q{
|
||||||
Fiber.new{
|
Fiber.new{
|
||||||
|
|
|
@ -22,7 +22,7 @@ assert_match /\Awrong number of arguments \(.*\b0\b.* 1\)\z/, %q{
|
||||||
}
|
}
|
||||||
|
|
||||||
# default argument
|
# default argument
|
||||||
assert_equal '1', 'def m(x=1) x end; m()'
|
assert_equal '1', 'def m(x=1) x end; m();'
|
||||||
assert_equal '1', 'def m(x=7) x end; m(1)'
|
assert_equal '1', 'def m(x=7) x end; m(1)'
|
||||||
assert_equal '1', 'def m(a,x=1) x end; m(7)'
|
assert_equal '1', 'def m(a,x=1) x end; m(7)'
|
||||||
assert_equal '1', 'def m(a,x=7) x end; m(7,1)'
|
assert_equal '1', 'def m(a,x=7) x end; m(7,1)'
|
||||||
|
|
|
@ -243,7 +243,7 @@ assert_equal 'true', %{
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_equal 'ok', %{
|
assert_equal 'ok', %{
|
||||||
open("zzz.rb", "w") do |f|
|
open("zzz_t1.rb", "w") do |f|
|
||||||
f.puts <<-END
|
f.puts <<-END
|
||||||
begin
|
begin
|
||||||
Thread.new { fork { GC.start } }.join
|
Thread.new { fork { GC.start } }.join
|
||||||
|
@ -254,7 +254,7 @@ assert_equal 'ok', %{
|
||||||
end
|
end
|
||||||
END
|
END
|
||||||
end
|
end
|
||||||
require "./zzz.rb"
|
require "./zzz_t1.rb"
|
||||||
$result
|
$result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,7 +408,7 @@ assert_equal 'ok', %q{
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_equal 'ok', %{
|
assert_equal 'ok', %{
|
||||||
open("zzz.rb", "w") do |f|
|
open("zzz_t2.rb", "w") do |f|
|
||||||
f.puts <<-'end;' # do
|
f.puts <<-'end;' # do
|
||||||
begin
|
begin
|
||||||
m = Thread::Mutex.new
|
m = Thread::Mutex.new
|
||||||
|
@ -432,7 +432,7 @@ assert_equal 'ok', %{
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
require "./zzz.rb"
|
require "./zzz_t2.rb"
|
||||||
$result
|
$result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue