class Minitest::Minitap

Base class for TapY and TapJ runners.

Constants

IGNORE_CALLERS

Backtrace patterns to be omitted.

REVISION

TAP-Y/J Revision

Attributes

case_start_time[RW]
suite_start_time[RW]
test_cases[R]
Test results.

attr_reader :test_results

test_count[R]
test_start_time[RW]

Public Class Methods

new(options = {}) click to toggle source

Initialize new Minitap Minitest reporter.

Calls superclass method
# File lib/minitap/minitest5.rb, line 39
def initialize(options = {})
  io = options.delete(:io) || $stdout

  super(io, options)

  # since tapout uses a unix pipe, we don't want any buffering
  io.sync = true

  #@_stdout = StringIO.new
  #@_stderr = StringIO.new

  #@test_results = {}
  #self.assertion_count = 0

  @_source_cache = {}
end

Public Instance Methods

after_case(test_case) click to toggle source
# File lib/minitap/minitest5.rb, line 147
def after_case(test_case)
  tapout_after_case(test_case)
end
after_suite() click to toggle source
# File lib/minitap/minitest5.rb, line 138
def after_suite() #(test_cases)
  tapout_after_suite()
end
after_test(result) click to toggle source
# File lib/minitap/minitest5.rb, line 156
def after_test(result)
  tapout_after_test(result)
end
before_case(test_case) click to toggle source
# File lib/minitap/minitest5.rb, line 142
def before_case(test_case)
  @case_start_time = Time.now
  tapout_before_case(test_case)
end
before_suite(test_cases) click to toggle source
# File lib/minitap/minitest5.rb, line 132
def before_suite(test_cases)
  @suite_start_time = Time.now
  count_tests!(test_cases)
  tapout_before_suite()
end
before_test(result) click to toggle source
# File lib/minitap/minitest5.rb, line 151
def before_test(result)
  @test_start_time = Time.now
  tapout_before_test(result)
end
p(*args) click to toggle source

Stub out the three IO methods used by the built-in reporter.

# File lib/minitap/minitest5.rb, line 231
def p(*args)
  args.each{ |a| io.print(a.inspect); puts }
end
print(*args) click to toggle source
puts(*args) click to toggle source
# File lib/minitap/minitest5.rb, line 235
def puts(*args)
  io.puts(*args)
end
record(minitest_result) click to toggle source

Process a test result.

Calls superclass method
# File lib/minitap/minitest5.rb, line 76
def record(minitest_result)
  super(minitest_result)

  result = TestResult.new(minitest_result)

  #if exception #&& ENV['minitap_debug']
  #  STDERR.puts exception
  #  STDERR.puts exception.backtrace.join("\n")
  #end

  #@test_results[suite] ||= {}
  #@test_results[suite][test.to_sym] = record

  case_change = false

  if @previous_case != result.test_case
    case_change = true  
    before_case(result.test_case)
  end

  #before_test(result)
  case result.type
  when :skip
    test_skip(result)
  when :pass
    test_pass(result)
  when :fail
    test_fail(result)
  when :err
    test_err(result)
  end
  after_test(result)

  if case_change
    after_case(result.test_case)
  end

  @previous_case = result.test_case
end
report() click to toggle source

Minitest’s finalization hook.

Calls superclass method
# File lib/minitap/minitest5.rb, line 119
def report
  super

  uncapture_io

  after_suite()
end
start() click to toggle source

Minitest’s initial hook ran just before testing begins.

Calls superclass method
# File lib/minitap/minitest5.rb, line 59
def start
  super

  @test_cases = Runnable.runnables

  capture_io

  #@_stdout, @_stderr = capture_io do
  #  super_result = super(suite, type)
  #end

  before_suite(@test_cases)
end
test_err(result) click to toggle source
# File lib/minitap/minitest5.rb, line 172
def test_err(result)
  tapout_test_error(result)
end
test_fail(result) click to toggle source
# File lib/minitap/minitest5.rb, line 168
def test_fail(result)
  tapout_test_failure(result)
end
test_pass(result) click to toggle source
# File lib/minitap/minitest5.rb, line 164
def test_pass(result)
  tapout_test_pass(result)
end
test_skip(result) click to toggle source
# File lib/minitap/minitest5.rb, line 160
def test_skip(result)
  tapout_test_skip(result)
end

Private Instance Methods

capture_io() click to toggle source
# File lib/minitap/minitest5.rb, line 624
def capture_io
  @_stdout, @_stderr = $stdout, $stderr
  $stdout, $stderr = StringIO.new, StringIO.new
end
clean_message(message) click to toggle source
# File lib/minitap/minitest5.rb, line 605
def clean_message(message)
  message.strip #.gsub(/\s*\n\s*/, "\n")
end
code_snippet(file, line) click to toggle source

Returns a String of source code.

# File lib/minitap/minitest5.rb, line 555
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
count_tests!(test_cases) click to toggle source

# def add_test_result(suite, test, test_runner)

self.report[suite] ||= {}
self.report[suite][test.to_sym] = test_runner

self.assertion_count += test_runner.assertions

case test_runner.result
when :skip then self.skips += 1
when :failure then self.failures += 1
when :error then self.errors += 1
end

end

# File lib/minitap/minitest5.rb, line 270
def count_tests!(test_cases)
  filter = options[:filter] || '/./'
  filter = Regexp.new $1 if filter =~ /\/(.*)\//

  @test_count = test_cases.inject(0) do |acc, test_case|
    acc + test_case.runnable_methods.grep(filter).length
  end
end
filter_backtrace(backtrace) click to toggle source

Clean the backtrace of any reference to test framework itself.

# File lib/minitap/minitest5.rb, line 537
def filter_backtrace(backtrace)
  ## remove backtraces that match any pattern in IGNORE_CALLERS
  trace = backtrace.reject{|b| 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
  ## 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
filtered_tests(test_case) click to toggle source
# File lib/minitap/minitest5.rb, line 248
def filtered_tests(test_case) #suite, type
  filter = options[:filter] || '/./'
  filter = Regexp.new($1) if filter =~ /\/(.*)\//
  test_case.runnable_methods.grep(filter)
end
parse_source_location(caller) click to toggle source

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

# File lib/minitap/minitest5.rb, line 579
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
record_stdcom(doc) click to toggle source
# File lib/minitap/minitest5.rb, line 515
def record_stdcom(doc)
  begin
    doc['stdout'] = $stdout.string unless $stdout.length == 0 #empty?
    doc['stderr'] = $stderr.string unless $stderr.length == 0 #empty?
    $stdout.close; $stderr.close
    $stdout, $stderr = StringIO.new, StringIO.new
  #end
  rescue => err
    uncapture_io
    raise err
  end
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/minitap/minitest5.rb, line 572
def source(file)
  @_source_cache[file] ||= (
    File.readlines(file)
  )
end
tapout_after_case(test_case) click to toggle source
# File lib/minitap/minitest5.rb, line 327
def tapout_after_case(test_case)
end
tapout_after_suite() click to toggle source
# File lib/minitap/minitest5.rb, line 296
def tapout_after_suite()
  doc = {
    'type' => 'final',
    'time' => Time.now - self.suite_start_time,
    'counts' => {
      'total' => self.test_count,
      'pass'  => self.test_count - self.failures - self.errors - self.skips,
      'fail'  => self.failures,
      'error' => self.errors,
      'omit'  => self.skips,
      'todo'  => 0  # TODO: does minitest support pending tests?
    }
  }
  return doc
end
tapout_after_test(result) click to toggle source
# File lib/minitap/minitest5.rb, line 335
def tapout_after_test(result)
end
tapout_before_case(test_case) click to toggle source
# File lib/minitap/minitest5.rb, line 313
def tapout_before_case(test_case)
  doc = {
    'type'    => 'case',
    'subtype' => '',
    'label'   => "#{test_case}",
    'level'   => 0
  }

  record_stdcom(doc)

  return doc
end
tapout_before_suite() click to toggle source
# File lib/minitap/minitest5.rb, line 284
def tapout_before_suite()
  doc = {
    'type'  => 'suite',
    'start' => self.suite_start_time.strftime('%Y-%m-%d %H:%M:%S'),
    'count' => self.test_count,
    'seed'  => self.options[:seed],
    'rev'   => REVISION
  }
  return doc
end
tapout_before_test(result) click to toggle source
# File lib/minitap/minitest5.rb, line 331
def tapout_before_test(result)
end
tapout_test_error(result) click to toggle source

TAP record for error.

Returns [Hash].

# File lib/minitap/minitest5.rb, line 465
def tapout_test_error(result)
  e = result.exception
  e_file, e_line = location(e)
  r_file = e_file.sub(Dir.pwd+'/', '')

  doc = {
    'type'        => 'test',
    'subtype'     => '',
    'status'      => 'error',
    'label'       => "#{result.label}",
    #'setup' => "foo instance",
    #'expected' => 2,
    #'returned' => 1,
    #'file' => test/test_foo.rb
    #'line' => 45
    #'source' => ok 1, 2
    #'snippet' =>
    #  - 44: ok 0,0
    #  - 45: ok 1,2
    #  - 46: ok 2,4
    #'coverage' =>
    #  'file' => lib/foo.rb
    #  'line' => 11..13
    #  'code' => Foo#*
    'exception' => {
      'message'   => clean_message(e.error.message),
      'class'     => e.error.class.name,
      'file'      => r_file,
      'line'      => e_line,
      'source'    => source(e_file)[e_line-1].strip,
      'snippet'   => code_snippet(e_file, e_line),
      'backtrace' => filter_backtrace(e.backtrace)
    },
    'time' => Time.now - self.suite_start_time  # result.time
  }

  record_stdcom(doc)

  #stdout, stderr = @_stdout, @_stderr
  #doc['stdout'] = stdout unless stdout.empty?
  #doc['stderr'] = stderr unless stderr.empty?

  return doc
end
tapout_test_failure(result) click to toggle source

TAP record for failure.

Returns [Hash].

# File lib/minitap/minitest5.rb, line 417
def tapout_test_failure(result)
  e = result.exception
  e_file, e_line = location(result.exception)
  r_file = e_file.sub(Dir.pwd+'/', '')

  doc = {
    'type'        => 'test',
    'subtype'     => '',
    'status'      => 'fail',
    'label'       => "#{result.label}",
    #'setup' => "foo instance",
    #'expected' => 2,
    #'returned' => 1,
    #'file' => test/test_foo.rb
    #'line' => 45
    #'source' => ok 1, 2
    #'snippet' =>
    #  - 44: ok 0,0
    #  - 45: ok 1,2
    #  - 46: ok 2,4
    #'coverage' =>
    #  'file' => lib/foo.rb
    #  'line' => 11..13
    #  'code' => Foo#*
    'exception' => {
      'message'   => clean_message(e.message),  
      'class'     => e.class.name,
      'file'      => r_file,
      'line'      => e_line,
      'source'    => source(e_file)[e_line-1].strip,
      'snippet'   => code_snippet(e_file, e_line),
      'backtrace' => filter_backtrace(e.backtrace)
    },
    'time' => Time.now - self.suite_start_time   # result.time
  }

  record_stdcom(doc)

  #stdout, stderr = @_stdout, @_stderr
  #doc['stdout'] = stdout unless stdout.empty?
  #doc['stderr'] = stderr unless stderr.empty?

  return doc
end
tapout_test_pass(result) click to toggle source

TAP record for pass.

Returns [Hash].

# File lib/minitap/minitest5.rb, line 341
def tapout_test_pass(result) #suite, test, test_runner
  doc = {
    'type'        => 'test',
    'subtype'     => '',
    'status'      => 'pass',
    #'setup': foo instance
    'label'       => "#{result.label}",
    #'expected' => 2
    #'returned' => 2
    #'file' => 'test/test_foo.rb'
    #'line': 45
    #'source': ok 1, 2
    #'snippet':
    #  - 44: ok 0,0
    #  - 45: ok 1,2
    #  - 46: ok 2,4
    #'coverage':
    #  file: lib/foo.rb
    #  line: 11..13
    #  code: Foo#*
    'time' => Time.now - self.suite_start_time
  }

  record_stdcom(doc)

  #stdout, stderr = @_stdout, @_stderr
  #doc['stdout'] = stdout unless stdout.empty?
  #doc['stderr'] = stderr unless stderr.empty?

  return doc
end
tapout_test_skip(result) click to toggle source

TAP record for skip.

Returns [Hash].

# File lib/minitap/minitest5.rb, line 376
def tapout_test_skip(result) #suite, test, test_runner
  e = result.exception
  e_file, e_line = location(result.exception)
  r_file = e_file.sub(Dir.pwd+'/', '')

  doc = {
    'type'        => 'test',
    'subtype'     => '',
    'status'      => 'skip',
    'label'       => "#{result.label}",
    #'setup' => "foo instance",
    #'expected' => 2,
    #'returned' => 1,
    #'file' => test/test_foo.rb
    #'line' => 45
    #'source' => ok 1, 2
    #'snippet' =>
    #  - 44: ok 0,0
    #  - 45: ok 1,2
    #  - 46: ok 2,4
    #'coverage' =>
    #  'file' => lib/foo.rb
    #  'line' => 11..13
    #  'code' => Foo#*
    'exception' => {
      'message'   => clean_message(e.message),
      'class'     => e.class.name,
      'file'      => r_file,
      'line'      => e_line,
      'source'    => source(e_file)[e_line-1].strip,
      'snippet'   => code_snippet(e_file, e_line),
      'backtrace' => filter_backtrace(e.backtrace)
    },
    'time' => Time.now - self.suite_start_time
  }
  return doc
end
uncapture_io() click to toggle source
# File lib/minitap/minitest5.rb, line 630
def uncapture_io
  $stdout, $stderr = @_stdout, @_stderr
end