minitest-heat 0.0.8 → 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -0
- data/Gemfile.lock +6 -6
- data/README.md +1 -1
- data/lib/minitest/heat/backtrace/line_parser.rb +25 -0
- data/lib/minitest/heat/backtrace.rb +39 -43
- data/lib/minitest/heat/hit.rb +36 -19
- data/lib/minitest/heat/issue.rb +98 -79
- data/lib/minitest/heat/location.rb +115 -116
- data/lib/minitest/heat/locations.rb +105 -0
- data/lib/minitest/heat/map.rb +16 -4
- data/lib/minitest/heat/output/backtrace.rb +52 -44
- data/lib/minitest/heat/output/issue.rb +65 -62
- data/lib/minitest/heat/output/map.rb +99 -25
- data/lib/minitest/heat/output/marker.rb +5 -3
- data/lib/minitest/heat/output/results.rb +3 -2
- data/lib/minitest/heat/output/source_code.rb +1 -1
- data/lib/minitest/heat/output/token.rb +2 -1
- data/lib/minitest/heat/output.rb +66 -6
- data/lib/minitest/heat/results.rb +23 -3
- data/lib/minitest/heat/source.rb +1 -1
- data/lib/minitest/heat/version.rb +1 -1
- data/lib/minitest/heat.rb +3 -2
- data/lib/minitest/heat_plugin.rb +9 -17
- data/lib/minitest/heat_reporter.rb +24 -34
- metadata +5 -4
- data/lib/minitest/heat/line.rb +0 -74
@@ -3,11 +3,13 @@
|
|
3
3
|
module Minitest
|
4
4
|
module Heat
|
5
5
|
class Output
|
6
|
-
|
7
|
-
|
6
|
+
# Formats issues to output based on the issue type
|
7
|
+
class Issue # rubocop:disable Metrics/ClassLength
|
8
|
+
attr_accessor :issue, :locations
|
8
9
|
|
9
10
|
def initialize(issue)
|
10
11
|
@issue = issue
|
12
|
+
@locations = issue.locations
|
11
13
|
end
|
12
14
|
|
13
15
|
def tokens
|
@@ -27,7 +29,6 @@ module Minitest
|
|
27
29
|
[
|
28
30
|
headline_tokens,
|
29
31
|
test_location_tokens,
|
30
|
-
location_tokens,
|
31
32
|
summary_tokens,
|
32
33
|
*backtrace_tokens,
|
33
34
|
newline_tokens
|
@@ -78,93 +79,96 @@ module Minitest
|
|
78
79
|
end
|
79
80
|
|
80
81
|
def headline_tokens
|
81
|
-
[
|
82
|
+
[label_token(issue), spacer_token, [:default, test_name(issue)]]
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_name(issue)
|
86
|
+
test_prefix = 'test_'
|
87
|
+
identifier = issue.test_identifier
|
88
|
+
|
89
|
+
if identifier.start_with?(test_prefix)
|
90
|
+
identifier.delete_prefix(test_prefix).gsub('_', ' ').capitalize
|
91
|
+
else
|
92
|
+
identifier
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def label_token(issue)
|
97
|
+
[issue.type, issue_label(issue.type)]
|
98
|
+
end
|
99
|
+
|
100
|
+
def issue_label(issue_type)
|
101
|
+
case issue_type
|
102
|
+
when :error then 'Error'
|
103
|
+
when :broken then 'Broken Test'
|
104
|
+
when :failure then 'Failure'
|
105
|
+
when :skipped then 'Skipped'
|
106
|
+
when :slow then 'Passed but Slow'
|
107
|
+
when :painful then 'Passed but Very Slow'
|
108
|
+
when :passed then 'Success'
|
109
|
+
else 'Unknown'
|
110
|
+
end
|
82
111
|
end
|
83
112
|
|
84
113
|
def test_name_and_class_tokens
|
85
|
-
[[:default, issue.test_class], *test_location_tokens
|
114
|
+
[[:default, issue.test_class], *test_location_tokens]
|
86
115
|
end
|
87
116
|
|
88
117
|
def backtrace_tokens
|
89
|
-
|
90
|
-
|
91
|
-
backtrace.tokens
|
118
|
+
@backtrace_tokens ||= ::Minitest::Heat::Output::Backtrace.new(locations).tokens
|
92
119
|
end
|
93
120
|
|
94
121
|
def test_location_tokens
|
95
|
-
[
|
122
|
+
[
|
123
|
+
[:default, locations.test_definition.relative_filename],
|
124
|
+
[:muted, ':'],
|
125
|
+
[:default, locations.test_definition.line_number],
|
126
|
+
arrow_token,
|
127
|
+
[:default, locations.test_failure.line_number],
|
128
|
+
[:muted, "\n #{locations.test_failure.source_code.line.strip}"]
|
129
|
+
]
|
96
130
|
end
|
97
131
|
|
98
132
|
def location_tokens
|
99
|
-
[
|
133
|
+
[
|
134
|
+
[:default, locations.most_relevant.relative_filename],
|
135
|
+
[:muted, ':'],
|
136
|
+
[:default, locations.most_relevant.line_number],
|
137
|
+
[:muted, "\n #{locations.most_relevant.source_code.line.strip}"]
|
138
|
+
]
|
100
139
|
end
|
101
140
|
|
102
141
|
def source_tokens
|
103
|
-
filename =
|
104
|
-
line_number =
|
105
|
-
|
106
|
-
# source_code = ::Minitest::Heat::Output::SourceCode.new(filename, line_number, max_line_count: 1)
|
107
|
-
# source_code.tokens
|
108
|
-
|
142
|
+
filename = locations.project.filename
|
143
|
+
line_number = locations.project.line_number
|
109
144
|
source = Minitest::Heat::Source.new(filename, line_number: line_number)
|
145
|
+
|
110
146
|
[[:muted, " #{Output::SYMBOLS[:arrow]} `#{source.line.strip}`"]]
|
111
147
|
end
|
112
148
|
|
113
149
|
def summary_tokens
|
114
|
-
[[:italicized, issue.summary.delete_suffix(
|
150
|
+
[[:italicized, issue.summary.delete_suffix('---------------').strip]]
|
115
151
|
end
|
116
152
|
|
117
153
|
def slowness_summary_tokens
|
118
|
-
[
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
issue.location.most_relevant_file.to_s.delete_prefix("#{Dir.pwd}/")
|
127
|
-
end
|
128
|
-
|
129
|
-
def test_file_short_location
|
130
|
-
issue.location.test_file.to_s.delete_prefix("#{Dir.pwd}/")
|
154
|
+
[
|
155
|
+
[:bold, slowness(issue)],
|
156
|
+
spacer_token,
|
157
|
+
[:default, locations.test_definition.relative_path],
|
158
|
+
[:default, locations.test_definition.filename],
|
159
|
+
[:muted, ':'],
|
160
|
+
[:default, locations.test_definition.line_number]
|
161
|
+
]
|
131
162
|
end
|
132
163
|
|
133
|
-
def
|
134
|
-
|
135
|
-
line_number = issue.location.project_failure_line
|
136
|
-
|
137
|
-
source = Minitest::Heat::Source.new(filename, line_number: line_number)
|
138
|
-
"\n #{source.line.strip}"
|
164
|
+
def slowness(issue)
|
165
|
+
"#{issue.execution_time.round(2)}s"
|
139
166
|
end
|
140
167
|
|
141
|
-
def
|
142
|
-
|
143
|
-
line_number = issue.location.test_failure_line
|
144
|
-
|
145
|
-
source = Minitest::Heat::Source.new(filename, line_number: line_number)
|
146
|
-
"\n #{source.line.strip}"
|
168
|
+
def newline_tokens
|
169
|
+
[]
|
147
170
|
end
|
148
171
|
|
149
|
-
|
150
|
-
# def failure_summary_tokens
|
151
|
-
# return unless issue_summary_lines.any?
|
152
|
-
|
153
|
-
# # Sometimes, the exception message is multiple lines, so this adjusts the lines to
|
154
|
-
# # visually group them together a bit
|
155
|
-
# if issue_summary_lines.one?
|
156
|
-
# [[[:italicized, issue_summary_lines.first]]]
|
157
|
-
# else
|
158
|
-
# issue_summary_lines.map do |line|
|
159
|
-
# [Output::TOKENS[:muted_lead], [:italicized, line]]
|
160
|
-
# end
|
161
|
-
# end
|
162
|
-
# end
|
163
|
-
|
164
|
-
# def issue_summary_lines
|
165
|
-
# @issue_summary_lines ||= issue.summary.split("\n")
|
166
|
-
# end
|
167
|
-
|
168
172
|
def spacer_token
|
169
173
|
Output::TOKENS[:spacer]
|
170
174
|
end
|
@@ -172,7 +176,6 @@ module Minitest
|
|
172
176
|
def arrow_token
|
173
177
|
Output::TOKENS[:muted_arrow]
|
174
178
|
end
|
175
|
-
|
176
179
|
end
|
177
180
|
end
|
178
181
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
module Minitest
|
4
4
|
module Heat
|
5
5
|
class Output
|
6
|
+
# Generates the tokens to output the resulting heat map
|
6
7
|
class Map
|
7
8
|
attr_accessor :results
|
8
9
|
|
@@ -12,16 +13,26 @@ module Minitest
|
|
12
13
|
end
|
13
14
|
|
14
15
|
def tokens
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
results.heat_map.file_hits.each do |hit|
|
17
|
+
# If there's legitimate failures or errors, skips and slows aren't relevant
|
18
|
+
next unless relevant_issue_types?(hit)
|
18
19
|
|
19
|
-
|
20
|
+
@tokens << [[:muted, ""]]
|
21
|
+
@tokens << file_summary_tokens(hit)
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
repeats = repeated_line_numbers(hit)
|
24
|
+
next unless repeats.any?
|
25
|
+
|
26
|
+
repeats.each do |line_number|
|
27
|
+
@tokens << [[:muted, " Issues on Line #{line_number} initially triggered from these locations:"]]
|
28
|
+
|
29
|
+
traces = hit.lines[line_number.to_s]
|
30
|
+
sorted_traces = traces.sort_by { |trace| trace.locations.last.line_number }
|
31
|
+
|
32
|
+
sorted_traces.each do |trace|
|
33
|
+
@tokens << origination_location_token(trace)
|
34
|
+
end
|
35
|
+
end
|
25
36
|
end
|
26
37
|
|
27
38
|
@tokens
|
@@ -29,13 +40,33 @@ module Minitest
|
|
29
40
|
|
30
41
|
private
|
31
42
|
|
32
|
-
def
|
33
|
-
|
43
|
+
def file_summary_tokens(hit)
|
44
|
+
pathname_tokens = pathname(hit)
|
45
|
+
line_number_list_tokens = sorted_line_number_list(hit)
|
46
|
+
|
47
|
+
[*pathname_tokens, *line_number_list_tokens]
|
48
|
+
end
|
49
|
+
|
50
|
+
def origination_location_token(trace)
|
51
|
+
# The earliest project line from the backtrace—this is probabyl wholly incorrect in terms
|
52
|
+
# of what would be the most helpful line to display, but it's a start.
|
53
|
+
location = trace.locations.last
|
54
|
+
|
55
|
+
[
|
56
|
+
[:muted, " #{Output::SYMBOLS[:arrow]} "],
|
57
|
+
[:default, location.relative_filename],
|
58
|
+
[:muted, ':'],
|
59
|
+
[:default, location.line_number],
|
60
|
+
[:muted, " in #{location.container}"],
|
61
|
+
[:muted, " #{Output::SYMBOLS[:arrow]} `#{location.source_code.line.strip}`"],
|
62
|
+
]
|
34
63
|
end
|
35
64
|
|
36
65
|
def relevant_issue_types
|
66
|
+
# These are always relevant.
|
37
67
|
issue_types = %i[error broken failure]
|
38
68
|
|
69
|
+
# These are only relevant if there aren't more serious isues.
|
39
70
|
issue_types << :skipped unless results.problems?
|
40
71
|
issue_types << :painful unless results.problems? || results.skips.any?
|
41
72
|
issue_types << :slow unless results.problems? || results.skips.any?
|
@@ -43,32 +74,75 @@ module Minitest
|
|
43
74
|
issue_types
|
44
75
|
end
|
45
76
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
77
|
+
def relevant_issue_types?(hit)
|
78
|
+
intersection_issue_types = relevant_issue_types & hit.issues.keys
|
79
|
+
|
80
|
+
intersection_issue_types.any?
|
81
|
+
end
|
82
|
+
|
83
|
+
def repeated_line_numbers(hit)
|
84
|
+
repeated_line_numbers = []
|
85
|
+
|
86
|
+
hit.lines.each_pair do |line_number, traces|
|
87
|
+
# If there aren't multiple traces for a line number, it's not a repeat, right?
|
88
|
+
next unless traces.size > 1
|
89
|
+
|
90
|
+
repeated_line_numbers << Integer(line_number)
|
91
|
+
end
|
92
|
+
|
93
|
+
repeated_line_numbers.sort
|
94
|
+
end
|
95
|
+
|
96
|
+
def repeated_line_numbers?(hit)
|
97
|
+
repeated_line_numbers(hit).any?
|
98
|
+
end
|
99
|
+
|
100
|
+
def pathname(hit)
|
101
|
+
directory = hit.pathname.dirname.to_s.delete_prefix("#{Dir.pwd}/")
|
102
|
+
filename = hit.pathname.basename.to_s
|
49
103
|
|
50
104
|
[
|
51
|
-
[:default, directory],
|
105
|
+
[:default, "#{directory}/"],
|
52
106
|
[:bold, filename],
|
53
107
|
[:default, ' · ']
|
54
108
|
]
|
55
109
|
end
|
56
110
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
numbers
|
111
|
+
def line_number_tokens_for_hit(hit)
|
112
|
+
line_number_tokens = []
|
113
|
+
|
114
|
+
relevant_issue_types.each do |issue_type|
|
115
|
+
# Retrieve any line numbers for the issue type
|
116
|
+
line_numbers_for_issue_type = hit.issues.fetch(issue_type) { [] }
|
117
|
+
|
118
|
+
# Build the list of tokens representing styled line numbers
|
119
|
+
line_numbers_for_issue_type.each do |line_number|
|
120
|
+
line_number_tokens << line_number_token(issue_type, line_number)
|
121
|
+
end
|
62
122
|
end
|
63
|
-
|
123
|
+
|
124
|
+
line_number_tokens.compact
|
64
125
|
end
|
65
126
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
|
127
|
+
def line_number_token(style, line_number)
|
128
|
+
[style, "#{line_number} "]
|
129
|
+
end
|
130
|
+
|
131
|
+
# Generates the line number tokens styled based on their error type
|
132
|
+
#
|
133
|
+
# @param [Hit] hit the instance of the hit file details to build the heat map entry
|
134
|
+
#
|
135
|
+
# @return [Array] the arrays representing the line number tokens to display next to a file
|
136
|
+
# name in the heat map
|
137
|
+
def sorted_line_number_list(hit)
|
138
|
+
# Sort the collected group of line number hits so they're in order
|
139
|
+
line_number_tokens_for_hit(hit).sort do |a, b|
|
140
|
+
# Ensure the line numbers are integers for sorting (otherwise '100' comes before '12')
|
141
|
+
first_line_number = Integer(a[1].strip)
|
142
|
+
second_line_number = Integer(b[1].strip)
|
143
|
+
|
144
|
+
first_line_number <=> second_line_number
|
70
145
|
end
|
71
|
-
line_number_tokens.compact.sort_by { |number_token| number_token[1] }
|
72
146
|
end
|
73
147
|
end
|
74
148
|
end
|
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
module Minitest
|
4
4
|
module Heat
|
5
|
-
# Friendly API for printing nicely-formatted output to the console
|
6
5
|
class Output
|
6
|
+
# Friendly API for printing consistent markers for the various issue types
|
7
7
|
class Marker
|
8
8
|
SYMBOLS = {
|
9
9
|
success: '·',
|
@@ -12,7 +12,8 @@ module Minitest
|
|
12
12
|
broken: 'B',
|
13
13
|
error: 'E',
|
14
14
|
skipped: 'S',
|
15
|
-
failure: 'F'
|
15
|
+
failure: 'F',
|
16
|
+
reporter: '✖'
|
16
17
|
}.freeze
|
17
18
|
|
18
19
|
STYLES = {
|
@@ -22,7 +23,8 @@ module Minitest
|
|
22
23
|
broken: :error,
|
23
24
|
error: :error,
|
24
25
|
skipped: :skipped,
|
25
|
-
failure: :failure
|
26
|
+
failure: :failure,
|
27
|
+
reporter: :error
|
26
28
|
}.freeze
|
27
29
|
|
28
30
|
attr_accessor :issue_type
|
@@ -3,6 +3,7 @@
|
|
3
3
|
module Minitest
|
4
4
|
module Heat
|
5
5
|
class Output
|
6
|
+
# Generates the output tokens to display the results summary
|
6
7
|
class Results
|
7
8
|
extend Forwardable
|
8
9
|
|
@@ -90,7 +91,7 @@ module Minitest
|
|
90
91
|
end
|
91
92
|
|
92
93
|
def test_count_token
|
93
|
-
[:default,
|
94
|
+
[:default, pluralize(timer.test_count, 'test').to_s]
|
94
95
|
end
|
95
96
|
|
96
97
|
def tests_performance_token
|
@@ -98,7 +99,7 @@ module Minitest
|
|
98
99
|
end
|
99
100
|
|
100
101
|
def assertions_count_token
|
101
|
-
[:default,
|
102
|
+
[:default, pluralize(timer.assertion_count, 'assertion').to_s]
|
102
103
|
end
|
103
104
|
|
104
105
|
def assertions_performance_token
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Minitest
|
4
4
|
module Heat
|
5
5
|
class Output
|
6
|
-
#
|
6
|
+
# Generates the tokens representing a specific set of source code lines
|
7
7
|
class SourceCode
|
8
8
|
DEFAULT_LINE_COUNT = 3
|
9
9
|
DEFAULT_INDENTATION_SPACES = 2
|
@@ -2,8 +2,9 @@
|
|
2
2
|
|
3
3
|
module Minitest
|
4
4
|
module Heat
|
5
|
-
# Friendly API for printing nicely-formatted output to the console
|
6
5
|
class Output
|
6
|
+
# Provides a convenient interface for creating console-friendly output while ensuring
|
7
|
+
# consistency in the applied styles.
|
7
8
|
class Token
|
8
9
|
class InvalidStyle < ArgumentError; end
|
9
10
|
|
data/lib/minitest/heat/output.rb
CHANGED
@@ -11,18 +11,18 @@ require_relative 'output/token'
|
|
11
11
|
module Minitest
|
12
12
|
module Heat
|
13
13
|
# Friendly API for printing nicely-formatted output to the console
|
14
|
-
class Output
|
14
|
+
class Output # rubocop:disable Metrics/ClassLength
|
15
15
|
SYMBOLS = {
|
16
16
|
middot: '·',
|
17
17
|
arrow: '➜',
|
18
|
-
lead: '|'
|
18
|
+
lead: '|'
|
19
19
|
}.freeze
|
20
20
|
|
21
21
|
TOKENS = {
|
22
|
-
spacer:
|
22
|
+
spacer: [:muted, " #{SYMBOLS[:middot]} "],
|
23
23
|
muted_arrow: [:muted, " #{SYMBOLS[:arrow]} "],
|
24
|
-
muted_lead:
|
25
|
-
}
|
24
|
+
muted_lead: [:muted, "#{SYMBOLS[:lead]} "]
|
25
|
+
}.freeze
|
26
26
|
|
27
27
|
attr_reader :stream
|
28
28
|
|
@@ -42,8 +42,30 @@ module Minitest
|
|
42
42
|
end
|
43
43
|
alias newline puts
|
44
44
|
|
45
|
+
def issues_list(results)
|
46
|
+
# A couple of blank lines to create some breathing room
|
47
|
+
newline
|
48
|
+
newline
|
49
|
+
|
50
|
+
# Issues start with the least critical and go up to the most critical so that the most
|
51
|
+
# pressing issues are displayed at the bottom of the report in order to reduce scrolling.
|
52
|
+
# This way, as you fix issues, the list gets shorter, and eventually the least critical
|
53
|
+
# issues will be displayed without scrolling once more problematic issues are resolved.
|
54
|
+
%i[slows painfuls skips failures brokens errors].each do |issue_category|
|
55
|
+
next unless show?(issue_category, results)
|
56
|
+
|
57
|
+
results.send(issue_category).each { |issue| issue_details(issue) }
|
58
|
+
end
|
59
|
+
rescue StandardError => e
|
60
|
+
message = "Sorry, but Minitest Heat couldn't display the details of any failures."
|
61
|
+
exception_guidance(message, e)
|
62
|
+
end
|
63
|
+
|
45
64
|
def issue_details(issue)
|
46
65
|
print_tokens Minitest::Heat::Output::Issue.new(issue).tokens
|
66
|
+
rescue StandardError => e
|
67
|
+
message = "Sorry, but Minitest Heat couldn't display output for a failure."
|
68
|
+
exception_guidance(message, e)
|
47
69
|
end
|
48
70
|
|
49
71
|
def marker(issue_type)
|
@@ -53,15 +75,49 @@ module Minitest
|
|
53
75
|
def compact_summary(results, timer)
|
54
76
|
newline
|
55
77
|
print_tokens ::Minitest::Heat::Output::Results.new(results, timer).tokens
|
78
|
+
rescue StandardError => e
|
79
|
+
message = "Sorry, but Minitest Heat couldn't display the summary."
|
80
|
+
exception_guidance(message, e)
|
56
81
|
end
|
57
82
|
|
58
83
|
def heat_map(map)
|
59
84
|
newline
|
60
85
|
print_tokens ::Minitest::Heat::Output::Map.new(map).tokens
|
86
|
+
newline
|
87
|
+
rescue StandardError => e
|
88
|
+
message = "Sorry, but Minitest Heat couldn't display the heat map."
|
89
|
+
exception_guidance(message, e)
|
90
|
+
end
|
91
|
+
|
92
|
+
def exception_guidance(message, exception)
|
93
|
+
newline
|
94
|
+
puts "#{message} Disabling Minitest Heat can get you back on track until the problem can be fixed."
|
95
|
+
puts 'Please use the following exception details to submit an issue at https://github.com/garrettdimon/minitest-heat/issues'
|
96
|
+
puts "#{exception.message}:"
|
97
|
+
exception.backtrace.each do |line|
|
98
|
+
puts " #{line}"
|
99
|
+
end
|
100
|
+
newline
|
61
101
|
end
|
62
102
|
|
63
103
|
private
|
64
104
|
|
105
|
+
def no_problems?(results)
|
106
|
+
!results.problems?
|
107
|
+
end
|
108
|
+
|
109
|
+
def no_problems_or_skips?(results)
|
110
|
+
!results.problems? && results.skips.none?
|
111
|
+
end
|
112
|
+
|
113
|
+
def show?(issue_category, results)
|
114
|
+
case issue_category
|
115
|
+
when :skips then no_problems?(results)
|
116
|
+
when :painfuls, :slows then no_problems_or_skips?(results)
|
117
|
+
else true
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
65
121
|
def style_enabled?
|
66
122
|
stream.tty?
|
67
123
|
end
|
@@ -82,7 +138,11 @@ module Minitest
|
|
82
138
|
def print_tokens(lines_of_tokens)
|
83
139
|
lines_of_tokens.each do |tokens|
|
84
140
|
tokens.each do |token|
|
85
|
-
|
141
|
+
begin
|
142
|
+
print Token.new(*token).to_s(token_format)
|
143
|
+
rescue
|
144
|
+
puts token.inspect
|
145
|
+
end
|
86
146
|
end
|
87
147
|
newline
|
88
148
|
end
|
@@ -11,9 +11,29 @@ module Minitest
|
|
11
11
|
@heat_map = Heat::Map.new
|
12
12
|
end
|
13
13
|
|
14
|
+
# Logs an issue to the results for later reporting
|
15
|
+
# @param issue [Issue] the issue generated from a given test result
|
16
|
+
#
|
17
|
+
# @return [type] [description]
|
14
18
|
def record(issue)
|
19
|
+
# Record everything—even if it's a success
|
15
20
|
@issues.push(issue)
|
16
|
-
|
21
|
+
|
22
|
+
# If it's not a genuine problem, we're done here...
|
23
|
+
return unless issue.hit?
|
24
|
+
|
25
|
+
# ...otherwise update the heat map
|
26
|
+
update_heat_map(issue)
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_heat_map(issue)
|
30
|
+
# For heat map purposes, only the project backtrace lines are interesting
|
31
|
+
pathname, line_number = issue.locations.project.to_a
|
32
|
+
|
33
|
+
# Backtrace is only relevant for exception-generating issues, not slows, skips, or failures
|
34
|
+
backtrace = issue.error? ? issue.locations.backtrace.project_locations : []
|
35
|
+
|
36
|
+
@heat_map.add(pathname, line_number, issue.type, backtrace: backtrace)
|
17
37
|
end
|
18
38
|
|
19
39
|
def problems?
|
@@ -37,11 +57,11 @@ module Minitest
|
|
37
57
|
end
|
38
58
|
|
39
59
|
def painfuls
|
40
|
-
@painfuls ||= select_issues(:painful).sort_by(&:
|
60
|
+
@painfuls ||= select_issues(:painful).sort_by(&:execution_time).reverse
|
41
61
|
end
|
42
62
|
|
43
63
|
def slows
|
44
|
-
@slows ||= select_issues(:slow).sort_by(&:
|
64
|
+
@slows ||= select_issues(:slow).sort_by(&:execution_time).reverse
|
45
65
|
end
|
46
66
|
|
47
67
|
private
|
data/lib/minitest/heat/source.rb
CHANGED
data/lib/minitest/heat.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
require_relative 'heat/backtrace'
|
4
4
|
require_relative 'heat/hit'
|
5
5
|
require_relative 'heat/issue'
|
6
|
-
require_relative 'heat/line'
|
7
6
|
require_relative 'heat/location'
|
7
|
+
require_relative 'heat/locations'
|
8
8
|
require_relative 'heat/map'
|
9
9
|
require_relative 'heat/output'
|
10
10
|
require_relative 'heat/results'
|
@@ -13,7 +13,8 @@ require_relative 'heat/timer'
|
|
13
13
|
require_relative 'heat/version'
|
14
14
|
|
15
15
|
module Minitest
|
16
|
-
# Custom
|
16
|
+
# Custom Minitest reporter focused on generating output designed around efficiently identifying
|
17
|
+
# issues and potential solutions
|
17
18
|
# - Colorize the Output
|
18
19
|
# - What files had the most errors?
|
19
20
|
# - Show the most impacted areas first.
|
data/lib/minitest/heat_plugin.rb
CHANGED
@@ -2,25 +2,17 @@
|
|
2
2
|
|
3
3
|
require_relative 'heat_reporter'
|
4
4
|
|
5
|
-
module Minitest
|
6
|
-
def self.plugin_heat_options(opts, _options)
|
7
|
-
opts.on '--show-fast', 'Show failures as they happen instead of waiting for the entire suite.' do
|
8
|
-
# Heat.show_fast!
|
9
|
-
end
|
10
|
-
|
11
|
-
# TODO: options.
|
12
|
-
# 1. Fail Fast
|
13
|
-
# 2. Don't worry about skips.
|
14
|
-
# 3. Skip coverage.
|
15
|
-
end
|
16
|
-
|
5
|
+
module Minitest # rubocop:disable Style/Documentation
|
17
6
|
def self.plugin_heat_init(options)
|
18
|
-
io = options
|
7
|
+
io = options.fetch(:io, $stdout)
|
19
8
|
|
20
|
-
|
21
|
-
|
9
|
+
reporter.reporters.reject! do |reporter|
|
10
|
+
# Minitest Heat acts as a unified Progress *and* Summary reporter. Using other reporters of
|
11
|
+
# those types in conjunction with it creates some overly-verbose output
|
12
|
+
reporter.is_a?(ProgressReporter) || reporter.is_a?(SummaryReporter)
|
13
|
+
end
|
22
14
|
|
23
|
-
#
|
24
|
-
reporter << HeatReporter.new(io, options)
|
15
|
+
# Hook up Reviewer
|
16
|
+
self.reporter.reporters << HeatReporter.new(io, options)
|
25
17
|
end
|
26
18
|
end
|