valo-rcov 0.8.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/BLURB +111 -0
- data/LICENSE +53 -0
- data/Rakefile +88 -0
- data/THANKS +96 -0
- data/bin/rcov +500 -0
- data/doc/readme_for_api +41 -0
- data/doc/readme_for_emacs +64 -0
- data/doc/readme_for_rake +62 -0
- data/doc/readme_for_vim +47 -0
- data/editor-extensions/rcov.el +131 -0
- data/editor-extensions/rcov.vim +38 -0
- data/ext/rcovrt/1.8/callsite.c +216 -0
- data/ext/rcovrt/1.8/rcovrt.c +287 -0
- data/ext/rcovrt/1.9/callsite.c +234 -0
- data/ext/rcovrt/1.9/rcovrt.c +264 -0
- data/ext/rcovrt/extconf.rb +21 -0
- data/lib/rcov.rb +1009 -0
- data/lib/rcov/formatters.rb +15 -0
- data/lib/rcov/formatters/base_formatter.rb +168 -0
- data/lib/rcov/formatters/full_text_report.rb +55 -0
- data/lib/rcov/formatters/html_coverage.rb +255 -0
- data/lib/rcov/formatters/html_erb_template.rb +128 -0
- data/lib/rcov/formatters/text_coverage_diff.rb +199 -0
- data/lib/rcov/formatters/text_report.rb +36 -0
- data/lib/rcov/formatters/text_summary.rb +15 -0
- data/lib/rcov/lowlevel.rb +145 -0
- data/lib/rcov/rcovtask.rb +156 -0
- data/lib/rcov/templates/detail.html.erb +78 -0
- data/lib/rcov/templates/index.html.erb +76 -0
- data/lib/rcov/templates/screen.css +165 -0
- data/lib/rcov/version.rb +10 -0
- data/setup.rb +1588 -0
- data/test/assets/sample_01.rb +7 -0
- data/test/assets/sample_02.rb +5 -0
- data/test/assets/sample_03.rb +20 -0
- data/test/assets/sample_04.rb +10 -0
- data/test/assets/sample_05-new.rb +17 -0
- data/test/assets/sample_05-old.rb +13 -0
- data/test/assets/sample_05.rb +17 -0
- data/test/call_site_analyzer_test.rb +171 -0
- data/test/code_coverage_analyzer_test.rb +184 -0
- data/test/file_statistics_test.rb +471 -0
- data/test/functional_test.rb +89 -0
- data/test/turn_off_rcovrt.rb +4 -0
- metadata +107 -0
| @@ -0,0 +1,128 @@ | |
| 1 | 
            +
            module Rcov
         | 
| 2 | 
            +
              module Formatters
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                class HtmlErbTemplate
         | 
| 5 | 
            +
                  attr_accessor :local_variables 
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  def initialize(template_file, locals={})
         | 
| 8 | 
            +
                    require "erb"
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    template_path = File.expand_path("#{File.dirname(__FILE__)}/../templates/#{template_file}")
         | 
| 11 | 
            +
                    @template = ERB.new(File.read(template_path))
         | 
| 12 | 
            +
                    @local_variables = locals
         | 
| 13 | 
            +
                    @path_relativizer = Hash.new{|h,base|
         | 
| 14 | 
            +
                      # TODO: Waaaahhhhh?
         | 
| 15 | 
            +
                      h[base] = Pathname.new(base).cleanpath.to_s.gsub(%r{^\w:[/\\]}, "").gsub(/\./, "_").gsub(/[\\\/]/, "-") + ".html"
         | 
| 16 | 
            +
                    }
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def render
         | 
| 20 | 
            +
                    @template.result(get_binding)
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def relative_filename(path)
         | 
| 24 | 
            +
                    @path_relativizer[path]
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  #def create_cross_refs(filename, lineno, linetext)
         | 
| 28 | 
            +
                  #form = formatter
         | 
| 29 | 
            +
                  #return linetext unless @callsite_analyzer && @do_callsites
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  #ref_blocks = []
         | 
| 32 | 
            +
                  #form.send(:_get_defsites, ref_blocks, filename, lineno, "Calls", linetext) do |ref|
         | 
| 33 | 
            +
                  #if ref.file
         | 
| 34 | 
            +
                  #where = "at #{formatter.normalize_filename(ref.file)}:#{ref.line}"
         | 
| 35 | 
            +
                  #else
         | 
| 36 | 
            +
                  #where = "(C extension/core)"
         | 
| 37 | 
            +
                  #end
         | 
| 38 | 
            +
                  #CGI.escapeHTML("%7d   %s" % [ref.count, "#{ref.klass}##{ref.mid} " + where])
         | 
| 39 | 
            +
                  #end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  #form.send(:_get_callsites, ref_blocks, filename, lineno, "Called by", linetext) do |ref|
         | 
| 42 | 
            +
                  #r = "%7d   %s" % [ref.count, "#{formatter.normalize_filename(ref.file||'C code')}:#{ref.line} " + "in '#{ref.klass}##{ref.mid}'"]
         | 
| 43 | 
            +
                  #CGI.escapeHTML(r)
         | 
| 44 | 
            +
                  #end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  #create_cross_reference_block(linetext, ref_blocks)
         | 
| 47 | 
            +
                  #end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  #def create_cross_reference_block(linetext, ref_blocks)
         | 
| 50 | 
            +
                  #return linetext if ref_blocks.empty?
         | 
| 51 | 
            +
                  #ret = ""
         | 
| 52 | 
            +
                  #@cross_ref_idx ||= 0
         | 
| 53 | 
            +
                  #@known_files ||= formatter.sorted_file_pairs.map{|fname, finfo| formatter.normalize_filename(fname)}
         | 
| 54 | 
            +
                  #ret << %[<a class="crossref-toggle" href="#" onclick="toggleCode('XREF-#{@cross_ref_idx+=1}'); return false;">#{linetext}</a>]
         | 
| 55 | 
            +
                  #ret << %[<span class="cross-ref" id="XREF-#{@cross_ref_idx}">]
         | 
| 56 | 
            +
                  #ret << "\n"
         | 
| 57 | 
            +
                  #ref_blocks.each do |refs, toplabel, label_proc|
         | 
| 58 | 
            +
                  #unless !toplabel || toplabel.empty?
         | 
| 59 | 
            +
                  #ret << %!<span class="cross-ref-title">#{toplabel}</span>\n!
         | 
| 60 | 
            +
                  #end
         | 
| 61 | 
            +
                  #refs.each do |dst|
         | 
| 62 | 
            +
                  #dstfile = formatter.normalize_filename(dst.file) if dst.file
         | 
| 63 | 
            +
                  #dstline = dst.line
         | 
| 64 | 
            +
                  #label = label_proc.call(dst)
         | 
| 65 | 
            +
                  #if dst.file && @known_files.include?(dstfile)
         | 
| 66 | 
            +
                  #ret << %[<a href="#{formatter.mangle_filename(dstfile)}#line#{dstline}">#{label}</a>]
         | 
| 67 | 
            +
                  #else
         | 
| 68 | 
            +
                  #ret << label
         | 
| 69 | 
            +
                  #end
         | 
| 70 | 
            +
                  #ret << "\n"
         | 
| 71 | 
            +
                  #end
         | 
| 72 | 
            +
                  #end
         | 
| 73 | 
            +
                  #ret << "</span>"
         | 
| 74 | 
            +
                  #end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  def line_css(line_number)
         | 
| 77 | 
            +
                    case fileinfo.coverage[line_number]
         | 
| 78 | 
            +
                    when true
         | 
| 79 | 
            +
                      "marked"
         | 
| 80 | 
            +
                    when :inferred
         | 
| 81 | 
            +
                      "inferred"
         | 
| 82 | 
            +
                    else
         | 
| 83 | 
            +
                      "uncovered"
         | 
| 84 | 
            +
                    end
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
             | 
| 88 | 
            +
                  #def format_lines(file)
         | 
| 89 | 
            +
                  #result = ""
         | 
| 90 | 
            +
                  #last = nil
         | 
| 91 | 
            +
                  #end_of_span = ""
         | 
| 92 | 
            +
                  #format_line = "%#{file.num_lines.to_s.size}d"
         | 
| 93 | 
            +
                  #file.num_lines.times do |i|
         | 
| 94 | 
            +
                  #line = file.lines[i].chomp
         | 
| 95 | 
            +
                  #marked = file.coverage[i]
         | 
| 96 | 
            +
                  #count = file.counts[i]
         | 
| 97 | 
            +
                  #spanclass = span_class(file, marked, count)
         | 
| 98 | 
            +
                  #if spanclass != last
         | 
| 99 | 
            +
                  #result += end_of_span
         | 
| 100 | 
            +
                  #case spanclass
         | 
| 101 | 
            +
                  #when nil
         | 
| 102 | 
            +
                  #end_of_span = ""
         | 
| 103 | 
            +
                  #else
         | 
| 104 | 
            +
                  #result += %[<span class="#{spanclass}">]
         | 
| 105 | 
            +
                  #end_of_span = "</span>"
         | 
| 106 | 
            +
                  #end
         | 
| 107 | 
            +
                  #end
         | 
| 108 | 
            +
                  #result += %[<a name="line#{i+1}"></a>] + (format_line % (i+1)) +
         | 
| 109 | 
            +
                  #" " + create_cross_refs(file.name, i+1, CGI.escapeHTML(line)) + "\n"
         | 
| 110 | 
            +
                  #last = spanclass
         | 
| 111 | 
            +
                  #end
         | 
| 112 | 
            +
                  #result += end_of_span
         | 
| 113 | 
            +
                  #"<pre>#{result}</pre>"
         | 
| 114 | 
            +
                  #end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
             | 
| 117 | 
            +
                  def method_missing(key, *args)
         | 
| 118 | 
            +
                    local_variables.has_key?(key) ? local_variables[key] : super
         | 
| 119 | 
            +
                  end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                  def get_binding
         | 
| 122 | 
            +
                    binding 
         | 
| 123 | 
            +
                  end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
              end
         | 
| 128 | 
            +
            end
         | 
| @@ -0,0 +1,199 @@ | |
| 1 | 
            +
            module Rcov
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              class TextCoverageDiff < BaseFormatter # :nodoc:
         | 
| 4 | 
            +
                FORMAT_VERSION = [0, 1, 0]
         | 
| 5 | 
            +
                DEFAULT_OPTS = { :textmode => :coverage_diff, :coverage_diff_mode => :record,
         | 
| 6 | 
            +
                  :coverage_diff_file => "coverage.info", :diff_cmd => "diff", :comments_run_by_default => true }
         | 
| 7 | 
            +
                HUNK_HEADER = /@@ -\d+,\d+ \+(\d+),(\d+) @@/
         | 
| 8 | 
            +
              
         | 
| 9 | 
            +
                def SERIALIZER
         | 
| 10 | 
            +
                  # mfp> this was going to be YAML but I caught it failing at basic
         | 
| 11 | 
            +
                  # round-tripping, turning "\n" into "" and corrupting the data, so
         | 
| 12 | 
            +
                  # it must be Marshal for now
         | 
| 13 | 
            +
                  Marshal
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def initialize(opts = {})
         | 
| 17 | 
            +
                  options = DEFAULT_OPTS.clone.update(opts)
         | 
| 18 | 
            +
                  @textmode = options[:textmode]
         | 
| 19 | 
            +
                  @color = options[:color]
         | 
| 20 | 
            +
                  @mode = options[:coverage_diff_mode]
         | 
| 21 | 
            +
                  @state_file = options[:coverage_diff_file]
         | 
| 22 | 
            +
                  @diff_cmd = options[:diff_cmd]
         | 
| 23 | 
            +
                  @gcc_output = options[:gcc_output]
         | 
| 24 | 
            +
                  super(options)
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def execute
         | 
| 28 | 
            +
                  case @mode
         | 
| 29 | 
            +
                  when :record
         | 
| 30 | 
            +
                    record_state
         | 
| 31 | 
            +
                  when :compare
         | 
| 32 | 
            +
                    compare_state
         | 
| 33 | 
            +
                  else
         | 
| 34 | 
            +
                    raise "Unknown TextCoverageDiff mode: #{mode.inspect}."
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def record_state
         | 
| 39 | 
            +
                  state = {}
         | 
| 40 | 
            +
                  each_file_pair_sorted do |filename, fileinfo|
         | 
| 41 | 
            +
                    state[filename] = {:lines    => SCRIPT_LINES__[filename], :coverage => fileinfo.coverage.to_a,:counts   => fileinfo.counts}
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                  File.open(@state_file, "w") do |f|
         | 
| 44 | 
            +
                    self.SERIALIZER.dump([FORMAT_VERSION, state], f)
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                rescue
         | 
| 47 | 
            +
                  $stderr.puts <<-EOF
         | 
| 48 | 
            +
                  Couldn't save coverage data to #{@state_file}.
         | 
| 49 | 
            +
                  EOF
         | 
| 50 | 
            +
                end                         # '
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                require 'tempfile'
         | 
| 53 | 
            +
                def compare_state
         | 
| 54 | 
            +
                  return unless verify_diff_available
         | 
| 55 | 
            +
                  begin
         | 
| 56 | 
            +
                    format, prev_state = File.open(@state_file){|f| self.SERIALIZER.load(f) }
         | 
| 57 | 
            +
                  rescue
         | 
| 58 | 
            +
                    $stderr.puts <<-EOF
         | 
| 59 | 
            +
                    Couldn't load coverage data from #{@state_file}.
         | 
| 60 | 
            +
                    EOF
         | 
| 61 | 
            +
                    return              # '
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
                  if !(Array === format) or
         | 
| 64 | 
            +
                    FORMAT_VERSION[0] != format[0] || FORMAT_VERSION[1] < format[1]
         | 
| 65 | 
            +
                    $stderr.puts <<-EOF
         | 
| 66 | 
            +
                    Couldn't load coverage data from #{@state_file}.
         | 
| 67 | 
            +
                    The file is saved in the format  #{format.inspect[0..20]}.
         | 
| 68 | 
            +
                    This rcov executable understands #{FORMAT_VERSION.inspect}.
         | 
| 69 | 
            +
                    EOF
         | 
| 70 | 
            +
                    return              # '
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
                  each_file_pair_sorted do |filename, fileinfo|
         | 
| 73 | 
            +
                    old_data = Tempfile.new("#{mangle_filename(filename)}-old")
         | 
| 74 | 
            +
                    new_data = Tempfile.new("#{mangle_filename(filename)}-new")
         | 
| 75 | 
            +
                    if prev_state.has_key? filename
         | 
| 76 | 
            +
                      old_code, old_cov = prev_state[filename].values_at(:lines, :coverage)
         | 
| 77 | 
            +
                      old_code.each_with_index do |line, i|
         | 
| 78 | 
            +
                        prefix = old_cov[i] ? "   " : "!! "
         | 
| 79 | 
            +
                        old_data.write "#{prefix}#{line}"
         | 
| 80 | 
            +
                      end
         | 
| 81 | 
            +
                    else
         | 
| 82 | 
            +
                      old_data.write ""
         | 
| 83 | 
            +
                    end
         | 
| 84 | 
            +
                    old_data.close
         | 
| 85 | 
            +
                    SCRIPT_LINES__[filename].each_with_index do |line, i|
         | 
| 86 | 
            +
                      prefix = fileinfo.coverage[i] ? "   " : "!! "
         | 
| 87 | 
            +
                      new_data.write "#{prefix}#{line}"
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
                    new_data.close
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    diff = `#{@diff_cmd} -u "#{old_data.path}" "#{new_data.path}"`
         | 
| 92 | 
            +
                    new_uncovered_hunks = process_unified_diff(filename, diff)
         | 
| 93 | 
            +
                    old_data.close!
         | 
| 94 | 
            +
                    new_data.close!
         | 
| 95 | 
            +
                    display_hunks(filename, new_uncovered_hunks)
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                def display_hunks(filename, hunks)
         | 
| 100 | 
            +
                  return if hunks.empty?
         | 
| 101 | 
            +
                  puts
         | 
| 102 | 
            +
                  puts "=" * 80
         | 
| 103 | 
            +
                  puts "!!!!! Uncovered code introduced in #{filename}"
         | 
| 104 | 
            +
                  
         | 
| 105 | 
            +
                  hunks.each do |offset, lines|
         | 
| 106 | 
            +
                    if @gcc_output
         | 
| 107 | 
            +
                      lines.each_with_index do |line,i|
         | 
| 108 | 
            +
                        lineno = offset + i
         | 
| 109 | 
            +
                        flag = (/^!! / !~ line) ? "-" : ":"
         | 
| 110 | 
            +
                        prefix = "#{filename}#{flag}#{lineno}#{flag}"
         | 
| 111 | 
            +
                        puts "#{prefix}#{line[3..-1]}"
         | 
| 112 | 
            +
                      end
         | 
| 113 | 
            +
                    elsif @color
         | 
| 114 | 
            +
                      puts "### #{filename}:#{offset}"
         | 
| 115 | 
            +
                      lines.each do |line|
         | 
| 116 | 
            +
                        prefix = (/^!! / !~ line) ? "\e[32;40m" : "\e[31;40m"
         | 
| 117 | 
            +
                        puts "#{prefix}#{line[3..-1].chomp}\e[37;40m"
         | 
| 118 | 
            +
                      end
         | 
| 119 | 
            +
                    else
         | 
| 120 | 
            +
                      puts "### #{filename}:#{offset}"
         | 
| 121 | 
            +
                      puts lines
         | 
| 122 | 
            +
                    end
         | 
| 123 | 
            +
                  end
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                def verify_diff_available
         | 
| 127 | 
            +
                  old_stderr = STDERR.dup
         | 
| 128 | 
            +
                  old_stdout = STDOUT.dup
         | 
| 129 | 
            +
                  # TODO: should use /dev/null or NUL(?), but I don't want to add the
         | 
| 130 | 
            +
                  # win32 check right now
         | 
| 131 | 
            +
                  new_stderr = Tempfile.new("rcov_check_diff")
         | 
| 132 | 
            +
                  STDERR.reopen new_stderr.path
         | 
| 133 | 
            +
                  STDOUT.reopen new_stderr.path
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                  retval = system "#{@diff_cmd} --version"
         | 
| 136 | 
            +
                  unless retval
         | 
| 137 | 
            +
            old_stderr.puts <<EOF
         | 
| 138 | 
            +
             | 
| 139 | 
            +
              The '#{@diff_cmd}' executable seems not to be available.
         | 
| 140 | 
            +
              You can specify which diff executable should be used with --diff-cmd.
         | 
| 141 | 
            +
              If your system doesn't have one, you might want to use Diff::LCS's:
         | 
| 142 | 
            +
              gem install diff-lcs
         | 
| 143 | 
            +
              and use --diff-cmd=ldiff.
         | 
| 144 | 
            +
            EOF
         | 
| 145 | 
            +
                    return false
         | 
| 146 | 
            +
                  end
         | 
| 147 | 
            +
                  true
         | 
| 148 | 
            +
                ensure
         | 
| 149 | 
            +
                  STDOUT.reopen old_stdout
         | 
| 150 | 
            +
                  STDERR.reopen old_stderr
         | 
| 151 | 
            +
                  new_stderr.close!
         | 
| 152 | 
            +
                end
         | 
| 153 | 
            +
             | 
| 154 | 
            +
             | 
| 155 | 
            +
                def process_unified_diff(filename, diff)
         | 
| 156 | 
            +
                  current_hunk = []
         | 
| 157 | 
            +
                  current_hunk_start = 0
         | 
| 158 | 
            +
                  keep_current_hunk = false
         | 
| 159 | 
            +
                  state = :init
         | 
| 160 | 
            +
                  interesting_hunks = []
         | 
| 161 | 
            +
                  diff.each_with_index do |line, i|
         | 
| 162 | 
            +
                    #puts "#{state} %5d #{line}" % i
         | 
| 163 | 
            +
                    case state
         | 
| 164 | 
            +
                    when :init
         | 
| 165 | 
            +
                      if md = HUNK_HEADER.match(line)
         | 
| 166 | 
            +
                        current_hunk = []
         | 
| 167 | 
            +
                        current_hunk_start = md[1].to_i
         | 
| 168 | 
            +
                        state = :body
         | 
| 169 | 
            +
                      end
         | 
| 170 | 
            +
                    when :body
         | 
| 171 | 
            +
                      case line
         | 
| 172 | 
            +
                      when HUNK_HEADER
         | 
| 173 | 
            +
                        new_start = $1.to_i
         | 
| 174 | 
            +
                        if keep_current_hunk
         | 
| 175 | 
            +
                          interesting_hunks << [current_hunk_start, current_hunk]
         | 
| 176 | 
            +
                        end
         | 
| 177 | 
            +
                        current_hunk_start = new_start
         | 
| 178 | 
            +
                        current_hunk = []
         | 
| 179 | 
            +
                        keep_current_hunk = false
         | 
| 180 | 
            +
                      when /^-/
         | 
| 181 | 
            +
                        # ignore
         | 
| 182 | 
            +
                      when /^\+!! /
         | 
| 183 | 
            +
                        keep_current_hunk = true
         | 
| 184 | 
            +
                        current_hunk << line[1..-1]
         | 
| 185 | 
            +
                      else
         | 
| 186 | 
            +
                        current_hunk << line[1..-1]
         | 
| 187 | 
            +
                      end
         | 
| 188 | 
            +
                    end
         | 
| 189 | 
            +
                  end
         | 
| 190 | 
            +
                  if keep_current_hunk
         | 
| 191 | 
            +
                    interesting_hunks << [current_hunk_start, current_hunk]
         | 
| 192 | 
            +
                  end
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                  interesting_hunks
         | 
| 195 | 
            +
                end
         | 
| 196 | 
            +
              
         | 
| 197 | 
            +
              end
         | 
| 198 | 
            +
             | 
| 199 | 
            +
            end
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            module Rcov
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              class TextReport < TextSummary # :nodoc:
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def execute
         | 
| 6 | 
            +
                  print_lines
         | 
| 7 | 
            +
                  print_header
         | 
| 8 | 
            +
                  print_lines
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  each_file_pair_sorted do |fname, finfo|
         | 
| 11 | 
            +
                    name = fname.size < 52 ? fname : "..." + fname[-48..-1]
         | 
| 12 | 
            +
                    print_info(name, finfo.num_lines, finfo.num_code_lines,
         | 
| 13 | 
            +
                    finfo.code_coverage)
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  print_lines
         | 
| 17 | 
            +
                  print_info("Total", num_lines, num_code_lines, code_coverage)
         | 
| 18 | 
            +
                  print_lines
         | 
| 19 | 
            +
                  puts summary
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def print_info(name, lines, loc, coverage)
         | 
| 23 | 
            +
                  puts "|%-51s | %5d | %5d | %5.1f%% |" % [name, lines, loc, 100 * coverage]
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def print_lines
         | 
| 27 | 
            +
                  puts "+----------------------------------------------------+-------+-------+--------+"
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def print_header
         | 
| 31 | 
            +
                  puts "|                  File                              | Lines |  LOC  |  COV   |"
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            end
         | 
| @@ -0,0 +1,145 @@ | |
| 1 | 
            +
            # rcov Copyright (c) 2004-2006 Mauricio Fernandez <mfp@acm.org>
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # See LEGAL and LICENSE for licensing information.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require 'rcov/version'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module Rcov
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              # RCOV__ performs the low-level tracing of the execution, gathering code
         | 
| 10 | 
            +
              # coverage information in the process. The C core made available through the
         | 
| 11 | 
            +
              # rcovrt extension will be used if possible. Otherwise the functionality
         | 
| 12 | 
            +
              # will be emulated using set_trace_func, but this is very expensive and
         | 
| 13 | 
            +
              # will fail if other libraries (e.g. breakpoint) change the trace_func.
         | 
| 14 | 
            +
              #
         | 
| 15 | 
            +
              # Do not use this module; it is very low-level and subject to frequent
         | 
| 16 | 
            +
              # changes. Rcov::CodeCoverageAnalyzer offers a much more convenient and
         | 
| 17 | 
            +
              # stable interface.
         | 
| 18 | 
            +
            module RCOV__
         | 
| 19 | 
            +
              COVER = {}
         | 
| 20 | 
            +
              CALLSITES = {}
         | 
| 21 | 
            +
              DEFSITES = {}
         | 
| 22 | 
            +
              pure_ruby_impl_needed = true
         | 
| 23 | 
            +
              unless defined? $rcov_do_not_use_rcovrt 
         | 
| 24 | 
            +
                begin
         | 
| 25 | 
            +
                  require 'rcovrt'
         | 
| 26 | 
            +
                  abi = [0,0,0]
         | 
| 27 | 
            +
                  begin
         | 
| 28 | 
            +
                    abi = RCOV__.ABI
         | 
| 29 | 
            +
                    raise if abi[0] != RCOVRT_ABI[0] || abi[1] < RCOVRT_ABI[1]
         | 
| 30 | 
            +
                    pure_ruby_impl_needed = false
         | 
| 31 | 
            +
                  rescue
         | 
| 32 | 
            +
                    $stderr.puts <<-EOF
         | 
| 33 | 
            +
            The rcovrt extension I found was built for a different version of rcov.
         | 
| 34 | 
            +
            The required ABI is:              #{RCOVRT_ABI.join(".")}
         | 
| 35 | 
            +
            Your current rcovrt extension is: #{abi.join(".")}
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            Please delete rcovrt.{so,bundle,dll,...} and install the required one.
         | 
| 38 | 
            +
                    EOF
         | 
| 39 | 
            +
                    raise LoadError
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                rescue LoadError
         | 
| 42 | 
            +
                  $stderr.puts <<-EOF
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            Since the rcovrt extension couldn't be loaded, rcov will run in pure-Ruby
         | 
| 45 | 
            +
            mode, which is about two orders of magnitude slower.
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            If you're on win32, you can find a pre-built extension (usable with recent
         | 
| 48 | 
            +
            One Click Installer and mswin32 builds) at http://eigenclass.org/hiki.rb?rcov .
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  EOF
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              if pure_ruby_impl_needed
         | 
| 55 | 
            +
                methods = %w[install_coverage_hook remove_coverage_hook reset_coverage 
         | 
| 56 | 
            +
                             install_callsite_hook remove_callsite_hook reset_callsite 
         | 
| 57 | 
            +
                             generate_coverage_info generate_callsite_info]
         | 
| 58 | 
            +
                sklass = class << self; self end
         | 
| 59 | 
            +
                (methods & sklass.instance_methods).each do |meth|
         | 
| 60 | 
            +
                  sklass.class_eval{ remove_method meth }
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
                
         | 
| 63 | 
            +
                @coverage_hook_activated = @callsite_hook_activated = false
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                def self.install_coverage_hook # :nodoc:
         | 
| 66 | 
            +
                  install_common_hook
         | 
| 67 | 
            +
                  @coverage_hook_activated = true
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
                
         | 
| 70 | 
            +
                def self.install_callsite_hook # :nodoc:
         | 
| 71 | 
            +
                  install_common_hook
         | 
| 72 | 
            +
                  @callsite_hook_activated = true
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
                
         | 
| 75 | 
            +
                def self.install_common_hook # :nodoc:
         | 
| 76 | 
            +
                  set_trace_func lambda {|event, file, line, id, binding, klass|
         | 
| 77 | 
            +
                    next unless SCRIPT_LINES__.has_key? file
         | 
| 78 | 
            +
                    case event
         | 
| 79 | 
            +
                    when 'call'
         | 
| 80 | 
            +
                      if @callsite_hook_activated
         | 
| 81 | 
            +
                        receiver = eval("self", binding)
         | 
| 82 | 
            +
                        klass = class << klass; self end unless klass === receiver
         | 
| 83 | 
            +
                        begin
         | 
| 84 | 
            +
                          DEFSITES[[klass.to_s, id.to_s]] = [file, line]
         | 
| 85 | 
            +
                        rescue Exception
         | 
| 86 | 
            +
                        end
         | 
| 87 | 
            +
                        caller_arr = self.format_backtrace_array(caller[1,1])
         | 
| 88 | 
            +
                        begin
         | 
| 89 | 
            +
                          hash = CALLSITES[[klass.to_s, id.to_s]] ||= {}
         | 
| 90 | 
            +
                          hash[caller_arr] ||= 0
         | 
| 91 | 
            +
                          hash[caller_arr] += 1
         | 
| 92 | 
            +
                          #puts "#{event} #{file} #{line} #{klass.inspect} " +
         | 
| 93 | 
            +
                          #     "#{klass.object_id} #{id} #{eval('self', binding)}"
         | 
| 94 | 
            +
                        rescue Exception
         | 
| 95 | 
            +
                        end
         | 
| 96 | 
            +
                      end
         | 
| 97 | 
            +
                    when 'c-call', 'c-return', 'class'
         | 
| 98 | 
            +
                      return
         | 
| 99 | 
            +
                    end
         | 
| 100 | 
            +
                    if @coverage_hook_activated
         | 
| 101 | 
            +
                      COVER[file] ||= Array.new(SCRIPT_LINES__[file].size, 0)
         | 
| 102 | 
            +
                      COVER[file][line - 1] ||= 0
         | 
| 103 | 
            +
                      COVER[file][line - 1] += 1
         | 
| 104 | 
            +
                    end
         | 
| 105 | 
            +
                  }
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                def self.remove_coverage_hook # :nodoc:
         | 
| 109 | 
            +
                  @coverage_hook_activated = false
         | 
| 110 | 
            +
                  set_trace_func(nil) if !@callsite_hook_activated
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
                
         | 
| 113 | 
            +
                def self.remove_callsite_hook # :nodoc:
         | 
| 114 | 
            +
                  @callsite_hook_activated = false
         | 
| 115 | 
            +
                  set_trace_func(nil) if !@coverage_hook_activated
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                def self.reset_coverage # :nodoc:
         | 
| 119 | 
            +
                  COVER.replace({})
         | 
| 120 | 
            +
                end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                def self.reset_callsite # :nodoc:
         | 
| 123 | 
            +
                  CALLSITES.replace({})
         | 
| 124 | 
            +
                  DEFSITES.replace({})
         | 
| 125 | 
            +
                end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                def self.generate_coverage_info # :nodoc:
         | 
| 128 | 
            +
                  Marshal.load(Marshal.dump(COVER))
         | 
| 129 | 
            +
                end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                def self.generate_callsite_info # :nodoc:
         | 
| 132 | 
            +
                  [CALLSITES, DEFSITES]
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                def self.format_backtrace_array(backtrace)
         | 
| 136 | 
            +
                  backtrace.map do |line|
         | 
| 137 | 
            +
                    md = /^([^:]*)(?::(\d+)(?::in `(.*)'))?/.match(line)
         | 
| 138 | 
            +
                    raise "Bad backtrace format" unless md
         | 
| 139 | 
            +
                    [nil, md[3] ? md[3].to_sym : nil, md[1], (md[2] || '').to_i]
         | 
| 140 | 
            +
                  end
         | 
| 141 | 
            +
                end
         | 
| 142 | 
            +
              end
         | 
| 143 | 
            +
            end 
         | 
| 144 | 
            +
             | 
| 145 | 
            +
            end 
         |