rcov 0.6.0.1-mswin32 → 0.7.0.1-mswin32
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 +1 -1
- data/CHANGES +18 -0
- data/Rakefile +4 -3
- data/THANKS +9 -0
- data/bin/rcov +43 -3
- data/ext/rcovrt/callsite.c +242 -0
- data/ext/rcovrt/{rcov.c → rcovrt.c} +48 -216
- data/lib/rcov/rcovtask.rb +1 -1
- data/lib/rcov/report.rb +8 -2
- data/lib/rcov/version.rb +2 -2
- data/lib/rcov.rb +45 -3
- data/lib/rcovrt.so +0 -0
- data/mingw-rbconfig.rb +2 -2
- data/rcov.vim +38 -0
- metadata +7 -4
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.
|
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
|
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
|
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
|
-
|
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
|
-
|
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(
|
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
|
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/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
|
-
|
8
|
-
|
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
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
|
-
|
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/rcovrt.so
CHANGED
Binary file
|
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.
|
11
|
-
|
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.
|
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.
|
7
|
-
date: 2006-
|
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: mswin32
|
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/
|
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
|