class Test::Unit::UI::Tap::BaseTestRunner

Base class for all TAP runners.

Constants

REVISION

TAP-Y/J Revision

Public Class Methods

new(suite, options={}) click to toggle source
Calls superclass method
# File lib/test/unit/ui/tap/base_testrunner.rb, line 18
def initialize(suite, options={})
  super

  @output = @options[:output] || STDOUT

  @level = 0

  @_source_cache = {}
  @already_outputted = false
  @top_level = true

  @counts = Hash.new{ |h,k| h[k] = 0 }
end

Private Instance Methods

attach_to_mediator() click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 45
def attach_to_mediator
  @mediator.add_listener(Test::Unit::TestResult::FAULT,                &method(:tapout_fault))
  @mediator.add_listener(Test::Unit::UI::TestRunnerMediator::STARTED,  &method(:tapout_before_suite))
  @mediator.add_listener(Test::Unit::UI::TestRunnerMediator::FINISHED, &method(:tapout_after_suite))
  @mediator.add_listener(Test::Unit::TestCase::STARTED_OBJECT,         &method(:tapout_before_test))
  @mediator.add_listener(Test::Unit::TestCase::FINISHED_OBJECT,        &method(:tapout_pass))
  @mediator.add_listener(Test::Unit::TestSuite::STARTED_OBJECT,        &method(:tapout_before_case))
  @mediator.add_listener(Test::Unit::TestSuite::FINISHED_OBJECT,       &method(:tapout_after_case))
end
captured_output() click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 476
def captured_output
  stdout = @_newout.string.chomp("\n")
  stderr = @_newerr.string.chomp("\n")

  doc = {}
  doc['stdout'] = stdout unless stdout.empty?
  doc['stderr'] = stderr unless stderr.empty?

  $stdout = @_oldout
  $stderr = @_olderr

  return doc
end
clean_label(name) click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 342
def clean_label(name)
  name.sub(/\(.+?\)\z/, '').chomp('()')
end
clean_message(message) click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 453
def clean_message(message)
  message.strip.gsub(/\n+/, "\n")
end
code_snippet(file, line) click to toggle source

Returns a Hash of source code.

# File lib/test/unit/ui/tap/base_testrunner.rb, line 373
def code_snippet(file, line)
  s = []
  if File.file?(file)
    source = source(file)
    radius = 2 # TODO: make customizable (number of surrounding lines to show)
    region = [line - radius, 1].max ..
             [line + radius, source.length].min

    s = region.map do |n|
      {n => source[n-1].chomp}
    end
  end
  return s
end
code_snippet_array(file, line) click to toggle source

Return Array of source code line numbers and text.

# File lib/test/unit/ui/tap/base_testrunner.rb, line 404
def code_snippet_array(file, line)
  snp = []
  if File.file?(file)
    source = source(file)
    radius = 2 # TODO: make customizable (number of surrounding lines to show)
    region = [line - radius, 1].max ..
             [line + radius, source.length].min
    snp = region.map do |n|
      [n, source[n-1].chomp]
    end
  end
  return snp
end
code_snippet_string(file, line) click to toggle source

Return nicely formated String of code lines.

# File lib/test/unit/ui/tap/base_testrunner.rb, line 389
def code_snippet_string(file, line)
  str = []
  snp = code_snippet_array(file, line)
  max = snp.map{ |n, c| n.to_s.size }.max
  snp.each do |n, c|
    if n == line
      str << "=> %#{max}d %s" % [n, c]
    else
      str << "   %#{max}d %s" % [n, c]
    end
  end
  str.join("\n")
end
filter_backtrace(backtrace) click to toggle source

Clean the backtrace of any reference to test framework itself.

# File lib/test/unit/ui/tap/base_testrunner.rb, line 347
    def filter_backtrace(backtrace)
      trace = backtrace

      ## remove backtraces that match any pattern in $RUBY_IGNORE_CALLERS
      #trace = race.reject{|b| $RUBY_IGNORE_CALLERS.any?{|i| i=~b}}

      ## remove `:in ...` portion of backtraces
      trace = trace.map do |bt| 
        i = bt.index(':in')
        i ? bt[0...i] :  bt
      end

# TODO: does TestUnit have a filter ?
      ## now apply MiniTest's own filter (note: doesn't work if done first, why?)
      #trace = MiniTest::filter_backtrace(trace)

      ## if the backtrace is empty now then revert to the original
      trace = backtrace if trace.empty?

      ## simplify paths to be relative to current workding diectory
      trace = trace.map{ |bt| bt.sub(Dir.pwd+File::SEPARATOR,'') }

      return trace
    end
location(backtrace) click to toggle source

Get location of exception.

# File lib/test/unit/ui/tap/base_testrunner.rb, line 441
def location(backtrace)
  last_before_assertion = ""
  backtrace.reverse_each do |s|
    break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
    last_before_assertion = s
  end
  file, line = last_before_assertion.sub(/:in .*$/, '').split(':')
  line = line.to_i if line
  return file, line
end
parse_source_location(caller) click to toggle source

Parse source location from caller, caller or an Exception object.

# File lib/test/unit/ui/tap/base_testrunner.rb, line 427
def parse_source_location(caller)
  case caller
  when Exception
    trace  = caller.backtrace.reject{ |bt| bt =~ INTERNALS }
    caller = trace.first
  when Array
    caller = caller.first
  end
  caller =~ /(.+?):(\d+(?=:|\z))/ or return ""
  source_file, source_line = $1, $2.to_i
  return source_file, source_line
end
puts(string='') click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 458
def puts(string='')
  @output.write(string.chomp+"\n")
  @output.flush      
end
reset_output() click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 464
def reset_output
  @_oldout = $stdout
  @_olderr = $stderr

  @_newout = StringIO.new
  @_newerr = StringIO.new

  $stdout = @_newout
  $stderr = @_newerr
end
setup_mediator() click to toggle source
Calls superclass method
# File lib/test/unit/ui/tap/base_testrunner.rb, line 35
def setup_mediator
  super

  #suite_name = @suite.to_s   # file name
  #suite_name = @suite.name if @suite.kind_of?(Module)

  #reset_output  # TODO: Should we do this up front?
end
source(file) click to toggle source

Cache source file text. This is only used if the TAP-Y stream doesn not provide a snippet and the test file is locatable.

# File lib/test/unit/ui/tap/base_testrunner.rb, line 420
def source(file)
  @_source_cache[file] ||= (
    File.readlines(file)
  )
end
tapout_after_case(testcase) click to toggle source

After each case, decrement the case level.

# File lib/test/unit/ui/tap/base_testrunner.rb, line 119
def tapout_after_case(testcase)
  @level -= 1
end
tapout_after_suite(elapsed_time) click to toggle source

After everything else.

# File lib/test/unit/ui/tap/base_testrunner.rb, line 75
def tapout_after_suite(elapsed_time)
  doc = {
    'type' => 'final',
    'time' => elapsed_time, #Time.now - @suite_start,
    'counts' => {
      'total' => @counts[:total],
      'pass'  => @counts[:pass], #self.test_count - self.failures - self.errors - self.skips,
      'fail'  => @counts[:fail],
      'error' => @counts[:error],
      'omit'  => @counts[:omit],
      'todo'  => @counts[:todo], 
    } #,
    #'assertions' => {
    #   'total' => @result.assertion_count + @counts[:fail],
    #   'pass'  => @result.assertion_count,
    #   'fail'  => @counts[:fail]
    #}
  }
  return doc
end
tapout_before_case(testcase) click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 99
def tapout_before_case(testcase)
  return nil if testcase.test_case.nil? 

  @test_case = testcase

  doc = {
    'type'    => 'case',
    #'subtype' => '',
    'label'   => testcase.name,
    'level'   => @level
  }

  @level += 1

  return doc
end
tapout_before_suite(result) click to toggle source

Before everything else.

# File lib/test/unit/ui/tap/base_testrunner.rb, line 58
def tapout_before_suite(result)
  @result = result
  @suite_start = Time.now

  doc = {
    'type'  => 'suite',
    'start' => @suite_start.strftime('%Y-%m-%d %H:%M:%S'),
    'count' => @suite.size,
    #'seed'  => #@suite.seed,  # no seed?
    'rev'   => REVISION
  }
  return doc
end
tapout_before_test(test) click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 124
def tapout_before_test(test)
  @test_start = Time.now
  # set up stdout and stderr to be captured
  reset_output
end
tapout_error(fault) click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 302
def tapout_error(fault)
  @counts[:total] += 1
  @counts[:error] += 1

  file, line = location(fault.location)
  rel_file = file.sub(Dir.pwd+'/', '')

  doc = {
    'type'        => 'test',
    #'subtype'     => '',
    'status'      => 'error',
    'label'       => clean_label(fault.test_name),
    #'setup' => "foo instance",
    #'expected'    => fault.inspected_expected,
    #'returned'    => fault.inspected_actual,
    #'file' => test_file
    #'line' => test_line
    #'source' => ok 1, 2
    #'snippet' =>
    #  - 44: ok 0,0
    #  - 45: ok 1,2
    #  - 46: ok 2,4
    'exception' => {
      'message'   => clean_message(fault.message),
      'class'     => fault.class.name,
      'file'      => rel_file,
      'line'      => line,
      'source'    => source(file)[line-1].strip,
      'snippet'   => code_snippet(file, line),
      'backtrace' => filter_backtrace(fault.location)
    },
    'time' => Time.now - @suite_start
  }

  doc.update(captured_output)

  return doc
end
tapout_fail(fault) click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 262
def tapout_fail(fault)
  @counts[:total] += 1
  @counts[:fail]  += 1

  file, line = location(fault.location)
  rel_file = file.sub(Dir.pwd+'/', '')

  doc = {
    'type'        => 'test',
    #'subtype'     => '',
    'status'      => 'fail',
    'label'       => clean_label(fault.test_name),
    #'setup' => "foo instance",
    'expected'    => fault.inspected_expected,
    'returned'    => fault.inspected_actual,
    #'file' => test_file
    #'line' => test_line
    #'source' => ok 1, 2
    #'snippet' =>
    #  - 44: ok 0,0
    #  - 45: ok 1,2
    #  - 46: ok 2,4
    'exception' => {
      'message'   => clean_message(fault.user_message || fault.message),
      'class'     => fault.class.name,
      'file'      => rel_file,
      'line'      => line,
      'source'    => source(file)[line-1].strip,
      'snippet'   => code_snippet(file, line),
      'backtrace' => filter_backtrace(fault.location)
    },
    'time' => Time.now - @suite_start
  }

  doc.update(captured_output)

  return doc
end
tapout_fault(fault) click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 131
def tapout_fault(fault)
  case fault
  when Test::Unit::Pending
    tapout_todo(fault)
  when Test::Unit::Omission
    tapout_omit(fault)
  when Test::Unit::Notification
    tapout_note(fault)
  when Test::Unit::Failure
    tapout_fail(fault)
  else
    tapout_error(fault)
  end

  @already_outputted = true #if fault.critical?
end
tapout_note(note) click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 149
def tapout_note(note)
  doc = {
    'type' => 'note',
    'text' => note.message
  }
  return doc
end
tapout_omit(fault) click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 225
def tapout_omit(fault)
  @counts[:total] += 1
  @counts[:omit]  += 1

  file, line = location(fault.location)
  rel_file   = file.sub(Dir.pwd+'/', '')

  doc = {
    'type'        => 'test',
    #'subtype'     => '',
    'status'      => 'skip',
    'label'       => clean_label(fault.test_name),
    #'setup' => "foo instance",
    #'expected' => 2,
    #'returned' => 1,
    #'file'     => test_file
    #'line'     => test_line
    #'source'   => source(test_file)[test_line-1].strip,
    #'snippet'  => code_snippet(test_file, test_line),
    'exception' => {
      'message'   => clean_message(fault.message),
      'class'     => fault.class.name,
      'file'      => rel_file,
      'line'      => line,
      'source'    => source(file)[line-1].strip,
      'snippet'   => code_snippet(file, line),
      'backtrace' => filter_backtrace(fault.location)
    },
    'time' => Time.now - @suite_start
  }

  doc.update(captured_output)

  return doc
end
tapout_pass(test) click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 158
def tapout_pass(test)
  if @already_outputted
    @already_outputted = false
    return nil
  end

  @counts[:total] += 1
  @counts[:pass]  += 1

  doc = {
    'type'        => 'test',
    #'subtype'     => '',
    'status'      => 'pass',
    #'setup': foo instance
    'label'       => clean_label(test.name),
    #'expected' => 2
    #'returned' => 2
    #'file'     => test_file
    #'line'     => test_line
    #'source'   => source(test_file)[test_line-1].strip,
    #'snippet'  => code_snippet(test_file, test_line),
    'time' => Time.now - @suite_start
  }

  doc.update(captured_output)

  return doc
end
tapout_todo(fault) click to toggle source
# File lib/test/unit/ui/tap/base_testrunner.rb, line 188
def tapout_todo(fault)
  @counts[:total] += 1
  @counts[:todo]  += 1

  file, line = location(fault.location)
  rel_file   = file.sub(Dir.pwd+'/', '')

  doc = {
    'type'        => 'test',
    #'subtype'     => '',
    'status'      => 'todo',
    'label'       => clean_label(fault.test_name),
    #'setup' => "foo instance",
    #'expected' => 2,
    #'returned' => 1,
    #'file'     => test_file
    #'line'     => test_line
    #'source'   => source(test_file)[test_line-1].strip,
    #'snippet'  => code_snippet(test_file, test_line),
    'exception' => {
      'message'   => clean_message(fault.message),
      'class'     => fault.class.name,
      'file'      => rel_file,
      'line'      => line,
      'source'    => source(file)[line-1].strip,
      'snippet'   => code_snippet(file, line),
      'backtrace' => filter_backtrace(fault.location)
    },
    'time' => Time.now - @suite_start
  }

  doc.update(captured_output)

  return doc
end