-
Notifications
You must be signed in to change notification settings - Fork 28
/
result.rb
133 lines (114 loc) · 4.08 KB
/
result.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# frozen_string_literal: true
require 'forwardable'
module Undercover
class Result
extend Forwardable
attr_reader :node, :coverage, :file_path
def_delegators :node, :first_line, :last_line, :name
def initialize(node, file_cov, file_path)
@node = node
@coverage = file_cov.select do |ln, _|
first_line == last_line ? ln == first_line : ln > first_line && ln < last_line
end
@file_path = file_path
@flagged = false
end
def flag
@flagged = true
end
def flagged?
@flagged
end
# rubocop:disable Metrics/CyclomaticComplexity
def uncovered?(line_no)
# check branch coverage for line_no
coverage.each do |ln, _block, _branch, cov|
return true if ln == line_no && cov && cov.zero?
end
# check line coverage for line_no
line_cov = coverage.select { |cov| cov.size == 2 }.find { |ln, _cov| ln == line_no }
line_cov && line_cov[1].zero?
end
# rubocop:enable Metrics/CyclomaticComplexity
# Method `coverage_f` returns the total coverage of this Undercover::Result
# as a % value, taking into account missing branches. Line coverage will be counted
# as 0 if any branch is untested.
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
def coverage_f
lines = {}
coverage.each do |ln, block_or_line_cov, _, branch_cov|
lines[ln] = 1 unless lines.key?(ln)
if branch_cov
lines[ln] = 0 if branch_cov.zero?
elsif block_or_line_cov.zero?
lines[ln] = 0
end
end
return 1.0 if lines.keys.empty?
(lines.values.sum.to_f / lines.keys.size).round(4)
end
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
# TODO: create a formatter interface instead and add some tests.
# TODO: re-enable rubocops
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
#
# Zips coverage data (that doesn't include any non-code lines) with
# full source for given code fragment (this includes non-code lines!)
def pretty_print_lines
cov_enum = coverage.each
cov_source_lines = (node.first_line..node.last_line).map do |line_no|
cov_line_no = begin
cov_enum.peek[0]
rescue StopIteration
-1
end
cov_enum.next[1] if cov_line_no == line_no
end
cov_source_lines.zip(node.source_lines_with_numbers)
end
# TODO: move to formatter interface instead!
def pretty_print
pad = node.last_line.to_s.length
pretty_print_lines.map do |covered, (num, line)|
formatted_line = "#{num.to_s.rjust(pad)}: #{line}"
if line.strip.empty?
Rainbow(formatted_line).darkgray.dark
elsif covered.nil?
Rainbow(formatted_line).darkgray.dark + \
Rainbow(' hits: n/a').italic.darkgray.dark
elsif covered.positive?
Rainbow(formatted_line).green + \
Rainbow(" hits: #{covered}").italic.darkgray.dark + \
count_covered_branches(num)
elsif covered.zero?
Rainbow(formatted_line).red + \
Rainbow(" hits: #{covered}").italic.darkgray.dark + \
count_covered_branches(num)
end
end.join("\n")
end
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
def file_path_with_lines
"#{file_path}:#{first_line}:#{last_line}"
end
def inspect
"#<Undercover::Report::Result:#{object_id} " \
"name: #{node.name}, coverage: #{coverage_f}>"
end
alias to_s inspect
private
# rubocop:disable Metrics/AbcSize
def count_covered_branches(line_number)
branches = coverage.select { |cov| cov.size == 4 && cov[0] == line_number }
count_covered = branches.count { |cov| cov[3].positive? }
return '' if branches.empty?
if count_covered < branches.size
Rainbow(' branches: ').italic.darkgray.dark + \
Rainbow("#{count_covered}/#{branches.size}").italic.red
else
Rainbow(" branches: #{count_covered}/#{branches.size}").italic.darkgray.dark
end
end
# rubocop:enable Metrics/AbcSize
end
end