зеркало из https://github.com/github/ruby.git
Revamp method coverage to support define_method
Traditionally, method coverage measurement was implemented by inserting `trace2` instruction to the head of method iseq. So, it just measured methods defined by `def` keyword. This commit drastically changes the measuring mechanism of method coverage; at `RUBY_EVENT_CALL`, it keeps a hash from rb_method_entry_t* to runs (i.e., it counts the runs per method entry), and at `Coverage.result`, it creates the result hash by enumerating all `rb_method_entry_t*` objects (by `ObjectSpace.each_object`). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61023 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
Родитель
aa87ae7a04
Коммит
0a6816ecd7
14
compile.c
14
compile.c
|
@ -294,19 +294,6 @@ struct iseq_compile_data_ensure_node_stack {
|
|||
ADD_INSN2((seq), (first_line), trace2, INT2FIX(RUBY_EVENT_COVERAGE), INT2FIX(counter_idx * 16 + COVERAGE_INDEX_BRANCHES)); \
|
||||
} \
|
||||
} while (0)
|
||||
#define ADD_TRACE_METHOD_COVERAGE(seq, line, method_name) \
|
||||
do { \
|
||||
if (ISEQ_COVERAGE(iseq) && \
|
||||
ISEQ_METHOD_COVERAGE(iseq) && \
|
||||
(line) > 0) { \
|
||||
VALUE methods = ISEQ_METHOD_COVERAGE(iseq); \
|
||||
long counter_idx = RARRAY_LEN(methods) / 3; \
|
||||
rb_ary_push(methods, ID2SYM(method_name)); \
|
||||
rb_ary_push(methods, INT2FIX(line)); \
|
||||
rb_ary_push(methods, INT2FIX(0)); \
|
||||
ADD_INSN2((seq), (line), trace2, INT2FIX(RUBY_EVENT_COVERAGE), INT2FIX(counter_idx * 16 + COVERAGE_INDEX_METHODS)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level);
|
||||
static void iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level);
|
||||
|
@ -670,7 +657,6 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
|
|||
case ISEQ_TYPE_METHOD:
|
||||
{
|
||||
ADD_TRACE(ret, RUBY_EVENT_CALL);
|
||||
ADD_TRACE_METHOD_COVERAGE(ret, FIX2INT(iseq->body->location.first_lineno), rb_intern_str(iseq->body->location.label));
|
||||
CHECK(COMPILE(ret, "scoped node", node->nd_body));
|
||||
ADD_TRACE(ret, RUBY_EVENT_RETURN);
|
||||
break;
|
||||
|
|
|
@ -10,8 +10,10 @@
|
|||
|
||||
#include "ruby.h"
|
||||
#include "vm_core.h"
|
||||
#include "gc.h"
|
||||
|
||||
static int current_mode;
|
||||
static VALUE me2counter = Qnil;
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
|
@ -55,13 +57,20 @@ rb_coverage_start(int argc, VALUE *argv, VALUE klass)
|
|||
}
|
||||
}
|
||||
|
||||
if (mode & COVERAGE_TARGET_METHODS) {
|
||||
me2counter = rb_hash_new_compare_by_id();
|
||||
}
|
||||
else {
|
||||
me2counter = Qnil;
|
||||
}
|
||||
|
||||
coverages = rb_get_coverages();
|
||||
if (!RTEST(coverages)) {
|
||||
coverages = rb_hash_new();
|
||||
rb_obj_hide(coverages);
|
||||
current_mode = mode;
|
||||
if (mode == 0) mode = COVERAGE_TARGET_LINES;
|
||||
rb_set_coverages(coverages, mode);
|
||||
rb_set_coverages(coverages, mode, me2counter);
|
||||
}
|
||||
else if (current_mode != mode) {
|
||||
rb_raise(rb_eRuntimeError, "cannot change the measuring target during coverage measurement");
|
||||
|
@ -101,21 +110,60 @@ branch_coverage(VALUE branches)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
method_coverage(VALUE methods)
|
||||
static int
|
||||
method_coverage_i(void *vstart, void *vend, size_t stride, void *data)
|
||||
{
|
||||
VALUE ret = rb_hash_new();
|
||||
int i;
|
||||
long id = 0;
|
||||
/*
|
||||
* ObjectSpace.each_object(Module){|mod|
|
||||
* mod.instance_methods.each{|mid|
|
||||
* m = mod.instance_method(mid)
|
||||
* if loc = m.source_location
|
||||
* p [m.name, loc, $g_method_cov_counts[m]]
|
||||
* end
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
VALUE ncoverages = *(VALUE*)data, v;
|
||||
|
||||
for (i = 0; i < RARRAY_LEN(methods); ) {
|
||||
VALUE method_name = RARRAY_AREF(methods, i++);
|
||||
VALUE lineno = RARRAY_AREF(methods, i++);
|
||||
VALUE counter = RARRAY_AREF(methods, i++);
|
||||
rb_hash_aset(ret, rb_ary_new_from_args(3, method_name, LONG2FIX(id++), lineno), counter);
|
||||
for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) {
|
||||
if (RB_TYPE_P(v, T_IMEMO) && imemo_type(v) == imemo_ment) {
|
||||
const rb_method_entry_t *me = (rb_method_entry_t *) v;
|
||||
VALUE path = Qundef, first_lineno = Qundef;
|
||||
VALUE data[2], ncoverage, methods;
|
||||
VALUE methods_id = ID2SYM(rb_intern("methods"));
|
||||
VALUE klass;
|
||||
const rb_method_entry_t *me2 = rb_resolve_me_location(me, data);
|
||||
if (me != me2) continue;
|
||||
klass = me->owner;
|
||||
if (RB_TYPE_P(klass, T_ICLASS)) {
|
||||
rb_bug("T_ICLASS");
|
||||
}
|
||||
path = data[0];
|
||||
first_lineno = data[1];
|
||||
if (FIX2LONG(first_lineno) <= 0) continue;
|
||||
ncoverage = rb_hash_aref(ncoverages, path);
|
||||
if (NIL_P(ncoverage)) continue;
|
||||
methods = rb_hash_aref(ncoverage, methods_id);
|
||||
|
||||
{
|
||||
VALUE method_id = ID2SYM(me->def->original_id);
|
||||
VALUE rcount = rb_hash_aref(me2counter, (VALUE) me);
|
||||
VALUE key = rb_ary_new_from_args(3, klass, method_id, first_lineno);
|
||||
VALUE rcount2 = rb_hash_aref(methods, key);
|
||||
|
||||
if (NIL_P(rcount)) rcount = LONG2FIX(0);
|
||||
if (NIL_P(rcount2)) rcount2 = LONG2FIX(0);
|
||||
if (!POSFIXABLE(FIX2LONG(rcount) + FIX2LONG(rcount2))) {
|
||||
rcount = LONG2FIX(FIXNUM_MAX);
|
||||
}
|
||||
else {
|
||||
rcount = LONG2FIX(FIX2LONG(rcount) + FIX2LONG(rcount2));
|
||||
}
|
||||
rb_hash_aset(methods, key, rcount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -132,25 +180,23 @@ coverage_peek_result_i(st_data_t key, st_data_t val, st_data_t h)
|
|||
}
|
||||
else {
|
||||
VALUE h = rb_hash_new();
|
||||
VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
|
||||
VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
|
||||
VALUE methods = RARRAY_AREF(coverage, COVERAGE_INDEX_METHODS);
|
||||
|
||||
if (lines) {
|
||||
if (current_mode & COVERAGE_TARGET_LINES) {
|
||||
VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
|
||||
lines = rb_ary_dup(lines);
|
||||
rb_ary_freeze(lines);
|
||||
rb_hash_aset(h, ID2SYM(rb_intern("lines")), lines);
|
||||
}
|
||||
|
||||
if (branches) {
|
||||
if (current_mode & COVERAGE_TARGET_BRANCHES) {
|
||||
VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
|
||||
rb_hash_aset(h, ID2SYM(rb_intern("branches")), branch_coverage(branches));
|
||||
}
|
||||
|
||||
if (methods) {
|
||||
rb_hash_aset(h, ID2SYM(rb_intern("methods")), method_coverage(methods));
|
||||
if (current_mode & COVERAGE_TARGET_METHODS) {
|
||||
rb_hash_aset(h, ID2SYM(rb_intern("methods")), rb_hash_new());
|
||||
}
|
||||
|
||||
rb_hash_freeze(h);
|
||||
coverage = h;
|
||||
}
|
||||
|
||||
|
@ -178,6 +224,11 @@ rb_coverage_peek_result(VALUE klass)
|
|||
rb_raise(rb_eRuntimeError, "coverage measurement is not enabled");
|
||||
}
|
||||
st_foreach(RHASH_TBL(coverages), coverage_peek_result_i, ncoverages);
|
||||
|
||||
if (current_mode & COVERAGE_TARGET_METHODS) {
|
||||
rb_objspace_each_objects(method_coverage_i, &ncoverages);
|
||||
}
|
||||
|
||||
rb_hash_freeze(ncoverages);
|
||||
return ncoverages;
|
||||
}
|
||||
|
@ -194,6 +245,7 @@ rb_coverage_result(VALUE klass)
|
|||
{
|
||||
VALUE ncoverages = rb_coverage_peek_result(klass);
|
||||
rb_reset_coverages();
|
||||
me2counter = Qnil;
|
||||
return ncoverages;
|
||||
}
|
||||
|
||||
|
@ -252,4 +304,5 @@ Init_coverage(void)
|
|||
rb_define_module_function(rb_mCoverage, "result", rb_coverage_result, 0);
|
||||
rb_define_module_function(rb_mCoverage, "peek_result", rb_coverage_peek_result, 0);
|
||||
rb_define_module_function(rb_mCoverage, "running?", rb_coverage_running, 0);
|
||||
rb_global_variable(&me2counter);
|
||||
}
|
||||
|
|
8
hash.c
8
hash.c
|
@ -426,6 +426,14 @@ rb_hash_new(void)
|
|||
return hash_alloc(rb_cHash);
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_hash_new_compare_by_id(void)
|
||||
{
|
||||
VALUE hash = rb_hash_new();
|
||||
RHASH(hash)->ntbl = rb_init_identtable();
|
||||
return hash;
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_hash_new_with_size(st_index_t size)
|
||||
{
|
||||
|
|
|
@ -1272,6 +1272,9 @@ void ruby_sized_xfree(void *x, size_t size);
|
|||
/* hash.c */
|
||||
struct st_table *rb_hash_tbl_raw(VALUE hash);
|
||||
VALUE rb_hash_new_with_size(st_index_t size);
|
||||
RUBY_SYMBOL_EXPORT_BEGIN
|
||||
VALUE rb_hash_new_compare_by_id(void);
|
||||
RUBY_SYMBOL_EXPORT_END
|
||||
VALUE rb_hash_has_key(VALUE hash, VALUE key);
|
||||
VALUE rb_hash_default_value(VALUE hash, VALUE key);
|
||||
VALUE rb_hash_set_default_proc(VALUE hash, VALUE proc);
|
||||
|
@ -1758,7 +1761,6 @@ struct timeval rb_time_timeval(VALUE);
|
|||
/* thread.c */
|
||||
#define COVERAGE_INDEX_LINES 0
|
||||
#define COVERAGE_INDEX_BRANCHES 1
|
||||
#define COVERAGE_INDEX_METHODS 2
|
||||
#define COVERAGE_TARGET_LINES 1
|
||||
#define COVERAGE_TARGET_BRANCHES 2
|
||||
#define COVERAGE_TARGET_METHODS 4
|
||||
|
|
1
iseq.h
1
iseq.h
|
@ -51,7 +51,6 @@ iseq_mark_ary_create(int flip_cnt)
|
|||
#define ISEQ_COVERAGE_SET(iseq, cov) RARRAY_ASET(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_COVERAGE, cov)
|
||||
#define ISEQ_LINE_COVERAGE(iseq) RARRAY_AREF(ISEQ_COVERAGE(iseq), COVERAGE_INDEX_LINES)
|
||||
#define ISEQ_BRANCH_COVERAGE(iseq) RARRAY_AREF(ISEQ_COVERAGE(iseq), COVERAGE_INDEX_BRANCHES)
|
||||
#define ISEQ_METHOD_COVERAGE(iseq) RARRAY_AREF(ISEQ_COVERAGE(iseq), COVERAGE_INDEX_METHODS)
|
||||
|
||||
#define ISEQ_FLIP_CNT(iseq) FIX2INT(RARRAY_AREF(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_FLIP_CNT))
|
||||
|
||||
|
|
3
method.h
3
method.h
|
@ -190,6 +190,9 @@ const rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id);
|
|||
const rb_method_entry_t *rb_method_entry(VALUE klass, ID id);
|
||||
const rb_method_entry_t *rb_method_entry_without_refinements(VALUE klass, ID id, VALUE *defined_class);
|
||||
const rb_method_entry_t *rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
|
||||
RUBY_SYMBOL_EXPORT_BEGIN
|
||||
const rb_method_entry_t *rb_resolve_me_location(const rb_method_entry_t *, VALUE[2]);
|
||||
RUBY_SYMBOL_EXPORT_END
|
||||
|
||||
const rb_callable_method_entry_t *rb_callable_method_entry(VALUE klass, ID id);
|
||||
const rb_callable_method_entry_t *rb_callable_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class);
|
||||
|
|
|
@ -188,7 +188,15 @@ class TestCoverage < Test::Unit::TestCase
|
|||
Coverage.start(#{ opt })
|
||||
tmp = Dir.pwd
|
||||
require tmp + '/test.rb'
|
||||
p Coverage.result[tmp + "/test.rb"]
|
||||
r = Coverage.result[tmp + "/test.rb"]
|
||||
if r[:methods]
|
||||
h = {}
|
||||
r[:methods].keys.sort_by {|key| key.drop(1) }.each do |key|
|
||||
h[key] = r[:methods][key]
|
||||
end
|
||||
r[:methods].replace h
|
||||
end
|
||||
p r
|
||||
end;
|
||||
}
|
||||
}
|
||||
|
@ -332,9 +340,9 @@ class TestCoverage < Test::Unit::TestCase
|
|||
def test_method_coverage
|
||||
result = {
|
||||
:methods => {
|
||||
[:foo, 0, 1] => 2,
|
||||
[:bar, 1, 2] => 1,
|
||||
[:baz, 2, 4] => 0,
|
||||
[Object, :bar, 2] => 1,
|
||||
[Object, :baz, 4] => 0,
|
||||
[Object, :foo, 1] => 2,
|
||||
}
|
||||
}
|
||||
assert_coverage(<<-"end;", { methods: true }, result)
|
||||
|
@ -348,4 +356,101 @@ class TestCoverage < Test::Unit::TestCase
|
|||
bar
|
||||
end;
|
||||
end
|
||||
|
||||
def test_method_coverage_for_define_method
|
||||
result = {
|
||||
:methods => {
|
||||
[Object, :bar, 2] => 1,
|
||||
[Object, :baz, 4] => 0,
|
||||
[Object, :foo, 1] => 2,
|
||||
}
|
||||
}
|
||||
assert_coverage(<<-"end;", { methods: true }, result)
|
||||
define_method(:foo) {}
|
||||
define_method(:bar) {
|
||||
}
|
||||
f = proc {}
|
||||
define_method(:baz, &f)
|
||||
|
||||
foo
|
||||
foo
|
||||
bar
|
||||
end;
|
||||
end
|
||||
|
||||
class DummyConstant < String
|
||||
def inspect
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
def test_method_coverage_for_alias
|
||||
_C = DummyConstant.new("C")
|
||||
_M = DummyConstant.new("M")
|
||||
code = <<-"end;"
|
||||
module M
|
||||
def foo
|
||||
end
|
||||
alias bar foo
|
||||
end
|
||||
class C
|
||||
include M
|
||||
def baz
|
||||
end
|
||||
alias qux baz
|
||||
end
|
||||
end;
|
||||
|
||||
result = {
|
||||
:methods => {
|
||||
[_C, :baz, 8] => 0,
|
||||
[_M, :foo, 2] => 0,
|
||||
}
|
||||
}
|
||||
assert_coverage(code, { methods: true }, result)
|
||||
|
||||
result = {
|
||||
:methods => {
|
||||
[_C, :baz, 8] => 12,
|
||||
[_M, :foo, 2] => 3,
|
||||
}
|
||||
}
|
||||
assert_coverage(code + <<-"end;", { methods: true }, result)
|
||||
obj = C.new
|
||||
1.times { obj.foo }
|
||||
2.times { obj.bar }
|
||||
4.times { obj.baz }
|
||||
8.times { obj.qux }
|
||||
end;
|
||||
end
|
||||
|
||||
def test_method_coverage_for_singleton_class
|
||||
_singleton_Foo = DummyConstant.new("#<Class:Foo>")
|
||||
_Foo = DummyConstant.new("Foo")
|
||||
code = <<-"end;"
|
||||
class Foo
|
||||
def foo
|
||||
end
|
||||
alias bar foo
|
||||
def self.baz
|
||||
end
|
||||
class << self
|
||||
alias qux baz
|
||||
end
|
||||
end
|
||||
|
||||
1.times { Foo.new.foo }
|
||||
2.times { Foo.new.bar }
|
||||
4.times { Foo.baz }
|
||||
8.times { Foo.qux }
|
||||
end;
|
||||
|
||||
result = {
|
||||
:methods => {
|
||||
[_singleton_Foo, :baz, 5] => 12,
|
||||
[_Foo, :foo, 2] => 3,
|
||||
}
|
||||
}
|
||||
assert_coverage(code, { methods: true }, result)
|
||||
end
|
||||
end
|
||||
|
|
109
thread.c
109
thread.c
|
@ -71,6 +71,7 @@
|
|||
#include "ruby/thread_native.h"
|
||||
#include "ruby/debug.h"
|
||||
#include "internal.h"
|
||||
#include "iseq.h"
|
||||
|
||||
#ifndef USE_NATIVE_THREAD_PRIORITY
|
||||
#define USE_NATIVE_THREAD_PRIORITY 0
|
||||
|
@ -4093,7 +4094,6 @@ clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
|
|||
VALUE coverage = (VALUE)val;
|
||||
VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
|
||||
VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
|
||||
VALUE methods = RARRAY_AREF(coverage, COVERAGE_INDEX_METHODS);
|
||||
|
||||
if (lines) {
|
||||
for (i = 0; i < RARRAY_LEN(lines); i++) {
|
||||
|
@ -4108,11 +4108,6 @@ clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
|
|||
RARRAY_ASET(counters, i, INT2FIX(0));
|
||||
}
|
||||
}
|
||||
if (methods) {
|
||||
for (i = 2; i < RARRAY_LEN(methods); i += 3) {
|
||||
RARRAY_ASET(methods, i, INT2FIX(0));
|
||||
}
|
||||
}
|
||||
|
||||
return ST_CONTINUE;
|
||||
}
|
||||
|
@ -5019,23 +5014,75 @@ update_coverage(VALUE data, const rb_trace_arg_t *trace_arg)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case COVERAGE_INDEX_METHODS: {
|
||||
VALUE methods = RARRAY_AREF(coverage, COVERAGE_INDEX_METHODS);
|
||||
if (methods) {
|
||||
long count;
|
||||
long idx = arg / 16 * 3 + 2;
|
||||
VALUE num = RARRAY_AREF(methods, idx);
|
||||
count = FIX2LONG(num) + 1;
|
||||
if (POSFIXABLE(count)) {
|
||||
RARRAY_ASET(methods, idx, LONG2FIX(count));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rb_method_entry_t *
|
||||
rb_resolve_me_location(const rb_method_entry_t *me, VALUE resolved_location[2])
|
||||
{
|
||||
VALUE path, first_lineno;
|
||||
|
||||
retry:
|
||||
switch (me->def->type) {
|
||||
case VM_METHOD_TYPE_ISEQ: {
|
||||
rb_iseq_location_t loc = me->def->body.iseq.iseqptr->body->location;
|
||||
path = loc.pathobj;
|
||||
first_lineno = loc.first_lineno;
|
||||
break;
|
||||
}
|
||||
case VM_METHOD_TYPE_BMETHOD: {
|
||||
const rb_iseq_t *iseq = rb_proc_get_iseq(me->def->body.proc, 0);
|
||||
if (iseq) {
|
||||
rb_iseq_check(iseq);
|
||||
path = rb_iseq_path(iseq);
|
||||
first_lineno = iseq->body->location.first_lineno;
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
case VM_METHOD_TYPE_ALIAS:
|
||||
me = me->def->body.alias.original_me;
|
||||
goto retry;
|
||||
case VM_METHOD_TYPE_REFINED:
|
||||
me = me->def->body.refined.orig_me;
|
||||
if (!me) return NULL;
|
||||
goto retry;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* found */
|
||||
if (RB_TYPE_P(path, T_ARRAY)) {
|
||||
path = rb_ary_entry(path, 1);
|
||||
if (!RB_TYPE_P(path, T_STRING)) return NULL; /* just for the case... */
|
||||
}
|
||||
if (resolved_location) {
|
||||
resolved_location[0] = path;
|
||||
resolved_location[1] = first_lineno;
|
||||
}
|
||||
return me;
|
||||
}
|
||||
|
||||
static void
|
||||
update_method_coverage(VALUE me2counter, rb_trace_arg_t *trace_arg)
|
||||
{
|
||||
const rb_control_frame_t *cfp = GET_EC()->cfp;
|
||||
const rb_callable_method_entry_t *cme = rb_vm_frame_method_entry(cfp);
|
||||
const rb_method_entry_t *me = (const rb_method_entry_t *)cme;
|
||||
VALUE rcount;
|
||||
long count;
|
||||
|
||||
me = rb_resolve_me_location(me, 0);
|
||||
if (!me) return;
|
||||
|
||||
rcount = rb_hash_aref(me2counter, (VALUE) me);
|
||||
count = FIXNUM_P(rcount) ? FIX2LONG(rcount) + 1 : 1;
|
||||
if (POSFIXABLE(count)) {
|
||||
rb_hash_aset(me2counter, (VALUE) me, LONG2FIX(count));
|
||||
}
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_get_coverages(void)
|
||||
{
|
||||
|
@ -5043,11 +5090,14 @@ rb_get_coverages(void)
|
|||
}
|
||||
|
||||
void
|
||||
rb_set_coverages(VALUE coverages, int mode)
|
||||
rb_set_coverages(VALUE coverages, int mode, VALUE me2counter)
|
||||
{
|
||||
GET_VM()->coverages = coverages;
|
||||
GET_VM()->coverage_mode = mode;
|
||||
rb_add_event_hook2((rb_event_hook_func_t) update_coverage, RUBY_EVENT_COVERAGE, Qnil, RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
|
||||
if (mode & COVERAGE_TARGET_METHODS) {
|
||||
rb_add_event_hook2((rb_event_hook_func_t) update_method_coverage, RUBY_EVENT_CALL, me2counter, RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
|
||||
}
|
||||
}
|
||||
|
||||
/* Make coverage arrays empty so old covered files are no longer tracked. */
|
||||
|
@ -5057,10 +5107,8 @@ reset_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
|
|||
VALUE coverage = (VALUE)val;
|
||||
VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
|
||||
VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
|
||||
VALUE methods = RARRAY_AREF(coverage, COVERAGE_INDEX_METHODS);
|
||||
if (lines) rb_ary_clear(lines);
|
||||
if (branches) rb_ary_clear(branches);
|
||||
if (methods) rb_ary_clear(methods);
|
||||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
|
@ -5071,13 +5119,16 @@ rb_reset_coverages(void)
|
|||
st_foreach(rb_hash_tbl_raw(coverages), reset_coverage_i, 0);
|
||||
GET_VM()->coverages = Qfalse;
|
||||
rb_remove_event_hook((rb_event_hook_func_t) update_coverage);
|
||||
if (GET_VM()->coverage_mode & COVERAGE_TARGET_METHODS) {
|
||||
rb_remove_event_hook((rb_event_hook_func_t) update_method_coverage);
|
||||
}
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_default_coverage(int n)
|
||||
{
|
||||
VALUE coverage = rb_ary_tmp_new_fill(3);
|
||||
VALUE lines = Qfalse, branches = Qfalse, methods = Qfalse;
|
||||
VALUE lines = Qfalse, branches = Qfalse;
|
||||
int mode = GET_VM()->coverage_mode;
|
||||
|
||||
if (mode & COVERAGE_TARGET_LINES) {
|
||||
|
@ -5105,18 +5156,6 @@ rb_default_coverage(int n)
|
|||
}
|
||||
RARRAY_ASET(coverage, COVERAGE_INDEX_BRANCHES, branches);
|
||||
|
||||
if (mode & COVERAGE_TARGET_METHODS) {
|
||||
methods = rb_ary_tmp_new(0);
|
||||
/* internal data structures for method coverage:
|
||||
*
|
||||
* [symbol_of_method_name, lineno_of_method_head, counter,
|
||||
* ...]
|
||||
*
|
||||
* Example: [:foobar, 1, 0, ...]
|
||||
*/
|
||||
}
|
||||
RARRAY_ASET(coverage, COVERAGE_INDEX_METHODS, methods);
|
||||
|
||||
return coverage;
|
||||
}
|
||||
|
||||
|
|
|
@ -87,15 +87,15 @@ def gen_rb_lcov(file)
|
|||
|
||||
# function coverage
|
||||
total = covered = 0
|
||||
cov[:methods].each do |(name, _, lineno), count|
|
||||
f.puts "FN:#{ lineno },#{ name }"
|
||||
cov[:methods].each do |(klass, name, lineno), count|
|
||||
f.puts "FN:#{ lineno },#{ klass }##{ name }"
|
||||
total += 1
|
||||
covered += 1 if count > 0
|
||||
end
|
||||
f.puts "FNF:#{ total }"
|
||||
f.puts "FNF:#{ covered }"
|
||||
cov[:methods].each do |(name, _), count|
|
||||
f.puts "FNDA:#{ count },#{ name }"
|
||||
cov[:methods].each do |(klass, name, _), count|
|
||||
f.puts "FNDA:#{ count },#{ klass }##{ name }"
|
||||
end
|
||||
|
||||
# line coverage
|
||||
|
|
|
@ -41,6 +41,15 @@ def add_count(h, key, count)
|
|||
end
|
||||
|
||||
def save_coverage_data(res1)
|
||||
res1.each do |_path, cov|
|
||||
if cov[:methods]
|
||||
h = {}
|
||||
cov[:methods].each do |(klass, *key), count|
|
||||
h[[klass.inspect, *key]] = count
|
||||
end
|
||||
cov[:methods].replace h
|
||||
end
|
||||
end
|
||||
File.open(TEST_COVERAGE_DATA_FILE, File::RDWR | File::CREAT | File::BINARY) do |f|
|
||||
f.flock(File::LOCK_EX)
|
||||
s = f.read
|
||||
|
|
|
@ -1753,7 +1753,7 @@ RUBY_SYMBOL_EXPORT_BEGIN
|
|||
int rb_thread_check_trap_pending(void);
|
||||
|
||||
extern VALUE rb_get_coverages(void);
|
||||
extern void rb_set_coverages(VALUE, int);
|
||||
extern void rb_set_coverages(VALUE, int, VALUE);
|
||||
extern void rb_reset_coverages(void);
|
||||
|
||||
void rb_postponed_job_flush(rb_vm_t *vm);
|
||||
|
|
Загрузка…
Ссылка в новой задаче