minitest-heat 0.0.9 → 0.0.10
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 +1 -1
- data/README.md +1 -1
- data/lib/minitest/heat/backtrace/line.rb +118 -0
- data/lib/minitest/heat/backtrace.rb +48 -14
- data/lib/minitest/heat/hit.rb +23 -18
- data/lib/minitest/heat/issue.rb +89 -70
- data/lib/minitest/heat/location.rb +71 -53
- data/lib/minitest/heat/map.rb +14 -1
- data/lib/minitest/heat/output/backtrace.rb +12 -7
- data/lib/minitest/heat/output/issue.rb +42 -6
- data/lib/minitest/heat/output/map.rb +17 -3
- 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 +60 -4
- data/lib/minitest/heat/results.rb +18 -3
- data/lib/minitest/heat/version.rb +1 -1
- data/lib/minitest/heat.rb +2 -2
- data/lib/minitest/heat_plugin.rb +9 -17
- data/lib/minitest/heat_reporter.rb +19 -36
- metadata +3 -3
- data/lib/minitest/heat/line.rb +0 -74
@@ -22,10 +22,10 @@ module Minitest
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
attr_reader :
|
25
|
+
attr_reader :test_definition_location, :backtrace
|
26
26
|
|
27
|
-
def initialize(
|
28
|
-
@
|
27
|
+
def initialize(test_definition_location, backtrace = [])
|
28
|
+
@test_definition_location = TestDefinition.new(*test_definition_location)
|
29
29
|
@backtrace = Backtrace.new(backtrace)
|
30
30
|
end
|
31
31
|
|
@@ -45,9 +45,9 @@ module Minitest
|
|
45
45
|
# test, and it raises an exception, then it's really a broken test rather than a proper
|
46
46
|
# faiure.
|
47
47
|
#
|
48
|
-
# @return [Boolean] true if
|
48
|
+
# @return [Boolean] true if final file in the backtrace is the same as the test location file
|
49
49
|
def broken_test?
|
50
|
-
!test_file.nil? && test_file ==
|
50
|
+
!test_file.nil? && test_file == final_file
|
51
51
|
end
|
52
52
|
|
53
53
|
# Knows if the failure occurred in the actual project source code—as opposed to the test or
|
@@ -59,6 +59,15 @@ module Minitest
|
|
59
59
|
!source_code_file.nil? && !broken_test?
|
60
60
|
end
|
61
61
|
|
62
|
+
|
63
|
+
|
64
|
+
# The final location of the stacktrace regardless of whether it's from within the project
|
65
|
+
#
|
66
|
+
# @return [String] the relative path to the file from the project root
|
67
|
+
def final_file
|
68
|
+
Pathname(final_location.pathname)
|
69
|
+
end
|
70
|
+
|
62
71
|
# The file most likely to be the source of the underlying problem. Often, the most recent
|
63
72
|
# backtrace files will be a gem or external library that's failing indirectly as a result
|
64
73
|
# of a problem with local source code (not always, but frequently). In that case, the best
|
@@ -69,78 +78,76 @@ module Minitest
|
|
69
78
|
Pathname(most_relevant_location.pathname)
|
70
79
|
end
|
71
80
|
|
72
|
-
# The
|
81
|
+
# The final location from the stacktrace that is a test file
|
73
82
|
#
|
74
|
-
# @return [
|
75
|
-
def
|
76
|
-
|
83
|
+
# @return [String, nil] the relative path to the file from the project root
|
84
|
+
def test_file
|
85
|
+
Pathname(final_test_location.pathname)
|
77
86
|
end
|
78
87
|
|
79
|
-
# The final location
|
88
|
+
# The final location from the stacktrace that is within the project directory
|
80
89
|
#
|
81
|
-
# @return [String] the relative path to the file from the project root
|
82
|
-
def
|
83
|
-
|
84
|
-
end
|
90
|
+
# @return [String, nil] the relative path to the file from the project root
|
91
|
+
def source_code_file
|
92
|
+
return nil if final_source_code_location.nil?
|
85
93
|
|
86
|
-
|
87
|
-
#
|
88
|
-
# @return [Integer] line number
|
89
|
-
def final_failure_line
|
90
|
-
final_location.line_number
|
94
|
+
Pathname(final_source_code_location.pathname)
|
91
95
|
end
|
92
96
|
|
93
|
-
# The final location of the stacktrace
|
97
|
+
# The final location of the stacktrace from within the project (source code or test code)
|
94
98
|
#
|
95
99
|
# @return [String] the relative path to the file from the project root
|
96
100
|
def project_file
|
97
|
-
|
101
|
+
return nil if project_location.nil?
|
102
|
+
|
103
|
+
Pathname(project_location.pathname)
|
98
104
|
end
|
99
105
|
|
100
|
-
|
106
|
+
|
107
|
+
# The line number of the `final_file` where the failure originated
|
101
108
|
#
|
102
109
|
# @return [Integer] line number
|
103
|
-
def
|
104
|
-
|
110
|
+
def final_failure_line
|
111
|
+
final_location.line_number
|
105
112
|
end
|
106
113
|
|
107
|
-
# The
|
114
|
+
# The line number of the `most_relevant_file` where the failure originated
|
108
115
|
#
|
109
|
-
# @return [
|
110
|
-
def
|
111
|
-
|
112
|
-
|
113
|
-
backtrace.final_source_code_location.pathname
|
116
|
+
# @return [Integer] line number
|
117
|
+
def most_relevant_failure_line
|
118
|
+
most_relevant_location.line_number
|
114
119
|
end
|
115
120
|
|
116
|
-
# The line number of the `
|
121
|
+
# The line number of the `test_file` where the test is defined
|
117
122
|
#
|
118
123
|
# @return [Integer] line number
|
119
|
-
def
|
120
|
-
|
121
|
-
|
122
|
-
backtrace.final_source_code_location.line_number
|
124
|
+
def test_definition_line
|
125
|
+
test_definition_location.line_number
|
123
126
|
end
|
124
127
|
|
125
|
-
# The
|
128
|
+
# The line number from within the `test_file` test definition where the failure occurred
|
126
129
|
#
|
127
|
-
# @return [
|
128
|
-
def
|
129
|
-
|
130
|
+
# @return [Integer] line number
|
131
|
+
def test_failure_line
|
132
|
+
final_test_location.line_number
|
130
133
|
end
|
131
134
|
|
132
|
-
# The line number of the `
|
135
|
+
# The line number of the `source_code_file` where the failure originated
|
133
136
|
#
|
134
137
|
# @return [Integer] line number
|
135
|
-
def
|
136
|
-
|
138
|
+
def source_code_failure_line
|
139
|
+
final_source_code_location&.line_number
|
137
140
|
end
|
138
141
|
|
139
|
-
# The line number
|
142
|
+
# The line number of the `project_file` where the failure originated
|
140
143
|
#
|
141
144
|
# @return [Integer] line number
|
142
|
-
def
|
143
|
-
|
145
|
+
def project_failure_line
|
146
|
+
if !broken_test? && !source_code_file.nil?
|
147
|
+
source_code_failure_line
|
148
|
+
else
|
149
|
+
test_failure_line
|
150
|
+
end
|
144
151
|
end
|
145
152
|
|
146
153
|
# The line number from within the `test_file` test definition where the failure occurred
|
@@ -148,7 +155,7 @@ module Minitest
|
|
148
155
|
# @return [Location] the last location from the backtrace or the test location if a backtrace
|
149
156
|
# was not passed to the initializer
|
150
157
|
def final_location
|
151
|
-
backtrace? ? backtrace.final_location :
|
158
|
+
backtrace.parsed_entries.any? ? backtrace.final_location : test_definition_location
|
152
159
|
end
|
153
160
|
|
154
161
|
# The file most likely to be the source of the underlying problem. Often, the most recent
|
@@ -159,22 +166,33 @@ module Minitest
|
|
159
166
|
# @return [Array] file and line number of the most likely source of the problem
|
160
167
|
def most_relevant_location
|
161
168
|
[
|
162
|
-
|
163
|
-
|
169
|
+
final_source_code_location,
|
170
|
+
final_test_location,
|
164
171
|
final_location
|
165
172
|
].compact.first
|
166
173
|
end
|
167
174
|
|
168
|
-
|
169
|
-
|
175
|
+
# Returns the final test location based on the backtrace if present. Otherwise falls back to
|
176
|
+
# the test location which represents the test definition.
|
177
|
+
#
|
178
|
+
# @return [Location] the final location from the test files
|
179
|
+
def final_test_location
|
180
|
+
backtrace.final_test_location || test_definition_location
|
170
181
|
end
|
171
182
|
|
172
|
-
|
183
|
+
# Returns the final source code location based on the backtrace
|
184
|
+
#
|
185
|
+
# @return [Location] the final location from the source code files
|
186
|
+
def final_source_code_location
|
173
187
|
backtrace.final_source_code_location
|
174
188
|
end
|
175
189
|
|
176
|
-
|
177
|
-
|
190
|
+
# Returns the final project location based on the backtrace if present. Otherwise falls back
|
191
|
+
# to the test location which represents the test definition.
|
192
|
+
#
|
193
|
+
# @return [Location] the final location from the project files
|
194
|
+
def project_location
|
195
|
+
backtrace.final_project_location || test_definition_location
|
178
196
|
end
|
179
197
|
end
|
180
198
|
end
|
data/lib/minitest/heat/map.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Minitest
|
4
4
|
module Heat
|
5
|
+
# Structured approach to collecting the locations of issues for generating a heat map
|
5
6
|
class Map
|
6
7
|
MAXIMUM_FILES_TO_SHOW = 5
|
7
8
|
|
@@ -11,18 +12,30 @@ module Minitest
|
|
11
12
|
@hits = {}
|
12
13
|
end
|
13
14
|
|
15
|
+
# Records a hit to the list of files and issue types
|
16
|
+
# @param filename [String] the unique path and file name for recordings hits
|
17
|
+
# @param line_number [Integer] the line number where the issue was encountered
|
18
|
+
# @param type [Symbol] the type of issue that was encountered (i.e. :failure, :error, etc.)
|
19
|
+
#
|
20
|
+
# @return [void]
|
14
21
|
def add(filename, line_number, type)
|
15
22
|
@hits[filename] ||= Hit.new(filename)
|
16
23
|
|
17
|
-
@hits[filename].log(type, line_number)
|
24
|
+
@hits[filename].log(type.to_sym, line_number)
|
18
25
|
end
|
19
26
|
|
27
|
+
# Returns a subset of affected files to keep the list from being overwhelming
|
28
|
+
#
|
29
|
+
# @return [Array] the list of files and the line numbers for each encountered issue type
|
20
30
|
def file_hits
|
21
31
|
hot_files.take(MAXIMUM_FILES_TO_SHOW)
|
22
32
|
end
|
23
33
|
|
24
34
|
private
|
25
35
|
|
36
|
+
# Sorts the files by hit "weight" so that the most problematic files are at the beginning
|
37
|
+
#
|
38
|
+
# @return [Array] the collection of files that encountred issues
|
26
39
|
def hot_files
|
27
40
|
hits.values.sort_by(&:weight).reverse
|
28
41
|
end
|
@@ -23,13 +23,9 @@ module Minitest
|
|
23
23
|
# Iterate over the selected lines from the backtrace
|
24
24
|
backtrace_entries.each do |backtrace_entry|
|
25
25
|
# Get the source code for the line from the backtrace
|
26
|
-
parts =
|
27
|
-
indentation_token,
|
28
|
-
path_token(backtrace_entry),
|
29
|
-
*file_and_line_number_tokens(backtrace_entry),
|
30
|
-
source_code_line_token(backtrace_entry.source_code)
|
31
|
-
]
|
26
|
+
parts = backtrace_line_tokens(backtrace_entry)
|
32
27
|
|
28
|
+
# If it's the most recently modified file in the trace, add the token for that
|
33
29
|
parts << file_freshness(backtrace_entry) if most_recently_modified?(backtrace_entry)
|
34
30
|
|
35
31
|
@tokens << parts
|
@@ -57,6 +53,15 @@ module Minitest
|
|
57
53
|
|
58
54
|
private
|
59
55
|
|
56
|
+
def backtrace_line_tokens(backtrace_entry)
|
57
|
+
[
|
58
|
+
indentation_token,
|
59
|
+
path_token(backtrace_entry),
|
60
|
+
*file_and_line_number_tokens(backtrace_entry),
|
61
|
+
source_code_line_token(backtrace_entry.source_code)
|
62
|
+
]
|
63
|
+
end
|
64
|
+
|
60
65
|
def all_backtrace_entries_from_project?
|
61
66
|
backtrace_entries.all? { |line| line.path.to_s.include?(project_root_dir) }
|
62
67
|
end
|
@@ -123,7 +128,7 @@ module Minitest
|
|
123
128
|
end
|
124
129
|
|
125
130
|
def style_for(path)
|
126
|
-
|
131
|
+
path.to_s.include?(Dir.pwd) ? :default : :muted
|
127
132
|
end
|
128
133
|
end
|
129
134
|
end
|
@@ -3,7 +3,8 @@
|
|
3
3
|
module Minitest
|
4
4
|
module Heat
|
5
5
|
class Output
|
6
|
-
|
6
|
+
# Formats issues to output based on the issue type
|
7
|
+
class Issue # rubocop:disable Metrics/ClassLength
|
7
8
|
attr_accessor :issue
|
8
9
|
|
9
10
|
def initialize(issue)
|
@@ -77,11 +78,43 @@ module Minitest
|
|
77
78
|
end
|
78
79
|
|
79
80
|
def headline_tokens
|
80
|
-
[[issue.type, issue
|
81
|
+
[[issue.type, label(issue)], spacer_token, [:default, test_name(issue)]]
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_name(issue)
|
85
|
+
test_prefix = 'test_'
|
86
|
+
identifier = issue.test_identifier
|
87
|
+
|
88
|
+
if identifier.start_with?(test_prefix)
|
89
|
+
identifier.delete_prefix(test_prefix).gsub('_', ' ').capitalize
|
90
|
+
else
|
91
|
+
identifier
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def label(issue)
|
96
|
+
if issue.error? && issue.in_test?
|
97
|
+
# When the exception came out of the test itself, that's a different kind of exception
|
98
|
+
# that really only indicates there's a problem with the code in the test. It's kind of
|
99
|
+
# between an error and a test.
|
100
|
+
'Broken Test'
|
101
|
+
elsif issue.error?
|
102
|
+
'Error'
|
103
|
+
elsif issue.skipped?
|
104
|
+
'Skipped'
|
105
|
+
elsif issue.painful?
|
106
|
+
'Passed but Very Slow'
|
107
|
+
elsif issue.slow?
|
108
|
+
'Passed but Slow'
|
109
|
+
elsif !issue.passed?
|
110
|
+
'Failure'
|
111
|
+
else
|
112
|
+
'Success'
|
113
|
+
end
|
81
114
|
end
|
82
115
|
|
83
116
|
def test_name_and_class_tokens
|
84
|
-
[[:default, issue.test_class], *test_location_tokens
|
117
|
+
[[:default, issue.test_class], *test_location_tokens]
|
85
118
|
end
|
86
119
|
|
87
120
|
def backtrace_tokens
|
@@ -107,12 +140,12 @@ module Minitest
|
|
107
140
|
end
|
108
141
|
|
109
142
|
def summary_tokens
|
110
|
-
[[:italicized, issue.summary.delete_suffix(
|
143
|
+
[[:italicized, issue.summary.delete_suffix('---------------').strip]]
|
111
144
|
end
|
112
145
|
|
113
146
|
def slowness_summary_tokens
|
114
147
|
[
|
115
|
-
[:bold, issue
|
148
|
+
[:bold, slowness(issue)],
|
116
149
|
spacer_token,
|
117
150
|
[:default, issue.location.test_file.to_s.delete_prefix(Dir.pwd)],
|
118
151
|
[:muted, ':'],
|
@@ -120,6 +153,10 @@ module Minitest
|
|
120
153
|
]
|
121
154
|
end
|
122
155
|
|
156
|
+
def slowness(issue)
|
157
|
+
"#{issue.execution_time.round(2)}s"
|
158
|
+
end
|
159
|
+
|
123
160
|
def newline_tokens
|
124
161
|
[]
|
125
162
|
end
|
@@ -155,7 +192,6 @@ module Minitest
|
|
155
192
|
def arrow_token
|
156
193
|
Output::TOKENS[:muted_arrow]
|
157
194
|
end
|
158
|
-
|
159
195
|
end
|
160
196
|
end
|
161
197
|
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
|
|
@@ -34,8 +35,10 @@ module Minitest
|
|
34
35
|
end
|
35
36
|
|
36
37
|
def relevant_issue_types
|
38
|
+
# These are always relevant.
|
37
39
|
issue_types = %i[error broken failure]
|
38
40
|
|
41
|
+
# These are only relevant if there aren't more serious isues.
|
39
42
|
issue_types << :skipped unless results.problems?
|
40
43
|
issue_types << :painful unless results.problems? || results.skips.any?
|
41
44
|
issue_types << :slow unless results.problems? || results.skips.any?
|
@@ -44,7 +47,7 @@ module Minitest
|
|
44
47
|
end
|
45
48
|
|
46
49
|
def pathname(file)
|
47
|
-
directory = "#{file.pathname.dirname.to_s.delete_prefix(Dir.pwd)}/"
|
50
|
+
directory = "#{file.pathname.dirname.to_s.delete_prefix(Dir.pwd)}/".delete_prefix('/')
|
48
51
|
filename = file.pathname.basename.to_s
|
49
52
|
|
50
53
|
[
|
@@ -57,18 +60,29 @@ module Minitest
|
|
57
60
|
def hit_line_numbers(file, issue_type)
|
58
61
|
numbers = []
|
59
62
|
line_numbers_for_issue_type = file.issues.fetch(issue_type) { [] }
|
60
|
-
line_numbers_for_issue_type.
|
63
|
+
line_numbers_for_issue_type.map do |line_number|
|
61
64
|
numbers << [issue_type, "#{line_number} "]
|
62
65
|
end
|
66
|
+
|
63
67
|
numbers
|
64
68
|
end
|
65
69
|
|
66
70
|
def line_numbers(file)
|
67
71
|
line_number_tokens = []
|
72
|
+
|
73
|
+
# Merge the hits for all issue types into one list
|
68
74
|
relevant_issue_types.each do |issue_type|
|
69
75
|
line_number_tokens += hit_line_numbers(file, issue_type)
|
70
76
|
end
|
71
|
-
|
77
|
+
|
78
|
+
# Sort the collected group of line number hits so they're in order
|
79
|
+
line_number_tokens.compact.sort do |a, b|
|
80
|
+
# Ensure the line numbers are integers for sorting (otherwise '100' comes before '12')
|
81
|
+
first_line_number = Integer(a[1].strip)
|
82
|
+
second_line_number = Integer(b[1].strip)
|
83
|
+
|
84
|
+
first_line_number <=> second_line_number
|
85
|
+
end
|
72
86
|
end
|
73
87
|
end
|
74
88
|
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
@@ -15,14 +15,14 @@ module Minitest
|
|
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 => 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 => 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 => 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 => 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
|
@@ -11,9 +11,24 @@ 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, otherwise update the heat map
|
23
|
+
update_heat_map(issue) if issue.hit?
|
24
|
+
end
|
25
|
+
|
26
|
+
def update_heat_map(issue)
|
27
|
+
# Get the elements we need to generate a heat map entry
|
28
|
+
pathname = issue.location.project_file.to_s
|
29
|
+
line_number = issue.location.project_failure_line.to_i
|
30
|
+
|
31
|
+
@heat_map.add(pathname, line_number, issue.type)
|
17
32
|
end
|
18
33
|
|
19
34
|
def problems?
|
@@ -37,11 +52,11 @@ module Minitest
|
|
37
52
|
end
|
38
53
|
|
39
54
|
def painfuls
|
40
|
-
@painfuls ||= select_issues(:painful).sort_by(&:
|
55
|
+
@painfuls ||= select_issues(:painful).sort_by(&:execution_time).reverse
|
41
56
|
end
|
42
57
|
|
43
58
|
def slows
|
44
|
-
@slows ||= select_issues(:slow).sort_by(&:
|
59
|
+
@slows ||= select_issues(:slow).sort_by(&:execution_time).reverse
|
45
60
|
end
|
46
61
|
|
47
62
|
private
|
data/lib/minitest/heat.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
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'
|
8
7
|
require_relative 'heat/map'
|
9
8
|
require_relative 'heat/output'
|
@@ -13,7 +12,8 @@ require_relative 'heat/timer'
|
|
13
12
|
require_relative 'heat/version'
|
14
13
|
|
15
14
|
module Minitest
|
16
|
-
# Custom
|
15
|
+
# Custom Minitest reporter focused on generating output designed around efficiently identifying
|
16
|
+
# issues and potential solutions
|
17
17
|
# - Colorize the Output
|
18
18
|
# - What files had the most errors?
|
19
19
|
# - Show the most impacted areas first.
|