rubinius-coverage 2.0.3 → 2.1
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.
- checksums.yaml +4 -4
- data/Rakefile +7 -0
- data/lib/rubinius/coverage.rb +167 -20
- data/lib/rubinius/coverage/version.rb +1 -1
- data/rubinius-coverage.gemspec +1 -1
- data/spec/basic_block/double_branch_spec.rb +99 -0
- data/spec/basic_block/loop_spec.rb +96 -0
- data/spec/basic_block/single_branch_spec.rb +96 -0
- data/spec/basic_block/single_spec.rb +73 -0
- data/spec/spec_helper.rb +1 -0
- metadata +31 -11
- data/ext/rubinius/coverage/coverage.cpp +0 -163
- data/ext/rubinius/coverage/extconf.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22c19371e1c8ec5d8177165d91c12f992ebe7d3e
|
4
|
+
data.tar.gz: 893646e9c62de4bcc66de954693a37d3c6ff5615
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02895b2ce0adefd94c062611273d0715be55416eb502ce557982141f2e6bff475dda338c0372e0f1e20b3bd674e805448ccc355154ba8d11bf43f759a385ade7
|
7
|
+
data.tar.gz: 12a4dcddd1d91c319186f4ae52edc46dbb404e089101563e60c86e5b2beb58ad1c71e185952e265750fb16213d5c51e8e5b2b677d6116ece850bed4b89c45278
|
data/Rakefile
CHANGED
data/lib/rubinius/coverage.rb
CHANGED
@@ -2,39 +2,186 @@ require "rubinius/coverage/version"
|
|
2
2
|
|
3
3
|
module Rubinius
|
4
4
|
class Coverage
|
5
|
-
|
6
|
-
|
7
|
-
end
|
5
|
+
class CFG
|
6
|
+
attr_accessor :code, :call_sites, :basic_blocks, :enter, :exit
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
def initialize(code, call_sites)
|
9
|
+
@code = code
|
10
|
+
@call_sites = call_sites
|
11
|
+
@basic_blocks = Hash.new { |h, k| h[k] = BasicBlock.new k }
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
14
|
+
def graph
|
15
|
+
iseq = @code.iseq
|
16
|
+
total = iseq.size
|
17
|
+
i = 0
|
15
18
|
|
16
|
-
|
17
|
-
|
19
|
+
@enter = BasicBlock.new 0
|
20
|
+
@enter.left = bb = @basic_blocks[0]
|
18
21
|
|
19
|
-
|
20
|
-
|
22
|
+
@exit = @basic_blocks[total]
|
23
|
+
|
24
|
+
while i < total
|
25
|
+
opcode = InstructionSet[iseq[i]]
|
26
|
+
|
27
|
+
if @basic_blocks.has_key? i
|
28
|
+
bb.exit_ip = i-1
|
29
|
+
bb = bb.left = @basic_blocks[i]
|
30
|
+
else
|
31
|
+
bb.exit_ip = i+opcode.width-1
|
32
|
+
|
33
|
+
case opcode.control_flow
|
34
|
+
when :branch
|
35
|
+
bb.left = @basic_blocks[iseq[i+1]]
|
36
|
+
bb.left.exit_ip = total
|
37
|
+
|
38
|
+
next_bb = @basic_blocks[i+opcode.width]
|
39
|
+
bb.right = next_bb if opcode.name != :goto
|
40
|
+
|
41
|
+
bb = next_bb
|
42
|
+
when :raise, :return
|
43
|
+
bb.left = @exit
|
44
|
+
bb = @basic_blocks[i+opcode.width]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
i += opcode.width
|
49
|
+
end
|
50
|
+
|
51
|
+
@basic_blocks.values.each do |bb|
|
52
|
+
# Split if a jump target intersects our range
|
53
|
+
i = bb.enter_ip
|
54
|
+
while i < bb.exit_ip
|
55
|
+
opcode = InstructionSet[iseq[i]]
|
56
|
+
i += opcode.width
|
57
|
+
|
58
|
+
if @basic_blocks.has_key? i
|
59
|
+
bb.exit_ip = i-1
|
60
|
+
|
61
|
+
if opcode.control_flow != :branch
|
62
|
+
bb.left = @basic_blocks[i]
|
63
|
+
bb.right = nil
|
64
|
+
end
|
65
|
+
|
66
|
+
break
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
bb.coverage @code, @call_sites
|
71
|
+
end
|
72
|
+
|
73
|
+
stack = [@enter]
|
74
|
+
cycles = {}
|
75
|
+
|
76
|
+
until stack.empty?
|
77
|
+
bb = stack.shift
|
78
|
+
|
79
|
+
if left = bb.left
|
80
|
+
left.count = bb.count if bb.count > left.count
|
81
|
+
unless cycles.has_key? left
|
82
|
+
stack << left
|
83
|
+
cycles[left] = true
|
84
|
+
end
|
85
|
+
end
|
21
86
|
|
22
|
-
|
87
|
+
if right = bb.right
|
88
|
+
right.count = bb.count if bb.count > right.count
|
89
|
+
unless cycles.has_key? right
|
90
|
+
stack << right
|
91
|
+
cycles[right] = true
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
23
95
|
|
24
|
-
|
25
|
-
|
96
|
+
# Flow coverage counts backwards
|
97
|
+
@basic_blocks.values.each do |bb|
|
98
|
+
if bb.count == 0
|
99
|
+
if (bb.left and bb.left.count > 0) or
|
100
|
+
(bb.right and bb.right.count > 0)
|
101
|
+
bb.count = 1
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class BasicBlock
|
108
|
+
attr_accessor :enter_ip, :exit_ip, :left, :right, :lines, :count
|
109
|
+
|
110
|
+
def initialize(enter_ip)
|
111
|
+
@enter_ip = @exit_ip = enter_ip
|
112
|
+
@lines = []
|
113
|
+
@count = 0
|
114
|
+
end
|
115
|
+
|
116
|
+
def coverage(code, call_sites)
|
117
|
+
total = code.lines.size - 2
|
118
|
+
i = 0
|
119
|
+
|
120
|
+
while i < total
|
121
|
+
if (@enter_ip < code.lines[i+2] and @exit_ip >= code.lines[i]) or
|
122
|
+
(code.lines[i+2] == 0 and @enter_ip == 0)
|
123
|
+
line = code.lines[i+1]
|
124
|
+
@lines << line unless @lines.include? line
|
125
|
+
end
|
126
|
+
|
127
|
+
i += 2
|
128
|
+
end
|
129
|
+
|
130
|
+
call_sites.each do |cs|
|
131
|
+
if cs.ip >= @enter_ip and cs.ip <= @exit_ip
|
132
|
+
cs_count = cs.invokes + cs.hits + cs.misses
|
133
|
+
@count = cs_count if cs_count > @count
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
26
138
|
end
|
27
139
|
|
140
|
+
# Coverage methods
|
141
|
+
|
28
142
|
def start
|
29
|
-
Rubinius::
|
143
|
+
ObjectSpace.each_object(Rubinius::CallSite) { |cs| cs.reset }
|
30
144
|
|
31
|
-
|
145
|
+
nil
|
32
146
|
end
|
33
147
|
|
34
|
-
def
|
35
|
-
|
148
|
+
def coverage(code)
|
149
|
+
call_sites = code.call_sites
|
150
|
+
return unless call_sites.any? { |cs| cs.invokes + cs.hits + cs.misses > 0 }
|
151
|
+
|
152
|
+
cfg = CFG.new code, call_sites
|
153
|
+
cfg.graph
|
154
|
+
|
155
|
+
counts = {}
|
156
|
+
|
157
|
+
total = code.lines.size
|
158
|
+
i = 1
|
159
|
+
|
160
|
+
while i < total
|
161
|
+
counts[code.lines[i]] = 0
|
162
|
+
i += 2
|
163
|
+
end
|
164
|
+
|
165
|
+
cfg.basic_blocks.values.each do |bb|
|
166
|
+
bb.lines.each { |l| counts[l] = bb.count }
|
167
|
+
end
|
168
|
+
|
169
|
+
counts
|
170
|
+
end
|
171
|
+
|
172
|
+
def result
|
173
|
+
code_coverage = Hash.new { |h, k| h[k] = [] }
|
174
|
+
|
175
|
+
ObjectSpace.each_object(Rubinius::CompiledCode) do |code|
|
176
|
+
lines = code_coverage[code.file.to_s]
|
177
|
+
counts = coverage code
|
178
|
+
next unless counts
|
179
|
+
|
180
|
+
counts.each { |line, count| lines[line] = count }
|
181
|
+
end
|
36
182
|
|
37
|
-
|
183
|
+
code_coverage.delete_if { |k, v| v.empty? }
|
184
|
+
code_coverage
|
38
185
|
end
|
39
186
|
end
|
40
187
|
end
|
data/rubinius-coverage.gemspec
CHANGED
@@ -13,10 +13,10 @@ Gem::Specification.new do |spec|
|
|
13
13
|
|
14
14
|
spec.files = `git ls-files`.split($/)
|
15
15
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
16
|
-
spec.extensions = ["ext/rubinius/coverage/extconf.rb"]
|
17
16
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
17
|
spec.require_paths = ["lib"]
|
19
18
|
|
20
19
|
spec.add_development_dependency "bundler", "~> 1.3"
|
21
20
|
spec.add_development_dependency "rake", "~> 10.0"
|
21
|
+
spec.add_development_dependency "rspec", "~> 3"
|
22
22
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module CoverageSpecs
|
4
|
+
class DoubleBranch
|
5
|
+
def m
|
6
|
+
a = 1
|
7
|
+
if a == 2
|
8
|
+
3
|
9
|
+
else
|
10
|
+
2
|
11
|
+
end
|
12
|
+
|
13
|
+
4
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe Rubinius::Coverage do
|
19
|
+
context "with a single branch" do
|
20
|
+
let(:obj) { CoverageSpecs::DoubleBranch.new }
|
21
|
+
let(:code) { obj.method(:m).executable }
|
22
|
+
|
23
|
+
describe Rubinius::Coverage::CFG do
|
24
|
+
let(:cfg) { Rubinius::Coverage::CFG.new code, code.call_sites }
|
25
|
+
|
26
|
+
before do
|
27
|
+
cfg.graph
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#graph" do
|
31
|
+
it "creates five BasicBlock instances" do
|
32
|
+
expect(cfg.basic_blocks.size).to eql(5)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "creates a basic block at IP 0-13" do
|
36
|
+
bb = cfg.basic_blocks[0]
|
37
|
+
|
38
|
+
expect(bb.enter_ip).to eql(0)
|
39
|
+
expect(bb.exit_ip).to eql(13)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "creates a basic block at IP 14-17" do
|
43
|
+
bb = cfg.basic_blocks[14]
|
44
|
+
|
45
|
+
expect(bb.enter_ip).to eql(14)
|
46
|
+
expect(bb.exit_ip).to eql(17)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "creates a basic block at IP 18-19" do
|
50
|
+
bb = cfg.basic_blocks[18]
|
51
|
+
|
52
|
+
expect(bb.enter_ip).to eql(18)
|
53
|
+
expect(bb.exit_ip).to eql(19)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "creates a basic block at IP 20-23" do
|
57
|
+
bb = cfg.basic_blocks[20]
|
58
|
+
|
59
|
+
expect(bb.enter_ip).to eql(20)
|
60
|
+
expect(bb.exit_ip).to eql(23)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "set the count of the times the basic block was executed" do
|
64
|
+
obj.m
|
65
|
+
cfg.graph
|
66
|
+
|
67
|
+
bb = cfg.basic_blocks[0]
|
68
|
+
|
69
|
+
expect(bb.count).to eql(1)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "sets the lines covered by the basic block" do
|
73
|
+
lines = cfg.basic_blocks[0].lines
|
74
|
+
|
75
|
+
expect(lines.size).to eql(3)
|
76
|
+
expect(lines[0]).to eql(5)
|
77
|
+
expect(lines[1]).to eql(6)
|
78
|
+
expect(lines[2]).to eql(7)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "#coverage" do
|
84
|
+
it "returns a map of source code lines to execution counts" do
|
85
|
+
coverage = Rubinius::Coverage.new.coverage code
|
86
|
+
|
87
|
+
expect(coverage.size).to eql(7)
|
88
|
+
expect(coverage[5]).to eql(1)
|
89
|
+
expect(coverage[6]).to eql(1)
|
90
|
+
expect(coverage[7]).to eql(1)
|
91
|
+
expect(coverage[8]).to eql(1)
|
92
|
+
# TODO: fix bug generating line info for lines 9, 11, 12 in the code
|
93
|
+
# above.
|
94
|
+
expect(coverage[10]).to eql(1)
|
95
|
+
expect(coverage[13]).to eql(1)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module CoverageSpecs
|
4
|
+
class LoopBranch
|
5
|
+
def m
|
6
|
+
i = 1
|
7
|
+
while i < 10
|
8
|
+
2
|
9
|
+
i += 1
|
10
|
+
end
|
11
|
+
|
12
|
+
4
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe Rubinius::Coverage do
|
18
|
+
context "with a loop branch" do
|
19
|
+
let(:obj) { CoverageSpecs::LoopBranch.new }
|
20
|
+
let(:code) { obj.method(:m).executable }
|
21
|
+
|
22
|
+
describe Rubinius::Coverage::CFG do
|
23
|
+
let(:cfg) { Rubinius::Coverage::CFG.new code, code.call_sites }
|
24
|
+
|
25
|
+
before do
|
26
|
+
cfg.graph
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#graph" do
|
30
|
+
it "creates five BasicBlock instances" do
|
31
|
+
expect(cfg.basic_blocks.size).to eql(5)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "creates a basic block at IP 0-4" do
|
35
|
+
bb = cfg.basic_blocks[0]
|
36
|
+
|
37
|
+
expect(bb.enter_ip).to eql(0)
|
38
|
+
expect(bb.exit_ip).to eql(4)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "creates a basic block at IP 5-13" do
|
42
|
+
bb = cfg.basic_blocks[5]
|
43
|
+
|
44
|
+
expect(bb.enter_ip).to eql(5)
|
45
|
+
expect(bb.exit_ip).to eql(13)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "creates a basic block at IP 14-29" do
|
49
|
+
bb = cfg.basic_blocks[14]
|
50
|
+
|
51
|
+
expect(bb.enter_ip).to eql(14)
|
52
|
+
expect(bb.exit_ip).to eql(29)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "creates a basic block at IP 30-34" do
|
56
|
+
bb = cfg.basic_blocks[30]
|
57
|
+
|
58
|
+
expect(bb.enter_ip).to eql(30)
|
59
|
+
expect(bb.exit_ip).to eql(34)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "set the count of the times the basic block was executed" do
|
63
|
+
obj.m
|
64
|
+
cfg.graph
|
65
|
+
|
66
|
+
bb = cfg.basic_blocks[0]
|
67
|
+
|
68
|
+
expect(bb.count).to eql(1)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "sets the lines covered by the basic block" do
|
72
|
+
lines = cfg.basic_blocks[0].lines
|
73
|
+
|
74
|
+
expect(lines.size).to eql(2)
|
75
|
+
expect(lines[0]).to eql(5)
|
76
|
+
expect(lines[1]).to eql(6)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#coverage" do
|
82
|
+
it "returns a map of source code lines to execution counts" do
|
83
|
+
coverage = Rubinius::Coverage.new.coverage code
|
84
|
+
|
85
|
+
expect(coverage.size).to eql(7)
|
86
|
+
expect(coverage[5]).to eql(1)
|
87
|
+
expect(coverage[6]).to eql(1)
|
88
|
+
expect(coverage[7]).to eql(10)
|
89
|
+
expect(coverage[8]).to eql(9)
|
90
|
+
expect(coverage[9]).to eql(9)
|
91
|
+
# TODO: fix bug generating line info for lines 10, 11, 12 in the code
|
92
|
+
# above.
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module CoverageSpecs
|
4
|
+
class SingleBranch
|
5
|
+
def m
|
6
|
+
a = 1
|
7
|
+
if a == 2
|
8
|
+
3
|
9
|
+
end
|
10
|
+
|
11
|
+
4
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe Rubinius::Coverage do
|
17
|
+
context "with a single branch" do
|
18
|
+
let(:obj) { CoverageSpecs::SingleBranch.new }
|
19
|
+
let(:code) { obj.method(:m).executable }
|
20
|
+
|
21
|
+
describe Rubinius::Coverage::CFG do
|
22
|
+
let(:cfg) { Rubinius::Coverage::CFG.new code, code.call_sites }
|
23
|
+
|
24
|
+
before do
|
25
|
+
cfg.graph
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#graph" do
|
29
|
+
it "creates five BasicBlock instances" do
|
30
|
+
expect(cfg.basic_blocks.size).to eql(5)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "creates a basic block at IP 0-13" do
|
34
|
+
bb = cfg.basic_blocks[0]
|
35
|
+
|
36
|
+
expect(bb.enter_ip).to eql(0)
|
37
|
+
expect(bb.exit_ip).to eql(13)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "creates a basic block at IP 14-17" do
|
41
|
+
bb = cfg.basic_blocks[14]
|
42
|
+
|
43
|
+
expect(bb.enter_ip).to eql(14)
|
44
|
+
expect(bb.exit_ip).to eql(17)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "creates a basic block at IP 18-18" do
|
48
|
+
bb = cfg.basic_blocks[18]
|
49
|
+
|
50
|
+
expect(bb.enter_ip).to eql(18)
|
51
|
+
expect(bb.exit_ip).to eql(18)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "creates a basic block at IP 19-22" do
|
55
|
+
bb = cfg.basic_blocks[19]
|
56
|
+
|
57
|
+
expect(bb.enter_ip).to eql(19)
|
58
|
+
expect(bb.exit_ip).to eql(22)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "set the count of the times the basic block was executed" do
|
62
|
+
obj.m
|
63
|
+
cfg.graph
|
64
|
+
|
65
|
+
bb = cfg.basic_blocks[0]
|
66
|
+
|
67
|
+
expect(bb.count).to eql(1)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "sets the lines covered by the basic block" do
|
71
|
+
lines = cfg.basic_blocks[0].lines
|
72
|
+
|
73
|
+
expect(lines.size).to eql(3)
|
74
|
+
expect(lines[0]).to eql(5)
|
75
|
+
expect(lines[1]).to eql(6)
|
76
|
+
expect(lines[2]).to eql(7)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#coverage" do
|
82
|
+
it "returns a map of source code lines to execution counts" do
|
83
|
+
coverage = Rubinius::Coverage.new.coverage code
|
84
|
+
|
85
|
+
expect(coverage.size).to eql(6)
|
86
|
+
expect(coverage[5]).to eql(1)
|
87
|
+
expect(coverage[6]).to eql(1)
|
88
|
+
expect(coverage[7]).to eql(1)
|
89
|
+
expect(coverage[8]).to eql(1)
|
90
|
+
# TODO: fix bug generating line info for lines 9, 10 in the code
|
91
|
+
# above.
|
92
|
+
expect(coverage[11]).to eql(1)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module CoverageSpecs
|
4
|
+
class Single
|
5
|
+
def m
|
6
|
+
a = 1
|
7
|
+
b = 2
|
8
|
+
c = a + b
|
9
|
+
c.to_s
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe Rubinius::Coverage do
|
15
|
+
context "with a single basic block" do
|
16
|
+
let(:obj) { CoverageSpecs::Single.new }
|
17
|
+
let(:code) { obj.method(:m).executable }
|
18
|
+
|
19
|
+
describe Rubinius::Coverage::CFG do
|
20
|
+
let(:cfg) { Rubinius::Coverage::CFG.new code, code.call_sites }
|
21
|
+
|
22
|
+
before do
|
23
|
+
cfg.graph
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#graph" do
|
27
|
+
it "creates a single BasicBlock instance" do
|
28
|
+
expect(cfg.basic_blocks.size).to eql(2)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "sets the enter and exit IP for the basic block" do
|
32
|
+
bb = cfg.basic_blocks[0]
|
33
|
+
|
34
|
+
expect(bb.enter_ip).to eql(0)
|
35
|
+
expect(bb.exit_ip).to eql(24)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "set the count of the times the basic block was executed" do
|
39
|
+
obj.m
|
40
|
+
cfg.graph
|
41
|
+
|
42
|
+
bb = cfg.basic_blocks[0]
|
43
|
+
|
44
|
+
expect(bb.count).to eql(1)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "sets the lines covered by the basic block" do
|
48
|
+
lines = cfg.basic_blocks[0].lines
|
49
|
+
|
50
|
+
expect(lines.size).to eql(5)
|
51
|
+
expect(lines[0]).to eql(5)
|
52
|
+
expect(lines[1]).to eql(6)
|
53
|
+
expect(lines[2]).to eql(7)
|
54
|
+
expect(lines[3]).to eql(8)
|
55
|
+
expect(lines[4]).to eql(9)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#coverage" do
|
61
|
+
it "returns a map of source code lines to execution counts" do
|
62
|
+
coverage = Rubinius::Coverage.new.coverage code
|
63
|
+
|
64
|
+
expect(coverage.size).to eql(5)
|
65
|
+
expect(coverage[5]).to eql(1)
|
66
|
+
expect(coverage[6]).to eql(1)
|
67
|
+
expect(coverage[7]).to eql(1)
|
68
|
+
expect(coverage[8]).to eql(1)
|
69
|
+
expect(coverage[9]).to eql(1)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "rubinius/coverage"
|
metadata
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubinius-coverage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: '2.1'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Shirai
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-05-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
16
15
|
requirements:
|
17
16
|
- - "~>"
|
18
17
|
- !ruby/object:Gem::Version
|
19
18
|
version: '1.3'
|
19
|
+
name: bundler
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -25,12 +25,12 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
30
29
|
requirements:
|
31
30
|
- - "~>"
|
32
31
|
- !ruby/object:Gem::Version
|
33
32
|
version: '10.0'
|
33
|
+
name: rake
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -38,12 +38,25 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3'
|
47
|
+
name: rspec
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3'
|
41
55
|
description: Rubinius coverage tool.
|
42
56
|
email:
|
43
57
|
- brixen@gmail.com
|
44
58
|
executables: []
|
45
|
-
extensions:
|
46
|
-
- ext/rubinius/coverage/extconf.rb
|
59
|
+
extensions: []
|
47
60
|
extra_rdoc_files: []
|
48
61
|
files:
|
49
62
|
- ".gitignore"
|
@@ -51,11 +64,14 @@ files:
|
|
51
64
|
- LICENSE
|
52
65
|
- README.md
|
53
66
|
- Rakefile
|
54
|
-
- ext/rubinius/coverage/coverage.cpp
|
55
|
-
- ext/rubinius/coverage/extconf.rb
|
56
67
|
- lib/rubinius/coverage.rb
|
57
68
|
- lib/rubinius/coverage/version.rb
|
58
69
|
- rubinius-coverage.gemspec
|
70
|
+
- spec/basic_block/double_branch_spec.rb
|
71
|
+
- spec/basic_block/loop_spec.rb
|
72
|
+
- spec/basic_block/single_branch_spec.rb
|
73
|
+
- spec/basic_block/single_spec.rb
|
74
|
+
- spec/spec_helper.rb
|
59
75
|
homepage: https://github.com/rubinius/rubinius-coverage
|
60
76
|
licenses:
|
61
77
|
- BSD
|
@@ -76,9 +92,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
92
|
version: '0'
|
77
93
|
requirements: []
|
78
94
|
rubyforge_project:
|
79
|
-
rubygems_version: 2.
|
95
|
+
rubygems_version: 2.6.2
|
80
96
|
signing_key:
|
81
97
|
specification_version: 4
|
82
98
|
summary: Rubinius coverage tool.
|
83
|
-
test_files:
|
84
|
-
|
99
|
+
test_files:
|
100
|
+
- spec/basic_block/double_branch_spec.rb
|
101
|
+
- spec/basic_block/loop_spec.rb
|
102
|
+
- spec/basic_block/single_branch_spec.rb
|
103
|
+
- spec/basic_block/single_spec.rb
|
104
|
+
- spec/spec_helper.rb
|
@@ -1,163 +0,0 @@
|
|
1
|
-
#include <rbxti.hpp>
|
2
|
-
#include <rbxti/atomic.hpp>
|
3
|
-
#include <rbx_config.h>
|
4
|
-
|
5
|
-
#include <iostream>
|
6
|
-
#ifdef RBX_HAVE_TR1
|
7
|
-
#include <tr1/unordered_map>
|
8
|
-
#define std_unordered_map std::tr1::unordered_map
|
9
|
-
#else
|
10
|
-
#include <unordered_map>
|
11
|
-
#define std_unordered_map std::unordered_map
|
12
|
-
#endif
|
13
|
-
|
14
|
-
using namespace rbxti;
|
15
|
-
|
16
|
-
#if defined(RBX_HAVE_TR1) && !defined(RBX_HAVE_TR1_HASH)
|
17
|
-
namespace std {
|
18
|
-
namespace tr1 {
|
19
|
-
template <>
|
20
|
-
struct hash<r_mint> {
|
21
|
-
size_t operator()(const r_mint id) const {
|
22
|
-
return id;
|
23
|
-
}
|
24
|
-
};
|
25
|
-
}
|
26
|
-
}
|
27
|
-
#endif
|
28
|
-
|
29
|
-
namespace coverage {
|
30
|
-
|
31
|
-
typedef std_unordered_map<int, int> InstructionCoverageMap;
|
32
|
-
typedef std_unordered_map<r_mint, InstructionCoverageMap*> CoverageMap;
|
33
|
-
|
34
|
-
class Coverage {
|
35
|
-
CoverageMap coverage_map_;
|
36
|
-
rbxti::SpinLock lock_;
|
37
|
-
|
38
|
-
public:
|
39
|
-
Coverage() { }
|
40
|
-
~Coverage();
|
41
|
-
|
42
|
-
void lock() {
|
43
|
-
lock_.lock();
|
44
|
-
}
|
45
|
-
|
46
|
-
void unlock() {
|
47
|
-
lock_.unlock();
|
48
|
-
}
|
49
|
-
|
50
|
-
void add(Env* env, r_mint id, int ip);
|
51
|
-
robject results(Env* env);
|
52
|
-
};
|
53
|
-
|
54
|
-
Coverage::~Coverage() {
|
55
|
-
for(CoverageMap::iterator i = coverage_map_.begin();
|
56
|
-
i != coverage_map_.end();
|
57
|
-
i++) {
|
58
|
-
delete i->second;
|
59
|
-
}
|
60
|
-
}
|
61
|
-
|
62
|
-
void Coverage::add(Env* env, r_mint id, int ip) {
|
63
|
-
lock();
|
64
|
-
|
65
|
-
InstructionCoverageMap *map;
|
66
|
-
CoverageMap::iterator i = coverage_map_.find(id);
|
67
|
-
|
68
|
-
if(i == coverage_map_.end()) {
|
69
|
-
map = new InstructionCoverageMap;
|
70
|
-
coverage_map_[id] = map;
|
71
|
-
} else {
|
72
|
-
map = i->second;
|
73
|
-
}
|
74
|
-
|
75
|
-
InstructionCoverageMap::iterator j = map->find(ip);
|
76
|
-
|
77
|
-
if(j == map->end()) {
|
78
|
-
(*map)[ip] = 1;
|
79
|
-
} else {
|
80
|
-
(*map)[ip] += 1;
|
81
|
-
}
|
82
|
-
|
83
|
-
unlock();
|
84
|
-
}
|
85
|
-
|
86
|
-
static void ccode_iterator(Env* env, rcompiled_code code, void* data) {
|
87
|
-
rtable map = (rtable)data;
|
88
|
-
r_mint id = env->method_id(code);
|
89
|
-
|
90
|
-
bool found = false;
|
91
|
-
robject obj = env->table_fetch(map, env->integer_new(id), &found);
|
92
|
-
if(!found) return;
|
93
|
-
|
94
|
-
rtable attr = env->cast_to_rtable(obj);
|
95
|
-
if(!attr) return;
|
96
|
-
|
97
|
-
env->table_store(attr, env->symbol("code"), code);
|
98
|
-
}
|
99
|
-
|
100
|
-
robject Coverage::results(Env* env) {
|
101
|
-
rtable coverage_map = env->table_new();
|
102
|
-
|
103
|
-
for(CoverageMap::iterator i = coverage_map_.begin();
|
104
|
-
i != coverage_map_.end();
|
105
|
-
i++) {
|
106
|
-
InstructionCoverageMap* map = i->second;
|
107
|
-
rtable method_map = env->table_new();
|
108
|
-
|
109
|
-
for(InstructionCoverageMap::iterator j = map->begin();
|
110
|
-
j != map->end();
|
111
|
-
j++) {
|
112
|
-
env->table_store(method_map,
|
113
|
-
env->integer_new(j->first),
|
114
|
-
env->integer_new(j->second));
|
115
|
-
}
|
116
|
-
|
117
|
-
rtable attr = env->table_new();
|
118
|
-
env->table_store(attr, env->symbol("counts"), method_map);
|
119
|
-
|
120
|
-
env->table_store(coverage_map, env->integer_new(i->first), attr);
|
121
|
-
}
|
122
|
-
|
123
|
-
env->find_all_compiled_code(ccode_iterator, (void*)coverage_map);
|
124
|
-
|
125
|
-
return coverage_map;
|
126
|
-
}
|
127
|
-
|
128
|
-
namespace {
|
129
|
-
void coverage_enable(Env* env) {
|
130
|
-
Coverage* coverage = new Coverage;
|
131
|
-
env->set_global_tool_data(coverage);
|
132
|
-
}
|
133
|
-
|
134
|
-
robject coverage_results(Env* env) {
|
135
|
-
Coverage* coverage = reinterpret_cast<Coverage*>(env->global_tool_data());
|
136
|
-
|
137
|
-
env->set_tool_at_ip(NULL);
|
138
|
-
env->set_global_tool_data(NULL);
|
139
|
-
|
140
|
-
robject results = coverage->results(env);
|
141
|
-
delete coverage;
|
142
|
-
|
143
|
-
return results;
|
144
|
-
}
|
145
|
-
|
146
|
-
void coverage_at_ip(Env* env, rmachine_code mcode, int ip) {
|
147
|
-
Coverage* coverage = reinterpret_cast<Coverage*>(env->global_tool_data());
|
148
|
-
|
149
|
-
if(!coverage) return;
|
150
|
-
|
151
|
-
coverage->add(env, env->machine_code_id(mcode), ip);
|
152
|
-
}
|
153
|
-
}
|
154
|
-
|
155
|
-
extern "C" int Tool_Init(Env* env) {
|
156
|
-
env->set_tool_enable(coverage_enable);
|
157
|
-
env->set_tool_results(coverage_results);
|
158
|
-
|
159
|
-
env->set_tool_at_ip(coverage_at_ip);
|
160
|
-
|
161
|
-
return 1;
|
162
|
-
}
|
163
|
-
}
|