minitest-heat 0.0.7 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +41 -28
- data/lib/minitest/heat/issue.rb +96 -77
- data/lib/minitest/heat/location.rb +115 -116
- data/lib/minitest/heat/locations.rb +105 -0
- data/lib/minitest/heat/map.rb +16 -20
- data/lib/minitest/heat/output/backtrace.rb +52 -44
- data/lib/minitest/heat/output/issue.rb +69 -62
- data/lib/minitest/heat/output/map.rb +115 -32
- 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 +26 -4
- 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 +26 -39
- 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,100 @@ module Minitest
|
|
78
79
|
end
|
79
80
|
|
80
81
|
def headline_tokens
|
81
|
-
[[issue.type, issue
|
82
|
+
[[issue.type, label(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(issue) # rubocop:disable Metrics
|
97
|
+
if issue.error? && issue.in_test?
|
98
|
+
# When the exception came out of the test itself, that's a different kind of exception
|
99
|
+
# that really only indicates there's a problem with the code in the test. It's kind of
|
100
|
+
# between an error and a test.
|
101
|
+
'Broken Test'
|
102
|
+
elsif issue.error?
|
103
|
+
'Error'
|
104
|
+
elsif issue.skipped?
|
105
|
+
'Skipped'
|
106
|
+
elsif issue.painful?
|
107
|
+
'Passed but Very Slow'
|
108
|
+
elsif issue.slow?
|
109
|
+
'Passed but Slow'
|
110
|
+
elsif !issue.passed?
|
111
|
+
'Failure'
|
112
|
+
else
|
113
|
+
'Success'
|
114
|
+
end
|
82
115
|
end
|
83
116
|
|
84
117
|
def test_name_and_class_tokens
|
85
|
-
[[:default, issue.test_class], *test_location_tokens
|
118
|
+
[[:default, issue.test_class], *test_location_tokens]
|
86
119
|
end
|
87
120
|
|
88
121
|
def backtrace_tokens
|
89
|
-
|
90
|
-
|
91
|
-
backtrace.tokens
|
122
|
+
@backtrace_tokens ||= ::Minitest::Heat::Output::Backtrace.new(locations).tokens
|
92
123
|
end
|
93
124
|
|
94
125
|
def test_location_tokens
|
95
|
-
[
|
126
|
+
[
|
127
|
+
[:default, locations.test_definition.relative_filename],
|
128
|
+
[:muted, ':'],
|
129
|
+
[:default, locations.test_definition.line_number],
|
130
|
+
arrow_token,
|
131
|
+
[:default, locations.test_failure.line_number],
|
132
|
+
[:muted, "\n #{locations.test_failure.source_code.line.strip}"]
|
133
|
+
]
|
96
134
|
end
|
97
135
|
|
98
136
|
def location_tokens
|
99
|
-
[
|
137
|
+
[
|
138
|
+
[:default, locations.most_relevant.relative_filename],
|
139
|
+
[:muted, ':'],
|
140
|
+
[:default, locations.most_relevant.line_number],
|
141
|
+
[:muted, "\n #{locations.most_relevant.source_code.line.strip}"]
|
142
|
+
]
|
100
143
|
end
|
101
144
|
|
102
145
|
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
|
-
|
146
|
+
filename = locations.project.filename
|
147
|
+
line_number = locations.project.line_number
|
109
148
|
source = Minitest::Heat::Source.new(filename, line_number: line_number)
|
149
|
+
|
110
150
|
[[:muted, " #{Output::SYMBOLS[:arrow]} `#{source.line.strip}`"]]
|
111
151
|
end
|
112
152
|
|
113
153
|
def summary_tokens
|
114
|
-
[[:italicized, issue.summary.delete_suffix(
|
154
|
+
[[:italicized, issue.summary.delete_suffix('---------------').strip]]
|
115
155
|
end
|
116
156
|
|
117
157
|
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}/")
|
158
|
+
[
|
159
|
+
[:bold, slowness(issue)],
|
160
|
+
spacer_token,
|
161
|
+
[:default, locations.test_definition.relative_path],
|
162
|
+
[:default, locations.test_definition.filename],
|
163
|
+
[:muted, ':'],
|
164
|
+
[:default, locations.test_definition.line_number]
|
165
|
+
]
|
131
166
|
end
|
132
167
|
|
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}"
|
168
|
+
def slowness(issue)
|
169
|
+
"#{issue.execution_time.round(2)}s"
|
139
170
|
end
|
140
171
|
|
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}"
|
172
|
+
def newline_tokens
|
173
|
+
[]
|
147
174
|
end
|
148
175
|
|
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
176
|
def spacer_token
|
169
177
|
Output::TOKENS[:spacer]
|
170
178
|
end
|
@@ -172,7 +180,6 @@ module Minitest
|
|
172
180
|
def arrow_token
|
173
181
|
Output::TOKENS[:muted_arrow]
|
174
182
|
end
|
175
|
-
|
176
183
|
end
|
177
184
|
end
|
178
185
|
end
|
@@ -3,24 +3,36 @@
|
|
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
|
|
9
|
-
|
10
|
-
|
11
|
-
# def_delegators :@results, :errors, :brokens, :failures, :slows, :skips, :problems?, :slows?
|
12
|
-
|
13
|
-
def initialize(map)
|
14
|
-
@map = map
|
10
|
+
def initialize(results)
|
11
|
+
@results = results
|
15
12
|
@tokens = []
|
16
13
|
end
|
17
14
|
|
18
15
|
def tokens
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
]
|
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)
|
19
|
+
|
20
|
+
@tokens << [[:muted, ""]]
|
21
|
+
@tokens << file_summary_tokens(hit)
|
22
|
+
|
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
|
24
36
|
end
|
25
37
|
|
26
38
|
@tokens
|
@@ -28,38 +40,109 @@ module Minitest
|
|
28
40
|
|
29
41
|
private
|
30
42
|
|
31
|
-
def
|
32
|
-
|
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
|
+
]
|
63
|
+
end
|
64
|
+
|
65
|
+
def relevant_issue_types
|
66
|
+
# These are always relevant.
|
67
|
+
issue_types = %i[error broken failure]
|
68
|
+
|
69
|
+
# These are only relevant if there aren't more serious isues.
|
70
|
+
issue_types << :skipped unless results.problems?
|
71
|
+
issue_types << :painful unless results.problems? || results.skips.any?
|
72
|
+
issue_types << :slow unless results.problems? || results.skips.any?
|
73
|
+
|
74
|
+
issue_types
|
75
|
+
end
|
76
|
+
|
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
|
34
103
|
|
35
104
|
[
|
36
|
-
[:default, directory],
|
105
|
+
[:default, "#{directory}/"],
|
37
106
|
[:bold, filename],
|
38
107
|
[:default, ' · ']
|
39
108
|
]
|
40
109
|
end
|
41
110
|
|
42
|
-
def
|
43
|
-
|
111
|
+
def line_number_tokens_for_hit(hit)
|
112
|
+
line_number_tokens = []
|
44
113
|
|
45
|
-
|
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) { [] }
|
46
117
|
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
50
122
|
end
|
51
|
-
|
123
|
+
|
124
|
+
line_number_tokens.compact
|
52
125
|
end
|
53
126
|
|
54
|
-
def
|
55
|
-
[
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
145
|
+
end
|
63
146
|
end
|
64
147
|
end
|
65
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
|
@@ -4,14 +4,36 @@ module Minitest
|
|
4
4
|
module Heat
|
5
5
|
# A collection of test failures
|
6
6
|
class Results
|
7
|
-
attr_reader :issues
|
7
|
+
attr_reader :issues, :heat_map
|
8
8
|
|
9
9
|
def initialize
|
10
10
|
@issues = []
|
11
|
+
@heat_map = Heat::Map.new
|
11
12
|
end
|
12
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]
|
13
18
|
def record(issue)
|
14
|
-
|
19
|
+
# Record everything—even if it's a success
|
20
|
+
@issues.push(issue)
|
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)
|
15
37
|
end
|
16
38
|
|
17
39
|
def problems?
|
@@ -35,11 +57,11 @@ module Minitest
|
|
35
57
|
end
|
36
58
|
|
37
59
|
def painfuls
|
38
|
-
@painfuls ||= select_issues(:painful).sort_by(&:
|
60
|
+
@painfuls ||= select_issues(:painful).sort_by(&:execution_time).reverse
|
39
61
|
end
|
40
62
|
|
41
63
|
def slows
|
42
|
-
@slows ||= select_issues(:slow).sort_by(&:
|
64
|
+
@slows ||= select_issues(:slow).sort_by(&:execution_time).reverse
|
43
65
|
end
|
44
66
|
|
45
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.
|