Ruby  2.5.0dev(2017-10-22revision60238)
coverage.c
Go to the documentation of this file.
1 /************************************************
2 
3  coverage.c -
4 
5  $Author: $
6 
7  Copyright (c) 2008 Yusuke Endoh
8 
9 ************************************************/
10 
11 #include "ruby.h"
12 #include "vm_core.h"
13 
14 static int current_mode;
15 
16 /*
17  * call-seq:
18  * Coverage.start => nil
19  *
20  * Enables coverage measurement.
21  */
22 static VALUE
23 rb_coverage_start(int argc, VALUE *argv, VALUE klass)
24 {
25  VALUE coverages, opt;
26  int mode, experimental_mode_enabled = 1;
27 
28  {
29  const char *e = getenv("COVERAGE_EXPERIMENTAL_MODE");
30  if (!e || !*e) experimental_mode_enabled = 0;
31  }
32 
33  if (!experimental_mode_enabled && argc != 0)
34  rb_error_arity(argc, 0, 0);
35  rb_scan_args(argc, argv, "01", &opt);
36 
37  if (argc == 0) {
38  mode = 0; /* compatible mode */
39  }
40  else if (opt == ID2SYM(rb_intern("all"))) {
42  }
43  else {
44  mode = 0;
45  opt = rb_convert_type(opt, T_HASH, "Hash", "to_hash");
46 
47  if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("lines")))))
48  mode |= COVERAGE_TARGET_LINES;
49  if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("branches")))))
51  if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("methods")))))
53  if (mode == 0) {
54  rb_raise(rb_eRuntimeError, "no measuring target is specified");
55  }
56  }
57 
58  coverages = rb_get_coverages();
59  if (!RTEST(coverages)) {
60  coverages = rb_hash_new();
61  rb_obj_hide(coverages);
62  current_mode = mode;
63  if (mode == 0) mode = COVERAGE_TARGET_LINES;
64  rb_set_coverages(coverages, mode);
65  }
66  else if (current_mode != mode) {
67  rb_raise(rb_eRuntimeError, "cannot change the measuring target during coverage measurement");
68  }
69  return Qnil;
70 }
71 
72 static VALUE
73 branch_coverage(VALUE branches)
74 {
75  VALUE ret = rb_hash_new();
76  VALUE structure = rb_ary_dup(RARRAY_AREF(branches, 0));
77  VALUE counters = rb_ary_dup(RARRAY_AREF(branches, 1));
78  int i, j;
79  long id = 0;
80 
81  for (i = 0; i < RARRAY_LEN(structure); i++) {
82  VALUE branches = RARRAY_AREF(structure, i);
83  VALUE base_type = RARRAY_AREF(branches, 0);
84  VALUE base_lineno = RARRAY_AREF(branches, 1);
85  VALUE children = rb_hash_new();
86  rb_hash_aset(ret, rb_ary_new_from_args(3, base_type, LONG2FIX(id++), base_lineno), children);
87  for (j = 2; j < RARRAY_LEN(branches); j += 3) {
88  VALUE target_label = RARRAY_AREF(branches, j);
89  VALUE target_lineno = RARRAY_AREF(branches, j + 1);
90  int idx = FIX2INT(RARRAY_AREF(branches, j + 2));
91  rb_hash_aset(children, rb_ary_new_from_args(3, target_label, LONG2FIX(id++), target_lineno), RARRAY_AREF(counters, idx));
92  }
93  }
94 
95  return ret;
96 }
97 
98 static VALUE
99 method_coverage(VALUE methods)
100 {
101  VALUE ret = rb_hash_new();
102  int i;
103  long id = 0;
104 
105  for (i = 0; i < RARRAY_LEN(methods); ) {
106  VALUE method_name = RARRAY_AREF(methods, i++);
107  VALUE lineno = RARRAY_AREF(methods, i++);
108  VALUE counter = RARRAY_AREF(methods, i++);
109  rb_hash_aset(ret, rb_ary_new_from_args(3, method_name, LONG2FIX(id++), lineno), counter);
110  }
111 
112  return ret;
113 }
114 
115 static int
116 coverage_peek_result_i(st_data_t key, st_data_t val, st_data_t h)
117 {
118  VALUE path = (VALUE)key;
119  VALUE coverage = (VALUE)val;
120  VALUE coverages = (VALUE)h;
121  if (current_mode == 0) {
122  /* compatible mode */
123  VALUE lines = rb_ary_dup(RARRAY_AREF(coverage, COVERAGE_INDEX_LINES));
124  rb_ary_freeze(lines);
125  coverage = lines;
126  }
127  else {
128  VALUE h = rb_hash_new();
129  VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
130  VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
131  VALUE methods = RARRAY_AREF(coverage, COVERAGE_INDEX_METHODS);
132 
133  if (lines) {
134  lines = rb_ary_dup(lines);
135  rb_ary_freeze(lines);
136  rb_hash_aset(h, ID2SYM(rb_intern("lines")), lines);
137  }
138 
139  if (branches) {
140  rb_hash_aset(h, ID2SYM(rb_intern("branches")), branch_coverage(branches));
141  }
142 
143  if (methods) {
144  rb_hash_aset(h, ID2SYM(rb_intern("methods")), method_coverage(methods));
145  }
146 
147  rb_hash_freeze(h);
148  coverage = h;
149  }
150 
151  rb_hash_aset(coverages, path, coverage);
152  return ST_CONTINUE;
153 }
154 
155 /*
156  * call-seq:
157  * Coverage.peek_result => hash
158  *
159  * Returns a hash that contains filename as key and coverage array as value.
160  *
161  * {
162  * "file.rb" => [1, 2, nil],
163  * ...
164  * }
165  */
166 static VALUE
167 rb_coverage_peek_result(VALUE klass)
168 {
169  VALUE coverages = rb_get_coverages();
170  VALUE ncoverages = rb_hash_new();
171  if (!RTEST(coverages)) {
172  rb_raise(rb_eRuntimeError, "coverage measurement is not enabled");
173  }
174  st_foreach(RHASH_TBL(coverages), coverage_peek_result_i, ncoverages);
175  rb_hash_freeze(ncoverages);
176  return ncoverages;
177 }
178 
179 /*
180  * call-seq:
181  * Coverage.result => hash
182  *
183  * Returns a hash that contains filename as key and coverage array as value
184  * and disables coverage measurement.
185  */
186 static VALUE
187 rb_coverage_result(VALUE klass)
188 {
189  VALUE ncoverages = rb_coverage_peek_result(klass);
191  return ncoverages;
192 }
193 
194 /*
195  * call-seq:
196  * Coverage.running? => bool
197  *
198  * Returns true if coverage stats are currently being collected (after
199  * Coverage.start call, but before Coverage.result call)
200  */
201 static VALUE
202 rb_coverage_running(VALUE klass)
203 {
204  VALUE coverages = rb_get_coverages();
205  return RTEST(coverages) ? Qtrue : Qfalse;
206 }
207 
208 /* Coverage provides coverage measurement feature for Ruby.
209  * This feature is experimental, so these APIs may be changed in future.
210  *
211  * = Usage
212  *
213  * 1. require "coverage"
214  * 2. do Coverage.start
215  * 3. require or load Ruby source file
216  * 4. Coverage.result will return a hash that contains filename as key and
217  * coverage array as value. A coverage array gives, for each line, the
218  * number of line execution by the interpreter. A +nil+ value means
219  * coverage is disabled for this line (lines like +else+ and +end+).
220  *
221  * = Example
222  *
223  * [foo.rb]
224  * s = 0
225  * 10.times do |x|
226  * s += x
227  * end
228  *
229  * if s == 45
230  * p :ok
231  * else
232  * p :ng
233  * end
234  * [EOF]
235  *
236  * require "coverage"
237  * Coverage.start
238  * require "foo.rb"
239  * p Coverage.result #=> {"foo.rb"=>[1, 1, 10, nil, nil, 1, 1, nil, 0, nil]}
240  */
241 void
243 {
244  VALUE rb_mCoverage = rb_define_module("Coverage");
245  rb_define_module_function(rb_mCoverage, "start", rb_coverage_start, -1);
246  rb_define_module_function(rb_mCoverage, "result", rb_coverage_result, 0);
247  rb_define_module_function(rb_mCoverage, "peek_result", rb_coverage_peek_result, 0);
248  rb_define_module_function(rb_mCoverage, "running?", rb_coverage_running, 0);
249 }
#define RARRAY_LEN(a)
Definition: ruby.h:1019
VALUE rb_ary_freeze(VALUE ary)
Definition: array.c:409
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2284
#define st_foreach
Definition: regint.h:186
#define Qtrue
Definition: ruby.h:437
Definition: st.h:99
#define COVERAGE_TARGET_LINES
Definition: internal.h:1697
void Init_coverage(void)
Definition: coverage.c:242
#define T_HASH
Definition: ruby.h:499
VALUE rb_hash_lookup(VALUE hash, VALUE key)
Definition: hash.c:853
void rb_set_coverages(VALUE coverages, int mode)
Definition: thread.c:5050
#define COVERAGE_INDEX_BRANCHES
Definition: internal.h:1695
#define RHASH_TBL(h)
Definition: ruby.h:1056
RUBY_SYMBOL_EXPORT_BEGIN typedef unsigned long st_data_t
Definition: st.h:22
VALUE rb_hash_aset(VALUE hash, VALUE key, VALUE val)
Definition: hash.c:1616
#define val
VALUE rb_get_coverages(void)
Definition: thread.c:5044
int argc
Definition: ruby.c:187
#define Qfalse
Definition: ruby.h:436
void rb_define_module_function(VALUE module, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a module function for module.
Definition: class.c:1731
VALUE rb_hash_new(void)
Definition: hash.c:424
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:1908
#define Qnil
Definition: ruby.h:438
unsigned long VALUE
Definition: ruby.h:85
#define FIX2INT(x)
Definition: ruby.h:686
#define COVERAGE_INDEX_LINES
Definition: internal.h:1694
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition: object.c:72
VALUE rb_hash_freeze(VALUE hash)
Definition: hash.c:77
#define getenv(name)
Definition: win32.c:71
void rb_reset_coverages(void)
Definition: thread.c:5072
#define RARRAY_AREF(a, i)
Definition: ruby.h:1033
#define COVERAGE_TARGET_METHODS
Definition: internal.h:1699
VALUE rb_eRuntimeError
Definition: error.c:800
void rb_error_arity(int argc, int min, int max)
VALUE rb_convert_type(VALUE, int, const char *, const char *)
Converts an object into another type.
Definition: object.c:2965
#define LONG2FIX(i)
Definition: ruby.h:234
#define RTEST(v)
Definition: ruby.h:450
VALUE rb_ary_dup(VALUE ary)
Definition: array.c:1930
#define ID2SYM(x)
Definition: ruby.h:383
VALUE() rb_ary_new_from_args(long n,...)
Definition: array.c:505
VALUE rb_define_module(const char *name)
Definition: class.c:768
#define rb_intern(str)
#define COVERAGE_TARGET_BRANCHES
Definition: internal.h:1698
char ** argv
Definition: ruby.c:188
#define COVERAGE_INDEX_METHODS
Definition: internal.h:1696