valo-rcov 0.8.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/BLURB +111 -0
  2. data/LICENSE +53 -0
  3. data/Rakefile +88 -0
  4. data/THANKS +96 -0
  5. data/bin/rcov +500 -0
  6. data/doc/readme_for_api +41 -0
  7. data/doc/readme_for_emacs +64 -0
  8. data/doc/readme_for_rake +62 -0
  9. data/doc/readme_for_vim +47 -0
  10. data/editor-extensions/rcov.el +131 -0
  11. data/editor-extensions/rcov.vim +38 -0
  12. data/ext/rcovrt/1.8/callsite.c +216 -0
  13. data/ext/rcovrt/1.8/rcovrt.c +287 -0
  14. data/ext/rcovrt/1.9/callsite.c +234 -0
  15. data/ext/rcovrt/1.9/rcovrt.c +264 -0
  16. data/ext/rcovrt/extconf.rb +21 -0
  17. data/lib/rcov.rb +1009 -0
  18. data/lib/rcov/formatters.rb +15 -0
  19. data/lib/rcov/formatters/base_formatter.rb +168 -0
  20. data/lib/rcov/formatters/full_text_report.rb +55 -0
  21. data/lib/rcov/formatters/html_coverage.rb +255 -0
  22. data/lib/rcov/formatters/html_erb_template.rb +128 -0
  23. data/lib/rcov/formatters/text_coverage_diff.rb +199 -0
  24. data/lib/rcov/formatters/text_report.rb +36 -0
  25. data/lib/rcov/formatters/text_summary.rb +15 -0
  26. data/lib/rcov/lowlevel.rb +145 -0
  27. data/lib/rcov/rcovtask.rb +156 -0
  28. data/lib/rcov/templates/detail.html.erb +78 -0
  29. data/lib/rcov/templates/index.html.erb +76 -0
  30. data/lib/rcov/templates/screen.css +165 -0
  31. data/lib/rcov/version.rb +10 -0
  32. data/setup.rb +1588 -0
  33. data/test/assets/sample_01.rb +7 -0
  34. data/test/assets/sample_02.rb +5 -0
  35. data/test/assets/sample_03.rb +20 -0
  36. data/test/assets/sample_04.rb +10 -0
  37. data/test/assets/sample_05-new.rb +17 -0
  38. data/test/assets/sample_05-old.rb +13 -0
  39. data/test/assets/sample_05.rb +17 -0
  40. data/test/call_site_analyzer_test.rb +171 -0
  41. data/test/code_coverage_analyzer_test.rb +184 -0
  42. data/test/file_statistics_test.rb +471 -0
  43. data/test/functional_test.rb +89 -0
  44. data/test/turn_off_rcovrt.rb +4 -0
  45. metadata +107 -0
@@ -0,0 +1,41 @@
1
+
2
+ = +rcov+
3
+
4
+ +rcov+ is a:
5
+ 1. tool for code coverage analysis for Ruby
6
+ 2. library for collecting code coverage and execution count information
7
+ introspectively
8
+
9
+ If you want to use the command line tool, the output from
10
+ rcov -h
11
+ is self-explicative.
12
+
13
+ If you want to automate the execution of +rcov+ via Rake take a look at
14
+ readme_for_rake[link:files/README_rake.html].
15
+
16
+ If you want to use the associated library, read on.
17
+
18
+ == Usage of the +rcov+ runtime/library
19
+
20
+ +rcov+ is primarily a tool for code coverage analysis, but since 0.4.0 it
21
+ exposes some of its code so that you can build on top of its heuristics for
22
+ code analysis and its capabilities for coverage information and execution
23
+ count gathering.
24
+
25
+ The main classes of interest are Rcov::FileStatistics,
26
+ Rcov::CodeCoverageAnalyzer and Rcov::CallSiteAnalyzer.
27
+
28
+ Rcov::FileStatistics can use some heuristics to determine
29
+ which parts of the file are executable and which are mere comments.
30
+
31
+ Rcov::CodeCoverageAnalyzer is used to gather code coverage and execution
32
+ count information inside a running Ruby program.
33
+
34
+ Rcov::CallSiteAnalyzer is used to obtain information about where methods are
35
+ defined and who calls them.
36
+
37
+ The parts of +rcov+'s runtime meant to be reused (i.e. the external API) are
38
+ documented with RDoc. Those not meant to be used are clearly marked as so or
39
+ were deliberately removed from the present documentation.
40
+
41
+
@@ -0,0 +1,64 @@
1
+
2
+ <tt>rcov.el</tt> allows you to use rcov from Emacs conveniently.
3
+ * Run unit tests and jump to uncovered code by <tt>C-x `</tt>.
4
+ * Run unit tests and save the current coverage status.
5
+ * Run unit tests and jump to uncovered code introduced since the last run.
6
+ * View cross-reference annotated code.
7
+
8
+ == Installation
9
+
10
+ Copy <tt>rcov.el</tt> to the appropriate directory, which is in load-path.
11
+ Then require it.
12
+ (require 'rcov)
13
+
14
+
15
+ == Usage
16
+
17
+ There are some commands to run rcov in Emacs.
18
+ All of them displays +rcov+ window, whose major-mode is compilation-mode.
19
+ Therefore you can jump to uncovered code by <tt>C-x `</tt>.
20
+
21
+ +rcov-command-line+, +rcovsave-command-line+, and +rcovdiff-command-line+ define
22
+ command line to run rcov.
23
+ If you do not use +rcov+ from Rake, you must modify them.
24
+
25
+ === Finding uncovered code
26
+
27
+ Type the following while editing your program:
28
+ M-x rcov
29
+
30
+ === Setting the reference point
31
+
32
+ +rcov+'s <tt>--text-coverage-diff</tt> mode compares the current coverage status against
33
+ the saved one. It therefore needs that information to be recorded
34
+ before you write new code (typically right after you perform a commit) in
35
+ order to have something to compare against.
36
+
37
+ You can save the current status with the <tt>--save</tt> option.
38
+
39
+ Type the following to save the current status in Emacs:
40
+ M-x rcovsave
41
+ If you do not use +rcov+ from Rake, you must modify +rcovsave-command-line+ variable.
42
+
43
+ === Finding new uncovered code
44
+
45
+ Type the following to save the current status in Emacs:
46
+ M-x rcovdiff
47
+
48
+ === Viewing cross-reference annotated code
49
+
50
+ If you read cross-reference annotated code, issue
51
+ rake rcov RCOVOPTS='-a'
52
+ at the beginning.
53
+ This command creates +coverage+ directory and many *.rb files in it.
54
+ Filenames of these Ruby scripts are converted from original path.
55
+ You can browse them by normally <tt>C-x C-f</tt>.
56
+ You can think of <tt>-a</tt> option as <tt>--xrefs</tt> option and output format is Ruby script.
57
+
58
+ After find-file-ed annotated script, the major-mode is rcov-xref-mode,
59
+ which is derived from ruby-mode and specializes navigation.
60
+
61
+ <tt>Tab</tt> and <tt>M-Tab</tt> goes forward/backward links.
62
+ <tt>Ret</tt> follows selected link.
63
+
64
+ This feature is useful to read third-party code or to follow control flow.
@@ -0,0 +1,62 @@
1
+
2
+ == Code coverage analysis automation with Rake
3
+
4
+ Since 0.4.0, <tt>rcov</tt> features a <tt>Rcov::RcovTask</tt> task for rake
5
+ which can be used to automate test coverage analysis. Basic usage is as
6
+ follows:
7
+
8
+ require 'rcov/rcovtask'
9
+ Rcov::RcovTask.new do |t|
10
+ t.test_files = FileList['test/test*.rb']
11
+ # t.verbose = true # uncomment to see the executed command
12
+ end
13
+
14
+ This will create by default a task named <tt>rcov</tt>, and also a task to
15
+ remove the output directory where the XHTML report is generated.
16
+ The latter will be named <tt>clobber_rcob</tt>, and will be added to the main
17
+ <tt>clobber</tt> target.
18
+
19
+ === Passing command line options to <tt>rcov</tt>
20
+
21
+ You can provide a description, change the name of the generated tasks (the
22
+ one used to generate the report(s) and the clobber_ one) and pass options to
23
+ <tt>rcov</tt>:
24
+
25
+ desc "Analyze code coverage of the unit tests."
26
+ Rcov::RcovTask.new(:coverage) do |t|
27
+ t.test_files = FileList['test/test*.rb']
28
+ t.verbose = true
29
+ ## get a text report on stdout when rake is run:
30
+ t.rcov_opts << "--text-report"
31
+ ## only report files under 80% coverage
32
+ t.rcov_opts << "--threshold 80"
33
+ end
34
+
35
+ That will generate a <tt>coverage</tt> task and the associated
36
+ <tt>clobber_coverage</tt> task to remove the directory the report is dumped
37
+ to ("<tt>coverage</tt>" by default).
38
+
39
+ You can specify a different destination directory, which comes handy if you
40
+ have several <tt>RcovTask</tt>s; the <tt>clobber_*</tt> will take care of
41
+ removing that directory:
42
+
43
+ desc "Analyze code coverage for the FileStatistics class."
44
+ Rcov::RcovTask.new(:rcov_sourcefile) do |t|
45
+ t.test_files = FileList['test/test_FileStatistics.rb']
46
+ t.verbose = true
47
+ t.rcov_opts << "--test-unit-only"
48
+ t.output_dir = "coverage.sourcefile"
49
+ end
50
+
51
+ Rcov::RcovTask.new(:rcov_ccanalyzer) do |t|
52
+ t.test_files = FileList['test/test_CodeCoverageAnalyzer.rb']
53
+ t.verbose = true
54
+ t.rcov_opts << "--test-unit-only"
55
+ t.output_dir = "coverage.ccanalyzer"
56
+ end
57
+
58
+ === Options passed through the <tt>rake</tt> command line
59
+
60
+ You can override the options defined in the RcovTask by passing the new
61
+ options at the time you invoke rake.
62
+ The documentation for the Rcov::RcovTask explains how this can be done.
@@ -0,0 +1,47 @@
1
+
2
+ <tt>rcov.vim</tt> allows you to run unit tests from vim and enter quickfix mode in
3
+ order to jump to uncovered code introduced since the last run.
4
+
5
+ == Installation
6
+ Copy <tt>rcov.vim</tt> to the appropriate "compiler" directory (typically
7
+ <tt>$HOME/.vim/compiler</tt>).
8
+
9
+ == Usage
10
+
11
+ === Setting the reference point
12
+
13
+ +rcov+'s <tt>--text-coverage-diff</tt> mode compares the current coverage status against
14
+ the saved one. It therefore needs that information to be recorded
15
+ before you write new code (typically right after you perform a commit) in
16
+ order to have something to compare against.
17
+
18
+ You can save the current status with the <tt>--save</tt> option.
19
+ If you're running +rcov+ from Rake, you can do something like
20
+ rake rcov_units RCOVOPTS="-T --save --rails"
21
+ in order to take the current status as the reference point.
22
+
23
+ === Finding new uncovered code
24
+
25
+ Type the following in command mode while editing your program:
26
+ :compiler rcov
27
+
28
+ rcov.vim assumes +rcov+ can be invoked with a rake task (see
29
+ readme_for_rake[link:files/README_rake.html] for
30
+ information on how to create it).
31
+
32
+ You can then execute +rcov+ and enter quickfix mode by typing
33
+
34
+ :make <taskname>
35
+
36
+ where taskname is the +rcov+ task you want to use; if you didn't override the
37
+ default name in the Rakefile, just
38
+
39
+ :make rcov
40
+
41
+ will do.
42
+
43
+ vim will then enter quickfix mode, allowing you to jump to the areas that were
44
+ not covered since the last time you saved the coverage data.
45
+
46
+ --------
47
+ # vim: ft=text :
@@ -0,0 +1,131 @@
1
+ ;;; rcov.el -- Ruby Coverage Analysis Tool
2
+
3
+ ;;; Copyright (c) 2006 rubikitch <rubikitch@ruby-lang.org>
4
+ ;;;
5
+ ;;; Use and distribution subject to the terms of the rcov license.
6
+
7
+ (defvar rcov-xref-before-visit-source-hook nil
8
+ "Hook executed before jump.")
9
+ (defvar rcov-xref-after-visit-source-hook nil
10
+ "Hook executed after jump.")
11
+ (defvar rcov-command-line "rake rcov RCOVOPTS='--gcc --no-html'"
12
+ "Rcov command line to find uncovered code.
13
+ It is good to use rcov with Rake because it `cd's appropriate directory.
14
+ `--gcc' option is strongly recommended because `rcov' uses compilation-mode.")
15
+ (defvar rcovsave-command-line "rake rcov RCOVOPTS='--gcc --no-html --save=coverage.info'"
16
+ "Rcov command line to save coverage status. See also `rcov-command-line'.")
17
+ (defvar rcovdiff-command-line "rake rcov RCOVOPTS='-D --gcc --no-html'"
18
+ "Rcov command line to find new uncovered code. See also `rcov-command-line'.")
19
+
20
+ ;;;; rcov-xref-mode
21
+ (define-derived-mode rcov-xref-mode ruby-mode "Rxref"
22
+ "Major mode for annotated Ruby scripts (coverage/*.rb) by rcov."
23
+ (setq truncate-lines t)
24
+ ;; ruby-electric-mode / pabbrev-mode hijacks TAB binding.
25
+ (and ruby-electric-mode (ruby-electric-mode -1))
26
+ (and (boundp 'pabbrev-mode) pabbrev-mode (pabbrev-mode -1))
27
+ (suppress-keymap rcov-xref-mode-map)
28
+ (define-key rcov-xref-mode-map "\C-i" 'rcov-xref-next-tag)
29
+ (define-key rcov-xref-mode-map "\M-\C-i" 'rcov-xref-previous-tag)
30
+ (define-key rcov-xref-mode-map "\C-m" 'rcov-xref-visit-source)
31
+ (set (make-local-variable 'automatic-hscrolling) nil)
32
+ )
33
+
34
+ (defvar rcov-xref-tag-regexp "\\[\\[\\(.*?\\)\\]\\]")
35
+
36
+ (defun rcov-xref-next-tag (n)
37
+ "Go to next LINK."
38
+ (interactive "p")
39
+ (when (looking-at rcov-xref-tag-regexp)
40
+ (goto-char (match-end 0)))
41
+ (when (re-search-forward rcov-xref-tag-regexp nil t n)
42
+ (goto-char (match-beginning 0)))
43
+ (rcov-xref-show-link))
44
+
45
+ (defun rcov-xref-previous-tag (n)
46
+ "Go to previous LINK."
47
+ (interactive "p")
48
+ (re-search-backward rcov-xref-tag-regexp nil t n)
49
+ (rcov-xref-show-link))
50
+
51
+ (defvar rcov-xref-link-tempbuffer " *rcov-link*")
52
+ (defun rcov-xref-show-link ()
53
+ "Follow current LINK."
54
+ (let ((link (match-string 1))
55
+ (eol (point-at-eol)))
56
+ (save-excursion
57
+ (when (and link
58
+ (re-search-backward "# \\(>>\\|<<\\) " (point-at-bol) t))
59
+ (while (re-search-forward rcov-xref-tag-regexp eol t)
60
+ (let ((matched (match-string 1)))
61
+ (when (string= link matched)
62
+ (add-text-properties 0 (length matched) '(face highlight) matched))
63
+ (with-current-buffer (get-buffer-create rcov-xref-link-tempbuffer)
64
+ (insert matched "\n"))))
65
+ (let (message-log-max) ; inhibit *Messages*
66
+ (message "%s" (with-current-buffer rcov-xref-link-tempbuffer
67
+ (substring (buffer-string) 0 -1)))) ; chomp
68
+ (kill-buffer rcov-xref-link-tempbuffer)))))
69
+
70
+
71
+ ;; copied from jw-visit-source
72
+ (defun rcov-xref-extract-file-lines (line)
73
+ "Extract a list of file/line pairs from the given line of text."
74
+ (let*
75
+ ((unix_fn "[^ \t\n\r\"'([<{]+")
76
+ (dos_fn "[a-zA-Z]:[^ \t\n\r\"'([<{]+")
77
+ (flre (concat "\\(" unix_fn "\\|" dos_fn "\\):\\([0-9]+\\)"))
78
+ (start nil)
79
+ (result nil))
80
+ (while (string-match flre line start)
81
+ (setq start (match-end 0))
82
+ (setq result
83
+ (cons (list
84
+ (substring line (match-beginning 1) (match-end 1))
85
+ (string-to-int (substring line (match-beginning 2) (match-end 2))))
86
+ result)))
87
+ result))
88
+
89
+ (defun rcov-xref-select-file-line (candidates)
90
+ "Select a file/line candidate that references an existing file."
91
+ (cond ((null candidates) nil)
92
+ ((file-readable-p (caar candidates)) (car candidates))
93
+ (t (rcov-xref-select-file-line (cdr candidates))) ))
94
+
95
+ (defun rcov-xref-visit-source ()
96
+ "If the current line contains text like '../src/program.rb:34', visit
97
+ that file in the other window and position point on that line."
98
+ (interactive)
99
+ (let* ((line (progn (looking-at rcov-xref-tag-regexp) (match-string 1)))
100
+ (candidates (rcov-xref-extract-file-lines line))
101
+ (file-line (rcov-xref-select-file-line candidates)))
102
+ (cond (file-line
103
+ (run-hooks 'rcov-xref-before-visit-source-hook)
104
+ (find-file (car file-line))
105
+ (goto-line (cadr file-line))
106
+ (run-hooks 'rcov-xref-after-visit-source-hook))
107
+ (t
108
+ (error "No source location on line.")) )))
109
+
110
+ ;;;; Running rcov with various options.
111
+ (defun rcov-internal (cmdline)
112
+ "Run rcov with various options."
113
+ (compile-internal cmdline ""
114
+ nil nil nil (lambda (x) "*rcov*")))
115
+
116
+ (defun rcov ()
117
+ "Run rcov to find uncovered code."
118
+ (interactive)
119
+ (rcov-internal rcov-command-line))
120
+
121
+ (defun rcovsave ()
122
+ "Run rcov to save coverage status."
123
+ (interactive)
124
+ (rcov-internal rcovsave-command-line))
125
+
126
+ (defun rcovdiff ()
127
+ "Run rcov to find new uncovered code."
128
+ (interactive)
129
+ (rcov-internal rcovdiff-command-line))
130
+
131
+ (provide 'rcov)
@@ -0,0 +1,38 @@
1
+ " Vim compiler file
2
+ " Language: Ruby
3
+ " Function: Code coverage information with rcov
4
+ " Maintainer: Mauricio Fernandez <mfp at acm dot org>
5
+ " Info:
6
+ " URL: http://eigenclass.org/hiki.rb?rcov
7
+ " ----------------------------------------------------------------------------
8
+ "
9
+ " Changelog:
10
+ " 0.1: initial version, shipped with rcov 0.6.0
11
+ "
12
+ " Comments:
13
+ " Initial attempt.
14
+ " ----------------------------------------------------------------------------
15
+
16
+ if exists("current_compiler")
17
+ finish
18
+ endif
19
+ let current_compiler = "rcov"
20
+
21
+ if exists(":CompilerSet") != 2 " older Vim always used :setlocal
22
+ command -nargs=* CompilerSet setlocal <args>
23
+ endif
24
+
25
+ let s:cpo_save = &cpo
26
+ set cpo-=C
27
+
28
+ CompilerSet makeprg=rake\ $*\ RCOVOPTS=\"-D\ --no-html\ --no-color\"\ $*
29
+
30
+ CompilerSet errorformat=
31
+ \%+W\#\#\#\ %f:%l\,
32
+ \%-C\ \ \ ,
33
+ \%-C!!\
34
+
35
+ let &cpo = s:cpo_save
36
+ unlet s:cpo_save
37
+
38
+ " vim: nowrap sw=2 sts=2 ts=8 ff=unix :
@@ -0,0 +1,216 @@
1
+ #include <ruby.h>
2
+ #include <env.h>
3
+ #include <node.h>
4
+ #include <st.h>
5
+ #include <stdlib.h>
6
+
7
+ static char callsite_hook_set_p;
8
+
9
+ typedef struct {
10
+ char *sourcefile;
11
+ unsigned int sourceline;
12
+ VALUE curr_meth;
13
+ } type_def_site;
14
+
15
+ static VALUE caller_info = 0;
16
+ static VALUE method_def_site_info = 0;
17
+
18
+ static caller_stack_len = 1;
19
+
20
+ static VALUE record_callsite_info(VALUE args) {
21
+ VALUE caller_ary;
22
+ VALUE curr_meth;
23
+ VALUE count_hash;
24
+ VALUE count;
25
+ VALUE *pargs = (VALUE *)args;
26
+
27
+ caller_ary = pargs[0];
28
+ curr_meth = pargs[1];
29
+ count_hash = rb_hash_aref(caller_info, curr_meth);
30
+
31
+ if(TYPE(count_hash) != T_HASH) {
32
+ /* Qnil, anything else should be impossible unless somebody's been
33
+ * messing with ObjectSpace */
34
+ count_hash = rb_hash_new();
35
+ rb_hash_aset(caller_info, curr_meth, count_hash);
36
+ }
37
+
38
+ count = rb_hash_aref(count_hash, caller_ary);
39
+
40
+ if(count == Qnil)
41
+ count = INT2FIX(0);
42
+
43
+ count = INT2FIX(FIX2UINT(count) + 1);
44
+ rb_hash_aset(count_hash, caller_ary, count);
45
+ /*
46
+ printf("CALLSITE: %s -> %s %d\n", RSTRING(rb_inspect(curr_meth))->ptr,
47
+ RSTRING(rb_inspect(caller_ary))->ptr, FIX2INT(count));
48
+ */
49
+
50
+ return Qnil;
51
+ }
52
+
53
+ static VALUE record_method_def_site(VALUE args) {
54
+ type_def_site *pargs = (type_def_site *)args;
55
+ VALUE def_site_info;
56
+ VALUE hash;
57
+
58
+ if(RTEST(rb_hash_aref(method_def_site_info, pargs->curr_meth)))
59
+ return Qnil;
60
+ def_site_info = rb_ary_new();
61
+ rb_ary_push(def_site_info, rb_str_new2(pargs->sourcefile));
62
+ rb_ary_push(def_site_info, INT2NUM(pargs->sourceline+1));
63
+ rb_hash_aset(method_def_site_info, pargs->curr_meth, def_site_info);
64
+ /*
65
+ printf("DEFSITE: %s:%d for %s\n", pargs->sourcefile, pargs->sourceline+1,
66
+ RSTRING(rb_inspect(pargs->curr_meth))->ptr);
67
+ */
68
+
69
+ return Qnil;
70
+ }
71
+
72
+ static VALUE callsite_custom_backtrace(int lev) {
73
+ struct FRAME *frame = ruby_frame;
74
+ VALUE ary;
75
+ NODE *n;
76
+ VALUE level;
77
+ VALUE klass;
78
+
79
+ ary = rb_ary_new();
80
+
81
+ if (frame->last_func == ID_ALLOCATOR) {
82
+ frame = frame->prev;
83
+ }
84
+
85
+ for (; frame && (n = frame->node); frame = frame->prev) {
86
+ if (frame->prev && frame->prev->last_func) {
87
+ if (frame->prev->node == n) continue;
88
+ level = rb_ary_new();
89
+ klass = frame->prev->last_class ? frame->prev->last_class : Qnil;
90
+ if(TYPE(klass) == T_ICLASS) {
91
+ klass = CLASS_OF(klass);
92
+ }
93
+ rb_ary_push(level, klass);
94
+ rb_ary_push(level, ID2SYM(frame->prev->last_func));
95
+ rb_ary_push(level, rb_str_new2(n->nd_file));
96
+ rb_ary_push(level, INT2NUM(nd_line(n)));
97
+ }
98
+ else {
99
+ level = rb_ary_new();
100
+ rb_ary_push(level, Qnil);
101
+ rb_ary_push(level, Qnil);
102
+ rb_ary_push(level, rb_str_new2(n->nd_file));
103
+ rb_ary_push(level, INT2NUM(nd_line(n)));
104
+ }
105
+ rb_ary_push(ary, level);
106
+ if(--lev == 0)
107
+ break;
108
+ }
109
+
110
+ return ary;
111
+ }
112
+
113
+ static void coverage_event_callsite_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass) {
114
+ VALUE caller_ary;
115
+ VALUE curr_meth;
116
+ VALUE args[2];
117
+ int status;
118
+
119
+ caller_ary = callsite_custom_backtrace(caller_stack_len);
120
+
121
+ if(TYPE(klass) == T_ICLASS) {
122
+ klass = CLASS_OF(klass);
123
+ }
124
+
125
+ curr_meth = rb_ary_new();
126
+ rb_ary_push(curr_meth, klass);
127
+ rb_ary_push(curr_meth, ID2SYM(mid));
128
+
129
+ args[0] = caller_ary;
130
+ args[1] = curr_meth;
131
+ rb_protect(record_callsite_info, (VALUE)args, &status);
132
+
133
+ if(!status && node) {
134
+ type_def_site args;
135
+
136
+ args.sourcefile = node->nd_file;
137
+ args.sourceline = nd_line(node) - 1;
138
+ args.curr_meth = curr_meth;
139
+ rb_protect(record_method_def_site, (VALUE)&args, NULL);
140
+ }
141
+
142
+ if(status)
143
+ rb_gv_set("$!", Qnil);
144
+ }
145
+
146
+ static VALUE cov_install_callsite_hook(VALUE self) {
147
+ if(!callsite_hook_set_p) {
148
+ if(TYPE(caller_info) != T_HASH)
149
+ caller_info = rb_hash_new();
150
+ callsite_hook_set_p = 1;
151
+ rb_add_event_hook(coverage_event_callsite_hook, RUBY_EVENT_CALL);
152
+
153
+ return Qtrue;
154
+ }
155
+ else
156
+ return Qfalse;
157
+ }
158
+
159
+ static VALUE cov_remove_callsite_hook(VALUE self) {
160
+ if(!callsite_hook_set_p)
161
+ return Qfalse;
162
+ else {
163
+ rb_remove_event_hook(coverage_event_callsite_hook);
164
+ callsite_hook_set_p = 0;
165
+ return Qtrue;
166
+ }
167
+ }
168
+
169
+ static VALUE cov_generate_callsite_info(VALUE self) {
170
+ VALUE ret;
171
+
172
+ ret = rb_ary_new();
173
+ rb_ary_push(ret, caller_info);
174
+ rb_ary_push(ret, method_def_site_info);
175
+ return ret;
176
+ }
177
+
178
+ static VALUE cov_reset_callsite(VALUE self) {
179
+ if(callsite_hook_set_p) {
180
+ rb_raise(rb_eRuntimeError, "Cannot reset the callsite info in the middle of a traced run.");
181
+ return Qnil;
182
+ }
183
+
184
+ caller_info = rb_hash_new();
185
+ method_def_site_info = rb_hash_new();
186
+ return Qnil;
187
+ }
188
+
189
+ void Init_rcov_callsite() {
190
+ VALUE mRcov;
191
+ VALUE mRCOV__;
192
+ ID id_rcov = rb_intern("Rcov");
193
+ ID id_coverage__ = rb_intern("RCOV__");
194
+ ID id_script_lines__ = rb_intern("SCRIPT_LINES__");
195
+
196
+ if(rb_const_defined(rb_cObject, id_rcov))
197
+ mRcov = rb_const_get(rb_cObject, id_rcov);
198
+ else
199
+ mRcov = rb_define_module("Rcov");
200
+
201
+ if(rb_const_defined(mRcov, id_coverage__))
202
+ mRCOV__ = rb_const_get_at(mRcov, id_coverage__);
203
+ else
204
+ mRCOV__ = rb_define_module_under(mRcov, "RCOV__");
205
+
206
+ callsite_hook_set_p = 0;
207
+ caller_info = rb_hash_new();
208
+ method_def_site_info = rb_hash_new();
209
+ rb_gc_register_address(&caller_info);
210
+ rb_gc_register_address(&method_def_site_info);
211
+
212
+ rb_define_singleton_method(mRCOV__, "install_callsite_hook", cov_install_callsite_hook, 0);
213
+ rb_define_singleton_method(mRCOV__, "remove_callsite_hook", cov_remove_callsite_hook, 0);
214
+ rb_define_singleton_method(mRCOV__, "generate_callsite_info", cov_generate_callsite_info, 0);
215
+ rb_define_singleton_method(mRCOV__, "reset_callsite", cov_reset_callsite, 0);
216
+ }