minitest-heat 0.0.7 → 0.0.11
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 +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.
|