katsuya-rcov 0.9.7.1
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 +103 -0
- data/THANKS +110 -0
- data/bin/rcov +514 -0
- data/doc/readme_for_api.markdown +22 -0
- data/doc/readme_for_emacs.markdown +52 -0
- data/doc/readme_for_rake.markdown +51 -0
- data/doc/readme_for_vim.markdown +34 -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 +294 -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 +33 -0
- data/lib/rcov/call_site_analyzer.rb +225 -0
- data/lib/rcov/code_coverage_analyzer.rb +271 -0
- data/lib/rcov/coverage_info.rb +36 -0
- data/lib/rcov/differential_analyzer.rb +116 -0
- data/lib/rcov/file_statistics.rb +355 -0
- data/lib/rcov/formatters.rb +13 -0
- data/lib/rcov/formatters/base_formatter.rb +174 -0
- data/lib/rcov/formatters/failure_report.rb +15 -0
- data/lib/rcov/formatters/full_text_report.rb +48 -0
- data/lib/rcov/formatters/html_coverage.rb +274 -0
- data/lib/rcov/formatters/html_erb_template.rb +62 -0
- data/lib/rcov/formatters/text_coverage_diff.rb +193 -0
- data/lib/rcov/formatters/text_report.rb +32 -0
- data/lib/rcov/formatters/text_summary.rb +11 -0
- data/lib/rcov/lowlevel.rb +146 -0
- data/lib/rcov/rcovtask.rb +155 -0
- data/lib/rcov/templates/detail.html.erb +64 -0
- data/lib/rcov/templates/index.html.erb +93 -0
- data/lib/rcov/templates/jquery-1.3.2.min.js +19 -0
- data/lib/rcov/templates/jquery.tablesorter.min.js +15 -0
- data/lib/rcov/templates/print.css +12 -0
- data/lib/rcov/templates/rcov.js +42 -0
- data/lib/rcov/templates/screen.css +270 -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/assets/sample_06.rb +8 -0
- data/test/call_site_analyzer_test.rb +171 -0
- data/test/code_coverage_analyzer_test.rb +219 -0
- data/test/expected_coverage/diff-gcc-all.out +7 -0
- data/test/expected_coverage/diff-gcc-diff.out +11 -0
- data/test/expected_coverage/diff-gcc-original.out +5 -0
- data/test/expected_coverage/diff-no-color.out +12 -0
- data/test/expected_coverage/diff.out +12 -0
- data/test/expected_coverage/gcc-text.out +10 -0
- data/test/expected_coverage/sample_03_rb.html +651 -0
- data/test/expected_coverage/sample_03_rb.rb +28 -0
- data/test/expected_coverage/sample_04_rb.html +641 -0
- data/test/file_statistics_test.rb +471 -0
- data/test/functional_test.rb +91 -0
- data/test/test_helper.rb +4 -0
- data/test/turn_off_rcovrt.rb +4 -0
- metadata +126 -0
| @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            require 'rcov/formatters/html_erb_template'
         | 
| 2 | 
            +
            require 'rcov/formatters/base_formatter'
         | 
| 3 | 
            +
            require 'rcov/formatters/text_summary'
         | 
| 4 | 
            +
            require 'rcov/formatters/text_report'
         | 
| 5 | 
            +
            require 'rcov/formatters/text_coverage_diff'
         | 
| 6 | 
            +
            require 'rcov/formatters/full_text_report'
         | 
| 7 | 
            +
            require 'rcov/formatters/html_coverage'
         | 
| 8 | 
            +
            require 'rcov/formatters/failure_report'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            module Rcov
         | 
| 11 | 
            +
              module Formatters
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
| @@ -0,0 +1,174 @@ | |
| 1 | 
            +
            module Rcov
         | 
| 2 | 
            +
              class BaseFormatter # :nodoc:
         | 
| 3 | 
            +
                require 'pathname'
         | 
| 4 | 
            +
                require 'rbconfig'
         | 
| 5 | 
            +
                RCOV_IGNORE_REGEXPS = [ /\A#{Regexp.escape(Pathname.new(::RbConfig::CONFIG['libdir']).cleanpath.to_s)}/, 
         | 
| 6 | 
            +
                                        /\btc_[^.]*.rb/, 
         | 
| 7 | 
            +
                                        /_test\.rb\z/, 
         | 
| 8 | 
            +
                                        /\btest\//, 
         | 
| 9 | 
            +
                                        /\bvendor\//, 
         | 
| 10 | 
            +
                                        /\A#{Regexp.escape(__FILE__)}\z/
         | 
| 11 | 
            +
                                      ]
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                DEFAULT_OPTS = { :ignore => RCOV_IGNORE_REGEXPS, :sort => :name, :sort_reverse => false,
         | 
| 14 | 
            +
                                 :output_threshold => 101, :dont_ignore => [], :callsite_analyzer => nil, \
         | 
| 15 | 
            +
                                 :comments_run_by_default => false }
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def initialize(opts = {})
         | 
| 18 | 
            +
                  options = DEFAULT_OPTS.clone.update(opts)
         | 
| 19 | 
            +
                  @failure_threshold = options[:failure_threshold]
         | 
| 20 | 
            +
                  @files = {}
         | 
| 21 | 
            +
                  @ignore_files = options[:ignore]
         | 
| 22 | 
            +
                  @dont_ignore_files = options[:dont_ignore]
         | 
| 23 | 
            +
                  @sort_criterium = case options[:sort]
         | 
| 24 | 
            +
                  when :loc then lambda{|fname, finfo| finfo.num_code_lines}
         | 
| 25 | 
            +
                  when :coverage then lambda{|fname, finfo| finfo.code_coverage}
         | 
| 26 | 
            +
                  else lambda { |fname, finfo| fname }
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                  @sort_reverse = options[:sort_reverse]
         | 
| 29 | 
            +
                  @output_threshold = options[:output_threshold]
         | 
| 30 | 
            +
                  @callsite_analyzer = options[:callsite_analyzer]
         | 
| 31 | 
            +
                  @comments_run_by_default = options[:comments_run_by_default]
         | 
| 32 | 
            +
                  @callsite_index = nil
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  @mangle_filename = Hash.new{|h,base|
         | 
| 35 | 
            +
                    h[base] = Pathname.new(base).cleanpath.to_s.gsub(%r{^\w:[/\\]}, "").gsub(/\./, "_").gsub(/[\\\/]/, "-") + ".html"
         | 
| 36 | 
            +
                  }
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def add_file(filename, lines, coverage, counts)
         | 
| 40 | 
            +
                  old_filename = filename
         | 
| 41 | 
            +
                  filename = normalize_filename(filename)
         | 
| 42 | 
            +
                  SCRIPT_LINES__[filename] = SCRIPT_LINES__[old_filename]
         | 
| 43 | 
            +
                  if @ignore_files.any?{|x| x === filename} &&
         | 
| 44 | 
            +
                    !@dont_ignore_files.any?{|x| x === filename}
         | 
| 45 | 
            +
                    return nil
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                  if @files[filename]
         | 
| 48 | 
            +
                    @files[filename].merge(lines, coverage, counts)
         | 
| 49 | 
            +
                  else
         | 
| 50 | 
            +
                    @files[filename] = FileStatistics.new(filename, lines, counts,
         | 
| 51 | 
            +
                    @comments_run_by_default)
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def normalize_filename(filename)
         | 
| 56 | 
            +
                  File.expand_path(filename).gsub(/^#{Regexp.escape(Dir.getwd)}\//, '')
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def mangle_filename(base)
         | 
| 60 | 
            +
                  @mangle_filename[base]
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def each_file_pair_sorted(&b)
         | 
| 64 | 
            +
                  return sorted_file_pairs unless block_given?
         | 
| 65 | 
            +
                  sorted_file_pairs.each(&b)
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                def sorted_file_pairs
         | 
| 69 | 
            +
                  pairs = @files.sort_by do |fname, finfo|
         | 
| 70 | 
            +
                    @sort_criterium.call(fname, finfo)
         | 
| 71 | 
            +
                  end.select{|_, finfo| 100 * finfo.code_coverage < @output_threshold}
         | 
| 72 | 
            +
                  @sort_reverse ? pairs.reverse : pairs
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                def total_coverage
         | 
| 76 | 
            +
                  lines = 0
         | 
| 77 | 
            +
                  total = 0.0
         | 
| 78 | 
            +
                  @files.each do |k,f|
         | 
| 79 | 
            +
                    total += f.num_lines * f.total_coverage
         | 
| 80 | 
            +
                    lines += f.num_lines
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                  return 0 if lines == 0
         | 
| 83 | 
            +
                  total / lines
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                def code_coverage
         | 
| 87 | 
            +
                  lines = 0
         | 
| 88 | 
            +
                  total = 0.0
         | 
| 89 | 
            +
                  @files.each do |k,f|
         | 
| 90 | 
            +
                    total += f.num_code_lines * f.code_coverage
         | 
| 91 | 
            +
                    lines += f.num_code_lines
         | 
| 92 | 
            +
                  end
         | 
| 93 | 
            +
                  return 0 if lines == 0
         | 
| 94 | 
            +
                  total / lines
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                def num_code_lines
         | 
| 98 | 
            +
                  lines = 0
         | 
| 99 | 
            +
                  @files.each{|k, f| lines += f.num_code_lines }
         | 
| 100 | 
            +
                  lines
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                def num_lines
         | 
| 104 | 
            +
                  lines = 0
         | 
| 105 | 
            +
                  @files.each{|k, f| lines += f.num_lines }
         | 
| 106 | 
            +
                  lines
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                private
         | 
| 110 | 
            +
                
         | 
| 111 | 
            +
                def cross_references_for(filename, lineno)
         | 
| 112 | 
            +
                  return nil unless @callsite_analyzer
         | 
| 113 | 
            +
                  @callsite_index ||= build_callsite_index
         | 
| 114 | 
            +
                  @callsite_index[normalize_filename(filename)][lineno]
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                def reverse_cross_references_for(filename, lineno)
         | 
| 118 | 
            +
                  return nil unless @callsite_analyzer
         | 
| 119 | 
            +
                  @callsite_reverse_index ||= build_reverse_callsite_index
         | 
| 120 | 
            +
                  @callsite_reverse_index[normalize_filename(filename)][lineno]
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                def build_callsite_index
         | 
| 124 | 
            +
                  index = Hash.new{|h,k| h[k] = {}}
         | 
| 125 | 
            +
                  @callsite_analyzer.analyzed_classes.each do |classname|
         | 
| 126 | 
            +
                    @callsite_analyzer.analyzed_methods(classname).each do |methname|
         | 
| 127 | 
            +
                      defsite = @callsite_analyzer.defsite(classname, methname)
         | 
| 128 | 
            +
                      index[normalize_filename(defsite.file)][defsite.line] =
         | 
| 129 | 
            +
                      @callsite_analyzer.callsites(classname, methname)
         | 
| 130 | 
            +
                    end
         | 
| 131 | 
            +
                  end
         | 
| 132 | 
            +
                  index
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                def build_reverse_callsite_index
         | 
| 136 | 
            +
                  index = Hash.new{|h,k| h[k] = {}}
         | 
| 137 | 
            +
                  @callsite_analyzer.analyzed_classes.each do |classname|
         | 
| 138 | 
            +
                    @callsite_analyzer.analyzed_methods(classname).each do |methname|
         | 
| 139 | 
            +
                      callsites = @callsite_analyzer.callsites(classname, methname)
         | 
| 140 | 
            +
                      defsite = @callsite_analyzer.defsite(classname, methname)
         | 
| 141 | 
            +
                      callsites.each_pair do |callsite, count|
         | 
| 142 | 
            +
                        next unless callsite.file
         | 
| 143 | 
            +
                        fname = normalize_filename(callsite.file)
         | 
| 144 | 
            +
                        (index[fname][callsite.line] ||= []) << [classname, methname, defsite, count]
         | 
| 145 | 
            +
                      end
         | 
| 146 | 
            +
                    end
         | 
| 147 | 
            +
                  end
         | 
| 148 | 
            +
                  index
         | 
| 149 | 
            +
                end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                class XRefHelper < Struct.new(:file, :line, :klass, :mid, :count) # :nodoc:
         | 
| 152 | 
            +
                end
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                def _get_defsites(ref_blocks, filename, lineno, linetext, label, &format_call_ref)
         | 
| 155 | 
            +
                  if @do_cross_references and
         | 
| 156 | 
            +
                    (rev_xref = reverse_cross_references_for(filename, lineno))
         | 
| 157 | 
            +
                    refs = rev_xref.map do |classname, methodname, defsite, count|
         | 
| 158 | 
            +
                      XRefHelper.new(defsite.file, defsite.line, classname, methodname, count)
         | 
| 159 | 
            +
                    end.sort_by{|r| r.count}.reverse
         | 
| 160 | 
            +
                    ref_blocks << [refs, label, format_call_ref]
         | 
| 161 | 
            +
                  end
         | 
| 162 | 
            +
                end
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                def _get_callsites(ref_blocks, filename, lineno, linetext, label, &format_called_ref)
         | 
| 165 | 
            +
                  if @do_callsites and
         | 
| 166 | 
            +
                    (refs = cross_references_for(filename, lineno))
         | 
| 167 | 
            +
                    refs = refs.sort_by{|k,count| count}.map do |ref, count|
         | 
| 168 | 
            +
                      XRefHelper.new(ref.file, ref.line, ref.calling_class, ref.calling_method, count)
         | 
| 169 | 
            +
                    end.reverse
         | 
| 170 | 
            +
                    ref_blocks << [refs, label, format_called_ref]
         | 
| 171 | 
            +
                  end
         | 
| 172 | 
            +
                end
         | 
| 173 | 
            +
              end
         | 
| 174 | 
            +
            end
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            module Rcov
         | 
| 2 | 
            +
              class FailureReport < TextSummary # :nodoc:
         | 
| 3 | 
            +
                def execute
         | 
| 4 | 
            +
                  puts summary
         | 
| 5 | 
            +
                  coverage = code_coverage * 100
         | 
| 6 | 
            +
                  if coverage < @failure_threshold
         | 
| 7 | 
            +
                    puts "You failed to satisfy the coverage theshold of #{@failure_threshold}%"
         | 
| 8 | 
            +
                    exit(1)
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
                  if (coverage - @failure_threshold) > 3
         | 
| 11 | 
            +
                    puts "Your coverage has significantly increased over your threshold of #{@failure_threshold}. Please increase it."
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
            end
         | 
| @@ -0,0 +1,48 @@ | |
| 1 | 
            +
            module Rcov
         | 
| 2 | 
            +
              class FullTextReport < BaseFormatter # :nodoc:
         | 
| 3 | 
            +
                DEFAULT_OPTS = {:textmode => :coverage}
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(opts = {})
         | 
| 6 | 
            +
                  options = DEFAULT_OPTS.clone.update(opts)
         | 
| 7 | 
            +
                  @textmode = options[:textmode]
         | 
| 8 | 
            +
                  @color = options[:color]
         | 
| 9 | 
            +
                  super(options)
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def execute
         | 
| 13 | 
            +
                  each_file_pair_sorted do |filename, fileinfo|
         | 
| 14 | 
            +
                    puts "=" * 80
         | 
| 15 | 
            +
                    puts filename
         | 
| 16 | 
            +
                    puts "=" * 80
         | 
| 17 | 
            +
                    lines = SCRIPT_LINES__[filename]
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    unless lines
         | 
| 20 | 
            +
                      # try to get the source code from the global code coverage
         | 
| 21 | 
            +
                      # analyzer
         | 
| 22 | 
            +
                      re = /#{Regexp.escape(filename)}\z/
         | 
| 23 | 
            +
                      if $rcov_code_coverage_analyzer and
         | 
| 24 | 
            +
                        (data = $rcov_code_coverage_analyzer.data_matching(re))
         | 
| 25 | 
            +
                        lines = data[0]
         | 
| 26 | 
            +
                      end
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    (lines || []).each_with_index do |line, i|
         | 
| 30 | 
            +
                      case @textmode
         | 
| 31 | 
            +
                      when :counts
         | 
| 32 | 
            +
                        puts "%-70s| %6d" % [line.chomp[0,70], fileinfo.counts[i]]
         | 
| 33 | 
            +
                      when :gcc
         | 
| 34 | 
            +
                        puts "%s:%d:%s" % [filename, i+1, line.chomp] unless fileinfo.coverage[i]
         | 
| 35 | 
            +
                      when :coverage
         | 
| 36 | 
            +
                        if @color
         | 
| 37 | 
            +
                          prefix = fileinfo.coverage[i] ? "\e[32;40m" : "\e[31;40m"
         | 
| 38 | 
            +
                          puts "#{prefix}%s\e[37;40m" % line.chomp
         | 
| 39 | 
            +
                        else
         | 
| 40 | 
            +
                          prefix = fileinfo.coverage[i] ? "   " : "!! "
         | 
| 41 | 
            +
                          puts "#{prefix}#{line}"
         | 
| 42 | 
            +
                        end
         | 
| 43 | 
            +
                      end
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
            end
         | 
| @@ -0,0 +1,274 @@ | |
| 1 | 
            +
            module Rcov
         | 
| 2 | 
            +
              class HTMLCoverage < BaseFormatter # :nodoc:
         | 
| 3 | 
            +
                require 'fileutils'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                DEFAULT_OPTS = {:color => false, :fsr => 30, :destdir => "coverage",
         | 
| 6 | 
            +
                                :callsites => false, :cross_references => false,
         | 
| 7 | 
            +
                                :charset => nil }
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def initialize(opts = {})
         | 
| 10 | 
            +
                  options = DEFAULT_OPTS.clone.update(opts)
         | 
| 11 | 
            +
                  super(options)
         | 
| 12 | 
            +
                  @dest = options[:destdir]
         | 
| 13 | 
            +
                  @css = options[:css]
         | 
| 14 | 
            +
                  @color = options[:color]
         | 
| 15 | 
            +
                  @fsr = options[:fsr]
         | 
| 16 | 
            +
                  @do_callsites = options[:callsites]
         | 
| 17 | 
            +
                  @do_cross_references = options[:cross_references]
         | 
| 18 | 
            +
                  @span_class_index = 0
         | 
| 19 | 
            +
                  @charset = options[:charset]
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def execute
         | 
| 23 | 
            +
                  return if @files.empty?
         | 
| 24 | 
            +
                  FileUtils.mkdir_p @dest
         | 
| 25 | 
            +
                  
         | 
| 26 | 
            +
                  # Copy collaterals
         | 
| 27 | 
            +
                  ['screen.css','print.css','rcov.js','jquery-1.3.2.min.js','jquery.tablesorter.min.js'].each do |_file|
         | 
| 28 | 
            +
                    _src = File.expand_path("#{File.dirname(__FILE__)}/../templates/#{_file}")
         | 
| 29 | 
            +
                    FileUtils.cp(_src, File.join(@dest, "#{_file}"))
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  # Copy custom CSS, if any
         | 
| 33 | 
            +
                  if @css
         | 
| 34 | 
            +
                    begin
         | 
| 35 | 
            +
                      _src = File.expand_path("#{@dest}/../#{@css}")
         | 
| 36 | 
            +
                      FileUtils.cp(_src, File.join(@dest, "custom.css"))
         | 
| 37 | 
            +
                    rescue
         | 
| 38 | 
            +
                      @css = nil
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                  
         | 
| 42 | 
            +
                  create_index(File.join(@dest, "index.html"))
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  each_file_pair_sorted do |filename, fileinfo|
         | 
| 45 | 
            +
                    create_file(File.join(@dest, mangle_filename(filename)), fileinfo)
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                private
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                class SummaryFileInfo  # :nodoc:
         | 
| 52 | 
            +
                  def initialize(obj)
         | 
| 53 | 
            +
                    @o = obj 
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  def num_lines
         | 
| 57 | 
            +
                    @o.num_lines
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  def num_code_lines
         | 
| 61 | 
            +
                    @o.num_code_lines
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  def code_coverage
         | 
| 65 | 
            +
                    @o.code_coverage
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  def code_coverage_for_report
         | 
| 69 | 
            +
                    code_coverage * 100
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  def total_coverage
         | 
| 73 | 
            +
                    @o.total_coverage
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  def total_coverage_for_report
         | 
| 77 | 
            +
                    total_coverage * 100
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  def name
         | 
| 81 | 
            +
                    "TOTAL" 
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                def create_index(destname)
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  doc = Rcov::Formatters::HtmlErbTemplate.new('index.html.erb',
         | 
| 88 | 
            +
                    :project_name => project_name,
         | 
| 89 | 
            +
                    :generated_on => Time.now,
         | 
| 90 | 
            +
                    :css => @css,
         | 
| 91 | 
            +
                    :rcov => Rcov,
         | 
| 92 | 
            +
                    :formatter => self,
         | 
| 93 | 
            +
                    :output_threshold => @output_threshold,
         | 
| 94 | 
            +
                    :total => SummaryFileInfo.new(self),
         | 
| 95 | 
            +
                    :files => each_file_pair_sorted.map{|k,v| v}
         | 
| 96 | 
            +
                  )
         | 
| 97 | 
            +
                  File.open(destname, "w") { |f| f.puts doc.render }
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                def create_file(destfile, fileinfo)
         | 
| 101 | 
            +
                  doc = Rcov::Formatters::HtmlErbTemplate.new('detail.html.erb',
         | 
| 102 | 
            +
                    :project_name => project_name,
         | 
| 103 | 
            +
                    :rcov_page_title => fileinfo.name, 
         | 
| 104 | 
            +
                    :css => @css,
         | 
| 105 | 
            +
                    :generated_on => Time.now,
         | 
| 106 | 
            +
                    :rcov => Rcov,
         | 
| 107 | 
            +
                    :formatter => self,
         | 
| 108 | 
            +
                    :output_threshold => @output_threshold,
         | 
| 109 | 
            +
                    :fileinfo => fileinfo
         | 
| 110 | 
            +
                  )
         | 
| 111 | 
            +
                  File.open(destfile, "w")  { |f| f.puts doc.render }
         | 
| 112 | 
            +
                end
         | 
| 113 | 
            +
                
         | 
| 114 | 
            +
                private
         | 
| 115 | 
            +
                
         | 
| 116 | 
            +
                def project_name
         | 
| 117 | 
            +
                  Dir.pwd.split('/')[-1].split(/[^a-zA-Z0-9]/).map{|i| i.gsub(/[^a-zA-Z0-9]/,'').capitalize} * " " || ""
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
                
         | 
| 120 | 
            +
              end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
              class HTMLProfiling < HTMLCoverage # :nodoc:
         | 
| 123 | 
            +
                DEFAULT_OPTS = {:destdir => "profiling"}
         | 
| 124 | 
            +
                def initialize(opts = {})
         | 
| 125 | 
            +
                  options = DEFAULT_OPTS.clone.update(opts)
         | 
| 126 | 
            +
                  super(options)
         | 
| 127 | 
            +
                  @max_cache = {}
         | 
| 128 | 
            +
                  @median_cache = {}
         | 
| 129 | 
            +
                end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                def default_title
         | 
| 132 | 
            +
                  "Bogo-profile information"
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                def default_color
         | 
| 136 | 
            +
                  if @color
         | 
| 137 | 
            +
                    "rgb(179,205,255)"
         | 
| 138 | 
            +
                  else
         | 
| 139 | 
            +
                    "rgb(255, 255, 255)"
         | 
| 140 | 
            +
                  end
         | 
| 141 | 
            +
                end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                def output_color_table?
         | 
| 144 | 
            +
                  false
         | 
| 145 | 
            +
                end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                def span_class(sourceinfo, marked, count)
         | 
| 148 | 
            +
                  full_scale_range = @fsr # dB
         | 
| 149 | 
            +
                  nz_count = sourceinfo.counts.select{|x| x && x != 0}
         | 
| 150 | 
            +
                  nz_count << 1 # avoid div by 0
         | 
| 151 | 
            +
                  max = @max_cache[sourceinfo] ||= nz_count.max
         | 
| 152 | 
            +
                  #avg = @median_cache[sourceinfo] ||= 1.0 *
         | 
| 153 | 
            +
                  #    nz_count.inject{|a,b| a+b} / nz_count.size
         | 
| 154 | 
            +
                  median = @median_cache[sourceinfo] ||= 1.0 * nz_count.sort[nz_count.size/2]
         | 
| 155 | 
            +
                  max ||= 2
         | 
| 156 | 
            +
                  max = 2 if max == 1
         | 
| 157 | 
            +
                  if marked == true
         | 
| 158 | 
            +
                    count = 1 if !count || count == 0
         | 
| 159 | 
            +
                    idx = 50 + 1.0 * (500/full_scale_range) * Math.log(count/median) / Math.log(10)
         | 
| 160 | 
            +
                    idx = idx.to_i
         | 
| 161 | 
            +
                    idx = 0 if idx < 0
         | 
| 162 | 
            +
                    idx = 100 if idx > 100
         | 
| 163 | 
            +
                    "run#{idx}"
         | 
| 164 | 
            +
                  else
         | 
| 165 | 
            +
                    nil
         | 
| 166 | 
            +
                  end
         | 
| 167 | 
            +
                end
         | 
| 168 | 
            +
              end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
              class RubyAnnotation < BaseFormatter # :nodoc:
         | 
| 171 | 
            +
                DEFAULT_OPTS = { :destdir => "coverage" }
         | 
| 172 | 
            +
                def initialize(opts = {})
         | 
| 173 | 
            +
                  options = DEFAULT_OPTS.clone.update(opts)
         | 
| 174 | 
            +
                  super(options)
         | 
| 175 | 
            +
                  @dest = options[:destdir]
         | 
| 176 | 
            +
                  @do_callsites = true
         | 
| 177 | 
            +
                  @do_cross_references = true
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                  @mangle_filename = Hash.new{ |h,base|
         | 
| 180 | 
            +
                    h[base] = Pathname.new(base).cleanpath.to_s.gsub(%r{^\w:[/\\]}, "").gsub(/\./, "_").gsub(/[\\\/]/, "-") + ".rb"
         | 
| 181 | 
            +
                  }
         | 
| 182 | 
            +
                end
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                def execute
         | 
| 185 | 
            +
                  return if @files.empty?
         | 
| 186 | 
            +
                  FileUtils.mkdir_p @dest
         | 
| 187 | 
            +
                  each_file_pair_sorted do |filename, fileinfo|
         | 
| 188 | 
            +
                    create_file(File.join(@dest, mangle_filename(filename)), fileinfo)
         | 
| 189 | 
            +
                  end
         | 
| 190 | 
            +
                end
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                private
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                def format_lines(file)
         | 
| 195 | 
            +
                  result = ""
         | 
| 196 | 
            +
                  format_line = "%#{file.num_lines.to_s.size}d"
         | 
| 197 | 
            +
                  file.num_lines.times do |i|
         | 
| 198 | 
            +
                    line = file.lines[i].chomp
         | 
| 199 | 
            +
                    marked = file.coverage[i]
         | 
| 200 | 
            +
                    count = file.counts[i]
         | 
| 201 | 
            +
                    result << create_cross_refs(file.name, i+1, line, marked) + "\n"
         | 
| 202 | 
            +
                  end
         | 
| 203 | 
            +
                  result
         | 
| 204 | 
            +
                end
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                def create_cross_refs(filename, lineno, linetext, marked)
         | 
| 207 | 
            +
                  return linetext unless @callsite_analyzer && @do_callsites
         | 
| 208 | 
            +
                  ref_blocks = []
         | 
| 209 | 
            +
                  _get_defsites(ref_blocks, filename, lineno, linetext, ">>") do |ref|
         | 
| 210 | 
            +
                    if ref.file
         | 
| 211 | 
            +
                      ref.file.sub!(%r!^./!, '')
         | 
| 212 | 
            +
                      where = "at #{mangle_filename(ref.file)}:#{ref.line}"
         | 
| 213 | 
            +
                    else
         | 
| 214 | 
            +
                      where = "(C extension/core)"
         | 
| 215 | 
            +
                    end
         | 
| 216 | 
            +
                    "#{ref.klass}##{ref.mid} " + where + ""
         | 
| 217 | 
            +
                  end
         | 
| 218 | 
            +
                  _get_callsites(ref_blocks, filename, lineno, linetext, "<<") do |ref| # "
         | 
| 219 | 
            +
                    ref.file.sub!(%r!^./!, '')
         | 
| 220 | 
            +
                    "#{mangle_filename(ref.file||'C code')}:#{ref.line} " + "in #{ref.klass}##{ref.mid}"
         | 
| 221 | 
            +
                  end
         | 
| 222 | 
            +
             | 
| 223 | 
            +
                  create_cross_reference_block(linetext, ref_blocks, marked)
         | 
| 224 | 
            +
                end
         | 
| 225 | 
            +
             | 
| 226 | 
            +
                def create_cross_reference_block(linetext, ref_blocks, marked)
         | 
| 227 | 
            +
                  codelen = 75
         | 
| 228 | 
            +
                  if ref_blocks.empty?
         | 
| 229 | 
            +
                    if marked
         | 
| 230 | 
            +
                      return "%-#{codelen}s #o" % linetext
         | 
| 231 | 
            +
                    else
         | 
| 232 | 
            +
                      return linetext
         | 
| 233 | 
            +
                    end
         | 
| 234 | 
            +
                  end
         | 
| 235 | 
            +
                  ret = ""
         | 
| 236 | 
            +
                  @cross_ref_idx ||= 0
         | 
| 237 | 
            +
                  @known_files ||= sorted_file_pairs.map{|fname, finfo| normalize_filename(fname)}
         | 
| 238 | 
            +
                  ret << "%-#{codelen}s # " % linetext
         | 
| 239 | 
            +
                  ref_blocks.each do |refs, toplabel, label_proc|
         | 
| 240 | 
            +
                    unless !toplabel || toplabel.empty?
         | 
| 241 | 
            +
                      ret << toplabel << " "
         | 
| 242 | 
            +
                    end
         | 
| 243 | 
            +
                    refs.each do |dst|
         | 
| 244 | 
            +
                      dstfile = normalize_filename(dst.file) if dst.file
         | 
| 245 | 
            +
                      dstline = dst.line
         | 
| 246 | 
            +
                      label = label_proc.call(dst)
         | 
| 247 | 
            +
                      if dst.file && @known_files.include?(dstfile)
         | 
| 248 | 
            +
                        ret << "[[" << label << "]], "
         | 
| 249 | 
            +
                      else
         | 
| 250 | 
            +
                        ret << label << ", "
         | 
| 251 | 
            +
                      end
         | 
| 252 | 
            +
                    end
         | 
| 253 | 
            +
                  end
         | 
| 254 | 
            +
                  ret
         | 
| 255 | 
            +
                end
         | 
| 256 | 
            +
             | 
| 257 | 
            +
                def create_file(destfile, fileinfo)
         | 
| 258 | 
            +
                  #body = format_lines(fileinfo)
         | 
| 259 | 
            +
                  #File.open(destfile, "w") do |f|
         | 
| 260 | 
            +
                    #f.puts body
         | 
| 261 | 
            +
                    #f.puts footer(fileinfo)
         | 
| 262 | 
            +
                  #end
         | 
| 263 | 
            +
                end
         | 
| 264 | 
            +
             | 
| 265 | 
            +
                def footer(fileinfo)
         | 
| 266 | 
            +
                  s  = "# Total lines    : %d\n" % fileinfo.num_lines
         | 
| 267 | 
            +
                  s << "# Lines of code  : %d\n" % fileinfo.num_code_lines
         | 
| 268 | 
            +
                  s << "# Total coverage : %3.1f%%\n" % [ fileinfo.total_coverage*100 ]
         | 
| 269 | 
            +
                  s << "# Code coverage  : %3.1f%%\n\n" % [ fileinfo.code_coverage*100 ]
         | 
| 270 | 
            +
                  # prevents false positives on Emacs
         | 
| 271 | 
            +
                  s << "# Local " "Variables:\n" "# mode: " "rcov-xref\n" "# End:\n"
         | 
| 272 | 
            +
                end
         | 
| 273 | 
            +
              end
         | 
| 274 | 
            +
            end
         |