minitest-heat 0.0.9 → 0.0.10
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 +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.
|