Feature #9508 » pull-request-511.patch
compile.c | ||
---|---|---|
iseq->compile_data->last_coverable_line = (line); \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_COVERAGE)); \
|
||
} \
|
||
if ((event) == RUBY_EVENT_DEFN && iseq->method_coverage) { \
|
||
/*iseq->compile_data->last_coverable_line = (line); \
|
||
VALUE info = rb_hash_new(); \
|
||
rb_hash_aset(info, ID2SYM(rb_intern("count")), INT2FIX(0));*/ \
|
||
rb_hash_aset(iseq->method_coverage, LONG2FIX(line), Qnil); \
|
||
} \
|
||
if ((event) == RUBY_EVENT_CALL && iseq->method_coverage && \
|
||
(line) != iseq->compile_data->last_coverable_line) { \
|
||
iseq->compile_data->last_coverable_line = (line); \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_MCOVERAGE)); \
|
||
} \
|
||
if (iseq->compile_data->option->trace_instruction) { \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(event)); \
|
||
} \
|
||
... | ... | |
debugp_param("defn/iseq", iseqval);
|
||
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_DEFN);
|
||
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
|
||
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
|
||
ADD_INSN1(ret, line, putobject, ID2SYM(node->nd_mid));
|
ext/coverage/coverage.c | ||
---|---|---|
VALUE path = (VALUE)key;
|
||
VALUE coverage = (VALUE)val;
|
||
VALUE coverages = (VALUE)h;
|
||
coverage = rb_ary_dup(coverage);
|
||
rb_ary_clear((VALUE)val);
|
||
VALUE line_coverage = rb_hash_lookup(coverage, ID2SYM(rb_intern("lines")));
|
||
line_coverage = rb_ary_dup(line_coverage);
|
||
coverage = rb_hash_dup(coverage);
|
||
rb_hash_clear((VALUE)val);
|
||
rb_hash_freeze(line_coverage);
|
||
rb_hash_aset(coverage, ID2SYM(rb_intern("lines")), line_coverage);
|
||
rb_ary_freeze(coverage);
|
||
rb_hash_aset(coverages, path, coverage);
|
||
return ST_CONTINUE;
|
include/ruby/ruby.h | ||
---|---|---|
#define RUBY_EVENT_C_CALL 0x0020
|
||
#define RUBY_EVENT_C_RETURN 0x0040
|
||
#define RUBY_EVENT_RAISE 0x0080
|
||
#define RUBY_EVENT_DEFN 0x0090
|
||
#define RUBY_EVENT_ALL 0x00ff
|
||
/* for TracePoint extended events */
|
||
... | ... | |
/* special events */
|
||
#define RUBY_EVENT_SPECIFIED_LINE 0x010000
|
||
#define RUBY_EVENT_COVERAGE 0x020000
|
||
#define RUBY_EVENT_MCOVERAGE 0x040000
|
||
/* internal events */
|
||
#define RUBY_INTERNAL_EVENT_SWITCH 0x040000
|
iseq.c | ||
---|---|---|
RUBY_MARK_UNLESS_NULL((VALUE)iseq->cref_stack);
|
||
RUBY_MARK_UNLESS_NULL(iseq->klass);
|
||
RUBY_MARK_UNLESS_NULL(iseq->coverage);
|
||
RUBY_MARK_UNLESS_NULL(iseq->method_coverage);
|
||
RUBY_MARK_UNLESS_NULL(iseq->orig);
|
||
if (iseq->compile_data != 0) {
|
||
... | ... | |
iseq->compile_data->last_coverable_line = -1;
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage, Qfalse);
|
||
RB_OBJ_WRITE(iseq->self, &iseq->method_coverage, Qfalse);
|
||
if (!GET_THREAD()->parse_in_eval) {
|
||
VALUE coverages = rb_get_coverages();
|
||
if (RTEST(coverages)) {
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage, rb_hash_lookup(coverages, path));
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage,
|
||
rb_hash_lookup(rb_hash_lookup(coverages, path), ID2SYM(rb_intern("lines"))));
|
||
RB_OBJ_WRITE(iseq->self, &iseq->method_coverage,
|
||
rb_hash_lookup(rb_hash_lookup(coverages, path), ID2SYM(rb_intern("methods"))));
|
||
if (NIL_P(iseq->coverage)) RB_OBJ_WRITE(iseq->self, &iseq->coverage, Qfalse);
|
||
if (NIL_P(iseq->method_coverage)) RB_OBJ_WRITE(iseq->self, &iseq->method_coverage, Qfalse);
|
||
}
|
||
}
|
||
parse.y | ||
---|---|---|
VALUE coverages = rb_get_coverages();
|
||
if (RTEST(coverages) && RBASIC(coverages)->klass == 0) {
|
||
VALUE lines = rb_ary_new2(n);
|
||
VALUE rb_file_coverage = rb_hash_new();
|
||
VALUE methods = rb_hash_new();
|
||
int i;
|
||
RBASIC_CLEAR_CLASS(lines);
|
||
for (i = 0; i < n; i++) RARRAY_ASET(lines, i, Qnil);
|
||
RARRAY(lines)->as.heap.len = n;
|
||
rb_hash_aset(coverages, fname, lines);
|
||
rb_hash_aset(rb_file_coverage, ID2SYM(rb_intern("lines")), lines);
|
||
rb_hash_aset(rb_file_coverage, ID2SYM(rb_intern("methods")), methods);
|
||
rb_hash_aset(coverages, fname, rb_file_coverage);
|
||
return lines;
|
||
}
|
||
return 0;
|
thread.c | ||
---|---|---|
clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
|
||
{
|
||
int i;
|
||
VALUE lines = (VALUE)val;
|
||
VALUE lines = rb_hash_lookup(val, ID2SYM(rb_intern("lines")));
|
||
for (i = 0; i < RARRAY_LEN(lines); i++) {
|
||
if (RARRAY_AREF(lines, i) != Qnil) {
|
||
... | ... | |
}
|
||
}
|
||
static void
|
||
update_method_coverage(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
|
||
{
|
||
VALUE method_coverage = GET_THREAD()->cfp->iseq->method_coverage;
|
||
if (method_coverage) {
|
||
long line = rb_sourceline();
|
||
VALUE info = rb_hash_lookup(method_coverage, LONG2FIX(line));
|
||
if (info == Qnil) {
|
||
VALUE info = rb_hash_new();
|
||
rb_hash_aset(info, ID2SYM(rb_intern("count")), INT2FIX(1));
|
||
rb_hash_aset(method_coverage, LONG2FIX(line), info);
|
||
} else {
|
||
long count = FIX2LONG(rb_hash_lookup(info, ID2SYM(rb_intern("count")))) + 1;
|
||
rb_hash_aset(info, ID2SYM(rb_intern("count")), LONG2FIX(count));
|
||
}
|
||
}
|
||
}
|
||
VALUE
|
||
rb_get_coverages(void)
|
||
{
|
||
... | ... | |
{
|
||
GET_VM()->coverages = coverages;
|
||
rb_add_event_hook(update_coverage, RUBY_EVENT_COVERAGE, Qnil);
|
||
rb_add_event_hook(update_method_coverage, RUBY_EVENT_MCOVERAGE, Qnil);
|
||
}
|
||
void
|
vm_core.h | ||
---|---|---|
VALUE *iseq_encoded; /* encoded iseq */
|
||
unsigned long iseq_size;
|
||
const VALUE mark_ary; /* Array: includes operands which should be GC marked */
|
||
const VALUE coverage; /* coverage array */
|
||
const VALUE coverage; /* coverage array */
|
||
const VALUE method_coverage; /* method coverage array */
|
||
/* insn info, must be freed */
|
||
struct iseq_line_info_entry *line_info_table;
|
||
-
|
compile.c | ||
---|---|---|
iseq->compile_data->last_coverable_line = (line); \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_COVERAGE)); \
|
||
} \
|
||
if ((event) == RUBY_EVENT_DEFN && iseq->method_coverage) { \
|
||
/*iseq->compile_data->last_coverable_line = (line); \
|
||
VALUE info = rb_hash_new(); \
|
||
rb_hash_aset(info, ID2SYM(rb_intern("count")), INT2FIX(0));*/ \
|
||
rb_hash_aset(iseq->method_coverage, LONG2FIX(line), Qnil); \
|
||
} \
|
||
if ((event) == RUBY_EVENT_CALL && iseq->method_coverage && \
|
||
(line) != iseq->compile_data->last_coverable_line) { \
|
||
iseq->compile_data->last_coverable_line = (line); \
|
||
... | ... | |
} \
|
||
} while (0)
|
||
#define ADD_COVERAGE_TRACE(seq, line, event) \
|
||
do { \
|
||
if ((event) == RUBY_EVENT_DEFN && iseq->method_coverage) { \
|
||
rb_hash_aset(iseq->method_coverage, LONG2FIX(line), INT2FIX(0)); \
|
||
} \
|
||
} while (0)
|
||
/* add label */
|
||
#define ADD_LABEL(seq, label) \
|
||
ADD_ELEM((seq), (LINK_ELEMENT *) (label))
|
||
... | ... | |
debugp_param("defn/iseq", iseqval);
|
||
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_DEFN);
|
||
ADD_COVERAGE_TRACE(ret, nd_line(node), RUBY_EVENT_DEFN);
|
||
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
|
||
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
|
||
ADD_INSN1(ret, line, putobject, ID2SYM(node->nd_mid));
|
thread.c | ||
---|---|---|
long line = rb_sourceline();
|
||
VALUE info = rb_hash_lookup(method_coverage, LONG2FIX(line));
|
||
if (info == Qnil) {
|
||
VALUE info = rb_hash_new();
|
||
rb_hash_aset(info, ID2SYM(rb_intern("count")), INT2FIX(1));
|
||
rb_hash_aset(method_coverage, LONG2FIX(line), info);
|
||
} else {
|
||
long count = FIX2LONG(rb_hash_lookup(info, ID2SYM(rb_intern("count")))) + 1;
|
||
rb_hash_aset(info, ID2SYM(rb_intern("count")), LONG2FIX(count));
|
||
}
|
||
long count = FIX2LONG(info) + 1;
|
||
rb_hash_aset(method_coverage, LONG2FIX(line), LONG2FIX(count));
|
||
}
|
||
}
|
||
-
|
compile.c | ||
---|---|---|
if ((event) == RUBY_EVENT_DEFN && iseq->method_coverage) { \
|
||
rb_hash_aset(iseq->method_coverage, LONG2FIX(line), INT2FIX(0)); \
|
||
} \
|
||
if ((event) == RUBY_EVENT_BRANCH && iseq->branch_coverage) { \
|
||
rb_hash_aset(iseq->branch_coverage, LONG2FIX(line), INT2FIX(0)); \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_BCOVERAGE)); \
|
||
} \
|
||
} while (0)
|
||
/* add label */
|
||
... | ... | |
ADD_SEQ(ret, cond_seq);
|
||
ADD_LABEL(ret, then_label);
|
||
if (node->nd_body)
|
||
ADD_COVERAGE_TRACE(ret, nd_line(node->nd_body), RUBY_EVENT_BRANCH);
|
||
ADD_SEQ(ret, then_seq);
|
||
ADD_INSNL(ret, line, jump, end_label);
|
||
ADD_LABEL(ret, else_label);
|
||
if (node->nd_else)
|
||
ADD_COVERAGE_TRACE(ret, nd_line(node->nd_else), RUBY_EVENT_BRANCH);
|
||
ADD_SEQ(ret, else_seq);
|
||
ADD_LABEL(ret, end_label);
|
include/ruby/ruby.h | ||
---|---|---|
#define RUBY_EVENT_C_RETURN 0x0040
|
||
#define RUBY_EVENT_RAISE 0x0080
|
||
#define RUBY_EVENT_DEFN 0x0090
|
||
#define RUBY_EVENT_BRANCH 0x00a0
|
||
#define RUBY_EVENT_ALL 0x00ff
|
||
/* for TracePoint extended events */
|
||
... | ... | |
#define RUBY_EVENT_SPECIFIED_LINE 0x010000
|
||
#define RUBY_EVENT_COVERAGE 0x020000
|
||
#define RUBY_EVENT_MCOVERAGE 0x040000
|
||
#define RUBY_EVENT_BCOVERAGE 0x080000
|
||
/* internal events */
|
||
#define RUBY_INTERNAL_EVENT_SWITCH 0x040000
|
iseq.c | ||
---|---|---|
RUBY_MARK_UNLESS_NULL(iseq->klass);
|
||
RUBY_MARK_UNLESS_NULL(iseq->coverage);
|
||
RUBY_MARK_UNLESS_NULL(iseq->method_coverage);
|
||
RUBY_MARK_UNLESS_NULL(iseq->branch_coverage);
|
||
RUBY_MARK_UNLESS_NULL(iseq->orig);
|
||
if (iseq->compile_data != 0) {
|
||
... | ... | |
RB_OBJ_WRITE(iseq->self, &iseq->coverage, Qfalse);
|
||
RB_OBJ_WRITE(iseq->self, &iseq->method_coverage, Qfalse);
|
||
RB_OBJ_WRITE(iseq->self, &iseq->branch_coverage, Qfalse);
|
||
if (!GET_THREAD()->parse_in_eval) {
|
||
VALUE coverages = rb_get_coverages();
|
||
if (RTEST(coverages)) {
|
||
... | ... | |
rb_hash_lookup(rb_hash_lookup(coverages, path), ID2SYM(rb_intern("lines"))));
|
||
RB_OBJ_WRITE(iseq->self, &iseq->method_coverage,
|
||
rb_hash_lookup(rb_hash_lookup(coverages, path), ID2SYM(rb_intern("methods"))));
|
||
RB_OBJ_WRITE(iseq->self, &iseq->branch_coverage,
|
||
rb_hash_lookup(rb_hash_lookup(coverages, path), ID2SYM(rb_intern("branches"))));
|
||
if (NIL_P(iseq->coverage)) RB_OBJ_WRITE(iseq->self, &iseq->coverage, Qfalse);
|
||
if (NIL_P(iseq->method_coverage)) RB_OBJ_WRITE(iseq->self, &iseq->method_coverage, Qfalse);
|
||
}
|
parse.y | ||
---|---|---|
VALUE lines = rb_ary_new2(n);
|
||
VALUE rb_file_coverage = rb_hash_new();
|
||
VALUE methods = rb_hash_new();
|
||
VALUE branches = rb_hash_new();
|
||
int i;
|
||
RBASIC_CLEAR_CLASS(lines);
|
||
for (i = 0; i < n; i++) RARRAY_ASET(lines, i, Qnil);
|
||
RARRAY(lines)->as.heap.len = n;
|
||
rb_hash_aset(rb_file_coverage, ID2SYM(rb_intern("lines")), lines);
|
||
rb_hash_aset(rb_file_coverage, ID2SYM(rb_intern("methods")), methods);
|
||
rb_hash_aset(rb_file_coverage, ID2SYM(rb_intern("branches")), branches);
|
||
rb_hash_aset(coverages, fname, rb_file_coverage);
|
||
return lines;
|
||
}
|
thread.c | ||
---|---|---|
}
|
||
static void
|
||
update_method_coverage(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
|
||
update_detailed_coverage(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
|
||
{
|
||
VALUE method_coverage = GET_THREAD()->cfp->iseq->method_coverage;
|
||
if (method_coverage) {
|
||
long line = rb_sourceline();
|
||
VALUE info = rb_hash_lookup(method_coverage, LONG2FIX(line));
|
||
VALUE coverage;
|
||
if (event == RUBY_EVENT_MCOVERAGE) {
|
||
coverage = GET_THREAD()->cfp->iseq->method_coverage;
|
||
} else if (event == RUBY_EVENT_BCOVERAGE) {
|
||
coverage = GET_THREAD()->cfp->iseq->branch_coverage;
|
||
} else {
|
||
rb_raise(rb_eArgError, "unknown detailed coverage event");
|
||
}
|
||
if (coverage) {
|
||
VALUE line = LONG2FIX(rb_sourceline());
|
||
VALUE count = rb_hash_lookup(coverage, line);
|
||
if (count == Qnil) {
|
||
return;
|
||
}
|
||
long count = FIX2LONG(info) + 1;
|
||
rb_hash_aset(method_coverage, LONG2FIX(line), LONG2FIX(count));
|
||
rb_hash_aset(coverage, line, LONG2FIX(FIX2LONG(count) + 1));
|
||
}
|
||
}
|
||
... | ... | |
{
|
||
GET_VM()->coverages = coverages;
|
||
rb_add_event_hook(update_coverage, RUBY_EVENT_COVERAGE, Qnil);
|
||
rb_add_event_hook(update_method_coverage, RUBY_EVENT_MCOVERAGE, Qnil);
|
||
rb_add_event_hook(update_detailed_coverage, RUBY_EVENT_MCOVERAGE, Qnil);
|
||
rb_add_event_hook(update_detailed_coverage, RUBY_EVENT_BCOVERAGE, Qnil);
|
||
}
|
||
void
|
vm_core.h | ||
---|---|---|
const VALUE mark_ary; /* Array: includes operands which should be GC marked */
|
||
const VALUE coverage; /* coverage array */
|
||
const VALUE method_coverage; /* method coverage array */
|
||
const VALUE branch_coverage; /* branch coverage array */
|
||
/* insn info, must be freed */
|
||
struct iseq_line_info_entry *line_info_table;
|
||
-
|
compile.c | ||
---|---|---|
ADD_INSNL(ret, line, jump, end_label);
|
||
ADD_LABEL(ret, else_label);
|
||
if (node->nd_else)
|
||
/* do not trace elsif node */
|
||
if (node->nd_else && nd_type(node->nd_else) != NODE_IF)
|
||
ADD_COVERAGE_TRACE(ret, nd_line(node->nd_else), RUBY_EVENT_BRANCH);
|
||
ADD_SEQ(ret, else_seq);
|
||
ext/coverage/coverage.c | ||
---|---|---|
VALUE path = (VALUE)key;
|
||
VALUE coverage = (VALUE)val;
|
||
VALUE coverages = (VALUE)h;
|
||
VALUE line_coverage = rb_hash_lookup(coverage, ID2SYM(rb_intern("lines")));
|
||
VALUE method_coverage = rb_hash_lookup(coverage, ID2SYM(rb_intern("methods")));
|
||
VALUE branch_coverage = rb_hash_lookup(coverage, ID2SYM(rb_intern("branches")));
|
||
line_coverage = rb_ary_dup(line_coverage);
|
||
method_coverage = rb_hash_dup(method_coverage);
|
||
branch_coverage = rb_hash_dup(branch_coverage);
|
||
rb_ary_clear(rb_hash_lookup(coverage, ID2SYM(rb_intern("lines"))));
|
||
rb_hash_clear(rb_hash_lookup(coverage, ID2SYM(rb_intern("methods"))));
|
||
rb_hash_clear(rb_hash_lookup(coverage, ID2SYM(rb_intern("branches"))));
|
||
coverage = rb_hash_dup(coverage);
|
||
rb_hash_clear((VALUE)val);
|
||
rb_hash_freeze(line_coverage);
|
||
rb_ary_freeze(line_coverage);
|
||
rb_hash_freeze(method_coverage);
|
||
rb_hash_freeze(branch_coverage);
|
||
rb_hash_aset(coverage, ID2SYM(rb_intern("lines")), line_coverage);
|
||
rb_hash_aset(coverage, ID2SYM(rb_intern("methods")), method_coverage);
|
||
rb_hash_aset(coverage, ID2SYM(rb_intern("branches")), branch_coverage);
|
||
rb_ary_freeze(coverage);
|
||
rb_hash_aset(coverages, path, coverage);
|
||
return ST_CONTINUE;
|
iseq.c | ||
---|---|---|
rb_hash_lookup(rb_hash_lookup(coverages, path), ID2SYM(rb_intern("branches"))));
|
||
if (NIL_P(iseq->coverage)) RB_OBJ_WRITE(iseq->self, &iseq->coverage, Qfalse);
|
||
if (NIL_P(iseq->method_coverage)) RB_OBJ_WRITE(iseq->self, &iseq->method_coverage, Qfalse);
|
||
if (NIL_P(iseq->branch_coverage)) RB_OBJ_WRITE(iseq->self, &iseq->branch_coverage, Qfalse);
|
||
}
|
||
}
|
||
test/coverage/test_branch_coverage.rb | ||
---|---|---|
require "test/unit"
|
||
require "coverage"
|
||
require "tmpdir"
|
||
class TestBranchCoverage < Test::Unit::TestCase
|
||
def test_branch_coverage
|
||
loaded_features = $".dup
|
||
Dir.mktmpdir {|tmp|
|
||
Dir.chdir(tmp) {
|
||
File.open("test.rb", "w") do |f|
|
||
f.puts <<-EOS
|
||
if 2+2 == 4
|
||
:ok
|
||
end
|
||
:ok unless [].size > 0
|
||
:bad unless [].size == 0
|
||
ary = [1,2,3]
|
||
if ary.include? 4
|
||
:bad
|
||
elsif ary.include? 5
|
||
:also_bad
|
||
else
|
||
:good
|
||
end
|
||
EOS
|
||
end
|
||
Coverage.start
|
||
require tmp + '/test.rb'
|
||
branch_coverage = Coverage.result[tmp + '/test.rb'][:branches]
|
||
assert_equal 6, branch_coverage.size
|
||
assert_equal 1, branch_coverage[2]
|
||
assert_equal 1, branch_coverage[5]
|
||
assert_equal 0, branch_coverage[6]
|
||
assert_equal 0, branch_coverage[10]
|
||
assert_equal 0, branch_coverage[12]
|
||
assert_equal 1, branch_coverage[14]
|
||
}
|
||
}
|
||
ensure
|
||
$".replace loaded_features
|
||
end
|
||
end
|
test/coverage/test_coverage.rb | ||
---|---|---|
def test_result_without_start
|
||
assert_raise(RuntimeError) {Coverage.result}
|
||
end
|
||
def test_result_with_nothing
|
||
Coverage.start
|
||
result = Coverage.result
|
||
assert_kind_of(Hash, result)
|
||
assert(! result.empty?)
|
||
result.each do |key, val|
|
||
assert_kind_of(String, key)
|
||
assert_kind_of(Array, val)
|
||
assert_kind_of(Hash, val)
|
||
assert_kind_of(Array, val[:lines])
|
||
assert_kind_of(Hash, val[:methods])
|
||
assert_kind_of(Hash, val[:branches])
|
||
end
|
||
end
|
||
... | ... | |
Coverage.start
|
||
require tmp + '/test.rb'
|
||
assert_equal 3, Coverage.result[tmp + '/test.rb'].size
|
||
assert_equal 3, Coverage.result[tmp + '/test.rb'][:lines].size
|
||
Coverage.start
|
||
coverage_test_method
|
||
assert_equal 0, Coverage.result[tmp + '/test.rb'].size
|
||
assert_equal 0, Coverage.result[tmp + '/test.rb'][:lines].size
|
||
}
|
||
}
|
||
ensure
|
||
... | ... | |
Coverage.start
|
||
require tmp + '/test.rb'
|
||
assert_equal 10003, Coverage.result[tmp + '/test.rb'].size
|
||
assert_equal 10003, Coverage.result[tmp + '/test.rb'][:lines].size
|
||
}
|
||
}
|
||
ensure
|
test/coverage/test_method_coverage.rb | ||
---|---|---|
require "test/unit"
|
||
require "coverage"
|
||
require "tmpdir"
|
||
class TestMethodCoverage < Test::Unit::TestCase
|
||
def test_method_coverage
|
||
loaded_features = $".dup
|
||
Dir.mktmpdir {|tmp|
|
||
Dir.chdir(tmp) {
|
||
File.open("test.rb", "w") do |f|
|
||
f.puts <<-EOS
|
||
def method_one
|
||
:one
|
||
end
|
||
def method_two
|
||
:two
|
||
end
|
||
method_two; method_two
|
||
EOS
|
||
end
|
||
Coverage.start
|
||
require tmp + '/test.rb'
|
||
method_coverage = Coverage.result[tmp + '/test.rb'][:methods]
|
||
assert_equal 2, method_coverage.size
|
||
assert_equal 0, method_coverage[1]
|
||
assert_equal 2, method_coverage[5]
|
||
}
|
||
}
|
||
ensure
|
||
$".replace loaded_features
|
||
end
|
||
end
|
thread.c | ||
---|---|---|
{
|
||
GET_VM()->coverages = Qfalse;
|
||
rb_remove_event_hook(update_coverage);
|
||
rb_remove_event_hook(update_detailed_coverage);
|
||
}
|
||
VALUE
|
||
-
|
compile.c | ||
---|---|---|
l1 = NEW_LABEL(line);
|
||
ADD_LABEL(body_seq, l1);
|
||
if (node->nd_body)
|
||
ADD_COVERAGE_TRACE(body_seq, nd_line(node->nd_body), RUBY_EVENT_BRANCH);
|
||
ADD_INSN(body_seq, line, pop);
|
||
COMPILE_(body_seq, "when body", node->nd_body, poped);
|
||
ADD_INSNL(body_seq, line, jump, endlabel);
|
||
... | ... | |
/* else */
|
||
if (node) {
|
||
ADD_LABEL(cond_seq, elselabel);
|
||
ADD_COVERAGE_TRACE(cond_seq, nd_line(node), RUBY_EVENT_BRANCH);
|
||
ADD_INSN(cond_seq, line, pop);
|
||
COMPILE_(cond_seq, "else", node, poped);
|
||
ADD_INSNL(cond_seq, line, jump, endlabel);
|
||
... | ... | |
while (node && nd_type(node) == NODE_WHEN) {
|
||
LABEL *l1 = NEW_LABEL(line = nd_line(node));
|
||
ADD_LABEL(body_seq, l1);
|
||
if (node->nd_body)
|
||
ADD_COVERAGE_TRACE(body_seq, nd_line(node->nd_body), RUBY_EVENT_BRANCH);
|
||
COMPILE_(body_seq, "when", node->nd_body, poped);
|
||
ADD_INSNL(body_seq, line, jump, endlabel);
|
||
... | ... | |
node = node->nd_next;
|
||
}
|
||
/* else */
|
||
ADD_COVERAGE_TRACE(ret, nd_line(node), RUBY_EVENT_BRANCH);
|
||
COMPILE_(ret, "else", node, poped);
|
||
ADD_INSNL(ret, nd_line(orig_node), jump, endlabel);
|
||
... | ... | |
debugp_param("defs/iseq", iseqval);
|
||
ADD_COVERAGE_TRACE(ret, nd_line(node), RUBY_EVENT_DEFN);
|
||
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
|
||
COMPILE(ret, "defs: recv", node->nd_recv);
|
||
ADD_INSN1(ret, line, putobject, ID2SYM(node->nd_mid));
|
test/coverage/test_branch_coverage.rb | ||
---|---|---|
require "tmpdir"
|
||
class TestBranchCoverage < Test::Unit::TestCase
|
||
def test_branch_coverage
|
||
def test_if_else_coverage
|
||
loaded_features = $".dup
|
||
Dir.mktmpdir {|tmp|
|
||
... | ... | |
ensure
|
||
$".replace loaded_features
|
||
end
|
||
def test_when_coverage
|
||
loaded_features = $".dup
|
||
Dir.mktmpdir {|tmp|
|
||
Dir.chdir(tmp) {
|
||
File.open("test.rb", "w") do |f|
|
||
f.puts <<-EOS
|
||
case
|
||
when 2 + 2 == 5
|
||
x = :bad
|
||
x = :math
|
||
when 2 + 2 == 4
|
||
x = :good
|
||
else
|
||
x = :also_bad
|
||
end
|
||
case [1,2,3]
|
||
when String
|
||
:string?
|
||
else
|
||
:else
|
||
end
|
||
EOS
|
||
end
|
||
Coverage.start
|
||
require tmp + '/test.rb'
|
||
branch_coverage = Coverage.result[tmp + '/test.rb'][:branches]
|
||
assert_equal 5, branch_coverage.size
|
||
assert_equal 0, branch_coverage[3]
|
||
assert_equal 1, branch_coverage[6]
|
||
assert_equal 0, branch_coverage[8]
|
||
assert_equal 0, branch_coverage[13]
|
||
assert_equal 1, branch_coverage[15]
|
||
}
|
||
}
|
||
ensure
|
||
$".replace loaded_features
|
||
end
|
||
end
|
||
-
|
compile.c | ||
---|---|---|
iseq->compile_data->last_coverable_line = (line); \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_COVERAGE)); \
|
||
} \
|
||
if ((event) == RUBY_EVENT_CALL && iseq->method_coverage && \
|
||
(line) != iseq->compile_data->last_coverable_line) { \
|
||
iseq->compile_data->last_coverable_line = (line); \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_MCOVERAGE)); \
|
||
} \
|
||
if (iseq->compile_data->option->trace_instruction) { \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(event)); \
|
||
} \
|
||
} while (0)
|
||
#define ADD_COVERAGE_TRACE(seq, line, event) \
|
||
#define ADD_METHOD_COVERAGE_TRACE(seq, line, event, end_line) \
|
||
do { \
|
||
if ((event) == RUBY_EVENT_DEFN && iseq->method_coverage) { \
|
||
rb_hash_aset(iseq->method_coverage, LONG2FIX(line), INT2FIX(0)); \
|
||
} \
|
||
if ((event) == RUBY_EVENT_CALL && iseq->method_coverage) { \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_MCOVERAGE)); \
|
||
} \
|
||
} while (0)
|
||
#define ADD_BRANCH_COVERAGE_TRACE(seq, line, event) \
|
||
do { \
|
||
if ((event) == RUBY_EVENT_BRANCH && iseq->branch_coverage) { \
|
||
rb_hash_aset(iseq->branch_coverage, LONG2FIX(line), INT2FIX(0)); \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_BCOVERAGE)); \
|
||
... | ... | |
case ISEQ_TYPE_METHOD:
|
||
{
|
||
ADD_TRACE(ret, FIX2INT(iseq->location.first_lineno), RUBY_EVENT_CALL);
|
||
ADD_METHOD_COVERAGE_TRACE(ret, FIX2INT(iseq->location.first_lineno), RUBY_EVENT_CALL, nd_line(node));
|
||
COMPILE(ret, "scoped node", node->nd_body);
|
||
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_RETURN);
|
||
break;
|
||
... | ... | |
ADD_LABEL(ret, then_label);
|
||
if (node->nd_body)
|
||
ADD_COVERAGE_TRACE(ret, nd_line(node->nd_body), RUBY_EVENT_BRANCH);
|
||
ADD_BRANCH_COVERAGE_TRACE(ret, nd_line(node->nd_body), RUBY_EVENT_BRANCH);
|
||
ADD_SEQ(ret, then_seq);
|
||
ADD_INSNL(ret, line, jump, end_label);
|
||
ADD_LABEL(ret, else_label);
|
||
/* do not trace elsif node */
|
||
if (node->nd_else && nd_type(node->nd_else) != NODE_IF)
|
||
ADD_COVERAGE_TRACE(ret, nd_line(node->nd_else), RUBY_EVENT_BRANCH);
|
||
ADD_BRANCH_COVERAGE_TRACE(ret, nd_line(node->nd_else), RUBY_EVENT_BRANCH);
|
||
ADD_SEQ(ret, else_seq);
|
||
ADD_LABEL(ret, end_label);
|
||
... | ... | |
l1 = NEW_LABEL(line);
|
||
ADD_LABEL(body_seq, l1);
|
||
if (node->nd_body)
|
||
ADD_COVERAGE_TRACE(body_seq, nd_line(node->nd_body), RUBY_EVENT_BRANCH);
|
||
ADD_BRANCH_COVERAGE_TRACE(body_seq, nd_line(node->nd_body), RUBY_EVENT_BRANCH);
|
||
ADD_INSN(body_seq, line, pop);
|
||
COMPILE_(body_seq, "when body", node->nd_body, poped);
|
||
ADD_INSNL(body_seq, line, jump, endlabel);
|
||
... | ... | |
/* else */
|
||
if (node) {
|
||
ADD_LABEL(cond_seq, elselabel);
|
||
ADD_COVERAGE_TRACE(cond_seq, nd_line(node), RUBY_EVENT_BRANCH);
|
||
ADD_BRANCH_COVERAGE_TRACE(cond_seq, nd_line(node), RUBY_EVENT_BRANCH);
|
||
ADD_INSN(cond_seq, line, pop);
|
||
COMPILE_(cond_seq, "else", node, poped);
|
||
ADD_INSNL(cond_seq, line, jump, endlabel);
|
||
... | ... | |
LABEL *l1 = NEW_LABEL(line = nd_line(node));
|
||
ADD_LABEL(body_seq, l1);
|
||
if (node->nd_body)
|
||
ADD_COVERAGE_TRACE(body_seq, nd_line(node->nd_body), RUBY_EVENT_BRANCH);
|
||
ADD_BRANCH_COVERAGE_TRACE(body_seq, nd_line(node->nd_body), RUBY_EVENT_BRANCH);
|
||
COMPILE_(body_seq, "when", node->nd_body, poped);
|
||
ADD_INSNL(body_seq, line, jump, endlabel);
|
||
... | ... | |
node = node->nd_next;
|
||
}
|
||
/* else */
|
||
ADD_COVERAGE_TRACE(ret, nd_line(node), RUBY_EVENT_BRANCH);
|
||
ADD_BRANCH_COVERAGE_TRACE(ret, nd_line(node), RUBY_EVENT_BRANCH);
|
||
COMPILE_(ret, "else", node, poped);
|
||
ADD_INSNL(ret, nd_line(orig_node), jump, endlabel);
|
||
... | ... | |
debugp_param("defn/iseq", iseqval);
|
||
ADD_COVERAGE_TRACE(ret, nd_line(node), RUBY_EVENT_DEFN);
|
||
ADD_METHOD_COVERAGE_TRACE(ret, nd_line(node), RUBY_EVENT_DEFN, nd_line(node->nd_defn));
|
||
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
|
||
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
|
||
ADD_INSN1(ret, line, putobject, ID2SYM(node->nd_mid));
|
||
... | ... | |
debugp_param("defs/iseq", iseqval);
|
||
ADD_COVERAGE_TRACE(ret, nd_line(node), RUBY_EVENT_DEFN);
|
||
ADD_METHOD_COVERAGE_TRACE(ret, nd_line(node), RUBY_EVENT_DEFN, nd_line(node->nd_defn));
|
||
ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
|
||
COMPILE(ret, "defs: recv", node->nd_recv);
|
||
ADD_INSN1(ret, line, putobject, ID2SYM(node->nd_mid));
|
||
-
|
compile.c | ||
---|---|---|
#define ADD_TRACE(seq, line, event) \
|
||
do { \
|
||
if ((event) == RUBY_EVENT_LINE && iseq->coverage && \
|
||
if ((event) == RUBY_EVENT_LINE && iseq->coverage->lines && \
|
||
(line) != iseq->compile_data->last_coverable_line) { \
|
||
RARRAY_ASET(iseq->coverage, (line) - 1, INT2FIX(0)); \
|
||
RARRAY_ASET(iseq->coverage->lines, (line) - 1, INT2FIX(0)); \
|
||
iseq->compile_data->last_coverable_line = (line); \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_COVERAGE)); \
|
||
} \
|
||
... | ... | |
#define ADD_METHOD_COVERAGE_TRACE(seq, line, event, end_line) \
|
||
do { \
|
||
if ((event) == RUBY_EVENT_DEFN && iseq->method_coverage) { \
|
||
rb_hash_aset(iseq->method_coverage, LONG2FIX(line), INT2FIX(0)); \
|
||
if ((event) == RUBY_EVENT_DEFN && iseq->coverage->methods) { \
|
||
rb_hash_aset(iseq->coverage->methods, LONG2FIX(line), INT2FIX(0)); \
|
||
} \
|
||
if ((event) == RUBY_EVENT_CALL && iseq->method_coverage) { \
|
||
if ((event) == RUBY_EVENT_CALL && iseq->coverage->methods) { \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_MCOVERAGE)); \
|
||
} \
|
||
} while (0)
|
||
#define ADD_BRANCH_COVERAGE_TRACE(seq, line, event) \
|
||
do { \
|
||
if ((event) == RUBY_EVENT_BRANCH && iseq->branch_coverage) { \
|
||
rb_hash_aset(iseq->branch_coverage, LONG2FIX(line), INT2FIX(0)); \
|
||
if ((event) == RUBY_EVENT_BRANCH && iseq->coverage->branches) { \
|
||
rb_hash_aset(iseq->coverage->branches, LONG2FIX(line), INT2FIX(0)); \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_BCOVERAGE)); \
|
||
} \
|
||
} while (0)
|
iseq.c | ||
---|---|---|
RUBY_MARK_UNLESS_NULL((VALUE)iseq->cref_stack);
|
||
RUBY_MARK_UNLESS_NULL(iseq->klass);
|
||
RUBY_MARK_UNLESS_NULL(iseq->coverage);
|
||
RUBY_MARK_UNLESS_NULL(iseq->method_coverage);
|
||
RUBY_MARK_UNLESS_NULL(iseq->branch_coverage);
|
||
RUBY_MARK_UNLESS_NULL(iseq->orig);
|
||
if (iseq->coverage != 0) {
|
||
struct rb_coverage_struct *const coverage = iseq->coverage;
|
||
RUBY_MARK_UNLESS_NULL(coverage->lines);
|
||
RUBY_MARK_UNLESS_NULL(coverage->methods);
|
||
RUBY_MARK_UNLESS_NULL(coverage->branches);
|
||
}
|
||
if (iseq->compile_data != 0) {
|
||
struct iseq_compile_data *const compile_data = iseq->compile_data;
|
||
RUBY_MARK_UNLESS_NULL(compile_data->mark_ary);
|
||
... | ... | |
iseq->compile_data->option = option;
|
||
iseq->compile_data->last_coverable_line = -1;
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage, Qfalse);
|
||
RB_OBJ_WRITE(iseq->self, &iseq->method_coverage, Qfalse);
|
||
RB_OBJ_WRITE(iseq->self, &iseq->branch_coverage, Qfalse);
|
||
iseq->coverage = ALLOC(struct rb_coverage_struct);
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage->lines, Qfalse);
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage->methods, Qfalse);
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage->branches, Qfalse);
|
||
if (!GET_THREAD()->parse_in_eval) {
|
||
VALUE coverages = rb_get_coverages();
|
||
if (RTEST(coverages)) {
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage,
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage->lines,
|
||
rb_hash_lookup(rb_hash_lookup(coverages, path), ID2SYM(rb_intern("lines"))));
|
||
RB_OBJ_WRITE(iseq->self, &iseq->method_coverage,
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage->methods,
|
||
rb_hash_lookup(rb_hash_lookup(coverages, path), ID2SYM(rb_intern("methods"))));
|
||
RB_OBJ_WRITE(iseq->self, &iseq->branch_coverage,
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage->branches,
|
||
rb_hash_lookup(rb_hash_lookup(coverages, path), ID2SYM(rb_intern("branches"))));
|
||
if (NIL_P(iseq->coverage)) RB_OBJ_WRITE(iseq->self, &iseq->coverage, Qfalse);
|
||
if (NIL_P(iseq->method_coverage)) RB_OBJ_WRITE(iseq->self, &iseq->method_coverage, Qfalse);
|
||
if (NIL_P(iseq->branch_coverage)) RB_OBJ_WRITE(iseq->self, &iseq->branch_coverage, Qfalse);
|
||
if (NIL_P(iseq->coverage->lines)) RB_OBJ_WRITE(iseq->self, &iseq->coverage->lines, Qfalse);
|
||
if (NIL_P(iseq->coverage->methods)) RB_OBJ_WRITE(iseq->self, &iseq->coverage->methods, Qfalse);
|
||
if (NIL_P(iseq->coverage->branches)) RB_OBJ_WRITE(iseq->self, &iseq->coverage->branches, Qfalse);
|
||
}
|
||
}
|
||
thread.c | ||
---|---|---|
static void
|
||
update_coverage(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
|
||
{
|
||
VALUE coverage = GET_THREAD()->cfp->iseq->coverage;
|
||
VALUE coverage = GET_THREAD()->cfp->iseq->coverage->lines;
|
||
if (coverage && RBASIC(coverage)->klass == 0) {
|
||
long line = rb_sourceline() - 1;
|
||
long count;
|
||
... | ... | |
{
|
||
VALUE coverage;
|
||
if (event == RUBY_EVENT_MCOVERAGE) {
|
||
coverage = GET_THREAD()->cfp->iseq->method_coverage;
|
||
coverage = GET_THREAD()->cfp->iseq->coverage->methods;
|
||
} else if (event == RUBY_EVENT_BCOVERAGE) {
|
||
coverage = GET_THREAD()->cfp->iseq->branch_coverage;
|
||
coverage = GET_THREAD()->cfp->iseq->coverage->branches;
|
||
} else {
|
||
rb_raise(rb_eArgError, "unknown detailed coverage event");
|
||
}
|
vm_core.h | ||
---|---|---|
size_t first_lineno;
|
||
} rb_iseq_location_t;
|
||
struct rb_coverage_struct {
|
||
const VALUE lines;
|
||
const VALUE methods;
|
||
const VALUE branches;
|
||
};
|
||
struct rb_iseq_struct;
|
||
struct rb_iseq_struct {
|
||
... | ... | |
VALUE *iseq_encoded; /* encoded iseq */
|
||
unsigned long iseq_size;
|
||
const VALUE mark_ary; /* Array: includes operands which should be GC marked */
|
||
const VALUE coverage; /* coverage array */
|
||
const VALUE method_coverage; /* method coverage array */
|
||
const VALUE branch_coverage; /* branch coverage array */
|
||
struct rb_coverage_struct *coverage;
|
||
/* insn info, must be freed */
|
||
struct iseq_line_info_entry *line_info_table;
|
||
-
|
compile.c | ||
---|---|---|
} \
|
||
} while (0)
|
||
#define ADD_BRANCH_COVERAGE_TRACE(seq, line, event) \
|
||
#define ADD_DECISION_COVERAGE_TRACE(seq, line, event) \
|
||
do { \
|
||
if ((event) == RUBY_EVENT_BRANCH && iseq->coverage->branches) { \
|
||
rb_hash_aset(iseq->coverage->branches, LONG2FIX(line), INT2FIX(0)); \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_BCOVERAGE)); \
|
||
if (((event) == RUBY_EVENT_DECISION_TRUE || (event) == RUBY_EVENT_DECISION_FALSE) \
|
||
&& iseq->coverage->decisions) { \
|
||
rb_hash_aset(iseq->coverage->decisions, LONG2FIX(line), rb_assoc_new(INT2FIX(0), INT2FIX(0))); \
|
||
if ((event) == RUBY_EVENT_DECISION_TRUE) \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_DCOVERAGE_TRUE)); \
|
||
if ((event) == RUBY_EVENT_DECISION_FALSE) \
|
||
ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_DCOVERAGE_FALSE)); \
|
||
} \
|
||
} while (0)
|
||
... | ... | |
ADD_INSN1(cond_seq, nd_line(vals), checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
|
||
ADD_INSNL(cond_seq, nd_line(val), branchif, l1);
|
||
ADD_DECISION_COVERAGE_TRACE(cond_seq, nd_line(vals), RUBY_EVENT_DECISION_FALSE);
|
||
vals = vals->nd_next;
|
||
}
|
||
return only_special_literals;
|
||
... | ... | |
ADD_SEQ(ret, cond_seq);
|
||
ADD_LABEL(ret, then_label);
|
||
if (node->nd_body)
|
||
ADD_BRANCH_COVERAGE_TRACE(ret, nd_line(node->nd_body), RUBY_EVENT_BRANCH);
|
||
ADD_DECISION_COVERAGE_TRACE(ret, nd_line(node), RUBY_EVENT_DECISION_TRUE);
|
||
ADD_SEQ(ret, then_seq);
|
||
ADD_INSNL(ret, line, jump, end_label);
|
||
ADD_LABEL(ret, else_label);
|
||
/* do not trace elsif node */
|
||
if (node->nd_else && nd_type(node->nd_else) != NODE_IF)
|
||
ADD_BRANCH_COVERAGE_TRACE(ret, nd_line(node->nd_else), RUBY_EVENT_BRANCH);
|
||
ADD_DECISION_COVERAGE_TRACE(ret, nd_line(node), RUBY_EVENT_DECISION_FALSE);
|
||
ADD_SEQ(ret, else_seq);
|
||
ADD_LABEL(ret, end_label);
|
||
... | ... | |
l1 = NEW_LABEL(line);
|
||
ADD_LABEL(body_seq, l1);
|
||
if (node->nd_body)
|
||
ADD_BRANCH_COVERAGE_TRACE(body_seq, nd_line(node->nd_body), RUBY_EVENT_BRANCH);
|
||
if (node->nd_head)
|
||
ADD_DECISION_COVERAGE_TRACE(body_seq, nd_line(node->nd_head), RUBY_EVENT_DECISION_TRUE);
|
||
ADD_INSN(body_seq, line, pop);
|
||
COMPILE_(body_seq, "when body", node->nd_body, poped);
|
||
ADD_INSNL(body_seq, line, jump, endlabel);
|
||
... | ... | |
COMPILE(cond_seq, "when/cond splat", vals);
|
||
ADD_INSN1(cond_seq, nd_line(vals), checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
|
||
ADD_INSNL(cond_seq, nd_line(vals), branchif, l1);
|
||
ADD_DECISION_COVERAGE_TRACE(cond_seq, nd_line(vals), RUBY_EVENT_DECISION_FALSE);
|
||
break;
|
||
default:
|
||
rb_bug("NODE_CASE: unknown node (%s)",
|
||
... | ... | |
/* else */
|
||
if (node) {
|
||
ADD_LABEL(cond_seq, elselabel);
|
||
ADD_BRANCH_COVERAGE_TRACE(cond_seq, nd_line(node), RUBY_EVENT_BRANCH);
|
||
ADD_INSN(cond_seq, line, pop);
|
||
COMPILE_(cond_seq, "else", node, poped);
|
||
ADD_INSNL(cond_seq, line, jump, endlabel);
|
||
... | ... | |
while (node && nd_type(node) == NODE_WHEN) {
|
||
LABEL *l1 = NEW_LABEL(line = nd_line(node));
|
||
ADD_LABEL(body_seq, l1);
|
||
if (node->nd_body)
|
||
ADD_BRANCH_COVERAGE_TRACE(body_seq, nd_line(node->nd_body), RUBY_EVENT_BRANCH);
|
||
if (node->nd_head)
|
||
ADD_DECISION_COVERAGE_TRACE(body_seq, nd_line(node->nd_head), RUBY_EVENT_DECISION_TRUE);
|
||
COMPILE_(body_seq, "when", node->nd_body, poped);
|
||
ADD_INSNL(body_seq, line, jump, endlabel);
|
||
... | ... | |
val = vals->nd_head;
|
||
COMPILE(ret, "when2", val);
|
||
ADD_INSNL(ret, nd_line(val), branchif, l1);
|
||
ADD_DECISION_COVERAGE_TRACE(ret, nd_line(vals), RUBY_EVENT_DECISION_FALSE);
|
||
vals = vals->nd_next;
|
||
}
|
||
break;
|
||
... | ... | |
COMPILE(ret, "when2/cond splat", vals);
|
||
ADD_INSN1(ret, nd_line(vals), checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
|
||
ADD_INSNL(ret, nd_line(vals), branchif, l1);
|
||
ADD_DECISION_COVERAGE_TRACE(ret, nd_line(vals), RUBY_EVENT_DECISION_FALSE);
|
||
break;
|
||
default:
|
||
rb_bug("NODE_WHEN: unknown node (%s)",
|
||
... | ... | |
node = node->nd_next;
|
||
}
|
||
/* else */
|
||
ADD_BRANCH_COVERAGE_TRACE(ret, nd_line(node), RUBY_EVENT_BRANCH);
|
||
COMPILE_(ret, "else", node, poped);
|
||
ADD_INSNL(ret, nd_line(orig_node), jump, endlabel);
|
||
ext/coverage/coverage.c | ||
---|---|---|
VALUE line_coverage = rb_hash_lookup(coverage, ID2SYM(rb_intern("lines")));
|
||
VALUE method_coverage = rb_hash_lookup(coverage, ID2SYM(rb_intern("methods")));
|
||
VALUE branch_coverage = rb_hash_lookup(coverage, ID2SYM(rb_intern("branches")));
|
||
VALUE decision_coverage = rb_hash_lookup(coverage, ID2SYM(rb_intern("decisions")));
|
||
line_coverage = rb_ary_dup(line_coverage);
|
||
method_coverage = rb_hash_dup(method_coverage);
|
||
branch_coverage = rb_hash_dup(branch_coverage);
|
||
decision_coverage = rb_hash_dup(decision_coverage);
|
||
rb_ary_clear(rb_hash_lookup(coverage, ID2SYM(rb_intern("lines"))));
|
||
rb_hash_clear(rb_hash_lookup(coverage, ID2SYM(rb_intern("methods"))));
|
||
rb_hash_clear(rb_hash_lookup(coverage, ID2SYM(rb_intern("branches"))));
|
||
rb_hash_clear(rb_hash_lookup(coverage, ID2SYM(rb_intern("decisions"))));
|
||
coverage = rb_hash_dup(coverage);
|
||
rb_ary_freeze(line_coverage);
|
||
rb_hash_freeze(method_coverage);
|
||
rb_hash_freeze(branch_coverage);
|
||
rb_hash_freeze(decision_coverage);
|
||
rb_hash_aset(coverage, ID2SYM(rb_intern("lines")), line_coverage);
|
||
rb_hash_aset(coverage, ID2SYM(rb_intern("methods")), method_coverage);
|
||
rb_hash_aset(coverage, ID2SYM(rb_intern("branches")), branch_coverage);
|
||
rb_hash_aset(coverage, ID2SYM(rb_intern("decisions")), decision_coverage);
|
||
rb_ary_freeze(coverage);
|
||
rb_hash_aset(coverages, path, coverage);
|
include/ruby/ruby.h | ||
---|---|---|
#define RUBY_EVENT_C_RETURN 0x0040
|
||
#define RUBY_EVENT_RAISE 0x0080
|
||
#define RUBY_EVENT_DEFN 0x0090
|
||
#define RUBY_EVENT_BRANCH 0x00a0
|
||
#define RUBY_EVENT_DECISION_TRUE 0x00a0
|
||
#define RUBY_EVENT_DECISION_FALSE 0x00b0
|
||
#define RUBY_EVENT_ALL 0x00ff
|
||
/* for TracePoint extended events */
|
||
... | ... | |
#define RUBY_EVENT_SPECIFIED_LINE 0x010000
|
||
#define RUBY_EVENT_COVERAGE 0x020000
|
||
#define RUBY_EVENT_MCOVERAGE 0x040000
|
||
#define RUBY_EVENT_BCOVERAGE 0x080000
|
||
#define RUBY_EVENT_DCOVERAGE_TRUE 0x080000
|
||
#define RUBY_EVENT_DCOVERAGE_FALSE 0x2000000
|
||
/* internal events */
|
||
#define RUBY_INTERNAL_EVENT_SWITCH 0x040000
|
iseq.c | ||
---|---|---|
struct rb_coverage_struct *const coverage = iseq->coverage;
|
||
RUBY_MARK_UNLESS_NULL(coverage->lines);
|
||
RUBY_MARK_UNLESS_NULL(coverage->methods);
|
||
RUBY_MARK_UNLESS_NULL(coverage->branches);
|
||
RUBY_MARK_UNLESS_NULL(coverage->decisions);
|
||
}
|
||
if (iseq->compile_data != 0) {
|
||
... | ... | |
iseq->coverage = ALLOC(struct rb_coverage_struct);
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage->lines, Qfalse);
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage->methods, Qfalse);
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage->branches, Qfalse);
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage->decisions, Qfalse);
|
||
if (!GET_THREAD()->parse_in_eval) {
|
||
VALUE coverages = rb_get_coverages();
|
||
if (RTEST(coverages)) {
|
||
... | ... | |
rb_hash_lookup(rb_hash_lookup(coverages, path), ID2SYM(rb_intern("lines"))));
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage->methods,
|
||
rb_hash_lookup(rb_hash_lookup(coverages, path), ID2SYM(rb_intern("methods"))));
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage->branches,
|
||
rb_hash_lookup(rb_hash_lookup(coverages, path), ID2SYM(rb_intern("branches"))));
|
||
RB_OBJ_WRITE(iseq->self, &iseq->coverage->decisions,
|
||
rb_hash_lookup(rb_hash_lookup(coverages, path), ID2SYM(rb_intern("decisions"))));
|
||
if (NIL_P(iseq->coverage->lines)) RB_OBJ_WRITE(iseq->self, &iseq->coverage->lines, Qfalse);
|
||
if (NIL_P(iseq->coverage->methods)) RB_OBJ_WRITE(iseq->self, &iseq->coverage->methods, Qfalse);
|
||
if (NIL_P(iseq->coverage->branches)) RB_OBJ_WRITE(iseq->self, &iseq->coverage->branches, Qfalse);
|
||
if (NIL_P(iseq->coverage->decisions)) RB_OBJ_WRITE(iseq->self, &iseq->coverage->decisions, Qfalse);
|
||
}
|
||
}
|
||
parse.y | ||
---|---|---|
VALUE lines = rb_ary_new2(n);
|
||
VALUE rb_file_coverage = rb_hash_new();
|
||
VALUE methods = rb_hash_new();
|
||
VALUE branches = rb_hash_new();
|
||
VALUE decisions = rb_hash_new();
|
||
int i;
|
||
RBASIC_CLEAR_CLASS(lines);
|
||
for (i = 0; i < n; i++) RARRAY_ASET(lines, i, Qnil);
|
||
RARRAY(lines)->as.heap.len = n;
|
||
rb_hash_aset(rb_file_coverage, ID2SYM(rb_intern("lines")), lines);
|
||
rb_hash_aset(rb_file_coverage, ID2SYM(rb_intern("methods")), methods);
|
||
rb_hash_aset(rb_file_coverage, ID2SYM(rb_intern("branches")), branches);
|
||
rb_hash_aset(rb_file_coverage, ID2SYM(rb_intern("decisions")), decisions);
|
||
rb_hash_aset(coverages, fname, rb_file_coverage);
|
||
return lines;
|
||
}
|
test/coverage/test_branch_coverage.rb | ||
---|---|---|
Coverage.start
|
||
require tmp + '/test.rb'
|
||
branch_coverage = Coverage.result[tmp + '/test.rb'][:branches]
|
||
assert_equal 6, branch_coverage.size
|
||
assert_equal 1, branch_coverage[2]
|
||
assert_equal 1, branch_coverage[5]
|
||
assert_equal 0, branch_coverage[6]
|
||
assert_equal 0, branch_coverage[10]
|
||
assert_equal 0, branch_coverage[12]
|
||
assert_equal 1, branch_coverage[14]
|
||
decision_coverage = Coverage.result[tmp + '/test.rb'][:decisions]
|
||
assert_equal 5, decision_coverage.size
|
||
assert_equal [1,0], decision_coverage[1]
|
||
assert_equal [0,1], decision_coverage[5]
|
||
assert_equal [1,0], decision_coverage[6]
|
||
assert_equal [0,1], decision_coverage[9]
|
||
assert_equal [0,1], decision_coverage[11]
|
||
}
|
||
}
|
||
ensure
|
||
... | ... | |
Dir.chdir(tmp) {
|
||
File.open("test.rb", "w") do |f|
|
||
f.puts <<-EOS
|
||
case
|
||
when 2 + 2 == 5
|
||
x = :bad
|
||
x = :math
|
||
when 2 + 2 == 4
|
||
x = :good
|
||
else
|
||
x = :also_bad
|
||
def foo(arg)
|
||
case
|
||
when arg == 1
|
||
x = :one
|
||
when arg == 2
|
||
x = :two
|
||
else
|
||
x = :none
|
||
end
|
||
end
|
||
foo(2); foo(2); foo(3)
|
||
case [1,2,3]
|
||
when String
|
||
:string?
|
||
else
|
||
:else
|
||
def bar(arg)
|
||
case arg
|
||
when String
|
||
:string?
|
||
when 1
|
||
:one
|
||
else
|
||
:else
|
||
end
|
||
end
|
||
bar("BAR"); bar("BAZ"); bar(1); bar(7)
|
||
EOS
|
||
end
|
||
Coverage.start
|
||
require tmp + '/test.rb'
|
||
decision_coverage = Coverage.result[tmp + '/test.rb'][:decisions]
|
||
assert_equal 4, decision_coverage.size
|
||
assert_equal [0,3], decision_coverage[3]
|
||
assert_equal [2,1], decision_coverage[5]
|
||
assert_equal [2,2], decision_coverage[15]
|
||
assert_equal [1,1], decision_coverage[17]
|
||
}
|
||
}
|
||
ensure
|
||
$".replace loaded_features
|
||
end
|
||
def test_when_without_else_coverage
|
||
loaded_features = $".dup
|
||
Dir.mktmpdir {|tmp|
|
||
Dir.chdir(tmp) {
|
||
File.open("test.rb", "w") do |f|
|
||
f.puts <<-EOS
|
||
def foo(arg)
|
||
case
|
||
when arg == 1
|
||
x = :one
|
||
when arg == 2
|
||
x = :two
|
||
end
|
||
end
|
||
foo(2); foo(2); foo(3)
|
||
def bar(arg)
|
||
case arg
|
||
when String
|
||
:string?
|
||
when 1
|
||
:one
|
||
end
|
||
end
|
||
bar("BAR"); bar("BAZ"); bar(1); bar(7)
|
||
EOS
|
||
end
|
||
Coverage.start
|
||
require tmp + '/test.rb'
|
||
decision_coverage = Coverage.result[tmp + '/test.rb'][:decisions]
|
||
assert_equal 4, decision_coverage.size
|
||
assert_equal [0,3], decision_coverage[3]
|
||
assert_equal [2,1], decision_coverage[5]
|
||
assert_equal [2,2], decision_coverage[13]
|
||
assert_equal [1,1], decision_coverage[15]
|
||
}
|
||
}
|
||
ensure
|
||
$".replace loaded_features
|
||
end
|
||
def test_when_with_splats_coverage
|
||
loaded_features = $".dup
|
||
Dir.mktmpdir {|tmp|
|
||
Dir.chdir(tmp) {
|
||
File.open("test.rb", "w") do |f|
|
||
f.puts <<-EOS
|
||
def prime?(arg)
|
||
primes = [2,3,5,7]
|
||
composites = [4,6,8,9]
|
||
case arg
|
||
when *primes
|
||
:prime
|
||
when *composites
|
||
:composite
|
||
end
|
||
end
|
||
9.times {|i| prime?(i+1) }
|
||
EOS
|
||
end
|
||
Coverage.start
|
||
require tmp + '/test.rb'
|
||
branch_coverage = Coverage.result[tmp + '/test.rb'][:branches]
|
||
assert_equal 5, branch_coverage.size
|
||
assert_equal 0, branch_coverage[3]
|
||
assert_equal 1, branch_coverage[6]
|
- « Previous
- 1
- 2
- 3
- Next »