mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00

This fixes a failed assertion reported to SimpleCov https://github.com/simplecov-ruby/simplecov/issues/1113 This can be repro'd as follows: 1. Create a file `test.rb` containing the following code ``` @foo&.(@bar) ``` 2. require it with branch coverage enabled ``` ruby -rcoverage -e "Coverage.start(branches: true); require_relative 'test.rb'" ``` The assertion is failing because the Prism compiler is incorrectly detecting the start and end cursor position of the call site for the implicit call .() This patch replicates the parse.y behaviour of setting the default end_cursor to be the final closing location of the call node. This behaviour can be verified against `parse.y` by modifying the test command as follows: ``` ruby --parser=parse.y -rcoverage -e "Coverage.start(branches: true); require_relative 'test.rb'" ``` [Bug #20866]
1027 lines
25 KiB
Ruby
1027 lines
25 KiB
Ruby
# frozen_string_literal: false
|
|
require "test/unit"
|
|
require "coverage"
|
|
require "tmpdir"
|
|
require "envutil"
|
|
|
|
class TestCoverage < Test::Unit::TestCase
|
|
# The command-line arguments that we will pass to the ruby subprocess invoked
|
|
# by assert_in_out_err. In general this is just requiring the coverage
|
|
# library, but if prism is enabled we want to additionally pass that option
|
|
# through.
|
|
ARGV = ["-rcoverage"]
|
|
|
|
if RUBY_ENGINE == "ruby" && RubyVM::InstructionSequence.compile('').to_a[4][:parser] == :prism
|
|
ARGV << "-W:no-experimental"
|
|
ARGV << "--parser=prism"
|
|
end
|
|
|
|
def test_result_without_start
|
|
assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not enabled/)
|
|
Coverage.result
|
|
p :NG
|
|
end;
|
|
end
|
|
|
|
def test_peek_result_without_start
|
|
assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not enabled/)
|
|
Coverage.peek_result
|
|
p :NG
|
|
end;
|
|
end
|
|
|
|
def test_result_with_nothing
|
|
assert_in_out_err(ARGV, <<-"end;", ["{}"], [])
|
|
Coverage.start
|
|
p Coverage.result
|
|
end;
|
|
end
|
|
|
|
def test_coverage_in_main_script
|
|
autostart_path = File.expand_path("autostart.rb", __dir__)
|
|
main_path = File.expand_path("main.rb", __dir__)
|
|
|
|
assert_in_out_err(['-r', autostart_path, main_path], "", ["1"], [])
|
|
end
|
|
|
|
def test_coverage_running?
|
|
assert_in_out_err(ARGV, <<-"end;", ["false", "true", "true", "false"], [])
|
|
p Coverage.running?
|
|
Coverage.start
|
|
p Coverage.running?
|
|
Coverage.peek_result
|
|
p Coverage.running?
|
|
Coverage.result
|
|
p Coverage.running?
|
|
end;
|
|
end
|
|
|
|
def test_coverage_snapshot
|
|
Dir.mktmpdir {|tmp|
|
|
Dir.chdir(tmp) {
|
|
File.open("test.rb", "w") do |f|
|
|
f.puts <<-EOS
|
|
def coverage_test_snapshot
|
|
:ok
|
|
end
|
|
EOS
|
|
end
|
|
|
|
assert_in_out_err(ARGV, <<-"end;", ["[1, 0, nil]", "[1, 1, nil]", "[1, 1, nil]"], [])
|
|
Coverage.start
|
|
tmp = Dir.pwd
|
|
require tmp + "/test.rb"
|
|
cov = Coverage.peek_result[tmp + "/test.rb"]
|
|
coverage_test_snapshot
|
|
cov2 = Coverage.peek_result[tmp + "/test.rb"]
|
|
p cov
|
|
p cov2
|
|
p Coverage.result[tmp + "/test.rb"]
|
|
end;
|
|
}
|
|
}
|
|
end
|
|
|
|
def test_restarting_coverage
|
|
Dir.mktmpdir {|tmp|
|
|
Dir.chdir(tmp) {
|
|
tmp = Dir.pwd
|
|
File.open("test.rb", "w") do |f|
|
|
f.puts <<-EOS
|
|
def coverage_test_restarting
|
|
:ok
|
|
end
|
|
EOS
|
|
end
|
|
|
|
File.open("test2.rb", "w") do |f|
|
|
f.puts <<-EOS
|
|
itself
|
|
EOS
|
|
end
|
|
|
|
exp1 = { "#{tmp}/test.rb" => [1, 0, nil] }.inspect
|
|
exp2 = {}.inspect
|
|
exp3 = { "#{tmp}/test2.rb" => [1] }.inspect
|
|
assert_in_out_err(ARGV, <<-"end;", [exp1, exp2, exp3], [])
|
|
Coverage.start
|
|
tmp = Dir.pwd
|
|
require tmp + "/test.rb"
|
|
p Coverage.result
|
|
|
|
# Restart coverage but '/test.rb' is required before restart,
|
|
# so coverage is not recorded.
|
|
Coverage.start
|
|
coverage_test_restarting
|
|
p Coverage.result
|
|
|
|
# Restart coverage and '/test2.rb' is required after restart,
|
|
# so coverage is recorded.
|
|
Coverage.start
|
|
require tmp + "/test2.rb"
|
|
p Coverage.result
|
|
end;
|
|
}
|
|
}
|
|
end
|
|
|
|
def test_big_code
|
|
Dir.mktmpdir {|tmp|
|
|
Dir.chdir(tmp) {
|
|
File.open("test.rb", "w") do |f|
|
|
f.puts "__id__\n" * 10000
|
|
f.puts "def ignore(x); end"
|
|
f.puts "ignore([1"
|
|
f.puts "])"
|
|
end
|
|
|
|
assert_in_out_err(ARGV, <<-"end;", ["10003"], [])
|
|
Coverage.start
|
|
tmp = Dir.pwd
|
|
require tmp + '/test.rb'
|
|
p Coverage.result[tmp + '/test.rb'].size
|
|
end;
|
|
}
|
|
}
|
|
end
|
|
|
|
def test_eval
|
|
bug13305 = '[ruby-core:80079] [Bug #13305]'
|
|
|
|
Dir.mktmpdir {|tmp|
|
|
Dir.chdir(tmp) {
|
|
File.open("test.rb", "w") do |f|
|
|
f.puts 'REPEATS = 400'
|
|
f.puts 'def add_method(target)'
|
|
f.puts ' REPEATS.times do'
|
|
f.puts ' target.class_eval(<<~RUBY)'
|
|
f.puts ' def foo'
|
|
f.puts ' #{"\n" * rand(REPEATS)}'
|
|
f.puts ' end'
|
|
f.puts ' 1'
|
|
f.puts ' RUBY'
|
|
f.puts ' end'
|
|
f.puts 'end'
|
|
end
|
|
|
|
assert_in_out_err(["-W0", *ARGV], <<-"end;", ["[1, 1, 1, 400, nil, nil, nil, nil, nil, nil, nil]"], [], bug13305)
|
|
Coverage.start(:all)
|
|
tmp = Dir.pwd
|
|
require tmp + '/test.rb'
|
|
add_method(Class.new)
|
|
p Coverage.result[tmp + "/test.rb"][:lines]
|
|
end;
|
|
}
|
|
}
|
|
end
|
|
|
|
def test_eval_coverage
|
|
assert_in_out_err(ARGV, <<-"end;", ["[1, 1, 1, nil, 0, nil]"], [])
|
|
Coverage.start(eval: true, lines: true)
|
|
|
|
eval(<<-RUBY, TOPLEVEL_BINDING, "test.rb")
|
|
_out = String.new
|
|
if _out.empty?
|
|
_out << 'Hello World'
|
|
else
|
|
_out << 'Goodbye World'
|
|
end
|
|
RUBY
|
|
|
|
p Coverage.result["test.rb"][:lines]
|
|
end;
|
|
end
|
|
|
|
def test_coverage_supported
|
|
assert Coverage.supported?(:lines)
|
|
assert Coverage.supported?(:oneshot_lines)
|
|
assert Coverage.supported?(:branches)
|
|
assert Coverage.supported?(:methods)
|
|
assert Coverage.supported?(:eval)
|
|
refute Coverage.supported?(:all)
|
|
end
|
|
|
|
def test_nocoverage_optimized_line
|
|
assert_ruby_status(%w[], "#{<<-"begin;"}\n#{<<-'end;'}")
|
|
begin;
|
|
def foo(x)
|
|
x # optimized away
|
|
nil
|
|
end
|
|
end;
|
|
end
|
|
|
|
def test_coverage_optimized_branch
|
|
result = {
|
|
:branches => {
|
|
[:"&.", 0, 1, 0, 1, 8] => {
|
|
[:then, 1, 1, 0, 1, 8] => 0,
|
|
[:else, 2, 1, 0, 1, 8] => 1,
|
|
},
|
|
},
|
|
}
|
|
assert_coverage(<<~"end;", { branches: true }, result) # Bug #15476
|
|
nil&.foo
|
|
end;
|
|
end
|
|
|
|
def test_coverage_ensure_if_return
|
|
result = {
|
|
:branches => {
|
|
[:if, 0, 3, 2, 6, 5] => {
|
|
[:then, 1, 3, 7, 3, 7] => 0,
|
|
[:else, 2, 5, 4, 5, 10] => 1,
|
|
},
|
|
},
|
|
}
|
|
assert_coverage(<<~"end;", { branches: true }, result)
|
|
def flush
|
|
ensure
|
|
if $!
|
|
else
|
|
return
|
|
end
|
|
end
|
|
flush
|
|
end;
|
|
end
|
|
|
|
def assert_coverage(code, opt, stdout)
|
|
stdout = [stdout] unless stdout.is_a?(Array)
|
|
stdout = stdout.map {|s| s.to_s }
|
|
Dir.mktmpdir {|tmp|
|
|
Dir.chdir(tmp) {
|
|
File.write("test.rb", code)
|
|
|
|
assert_in_out_err(["-W0", *ARGV], <<-"end;", stdout, [])
|
|
Coverage.start(#{ opt })
|
|
tmp = Dir.pwd
|
|
require 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;
|
|
}
|
|
}
|
|
end
|
|
|
|
def test_line_coverage_for_multiple_lines
|
|
result = {
|
|
:lines => [nil, 1, nil, nil, nil, 1, nil, nil, nil, 1, nil, 1, nil, nil, nil, nil, 1, 1, nil, 1, nil, nil, nil, nil, 1]
|
|
}
|
|
assert_coverage(<<~"end;", { lines: true }, result) # Bug #14191
|
|
FOO = [
|
|
{ foo: 'bar' },
|
|
{ bar: 'baz' }
|
|
]
|
|
|
|
'some string'.split
|
|
.map(&:length)
|
|
|
|
some =
|
|
'value'
|
|
|
|
Struct.new(
|
|
:foo,
|
|
:bar
|
|
).new
|
|
|
|
class Test
|
|
def foo(bar)
|
|
{
|
|
foo: bar
|
|
}
|
|
end
|
|
end
|
|
|
|
Test.new.foo(Object.new)
|
|
end;
|
|
end
|
|
|
|
def test_branch_coverage_for_if_statement
|
|
result = {
|
|
:branches => {
|
|
[:if , 0, 2, 2, 6, 5] => {[:then, 1, 3, 4, 3, 5]=>2, [:else, 2, 5, 4, 5, 5]=>1},
|
|
[:unless, 3, 8, 2, 12, 5] => {[:else, 4, 11, 4, 11, 5]=>2, [:then, 5, 9, 4, 9, 5]=>1},
|
|
[:if , 6, 14, 2, 16, 5] => {[:then, 7, 15, 4, 15, 5]=>2, [:else, 8, 14, 2, 16, 5]=>1},
|
|
[:unless, 9, 18, 2, 20, 5] => {[:else, 10, 18, 2, 20, 5]=>2, [:then, 11, 19, 4, 19, 5]=>1},
|
|
[:if , 12, 22, 2, 22, 13] => {[:then, 13, 22, 2, 22, 3]=>2, [:else, 14, 22, 2, 22, 13]=>1},
|
|
[:unless, 15, 23, 2, 23, 17] => {[:else, 16, 23, 2, 23, 17]=>2, [:then, 17, 23, 2, 23, 3]=>1},
|
|
[:if , 18, 25, 2, 25, 16] => {[:then, 19, 25, 11, 25, 12]=>2, [:else, 20, 25, 15, 25, 16]=>1},
|
|
}
|
|
}
|
|
assert_coverage(<<~"end;", { branches: true }, result)
|
|
def foo(x)
|
|
if x == 0
|
|
0
|
|
else
|
|
1
|
|
end
|
|
|
|
unless x == 0
|
|
0
|
|
else
|
|
1
|
|
end
|
|
|
|
if x == 0
|
|
0
|
|
end
|
|
|
|
unless x == 0
|
|
0
|
|
end
|
|
|
|
0 if x == 0
|
|
0 unless x == 0
|
|
|
|
x == 0 ? 0 : 1
|
|
end
|
|
|
|
foo(0)
|
|
foo(0)
|
|
foo(1)
|
|
end;
|
|
end
|
|
|
|
def test_branch_coverage_for_while_statement
|
|
result = {
|
|
:branches => {
|
|
[:while, 0, 2, 0, 4, 3] => {[:body, 1, 3, 2, 3, 8]=> 3},
|
|
[:until, 2, 5, 0, 7, 3] => {[:body, 3, 6, 2, 6, 8]=>10},
|
|
[:while, 4, 10, 0, 10, 18] => {[:body, 5, 10, 0, 10, 6]=> 3},
|
|
[:until, 6, 11, 0, 11, 20] => {[:body, 7, 11, 0, 11, 6]=>10},
|
|
}
|
|
}
|
|
assert_coverage(<<~"end;", { branches: true }, result)
|
|
x = 3
|
|
while x > 0
|
|
x -= 1
|
|
end
|
|
until x == 10
|
|
x += 1
|
|
end
|
|
|
|
y = 3
|
|
y -= 1 while y > 0
|
|
y += 1 until y == 10
|
|
end;
|
|
end
|
|
|
|
def test_branch_coverage_for_case_statement
|
|
result = {
|
|
:branches => {
|
|
[:case, 0, 2, 2, 7, 5] => {[:when, 1, 4, 4, 4, 5]=>2, [:when, 2, 6, 4, 6, 5]=>0, [:else, 3, 2, 2, 7, 5]=>1},
|
|
[:case, 4, 9, 2, 14, 5] => {[:when, 5, 11, 4, 11, 5]=>2, [:when, 6, 13, 4, 13, 5]=>0, [:else, 7, 9, 2, 14, 5]=>1},
|
|
[:case, 8, 16, 2, 23, 5] => {[:when, 9, 18, 4, 18, 5]=>2, [:when, 10, 20, 4, 20, 5]=>0, [:else, 11, 22, 4, 22, 10]=>1},
|
|
[:case, 12, 25, 2, 32, 5] => {[:when, 13, 27, 4, 27, 5]=>2, [:when, 14, 29, 4, 29, 5]=>0, [:else, 15, 31, 4, 31, 10]=>1},
|
|
}
|
|
}
|
|
assert_coverage(<<~"end;", { branches: true }, result)
|
|
def foo(x)
|
|
case x
|
|
when 0
|
|
0
|
|
when 1
|
|
1
|
|
end
|
|
|
|
case
|
|
when x == 0
|
|
0
|
|
when x == 1
|
|
1
|
|
end
|
|
|
|
case x
|
|
when 0
|
|
0
|
|
when 1
|
|
1
|
|
else
|
|
:other
|
|
end
|
|
|
|
case
|
|
when x == 0
|
|
0
|
|
when x == 1
|
|
1
|
|
else
|
|
:other
|
|
end
|
|
end
|
|
|
|
foo(0)
|
|
foo(0)
|
|
foo(2)
|
|
end;
|
|
end
|
|
|
|
def test_branch_coverage_for_pattern_matching
|
|
result = {
|
|
:branches=> {
|
|
[:case, 0, 3, 4, 8, 7] => {[:in, 1, 5, 6, 5, 7]=>2, [:in, 2, 7, 6, 7, 7]=>0, [:else, 3, 3, 4, 8, 7]=>1},
|
|
[:case, 4, 12, 2, 17, 5] => {[:in, 5, 14, 4, 14, 5]=>2, [:else, 6, 16, 4, 16, 5]=>1}},
|
|
}
|
|
assert_coverage(<<~"end;", { branches: true }, result)
|
|
def foo(x)
|
|
begin
|
|
case x
|
|
in 0
|
|
0
|
|
in 1
|
|
1
|
|
end
|
|
rescue NoMatchingPatternError
|
|
end
|
|
|
|
case x
|
|
in 0
|
|
0
|
|
else
|
|
1
|
|
end
|
|
end
|
|
|
|
foo(0)
|
|
foo(0)
|
|
foo(2)
|
|
end;
|
|
end
|
|
|
|
def test_branch_coverage_for_safe_method_invocation
|
|
result = {
|
|
:branches=>{
|
|
[:"&.", 0, 6, 0, 6, 6] => {[:then, 1, 6, 0, 6, 6]=>1, [:else, 2, 6, 0, 6, 6]=>0},
|
|
[:"&.", 3, 7, 0, 7, 6] => {[:then, 4, 7, 0, 7, 6]=>0, [:else, 5, 7, 0, 7, 6]=>1},
|
|
[:"&.", 6, 8, 0, 8, 10] => {[:then, 7, 8, 0, 8, 10]=>1, [:else, 8, 8, 0, 8, 10]=>0},
|
|
[:"&.", 9, 9, 0, 9, 10] => {[:then, 10, 9, 0, 9, 10]=>0, [:else, 11, 9, 0, 9, 10]=>1},
|
|
[:"&.", 12, 10, 0, 10, 6] => {[:then, 13, 10, 0, 10, 6] => 0, [:else, 14, 10, 0, 10, 6] => 1},
|
|
[:"&.", 15, 11, 0, 11, 5] => {[:then, 16, 11, 0, 11, 5] => 0, [:else, 17, 11, 0, 11, 5] => 1},
|
|
}
|
|
}
|
|
assert_coverage(<<~"end;", { branches: true }, result)
|
|
class Dummy; def foo; end; def foo=(x); end; end
|
|
a = Dummy.new
|
|
b = nil
|
|
c = Dummy.new
|
|
d = nil
|
|
a&.foo
|
|
b&.foo
|
|
c&.foo = 1
|
|
d&.foo = 1
|
|
d&.(b)
|
|
d&.()
|
|
end;
|
|
end
|
|
|
|
def test_method_coverage
|
|
result = {
|
|
:methods => {
|
|
[Object, :bar, 2, 0, 3, 3] => 1,
|
|
[Object, :baz, 4, 1, 4, 13] => 0,
|
|
[Object, :foo, 1, 0, 1, 12] => 2,
|
|
}
|
|
}
|
|
assert_coverage(<<~"end;", { methods: true }, result)
|
|
def foo; end
|
|
def bar
|
|
end
|
|
def baz; end
|
|
|
|
foo
|
|
foo
|
|
bar
|
|
end;
|
|
end
|
|
|
|
def test_method_coverage_for_define_method
|
|
result = {
|
|
:methods => {
|
|
[Object, :a, 6, 18, 6, 25] => 2,
|
|
[Object, :b, 7, 18, 8, 3] => 0,
|
|
[Object, :bar, 2, 20, 3, 1] => 1,
|
|
[Object, :baz, 4, 9, 4, 11] => 0,
|
|
[Object, :foo, 1, 20, 1, 22] => 2,
|
|
}
|
|
}
|
|
assert_coverage(<<~"end;", { methods: true }, result)
|
|
define_method(:foo) {}
|
|
define_method(:bar) {
|
|
}
|
|
f = proc {}
|
|
define_method(:baz, &f)
|
|
define_method(:a) do; end
|
|
define_method(:b) do
|
|
end
|
|
|
|
foo
|
|
foo
|
|
bar
|
|
a
|
|
a
|
|
end;
|
|
end
|
|
|
|
class DummyConstant < String
|
|
def inspect
|
|
self
|
|
end
|
|
end
|
|
|
|
def test_method_coverage_for_alias
|
|
_C = DummyConstant.new("C")
|
|
_M = DummyConstant.new("M")
|
|
code = <<~"end;"
|
|
module M
|
|
def foo
|
|
end
|
|
alias bar foo
|
|
end
|
|
class C
|
|
include M
|
|
def baz
|
|
end
|
|
alias qux baz
|
|
end
|
|
end;
|
|
|
|
result = {
|
|
:methods => {
|
|
[_C, :baz, 8, 2, 9, 5] => 0,
|
|
[_M, :foo, 2, 2, 3, 5] => 0,
|
|
}
|
|
}
|
|
assert_coverage(code, { methods: true }, result)
|
|
|
|
result = {
|
|
:methods => {
|
|
[_C, :baz, 8, 2, 9, 5] => 12,
|
|
[_M, :foo, 2, 2, 3, 5] => 3,
|
|
}
|
|
}
|
|
assert_coverage(code + <<~"end;", { methods: true }, result)
|
|
obj = C.new
|
|
1.times { obj.foo }
|
|
2.times { obj.bar }
|
|
4.times { obj.baz }
|
|
8.times { obj.qux }
|
|
end;
|
|
end
|
|
|
|
def test_method_coverage_for_singleton_class
|
|
_singleton_Foo = DummyConstant.new("#<Class:Foo>")
|
|
_Foo = DummyConstant.new("Foo")
|
|
code = <<~"end;"
|
|
class Foo
|
|
def foo
|
|
end
|
|
alias bar foo
|
|
def self.baz
|
|
end
|
|
class << self
|
|
alias qux baz
|
|
end
|
|
end
|
|
|
|
1.times { Foo.new.foo }
|
|
2.times { Foo.new.bar }
|
|
4.times { Foo.baz }
|
|
8.times { Foo.qux }
|
|
end;
|
|
|
|
result = {
|
|
:methods => {
|
|
[_singleton_Foo, :baz, 5, 2, 6, 5] => 12,
|
|
[_Foo, :foo, 2, 2, 3, 5] => 3,
|
|
}
|
|
}
|
|
assert_coverage(code, { methods: true }, result)
|
|
end
|
|
|
|
def test_oneshot_line_coverage
|
|
result = {
|
|
:oneshot_lines => [2, 6, 10, 12, 17, 18, 25, 20]
|
|
}
|
|
assert_coverage(<<~"end;", { oneshot_lines: true }, result)
|
|
FOO = [
|
|
{ foo: 'bar' }, # 2
|
|
{ bar: 'baz' }
|
|
]
|
|
|
|
'some string'.split # 6
|
|
.map(&:length)
|
|
|
|
some =
|
|
'value' # 10
|
|
|
|
Struct.new( # 12
|
|
:foo,
|
|
:bar
|
|
).new
|
|
|
|
class Test # 17
|
|
def foo(bar) # 18
|
|
{
|
|
foo: bar # 20
|
|
}
|
|
end
|
|
end
|
|
|
|
Test.new.foo(Object.new) # 25
|
|
end;
|
|
end
|
|
|
|
def test_clear_with_lines
|
|
Dir.mktmpdir {|tmp|
|
|
Dir.chdir(tmp) {
|
|
File.open("test.rb", "w") do |f|
|
|
f.puts "def foo(x)"
|
|
f.puts " if x > 0"
|
|
f.puts " :pos"
|
|
f.puts " else"
|
|
f.puts " :non_pos"
|
|
f.puts " end"
|
|
f.puts "end"
|
|
end
|
|
|
|
exp = [
|
|
{ lines: [1, 0, 0, nil, 0, nil, nil] }.inspect,
|
|
{ lines: [0, 1, 1, nil, 0, nil, nil] }.inspect,
|
|
{ lines: [0, 1, 0, nil, 1, nil, nil] }.inspect,
|
|
]
|
|
assert_in_out_err(ARGV, <<-"end;", exp, [])
|
|
Coverage.start(lines: true)
|
|
tmp = Dir.pwd
|
|
f = tmp + "/test.rb"
|
|
require f
|
|
p Coverage.result(stop: false, clear: true)[f]
|
|
foo(1)
|
|
p Coverage.result(stop: false, clear: true)[f]
|
|
foo(-1)
|
|
p Coverage.result[f]
|
|
end;
|
|
}
|
|
}
|
|
end
|
|
|
|
def test_clear_with_branches
|
|
Dir.mktmpdir {|tmp|
|
|
Dir.chdir(tmp) {
|
|
File.open("test.rb", "w") do |f|
|
|
f.puts "def foo(x)"
|
|
f.puts " if x > 0"
|
|
f.puts " :pos"
|
|
f.puts " else"
|
|
f.puts " :non_pos"
|
|
f.puts " end"
|
|
f.puts "end"
|
|
end
|
|
|
|
exp = [
|
|
{ branches: { [:if, 0, 2, 2, 6, 5] => { [:then, 1, 3, 4, 3, 8] => 0, [:else, 2, 5, 4, 5, 12] => 0 } } }.inspect,
|
|
{ branches: { [:if, 0, 2, 2, 6, 5] => { [:then, 1, 3, 4, 3, 8] => 1, [:else, 2, 5, 4, 5, 12] => 0 } } }.inspect,
|
|
{ branches: { [:if, 0, 2, 2, 6, 5] => { [:then, 1, 3, 4, 3, 8] => 0, [:else, 2, 5, 4, 5, 12] => 1 } } }.inspect,
|
|
{ branches: { [:if, 0, 2, 2, 6, 5] => { [:then, 1, 3, 4, 3, 8] => 0, [:else, 2, 5, 4, 5, 12] => 1 } } }.inspect,
|
|
]
|
|
assert_in_out_err(ARGV, <<-"end;", exp, [])
|
|
Coverage.start(branches: true)
|
|
tmp = Dir.pwd
|
|
f = tmp + "/test.rb"
|
|
require f
|
|
p Coverage.result(stop: false, clear: true)[f]
|
|
foo(1)
|
|
p Coverage.result(stop: false, clear: true)[f]
|
|
foo(-1)
|
|
p Coverage.result(stop: false, clear: true)[f]
|
|
foo(-1)
|
|
p Coverage.result(stop: false, clear: true)[f]
|
|
end;
|
|
}
|
|
}
|
|
end
|
|
|
|
def test_clear_with_methods
|
|
Dir.mktmpdir {|tmp|
|
|
Dir.chdir(tmp) {
|
|
File.open("test.rb", "w") do |f|
|
|
f.puts "def foo(x)"
|
|
f.puts " if x > 0"
|
|
f.puts " :pos"
|
|
f.puts " else"
|
|
f.puts " :non_pos"
|
|
f.puts " end"
|
|
f.puts "end"
|
|
end
|
|
|
|
exp = [
|
|
{ methods: { [Object, :foo, 1, 0, 7, 3] => 0 } }.inspect,
|
|
{ methods: { [Object, :foo, 1, 0, 7, 3] => 1 } }.inspect,
|
|
{ methods: { [Object, :foo, 1, 0, 7, 3] => 1 } }.inspect,
|
|
{ methods: { [Object, :foo, 1, 0, 7, 3] => 1 } }.inspect
|
|
]
|
|
assert_in_out_err(ARGV, <<-"end;", exp, [])
|
|
Coverage.start(methods: true)
|
|
tmp = Dir.pwd
|
|
f = tmp + "/test.rb"
|
|
require f
|
|
p Coverage.result(stop: false, clear: true)[f]
|
|
foo(1)
|
|
p Coverage.result(stop: false, clear: true)[f]
|
|
foo(-1)
|
|
p Coverage.result(stop: false, clear: true)[f]
|
|
foo(-1)
|
|
p Coverage.result(stop: false, clear: true)[f]
|
|
end;
|
|
}
|
|
}
|
|
end
|
|
|
|
def test_clear_with_oneshot_lines
|
|
Dir.mktmpdir {|tmp|
|
|
Dir.chdir(tmp) {
|
|
File.open("test.rb", "w") do |f|
|
|
f.puts "def foo(x)"
|
|
f.puts " if x > 0"
|
|
f.puts " :pos"
|
|
f.puts " else"
|
|
f.puts " :non_pos"
|
|
f.puts " end"
|
|
f.puts "end"
|
|
end
|
|
|
|
exp = [
|
|
{ oneshot_lines: [1] }.inspect,
|
|
{ oneshot_lines: [2, 3] }.inspect,
|
|
{ oneshot_lines: [5] }.inspect,
|
|
{ oneshot_lines: [] }.inspect,
|
|
]
|
|
assert_in_out_err(ARGV, <<-"end;", exp, [])
|
|
Coverage.start(oneshot_lines: true)
|
|
tmp = Dir.pwd
|
|
f = tmp + "/test.rb"
|
|
require f
|
|
p Coverage.result(stop: false, clear: true)[f]
|
|
foo(1)
|
|
p Coverage.result(stop: false, clear: true)[f]
|
|
foo(-1)
|
|
p Coverage.result(stop: false, clear: true)[f]
|
|
foo(-1)
|
|
p Coverage.result(stop: false, clear: true)[f]
|
|
end;
|
|
}
|
|
}
|
|
end
|
|
|
|
def test_line_stub
|
|
Dir.mktmpdir {|tmp|
|
|
Dir.chdir(tmp) {
|
|
File.open("test.rb", "w") do |f|
|
|
f.puts "def foo(x)"
|
|
f.puts " if x > 0"
|
|
f.puts " :pos"
|
|
f.puts " else"
|
|
f.puts " :non_pos"
|
|
f.puts " end"
|
|
f.puts "end"
|
|
end
|
|
|
|
assert_equal([0, 0, 0, nil, 0, nil, nil], Coverage.line_stub("test.rb"))
|
|
}
|
|
}
|
|
end
|
|
|
|
def test_stop_wrong_peephole_optimization
|
|
result = {
|
|
:lines => [1, 1, 1, nil]
|
|
}
|
|
assert_coverage(<<~"end;", { lines: true }, result)
|
|
raise if 1 == 2
|
|
while true
|
|
break
|
|
end
|
|
end;
|
|
end
|
|
|
|
def test_branch_coverage_in_ensure_clause
|
|
result = {
|
|
:branches => {
|
|
[:if, 0, 4, 2, 4, 11] => {
|
|
[:then, 1, 4, 2, 4, 5] => 1,
|
|
[:else, 2, 4, 2, 4, 11] => 1,
|
|
}
|
|
}
|
|
}
|
|
assert_coverage(<<~"end;", { branches: true }, result) # Bug #16967
|
|
def foo
|
|
yield
|
|
ensure
|
|
:ok if $!
|
|
end
|
|
foo {}
|
|
foo { raise } rescue nil
|
|
end;
|
|
end
|
|
|
|
def test_coverage_with_asan
|
|
result = { :lines => [1, 1, 0, 0, nil, nil, nil] }
|
|
|
|
assert_coverage(<<~"end;", { lines: true }, result) # Bug #18001
|
|
class Foo
|
|
def bar
|
|
baz do |x|
|
|
next unless Integer == x
|
|
end
|
|
end
|
|
end
|
|
end;
|
|
end
|
|
|
|
def test_coverage_suspendable
|
|
Dir.mktmpdir {|tmp|
|
|
Dir.chdir(tmp) {
|
|
File.open("test.rb", "w") do |f|
|
|
f.puts <<-EOS
|
|
def foo
|
|
:ok
|
|
end
|
|
|
|
def bar
|
|
:ok
|
|
end
|
|
|
|
def baz
|
|
:ok
|
|
end
|
|
EOS
|
|
end
|
|
|
|
assert_separately(%w[-rcoverage], "#{<<~"begin;"}\n#{<<~'end;'}")
|
|
begin;
|
|
cov1 = [0, 0, nil, nil, 0, 1, nil, nil, 0, 0, nil]
|
|
cov2 = [0, 0, nil, nil, 0, 1, nil, nil, 0, 1, nil]
|
|
Coverage.setup
|
|
tmp = Dir.pwd
|
|
require tmp + "/test.rb"
|
|
foo
|
|
Coverage.resume
|
|
bar
|
|
Coverage.suspend
|
|
baz
|
|
assert_equal cov1, Coverage.peek_result[tmp + "/test.rb"]
|
|
Coverage.resume
|
|
baz
|
|
assert_equal cov2, Coverage.result[tmp + "/test.rb"]
|
|
end;
|
|
|
|
assert_separately(%w[-rcoverage], "#{<<~"begin;"}\n#{<<~'end;'}")
|
|
begin;
|
|
cov1 = {
|
|
lines: [0, 0, nil, nil, 0, 1, nil, nil, 0, 0, nil],
|
|
branches: {},
|
|
methods: {
|
|
[Object, :baz, 9, 12, 11, 15]=>0,
|
|
[Object, :bar, 5, 12, 7, 15]=>1,
|
|
[Object, :foo, 1, 12, 3, 15]=>0,
|
|
}
|
|
}
|
|
|
|
cov2 = {
|
|
lines: [0, 0, nil, nil, 0, 1, nil, nil, 0, 1, nil],
|
|
branches: {},
|
|
methods: {
|
|
[Object, :baz, 9, 12, 11, 15]=>1,
|
|
[Object, :bar, 5, 12, 7, 15]=>1,
|
|
[Object, :foo, 1, 12, 3, 15]=>0,
|
|
}
|
|
}
|
|
|
|
Coverage.setup(:all)
|
|
tmp = Dir.pwd
|
|
require tmp + "/test.rb"
|
|
foo
|
|
Coverage.resume
|
|
bar
|
|
Coverage.suspend
|
|
baz
|
|
assert_equal cov1, Coverage.peek_result[tmp + "/test.rb"]
|
|
Coverage.resume
|
|
baz
|
|
assert_equal cov2, Coverage.result[tmp + "/test.rb"]
|
|
end;
|
|
|
|
assert_separately(%w[-rcoverage], "#{<<~"begin;"}\n#{<<~'end;'}")
|
|
begin;
|
|
cov1 = {:oneshot_lines=>[6]}
|
|
cov2 = {:oneshot_lines=>[6, 10]}
|
|
Coverage.setup(oneshot_lines: true)
|
|
tmp = Dir.pwd
|
|
require tmp + "/test.rb"
|
|
foo
|
|
Coverage.resume
|
|
bar
|
|
Coverage.suspend
|
|
baz
|
|
assert_equal cov1, Coverage.peek_result[tmp + "/test.rb"]
|
|
Coverage.resume
|
|
baz
|
|
assert_equal cov2, Coverage.result[tmp + "/test.rb"]
|
|
end;
|
|
}
|
|
}
|
|
end
|
|
|
|
def test_coverage_state
|
|
assert_in_out_err(ARGV, <<-"end;", [":idle", ":running", ":running", ":idle"], [])
|
|
p Coverage.state
|
|
Coverage.start
|
|
p Coverage.state
|
|
Coverage.peek_result
|
|
p Coverage.state
|
|
Coverage.result
|
|
p Coverage.state
|
|
end;
|
|
|
|
assert_in_out_err(ARGV, <<-"end;", [":idle", ":suspended", ":running", ":suspended", ":running", ":suspended", ":idle"], [])
|
|
p Coverage.state
|
|
Coverage.setup
|
|
p Coverage.state
|
|
Coverage.resume
|
|
p Coverage.state
|
|
Coverage.suspend
|
|
p Coverage.state
|
|
Coverage.resume
|
|
p Coverage.state
|
|
Coverage.suspend
|
|
p Coverage.state
|
|
Coverage.result
|
|
p Coverage.state
|
|
end;
|
|
end
|
|
|
|
def test_result_without_resume
|
|
assert_in_out_err(ARGV, <<-"end;", ["{}"], [])
|
|
Coverage.setup
|
|
p Coverage.result
|
|
end;
|
|
end
|
|
|
|
def test_result_after_suspend
|
|
assert_in_out_err(ARGV, <<-"end;", ["{}"], [])
|
|
Coverage.start
|
|
Coverage.suspend
|
|
p Coverage.result
|
|
end;
|
|
end
|
|
|
|
def test_resume_without_setup
|
|
assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not set up yet/)
|
|
Coverage.resume
|
|
p :NG
|
|
end;
|
|
end
|
|
|
|
def test_suspend_without_setup
|
|
assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not running/)
|
|
Coverage.suspend
|
|
p :NG
|
|
end;
|
|
end
|
|
|
|
def test_double_resume
|
|
assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is already running/)
|
|
Coverage.start
|
|
Coverage.resume
|
|
p :NG
|
|
end;
|
|
end
|
|
|
|
def test_double_suspend
|
|
assert_in_out_err(ARGV, <<-"end;", [], /coverage measurement is not running/)
|
|
Coverage.setup
|
|
Coverage.suspend
|
|
p :NG
|
|
end;
|
|
end
|
|
|
|
def test_tag_break_with_branch_coverage
|
|
result = {
|
|
:branches => {
|
|
[:"&.", 0, 1, 0, 1, 6] => {
|
|
[:then, 1, 1, 0, 1, 6] => 1,
|
|
[:else, 2, 1, 0, 1, 6] => 0,
|
|
},
|
|
},
|
|
}
|
|
assert_coverage(<<~"end;", { branches: true }, result)
|
|
1&.tap do break end
|
|
end;
|
|
end
|
|
end
|