rcov 0.6.0.1 → 0.7.0.1

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.6.0
5
+ http://eigenclass.org/hiki.rb?rcov+0.7.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,24 @@
1
1
 
2
2
  User-visible changes.
3
3
 
4
+ Since 0.6.0 (2006-06-12)
5
+ ========================
6
+ Features
7
+ --------
8
+ * coverage/callsite data from multiple runs can be aggregated (--aggregate)
9
+
10
+ Bugfixes
11
+ --------
12
+ * the SCRIPT_LINES__ workaround works better
13
+ * fixed silly bug in coverage data acquisition (line after the correct one
14
+ marked in some situations)
15
+ * avoid problems with repeated path separators in default ignore list, based
16
+ on rbconfig's data
17
+
18
+
19
+ Minor enhancements
20
+ ------------------
21
+
4
22
  Since 0.5.0 (2006-05-30)
5
23
  ========================
6
24
  Features
data/Rakefile CHANGED
@@ -48,7 +48,7 @@ Rake::TestTask.new(:test_rcovrt => ["ext/rcovrt/rcovrt.so"]) do |t|
48
48
  t.verbose = true
49
49
  end
50
50
 
51
- file "ext/rcovrt/rcovrt.so" => "ext/rcovrt/rcov.c" do
51
+ file "ext/rcovrt/rcovrt.so" => FileList["ext/rcovrt/*.c"] do
52
52
  ruby "setup.rb config"
53
53
  ruby "setup.rb setup"
54
54
  end
@@ -87,9 +87,10 @@ PKG_FILES = FileList[
87
87
  "bin/rcov",
88
88
  "lib/**/*.rb",
89
89
  "ext/rcovrt/extconf.rb",
90
- "ext/rcovrt/rcov.c",
90
+ "ext/rcovrt/*.c",
91
+ "ext/rcovrt/*.h",
91
92
  "LEGAL", "LICENSE", "Rakefile", "Rantfile", "README.*", "THANKS", "test/*.rb",
92
- "mingw-rbconfig.rb",
93
+ "mingw-rbconfig.rb", "rcov.vim",
93
94
  "setup.rb", "BLURB", "CHANGES"
94
95
  ]
95
96
 
data/THANKS CHANGED
@@ -41,3 +41,12 @@ Coda Hale:
41
41
  Tim Shadel:
42
42
  * reported that the last comment block was not being marked even when
43
43
  it was the last thing in the file
44
+
45
+ Thomas Leitner:
46
+ * reported that the SCRIPT_LINES__ workaround did not always work
47
+
48
+ Assaph Mehr:
49
+ * beta-tested 0.7.0 and found a bug in --aggregate (missing files)
50
+
51
+ Ryan Kinderman:
52
+ * suggested that -Ipath be passed to ruby instead of rcov in RcovTasks
data/bin/rcov CHANGED
@@ -56,6 +56,7 @@ options.coverage_diff_mode = :compare
56
56
  options.coverage_diff_save = false
57
57
  options.diff_cmd = "diff"
58
58
  options.report_cov_bug_for = nil
59
+ options.aggregate_file = nil
59
60
 
60
61
  EXTRA_HELP = <<-EOF
61
62
 
@@ -165,6 +166,12 @@ EOF
165
166
  "ANSI color sequences unless -n.") do
166
167
  options.textmode = :coverage
167
168
  end
169
+ opts.on("--aggregate FILE", "Aggregate data from previous runs",
170
+ "in FILE. Overwrites FILE with the",
171
+ "merged data. FILE is created if",
172
+ "necessary.") do |file|
173
+ options.aggregate_file = file
174
+ end
168
175
  opts.on("-D [FILE]", "--text-coverage-diff [FILE]",
169
176
  "Compare code coverage with saved state",
170
177
  "in FILE, defaults to coverage.info.",
@@ -177,7 +184,8 @@ EOF
177
184
  options.coverage_diff_mode = :compare
178
185
  options.coverage_diff_file = file if file && !file.empty?
179
186
  end
180
- opts.on("--save [FILE]", "Save coverage data to FILE.",
187
+ opts.on("--save [FILE]", "Save coverage data to FILE,",
188
+ "for later use with rcov -D.",
181
189
  "(default: coverage.info)") do |file|
182
190
  options.coverage_diff_save = true
183
191
  options.coverage_diff_mode = :record
@@ -289,8 +297,33 @@ require 'rcov'
289
297
 
290
298
  options.callsites = true if options.report_cov_bug_for
291
299
 
300
+ def rcov_load_aggregate_data(file)
301
+ require 'zlib'
302
+ begin
303
+ old_data = nil
304
+ Zlib::GzipReader.open(file){|gz| old_data = Marshal.load(gz) }
305
+ rescue
306
+ old_data = {}
307
+ end
308
+ old_data || {}
309
+ end
310
+
311
+ def rcov_save_aggregate_data(file)
312
+ require 'zlib'
313
+ Zlib::GzipWriter.open(file) do |f|
314
+ Marshal.dump({:callsites => $rcov_callsite_analyzer,
315
+ :coverage => $rcov_code_coverage_analyzer}, f)
316
+ end
317
+ end
318
+
292
319
  if options.callsites
293
- $rcov_callsite_analyzer = Rcov::CallSiteAnalyzer.new
320
+ if options.aggregate_file
321
+ saved_aggregate_data = rcov_load_aggregate_data(options.aggregate_file)
322
+ if saved_aggregate_data[:callsites]
323
+ $rcov_callsite_analyzer = saved_aggregate_data[:callsites]
324
+ end
325
+ end
326
+ $rcov_callsite_analyzer ||= Rcov::CallSiteAnalyzer.new
294
327
  $rcov_callsite_analyzer.install_hook
295
328
  else
296
329
  $rcov_callsite_analyzer = nil
@@ -333,12 +366,19 @@ end
333
366
 
334
367
  formatters << make_formatter[Rcov::TextCoverageDiff] if options.coverage_diff_save
335
368
 
336
- $rcov_code_coverage_analyzer = Rcov::CodeCoverageAnalyzer.new
369
+ if options.aggregate_file
370
+ saved_aggregate_data ||= rcov_load_aggregate_data(options.aggregate_file)
371
+ if saved_aggregate_data[:coverage]
372
+ $rcov_code_coverage_analyzer = saved_aggregate_data[:coverage]
373
+ end
374
+ end
375
+ $rcov_code_coverage_analyzer ||= Rcov::CodeCoverageAnalyzer.new
337
376
 
338
377
  # must be registered before test/unit puts its own
339
378
  END {
340
379
  $rcov_code_coverage_analyzer.remove_hook
341
380
  $rcov_callsite_analyzer.remove_hook if $rcov_callsite_analyzer
381
+ rcov_save_aggregate_data(options.aggregate_file) if options.aggregate_file
342
382
  $rcov_code_coverage_analyzer.dump_coverage_info(formatters)
343
383
  if options.report_cov_bug_for
344
384
  defsite = $rcov_callsite_analyzer.defsite(options.report_cov_bug_for)
@@ -0,0 +1,242 @@
1
+ #include <ruby.h>
2
+ #include <env.h>
3
+ #include <node.h>
4
+ #include <st.h>
5
+ #include <stdlib.h>
6
+
7
+
8
+ static char callsite_hook_set_p;
9
+
10
+ typedef struct {
11
+ char *sourcefile;
12
+ unsigned int sourceline;
13
+ VALUE curr_meth;
14
+ } type_def_site;
15
+ static VALUE caller_info = 0;
16
+ static VALUE method_def_site_info = 0;
17
+
18
+ static caller_stack_len = 1;
19
+
20
+ /*
21
+ *
22
+ * callsite hook and associated functions
23
+ *
24
+ * */
25
+
26
+ static VALUE
27
+ record_callsite_info(VALUE args)
28
+ {
29
+ VALUE caller_ary;
30
+ VALUE curr_meth;
31
+ VALUE count_hash;
32
+ VALUE count;
33
+ VALUE *pargs = (VALUE *)args;
34
+
35
+ caller_ary = pargs[0];
36
+ curr_meth = pargs[1];
37
+ count_hash = rb_hash_aref(caller_info, curr_meth);
38
+ if(TYPE(count_hash) != T_HASH) {
39
+ /* Qnil, anything else should be impossible unless somebody's been
40
+ * messing with ObjectSpace */
41
+ count_hash = rb_hash_new();
42
+ rb_hash_aset(caller_info, curr_meth, count_hash);
43
+ }
44
+ count = rb_hash_aref(count_hash, caller_ary);
45
+ if(count == Qnil)
46
+ count = INT2FIX(0);
47
+ count = INT2FIX(FIX2UINT(count) + 1);
48
+ rb_hash_aset(count_hash, caller_ary, count);
49
+ /*
50
+ printf("CALLSITE: %s -> %s %d\n", RSTRING(rb_inspect(curr_meth))->ptr,
51
+ RSTRING(rb_inspect(caller_ary))->ptr, FIX2INT(count));
52
+ */
53
+
54
+ return Qnil;
55
+ }
56
+
57
+
58
+ static VALUE
59
+ record_method_def_site(VALUE args)
60
+ {
61
+ type_def_site *pargs = (type_def_site *)args;
62
+ VALUE def_site_info;
63
+ VALUE hash;
64
+
65
+ if( RTEST(rb_hash_aref(method_def_site_info, pargs->curr_meth)) )
66
+ return Qnil;
67
+ def_site_info = rb_ary_new();
68
+ rb_ary_push(def_site_info, rb_str_new2(pargs->sourcefile));
69
+ rb_ary_push(def_site_info, INT2NUM(pargs->sourceline+1));
70
+ rb_hash_aset(method_def_site_info, pargs->curr_meth, def_site_info);
71
+ /*
72
+ printf("DEFSITE: %s:%d for %s\n", pargs->sourcefile, pargs->sourceline+1,
73
+ RSTRING(rb_inspect(pargs->curr_meth))->ptr);
74
+ */
75
+
76
+ return Qnil;
77
+ }
78
+
79
+ static VALUE
80
+ callsite_custom_backtrace(int lev)
81
+ {
82
+ struct FRAME *frame = ruby_frame;
83
+ VALUE ary;
84
+ NODE *n;
85
+ VALUE level;
86
+ VALUE klass;
87
+
88
+ ary = rb_ary_new();
89
+ if (frame->last_func == ID_ALLOCATOR) {
90
+ frame = frame->prev;
91
+ }
92
+ for (; frame && (n = frame->node); frame = frame->prev) {
93
+ if (frame->prev && frame->prev->last_func) {
94
+ if (frame->prev->node == n) continue;
95
+ level = rb_ary_new();
96
+ klass = frame->prev->last_class ? frame->prev->last_class : Qnil;
97
+ if(TYPE(klass) == T_ICLASS) {
98
+ klass = CLASS_OF(klass);
99
+ }
100
+ rb_ary_push(level, klass);
101
+ rb_ary_push(level, ID2SYM(frame->prev->last_func));
102
+ rb_ary_push(level, rb_str_new2(n->nd_file));
103
+ rb_ary_push(level, INT2NUM(nd_line(n)));
104
+ }
105
+ else {
106
+ level = rb_ary_new();
107
+ rb_ary_push(level, Qnil);
108
+ rb_ary_push(level, Qnil);
109
+ rb_ary_push(level, rb_str_new2(n->nd_file));
110
+ rb_ary_push(level, INT2NUM(nd_line(n)));
111
+ }
112
+ rb_ary_push(ary, level);
113
+ if(--lev == 0)
114
+ break;
115
+ }
116
+
117
+ return ary;
118
+ }
119
+
120
+ static void
121
+ coverage_event_callsite_hook(rb_event_t event, NODE *node, VALUE self,
122
+ ID mid, VALUE klass)
123
+ {
124
+ VALUE caller_ary;
125
+ VALUE curr_meth;
126
+ VALUE args[2];
127
+ int status;
128
+
129
+ caller_ary = callsite_custom_backtrace(caller_stack_len);
130
+
131
+ if(TYPE(klass) == T_ICLASS) {
132
+ klass = CLASS_OF(klass);
133
+ }
134
+ curr_meth = rb_ary_new();
135
+ rb_ary_push(curr_meth, klass);
136
+ rb_ary_push(curr_meth, ID2SYM(mid));
137
+
138
+ args[0] = caller_ary;
139
+ args[1] = curr_meth;
140
+ rb_protect(record_callsite_info, (VALUE)args, &status);
141
+ if(!status && node) {
142
+ type_def_site args;
143
+
144
+ args.sourcefile = node->nd_file;
145
+ args.sourceline = nd_line(node) - 1;
146
+ args.curr_meth = curr_meth;
147
+ rb_protect(record_method_def_site, (VALUE)&args, NULL);
148
+ }
149
+ if(status)
150
+ rb_gv_set("$!", Qnil);
151
+ }
152
+
153
+
154
+ static VALUE
155
+ cov_install_callsite_hook(VALUE self)
156
+ {
157
+ if(!callsite_hook_set_p) {
158
+ if(TYPE(caller_info) != T_HASH)
159
+ caller_info = rb_hash_new();
160
+ callsite_hook_set_p = 1;
161
+ rb_add_event_hook(coverage_event_callsite_hook,
162
+ RUBY_EVENT_CALL);
163
+
164
+ return Qtrue;
165
+ } else
166
+ return Qfalse;
167
+ }
168
+
169
+
170
+ static VALUE
171
+ cov_remove_callsite_hook(VALUE self)
172
+ {
173
+ if(!callsite_hook_set_p)
174
+ return Qfalse;
175
+ else {
176
+ rb_remove_event_hook(coverage_event_callsite_hook);
177
+ callsite_hook_set_p = 0;
178
+ return Qtrue;
179
+ }
180
+ }
181
+
182
+
183
+ static VALUE
184
+ cov_generate_callsite_info(VALUE self)
185
+ {
186
+ VALUE ret;
187
+
188
+ ret = rb_ary_new();
189
+ rb_ary_push(ret, caller_info);
190
+ rb_ary_push(ret, method_def_site_info);
191
+ return ret;
192
+ }
193
+
194
+
195
+ static VALUE
196
+ cov_reset_callsite(VALUE self)
197
+ {
198
+ if(callsite_hook_set_p) {
199
+ rb_raise(rb_eRuntimeError,
200
+ "Cannot reset the callsite info in the middle of a traced run.");
201
+ return Qnil;
202
+ }
203
+
204
+ caller_info = rb_hash_new();
205
+ method_def_site_info = rb_hash_new();
206
+ return Qnil;
207
+ }
208
+
209
+ void
210
+ Init_rcov_callsite()
211
+ {
212
+ VALUE mRcov;
213
+ VALUE mRCOV__;
214
+ ID id_rcov = rb_intern("Rcov");
215
+ ID id_coverage__ = rb_intern("RCOV__");
216
+ ID id_script_lines__ = rb_intern("SCRIPT_LINES__");
217
+
218
+ if(rb_const_defined(rb_cObject, id_rcov))
219
+ mRcov = rb_const_get(rb_cObject, id_rcov);
220
+ else
221
+ mRcov = rb_define_module("Rcov");
222
+
223
+ if(rb_const_defined(mRcov, id_coverage__))
224
+ mRCOV__ = rb_const_get_at(mRcov, id_coverage__);
225
+ else
226
+ mRCOV__ = rb_define_module_under(mRcov, "RCOV__");
227
+
228
+ callsite_hook_set_p = 0;
229
+ caller_info = rb_hash_new();
230
+ method_def_site_info = rb_hash_new();
231
+ rb_gc_register_address(&caller_info);
232
+ rb_gc_register_address(&method_def_site_info);
233
+
234
+ rb_define_singleton_method(mRCOV__, "install_callsite_hook",
235
+ cov_install_callsite_hook, 0);
236
+ rb_define_singleton_method(mRCOV__, "remove_callsite_hook",
237
+ cov_remove_callsite_hook, 0);
238
+ rb_define_singleton_method(mRCOV__, "generate_callsite_info",
239
+ cov_generate_callsite_info, 0);
240
+ rb_define_singleton_method(mRCOV__, "reset_callsite", cov_reset_callsite, 0);
241
+ }
242
+ /* vim: set sw=8 expandtab: */
@@ -5,6 +5,8 @@
5
5
  #include <st.h>
6
6
  #include <stdlib.h>
7
7
 
8
+ #define COVERAGE_DEBUG_EVENTS 0
9
+
8
10
  #define RCOVRT_VERSION_MAJOR 2
9
11
  #define RCOVRT_VERSION_MINOR 0
10
12
  #define RCOVRT_VERSION_REV 0
@@ -15,7 +17,6 @@ static VALUE oSCRIPT_LINES__;
15
17
  static ID id_cover;
16
18
  static st_table* coverinfo = 0;
17
19
  static char coverage_hook_set_p;
18
- static char callsite_hook_set_p;
19
20
 
20
21
  struct cov_array {
21
22
  unsigned int len;
@@ -25,204 +26,6 @@ struct cov_array {
25
26
  static struct cov_array *cached_array = 0;
26
27
  static char *cached_file = 0;
27
28
 
28
- typedef struct {
29
- char *sourcefile;
30
- unsigned int sourceline;
31
- VALUE curr_meth;
32
- } type_def_site;
33
- static VALUE caller_info = 0;
34
- static VALUE method_def_site_info = 0;
35
-
36
- static caller_stack_len = 1;
37
-
38
- /*
39
- *
40
- * callsite hook and associated functions
41
- *
42
- * */
43
-
44
- static VALUE
45
- record_callsite_info(VALUE args)
46
- {
47
- VALUE caller_ary;
48
- VALUE curr_meth;
49
- VALUE count_hash;
50
- VALUE count;
51
- VALUE *pargs = (VALUE *)args;
52
-
53
- caller_ary = pargs[0];
54
- curr_meth = pargs[1];
55
- count_hash = rb_hash_aref(caller_info, curr_meth);
56
- if(TYPE(count_hash) != T_HASH) {
57
- /* Qnil, anything else should be impossible unless somebody's been
58
- * messing with ObjectSpace */
59
- count_hash = rb_hash_new();
60
- rb_hash_aset(caller_info, curr_meth, count_hash);
61
- }
62
- count = rb_hash_aref(count_hash, caller_ary);
63
- if(count == Qnil)
64
- count = INT2FIX(0);
65
- count = INT2FIX(FIX2UINT(count) + 1);
66
- rb_hash_aset(count_hash, caller_ary, count);
67
- /*
68
- printf("CALLSITE: %s -> %s %d\n", RSTRING(rb_inspect(curr_meth))->ptr,
69
- RSTRING(rb_inspect(caller_ary))->ptr, FIX2INT(count));
70
- */
71
-
72
- return Qnil;
73
- }
74
-
75
-
76
- static VALUE
77
- record_method_def_site(VALUE args)
78
- {
79
- type_def_site *pargs = (type_def_site *)args;
80
- VALUE def_site_info;
81
- VALUE hash;
82
-
83
- if( RTEST(rb_hash_aref(method_def_site_info, pargs->curr_meth)) )
84
- return Qnil;
85
- def_site_info = rb_ary_new();
86
- rb_ary_push(def_site_info, rb_str_new2(pargs->sourcefile));
87
- rb_ary_push(def_site_info, INT2NUM(pargs->sourceline+1));
88
- rb_hash_aset(method_def_site_info, pargs->curr_meth, def_site_info);
89
- /*
90
- printf("DEFSITE: %s:%d for %s\n", pargs->sourcefile, pargs->sourceline+1,
91
- RSTRING(rb_inspect(pargs->curr_meth))->ptr);
92
- */
93
-
94
- return Qnil;
95
- }
96
-
97
- static VALUE
98
- callsite_custom_backtrace(int lev)
99
- {
100
- struct FRAME *frame = ruby_frame;
101
- VALUE ary;
102
- NODE *n;
103
- VALUE level;
104
- VALUE klass;
105
-
106
- ary = rb_ary_new();
107
- if (frame->last_func == ID_ALLOCATOR) {
108
- frame = frame->prev;
109
- }
110
- for (; frame && (n = frame->node); frame = frame->prev) {
111
- if (frame->prev && frame->prev->last_func) {
112
- if (frame->prev->node == n) continue;
113
- level = rb_ary_new();
114
- klass = frame->prev->last_class ? frame->prev->last_class : Qnil;
115
- if(TYPE(klass) == T_ICLASS) {
116
- klass = CLASS_OF(klass);
117
- }
118
- rb_ary_push(level, klass);
119
- rb_ary_push(level, ID2SYM(frame->prev->last_func));
120
- rb_ary_push(level, rb_str_new2(n->nd_file));
121
- rb_ary_push(level, INT2NUM(nd_line(n)));
122
- }
123
- else {
124
- level = rb_ary_new();
125
- rb_ary_push(level, Qnil);
126
- rb_ary_push(level, Qnil);
127
- rb_ary_push(level, rb_str_new2(n->nd_file));
128
- rb_ary_push(level, INT2NUM(nd_line(n)));
129
- }
130
- rb_ary_push(ary, level);
131
- if(--lev == 0)
132
- break;
133
- }
134
-
135
- return ary;
136
- }
137
-
138
- static void
139
- coverage_event_callsite_hook(rb_event_t event, NODE *node, VALUE self,
140
- ID mid, VALUE klass)
141
- {
142
- VALUE caller_ary;
143
- VALUE curr_meth;
144
- VALUE args[2];
145
- int status;
146
-
147
- caller_ary = callsite_custom_backtrace(caller_stack_len);
148
-
149
- if(TYPE(klass) == T_ICLASS) {
150
- klass = CLASS_OF(klass);
151
- }
152
- curr_meth = rb_ary_new();
153
- rb_ary_push(curr_meth, klass);
154
- rb_ary_push(curr_meth, ID2SYM(mid));
155
-
156
- args[0] = caller_ary;
157
- args[1] = curr_meth;
158
- rb_protect(record_callsite_info, (VALUE)args, &status);
159
- if(!status && node) {
160
- type_def_site args;
161
-
162
- args.sourcefile = node->nd_file;
163
- args.sourceline = nd_line(node) - 1;
164
- args.curr_meth = curr_meth;
165
- rb_protect(record_method_def_site, (VALUE)&args, NULL);
166
- }
167
- if(status)
168
- rb_gv_set("$!", Qnil);
169
- }
170
-
171
-
172
- static VALUE
173
- cov_install_callsite_hook(VALUE self)
174
- {
175
- if(!callsite_hook_set_p) {
176
- if(TYPE(caller_info) != T_HASH)
177
- caller_info = rb_hash_new();
178
- callsite_hook_set_p = 1;
179
- rb_add_event_hook(coverage_event_callsite_hook,
180
- RUBY_EVENT_CALL);
181
-
182
- return Qtrue;
183
- } else
184
- return Qfalse;
185
- }
186
-
187
-
188
- static VALUE
189
- cov_remove_callsite_hook(VALUE self)
190
- {
191
- if(!callsite_hook_set_p)
192
- return Qfalse;
193
- else {
194
- rb_remove_event_hook(coverage_event_callsite_hook);
195
- callsite_hook_set_p = 0;
196
- return Qtrue;
197
- }
198
- }
199
-
200
-
201
- static VALUE
202
- cov_generate_callsite_info(VALUE self)
203
- {
204
- VALUE ret;
205
-
206
- ret = rb_ary_new();
207
- rb_ary_push(ret, caller_info);
208
- rb_ary_push(ret, method_def_site_info);
209
- return ret;
210
- }
211
-
212
-
213
- static VALUE
214
- cov_reset_callsite(VALUE self)
215
- {
216
- if(callsite_hook_set_p) {
217
- rb_raise(rb_eRuntimeError,
218
- "Cannot reset the callsite info in the middle of a traced run.");
219
- return Qnil;
220
- }
221
-
222
- caller_info = rb_hash_new();
223
- method_def_site_info = rb_hash_new();
224
- return Qnil;
225
- }
226
29
 
227
30
  /*
228
31
  *
@@ -272,10 +75,10 @@ coverage_mark_caller()
272
75
  for (; frame && (n = frame->node); frame = frame->prev) {
273
76
  if (frame->prev && frame->prev->last_func) {
274
77
  if (frame->prev->node == n) continue;
275
- coverage_increase_counter_uncached(n->nd_file, nd_line(n), 1);
78
+ coverage_increase_counter_uncached(n->nd_file, nd_line(n) - 1, 1);
276
79
  }
277
80
  else {
278
- coverage_increase_counter_uncached(n->nd_file, nd_line(n), 1);
81
+ coverage_increase_counter_uncached(n->nd_file, nd_line(n) - 1, 1);
279
82
  }
280
83
  break;
281
84
  }
@@ -300,19 +103,56 @@ coverage_event_coverage_hook(rb_event_t event, NODE *node, VALUE self,
300
103
  {
301
104
  char *sourcefile;
302
105
  unsigned int sourceline;
106
+ static unsigned int in_hook = 0;
303
107
 
304
- if(event & (RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN | RUBY_EVENT_CLASS))
108
+ if(in_hook) {
305
109
  return;
110
+ }
111
+
112
+ in_hook++;
113
+
114
+ #if COVERAGE_DEBUG_EVENTS
115
+ do {
116
+ int status;
117
+ VALUE old_exception;
118
+ old_exception = rb_gv_get("$!");
119
+ rb_protect(rb_inspect, klass, &status);
120
+ if(!status) {
121
+ printf("EVENT: %d %s %s %s %d\n", event,
122
+ klass ? RSTRING(rb_inspect(klass))->ptr : "",
123
+ mid ? (mid == ID_ALLOCATOR ? "ID_ALLOCATOR" : rb_id2name(mid))
124
+ : "unknown",
125
+ node ? node->nd_file : "", node ? nd_line(node) : 0);
126
+ } else {
127
+ printf("EVENT: %d %s %s %d\n", event,
128
+ mid ? (mid == ID_ALLOCATOR ? "ID_ALLOCATOR" : rb_id2name(mid))
129
+ : "unknown",
130
+ node ? node->nd_file : "", node ? nd_line(node) : 0);
131
+ }
132
+ rb_gv_set("$!", old_exception);
133
+ } while (0);
134
+ #endif
135
+
136
+ if(event & RUBY_EVENT_C_CALL) {
137
+ coverage_mark_caller();
138
+ }
139
+ if(event & (RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN | RUBY_EVENT_CLASS)) {
140
+ in_hook--;
141
+ return;
142
+ }
306
143
 
307
- if(node == NULL)
144
+ if(node == NULL) {
145
+ in_hook--;
308
146
  return;
147
+ }
309
148
 
310
149
  sourcefile = node->nd_file;
311
150
  sourceline = nd_line(node) - 1;
312
151
 
313
152
  coverage_increase_counter_cached(sourcefile, sourceline);
314
- if(event == RUBY_EVENT_CALL)
153
+ if(event & RUBY_EVENT_CALL)
315
154
  coverage_mark_caller();
155
+ in_hook--;
316
156
  }
317
157
 
318
158
 
@@ -323,6 +163,8 @@ cov_install_coverage_hook(VALUE self)
323
163
  if(!coverinfo)
324
164
  coverinfo = st_init_strtable();
325
165
  coverage_hook_set_p = 1;
166
+ /* TODO: allow C_CALL too, since it's supported already
167
+ * the overhead is around ~30%, tested on typo */
326
168
  rb_add_event_hook(coverage_event_coverage_hook,
327
169
  RUBY_EVENT_ALL & ~RUBY_EVENT_C_CALL &
328
170
  ~RUBY_EVENT_C_RETURN & ~RUBY_EVENT_CLASS);
@@ -461,27 +303,17 @@ Init_rcovrt()
461
303
  rb_const_set(rb_cObject, id_script_lines__, oSCRIPT_LINES__);
462
304
  }
463
305
 
464
- caller_info = rb_hash_new();
465
- method_def_site_info = rb_hash_new();
466
- rb_gc_register_address(&caller_info);
467
- rb_gc_register_address(&method_def_site_info);
468
-
469
306
  coverage_hook_set_p = 0;
470
307
 
471
308
  rb_define_singleton_method(mRCOV__, "install_coverage_hook",
472
309
  cov_install_coverage_hook, 0);
473
310
  rb_define_singleton_method(mRCOV__, "remove_coverage_hook",
474
311
  cov_remove_coverage_hook, 0);
475
- rb_define_singleton_method(mRCOV__, "install_callsite_hook",
476
- cov_install_callsite_hook, 0);
477
- rb_define_singleton_method(mRCOV__, "remove_callsite_hook",
478
- cov_remove_callsite_hook, 0);
479
312
  rb_define_singleton_method(mRCOV__, "generate_coverage_info",
480
313
  cov_generate_coverage_info, 0);
481
- rb_define_singleton_method(mRCOV__, "generate_callsite_info",
482
- cov_generate_callsite_info, 0);
483
314
  rb_define_singleton_method(mRCOV__, "reset_coverage", cov_reset_coverage, 0);
484
- rb_define_singleton_method(mRCOV__, "reset_callsite", cov_reset_callsite, 0);
485
315
  rb_define_singleton_method(mRCOV__, "ABI", cov_ABI, 0);
316
+
317
+ Init_rcov_callsite();
486
318
  }
487
319
  /* vim: set sw=8 expandtab: */
data/lib/rcov.rb CHANGED
@@ -559,6 +559,7 @@ class CodeCoverageAnalyzer < DifferentialAnalyzer
559
559
  # Return an array with the names of the files whose code was executed inside
560
560
  # the block given to #run_hooked or between #install_hook and #remove_hook.
561
561
  def analyzed_files
562
+ update_script_lines__
562
563
  raw_data_relative.select do |file, lines|
563
564
  @script_lines__.has_key?(file)
564
565
  end.map{|fname,| fname}
@@ -581,6 +582,7 @@ class CodeCoverageAnalyzer < DifferentialAnalyzer
581
582
  # reset the data at any time with #reset to start from scratch.
582
583
  def data(filename)
583
584
  raw_data = raw_data_relative
585
+ update_script_lines__
584
586
  unless @script_lines__.has_key?(filename) &&
585
587
  raw_data.has_key?(filename)
586
588
  return nil
@@ -611,6 +613,7 @@ class CodeCoverageAnalyzer < DifferentialAnalyzer
611
613
  def reset; super end
612
614
 
613
615
  def dump_coverage_info(formatters) # :nodoc:
616
+ update_script_lines__
614
617
  raw_data_relative.each do |file, lines|
615
618
  next if @script_lines__.has_key?(file) == false
616
619
  lines = @script_lines__[file]
@@ -690,19 +693,58 @@ class CodeCoverageAnalyzer < DifferentialAnalyzer
690
693
  ! different
691
694
  end
692
695
 
693
- # FIXME find all prime factors, try them all
694
- factors = (2..10).to_a.reverse
696
+ factors = braindead_factorize(line_info.size)
695
697
  factors.each do |n|
696
698
  if is_repeated[n]
697
699
  line_info = line_info[0, line_info.size / n]
698
700
  coverage_info = coverage_info[0, coverage_info.size / n]
699
701
  count_info = count_info[0, count_info.size / n]
700
702
  end
701
- end
703
+ end if factors.size > 1 # don't even try if it's prime
702
704
 
703
705
  [line_info, coverage_info, count_info]
704
706
  end
705
707
 
708
+ def braindead_factorize(num)
709
+ return [0] if num == 0
710
+ return [-1] + braindead_factorize(-num) if num < 0
711
+ factors = []
712
+ while num % 2 == 0
713
+ factors << 2
714
+ num /= 2
715
+ end
716
+ size = num
717
+ n = 3
718
+ max = Math.sqrt(num)
719
+ while n <= max && n <= size
720
+ while size % n == 0
721
+ size /= n
722
+ factors << n
723
+ end
724
+ n += 2
725
+ end
726
+ factors << size if size != 1
727
+ factors
728
+ end
729
+
730
+ def update_script_lines__
731
+ @script_lines__ = @script_lines__.merge(SCRIPT_LINES__)
732
+ end
733
+
734
+ public
735
+ def marshal_dump # :nodoc:
736
+ # @script_lines__ is updated just before serialization so as to avoid
737
+ # missing files in SCRIPT_LINES__
738
+ ivs = {}
739
+ update_script_lines__
740
+ instance_variables.each{|iv| ivs[iv] = instance_variable_get(iv)}
741
+ ivs
742
+ end
743
+
744
+ def marshal_load(ivs) # :nodoc:
745
+ ivs.each_pair{|iv, val| instance_variable_set(iv, val)}
746
+ end
747
+
706
748
  end # CodeCoverageAnalyzer
707
749
 
708
750
  # A CallSiteAnalyzer can be used to obtain information about:
data/lib/rcov/rcovtask.rb CHANGED
@@ -112,8 +112,8 @@ module Rcov
112
112
  else %!"#{rcov_path}"!
113
113
  end
114
114
  ruby_opts = @ruby_opts.clone
115
- ruby_opts.push run_code
116
115
  ruby_opts.push( "-I#{lib_path}" )
116
+ ruby_opts.push run_code
117
117
  ruby_opts.push( "-w" ) if @warning
118
118
  ruby ruby_opts.join(" ") + " " + option_list +
119
119
  %[ -o "#{@output_dir}" ] +
data/lib/rcov/report.rb CHANGED
@@ -4,8 +4,14 @@
4
4
  module Rcov
5
5
 
6
6
  class Formatter # :nodoc:
7
- ignore_files = [/\A#{Regexp.escape(Config::CONFIG["libdir"])}/, /\btc_[^.]*.rb/,
8
- /_test\.rb\z/, /\btest\//, /\bvendor\//, /\A#{Regexp.escape(__FILE__)}\z/]
7
+ require 'pathname'
8
+ ignore_files = [
9
+ /\A#{Regexp.escape(Pathname.new(Config::CONFIG["libdir"]).cleanpath.to_s)}/,
10
+ /\btc_[^.]*.rb/,
11
+ /_test\.rb\z/,
12
+ /\btest\//,
13
+ /\bvendor\//,
14
+ /\A#{Regexp.escape(__FILE__)}\z/]
9
15
  DEFAULT_OPTS = {:ignore => ignore_files, :sort => :name, :sort_reverse => false,
10
16
  :output_threshold => 101, :dont_ignore => [],
11
17
  :callsite_analyzer => nil, :comments_run_by_default => false}
data/lib/rcov/version.rb CHANGED
@@ -4,8 +4,8 @@
4
4
 
5
5
  module Rcov
6
6
 
7
- VERSION = "0.6.0"
8
- RELEASE_DATE = "2006-06-12"
7
+ VERSION = "0.7.0"
8
+ RELEASE_DATE = "2006/08/04"
9
9
  RCOVRT_ABI = [2,0,0]
10
10
  UPSTREAM_URL = "http://eigenclass.org/hiki.rb?rcov"
11
11
 
data/mingw-rbconfig.rb CHANGED
@@ -7,8 +7,8 @@
7
7
  # mswin32 builds) is installed under $HOME/ruby-mingw32.
8
8
 
9
9
  module Config
10
- RUBY_VERSION == "1.8.4" or
11
- raise "ruby lib version (1.8.4) doesn't match executable version (#{RUBY_VERSION})"
10
+ #RUBY_VERSION == "1.8.5" or
11
+ # raise "ruby lib version (1.8.5) doesn't match executable version (#{RUBY_VERSION})"
12
12
 
13
13
  TOPDIR = File.dirname(__FILE__).chomp!("/lib/ruby/1.8/i386-mingw32")
14
14
  DESTDIR = '' unless defined? DESTDIR
data/rcov.vim ADDED
@@ -0,0 +1,38 @@
1
+ " Vim compiler file
2
+ " Language: Ruby
3
+ " Function: Code coverage information with rcov
4
+ " Maintainer: Mauricio Fernandez <mfp at acm dot org>
5
+ " Info:
6
+ " URL: http://eigenclass.org/hiki.rb?rcov
7
+ " ----------------------------------------------------------------------------
8
+ "
9
+ " Changelog:
10
+ " 0.1: initial version, shipped with rcov 0.6.0
11
+ "
12
+ " Comments:
13
+ " Initial attempt.
14
+ " ----------------------------------------------------------------------------
15
+
16
+ if exists("current_compiler")
17
+ finish
18
+ endif
19
+ let current_compiler = "rcov"
20
+
21
+ if exists(":CompilerSet") != 2 " older Vim always used :setlocal
22
+ command -nargs=* CompilerSet setlocal <args>
23
+ endif
24
+
25
+ let s:cpo_save = &cpo
26
+ set cpo-=C
27
+
28
+ CompilerSet makeprg=rake\ $*\ RCOVOPTS=\"-D\ --no-html\ --no-color\"\ $*
29
+
30
+ CompilerSet errorformat=
31
+ \%+W\#\#\#\ %f:%l\,
32
+ \%-C\ \ \ ,
33
+ \%-C!!\
34
+
35
+ let &cpo = s:cpo_save
36
+ unlet s:cpo_save
37
+
38
+ " vim: nowrap sw=2 sts=2 ts=8 ff=unix :
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.11
2
+ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: rcov
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.6.0.1
7
- date: 2006-06-12 00:00:00 +02:00
6
+ version: 0.7.0.1
7
+ date: 2006-08-04 00:00:00 +02:00
8
8
  summary: Code coverage analysis tool for Ruby
9
9
  require_paths:
10
10
  - lib
@@ -25,6 +25,7 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
25
25
  platform: ruby
26
26
  signing_key:
27
27
  cert_chain:
28
+ post_install_message:
28
29
  authors:
29
30
  - Mauricio Fernandez
30
31
  files:
@@ -36,7 +37,8 @@ files:
36
37
  - lib/rcov/rant.rb
37
38
  - lib/rcov/report.rb
38
39
  - ext/rcovrt/extconf.rb
39
- - ext/rcovrt/rcov.c
40
+ - ext/rcovrt/rcovrt.c
41
+ - ext/rcovrt/callsite.c
40
42
  - LEGAL
41
43
  - LICENSE
42
44
  - Rakefile
@@ -55,6 +57,7 @@ files:
55
57
  - test/turn_off_rcovrt.rb
56
58
  - test/test_CallSiteAnalyzer.rb
57
59
  - mingw-rbconfig.rb
60
+ - rcov.vim
58
61
  - setup.rb
59
62
  - BLURB
60
63
  - CHANGES