New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add method coverage and decision coverage metrics #511
Conversation
…se nodes following NODE_WHENs
…EVENT_MCOVERAGE instruction from ADD_TRACE
…uct if coverage is enabled
I'd love to see this make its way into ruby core. Is this effectively a dead proposal? |
@brianphillips , I let this one drift for a looooong time. I should add one commit just to fix merge conflicts 😛 All of the discussion has been at Ruby bug #9508. Not sure why I never linked back to that bug here... After 2.2.0-preview1 was announced, I let this go, fairly certain that it wouldn't be accepted into 2.2.0 after preview1 was already out. Now that 2.2.0 is out, I should fix the merge conflicts and poke Yusuke, who has generously dedicated a lot of time reviewing this already. We left off on a discussion about how this PR makes testing-with-
The only problem with this solution is that it makes tons of the deep C code wrapped in gross if/else statements. But it should make it faster... |
Any updates on branch/decision coverage support? I believe this is specially important in ruby, given that statements like:
are reported as 100% covered with the current approach. |
@srawlins, first of all, thanks a lot for all the effort you've put into this PR! I'd like to make a suggestion about offered syntax: we could add an optional hash instead of a symbol (or a list of them) as an argument. It would make possible to add more options to the method in future without problems with backward compatibility. For example, something like Coverage.start(only_lines: true) Regarding speed issues: personally, I think that more complete coverage report overweight tests slowing down. After all, people use coverage tools to track down bugs and false 100% report may lead to missed issues. That's why I also think splitting internal coverage modes into So, from my point of view, we could just format output differently depending on an option given to Looking forward to merging this feature into Ruby! |
Any update's on this? Coverage reports are really misleading when conditional branches are excluded from reports and makes using single-line conditionals riskier as a result, even though they're considered in some cases to be idiomatic Ruby. This leads to a case where using the idiomatic syntax style results in riskier code since you can't tell from a coverage report if you've covered all of your code paths correctly. This effectively leads to having to make a decision over whether you want your code to be easier to cover but less readable or more readable but more difficult to cover. |
I'm VERY SORRY for whopping three-year delay... I'm now working to implement branch coverage and method coverage. I plan to finish and include it in Ruby 2.5. This pull request is so inspiring, but there seem some issues. I show them below. Anyway, I really appreciate what you've done. Branch coverage of
|
This PR is obsolete with Ruby 2.5 introducing this feature. Thanks very much for the effort, though! I learned a lot by reading this PR first. |
Yeah, I'm closing this. This PR was really helpful to serve as the fundamental API design of advanced coverage measurement. Thank you very much! |
This is a working proof-of-concept for tracking more advanced code coverage metrics in the Coverage module. Namely, Method Coverage (Function Coverage), and Decision Coverage (as defined in Steve Cornett's Code Coverage Analysis paper). I'd love feedback!
Since Ruby 1.9, the format of a Coverage report looks like:
(from the rdoc) which says that, within "foo.rb":
This pull request changes the format to be more complicated. For the following ruby file:
Coverage.result would yield the following:
So the following things change about the coverage report:
Coverage.result[file][:lines]
, changed fromCoverage.result[file]
.Coverage.result[file][:methods]
. This object is a hash, with line number keys (where each method was defined), and execution count values ("calls"). This could be changed to be the same format as[:lines]
, but a Hash presumably saves space and time.Coverage.result[file][:decisions]
. This object is a Hash, with line number keys (the condition of anif
, awhen
line, etc.), and count values fortrue
decisions andfalse
decisions.7=>[2,1]
states that the condition resulted in a truthy value 2 times, and a falsey value 1 time.Decisions
A decision is the result of an
if
,elsif
,unless
,?:
,when
,while
, oruntil
condition, which are all tracked with this new feature. Since the results of the conditions are tracked, void bodies will be noted. For example:will report that a conditional statement exists at line 1, and that statement resulted in a truthy decision 1 time, and a falsey decision 0 times, which means not all decisions were tested.
More Coverage
Caveats
I was not very clear on the magic numbers used in ruby.h, and just used values for the new macros that seemed to work.
for a in b
is not tracked in Decision Coverage because that syntax does not really involve "Decisions". The sequencefor a in b; code; end
is just transformed intob.each { |a| code }
at compile time. This makes me want to add Proc call coverage to the Method Coverage metric.Updates
I completely rewrote and rebranded "Branch" coverage into "Decision" coverage after reading more of Steve Cornett's paper. The resultant metric is much more robust, tracking void
else
s, and one-line if/else combos much better.