rcov 0.7.0.1 → 0.8.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/BLURB CHANGED
@@ -2,7 +2,7 @@
2
2
  Source code, additional information, screenshots... available at
3
3
  http://eigenclass.org/hiki.rb?rcov
4
4
  Release information:
5
- http://eigenclass.org/hiki.rb?rcov+0.7.0
5
+ http://eigenclass.org/hiki.rb?rcov+0.8.0
6
6
 
7
7
  If you're on win32, you can also find a pre-built rcovrt.so (which makes
8
8
  code coverage analysis >100 times faster) in the above-mentioned pages.
data/CHANGES CHANGED
@@ -1,6 +1,29 @@
1
1
 
2
2
  User-visible changes.
3
3
 
4
+ Since 0.7.0 (2006-08-04)
5
+ ========================
6
+ Features
7
+ --------
8
+ * --annotate mode, which dumps annotated source code which can be used to
9
+ follow the control flow (very useful when reading third-party code)
10
+ * --gcc option to display uncovered lines in GCC error format
11
+ * superior Emacs support: running rcov, jumping to uncovered code, navigate
12
+ through cross-referenced annotated code
13
+ * --[no-]validator-links
14
+
15
+ Bugfixes
16
+ --------
17
+ * differential code coverage reports work with filenames containing special
18
+ characters
19
+ * fixed recent segfaults happening with rspec
20
+ * more care name mangling
21
+
22
+ Minor enhancements
23
+ ------------------
24
+ * relevant summary values are identified using separate CSS classes
25
+ (microformat-style)
26
+
4
27
  Since 0.6.0 (2006-06-12)
5
28
  ========================
6
29
  Features
@@ -0,0 +1,64 @@
1
+
2
+ <tt>rcov.el</tt> allows you to use rcov from Emacs conveniently.
3
+ * Run unit tests and jump to uncovered code by <tt>C-x `</tt>.
4
+ * Run unit tests and save the current coverage status.
5
+ * Run unit tests and jump to uncovered code introduced since the last run.
6
+ * View cross-reference annotated code.
7
+
8
+ == Installation
9
+
10
+ Copy <tt>rcov.el</tt> to the appropriate directory, which is in load-path.
11
+ Then require it.
12
+ (require 'rcov)
13
+
14
+
15
+ == Usage
16
+
17
+ There are some commands to run rcov in Emacs.
18
+ All of them displays +rcov+ window, whose major-mode is compilation-mode.
19
+ Therefore you can jump to uncovered code by <tt>C-x `</tt>.
20
+
21
+ +rcov-command-line+, +rcovsave-command-line+, and +rcovdiff-command-line+ define
22
+ command line to run rcov.
23
+ If you do not use +rcov+ from Rake, you must modify them.
24
+
25
+ === Finding uncovered code
26
+
27
+ Type the following while editing your program:
28
+ M-x rcov
29
+
30
+ === Setting the reference point
31
+
32
+ +rcov+'s <tt>--text-coverage-diff</tt> mode compares the current coverage status against
33
+ the saved one. It therefore needs that information to be recorded
34
+ before you write new code (typically right after you perform a commit) in
35
+ order to have something to compare against.
36
+
37
+ You can save the current status with the <tt>--save</tt> option.
38
+
39
+ Type the following to save the current status in Emacs:
40
+ M-x rcovsave
41
+ If you do not use +rcov+ from Rake, you must modify +rcovsave-command-line+ variable.
42
+
43
+ === Finding new uncovered code
44
+
45
+ Type the following to save the current status in Emacs:
46
+ M-x rcovdiff
47
+
48
+ === Viewing cross-reference annotated code
49
+
50
+ If you read cross-reference annotated code, issue
51
+ rake rcov RCOVOPTS='-a'
52
+ at the beginning.
53
+ This command creates +coverage+ directory and many *.rb files in it.
54
+ Filenames of these Ruby scripts are converted from original path.
55
+ You can browse them by normally <tt>C-x C-f</tt>.
56
+ You can think of <tt>-a</tt> option as <tt>--xrefs</tt> option and output format is Ruby script.
57
+
58
+ After find-file-ed annotated script, the major-mode is rcov-xref-mode,
59
+ which is derived from ruby-mode and specializes navigation.
60
+
61
+ <tt>Tab</tt> and <tt>M-Tab</tt> goes forward/backward links.
62
+ <tt>Ret</tt> follows selected link.
63
+
64
+ This feature is useful to read third-party code or to follow control flow.
data/Rakefile CHANGED
@@ -60,11 +60,16 @@ Rake::TestTask.new(:test_pure_ruby) do |t|
60
60
  t.verbose = true
61
61
  end
62
62
 
63
- desc "Run the unit tests, both rcovrt and pure-Ruby modes"
64
- task :test => [:test_rcovrt, :test_pure_ruby]
63
+ desc "Run the unit tests"
64
+ task :test => [:test_rcovrt]
65
65
 
66
66
  task :default => :test
67
67
 
68
+ desc "install by setup.rb"
69
+ task :install do
70
+ sh "sudo ruby setup.rb install"
71
+ end
72
+
68
73
  desc "Generate rdoc documentation for the rcov library"
69
74
  Rake::RDocTask.new("rdoc") { |rdoc|
70
75
  rdoc.rdoc_dir = 'doc'
@@ -82,7 +87,7 @@ Rake::RDocTask.new("rdoc") { |rdoc|
82
87
 
83
88
  require 'rcov/version'
84
89
 
85
- PKG_REVISION = ".1"
90
+ PKG_REVISION = ".0"
86
91
  PKG_FILES = FileList[
87
92
  "bin/rcov",
88
93
  "lib/**/*.rb",
@@ -90,7 +95,7 @@ PKG_FILES = FileList[
90
95
  "ext/rcovrt/*.c",
91
96
  "ext/rcovrt/*.h",
92
97
  "LEGAL", "LICENSE", "Rakefile", "Rantfile", "README.*", "THANKS", "test/*.rb",
93
- "mingw-rbconfig.rb", "rcov.vim",
98
+ "mingw-rbconfig.rb", "rcov.vim", "rcov.el",
94
99
  "setup.rb", "BLURB", "CHANGES"
95
100
  ]
96
101
 
data/THANKS CHANGED
@@ -44,9 +44,25 @@ Tim Shadel:
44
44
 
45
45
  Thomas Leitner:
46
46
  * reported that the SCRIPT_LINES__ workaround did not always work
47
+ * fixed the bug which broke differential reports for filenames with
48
+ special characters
47
49
 
48
50
  Assaph Mehr:
49
51
  * beta-tested 0.7.0 and found a bug in --aggregate (missing files)
50
52
 
51
53
  Ryan Kinderman:
52
54
  * suggested that -Ipath be passed to ruby instead of rcov in RcovTasks
55
+
56
+ Jan Svitok:
57
+ * reported typo in rcovtask.rb's RDoc
58
+
59
+ rubikitch:
60
+ * implemented --annotate mode
61
+ * implemented --gcc option
62
+ * superior emacs support
63
+ * testing, refactoring...
64
+ * many other things, see darcs changes
65
+
66
+
67
+ Zed A. Shaw:
68
+ * reported and fixed segfault triggered by rspec
data/bin/rcov CHANGED
@@ -1,4 +1,4 @@
1
- #! /home/batsman/usr/bin/ruby
1
+ #!/usr/bin/env ruby
2
2
  # rcov Copyright (c) 2004-2006 Mauricio Fernandez <mfp@acm.org>
3
3
  #
4
4
  # rcov originally based on
@@ -57,6 +57,8 @@ options.coverage_diff_save = false
57
57
  options.diff_cmd = "diff"
58
58
  options.report_cov_bug_for = nil
59
59
  options.aggregate_file = nil
60
+ options.gcc_output = false
61
+ options.show_validator_links = true
60
62
 
61
63
  EXTRA_HELP = <<-EOF
62
64
 
@@ -152,6 +154,15 @@ EOF
152
154
  "Color scale range for profiling info (dB).") do |val|
153
155
  options.range = val
154
156
  end
157
+ opts.on("-a", "--annotate",
158
+ "Generate annotated source code.") do
159
+ options.html = false
160
+ options.textmode = :annotate
161
+ options.crossrefs = true
162
+ options.callsites = true
163
+ options.skip = [ %r!/test/unit/! ]
164
+ end
165
+
155
166
  opts.on("-T", "--text-report", "Dump detailed plain-text report to stdout.",
156
167
  "(filename, LoC, total lines, coverage)") do
157
168
  options.textmode = :report
@@ -166,6 +177,9 @@ EOF
166
177
  "ANSI color sequences unless -n.") do
167
178
  options.textmode = :coverage
168
179
  end
180
+ opts.on("--gcc", "Dump uncovered line in GCC error format.") do
181
+ options.gcc_output = true
182
+ end
169
183
  opts.on("--aggregate FILE", "Aggregate data from previous runs",
170
184
  "in FILE. Overwrites FILE with the",
171
185
  "merged data. FILE is created if",
@@ -216,6 +230,10 @@ EOF
216
230
  end
217
231
  options.output_threshold = threshold
218
232
  end
233
+ opts.on("--[no-]validator-links", "Add link to W3C's validation services.",
234
+ "(default: true)") do |show_validator_links|
235
+ options.show_validator_links = show_validator_links
236
+ end
219
237
  opts.on("--only-uncovered", "Same as --threshold 100") do
220
238
  options.output_threshold = 100
221
239
  end
@@ -296,6 +314,7 @@ options.loadpaths.reverse_each{|x| $:.unshift x}
296
314
  require 'rcov'
297
315
 
298
316
  options.callsites = true if options.report_cov_bug_for
317
+ options.textmode = :gcc if !options.textmode and options.gcc_output
299
318
 
300
319
  def rcov_load_aggregate_data(file)
301
320
  require 'zlib'
@@ -344,7 +363,9 @@ make_formatter = lambda do |klass|
344
363
  :callsites => options.callsites,
345
364
  :cross_references => options.crossrefs,
346
365
  :diff_cmd => options.diff_cmd,
347
- :comments_run_by_default => options.comments_run_by_default
366
+ :comments_run_by_default => options.comments_run_by_default,
367
+ :gcc_output => options.gcc_output,
368
+ :validator_links => options.show_validator_links
348
369
  )
349
370
  end
350
371
 
@@ -357,6 +378,8 @@ if options.html
357
378
  end
358
379
  textual_formatters = {:counts => Rcov::FullTextReport,
359
380
  :coverage => Rcov::FullTextReport,
381
+ :gcc => Rcov::FullTextReport,
382
+ :annotate => Rcov::RubyAnnotation,
360
383
  :summary => Rcov::TextSummary, :report => Rcov::TextReport,
361
384
  :coverage_diff => Rcov::TextCoverageDiff}
362
385
 
@@ -1258,3 +1281,7 @@ if __FILE__ == $0
1258
1281
 
1259
1282
  end
1260
1283
  # vi: set sw=4:
1284
+ # Here is Emacs setting. DO NOT REMOVE!
1285
+ # Local Variables:
1286
+ # ruby-indent-level: 4
1287
+ # End:
@@ -4,6 +4,7 @@
4
4
  #include <node.h>
5
5
  #include <st.h>
6
6
  #include <stdlib.h>
7
+ #include <assert.h>
7
8
 
8
9
  #define COVERAGE_DEBUG_EVENTS 0
9
10
 
@@ -34,12 +35,15 @@ static char *cached_file = 0;
34
35
  * */
35
36
 
36
37
  static struct cov_array *
37
- coverage_increase_counter_uncached(char *sourcefile, int sourceline,
38
+ coverage_increase_counter_uncached(char *sourcefile, unsigned int sourceline,
38
39
  char mark_only)
39
40
  {
40
- struct cov_array *carray;
41
-
42
- if(!st_lookup(coverinfo, (st_data_t)sourcefile, (st_data_t*)&carray)) {
41
+ struct cov_array *carray = NULL;
42
+
43
+ if(sourcefile == NULL) {
44
+ /* "can't happen", just ignore and avoid segfault */
45
+ return NULL;
46
+ } else if(!st_lookup(coverinfo, (st_data_t)sourcefile, (st_data_t*)&carray)) {
43
47
  VALUE arr;
44
48
 
45
49
  arr = rb_hash_aref(oSCRIPT_LINES__, rb_str_new2(sourcefile));
@@ -51,7 +55,11 @@ coverage_increase_counter_uncached(char *sourcefile, int sourceline,
51
55
  carray->len = RARRAY(arr)->len;
52
56
  st_insert(coverinfo, (st_data_t)strdup(sourcefile),
53
57
  (st_data_t) carray);
58
+ } else {
59
+ /* recovered carray, sanity check */
60
+ assert(carray && "failed to create valid carray");
54
61
  }
62
+
55
63
  if(mark_only) {
56
64
  if(!carray->ptr[sourceline])
57
65
  carray->ptr[sourceline] = 1;
@@ -74,7 +82,9 @@ coverage_mark_caller()
74
82
  }
75
83
  for (; frame && (n = frame->node); frame = frame->prev) {
76
84
  if (frame->prev && frame->prev->last_func) {
77
- if (frame->prev->node == n) continue;
85
+ if (frame->prev->node == n) {
86
+ if (frame->prev->last_func == frame->last_func) continue;
87
+ }
78
88
  coverage_increase_counter_uncached(n->nd_file, nd_line(n) - 1, 1);
79
89
  }
80
90
  else {
@@ -42,7 +42,7 @@ module Rcov
42
42
  #
43
43
  class RcovTask < Rake::TaskLib
44
44
 
45
- # Name of test task. (default is :test)
45
+ # Name of test task. (default is :rcov)
46
46
  attr_accessor :name
47
47
 
48
48
  # List of directories to added to $LOAD_PATH before running the
@@ -1,6 +1,7 @@
1
1
  # rcov Copyright (c) 2004-2006 Mauricio Fernandez <mfp@acm.org>
2
2
  # See LEGAL and LICENSE for additional licensing information.
3
3
 
4
+ require 'pathname'
4
5
  module Rcov
5
6
 
6
7
  class Formatter # :nodoc:
@@ -30,6 +31,10 @@ class Formatter # :nodoc:
30
31
  @callsite_analyzer = options[:callsite_analyzer]
31
32
  @comments_run_by_default = options[:comments_run_by_default]
32
33
  @callsite_index = nil
34
+
35
+ @mangle_filename = Hash.new{|h,base|
36
+ h[base] = Pathname(base).cleanpath.to_s.gsub(%r{^\w:[/\\]}, "").gsub(/\./, "_").gsub(/[\\\/]/, "-") + ".html"
37
+ }
33
38
  end
34
39
 
35
40
  def add_file(filename, lines, coverage, counts)
@@ -53,7 +58,7 @@ class Formatter # :nodoc:
53
58
  end
54
59
 
55
60
  def mangle_filename(base)
56
- base.gsub(%r{^\w:[/\\]}, "").gsub(/\./, "_").gsub(/[\\\/]/, "-") + ".html"
61
+ @mangle_filename[base]
57
62
  end
58
63
 
59
64
  def each_file_pair_sorted(&b)
@@ -142,6 +147,29 @@ class Formatter # :nodoc:
142
147
  end
143
148
  index
144
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
145
173
  end
146
174
 
147
175
  class TextSummary < Formatter # :nodoc:
@@ -202,6 +230,8 @@ class FullTextReport < Formatter # :nodoc:
202
230
  case @textmode
203
231
  when :counts
204
232
  puts "%-70s| %6d" % [line.chomp[0,70], fileinfo.counts[i]]
233
+ when :gcc
234
+ puts "%s:%d:%s" % [filename, i+1, line.chomp] unless fileinfo.coverage[i]
205
235
  when :coverage
206
236
  if @color
207
237
  prefix = fileinfo.coverage[i] ? "\e[32;40m" : "\e[31;40m"
@@ -236,7 +266,8 @@ class TextCoverageDiff < Formatter # :nodoc:
236
266
  @mode = options[:coverage_diff_mode]
237
267
  @state_file = options[:coverage_diff_file]
238
268
  @diff_cmd = options[:diff_cmd]
239
- super(options)
269
+ @gcc_output = options[:gcc_output]
270
+ super(options)
240
271
  end
241
272
 
242
273
  def execute
@@ -264,7 +295,7 @@ class TextCoverageDiff < Formatter # :nodoc:
264
295
  $stderr.puts <<-EOF
265
296
  Couldn't save coverage data to #{@state_file}.
266
297
  EOF
267
- end
298
+ end # '
268
299
 
269
300
  require 'tempfile'
270
301
  def compare_state
@@ -275,7 +306,7 @@ EOF
275
306
  $stderr.puts <<-EOF
276
307
  Couldn't load coverage data from #{@state_file}.
277
308
  EOF
278
- return
309
+ return # '
279
310
  end
280
311
  if !(Array === format) or
281
312
  FORMAT_VERSION[0] != format[0] || FORMAT_VERSION[1] < format[1]
@@ -284,7 +315,7 @@ Couldn't load coverage data from #{@state_file}.
284
315
  The file is saved in the format #{format.inspect[0..20]}.
285
316
  This rcov executable understands #{FORMAT_VERSION.inspect}.
286
317
  EOF
287
- return
318
+ return # '
288
319
  end
289
320
  each_file_pair_sorted do |filename, fileinfo|
290
321
  old_data = Tempfile.new("#{mangle_filename(filename)}-old")
@@ -305,7 +336,7 @@ EOF
305
336
  end
306
337
  new_data.close
307
338
 
308
- diff = `#{@diff_cmd} -u #{old_data.path} #{new_data.path}`
339
+ diff = `#{@diff_cmd} -u "#{old_data.path}" "#{new_data.path}"`
309
340
  new_uncovered_hunks = process_unified_diff(filename, diff)
310
341
  old_data.close!
311
342
  new_data.close!
@@ -322,13 +353,21 @@ EOF
322
353
 
323
354
  EOF
324
355
  hunks.each do |offset, lines|
325
- puts "### #{filename}:#{offset}"
326
- if @color
356
+ if @gcc_output
357
+ lines.each_with_index do |line,i|
358
+ lineno = offset + i
359
+ flag = (/^!! / !~ line) ? "-" : ":"
360
+ prefix = "#{filename}#{flag}#{lineno}#{flag}"
361
+ puts "#{prefix}#{line[3..-1]}"
362
+ end
363
+ elsif @color
364
+ puts "### #{filename}:#{offset}"
327
365
  lines.each do |line|
328
366
  prefix = (/^!! / !~ line) ? "\e[32;40m" : "\e[31;40m"
329
367
  puts "#{prefix}#{line[3..-1].chomp}\e[37;40m"
330
368
  end
331
369
  else
370
+ puts "### #{filename}:#{offset}"
332
371
  puts lines
333
372
  end
334
373
  end
@@ -561,7 +600,9 @@ table.report td.text {
561
600
  border: #d0d0d0 1px solid;
562
601
  }
563
602
 
564
- table.report td.value {
603
+ table.report td.value,
604
+ table.report td.lines_total,
605
+ table.report td.lines_code {
565
606
  text-align: right;
566
607
  border: #d0d0d0 1px solid;
567
608
  }
@@ -574,7 +615,9 @@ table.report tr.dark {
574
615
  EOS
575
616
 
576
617
  DEFAULT_OPTS = {:color => false, :fsr => 30, :destdir => "coverage",
577
- :callsites => false, :cross_references => false}
618
+ :callsites => false, :cross_references => false,
619
+ :validator_links => true
620
+ }
578
621
  def initialize(opts = {})
579
622
  options = DEFAULT_OPTS.clone.update(opts)
580
623
  super(options)
@@ -584,6 +627,7 @@ EOS
584
627
  @do_callsites = options[:callsites]
585
628
  @do_cross_references = options[:cross_references]
586
629
  @span_class_index = 0
630
+ @show_validator_links = options[:validator_links]
587
631
  end
588
632
 
589
633
  def execute
@@ -647,16 +691,18 @@ EOS
647
691
  a_(:href => mangle_filename(f.name)){ t_ { f.name } }
648
692
  end
649
693
  }
650
- [f.num_lines, f.num_code_lines].each do |value|
651
- td_(:class => "value") { tt_{ value } }
694
+ [[f.num_lines, "lines_total"],
695
+ [f.num_code_lines, "lines_code"]].each do |value, css_class|
696
+ td_(:class => css_class) { tt_{ value } }
652
697
  end
653
- [f.total_coverage, f.code_coverage].each do |value|
698
+ [[f.total_coverage, "coverage_total"],
699
+ [f.code_coverage, "coverage_code"]].each do |value, css_class|
654
700
  value *= 100
655
701
  td_ {
656
702
  table_(:cellpadding => 0, :cellspacing => 0, :align => "right") {
657
703
  tr_ {
658
704
  td_ {
659
- tt_ { "%3.1f%%" % value }
705
+ tt_(:class => css_class) { "%3.1f%%" % value }
660
706
  x_ "&nbsp;"
661
707
  }
662
708
  ivalue = value.round
@@ -712,17 +758,20 @@ EOS
712
758
  x_{ format_overview(*files) }
713
759
  hr_
714
760
  x_{ blurb }
715
- p_ {
716
- a_(:href => "http://validator.w3.org/check/referer") {
717
- img_(:src => "http://www.w3.org/Icons/valid-xhtml11",
718
- :alt => "Valid XHTML 1.1!", :height => 31, :width => 88)
719
- }
720
- a_(:href => "http://jigsaw.w3.org/css-validator/check/referer") {
721
- img_(:style => "border:0;width:88px;height:31px",
722
- :src => "http://jigsaw.w3.org/css-validator/images/vcss",
723
- :alt => "Valid CSS!")
761
+
762
+ if @show_validator_links
763
+ p_ {
764
+ a_(:href => "http://validator.w3.org/check/referer") {
765
+ img_(:src => "http://www.w3.org/Icons/valid-xhtml11",
766
+ :alt => "Valid XHTML 1.1!", :height => 31, :width => 88)
767
+ }
768
+ a_(:href => "http://jigsaw.w3.org/css-validator/check/referer") {
769
+ img_(:style => "border:0;width:88px;height:31px",
770
+ :src => "http://jigsaw.w3.org/css-validator/images/vcss",
771
+ :alt => "Valid CSS!")
772
+ }
724
773
  }
725
- }
774
+ end
726
775
  }
727
776
  } }
728
777
  lines = output.pretty.to_a
@@ -760,41 +809,23 @@ EOS
760
809
  "<pre>#{result}</pre>"
761
810
  end
762
811
 
763
- class XRefHelper < Struct.new(:file, :line, :klass, :mid, :count) # :nodoc:
764
- end
765
-
766
812
  def create_cross_refs(filename, lineno, linetext)
767
813
  return linetext unless @callsite_analyzer && @do_callsites
768
- ret = ""
769
814
  ref_blocks = []
770
- if @do_cross_references and
771
- (rev_xref = reverse_cross_references_for(filename, lineno))
772
- refs = rev_xref.map do |classname, methodname, defsite, count|
773
- XRefHelper.new(defsite.file, defsite.line, classname, methodname, count)
774
- end.sort_by{|r| r.count}.reverse
775
- format_call_ref = lambda do |ref|
776
- if ref.file
777
- where = "at #{normalize_filename(ref.file)}:#{ref.line}"
778
- else
779
- where = "(C extension/core)"
780
- end
781
- CGI.escapeHTML("%7d %s" %
782
- [ref.count, "#{ref.klass}##{ref.mid} " + where])
815
+ _get_defsites(ref_blocks, filename, lineno, "Calls", linetext) do |ref|
816
+ if ref.file
817
+ where = "at #{normalize_filename(ref.file)}:#{ref.line}"
818
+ else
819
+ where = "(C extension/core)"
783
820
  end
784
- ref_blocks << [refs, "Calls", format_call_ref]
821
+ CGI.escapeHTML("%7d %s" %
822
+ [ref.count, "#{ref.klass}##{ref.mid} " + where])
785
823
  end
786
- if @do_callsites and
787
- (refs = cross_references_for(filename, lineno))
788
- refs = refs.sort_by{|k,count| count}.map do |ref, count|
789
- XRefHelper.new(ref.file, ref.line, ref.calling_class, ref.calling_method, count)
790
- end.reverse
791
- format_called_ref = lambda do |ref|
792
- r = "%7d %s" % [ref.count,
793
- "#{normalize_filename(ref.file||'C code')}:#{ref.line} " +
824
+ _get_callsites(ref_blocks, filename, lineno, "Called by", linetext) do |ref|
825
+ r = "%7d %s" % [ref.count,
826
+ "#{normalize_filename(ref.file||'C code')}:#{ref.line} " +
794
827
  "in '#{ref.klass}##{ref.mid}'"]
795
- CGI.escapeHTML(r)
796
- end
797
- ref_blocks << [refs, "Called by", format_called_ref]
828
+ CGI.escapeHTML(r)
798
829
  end
799
830
 
800
831
  create_cross_reference_block(linetext, ref_blocks)
@@ -874,17 +905,20 @@ EOS
874
905
  x_{ body }
875
906
  hr_
876
907
  x_ { blurb }
877
- p_ {
878
- a_(:href => "http://validator.w3.org/check/referer") {
879
- img_(:src => "http://www.w3.org/Icons/valid-xhtml10",
880
- :alt => "Valid XHTML 1.0!", :height => 31, :width => 88)
881
- }
882
- a_(:href => "http://jigsaw.w3.org/css-validator/check/referer") {
883
- img_(:style => "border:0;width:88px;height:31px",
884
- :src => "http://jigsaw.w3.org/css-validator/images/vcss",
885
- :alt => "Valid CSS!")
908
+
909
+ if @show_validator_links
910
+ p_ {
911
+ a_(:href => "http://validator.w3.org/check/referer") {
912
+ img_(:src => "http://www.w3.org/Icons/valid-xhtml10",
913
+ :alt => "Valid XHTML 1.0!", :height => 31, :width => 88)
914
+ }
915
+ a_(:href => "http://jigsaw.w3.org/css-validator/check/referer") {
916
+ img_(:style => "border:0;width:88px;height:31px",
917
+ :src => "http://jigsaw.w3.org/css-validator/images/vcss",
918
+ :alt => "Valid CSS!")
919
+ }
886
920
  }
887
- }
921
+ end
888
922
  }
889
923
  } }
890
924
  # .pretty needed to make sure DOCTYPE is in a separate line
@@ -1006,6 +1040,119 @@ class HTMLProfiling < HTMLCoverage # :nodoc:
1006
1040
  end
1007
1041
  end
1008
1042
 
1043
+ class RubyAnnotation < Formatter # :nodoc:
1044
+ DEFAULT_OPTS = { :destdir => "coverage" }
1045
+ def initialize(opts = {})
1046
+ options = DEFAULT_OPTS.clone.update(opts)
1047
+ super(options)
1048
+ @dest = options[:destdir]
1049
+ @do_callsites = true
1050
+ @do_cross_references = true
1051
+
1052
+ @mangle_filename = Hash.new{|h,base|
1053
+ h[base] = Pathname(base).cleanpath.to_s.gsub(%r{^\w:[/\\]}, "").gsub(/\./, "_").gsub(/[\\\/]/, "-") + ".rb"
1054
+ }
1055
+ end
1056
+
1057
+ def execute
1058
+ return if @files.empty?
1059
+ FileUtils.mkdir_p @dest
1060
+ each_file_pair_sorted do |filename, fileinfo|
1061
+ create_file(File.join(@dest, mangle_filename(filename)), fileinfo)
1062
+ end
1063
+ end
1064
+
1065
+ private
1066
+
1067
+ def format_lines(file)
1068
+ result = ""
1069
+ format_line = "%#{file.num_lines.to_s.size}d"
1070
+ file.num_lines.times do |i|
1071
+ line = file.lines[i].chomp
1072
+ marked = file.coverage[i]
1073
+ count = file.counts[i]
1074
+ result << create_cross_refs(file.name, i+1, line, marked) + "\n"
1075
+ end
1076
+ result
1077
+ end
1078
+
1079
+ def create_cross_refs(filename, lineno, linetext, marked)
1080
+ return linetext unless @callsite_analyzer && @do_callsites
1081
+ ref_blocks = []
1082
+ _get_defsites(ref_blocks, filename, lineno, linetext, ">>") do |ref|
1083
+ ref.file.sub!(%r!^./!, '')
1084
+ if ref.file
1085
+ where = "at #{mangle_filename(ref.file)}:#{ref.line}"
1086
+ else
1087
+ where = "(C extension/core)"
1088
+ end
1089
+ "#{ref.klass}##{ref.mid} " + where + ""
1090
+ end
1091
+ _get_callsites(ref_blocks, filename, lineno, linetext, "<<") do |ref| # "
1092
+ ref.file.sub!(%r!^./!, '')
1093
+ "#{mangle_filename(ref.file||'C code')}:#{ref.line} " +
1094
+ "in #{ref.klass}##{ref.mid}"
1095
+ end
1096
+
1097
+ create_cross_reference_block(linetext, ref_blocks, marked)
1098
+ end
1099
+
1100
+ def create_cross_reference_block(linetext, ref_blocks, marked)
1101
+ codelen = 75
1102
+ if ref_blocks.empty?
1103
+ if marked
1104
+ return "%-#{codelen}s #o" % linetext
1105
+ else
1106
+ return linetext
1107
+ end
1108
+ end
1109
+ ret = ""
1110
+ @cross_ref_idx ||= 0
1111
+ @known_files ||= sorted_file_pairs.map{|fname, finfo| normalize_filename(fname)}
1112
+ ret << "%-#{codelen}s # " % linetext
1113
+ ref_blocks.each do |refs, toplabel, label_proc|
1114
+ unless !toplabel || toplabel.empty?
1115
+ ret << toplabel << " "
1116
+ end
1117
+ refs.each do |dst|
1118
+ dstfile = normalize_filename(dst.file) if dst.file
1119
+ dstline = dst.line
1120
+ label = label_proc.call(dst)
1121
+ if dst.file && @known_files.include?(dstfile)
1122
+ ret << "[[" << label << "]], "
1123
+ else
1124
+ ret << label << ", "
1125
+ end
1126
+ end
1127
+ end
1128
+
1129
+ ret
1130
+ end
1131
+
1132
+ def create_file(destfile, fileinfo)
1133
+ #$stderr.puts "Generating #{destfile.inspect}"
1134
+ body = format_lines(fileinfo)
1135
+ File.open(destfile, "w") do |f|
1136
+ f.puts body
1137
+ f.puts footer(fileinfo)
1138
+ end
1139
+ end
1140
+
1141
+ def footer(fileinfo)
1142
+ s = "# Total lines : %d\n" % fileinfo.num_lines
1143
+ s << "# Lines of code : %d\n" % fileinfo.num_code_lines
1144
+ s << "# Total coverage : %3.1f%%\n" % [ fileinfo.total_coverage*100 ]
1145
+ s << "# Code coverage : %3.1f%%\n\n" % [ fileinfo.code_coverage*100 ]
1146
+ # prevents false positives on Emacs
1147
+ s << "# Local " "Variables:\n" "# mode: " "rcov-xref\n" "# End:\n"
1148
+ end
1149
+ end
1150
+
1151
+
1009
1152
  end # Rcov
1010
1153
 
1011
1154
  # vi: set sw=4:
1155
+ # Here is Emacs setting. DO NOT REMOVE!
1156
+ # Local Variables:
1157
+ # ruby-indent-level: 4
1158
+ # End:
@@ -4,8 +4,8 @@
4
4
 
5
5
  module Rcov
6
6
 
7
- VERSION = "0.7.0"
8
- RELEASE_DATE = "2006/08/04"
7
+ VERSION = "0.8.0"
8
+ RELEASE_DATE = "2007-02-28"
9
9
  RCOVRT_ABI = [2,0,0]
10
10
  UPSTREAM_URL = "http://eigenclass.org/hiki.rb?rcov"
11
11
 
data/rcov.el ADDED
@@ -0,0 +1,131 @@
1
+ ;;; rcov.el -- Ruby Coverage Analysis Tool
2
+
3
+ ;;; Copyright (c) 2006 rubikitch <rubikitch@ruby-lang.org>
4
+ ;;;
5
+ ;;; Use and distribution subject to the terms of the rcov license.
6
+
7
+ (defvar rcov-xref-before-visit-source-hook nil
8
+ "Hook executed before jump.")
9
+ (defvar rcov-xref-after-visit-source-hook nil
10
+ "Hook executed after jump.")
11
+ (defvar rcov-command-line "rake rcov RCOVOPTS='--gcc --no-html'"
12
+ "Rcov command line to find uncovered code.
13
+ It is good to use rcov with Rake because it `cd's appropriate directory.
14
+ `--gcc' option is strongly recommended because `rcov' uses compilation-mode.")
15
+ (defvar rcovsave-command-line "rake rcov RCOVOPTS='--gcc --no-html --save=coverage.info'"
16
+ "Rcov command line to save coverage status. See also `rcov-command-line'.")
17
+ (defvar rcovdiff-command-line "rake rcov RCOVOPTS='-D --gcc --no-html'"
18
+ "Rcov command line to find new uncovered code. See also `rcov-command-line'.")
19
+
20
+ ;;;; rcov-xref-mode
21
+ (define-derived-mode rcov-xref-mode ruby-mode "Rxref"
22
+ "Major mode for annotated Ruby scripts (coverage/*.rb) by rcov."
23
+ (setq truncate-lines t)
24
+ ;; ruby-electric-mode / pabbrev-mode hijacks TAB binding.
25
+ (and ruby-electric-mode (ruby-electric-mode -1))
26
+ (and (boundp 'pabbrev-mode) pabbrev-mode (pabbrev-mode -1))
27
+ (suppress-keymap rcov-xref-mode-map)
28
+ (define-key rcov-xref-mode-map "\C-i" 'rcov-xref-next-tag)
29
+ (define-key rcov-xref-mode-map "\M-\C-i" 'rcov-xref-previous-tag)
30
+ (define-key rcov-xref-mode-map "\C-m" 'rcov-xref-visit-source)
31
+ (set (make-local-variable 'automatic-hscrolling) nil)
32
+ )
33
+
34
+ (defvar rcov-xref-tag-regexp "\\[\\[\\(.*?\\)\\]\\]")
35
+
36
+ (defun rcov-xref-next-tag (n)
37
+ "Go to next LINK."
38
+ (interactive "p")
39
+ (when (looking-at rcov-xref-tag-regexp)
40
+ (goto-char (match-end 0)))
41
+ (when (re-search-forward rcov-xref-tag-regexp nil t n)
42
+ (goto-char (match-beginning 0)))
43
+ (rcov-xref-show-link))
44
+
45
+ (defun rcov-xref-previous-tag (n)
46
+ "Go to previous LINK."
47
+ (interactive "p")
48
+ (re-search-backward rcov-xref-tag-regexp nil t n)
49
+ (rcov-xref-show-link))
50
+
51
+ (defvar rcov-xref-link-tempbuffer " *rcov-link*")
52
+ (defun rcov-xref-show-link ()
53
+ "Follow current LINK."
54
+ (let ((link (match-string 1))
55
+ (eol (point-at-eol)))
56
+ (save-excursion
57
+ (when (and link
58
+ (re-search-backward "# \\(>>\\|<<\\) " (point-at-bol) t))
59
+ (while (re-search-forward rcov-xref-tag-regexp eol t)
60
+ (let ((matched (match-string 1)))
61
+ (when (string= link matched)
62
+ (add-text-properties 0 (length matched) '(face highlight) matched))
63
+ (with-current-buffer (get-buffer-create rcov-xref-link-tempbuffer)
64
+ (insert matched "\n"))))
65
+ (let (message-log-max) ; inhibit *Messages*
66
+ (message "%s" (with-current-buffer rcov-xref-link-tempbuffer
67
+ (substring (buffer-string) 0 -1)))) ; chomp
68
+ (kill-buffer rcov-xref-link-tempbuffer)))))
69
+
70
+
71
+ ;; copied from jw-visit-source
72
+ (defun rcov-xref-extract-file-lines (line)
73
+ "Extract a list of file/line pairs from the given line of text."
74
+ (let*
75
+ ((unix_fn "[^ \t\n\r\"'([<{]+")
76
+ (dos_fn "[a-zA-Z]:[^ \t\n\r\"'([<{]+")
77
+ (flre (concat "\\(" unix_fn "\\|" dos_fn "\\):\\([0-9]+\\)"))
78
+ (start nil)
79
+ (result nil))
80
+ (while (string-match flre line start)
81
+ (setq start (match-end 0))
82
+ (setq result
83
+ (cons (list
84
+ (substring line (match-beginning 1) (match-end 1))
85
+ (string-to-int (substring line (match-beginning 2) (match-end 2))))
86
+ result)))
87
+ result))
88
+
89
+ (defun rcov-xref-select-file-line (candidates)
90
+ "Select a file/line candidate that references an existing file."
91
+ (cond ((null candidates) nil)
92
+ ((file-readable-p (caar candidates)) (car candidates))
93
+ (t (rcov-xref-select-file-line (cdr candidates))) ))
94
+
95
+ (defun rcov-xref-visit-source ()
96
+ "If the current line contains text like '../src/program.rb:34', visit
97
+ that file in the other window and position point on that line."
98
+ (interactive)
99
+ (let* ((line (progn (looking-at rcov-xref-tag-regexp) (match-string 1)))
100
+ (candidates (rcov-xref-extract-file-lines line))
101
+ (file-line (rcov-xref-select-file-line candidates)))
102
+ (cond (file-line
103
+ (run-hooks 'rcov-xref-before-visit-source-hook)
104
+ (find-file (car file-line))
105
+ (goto-line (cadr file-line))
106
+ (run-hooks 'rcov-xref-after-visit-source-hook))
107
+ (t
108
+ (error "No source location on line.")) )))
109
+
110
+ ;;;; Running rcov with various options.
111
+ (defun rcov-internal (cmdline)
112
+ "Run rcov with various options."
113
+ (compile-internal cmdline ""
114
+ nil nil nil (lambda (x) "*rcov*")))
115
+
116
+ (defun rcov ()
117
+ "Run rcov to find uncovered code."
118
+ (interactive)
119
+ (rcov-internal rcov-command-line))
120
+
121
+ (defun rcovsave ()
122
+ "Run rcov to save coverage status."
123
+ (interactive)
124
+ (rcov-internal rcovsave-command-line))
125
+
126
+ (defun rcovdiff ()
127
+ "Run rcov to find new uncovered code."
128
+ (interactive)
129
+ (rcov-internal rcovdiff-command-line))
130
+
131
+ (provide 'rcov)
@@ -0,0 +1,10 @@
1
+ $: << File.dirname(__FILE__)
2
+ require 'sample_03'
3
+
4
+ klass = Rcov::Test::Temporary::Sample03
5
+ obj = klass.new
6
+ obj.f1
7
+ obj.f2
8
+ obj.f3
9
+ #klass.g1 uncovered
10
+ klass.g2
@@ -0,0 +1,17 @@
1
+ def d(x)
2
+ 4*x
3
+ end
4
+
5
+ def a
6
+ b 10
7
+ end
8
+
9
+ def b(x)
10
+ x*10
11
+ end
12
+
13
+ def c(x)
14
+ 3*x
15
+ end
16
+
17
+ a()
@@ -0,0 +1,13 @@
1
+ def a
2
+ b 10
3
+ end
4
+
5
+ def b(x)
6
+ x*10
7
+ end
8
+
9
+ def c(x)
10
+ 3*x
11
+ end
12
+
13
+ a()
@@ -0,0 +1,17 @@
1
+ def d(x)
2
+ 4*x
3
+ end
4
+
5
+ def a
6
+ b 10
7
+ end
8
+
9
+ def b(x)
10
+ x*10
11
+ end
12
+
13
+ def c(x)
14
+ 3*x
15
+ end
16
+
17
+ a()
@@ -0,0 +1,105 @@
1
+ require 'test/unit'
2
+ require 'pathname'
3
+ require 'fileutils'
4
+
5
+ =begin
6
+ Updating functional testdata automatically is DANGEROUS, so I do manually.
7
+
8
+ == update functional test
9
+ cd ~/src/rcov/test
10
+ rcov="ruby ../bin/rcov -I../lib:../ext/rcovrt -o expected_coverage"
11
+
12
+ $rcov -a sample_04.rb
13
+ $rcov sample_04.rb
14
+ $rcov --gcc --include-file=sample --exclude=rcov sample_04.rb > expected_coverage/gcc-text.out
15
+
16
+ cp sample_05-old.rb sample_05.rb
17
+ $rcov --no-html --gcc --include-file=sample --exclude=rcov --save=coverage.info sample_05.rb > expected_coverage/diff-gcc-original.out
18
+ cp sample_05-new.rb sample_05.rb
19
+ $rcov --no-html --gcc -D --include-file=sample --exclude=rcov sample_05.rb > expected_coverage/diff-gcc-diff.out
20
+ $rcov --no-html -D --include-file=sample --exclude=rcov sample_05.rb > expected_coverage/diff.out
21
+ $rcov --no-html --no-color -D --include-file=sample --exclude=rcov sample_05.rb > expected_coverage/diff-no-color.out
22
+ $rcov --no-html --gcc --include-file=sample --exclude=rcov sample_05.rb > expected_coverage/diff-gcc-all.out
23
+
24
+ =end
25
+
26
+ class TestFunctional < Test::Unit::TestCase
27
+ @@dir = Pathname(__FILE__).expand_path.dirname
28
+
29
+ def strip_time(str)
30
+ str.sub(/Generated on.+$/, '')
31
+ end
32
+
33
+ def cmp(file)
34
+ content = lambda{|dir| strip_time(File.read(@@dir+dir+file))}
35
+ assert_equal(content["expected_coverage"], content["actual_coverage"])
36
+ end
37
+
38
+ def with_testdir(&block)
39
+ Dir.chdir(@@dir, &block)
40
+ end
41
+
42
+ def run_rcov(opts, script="sample_04.rb", opts_tail="")
43
+ rcov = @@dir+"../bin/rcov"
44
+ ruby_opts = "-I../lib:../ext/rcovrt"
45
+ with_testdir do
46
+ `cd #{@@dir}; ruby #{ruby_opts} #{rcov} #{opts} -o actual_coverage #{script} #{opts_tail}`
47
+ yield if block_given?
48
+ end
49
+ end
50
+
51
+ def test_annotation
52
+ run_rcov("-a") do
53
+ cmp "sample_04_rb.rb"
54
+ cmp "sample_03_rb.rb"
55
+ end
56
+ end
57
+
58
+ def test_html
59
+ run_rcov("") do
60
+ cmp "sample_04_rb.html"
61
+ cmp "sample_03_rb.html"
62
+ end
63
+ end
64
+
65
+ @@selection = "--include-file=sample --exclude=rcov"
66
+ def test_text_gcc
67
+ run_rcov("--gcc #{@@selection}",
68
+ "sample_04.rb",
69
+ "> actual_coverage/gcc-text.out") do
70
+ cmp "gcc-text.out"
71
+ end
72
+ end
73
+
74
+ def test_diff
75
+ with_testdir { FileUtils.cp "sample_05-old.rb", "sample_05.rb" }
76
+ run_rcov("--no-html --gcc #{@@selection} --save=coverage.info", "sample_05.rb",
77
+ "> actual_coverage/diff-gcc-original.out") do
78
+ cmp "diff-gcc-original.out"
79
+ end
80
+
81
+ with_testdir { FileUtils.cp "sample_05-new.rb", "sample_05.rb" }
82
+ run_rcov("--no-html -D --gcc #{@@selection}", "sample_05.rb",
83
+ "> actual_coverage/diff-gcc-diff.out") do
84
+ cmp "diff-gcc-diff.out"
85
+ end
86
+
87
+ run_rcov("--no-html -D #{@@selection}", "sample_05.rb",
88
+ "> actual_coverage/diff.out") do
89
+ cmp "diff.out"
90
+ end
91
+
92
+ run_rcov("--no-html --no-color -D #{@@selection}", "sample_05.rb",
93
+ "> actual_coverage/diff-no-color.out") do
94
+ cmp "diff-no-color.out"
95
+ end
96
+
97
+ run_rcov("--no-html --gcc #{@@selection}", "sample_05.rb",
98
+ "> actual_coverage/diff-gcc-all.out") do
99
+ cmp "diff-gcc-all.out"
100
+ end
101
+
102
+
103
+ end
104
+
105
+ end
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.0
2
+ rubygems_version: 0.9.1
3
3
  specification_version: 1
4
4
  name: rcov
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.7.0.1
7
- date: 2006-08-04 00:00:00 +02:00
6
+ version: 0.8.0.0
7
+ date: 2007-02-28 00:00:00 +01:00
8
8
  summary: Code coverage analysis tool for Ruby
9
9
  require_paths:
10
10
  - lib
@@ -48,16 +48,23 @@ files:
48
48
  - README.API
49
49
  - README.vim
50
50
  - README.rant
51
+ - README.emacs
51
52
  - THANKS
52
53
  - test/sample_02.rb
53
54
  - test/sample_01.rb
54
55
  - test/sample_03.rb
56
+ - test/sample_04.rb
55
57
  - test/test_CodeCoverageAnalyzer.rb
56
58
  - test/test_FileStatistics.rb
57
59
  - test/turn_off_rcovrt.rb
58
60
  - test/test_CallSiteAnalyzer.rb
61
+ - test/sample_05.rb
62
+ - test/test_functional.rb
63
+ - test/sample_05-new.rb
64
+ - test/sample_05-old.rb
59
65
  - mingw-rbconfig.rb
60
66
  - rcov.vim
67
+ - rcov.el
61
68
  - setup.rb
62
69
  - BLURB
63
70
  - CHANGES
@@ -65,6 +72,7 @@ test_files:
65
72
  - test/test_CodeCoverageAnalyzer.rb
66
73
  - test/test_FileStatistics.rb
67
74
  - test/test_CallSiteAnalyzer.rb
75
+ - test/test_functional.rb
68
76
  rdoc_options:
69
77
  - --main
70
78
  - README.API