In Files

  • test/unit.rb

Test::Unit::Runner

Public Class Methods

autorun() click to toggle source
 
               # File test/unit.rb, line 323
def self.autorun
  at_exit {
    Test::Unit::RunCount.run_once {
      exit(Test::Unit::Runner.new.run(ARGV) || true)
    } unless @@stop_auto_run
  } unless @@installed_at_exit
  @@installed_at_exit = true
end
            

Public Instance Methods

_run_parallel(suites, type, result) click to toggle source
 
               # File test/unit.rb, line 386
def _run_parallel suites, type, result
  if @options[:parallel] < 1
    warn "Error: parameter of -j option should be greater than 0."
    return
  end

  begin
    # Require needed things for parallel running
    require 'thread'
    require 'timeout'
    @tasks = @files.dup # Array of filenames.
    @need_quit = false
    @dead_workers = []  # Array of dead workers.
    @warnings = []
    shutting_down = false
    rep = [] # FIXME: more good naming

    # Array of workers.
    @workers = @options[:parallel].times.map {
      worker = Worker.launch(@options[:ruby],@args)
      worker.hook(:dead) do |w,info|
        after_worker_quit w
        after_worker_down w, *info unless info.empty?
      end
      worker
    }

    # Thread: watchdog
    watchdog = Thread.new do
      while stat = Process.wait2
        break if @interrupt # Break when interrupt
        pid, stat = stat
        w = (@workers + @dead_workers).find{|x| pid == x.pid }.dup
        next unless w
        unless w.status == :quit
          # Worker down
          w.died(nil, !stat.signaled? && stat.exitstatus)
        end
      end
    end

    @workers_hash = Hash[@workers.map {|w| [w.io,w] }] # out-IO => worker
    @ios = @workers.map{|w| w.io } # Array of worker IOs

    while _io = IO.select(@ios)[0]
      break unless _io.each do |io|
        break if @need_quit
        worker = @workers_hash[io]
        case worker.read
        when /^okay$/
          worker.status = :running
          jobs_status
        when /^ready$/
          worker.status = :ready
          if @tasks.empty?
            break unless @workers.find{|x| x.status == :running }
          else
            worker.run(@tasks.shift, type)
          end

          jobs_status
        when /^done (.+?)$/
          r = Marshal.load($1.unpack("m")[0])
          result << r[0..1] unless r[0..1] == [nil,nil]
          rep    << {file: worker.real_file,
                     report: r[2], result: r[3], testcase: r[5]}
          $:.push(*r[4]).uniq!
        when /^p (.+?)$/
          del_jobs_status
          print $1.unpack("m")[0]
          jobs_status if @options[:job_status] == :replace
        when /^after (.+?)$/
          @warnings << Marshal.load($1.unpack("m")[0])
        when /^bye (.+?)$/
          after_worker_down worker, Marshal.load($1.unpack("m")[0])
        when /^bye$/
          if shutting_down
            after_worker_quit worker
          else
            after_worker_down worker
          end
        end
        break if @need_quit
      end
    end
  rescue Interrupt => e
    @interrupt = e
    return result
  ensure
    shutting_down = true

    watchdog.kill if watchdog
    if @interrupt
      @ios.select!{|x| @workers_hash[x].status == :running }
      while !@ios.empty? && (__io = IO.select(@ios,[],[],10))
          _io = __io[0]
          _io.each do |io|
            worker = @workers_hash[io]
            case worker.read
            when /^done (.+?)$/
              r = Marshal.load($1.unpack("m")[0])
              result << r[0..1] unless r[0..1] == [nil,nil]
              rep    << {file: worker.real_file,
                         report: r[2], result: r[3], testcase: r[5]}
              $:.push(*r[4]).uniq!
              @ios.delete(io)
            end
          end
      end
    end
    @workers.each do |worker|
      begin
        timeout(1) do
          worker.puts "quit"
        end
      rescue Errno::EPIPE
      rescue Timeout::Error
      end
      worker.close
    end
    begin
      timeout(0.2*@workers.size) do
        Process.waitall
      end
    rescue Timeout::Error
      @workers.each do |worker|
        begin
          Process.kill(:KILL,worker.pid)
        rescue Errno::ESRCH; end
      end
    end

    if @interrupt || @options[:no_retry] || @need_quit
      rep.each do |r|
        report.push(*r[:report])
      end
      @errors   += rep.map{|x| x[:result][0] }.inject(:+)
      @failures += rep.map{|x| x[:result][1] }.inject(:+)
      @skips    += rep.map{|x| x[:result][2] }.inject(:+)
    else
      puts ""
      puts "Retrying..."
      puts ""
      rep.each do |r|
        if r[:testcase] && r[:file] && !r[:report].empty?
          require r[:file]
          _run_suite(eval(r[:testcase]),type)
        else
          report.push(*r[:report])
          @errors += r[:result][0]
          @failures += r[:result][1]
          @skips += r[:result][2]
        end
      end
    end
    if @warnings
      warn ""
      ary = []
      @warnings.reject! do |w|
        r = ary.include?(w[1].message)
        ary << w[1].message
        r
      end
      @warnings.each do |w|
        warn "#{w[0]}: #{w[1].message} (#{w[1].class})"
      end
      warn ""
    end
  end
end
            
_run_suites(suites, type) click to toggle source
 
               # File test/unit.rb, line 557
def _run_suites suites, type
  @interrupt = nil
  result = []
  if @options[:parallel]
    _run_parallel suites, type, result
  else
    suites.each {|suite|
      begin
        result << _run_suite(suite, type)
      rescue Interrupt => e
        @interrupt = e
        break
      end
    }
  end
  report.reject!{|r| r.start_with? "Skipped:" } if @options[:hide_skip]
  result
end
            
after_worker_down(worker, e=nil, c=false) click to toggle source
 
               # File test/unit.rb, line 332
def after_worker_down(worker, e=nil, c=false)
  return unless @options[:parallel]
  return if @interrupt
  if e
    b = e.backtrace
    warn "#{b.shift}: #{e.message} (#{e.class})"
    STDERR.print b.map{|s| "\tfrom #{s}"}.join("\n")
  end
  @need_quit = true
  warn ""
  warn "Some worker was crashed. It seems ruby interpreter's bug"
  warn "or, a bug of test/unit/parallel.rb. try again without -j"
  warn "option."
  warn ""
  STDERR.flush
  exit c
end
            
after_worker_quit(worker) click to toggle source
 
               # File test/unit.rb, line 378
def after_worker_quit(worker)
  return unless @options[:parallel]
  return if @interrupt
  @workers.delete(worker)
  @dead_workers << worker
  @ios = @workers.map(&:io)
end
            
del_jobs_status() click to toggle source
 
               # File test/unit.rb, line 373
def del_jobs_status
  return unless @options[:job_status] == :replace && @jstr_size.nonzero?
  print "\r"+" "*@jstr_size+"\r"
end
            
jobs_status() click to toggle source
 
               # File test/unit.rb, line 350
def jobs_status
  return unless @options[:job_status]
  puts "" unless @options[:verbose]
  status_line = @workers.map(&:to_s).join(" ")
  if @options[:job_status] == :replace and $stdout.tty?
    @terminal_width ||=
      begin
        require 'io/console'
        $stdout.winsize[1]
      rescue LoadError, NoMethodError
        ENV["COLUMNS"].to_i.nonzero? || 80
      end
    @jstr_size ||= 0
    del_jobs_status
    $stdout.flush
    print status_line[0...@terminal_width]
    $stdout.flush
    @jstr_size = [status_line.size, @terminal_width].min
  else
    puts status_line
  end
end
            
puke(klass, meth, e) click to toggle source

Overriding of MiniTest::Unit#puke

 
               # File test/unit.rb, line 577
def puke klass, meth, e
  # TODO:
  #   this overriding is for minitest feature that skip messages are
  #   hidden when not verbose (-v), note this is temporally.
  e = case e
      when MiniTest::Skip then
        @skips += 1
        "Skipped:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
      when MiniTest::Assertion then
        @failures += 1
        "Failure:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
      else
        @errors += 1
        bt = MiniTest::filter_backtrace(e.backtrace).join "\n    "
        "Error:\n#{meth}(#{klass}):\n#{e.class}: #{e.message}\n    #{bt}\n"
      end
  @report << e
  e[0, 1]
end
            
status(*args) click to toggle source
 
               # File test/unit.rb, line 597
def status(*args)
  result = super
  raise @interrupt if @interrupt
  result
end