[#85349] [Ruby trunk Bug#14334] Segmentation fault after running rspec (ruby/2.5.0/erb.rb:885 / simplecov/source_file.rb:85) — pragtob@...
Issue #14334 has been updated by PragTob (Tobias Pfeiffer).
3 messages
2018/02/02
[#85358] Re: [ruby-cvs:69220] nobu:r62039 (trunk): compile.c: unnecessary freezing — Eric Wong <normalperson@...>
nobu@ruby-lang.org wrote:
5 messages
2018/02/03
[#85612] Why require autoconf 2.67+ — leam hall <leamhall@...>
Please pardon the intrusion; I am new to Ruby and like to pull the
6 messages
2018/02/17
[#85634] [Ruby trunk Bug#14494] [PATCH] tool/m4/ruby_replace_type.m4 use AC_CHECK_TYPES for HAVE_* macros — normalperson@...
Issue #14494 has been reported by normalperson (Eric Wong).
3 messages
2018/02/19
[#85674] [Ruby trunk Feature#13618] [PATCH] auto fiber schedule for rb_wait_for_single_fd and rb_waitpid — matz@...
Issue #13618 has been updated by matz (Yukihiro Matsumoto).
5 messages
2018/02/20
[#85686] Re: [Ruby trunk Feature#13618] [PATCH] auto fiber schedule for rb_wait_for_single_fd and rb_waitpid
— Eric Wong <normalperson@...>
2018/02/20
matz@ruby-lang.org wrote:
[#85704] Re: [Ruby trunk Feature#13618] [PATCH] auto fiber schedule for rb_wait_for_single_fd and rb_waitpid
— Koichi Sasada <ko1@...>
2018/02/21
On 2018/02/20 18:06, Eric Wong wrote:
[ruby-core:85852] [Ruby trunk Feature#13901][Closed] Add branch coverage
From:
mame@...
Date:
2018-02-28 01:06:52 UTC
List:
ruby-core #85852
Issue #13901 has been updated by mame (Yusuke Endoh).
Status changed from Open to Closed
Thank you marcandre for proposing the format, and really sorry for my super-late reply.
I considered it seriously (before release of 2.5.0). I understand its advantage (easy and efficient to merge). But I thought it was difficult to implement it for method coverage. The order and the count of methods in coverage data may vary because some methods may or may not be defined according to environment due to Ruby's dynamic property (i.e., conditional define_method). In this case, merging method coverage data blindly depending upon only the index of "runs" data, may lead to broken coverage data.
To make it easy to merge coverage data, I released [`coverage-helpers` gem](https://github.com/mame/coverage-helpers) which includes:
* `Coverage::Helpers.merge(*covs)`: Sum up all coverage results.
* `Coverage::Helpers.diff(cov1, cov2)`: Extract the coverage results that is covered by cov1 but not covered by cov2.
I hope this gem helpful for users.
I'm closing this ticket since Ruby 2.5.0 has been already released, but let me know if you have any idea to make coverage.so helpful for any use case (including DeepCover).
----------------------------------------
Feature #13901: Add branch coverage
https://bugs.ruby-lang.org/issues/13901#change-70709
* Author: mame (Yusuke Endoh)
* Status: Closed
* Priority: Normal
* Assignee:
* Target version:
----------------------------------------
I plan to add "branch coverage" (and "method coverage") as new target types of coverage.so, the coverage measurement library. I'd like to introduce this feature for Ruby 2.5.0. Let me to hear your opinions.
## Basic Usage of the Coverage API
The sequence is the same as the current: (1) require "coverage.so", (2) start coverage measurement by `Coverage.start`, (3) load a program being measured (typically, a test runner program), and (4) get the result by `Coverage.result`.
When you pass to `Coverage.start` with keyword argument "`branches: true`", branch coverage measurement is enabled.
test.rb
~~~
require "coverage"
Coverage.start(lines: true, branches: true)
load "target.rb"
p Coverage.result
~~~
target.rb
~~~
1: if 1 == 0
2: p :match
3: else
4: p :not_match
5: end
~~~
By measuring coverage of target.rb, the result will be output (manually formatted):
~~~
$ ruby test.rb
:not_match
{".../target.rb" => {
:lines => [1, 0, nil, 1, nil],
:branches => {
[:if, 0, 1] => { [:then, 1, 2] => 0, [:else, 2, 4] => 1 }
}
}
~~~
`[:if, 0, 1]` reads "if branch at Line 1", and `[:then, 1, 2]` reads "then clause at Line 2". So, `[:if,0,1] => { [:then,1,2]=>0, [:else,2,4]=>0 }` reads "the branch from Line 1 to Line 2 has never executed, and the branch from Line 1 to Line 4 has executed once."
The second number (`0` of `[:if, 0, 1]`) is a unique ID to avoid conflict, just in case where multiple branches are written in one line. This format of a key is discussed in "Key format" section.
## Why needed
Traditional coverage (line coverage) misses a branch in one line. Branch coverage is useful to find such untested code. See the following example.
target.rb
~~~
p(:foo) unless 1 == 0
p(1 == 0 ? :foo : :bar)
~~~
The result is:
~~~
{".../target.rb" => {
:lines => [1, 1],
:branches => {
[:unless, 0, 1] => { [:else, 1, 1] => 0, [:then, 2, 1] => 1 },
[:if, 3, 2] => { [:then, 4, 2] => 0, [:else, 5, 2] => 1 }
}
}}
~~~
Line coverage tells coverage 100%, but branch coverage shows that the `unless` statement of the first line has never taken true and that the ternary operator has never taken true.
## Current status
I've already committed the feature in trunk as an experimental feature. To enable the feature, you need to set the environment variable `COVERAGE_EXPERIMENTAL_MODE` = `true`. I plan to activate this feature by default by Ruby 2.5 release, if there is no big problem.
## Key format
The current implementation uses `[<label>, <unique ID>, <lineno>]`, like `[:if, 0, 1]`, to represent the site of branch. `<unique ID>` is required for the case where multiple branches are in one line.
I think this format is arguable. I thought of some other candidates:
* `[<label>, <lineno>, <column-no>]`: A big problem, how should we handle TAB character?
* `[<label>, <offset from file head>]`: Looks good for machine readability.
* Are `<label>` and `<lineno>` needed? They are useful for human, but normally, this result will be processed by a visualization script (such as SimpleCov).
What do you think? I'm now thinking that `[<label>, <lineno>, <offset from file head>]` is reasonable, but it is hard for me to implement. I'll try later but parse.y is so difficult... (A patch is welcome!)
## Compatibility
This API is 100% compatible. If no keyword argument is given, the result will be old format, i.e., a hash from filename to an array that represents line coverage.
~~~
# compatiblie mode
Coverage.start
load "target.rb"
p Coverage.result
#=> {".../target.rb" => [1, 1, 1, ...] }
# If "lines: true" is given, the result format differs slightly
Coverage.start(lines: true)
load "target.rb"
p Coverage.result
#=> {".../target.rb" => { :lines => [1, 1, 1, ...] } }
~~~
## Method coverage
Method coverage is also supported. You can measure it by using `Coverage.start(methods: true)`.
target.rb
~~~
1: def foo
2: end
3: def bar
4: end
5: def baz
6: end
7:
8: foo
9: foo
10: bar
~~~
result (manually formatted)
~~~
{".../target.rb"=> {
:methods => {
[:foo, 0, 1] => 2,
[:bar, 1, 3] => 1,
[:baz, 2, 5] => 0,
}
}}
~~~
## Notes
* `if` statements whose condtion is literal, such as `if true` and `if false`, are not considered as a branch.
* `while`, `until`, and `case` are also supported. See Examples 2 and 3.
* This proposal is based on #9508. The proposal has [some spec-level issues](https://github.com/ruby/ruby/pull/511#issuecomment-328753499), but the work was really inspiring me.
## Future work
* Someone may want to know how many times an one-line block is executed, such as `n.times { }`.
* Someone may want to know how many times each method call is executed, such as `obj.foo.bar.baz` (For example, if method `foo` always raises an exception, calls to `bar` and `baz` are not executed.)
## Some examples
### Example 1
target.rb
~~~
1: if 1 == 0
2: p :match1
3: elsif 1 == 0
4: p :match2
5: else
6: p :not_match
7: end
~~~
result (manually formatted)
~~~
{"target.rb" => {
:lines => [1, 0, 1, 0, nil, 1, nil],
:branches => {
[:if, 1] => { [:then, 2] => 0, [:else, 3] => 1 },
[:if, 3] => { [:then, 4] => 0, [:else, 5] => 1 },
}
}
~~~
### Example 2
target.rb
~~~
1:case :BOO
2:when :foo then p :foo
3:when :bar then p :bar
4:when :baz then p :baz
5:else p :other
6:end
7:
8:x = 3
9:case
10:when x == 0 then p :foo
11:when x == 1 then p :bar
12:when x == 2 then p :baz
13:else p :other
14:end
~~~
result (manually formatted)
~~~
{"target.rb" => {
:lines => [1, 0, 0, 0, 1, nil, nil, 1, 1, 1, 1, 1, 1, nil],
:branches => {
[:case, 1] => {
[:when, 2] => 0,
[:when, 3] => 0,
[:when, 4] => 0,
[:else, 5] => 1
},
[:case, 8] => {
[:when, 9] => 0,
[:when, 10] => 0,
[:when, 11] => 0,
[:else, 12] => 1
}
}
}
~~~
### Example 3
target.rb
~~~
1:n = 0
2:while n < 100
3: n += 1
4:end
~~~
result (manually formatted)
~~~
{"target.rb" => {
:lines => [1, 101, 100, nil],
:branches => {
[:while, 2] => {
[:body, 3] => 100,
[:end, 5] => 1
}
}
}
~~~
--
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>