undercover reports <100% method coverage even though it appears by eye to be 100%
gc-tim opened this issue · comments
Undercover does not show any red in the output, other than the initial "loc" line, and there are no "hits: 0/" or "branches: 0/" lines. Every line that is not "hits: n/a" is green.
Output from undercover:
undercover: 👮♂️ some methods have no test coverage! Please add specs for methods listed below
🚨 1) node `fun` type: instance method,
loc: foo/applications/bar/services/baz.rb:92:168, coverage: 94.12%
92: def fun( hits: n/a
93: arg_a:, hits: n/a
94: arg_b:, hits: n/a
95: arg_c:, hits: n/a
96: arg_d:, hits: n/a
97: arg_e:, hits: n/a
98: arg_f:, hits: n/a
99: arg_g:, hits: n/a
100: arg_h:, hits: n/a
101: arg_i:, hits: n/a
102: arg_j:, hits: n/a
103: arg_k:, hits: n/a
104: arg_l: hits: n/a
105: ) hits: n/a
106: var_a = fun_a(arg_b, arg_h, arg_l) hits: 181
107: var_b = arg_e || arg_c hits: 181
108: var_c = arg_g || arg_d hits: 181
109: var_d = { hits: n/a
110: key_a: arg_b.key_a, hits: 181
111: key_b: var_b, hits: n/a
112: key_c: var_c, hits: n/a
113: }.merge(arg_i) hits: n/a
114:
115: if arg_k == :sym_a && (var_b.nil? || var_c.nil?) hits: 181 branches: 2/2
116: return Result.new(var_a.fun_b, true, var_d) hits: 2
117: end hits: n/a
118:
119: var_e = ModuleA::ClassA.new( hits: 179
120: arg_f, hits: n/a
121: var_b, hits: n/a
122: var_c, hits: n/a
123: nil, hits: n/a
124: arg_k, hits: n/a
125: ) hits: n/a
126:
127: var_f = var_a.find_unit( hits: 179
128: var_e, hits: n/a
129: key_d: arg_a, hits: n/a
130: key_e: false, hits: n/a
131: key_f: arg_j, hits: n/a
132: ) hits: n/a
133:
134: if var_f hits: 179 branches: 1/1
135: Result.new(var_f, false, var_d) hits: 125
136: else hits: n/a
137: var_f = var_a.find_unit( hits: 54
138: var_e, hits: n/a
139: key_d: arg_a, hits: n/a
140: key_e: true, hits: n/a
141: key_f: arg_j, hits: n/a
142: ) hits: n/a
143: if var_f hits: 54 branches: 1/1
144: Result.new(var_f, true, var_d) hits: 32
145: else hits: n/a
146: raise( hits: 22
147: RuntimeError.new( hits: n/a
148: { hits: n/a
149: reason: 'Something sensible', hits: n/a
150:
151: arg_k: arg_k, hits: n/a
152: arg_i: arg_i, hits: n/a
153: arg_d: arg_d, hits: n/a
154: arg_g: arg_g, hits: n/a
155: arg_f: arg_f, hits: n/a
156: key_a: arg_b&.key_a, hits: n/a
157: arg_a: arg_a, hits: n/a
158: arg_e: arg_e, hits: n/a
159: arg_c: arg_c, hits: n/a
160: arg_l: arg_l, hits: n/a
161: arg_h: arg_h, hits: n/a
162: arg_j: arg_j, hits: n/a
163: }, hits: n/a
164: ), hits: n/a
165: ) hits: n/a
166: end hits: n/a
167: end hits: n/a
168: end hits: n/a
Undercover finished in 0.953s
Relevant lines from lcov file:
…
SF:./foo/applications/bar/services/baz.rb
…
DA:92,1
DA:106,181
DA:107,181
DA:108,181
DA:110,181
DA:115,181
DA:116,2
DA:119,179
DA:127,179
DA:134,179
DA:135,125
DA:137,54
DA:143,54
DA:144,32
DA:146,22
…
Thank you for reporting this and sharing both the method snippet and related lcov. I suspect there could be an issue with how line coverage is interpreted in one or more of the multi-line statements...
Above bug could potentially live in Undercover::Result#uncovered?
and Undercover::Result#coverage_f
That sounds very likely. A few days ago I encountered a similar issue where undercover was saying that coverage was below 100%, but its output showed all branches visited.
I eventually put a multi-line function call onto one line, and then it started to show branches: 3/4
on that line. One of the arguments was foo&.fun
(with a safe navigation operator). It looks like that was the issue here as well, as I can now see key_a: arg_b&.key_a
on line 156.
As for the specifics of how Undercover deals (or does not) with this case, I am afraid that I have no idea. 😂
Let me know if you need any more information.