relevance-rcov 0.9.3-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/BLURB +111 -0
- data/LICENSE +53 -0
- data/Rakefile +94 -0
- data/THANKS +110 -0
- data/bin/rcov +514 -0
- data/doc/readme_for_api.markdown +22 -0
- data/doc/readme_for_emacs.markdown +52 -0
- data/doc/readme_for_rake.markdown +51 -0
- data/doc/readme_for_vim.markdown +34 -0
- data/editor-extensions/rcov.el +131 -0
- data/editor-extensions/rcov.vim +38 -0
- data/ext/java/src/CallsiteHook.java +137 -0
- data/ext/java/src/CoverageHook.java +117 -0
- data/ext/java/src/RcovHook.java +9 -0
- data/ext/java/src/RcovrtService.java +130 -0
- data/lib/rcov.rb +33 -0
- data/lib/rcov/call_site_analyzer.rb +225 -0
- data/lib/rcov/code_coverage_analyzer.rb +268 -0
- data/lib/rcov/coverage_info.rb +36 -0
- data/lib/rcov/differential_analyzer.rb +116 -0
- data/lib/rcov/file_statistics.rb +334 -0
- data/lib/rcov/formatters.rb +13 -0
- data/lib/rcov/formatters/base_formatter.rb +173 -0
- data/lib/rcov/formatters/failure_report.rb +15 -0
- data/lib/rcov/formatters/full_text_report.rb +48 -0
- data/lib/rcov/formatters/html_coverage.rb +274 -0
- data/lib/rcov/formatters/html_erb_template.rb +62 -0
- data/lib/rcov/formatters/text_coverage_diff.rb +193 -0
- data/lib/rcov/formatters/text_report.rb +32 -0
- data/lib/rcov/formatters/text_summary.rb +11 -0
- data/lib/rcov/lowlevel.rb +146 -0
- data/lib/rcov/rcovtask.rb +155 -0
- data/lib/rcov/templates/detail.html.erb +64 -0
- data/lib/rcov/templates/index.html.erb +93 -0
- data/lib/rcov/templates/jquery-1.3.2.min.js +19 -0
- data/lib/rcov/templates/jquery.tablesorter.min.js +15 -0
- data/lib/rcov/templates/print.css +12 -0
- data/lib/rcov/templates/rcov.js +42 -0
- data/lib/rcov/templates/screen.css +270 -0
- data/lib/rcov/version.rb +10 -0
- data/lib/rcovrt.jar +0 -0
- data/setup.rb +1588 -0
- data/test/assets/sample_01.rb +7 -0
- data/test/assets/sample_02.rb +5 -0
- data/test/assets/sample_03.rb +20 -0
- data/test/assets/sample_04.rb +10 -0
- data/test/assets/sample_05-new.rb +17 -0
- data/test/assets/sample_05-old.rb +13 -0
- data/test/assets/sample_05.rb +17 -0
- data/test/assets/sample_06.rb +8 -0
- data/test/call_site_analyzer_test.rb +171 -0
- data/test/code_coverage_analyzer_test.rb +219 -0
- data/test/file_statistics_test.rb +471 -0
- data/test/functional_test.rb +91 -0
- data/test/turn_off_rcovrt.rb +4 -0
- metadata +115 -0
@@ -0,0 +1,117 @@
|
|
1
|
+
import org.jruby.Ruby;
|
2
|
+
import org.jruby.RubyArray;
|
3
|
+
import org.jruby.RubyHash;
|
4
|
+
import org.jruby.runtime.ThreadContext;
|
5
|
+
import org.jruby.runtime.RubyEvent;
|
6
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
7
|
+
|
8
|
+
public class CoverageHook extends RcovHook {
|
9
|
+
|
10
|
+
private static CoverageHook hook;
|
11
|
+
|
12
|
+
public static CoverageHook getCoverageHook() {
|
13
|
+
if (hook == null) {
|
14
|
+
hook = new CoverageHook();
|
15
|
+
}
|
16
|
+
|
17
|
+
return hook;
|
18
|
+
}
|
19
|
+
|
20
|
+
private boolean active;
|
21
|
+
private RubyHash cover;
|
22
|
+
|
23
|
+
private CoverageHook() {
|
24
|
+
super();
|
25
|
+
}
|
26
|
+
|
27
|
+
public boolean isActive() {
|
28
|
+
return active;
|
29
|
+
}
|
30
|
+
|
31
|
+
public void setActive(boolean active) {
|
32
|
+
this.active = active;
|
33
|
+
}
|
34
|
+
|
35
|
+
public void eventHandler(ThreadContext context, String event, String file, int line, String name, IRubyObject type) {
|
36
|
+
//Line numbers are 1s based. Arrays are zero based. We need to compensate for that.
|
37
|
+
line -= 1;
|
38
|
+
|
39
|
+
// Make sure that we have SCRIPT_LINES__ and it's a hash
|
40
|
+
RubyHash scriptLines = getScriptLines(context.getRuntime());
|
41
|
+
if (scriptLines == null || !scriptLines.containsKey(file)) {
|
42
|
+
return;
|
43
|
+
}
|
44
|
+
|
45
|
+
// make sure the file's source lines are in SCRIPT_LINES__
|
46
|
+
cover = getCover(context.getRuntime());
|
47
|
+
RubyArray lines = (RubyArray) scriptLines.get(file);
|
48
|
+
if (lines == null || cover == null){
|
49
|
+
return;
|
50
|
+
}
|
51
|
+
|
52
|
+
// make sure file's counts are in COVER and set to zero
|
53
|
+
RubyArray counts = (RubyArray) cover.get(file);
|
54
|
+
if (counts == null) {
|
55
|
+
counts = context.getRuntime().newArray();
|
56
|
+
for (int i = 0; i < lines.size(); i++) {
|
57
|
+
counts.add(Long.valueOf(0));
|
58
|
+
}
|
59
|
+
cover.put(file, counts);
|
60
|
+
}
|
61
|
+
|
62
|
+
// in the case of code generation (one example is instance_eval for routes optimization)
|
63
|
+
// We could get here and see that we are not matched up with what we expect
|
64
|
+
if (counts.size() <= line ) {
|
65
|
+
for (int i=counts.size(); i<= line; i++) {
|
66
|
+
counts.add(Long.valueOf(0));
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
if (!context.isWithinTrace()) {
|
71
|
+
try {
|
72
|
+
context.setWithinTrace(true);
|
73
|
+
// update counts in COVER
|
74
|
+
Long count = (Long) counts.get(line);
|
75
|
+
if (count == null) {
|
76
|
+
count = Long.valueOf(0);
|
77
|
+
}
|
78
|
+
count = Long.valueOf(count.longValue() + 1);
|
79
|
+
counts.set(line , count);
|
80
|
+
}
|
81
|
+
finally{
|
82
|
+
context.setWithinTrace(false);
|
83
|
+
}
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
public boolean isInterestedInEvent(RubyEvent event) {
|
88
|
+
return event == RubyEvent.CALL || event == RubyEvent.LINE || event == RubyEvent.RETURN || event == RubyEvent.CLASS || event == RubyEvent.C_RETURN || event == RubyEvent.C_CALL;
|
89
|
+
}
|
90
|
+
|
91
|
+
/*
|
92
|
+
* Returns the COVER hash, setting up the COVER constant if necessary.
|
93
|
+
* @param runtime
|
94
|
+
* @return
|
95
|
+
*/
|
96
|
+
public RubyHash getCover(Ruby runtime) {
|
97
|
+
if (cover == null) {
|
98
|
+
cover = RubyHash.newHash(runtime);
|
99
|
+
}
|
100
|
+
|
101
|
+
return cover;
|
102
|
+
}
|
103
|
+
|
104
|
+
public RubyHash getScriptLines(Ruby runtime) {
|
105
|
+
IRubyObject scriptLines = runtime.getObject().getConstantAt("SCRIPT_LINES__");
|
106
|
+
if (scriptLines instanceof RubyHash) {
|
107
|
+
return (RubyHash) scriptLines;
|
108
|
+
} else {
|
109
|
+
return null;
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
public IRubyObject resetCoverage(Ruby runtime) {
|
114
|
+
getCover(runtime).clear();
|
115
|
+
return runtime.getNil();
|
116
|
+
}
|
117
|
+
}
|
@@ -0,0 +1,130 @@
|
|
1
|
+
import org.jruby.Ruby;
|
2
|
+
import org.jruby.RubyArray;
|
3
|
+
import org.jruby.RubyFixnum;
|
4
|
+
import org.jruby.RubyHash;
|
5
|
+
import org.jruby.RubyModule;
|
6
|
+
import org.jruby.RubyObjectAdapter;
|
7
|
+
import org.jruby.RubySymbol;
|
8
|
+
import org.jruby.exceptions.RaiseException;
|
9
|
+
import org.jruby.anno.JRubyMethod;
|
10
|
+
import org.jruby.runtime.ThreadContext;
|
11
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
12
|
+
import org.jruby.runtime.load.BasicLibraryService;
|
13
|
+
import org.jruby.javasupport.JavaEmbedUtils;
|
14
|
+
import org.jruby.javasupport.JavaUtil;
|
15
|
+
|
16
|
+
public class RcovrtService implements BasicLibraryService {
|
17
|
+
|
18
|
+
private static RubyObjectAdapter rubyApi;
|
19
|
+
|
20
|
+
public boolean basicLoad(Ruby runtime) {
|
21
|
+
RubyModule rcov = runtime.getOrCreateModule("Rcov");
|
22
|
+
RubyModule rcov__ = runtime.defineModuleUnder("RCOV__", rcov);
|
23
|
+
IRubyObject sl = runtime.getObject().getConstantAt("SCRIPT_LINES__");
|
24
|
+
|
25
|
+
if (sl == null) {
|
26
|
+
runtime.getObject().setConstant("SCRIPT_LINES__", RubyHash.newHash(runtime));
|
27
|
+
}
|
28
|
+
|
29
|
+
rubyApi = JavaEmbedUtils.newObjectAdapter();
|
30
|
+
rcov__.defineAnnotatedMethods(RcovrtService.class);
|
31
|
+
return true;
|
32
|
+
}
|
33
|
+
|
34
|
+
@JRubyMethod(name="reset_callsite", meta = true)
|
35
|
+
public static IRubyObject resetCallsite(IRubyObject recv) {
|
36
|
+
CallsiteHook hook = CallsiteHook.getCallsiteHook();
|
37
|
+
if (hook.isActive()) {
|
38
|
+
throw RaiseException.createNativeRaiseException(recv.getRuntime(),
|
39
|
+
new RuntimeException("Cannot reset the callsite info in the middle of a traced run."));
|
40
|
+
}
|
41
|
+
return hook.resetDefsites();
|
42
|
+
}
|
43
|
+
|
44
|
+
@JRubyMethod(name="reset_coverage", meta = true)
|
45
|
+
public static IRubyObject resetCoverage(IRubyObject recv) {
|
46
|
+
CoverageHook hook = CoverageHook.getCoverageHook();
|
47
|
+
if (hook.isActive()) {
|
48
|
+
throw RaiseException.createNativeRaiseException(recv.getRuntime(),
|
49
|
+
new RuntimeException("Cannot reset the coverage info in the middle of a traced run."));
|
50
|
+
}
|
51
|
+
return hook.resetCoverage(recv.getRuntime());
|
52
|
+
}
|
53
|
+
|
54
|
+
@JRubyMethod(name="remove_coverage_hook", meta = true)
|
55
|
+
public static IRubyObject removeCoverageHook(IRubyObject recv) {
|
56
|
+
return removeRcovHook(recv, CoverageHook.getCoverageHook());
|
57
|
+
}
|
58
|
+
|
59
|
+
@JRubyMethod(name="install_coverage_hook", meta = true)
|
60
|
+
public static IRubyObject installCoverageHook(IRubyObject recv) {
|
61
|
+
return installRcovHook(recv, CoverageHook.getCoverageHook());
|
62
|
+
}
|
63
|
+
|
64
|
+
/*
|
65
|
+
TODO: I think this is broken. I'm not sure why, but recreating
|
66
|
+
cover all the time seems bad.
|
67
|
+
*/
|
68
|
+
@JRubyMethod(name="generate_coverage_info", meta = true)
|
69
|
+
public static IRubyObject generateCoverageInfo(IRubyObject recv) {
|
70
|
+
Ruby run = recv.getRuntime();
|
71
|
+
RubyHash cover = (RubyHash)CoverageHook.getCoverageHook().getCover(run);
|
72
|
+
RubyHash xcover = RubyHash.newHash(run);
|
73
|
+
RubyArray keys = cover.keys();
|
74
|
+
RubyArray temp;
|
75
|
+
ThreadContext ctx = run.getCurrentContext();
|
76
|
+
for (int i=0; i < keys.length().getLongValue(); i++) {
|
77
|
+
IRubyObject key = keys.aref(JavaUtil.convertJavaToRuby(run, Long.valueOf(i)));
|
78
|
+
temp = ((RubyArray)cover.op_aref(ctx, key)).aryDup();
|
79
|
+
xcover.op_aset(ctx,key, temp);
|
80
|
+
}
|
81
|
+
RubyModule rcov__ = (RubyModule) recv.getRuntime().getModule("Rcov").getConstant("RCOV__");
|
82
|
+
|
83
|
+
if (rcov__.const_defined_p(ctx, RubySymbol.newSymbol(recv.getRuntime(), "COVER")).isTrue()) {
|
84
|
+
rcov__.remove_const(ctx, recv.getRuntime().newString("COVER"));
|
85
|
+
}
|
86
|
+
rcov__.defineConstant( "COVER", xcover );
|
87
|
+
|
88
|
+
return xcover;
|
89
|
+
}
|
90
|
+
|
91
|
+
@JRubyMethod(name="remove_callsite_hook", meta = true)
|
92
|
+
public static IRubyObject removeCallsiteHook(IRubyObject recv) {
|
93
|
+
return removeRcovHook( recv, CallsiteHook.getCallsiteHook() );
|
94
|
+
}
|
95
|
+
|
96
|
+
@JRubyMethod(name="install_callsite_hook", meta = true)
|
97
|
+
public static IRubyObject installCallsiteHook(IRubyObject recv) {
|
98
|
+
return installRcovHook( recv, CallsiteHook.getCallsiteHook() );
|
99
|
+
}
|
100
|
+
|
101
|
+
@JRubyMethod(name="generate_callsite_info", meta = true)
|
102
|
+
public static IRubyObject generateCallsiteInfo(IRubyObject recv) {
|
103
|
+
return CallsiteHook.getCallsiteHook().getCallsiteInfo( recv.getRuntime() ).dup();
|
104
|
+
}
|
105
|
+
|
106
|
+
@JRubyMethod(name="ABI", meta = true)
|
107
|
+
public static IRubyObject getAbi(IRubyObject recv) {
|
108
|
+
RubyArray ary = recv.getRuntime().newArray();
|
109
|
+
ary.add(RubyFixnum.int2fix( recv.getRuntime(), 2L));
|
110
|
+
ary.add(RubyFixnum.int2fix( recv.getRuntime(), 0L));
|
111
|
+
ary.add(RubyFixnum.int2fix( recv.getRuntime(), 0L));
|
112
|
+
return ary;
|
113
|
+
}
|
114
|
+
|
115
|
+
private static IRubyObject removeRcovHook(IRubyObject recv, RcovHook hook) {
|
116
|
+
hook.setActive(false);
|
117
|
+
recv.getRuntime().removeEventHook(hook);
|
118
|
+
return recv.getRuntime().getFalse();
|
119
|
+
}
|
120
|
+
|
121
|
+
private static IRubyObject installRcovHook( IRubyObject recv, RcovHook hook ) {
|
122
|
+
if (!hook.isActive()) {
|
123
|
+
hook.setActive(true);
|
124
|
+
recv.getRuntime().addEventHook(hook);
|
125
|
+
return recv.getRuntime().getTrue();
|
126
|
+
} else {
|
127
|
+
return recv.getRuntime().getFalse();
|
128
|
+
}
|
129
|
+
}
|
130
|
+
}
|
data/lib/rcov.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# rcov Copyright (c) 2004-2006 Mauricio Fernandez <mfp@acm.org>
|
2
|
+
#
|
3
|
+
# See LICENSE for licensing information.
|
4
|
+
|
5
|
+
# NOTE: if you're reading this in the XHTML code coverage report generated by
|
6
|
+
# rcov, you'll notice that only code inside methods is reported as covered,
|
7
|
+
# very much like what happens when you run it with --test-unit-only.
|
8
|
+
# This is due to the fact that we're running rcov on itself: the code below is
|
9
|
+
# already loaded before coverage tracing is activated, so only code inside
|
10
|
+
# methods is actually executed under rcov's inspection.
|
11
|
+
|
12
|
+
require 'rcov/version'
|
13
|
+
require 'rcov/formatters'
|
14
|
+
require 'rcov/coverage_info'
|
15
|
+
require 'rcov/file_statistics'
|
16
|
+
require 'rcov/differential_analyzer'
|
17
|
+
require 'rcov/code_coverage_analyzer'
|
18
|
+
require 'rcov/call_site_analyzer'
|
19
|
+
|
20
|
+
SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
|
21
|
+
|
22
|
+
module Rcov
|
23
|
+
# TODO: Move to Ruby 1.8.6 Backport module
|
24
|
+
unless RUBY_VERSION =~ /1.9/
|
25
|
+
class ::String
|
26
|
+
def lines
|
27
|
+
map
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
autoload :RCOV__, "rcov/lowlevel.rb"
|
33
|
+
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
module Rcov
|
2
|
+
# A CallSiteAnalyzer can be used to obtain information about:
|
3
|
+
# * where a method is defined ("+defsite+")
|
4
|
+
# * where a method was called from ("+callsite+")
|
5
|
+
#
|
6
|
+
# == Example
|
7
|
+
# <tt>example.rb</tt>:
|
8
|
+
# class X
|
9
|
+
# def f1; f2 end
|
10
|
+
# def f2; 1 + 1 end
|
11
|
+
# def f3; f1 end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# analyzer = Rcov::CallSiteAnalyzer.new
|
15
|
+
# x = X.new
|
16
|
+
# analyzer.run_hooked do
|
17
|
+
# x.f1
|
18
|
+
# end
|
19
|
+
# # ....
|
20
|
+
#
|
21
|
+
# analyzer.run_hooked do
|
22
|
+
# x.f3
|
23
|
+
# # the information generated in this run is aggregated
|
24
|
+
# # to the previously recorded one
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# analyzer.analyzed_classes # => ["X", ... ]
|
28
|
+
# analyzer.methods_for_class("X") # => ["f1", "f2", "f3"]
|
29
|
+
# analyzer.defsite("X#f1") # => DefSite object
|
30
|
+
# analyzer.callsites("X#f2") # => hash with CallSite => count
|
31
|
+
# # associations
|
32
|
+
# defsite = analyzer.defsite("X#f1")
|
33
|
+
# defsite.file # => "example.rb"
|
34
|
+
# defsite.line # => 2
|
35
|
+
#
|
36
|
+
# You can have several CallSiteAnalyzer objects at a time, and it is
|
37
|
+
# possible to nest the #run_hooked / #install_hook/#remove_hook blocks: each
|
38
|
+
# analyzer will manage its data separately. Note however that no special
|
39
|
+
# provision is taken to ignore code executed "inside" the CallSiteAnalyzer
|
40
|
+
# class.
|
41
|
+
#
|
42
|
+
# +defsite+ information is only available for methods that were called under
|
43
|
+
# the inspection of the CallSiteAnalyzer, i.o.w. you will only have +defsite+
|
44
|
+
# information for those methods for which callsite information is
|
45
|
+
# available.
|
46
|
+
class CallSiteAnalyzer < DifferentialAnalyzer
|
47
|
+
# A method definition site.
|
48
|
+
class DefSite < Struct.new(:file, :line)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Object representing a method call site.
|
52
|
+
# It corresponds to a part of the callstack starting from the context that
|
53
|
+
# called the method.
|
54
|
+
class CallSite < Struct.new(:backtrace)
|
55
|
+
# The depth of a CallSite is the number of stack frames
|
56
|
+
# whose information is included in the CallSite object.
|
57
|
+
def depth
|
58
|
+
backtrace.size
|
59
|
+
end
|
60
|
+
|
61
|
+
# File where the method call originated.
|
62
|
+
# Might return +nil+ or "" if it is not meaningful (C extensions, etc).
|
63
|
+
def file(level = 0)
|
64
|
+
stack_frame = backtrace[level]
|
65
|
+
stack_frame ? stack_frame[2] : nil
|
66
|
+
end
|
67
|
+
|
68
|
+
# Line where the method call originated.
|
69
|
+
# Might return +nil+ or 0 if it is not meaningful (C extensions, etc).
|
70
|
+
def line(level = 0)
|
71
|
+
stack_frame = backtrace[level]
|
72
|
+
stack_frame ? stack_frame[3] : nil
|
73
|
+
end
|
74
|
+
|
75
|
+
# Name of the method where the call originated.
|
76
|
+
# Returns +nil+ if the call originated in +toplevel+.
|
77
|
+
# Might return +nil+ if it could not be determined.
|
78
|
+
def calling_method(level = 0)
|
79
|
+
stack_frame = backtrace[level]
|
80
|
+
stack_frame ? stack_frame[1] : nil
|
81
|
+
end
|
82
|
+
|
83
|
+
# Name of the class holding the method where the call originated.
|
84
|
+
# Might return +nil+ if it could not be determined.
|
85
|
+
def calling_class(level = 0)
|
86
|
+
stack_frame = backtrace[level]
|
87
|
+
stack_frame ? stack_frame[0] : nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
@hook_level = 0
|
92
|
+
# defined this way instead of attr_accessor so that it's covered
|
93
|
+
def self.hook_level # :nodoc:
|
94
|
+
@hook_level
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.hook_level=(x) # :nodoc:
|
98
|
+
@hook_level = x
|
99
|
+
end
|
100
|
+
|
101
|
+
def initialize
|
102
|
+
super(:install_callsite_hook, :remove_callsite_hook,
|
103
|
+
:reset_callsite)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Classes whose methods have been called.
|
107
|
+
# Returns an array of strings describing the classes (just klass.to_s for
|
108
|
+
# each of them). Singleton classes are rendered as:
|
109
|
+
# #<Class:MyNamespace::MyClass>
|
110
|
+
def analyzed_classes
|
111
|
+
raw_data_relative.first.keys.map{|klass, meth| klass}.uniq.sort
|
112
|
+
end
|
113
|
+
|
114
|
+
# Methods that were called for the given class. See #analyzed_classes for
|
115
|
+
# the notation used for singleton classes.
|
116
|
+
# Returns an array of strings or +nil+
|
117
|
+
def methods_for_class(classname)
|
118
|
+
a = raw_data_relative.first.keys.select{|kl,_| kl == classname}.map{|_,meth| meth}.sort
|
119
|
+
a.empty? ? nil : a
|
120
|
+
end
|
121
|
+
alias_method :analyzed_methods, :methods_for_class
|
122
|
+
|
123
|
+
# Returns a hash with <tt>CallSite => call count</tt> associations or +nil+
|
124
|
+
# Can be called in two ways:
|
125
|
+
# analyzer.callsites("Foo#f1") # instance method
|
126
|
+
# analyzer.callsites("Foo.g1") # singleton method of the class
|
127
|
+
# or
|
128
|
+
# analyzer.callsites("Foo", "f1")
|
129
|
+
# analyzer.callsites("#<class:Foo>", "g1")
|
130
|
+
def callsites(classname_or_fullname, methodname = nil)
|
131
|
+
rawsites = raw_data_relative.first[expand_name(classname_or_fullname, methodname)]
|
132
|
+
return nil unless rawsites
|
133
|
+
ret = {}
|
134
|
+
# could be a job for inject but it's slow and I don't mind the extra loc
|
135
|
+
rawsites.each_pair do |backtrace, count|
|
136
|
+
ret[CallSite.new(backtrace)] = count
|
137
|
+
end
|
138
|
+
ret
|
139
|
+
end
|
140
|
+
|
141
|
+
# Returns a DefSite object corresponding to the given method
|
142
|
+
# Can be called in two ways:
|
143
|
+
# analyzer.defsite("Foo#f1") # instance method
|
144
|
+
# analyzer.defsite("Foo.g1") # singleton method of the class
|
145
|
+
# or
|
146
|
+
# analyzer.defsite("Foo", "f1")
|
147
|
+
# analyzer.defsite("#<class:Foo>", "g1")
|
148
|
+
def defsite(classname_or_fullname, methodname = nil)
|
149
|
+
file, line = raw_data_relative[1][expand_name(classname_or_fullname, methodname)]
|
150
|
+
return nil unless file && line
|
151
|
+
DefSite.new(file, line)
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def expand_name(classname_or_fullname, methodname = nil)
|
157
|
+
if methodname.nil?
|
158
|
+
case classname_or_fullname
|
159
|
+
when /(.*)#(.*)/ then classname, methodname = $1, $2
|
160
|
+
when /(.*)\.(.*)/ then classname, methodname = "#<Class:#{$1}>", $2
|
161
|
+
else
|
162
|
+
raise ArgumentError, "Incorrect method name"
|
163
|
+
end
|
164
|
+
|
165
|
+
return [classname, methodname]
|
166
|
+
end
|
167
|
+
|
168
|
+
[classname_or_fullname, methodname]
|
169
|
+
end
|
170
|
+
|
171
|
+
def data_default; [{}, {}] end
|
172
|
+
|
173
|
+
def raw_data_absolute
|
174
|
+
raw, method_def_site = RCOV__.generate_callsite_info
|
175
|
+
ret1 = {}
|
176
|
+
ret2 = {}
|
177
|
+
raw.each_pair do |(klass, method), hash|
|
178
|
+
begin
|
179
|
+
key = [klass.to_s, method.to_s]
|
180
|
+
ret1[key] = hash.clone #Marshal.load(Marshal.dump(hash))
|
181
|
+
ret2[key] = method_def_site[[klass, method]]
|
182
|
+
#rescue Exception
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
[ret1, ret2]
|
187
|
+
end
|
188
|
+
|
189
|
+
def aggregate_data(aggregated_data, delta)
|
190
|
+
callsites1, defsites1 = aggregated_data
|
191
|
+
callsites2, defsites2 = delta
|
192
|
+
|
193
|
+
callsites2.each_pair do |(klass, method), hash|
|
194
|
+
dest_hash = (callsites1[[klass, method]] ||= {})
|
195
|
+
hash.each_pair do |callsite, count|
|
196
|
+
dest_hash[callsite] ||= 0
|
197
|
+
dest_hash[callsite] += count
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
defsites1.update(defsites2)
|
202
|
+
end
|
203
|
+
|
204
|
+
def compute_raw_data_difference(first, last)
|
205
|
+
difference = {}
|
206
|
+
default = Hash.new(0)
|
207
|
+
|
208
|
+
callsites1, defsites1 = *first
|
209
|
+
callsites2, defsites2 = *last
|
210
|
+
|
211
|
+
callsites2.each_pair do |(klass, method), hash|
|
212
|
+
old_hash = callsites1[[klass, method]] || default
|
213
|
+
hash.each_pair do |callsite, count|
|
214
|
+
diff = hash[callsite] - (old_hash[callsite] || 0)
|
215
|
+
if diff > 0
|
216
|
+
difference[[klass, method]] ||= {}
|
217
|
+
difference[[klass, method]][callsite] = diff
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
[difference, defsites1.update(defsites2)]
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|