lib/qed/reporter/abstract.rb in qed-2.5.1 vs lib/qed/reporter/abstract.rb in qed-2.6.0

- old
+ new

@@ -1,19 +1,28 @@ module QED module Reporter require 'facets/string' - require 'ansi/code' + begin + require 'ansi/core' + rescue LoadError + require 'ansi/code' + end + # = Reporter Absract Base Class # # Serves as the base class for all other output formats. class Abstract + attr :session + attr :io + attr :record + # TODO: pass session into initialize def initialize(options={}) @io = options[:io] || STDOUT @trace = options[:trace] @record = { @@ -108,10 +117,11 @@ end # At the start of a session, before running any demonstrations. def before_session(session) + @session = session @start_time = Time.now end # Beginning of a demonstration. def before_demo(demo) #demo(demo) @@ -246,16 +256,28 @@ io.puts mask % vars end # - def clean_backtrace(btrace) - btrace.chomp(":in \`__binding__'") + INTERNALS = /(lib|bin)[\\\/](qed|ae)/ + + # + def sane_backtrace(exception) + if trace_count + clean_backtrace(*exception.backtrace[0, trace_count]) + else + clean_backtrace(*exception.backtrace) + end end # - INTERNALS = /(lib|bin)[\\\/](qed|ae)/ + def clean_backtrace(*btrace) + stack = btrace.reject{ |bt| bt =~ INTERNALS } unless $DEBUG + stack.map do |bt| + bt.chomp(":in \`__binding__'") + end + end =begin # Clean the backtrace of any reference to ko/ paths and code. def clean_backtrace(backtrace) trace = backtrace.reject{ |bt| bt =~ INTERNALS } @@ -267,47 +289,143 @@ end end end =end + # Produce a pretty code snippet given an exception. # - def code_snippet(exception, bredth=3) - backtrace = exception.backtrace.reject{ |bt| bt =~ INTERNALS } - backtrace.first =~ /(.+?):(\d+(?=:|\z))/ or return "" - source_file, source_line = $1, $2.to_i + # @param exception [Exception, String] + # An exception or backtrace. + # + # @param radius [Integer] + # The number of surrounding lines to show. + # + # @return [String] pretty code snippet + def code_snippet(exception, radius=2) + radius = radius.to_i - source = source(source_file) + file, lineno = file_and_line(exception) + + return nil if file.empty? + + source = source(file) - radius = bredth # number of surrounding lines to show - region = [source_line - radius, 1].max .. - [source_line + radius, source.length].min + region = [lineno - radius, 1].max .. + [lineno + radius, source.length].min # ensure proper alignment by zero-padding line numbers format = " %2s %0#{region.last.to_s.length}d %s" pretty = region.map do |n| - format % [('=>' if n == source_line), n, source[n-1].chomp] + format % [('=>' if n == lineno), n, source[n-1].chomp] end #.unshift "[#{region.inspect}] in #{source_file}" pretty end + # Return a structure code snippet in an array of lineno=>line + # hash elements. # + # @param exception [Exception, String] + # An exception or backtrace. + # + # @param radius [Integer] + # The number of surrounding lines to show. + # + # @return [Hash] structured code snippet + def structured_code_snippet(exception, radius=2) + radius = radius.to_i + + file, lineno = file_and_line(exception) + + return {} if file.empty? + + source = source(file) + + region = [lineno - radius, 1].max .. + [lineno + radius, source.length].min + + region.map do |n| + {n => source[n-1].chomp} + end + end + + # Cache the source code of a file. + # + # @param file [String] full pathname to file + # + # @return [String] source code def source(file) @source[file] ||= ( File.readlines(file) ) end + # @param exception [Exception,Array,String] + # An exception or backtrace. + # + #-- # TODO: Show more of the file name than just the basename. + #++ def file_and_line(exception) - line = exception.backtrace[0] - return "" unless line - i = line.rindex(':in') - line = i ? line[0...i] : line - File.basename(line) + backtrace = case exception + when Exception + exception.backtrace.reject{ |bt| bt =~ INTERNALS }.first + when Array + exception.first + else + exception + end + + backtrace =~ /(.+?):(\d+(?=:|\z))/ or return "" + + file, lineno = $1, $2.to_i + + return file, lineno + + #i = backtrace.rindex(':in') + #line = i ? line[0...i] : line + #relative_file(line) end + # Same as file_and_line, exception return file path is relative. + def file_line(exception) + file, lineno = file_and_line(exception) + return relative_file(file), lineno + end + + # Default trace count. This is the number of backtrace lines that + # will be provided on errors and failed assertions, unless otherwise + # overridden with ENV['trace']. + DEFAULT_TRACE_COUNT = 3 + + # Looks at ENV['trace'] to determine how much trace output to provide. + # If it is not set, or set to`false` or `off`, then the default trace count + # is used. If set to `0`, `true`, 'on' or 'all' then aa complete trace dump + # is provided. Otherwise the value is converted to an integer and that many + # line of trace is given. + # + # @return [Integer, nil] trace count + def trace_count + cnt = ENV['trace'] + case cnt + when nil, 'false', 'off' + DEFAULT_TRACE_COUNT + when 0, 'all', 'true', 'on' + nil + else + Integer(cnt) + end + end + + # + def relative_file(file) + pwd = Dir.pwd + idx = (0...pwd.size).find do |i| + file[i,1] != pwd[i,1] + end + file[(idx || 0)..-1] + end end end end