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

ruby-changes:48905

From: mame <ko1@a...>
Date: Tue, 5 Dec 2017 16:16:48 +0900 (JST)
Subject: [ruby-changes:48905] mame:r61023 (trunk): Revamp method coverage to support define_method

mame	2017-12-05 16:16:42 +0900 (Tue, 05 Dec 2017)

  New Revision: 61023

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

  Log:
    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`).

  Modified files:
    trunk/compile.c
    trunk/ext/coverage/coverage.c
    trunk/hash.c
    trunk/internal.h
    trunk/iseq.h
    trunk/method.h
    trunk/test/coverage/test_coverage.rb
    trunk/thread.c
    trunk/tool/run-lcov.rb
    trunk/tool/test-coverage.rb
    trunk/vm_core.h
Index: method.h
===================================================================
--- method.h	(revision 61022)
+++ method.h	(revision 61023)
@@ -190,6 +190,9 @@ const rb_method_entry_t *rb_method_entry https://github.com/ruby/ruby/blob/trunk/method.h#L190
 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);
Index: vm_core.h
===================================================================
--- vm_core.h	(revision 61022)
+++ vm_core.h	(revision 61023)
@@ -1753,7 +1753,7 @@ RUBY_SYMBOL_EXPORT_BEGIN https://github.com/ruby/ruby/blob/trunk/vm_core.h#L1753
 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);
Index: iseq.h
===================================================================
--- iseq.h	(revision 61022)
+++ iseq.h	(revision 61023)
@@ -51,7 +51,6 @@ iseq_mark_ary_create(int flip_cnt) https://github.com/ruby/ruby/blob/trunk/iseq.h#L51
 #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))
 
Index: compile.c
===================================================================
--- compile.c	(revision 61022)
+++ compile.c	(revision 61023)
@@ -294,19 +294,6 @@ struct iseq_compile_data_ensure_node_sta https://github.com/ruby/ruby/blob/trunk/compile.c#L294
 	  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, co https://github.com/ruby/ruby/blob/trunk/compile.c#L657
 	  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;
Index: thread.c
===================================================================
--- thread.c	(revision 61022)
+++ thread.c	(revision 61023)
@@ -71,6 +71,7 @@ https://github.com/ruby/ruby/blob/trunk/thread.c#L71
 #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_ https://github.com/ruby/ruby/blob/trunk/thread.c#L4094
     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_ https://github.com/ruby/ruby/blob/trunk/thread.c#L4108
 	    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,20 +5014,72 @@ update_coverage(VALUE data, const rb_tra https://github.com/ruby/ruby/blob/trunk/thread.c#L5014
 	    }
 	    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));
-		}
-	    }
+	}
+    }
+}
+
+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));
     }
 }
 
@@ -5043,11 +5090,14 @@ rb_get_coverages(void) https://github.com/ruby/ruby/blob/trunk/thread.c#L5090
 }
 
 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_ https://github.com/ruby/ruby/blob/trunk/thread.c#L5107
     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) https://github.com/ruby/ruby/blob/trunk/thread.c#L5119
     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) https://github.com/ruby/ruby/blob/trunk/thread.c#L5156
     }
     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;
 }
 
Index: ext/coverage/coverage.c
===================================================================
--- ext/coverage/coverage.c	(revision 61022)
+++ ext/coverage/coverage.c	(revision 61023)
@@ -10,8 +10,10 @@ https://github.com/ruby/ruby/blob/trunk/ext/coverage/coverage.c#L10
 
 #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, https://github.com/ruby/ruby/blob/trunk/ext/coverage/coverage.c#L57
 	}
     }
 
+    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) https://github.com/ruby/ruby/blob/trunk/ext/coverage/coverage.c#L110
     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;
-
-    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);
+    /*
+     * 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 (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 https://github.com/ruby/ruby/blob/trunk/ext/coverage/coverage.c#L180
     }
     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) https://github.com/ruby/ruby/blob/trunk/ext/coverage/coverage.c#L224
 	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) https://github.com/ruby/ruby/blob/trunk/ext/coverage/coverage.c#L245
 {
     VALUE ncoverages = rb_coverage_peek_result(klass);
     rb_reset_coverages();
+    me2counter = Qnil;
     return ncoverages;
 }
 
@@ -252,4 +304,5 @@ Init_coverage(void) https://github.com/ruby/ruby/blob/trunk/ext/coverage/coverage.c#L304
     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);
 }
Index: hash.c
===================================================================
--- hash.c	(revision 61022)
+++ hash.c	(revision 61023)
@@ -427,6 +427,14 @@ rb_hash_new(void) https://github.com/ruby/ruby/blob/trunk/hash.c#L427
 }
 
 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)
 {
     VALUE ret = rb_hash_new();
Index: internal.h
===================================================================
--- internal.h	(revision 61022)
+++ internal.h	(revision 61023)
@@ -1272,6 +1272,9 @@ void ruby_sized_xfree(void *x, size_t si https://github.com/ruby/ruby/blob/trunk/internal.h#L1272
 /* 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); https://github.com/ruby/ruby/blob/trunk/internal.h#L1761
 /* 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
Index: tool/run-lcov.rb
===================================================================
--- tool/run-lcov.rb	(revision 61022)
+++ tool/run-lcov.rb	(revision 61023)
@@ -87,15 +87,15 @@ def gen_rb_lcov(file) https://github.com/ruby/ruby/blob/trunk/tool/run-lcov.rb#L87
 
       # 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
Index: tool/test-coverage.rb
===================================================================
--- tool/test-coverage.rb	(revision 61022)
+++ tool/test-coverage.rb	(revision 61023)
@@ -41,6 +41,15 @@ def add_count(h, key, count) https://github.com/ruby/ruby/blob/trunk/tool/test-coverage.rb#L41
 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
Index: test/coverage/test_coverage.rb
===================================================================
--- test/coverage/test_coverage.rb	(revision 61022)
+++ test/coverage/test_coverage.rb	(revision 61023)
@@ -188,7 +188,15 @@ class TestCoverage < Test::Unit::TestCas https://github.com/ruby/ruby/blob/trunk/test/coverage/test_coverage.rb#L188
           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::TestCas https://github.com/ruby/ruby/blob/trunk/test/coverage/test_coverage.rb#L340
   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::TestCas https://github.com/ruby/ruby/blob/trunk/test/coverage/test_coverage.rb#L356
       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) (... truncated)

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

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