[前][次][番号順一覧][スレッド一覧]

ruby-changes:47758

From: mame <ko1@a...>
Date: Thu, 14 Sep 2017 12:25:41 +0900 (JST)
Subject: [ruby-changes:47758] mame:r59876 (trunk): Add branch coverage for if statement

mame	2017-09-14 12:25:36 +0900 (Thu, 14 Sep 2017)

  New Revision: 59876

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=59876

  Log:
    Add branch coverage for if statement

  Modified files:
    trunk/compile.c
    trunk/ext/coverage/coverage.c
    trunk/test/coverage/test_coverage.rb
    trunk/thread.c
Index: ext/coverage/coverage.c
===================================================================
--- ext/coverage/coverage.c	(revision 59875)
+++ ext/coverage/coverage.c	(revision 59876)
@@ -67,6 +67,31 @@ rb_coverage_start(int argc, VALUE *argv, https://github.com/ruby/ruby/blob/trunk/ext/coverage/coverage.c#L67
     return Qnil;
 }
 
+static VALUE
+branch_coverage(VALUE branches)
+{
+    VALUE ret = rb_hash_new();
+    VALUE structure = rb_ary_dup(RARRAY_AREF(branches, 0));
+    VALUE counters = rb_ary_dup(RARRAY_AREF(branches, 1));
+    int i, j, id = 0;
+
+    for (i = 0; i < RARRAY_LEN(structure); i++) {
+	VALUE branches = RARRAY_AREF(structure, i);
+	VALUE base_type = RARRAY_AREF(branches, 0);
+	VALUE base_lineno = RARRAY_AREF(branches, 1);
+	VALUE children = rb_hash_new();
+	rb_hash_aset(ret, rb_ary_new_from_args(3, base_type, INT2FIX(id++), base_lineno), children);
+	for (j = 2; j < RARRAY_LEN(branches); j += 3) {
+	    VALUE target_label = RARRAY_AREF(branches, j);
+	    VALUE target_lineno = RARRAY_AREF(branches, j + 1);
+	    int idx = FIX2INT(RARRAY_AREF(branches, j + 2));
+	    rb_hash_aset(children, rb_ary_new_from_args(3, target_label, INT2FIX(id++), target_lineno), RARRAY_AREF(counters, idx));
+	}
+    }
+
+    return ret;
+}
+
 static int
 coverage_peek_result_i(st_data_t key, st_data_t val, st_data_t h)
 {
@@ -92,7 +117,7 @@ coverage_peek_result_i(st_data_t key, st https://github.com/ruby/ruby/blob/trunk/ext/coverage/coverage.c#L117
 	}
 
 	if (branches) {
-	    rb_hash_aset(h, ID2SYM(rb_intern("branches")), branches);
++	    rb_hash_aset(h, ID2SYM(rb_intern("branches")), branch_coverage(branches));
 	}
 
 	if (methods) {
Index: compile.c
===================================================================
--- compile.c	(revision 59875)
+++ compile.c	(revision 59876)
@@ -251,6 +251,32 @@ struct iseq_compile_data_ensure_node_sta https://github.com/ruby/ruby/blob/trunk/compile.c#L251
 	  ADD_INSN2((seq), (line), trace2, INT2FIX(RUBY_EVENT_COVERAGE), INT2FIX(COVERAGE_INDEX_LINES)); \
       } \
   } while (0)
+#define DECL_BRANCH_BASE(branches, line, type) \
+  do { \
+      if (ISEQ_COVERAGE(iseq) && \
+	  ISEQ_BRANCH_COVERAGE(iseq) && \
+	  (line) > 0) { \
+	  VALUE structure = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 0); \
+	  branches = rb_ary_tmp_new(0); \
+	  rb_ary_push(structure, branches); \
+	  rb_ary_push(branches, ID2SYM(rb_intern(type))); \
+	  rb_ary_push(branches, INT2FIX(line)); \
+      } \
+  } while (0)
+#define ADD_TRACE_BRANCH_COVERAGE(seq, line, type, branches) \
+  do { \
+      if (ISEQ_COVERAGE(iseq) && \
+	  ISEQ_BRANCH_COVERAGE(iseq) && \
+	  (line) > 0) { \
+	  VALUE counters = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 1); \
+	  long counter_idx = RARRAY_LEN(counters); \
+	  rb_ary_push(counters, INT2FIX(0)); \
+	  rb_ary_push(branches, ID2SYM(rb_intern(type))); \
+	  rb_ary_push(branches, INT2FIX(line)); \
+	  rb_ary_push(branches, INT2FIX(counter_idx)); \
+	  ADD_INSN2((seq), (line), trace2, INT2FIX(RUBY_EVENT_COVERAGE), INT2FIX(counter_idx * 16 + COVERAGE_INDEX_BRANCHES)); \
+      } \
+  } while (0)
 
 #define ADD_TRACE(seq, line, event) \
   do { \
@@ -4145,6 +4171,7 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR https://github.com/ruby/ruby/blob/trunk/compile.c#L4171
     DECL_ANCHOR(then_seq);
     DECL_ANCHOR(else_seq);
     LABEL *then_label, *else_label, *end_label;
+    VALUE branches;
 
     INIT_ANCHOR(cond_seq);
     INIT_ANCHOR(then_seq);
@@ -4160,8 +4187,15 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR https://github.com/ruby/ruby/blob/trunk/compile.c#L4187
 
     ADD_SEQ(ret, cond_seq);
 
+    if (then_label->refcnt && else_label->refcnt) {
+	DECL_BRANCH_BASE(branches, line, "if");
+    }
+
     if (then_label->refcnt) {
 	ADD_LABEL(ret, then_label);
+	if (else_label->refcnt) {
+	    ADD_TRACE_BRANCH_COVERAGE(ret, node->nd_body ? nd_line(node->nd_body) : line, "then", branches);
+	}
 	ADD_SEQ(ret, then_seq);
 	end_label = NEW_LABEL(line);
 	ADD_INSNL(ret, line, jump, end_label);
@@ -4169,6 +4203,9 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR https://github.com/ruby/ruby/blob/trunk/compile.c#L4203
 
     if (else_label->refcnt) {
 	ADD_LABEL(ret, else_label);
+	if (then_label->refcnt) {
+	    ADD_TRACE_BRANCH_COVERAGE(ret, node->nd_else ? nd_line(node->nd_else) : line, "else", branches);
+	}
 	ADD_SEQ(ret, else_seq);
     }
 
Index: test/coverage/test_coverage.rb
===================================================================
--- test/coverage/test_coverage.rb	(revision 59875)
+++ test/coverage/test_coverage.rb	(revision 59876)
@@ -175,4 +175,31 @@ class TestCoverage < Test::Unit::TestCas https://github.com/ruby/ruby/blob/trunk/test/coverage/test_coverage.rb#L175
       end
     end;
   end
+
+  def test_branch_coverage_for_if_statement
+    Dir.mktmpdir {|tmp|
+      Dir.chdir(tmp) {
+        File.open("test.rb", "w") do |f|
+          f.puts 'def foo(x)'
+          f.puts '  if x == 0'
+          f.puts '    0'
+          f.puts '  else'
+          f.puts '    1'
+          f.puts '  end'
+          f.puts 'end'
+          f.puts 'foo(0)'
+          f.puts 'foo(0)'
+          f.puts 'foo(1)'
+        end
+
+        assert_in_out_err(%w[-W0 -rcoverage], <<-"end;", ["{:branches=>{[:if, 0, 2]=>{[:then, 1, 3]=>2, [:else, 2, 5]=>1}}}"], [])
+          ENV["COVERAGE_EXPERIMENTAL_MODE"] = "true"
+          Coverage.start(branches: true)
+          tmp = Dir.pwd
+          require tmp + '/test.rb'
+          p Coverage.result[tmp + "/test.rb"]
+        end;
+      }
+    }
+  end
 end
Index: thread.c
===================================================================
--- thread.c	(revision 59875)
+++ thread.c	(revision 59876)
@@ -4093,6 +4093,7 @@ clear_coverage_i(st_data_t key, st_data_ https://github.com/ruby/ruby/blob/trunk/thread.c#L4093
     int i;
     VALUE coverage = (VALUE)val;
     VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
+    VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
 
     if (lines) {
 	for (i = 0; i < RARRAY_LEN(lines); i++) {
@@ -4101,6 +4102,12 @@ clear_coverage_i(st_data_t key, st_data_ https://github.com/ruby/ruby/blob/trunk/thread.c#L4102
 	    }
 	}
     }
+    if (branches) {
+	VALUE counters = RARRAY_AREF(branches, 1);
+	for (i = 0; i < RARRAY_LEN(counters); i++) {
+	    RARRAY_ASET(counters, i, INT2FIX(0));
+	}
+    }
 
     return ST_CONTINUE;
 }
@@ -4975,7 +4982,8 @@ update_coverage(VALUE data, const rb_tra https://github.com/ruby/ruby/blob/trunk/thread.c#L4982
 {
     VALUE coverage = rb_iseq_coverage(GET_THREAD()->ec.cfp->iseq);
     if (RB_TYPE_P(coverage, T_ARRAY) && !RBASIC_CLASS(coverage)) {
-	switch (FIX2INT(trace_arg->data)) {
+	long arg = FIX2INT(trace_arg->data);
+	switch (arg % 16) {
 	  case COVERAGE_INDEX_LINES: {
 	    VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
 	    if (lines) {
@@ -4994,6 +5002,20 @@ update_coverage(VALUE data, const rb_tra https://github.com/ruby/ruby/blob/trunk/thread.c#L5002
 	    }
 	    break;
 	  }
+	  case COVERAGE_INDEX_BRANCHES: {
+	    VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
+	    if (branches) {
+		long count;
+		long idx = arg / 16;
+		VALUE counters = RARRAY_AREF(branches, 1);
+		VALUE num = RARRAY_AREF(counters, idx);
+		count = FIX2LONG(num) + 1;
+		if (POSFIXABLE(count)) {
+		    RARRAY_ASET(counters, idx, LONG2FIX(count));
+		}
+	    }
+	    break;
+	  }
 	}
     }
 }
@@ -5018,7 +5040,9 @@ reset_coverage_i(st_data_t key, st_data_ https://github.com/ruby/ruby/blob/trunk/thread.c#L5040
 {
     VALUE coverage = (VALUE)val;
     VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
-    rb_ary_clear(lines);
+    VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
+    if (lines) rb_ary_clear(lines);
+    if (branches) rb_ary_clear(branches);
     return ST_CONTINUE;
 }
 
@@ -5044,7 +5068,22 @@ rb_default_coverage(int n) https://github.com/ruby/ruby/blob/trunk/thread.c#L5068
     RARRAY_ASET(coverage, COVERAGE_INDEX_LINES, lines);
 
     if (mode & COVERAGE_TARGET_BRANCHES) {
-	/* not implemented yet */
+	branches = rb_ary_tmp_new_fill(2);
+	/* internal data structures for branch coverage:
+	 *
+	 * [[base_type, base_lineno,
+	 *   target_type_1, target_lineno_1, target_counter_index_1,
+	 *   target_type_2, target_lineno_2, target_counter_index_2, ...],
+	 *  ...]
+	 *
+	 * Example: [[:case, 1,
+	 *            :when, 2, 0,
+	 *            :when, 3, 1, ...],
+	 *           ...]
+	 */
+	RARRAY_ASET(branches, 0, rb_ary_tmp_new(0));
+	/* branch execution counters */
+	RARRAY_ASET(branches, 1, rb_ary_tmp_new(0));
     }
     RARRAY_ASET(coverage, COVERAGE_INDEX_BRANCHES, branches);
 

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]