From: merch-redmine@... Date: 2020-06-18T17:29:03+00:00 Subject: [ruby-core:98874] [Ruby master Bug#16967] Branch coverage duplicates branches inside ensure Issue #16967 has been updated by jeremyevans0 (Jeremy Evans). Here is a lightly tested workaround that merges the coverage: ```ruby Coverage.singleton_class.prepend(Module.new do def result res = super check_branch = true skip_2nd = lambda do |ary| ary = ary.dup ary.slice!(1) ary end res.values.each do |hash| if check_branch unless hash.is_a?(Hash) && hash[:branches] return res end check_branch = false end unique_branches = {} branch_counters = {} new_branches = {} branches = hash[:branches] branches.each do |k, v| new_k = skip_2nd[k] if branch_values = unique_branches[new_k] v.each do |k1, v1| branch_counters[skip_2nd[k1]] += v1 end branch_values.keys.each do |k1| branch_values[k1] = branch_counters[skip_2nd[k1]] end else unique_branches[new_k] = new_branches[k] = v v.each do |k1, v1| branch_counters[skip_2nd[k1]] = v1 end end end hash[:branches] = new_branches end res end end) end ``` We'd probably want a more efficient version in core. Also, I'm assuming the approach is sound, but I'm not sure about that. It ignores the 2nd array element when merging, which I think is the unique id, relying solely on the line/column information to determine which branches are equivalent. ---------------------------------------- Bug #16967: Branch coverage duplicates branches inside ensure https://bugs.ruby-lang.org/issues/16967#change-86246 * Author: jeremyevans0 (Jeremy Evans) * Status: Open * Priority: Normal * ruby -v: ruby 2.8.0dev (2020-06-05T21:26:28Z master ca15b7b8ee) [x86_64-openbsd6.7] * Backport: 2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: UNKNOWN ---------------------------------------- The following file, which should have perfect coverage, is reported as only have 50% of the branches covered: ```ruby def a yield ensure p 1 p 2 if $! end a{} a{raise} rescue nil ``` The branches reported by branch coverage are: ```ruby {[:if, 0, 5, 2, 5, 11]=> {[:then, 1, 5, 2, 5, 5]=>1, [:else, 2, 5, 2, 5, 11]=>0}, [:if, 3, 5, 2, 5, 11]=> {[:then, 4, 5, 2, 5, 5]=>0, [:else, 5, 5, 2, 5, 11]=>1}} ``` Instead of reporting 2 branches for the `if`, 4 branches are reported: 1. exception raised, if condition true 2. exception raised, if condition false 3. exception not raised, if condition true 4. exception not raised, if condition false In this example, it is impossible to cover branches 2 and 3, because the if condition is only true if an exception is raised. Note that ensure blocks by themselves are not considered branches. This code results in no branches reported by branch coverage: ```ruby def a yield ensure p $! end a{} a{raise} rescue nil ``` Nested ensure usage duplicates all branches. This code with 3 nested ensures generates 16 branches: ```ruby def a yield ensure begin ensure begin ensure p 1 p 2 if $! end end end a{} a{raise} rescue nil ``` I think this is a bug in the coverage library, and that it should not duplicate branches inside ensure. This issue is not theoretical, it affects branch coverage testing in my libraries. -- https://bugs.ruby-lang.org/ Unsubscribe: