class Tapout::Reporters::Abstract

The Abstract class serves as a base class for all reporters. Reporters must sublcass Abstract in order to be added the the Reporters Index.

Constants

INTERNALS

Used to clean-up backtrace.

TODO: Use Rubinius global system instead.

Public Class Methods

inherited(subclass) click to toggle source

When Abstract is inherited it saves a reference to it in `Reporters.index`.

# File lib/tapout/reporters/abstract.rb, line 43
def self.inherited(subclass)
  name = subclass.name.split('::').last.downcase
  name = name.chomp('reporter')
  Reporters.index[name] = subclass
end
new() click to toggle source

New reporter.

# File lib/tapout/reporters/abstract.rb, line 52
def initialize
  @passed  = []
  @failed  = []
  @raised  = []
  @skipped = []
  @omitted = []

  #$stdin.sync  = true
  $stdout.sync = true

  @case_stack = []
  @source     = {}
  @exit_code  = 0  # assume passing
end

Public Instance Methods

<<(entry) click to toggle source
# File lib/tapout/reporters/abstract.rb, line 136
def <<(entry)
  handle(entry)
end
backtrace(test) click to toggle source

Give a test entry, returns a clean and filtered backtrace.

# File lib/tapout/reporters/abstract.rb, line 253
def backtrace(test)
  exception = test['exception']

  trace   = exception['backtrace']
  file    = exception['file']
  line    = exception['line']

  if trace
    trace = clean_backtrace(trace)
  else
    trace = []
    trace << "#{file}:#{line}" if file && line
  end

  trace
end
backtrace_snippets(test) click to toggle source

Get s nicely formatted string of backtrace and source code, ready for output.

@return [String] Formatted backtrace with source code.

# File lib/tapout/reporters/abstract.rb, line 299
def backtrace_snippets(test)
  string = []
  backtrace_snippets_chain(test).each do |(stamp, snip)|
    string << stamp.ansi(*config.highlight)
    if snip
      if snip.index('=>')
        string << snip.sub(/(\=\>.*?)$/, '\1'.ansi(*config.highlight))
      else
        string << snip
      end
    end
  end
  string.join("\n")
end
backtrace_snippets_chain(test) click to toggle source

Returns an associative array of backtraces along with corresponding source code, if available.

@return [Array<String,String>]

Array of backtrace line and source code.
# File lib/tapout/reporters/abstract.rb, line 319
def backtrace_snippets_chain(test)
  code  = test['exception']['snippet']
  file  = test['exception']['file']
  line  = test['exception']['line']

  chain = []

  bts = backtrace(test)

  if bts.empty?
    if file && line
      bts << "#{file}:#{line}"
    end
  end

  bts.each do |bt|
    if md = /(.+?):(\d+)/.match(bt)
      chain << [bt, code_snippet('file'=>md[1], 'line'=>md[2].to_i)]
    else
      chain << [bt, nil]
    end
  end

  if chain.first && chain.first.last.nil?
    chain[0][1] = code_snippet('snippet'=>code, 'line'=>line)
  end

  chain
end
captured_output(test) click to toggle source
# File lib/tapout/reporters/abstract.rb, line 497
def captured_output(test)
  str = ""
  str += captured_stdout(test){ |c| "\nSTDOUT\n#{c.tabto(4)}\n" }.to_s
  str += captured_stderr(test){ |c| "\nSTDERR\n#{c.tabto(4)}\n" }.to_s
  str
end
captured_output?(test) click to toggle source
# File lib/tapout/reporters/abstract.rb, line 505
def captured_output?(test)
  captured_stdout?(test) || captured_stderr?(test)
end
captured_stderr(test) { |stderr| ... } click to toggle source
# File lib/tapout/reporters/abstract.rb, line 486
def captured_stderr(test)
  stderr = test['stderr'].to_s.strip
  return if stderr.empty?
  if block_given?
    yield(stderr)
  else
    stderr
  end
end
captured_stderr?(test) click to toggle source
# File lib/tapout/reporters/abstract.rb, line 516
def captured_stderr?(test)
  stderr = test['stderr'].to_s.strip
  !stderr.empty?
end
captured_stdout(test) { |stdout| ... } click to toggle source
# File lib/tapout/reporters/abstract.rb, line 475
def captured_stdout(test)
  stdout = test['stdout'].to_s.strip
  return if stdout.empty?
  if block_given?
    yield(stdout)
  else
    stdout
  end
end
captured_stdout?(test) click to toggle source
# File lib/tapout/reporters/abstract.rb, line 510
def captured_stdout?(test)
  stderr = test['stdout'].to_s.strip
  !stderr.empty?
end
clean_backtrace(backtrace) click to toggle source

Clean the backtrace of any “boring” reference.

# File lib/tapout/reporters/abstract.rb, line 276
def clean_backtrace(backtrace)
  if ENV['debug']
    trace = backtrace
  else
    trace = backtrace.reject{ |bt| bt =~ INTERNALS }
  end
  trace = trace.map do |bt| 
    if i = bt.index(':in')
      bt[0...i]
    else
      bt
    end
  end
  trace = backtrace if trace.empty?
  trace = trace.map{ |bt| bt.sub(Dir.pwd+File::SEPARATOR,'') }
  trace = trace[0, config.trace_depth]
  trace
end
code_snippet(entry) click to toggle source

Returns a String of source code.

# File lib/tapout/reporters/abstract.rb, line 362
      def code_snippet(entry)
        file    = entry['file']
        line    = entry['line']
        snippet = entry['snippet']

        s = []

        case snippet
        when String
          lines = snippet.lines.to_a
          index = line - ((lines.size - 1) / 2)
          lines.each do |line|
            s << [index, line]
            index += 1
          end
        when Array
          snippet.each do |h|
            s << [h.keys.first, h.values.first]
          end
        else
          ##backtrace = exception.backtrace.reject{ |bt| bt =~ INTERNALS }
          ##backtrace.first =~ /(.+?):(\d+(?=:|\z))/ or return ""
          #caller =~ /(.+?):(\d+(?=:|\z))/ or return ""
          #source_file, source_line = $1, $2.to_i

          if file && File.file?(file)
            source = source(file)

            radius = config.lines # number of surrounding lines to show
            region = [line - radius, 1].max ..
                     [line + radius, source.length].min

            #len = region.last.to_s.length

            s = region.map do |n|
              #format % [n, source[n-1].chomp]
              [n, source[n-1].chomp]
            end
          end
        end

        format_snippet_array(s, line)

#        len = s.map{ |(n,t)| n }.max.to_s.length
#
#        # ensure proper alignment by zero-padding line numbers
#        format = " %5s %0#{len}d %s"
#
#        #s = s.map{|n,t|[n,t]}.sort{|a,b|a[0]<=>b[0]}
#
#        pretty = s.map do |(n,t)|
#          format % [('=>' if n == line), n, t.rstrip]
#        end #.unshift "[#{region.inspect}] in #{source_file}"
#
#        return pretty
      end
complete_cases(case_entry=nil) click to toggle source
# File lib/tapout/reporters/abstract.rb, line 462
def complete_cases(case_entry=nil)
  if case_entry
    while @case_stack.last and @case_stack.last['level'].to_i >= case_entry['level'].to_i
      finish_case(@case_stack.pop)
    end
  else
    while @case_stack.last
      finish_case(@case_stack.pop)
    end
  end
end
config() click to toggle source

Access to configurtion.

# File lib/tapout/reporters/abstract.rb, line 532
def config
  Tapout.config
end
count_tally(entry) click to toggle source

Return the total counts given a tally or final entry.

@return [Array<Integer>] The total, fail, error, todo and omit counts.

# File lib/tapout/reporters/abstract.rb, line 199
def count_tally(entry)
  total = @passed.size + @failed.size + @raised.size + @skipped.size + @omitted.size
  total = entry['counts']['total'] || total

  if counts = entry['counts']
    pass  = counts['pass']  || @passed.size
    fail  = counts['fail']  || @failed.size
    error = counts['error'] || @raised.size
    todo  = counts['todo']  || @skipped.size
    omit  = counts['omit']  || @omitted.size
  else
    pass, fail, error, todo, omit = *[@passed, @failed, @raised, @skipped, @omitted].map{ |e| e.size }
  end

  return total, pass, fail, error, todo, omit
end
duration(seconds, precision=2) click to toggle source
# File lib/tapout/reporters/abstract.rb, line 522
def duration(seconds, precision=2)
  p = precision.to_i
  s = seconds.to_i
  f = seconds - s
  h, s = s.divmod(60)
  m, s = s.divmod(60)
  "%02d:%02d:%02d.%0#{p}d" % [h, m, s, f * 10**p]
end
error(entry) click to toggle source

Handle test with error status.

# File lib/tapout/reporters/abstract.rb, line 96
def error(entry)
  @raised << entry
end
exit_code() click to toggle source

Get the exit code.

# File lib/tapout/reporters/abstract.rb, line 178
def exit_code
  @exit_code
end
fail(entry) click to toggle source

Handle test with fail status.

# File lib/tapout/reporters/abstract.rb, line 91
def fail(entry)
  @failed << entry
end
finalize() click to toggle source

When all is said and done.

# File lib/tapout/reporters/abstract.rb, line 68
def finalize
  @exit_code
end
finish_case(entry) click to toggle source

When a test case is complete.

# File lib/tapout/reporters/abstract.rb, line 126
def finish_case(entry)
end
finish_suite(entry) click to toggle source

Handle final entry.

# File lib/tapout/reporters/abstract.rb, line 130
def finish_suite(entry)
end
finish_test(entry) click to toggle source

When a test unit is complete.

# File lib/tapout/reporters/abstract.rb, line 122
def finish_test(entry)
end
format_snippet_array(array, line) click to toggle source
# File lib/tapout/reporters/abstract.rb, line 420
def format_snippet_array(array, line)
  s = array

  len = s.map{ |(n,t)| n }.max.to_s.length

  # ensure proper alignment by zero-padding line numbers
  format = " %5s %0#{len}d %s"

  #s = s.map{|n,t|[n,t]}.sort{|a,b|a[0]<=>b[0]}

  pretty = s.map do |(n,t)|
    format % [('=>' if n == line), n, t.rstrip]
  end #.unshift "[#{region.inspect}] in #{source_file}"

  pretty.join("\n")
end
handle(entry) click to toggle source

Handler method. This dispatches a given entry to the appropriate report methods.

# File lib/tapout/reporters/abstract.rb, line 142
def handle(entry)
  case entry['type']
  when 'suite'
    start_suite(entry)
  when 'case'
    complete_cases(entry)
    @case_stack << entry
    start_case(entry)
  when 'note'
    note(entry)
  when 'test'
    start_test(entry)
    case entry['status']
    when 'pass'
      pass(entry)
    when 'fail'
      @exit_code = -1
      fail(entry)
    when 'error'
      @exit_code = -1
      error(entry)
    when 'omit'
      omit(entry)
    when 'todo', 'skip', 'pending'
      todo(entry)
    end
    finish_test(entry)
  when 'tally'
    tally(entry)
  when 'final'
    complete_cases
    finish_suite(entry)
  end
end
note(entry) click to toggle source

Handle an arbitray note.

# File lib/tapout/reporters/abstract.rb, line 114
def note(entry)
end
omit(entry) click to toggle source

Handle test with omit status.

# File lib/tapout/reporters/abstract.rb, line 101
def omit(entry)
  @omitted << entry
end
parse_backtrace(bt) click to toggle source

Parse a bactrace line into file and line number. Returns nil for both if parsing fails.

@return [Array<String,Integer>] File and line number.

# File lib/tapout/reporters/abstract.rb, line 353
def parse_backtrace(bt)
  if md = /(.+?):(\d+)/.match(bt)
    return md[1], md[2].to_i
  else
    return nil, nil
  end
end
parse_source_location(caller) click to toggle source

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

# File lib/tapout/reporters/abstract.rb, line 448
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
  returnf source_file, source_line
end
pass(entry) click to toggle source

Handle test with pass status.

# File lib/tapout/reporters/abstract.rb, line 86
def pass(entry)
  @passed << entry
end
skip(entry)

Same as todo.

Alias for: todo
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.

@return [String] File contents.

# File lib/tapout/reporters/abstract.rb, line 441
def source(file)
  @source[file] ||= (
    File.readlines(file)
  )
end
start_case(entry) click to toggle source

At the start of a new test case.

# File lib/tapout/reporters/abstract.rb, line 78
def start_case(entry)
end
start_suite(entry) click to toggle source

Handle header.

# File lib/tapout/reporters/abstract.rb, line 73
def start_suite(entry)
  @start_time = Time.now
end
start_test(entry) click to toggle source

Handle test. This is run before the status handlers.

# File lib/tapout/reporters/abstract.rb, line 82
def start_test(entry)
end
tally(entry) click to toggle source

Handle running tally.

# File lib/tapout/reporters/abstract.rb, line 118
def tally(entry)
end
tally_message(entry) click to toggle source

Generate a tally message given a tally or final entry.

@return [String] tally message

# File lib/tapout/reporters/abstract.rb, line 219
def tally_message(entry)
  sums = count_tally(entry)

  total, pass, fail, error, todo, omit = *sums

  # TODO: Assertion counts isn't TAP-Y/J spec, is it a good idea to add ?
  if entry['counts'] && entry['counts']['assertions']
    assertions = entry['counts']['assertions']['pass']
    failures   = entry['counts']['assertions']['fail']
  else
    assertions = nil
    failures   = nil
  end

  text = []
  text << "%s pass".ansi(*config.pass)
  text << "%s fail".ansi(*config.fail)
  text << "%s errs".ansi(*config.error)
  text << "%s todo".ansi(*config.todo)
  text << "%s omit".ansi(*config.omit)
  text = "%s tests: " + text.join(", ")

  if assertions
    text << " (%s/%s assertions)"
    text = text % (sums + [assertions - failures, assertions])
  else
    text = text % sums
  end

  text
end
time_tally(entry) click to toggle source

Calculate the lapsed time, the rate of testing and average time per test.

@return [Array<Float>] Lapsed time, rate and average.

# File lib/tapout/reporters/abstract.rb, line 185
def time_tally(entry)
  total = @passed.size + @failed.size + @raised.size + @skipped.size + @omitted.size
  total = entry['counts']['total'] || total

  time = (entry['time'] || (Time.now - @start_time)).to_f
  rate = total / time
  avg  = time / total

  return time, rate, avg
end
todo(entry) click to toggle source

Handle test with skip or pending status.

# File lib/tapout/reporters/abstract.rb, line 106
def todo(entry)
  @skipped << entry
end
Also aliased as: skip