minitest-heat 2.1.0 → 2.1.1
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/.flayignore +1 -0
- data/.reek.yml +87 -0
- data/.reviewer.yml +87 -0
- data/.reviewer_history.yml +42 -0
- data/.rubocop.yml +33 -2
- data/CHANGELOG.md +11 -0
- data/Gemfile +12 -2
- data/Gemfile.lock +61 -4
- data/lib/minitest/heat/backtrace/line_count.rb +1 -1
- data/lib/minitest/heat/backtrace.rb +5 -15
- data/lib/minitest/heat/hit.rb +5 -12
- data/lib/minitest/heat/issue.rb +15 -39
- data/lib/minitest/heat/location.rb +25 -63
- data/lib/minitest/heat/locations.rb +8 -24
- data/lib/minitest/heat/map.rb +3 -9
- data/lib/minitest/heat/output/backtrace.rb +16 -26
- data/lib/minitest/heat/output/issue.rb +1 -1
- data/lib/minitest/heat/output/map.rb +7 -7
- data/lib/minitest/heat/output/marker.rb +3 -9
- data/lib/minitest/heat/output/results.rb +15 -37
- data/lib/minitest/heat/output/source_code.rb +4 -12
- data/lib/minitest/heat/output/token.rb +23 -22
- data/lib/minitest/heat/output.rb +9 -11
- data/lib/minitest/heat/source.rb +14 -27
- data/lib/minitest/heat/timer.rb +6 -12
- data/lib/minitest/heat/version.rb +1 -1
- data/lib/minitest/heat_plugin.rb +2 -1
- data/lib/minitest/heat_reporter.rb +4 -8
- data/minitest-heat.gemspec +1 -9
- metadata +5 -99
|
@@ -30,19 +30,12 @@ module Minitest
|
|
|
30
30
|
# Generates a formatted string describing the line of code similar to the original backtrace
|
|
31
31
|
#
|
|
32
32
|
# @return [String] a consistently-formatted, human-readable string about the line of code
|
|
33
|
-
def to_s
|
|
34
|
-
"#{absolute_path}#{filename}:#{line_number} in `#{container}`"
|
|
35
|
-
end
|
|
33
|
+
def to_s = "#{absolute_path}#{filename}:#{line_number} in `#{container}`"
|
|
36
34
|
|
|
37
35
|
# Generates a simplified location array with the pathname and line number
|
|
38
36
|
#
|
|
39
37
|
# @return [Array<Pathname, Integer>] a no-frills location pair
|
|
40
|
-
def to_a
|
|
41
|
-
[
|
|
42
|
-
pathname,
|
|
43
|
-
line_number
|
|
44
|
-
]
|
|
45
|
-
end
|
|
38
|
+
def to_a = [pathname, line_number]
|
|
46
39
|
|
|
47
40
|
# Generates a hash representation for JSON serialization
|
|
48
41
|
#
|
|
@@ -58,16 +51,17 @@ module Minitest
|
|
|
58
51
|
# A short relative pathname and line number pair
|
|
59
52
|
#
|
|
60
53
|
# @return [String] the short filename/line number combo. ex. `dir/file.rb:23`
|
|
61
|
-
def short
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
def short = "#{relative_filename}:#{line_number}"
|
|
55
|
+
|
|
56
|
+
# Determine if the file exists on disk
|
|
57
|
+
#
|
|
58
|
+
# @return [Boolean] true if the file exists
|
|
59
|
+
def file_exists? = pathname.exist?
|
|
64
60
|
|
|
65
61
|
# Determine if there is a file and text at the given line number
|
|
66
62
|
#
|
|
67
63
|
# @return [Boolean] true if the file exists and has text at the given line number
|
|
68
|
-
def exists?
|
|
69
|
-
pathname.exist? && source_code.lines.any?
|
|
70
|
-
end
|
|
64
|
+
def exists? = file_exists? && source_code.lines.any?
|
|
71
65
|
|
|
72
66
|
# The pathanme for the location. Written to be safe and fallbackto the project directory if
|
|
73
67
|
# an exception is raised ocnverting the value to a pathname
|
|
@@ -83,33 +77,21 @@ module Minitest
|
|
|
83
77
|
#
|
|
84
78
|
# @return [String] either the path/directory portion of the file name or '(Unrecognized File)'
|
|
85
79
|
# if the offending file can't be found for some reason
|
|
86
|
-
def path
|
|
87
|
-
pathname.exist? ? pathname.dirname.to_s : UNRECOGNIZED
|
|
88
|
-
end
|
|
80
|
+
def path = file_exists? ? pathname.dirname.to_s : UNRECOGNIZED
|
|
89
81
|
|
|
90
|
-
def absolute_path
|
|
91
|
-
pathname.exist? ? "#{path}/" : UNRECOGNIZED
|
|
92
|
-
end
|
|
82
|
+
def absolute_path = file_exists? ? "#{path}/" : UNRECOGNIZED
|
|
93
83
|
|
|
94
|
-
def relative_path
|
|
95
|
-
pathname.exist? ? absolute_path.delete_prefix("#{project_root_dir}/") : UNRECOGNIZED
|
|
96
|
-
end
|
|
84
|
+
def relative_path = file_exists? ? absolute_path.delete_prefix("#{project_root_dir}/") : UNRECOGNIZED
|
|
97
85
|
|
|
98
86
|
# A safe interface for getting a string representing the filename portion of the file
|
|
99
87
|
#
|
|
100
88
|
# @return [String] either the filename portion of the file or '(Unrecognized File)'
|
|
101
89
|
# if the offending file can't be found for some reason
|
|
102
|
-
def filename
|
|
103
|
-
pathname.exist? ? pathname.basename.to_s : UNRECOGNIZED
|
|
104
|
-
end
|
|
90
|
+
def filename = file_exists? ? pathname.basename.to_s : UNRECOGNIZED
|
|
105
91
|
|
|
106
|
-
def absolute_filename
|
|
107
|
-
pathname.exist? ? pathname.to_s : UNRECOGNIZED
|
|
108
|
-
end
|
|
92
|
+
def absolute_filename = file_exists? ? pathname.to_s : UNRECOGNIZED
|
|
109
93
|
|
|
110
|
-
def relative_filename
|
|
111
|
-
pathname.exist? ? pathname.to_s.delete_prefix("#{project_root_dir}/") : UNRECOGNIZED
|
|
112
|
-
end
|
|
94
|
+
def relative_filename = file_exists? ? pathname.to_s.delete_prefix("#{project_root_dir}/") : UNRECOGNIZED
|
|
113
95
|
|
|
114
96
|
# Line number identifying the specific line in the file
|
|
115
97
|
#
|
|
@@ -124,9 +106,7 @@ module Minitest
|
|
|
124
106
|
# The containing method or block details for the location
|
|
125
107
|
#
|
|
126
108
|
# @return [String] the containing method of the line of code
|
|
127
|
-
def container
|
|
128
|
-
raw_container.nil? ? '(Unknown Container)' : String(raw_container)
|
|
129
|
-
end
|
|
109
|
+
def container = raw_container.nil? ? '(Unknown Container)' : String(raw_container)
|
|
130
110
|
|
|
131
111
|
# Looks up the source code for the location. Can return multiple lines of source code from
|
|
132
112
|
# the surrounding lines of code for the primary line
|
|
@@ -145,65 +125,47 @@ module Minitest
|
|
|
145
125
|
# Determines if a given file is from the project directory
|
|
146
126
|
#
|
|
147
127
|
# @return [Boolean] true if the file is in the project (source code or test) but not vendored
|
|
148
|
-
def project_file?
|
|
149
|
-
path.include?(project_root_dir) && !bundled_file? && !binstub_file?
|
|
150
|
-
end
|
|
128
|
+
def project_file? = path.include?(project_root_dir) && !bundled_file? && !binstub_file?
|
|
151
129
|
|
|
152
130
|
# Determines if the file is in the project `vendor/bundle` directory.
|
|
153
131
|
#
|
|
154
132
|
# @return [Boolean] true if the file is in `<project_root>/vendor/bundle
|
|
155
|
-
def bundled_file?
|
|
156
|
-
path.include?("#{project_root_dir}/vendor/bundle")
|
|
157
|
-
end
|
|
133
|
+
def bundled_file? = path.include?("#{project_root_dir}/vendor/bundle")
|
|
158
134
|
|
|
159
135
|
# Determines if the file is in the project `bin` directory. With binstub'd gems, they'll
|
|
160
136
|
# appear to be source code because the code is located in the project directory. This helps
|
|
161
137
|
# make sure the backtraces don't think that's the case
|
|
162
138
|
#
|
|
163
139
|
# @return [Boolean] true if the file is in `<project_root>/bin
|
|
164
|
-
def binstub_file?
|
|
165
|
-
path.include?("#{project_root_dir}/bin")
|
|
166
|
-
end
|
|
140
|
+
def binstub_file? = path.include?("#{project_root_dir}/bin")
|
|
167
141
|
|
|
168
142
|
# Determines if a given file follows the standard approaching to naming test files.
|
|
169
143
|
#
|
|
170
144
|
# @return [Boolean] true if the file name starts with `test_` or ends with `_test.rb`
|
|
171
|
-
def test_file?
|
|
172
|
-
filename.to_s.start_with?('test_') || filename.to_s.end_with?('_test.rb')
|
|
173
|
-
end
|
|
145
|
+
def test_file? = filename.to_s.start_with?('test_') || filename.to_s.end_with?('_test.rb')
|
|
174
146
|
|
|
175
147
|
# Determines if a given file is a non-test file from the project directory
|
|
176
148
|
#
|
|
177
149
|
# @return [Boolean] true if the file is in the project but not a test file or vendored file
|
|
178
|
-
def source_code_file?
|
|
179
|
-
project_file? && !test_file?
|
|
180
|
-
end
|
|
150
|
+
def source_code_file? = project_file? && !test_file?
|
|
181
151
|
|
|
182
152
|
# A safe interface to getting the last modified time for the file in question
|
|
183
153
|
#
|
|
184
154
|
# @return [Time] the timestamp for when the file in question was last modified or `Time.at(0)`
|
|
185
155
|
# if the offending file can't be found for some reason
|
|
186
|
-
def mtime
|
|
187
|
-
pathname.exist? ? pathname.mtime : UNKNOWN_MODIFICATION_TIME
|
|
188
|
-
end
|
|
156
|
+
def mtime = file_exists? ? pathname.mtime : UNKNOWN_MODIFICATION_TIME
|
|
189
157
|
|
|
190
158
|
# A safe interface to getting the number of seconds since the file was modified
|
|
191
159
|
#
|
|
192
160
|
# @return [Integer] the number of seconds since the file was modified or `-1` if the offending
|
|
193
161
|
# file can't be found for some reason
|
|
194
|
-
def age_in_seconds
|
|
195
|
-
pathname.exist? ? seconds_ago : UNKNOWN_MODIFICATION_SECONDS
|
|
196
|
-
end
|
|
162
|
+
def age_in_seconds = file_exists? ? seconds_ago : UNKNOWN_MODIFICATION_SECONDS
|
|
197
163
|
|
|
198
164
|
private
|
|
199
165
|
|
|
200
|
-
def project_root_dir
|
|
201
|
-
Dir.pwd
|
|
202
|
-
end
|
|
166
|
+
def project_root_dir = Dir.pwd
|
|
203
167
|
|
|
204
|
-
def seconds_ago
|
|
205
|
-
(Time.now - mtime).to_i
|
|
206
|
-
end
|
|
168
|
+
def seconds_ago = (Time.now - mtime).to_i
|
|
207
169
|
end
|
|
208
170
|
end
|
|
209
171
|
end
|
|
@@ -28,27 +28,21 @@ module Minitest
|
|
|
28
28
|
# test failure
|
|
29
29
|
#
|
|
30
30
|
# @return [String] ex. 'path/to/file.rb:12'
|
|
31
|
-
def to_s
|
|
32
|
-
"#{most_relevant.absolute_filename}:#{most_relevant.line_number}"
|
|
33
|
-
end
|
|
31
|
+
def to_s = "#{most_relevant.absolute_filename}:#{most_relevant.line_number}"
|
|
34
32
|
|
|
35
33
|
# Knows if the failure is contained within the test. For example, if there's bad code in a
|
|
36
34
|
# test, and it raises an exception, then it's really a broken test rather than a proper
|
|
37
35
|
# faiure.
|
|
38
36
|
#
|
|
39
37
|
# @return [Boolean] true if final file in the backtrace is the same as the test location file
|
|
40
|
-
def broken_test?
|
|
41
|
-
!test_failure.nil? && test_failure == final
|
|
42
|
-
end
|
|
38
|
+
def broken_test? = !test_failure.nil? && test_failure == final
|
|
43
39
|
|
|
44
40
|
# Knows if the failure occurred in the actual project source code—as opposed to the test or
|
|
45
41
|
# an external piece of code like a gem.
|
|
46
42
|
#
|
|
47
43
|
# @return [Boolean] true if there's a non-test project file in the stacktrace but it's not
|
|
48
44
|
# a result of a broken test
|
|
49
|
-
def proper_failure?
|
|
50
|
-
!source_code.nil? && !broken_test?
|
|
51
|
-
end
|
|
45
|
+
def proper_failure? = !source_code.nil? && !broken_test?
|
|
52
46
|
|
|
53
47
|
# The file most likely to be the source of the underlying problem. Often, the most recent
|
|
54
48
|
# backtrace files will be a gem or external library that's failing indirectly as a result
|
|
@@ -64,9 +58,7 @@ module Minitest
|
|
|
64
58
|
].compact.first
|
|
65
59
|
end
|
|
66
60
|
|
|
67
|
-
def freshest
|
|
68
|
-
backtrace.recently_modified_locations.first
|
|
69
|
-
end
|
|
61
|
+
def freshest = backtrace.recently_modified_locations.first
|
|
70
62
|
|
|
71
63
|
# Returns the final test location based on the backtrace if present. Otherwise falls back to
|
|
72
64
|
# the test location which represents the test definition. The `test_definition` attribute
|
|
@@ -74,32 +66,24 @@ module Minitest
|
|
|
74
66
|
# line from within the test where the problem occurred
|
|
75
67
|
#
|
|
76
68
|
# @return [Location] the final location from the test files
|
|
77
|
-
def test_failure
|
|
78
|
-
backtrace.test_locations.any? ? backtrace.test_locations.first : test_definition
|
|
79
|
-
end
|
|
69
|
+
def test_failure = backtrace.test_locations.any? ? backtrace.test_locations.first : test_definition
|
|
80
70
|
|
|
81
71
|
# Returns the final source code location based on the backtrace
|
|
82
72
|
#
|
|
83
73
|
# @return [Location] the final location from the source code files
|
|
84
|
-
def source_code
|
|
85
|
-
backtrace.source_code_locations.first
|
|
86
|
-
end
|
|
74
|
+
def source_code = backtrace.source_code_locations.first
|
|
87
75
|
|
|
88
76
|
# Returns the final project location based on the backtrace if present. Otherwise falls back
|
|
89
77
|
# to the test location which represents the test definition.
|
|
90
78
|
#
|
|
91
79
|
# @return [Location] the final location from the project files
|
|
92
|
-
def project
|
|
93
|
-
backtrace.project_locations.any? ? backtrace.project_locations.first : test_definition
|
|
94
|
-
end
|
|
80
|
+
def project = backtrace.project_locations.any? ? backtrace.project_locations.first : test_definition
|
|
95
81
|
|
|
96
82
|
# The line number from within the `test_file` test definition where the failure occurred
|
|
97
83
|
#
|
|
98
84
|
# @return [Location] the last location from the backtrace or the test location if a backtrace
|
|
99
85
|
# was not passed to the initializer
|
|
100
|
-
def final
|
|
101
|
-
backtrace.locations.any? ? backtrace.locations.first : test_definition
|
|
102
|
-
end
|
|
86
|
+
def final = backtrace.locations.any? ? backtrace.locations.first : test_definition
|
|
103
87
|
end
|
|
104
88
|
end
|
|
105
89
|
end
|
data/lib/minitest/heat/map.rb
CHANGED
|
@@ -26,25 +26,19 @@ module Minitest
|
|
|
26
26
|
# Returns a subset of affected files to keep the list from being overwhelming
|
|
27
27
|
#
|
|
28
28
|
# @return [Array] the list of files and the line numbers for each encountered issue type
|
|
29
|
-
def file_hits
|
|
30
|
-
hot_files.take(MAXIMUM_FILES_TO_SHOW)
|
|
31
|
-
end
|
|
29
|
+
def file_hits = hot_files.take(MAXIMUM_FILES_TO_SHOW)
|
|
32
30
|
|
|
33
31
|
# Generates a hash representation for JSON serialization
|
|
34
32
|
#
|
|
35
33
|
# @return [Array<Hash>] array of hit hashes sorted by weight (highest first)
|
|
36
|
-
def to_h
|
|
37
|
-
hot_files.map(&:to_h)
|
|
38
|
-
end
|
|
34
|
+
def to_h = hot_files.map(&:to_h)
|
|
39
35
|
|
|
40
36
|
private
|
|
41
37
|
|
|
42
38
|
# Sorts the files by hit "weight" so that the most problematic files are at the beginning
|
|
43
39
|
#
|
|
44
40
|
# @return [Array] the collection of files that encountred issues
|
|
45
|
-
def hot_files
|
|
46
|
-
hits.values.sort_by(&:weight).reverse
|
|
47
|
-
end
|
|
41
|
+
def hot_files = hits.values.sort_by(&:weight).reverse
|
|
48
42
|
end
|
|
49
43
|
end
|
|
50
44
|
end
|
|
@@ -23,16 +23,14 @@ module Minitest
|
|
|
23
23
|
# Determines the number of lines to display from the backtrace.
|
|
24
24
|
#
|
|
25
25
|
# @return [Integer] the number of lines to limit the backtrace to
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
::Minitest::Heat::Backtrace::LineCount.new(backtrace.locations).limit
|
|
35
|
-
end
|
|
26
|
+
# Defined as a method instead of using the constant directly in order to easily support
|
|
27
|
+
# adding options for controlling how many lines are displayed from a backtrace.
|
|
28
|
+
#
|
|
29
|
+
# For example, instead of a fixed number, the backtrace could dynamically calculate how
|
|
30
|
+
# many lines it should displaye in order to get to the origination point. Or it could have
|
|
31
|
+
# a default, but inteligently go back further if the backtrace meets some criteria for
|
|
32
|
+
# displaying more lines.
|
|
33
|
+
def line_count = ::Minitest::Heat::Backtrace::LineCount.new(backtrace.locations).limit
|
|
36
34
|
|
|
37
35
|
# A subset of parsed lines from the backtrace.
|
|
38
36
|
#
|
|
@@ -69,16 +67,14 @@ module Minitest
|
|
|
69
67
|
*file_and_line_number_tokens(location),
|
|
70
68
|
containining_element_token(location),
|
|
71
69
|
source_code_line_token(location),
|
|
72
|
-
most_recently_modified_token(location)
|
|
70
|
+
most_recently_modified_token(location)
|
|
73
71
|
].compact
|
|
74
72
|
end
|
|
75
73
|
|
|
76
74
|
# Determines if all lines to be displayed are from within the project directory
|
|
77
75
|
#
|
|
78
76
|
# @return [Boolean] true if all lines of the backtrace being displayed are from the project
|
|
79
|
-
def all_backtrace_from_project?
|
|
80
|
-
backtrace_locations.all?(&:project_file?)
|
|
81
|
-
end
|
|
77
|
+
def all_backtrace_from_project? = backtrace_locations.all?(&:project_file?)
|
|
82
78
|
|
|
83
79
|
# Determines if the file referenced by a backtrace line is the most recently modified file
|
|
84
80
|
# of all the files referenced in the visible backtrace locations.
|
|
@@ -87,16 +83,12 @@ module Minitest
|
|
|
87
83
|
#
|
|
88
84
|
# @return [<type>] <description>
|
|
89
85
|
#
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
backtrace_locations.size > 1 && location == locations.freshest
|
|
95
|
-
end
|
|
86
|
+
# If there's more than one line being displayed (otherwise, with one line, of course it's
|
|
87
|
+
# the most recently modified because there_aren't any others) and the current line is the
|
|
88
|
+
# same as the freshest location in the backtrace
|
|
89
|
+
def most_recently_modified?(location) = backtrace_locations.size > 1 && location == locations.freshest
|
|
96
90
|
|
|
97
|
-
def indentation_token
|
|
98
|
-
[:default, ' ' * indentation]
|
|
99
|
-
end
|
|
91
|
+
def indentation_token = [:default, ' ' * indentation]
|
|
100
92
|
|
|
101
93
|
def path_token(location)
|
|
102
94
|
# If the line is a project file, help it stand out from the backtrace noise
|
|
@@ -145,9 +137,7 @@ module Minitest
|
|
|
145
137
|
# option that would reduce the space used.
|
|
146
138
|
#
|
|
147
139
|
# @return [type] [description]
|
|
148
|
-
def indentation
|
|
149
|
-
DEFAULT_INDENTATION_SPACES
|
|
150
|
-
end
|
|
140
|
+
def indentation = DEFAULT_INDENTATION_SPACES
|
|
151
141
|
end
|
|
152
142
|
end
|
|
153
143
|
end
|
|
@@ -19,7 +19,7 @@ module Minitest
|
|
|
19
19
|
next unless relevant_issue_types?(hit)
|
|
20
20
|
|
|
21
21
|
# Add a new line
|
|
22
|
-
@tokens << [[:muted,
|
|
22
|
+
@tokens << [[:muted, '']]
|
|
23
23
|
|
|
24
24
|
# Build the summary line for the file
|
|
25
25
|
@tokens << file_summary_tokens(hit)
|
|
@@ -54,10 +54,10 @@ module Minitest
|
|
|
54
54
|
# 1. Only pull the traces that have proper locations
|
|
55
55
|
# 2. Sort the traces by the most recent line number so they're displayed in numeric order
|
|
56
56
|
# 3. Get the final relevant location from the trace
|
|
57
|
-
traces
|
|
58
|
-
select { |trace| trace.locations.any? }
|
|
59
|
-
sort_by { |trace| trace.locations.last.line_number }
|
|
60
|
-
map { |trace| origination_location_token(trace) }
|
|
57
|
+
traces
|
|
58
|
+
.select { |trace| trace.locations.any? }
|
|
59
|
+
.sort_by { |trace| trace.locations.last.line_number }
|
|
60
|
+
.map { |trace| origination_location_token(trace) }
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def file_summary_tokens(hit)
|
|
@@ -80,7 +80,7 @@ module Minitest
|
|
|
80
80
|
[:muted, ':'],
|
|
81
81
|
[:default, location.line_number],
|
|
82
82
|
[:muted, " in #{location.container}"],
|
|
83
|
-
[:muted, " #{Output::SYMBOLS[:arrow]} `#{location.source_code.line.strip}`"]
|
|
83
|
+
[:muted, " #{Output::SYMBOLS[:arrow]} `#{location.source_code.line.strip}`"]
|
|
84
84
|
]
|
|
85
85
|
end
|
|
86
86
|
|
|
@@ -160,7 +160,7 @@ module Minitest
|
|
|
160
160
|
def line_number_token(style, line_number, frequency)
|
|
161
161
|
if frequency > 1
|
|
162
162
|
[
|
|
163
|
-
[style,
|
|
163
|
+
[style, line_number.to_s],
|
|
164
164
|
[:muted, "✕#{frequency} "]
|
|
165
165
|
]
|
|
166
166
|
else
|
|
@@ -33,19 +33,13 @@ module Minitest
|
|
|
33
33
|
@issue_type = issue_type
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def token
|
|
37
|
-
[style, symbol]
|
|
38
|
-
end
|
|
36
|
+
def token = [style, symbol]
|
|
39
37
|
|
|
40
38
|
private
|
|
41
39
|
|
|
42
|
-
def style
|
|
43
|
-
STYLES.fetch(issue_type, :default)
|
|
44
|
-
end
|
|
40
|
+
def style = STYLES.fetch(issue_type, :default)
|
|
45
41
|
|
|
46
|
-
def symbol
|
|
47
|
-
SYMBOLS.fetch(issue_type, '?')
|
|
48
|
-
end
|
|
42
|
+
def symbol = SYMBOLS.fetch(issue_type, '?')
|
|
49
43
|
end
|
|
50
44
|
end
|
|
51
45
|
end
|
|
@@ -10,9 +10,7 @@ module Minitest
|
|
|
10
10
|
attr_accessor :results, :timer
|
|
11
11
|
|
|
12
12
|
# Explicitly define issues to avoid Forwardable warning about Object#issues private method
|
|
13
|
-
def issues
|
|
14
|
-
@results.issues
|
|
15
|
-
end
|
|
13
|
+
def issues = @results.issues
|
|
16
14
|
|
|
17
15
|
def_delegators :@results, :errors, :brokens, :failures, :skips, :painfuls, :slows, :problems?
|
|
18
16
|
|
|
@@ -68,17 +66,11 @@ module Minitest
|
|
|
68
66
|
counts_with_separators
|
|
69
67
|
end
|
|
70
68
|
|
|
71
|
-
def error_count_token
|
|
72
|
-
issue_count_token(:error, errors)
|
|
73
|
-
end
|
|
69
|
+
def error_count_token = issue_count_token(:error, errors)
|
|
74
70
|
|
|
75
|
-
def broken_count_token
|
|
76
|
-
issue_count_token(:broken, brokens)
|
|
77
|
-
end
|
|
71
|
+
def broken_count_token = issue_count_token(:broken, brokens)
|
|
78
72
|
|
|
79
|
-
def failure_count_token
|
|
80
|
-
issue_count_token(:failure, failures)
|
|
81
|
-
end
|
|
73
|
+
def failure_count_token = issue_count_token(:failure, failures)
|
|
82
74
|
|
|
83
75
|
def skip_count_token
|
|
84
76
|
style = problems? ? :muted : :skipped
|
|
@@ -86,34 +78,24 @@ module Minitest
|
|
|
86
78
|
end
|
|
87
79
|
|
|
88
80
|
def painful_count_token
|
|
89
|
-
|
|
90
|
-
issue_count_token(style, painfuls, name: 'Painfully Slow')
|
|
81
|
+
issue_count_token(secondary_style(:painful), painfuls, name: 'Painfully Slow')
|
|
91
82
|
end
|
|
92
83
|
|
|
93
84
|
def slow_count_token
|
|
94
|
-
|
|
95
|
-
issue_count_token(style, slows, name: 'Slow')
|
|
85
|
+
issue_count_token(secondary_style(:slow), slows, name: 'Slow')
|
|
96
86
|
end
|
|
97
87
|
|
|
98
|
-
def
|
|
99
|
-
[:default, pluralize(timer.test_count, 'test').to_s]
|
|
100
|
-
end
|
|
88
|
+
def secondary_style(style) = problems? || skips.any? ? :muted : style
|
|
101
89
|
|
|
102
|
-
def
|
|
103
|
-
[:default, " (#{timer.tests_per_second}/s)"]
|
|
104
|
-
end
|
|
90
|
+
def test_count_token = [:default, pluralize(timer.test_count, 'test').to_s]
|
|
105
91
|
|
|
106
|
-
def
|
|
107
|
-
[:default, pluralize(timer.assertion_count, 'assertion').to_s]
|
|
108
|
-
end
|
|
92
|
+
def tests_performance_token = [:default, " (#{timer.tests_per_second}/s)"]
|
|
109
93
|
|
|
110
|
-
def
|
|
111
|
-
[:default, " (#{timer.assertions_per_second}/s)"]
|
|
112
|
-
end
|
|
94
|
+
def assertions_count_token = [:default, pluralize(timer.assertion_count, 'assertion').to_s]
|
|
113
95
|
|
|
114
|
-
def
|
|
115
|
-
|
|
116
|
-
|
|
96
|
+
def assertions_performance_token = [:default, " (#{timer.assertions_per_second}/s)"]
|
|
97
|
+
|
|
98
|
+
def timing_token = [:bold, "#{timer.total_time.round(2)}s"]
|
|
117
99
|
|
|
118
100
|
def issue_count_token(type, collection, name: type.capitalize)
|
|
119
101
|
return nil if collection.empty?
|
|
@@ -121,13 +103,9 @@ module Minitest
|
|
|
121
103
|
[type, pluralize(collection.size, name)]
|
|
122
104
|
end
|
|
123
105
|
|
|
124
|
-
def spacer_token
|
|
125
|
-
Output::TOKENS[:spacer]
|
|
126
|
-
end
|
|
106
|
+
def spacer_token = Output::TOKENS[:spacer]
|
|
127
107
|
|
|
128
|
-
def join_token
|
|
129
|
-
[:default, ' with ']
|
|
130
|
-
end
|
|
108
|
+
def join_token = [:default, ' with ']
|
|
131
109
|
end
|
|
132
110
|
end
|
|
133
111
|
end
|
|
@@ -63,9 +63,7 @@ module Minitest
|
|
|
63
63
|
# three lines. Or it could be something end users could disable in order to reduce noise.
|
|
64
64
|
#
|
|
65
65
|
# @return [Boolean] true if the target line should be highlighted
|
|
66
|
-
def highlight_key_line?
|
|
67
|
-
HIGHLIGHT_KEY_LINE
|
|
68
|
-
end
|
|
66
|
+
def highlight_key_line? = HIGHLIGHT_KEY_LINE
|
|
69
67
|
|
|
70
68
|
# The number of spaces each line of code should be indented. Currently defaults to 2 in
|
|
71
69
|
# order to provide visual separation between test failures, but in the future, it could
|
|
@@ -75,9 +73,7 @@ module Minitest
|
|
|
75
73
|
# option that would reduce the space used.
|
|
76
74
|
#
|
|
77
75
|
# @return [type] [description]
|
|
78
|
-
def indentation
|
|
79
|
-
DEFAULT_INDENTATION_SPACES
|
|
80
|
-
end
|
|
76
|
+
def indentation = DEFAULT_INDENTATION_SPACES
|
|
81
77
|
|
|
82
78
|
private
|
|
83
79
|
|
|
@@ -113,18 +109,14 @@ module Minitest
|
|
|
113
109
|
# @param line_number [Integer,String] the digits representing the line number
|
|
114
110
|
#
|
|
115
111
|
# @return [Array] the style/content token for the current line number
|
|
116
|
-
def line_number_token(style, line_number)
|
|
117
|
-
[style, "#{' ' * indentation}#{line_number.to_s.rjust(max_line_number_digits)} "]
|
|
118
|
-
end
|
|
112
|
+
def line_number_token(style, line_number) = [style, "#{' ' * indentation}#{line_number.to_s.rjust(max_line_number_digits)} "]
|
|
119
113
|
|
|
120
114
|
# The token representing the content of a given line of code.
|
|
121
115
|
# @param style [Symbol] the symbol representing the style for the line of code token
|
|
122
116
|
# @param line_number [Integer,String] the content of the line of code
|
|
123
117
|
#
|
|
124
118
|
# @return [Array] the style/content token for the current line of code
|
|
125
|
-
def line_of_code_token(style, line_of_code)
|
|
126
|
-
[style, line_of_code]
|
|
127
|
-
end
|
|
119
|
+
def line_of_code_token(style, line_of_code) = [style, line_of_code]
|
|
128
120
|
end
|
|
129
121
|
end
|
|
130
122
|
end
|
|
@@ -6,6 +6,7 @@ module Minitest
|
|
|
6
6
|
# Provides a convenient interface for creating console-friendly output while ensuring
|
|
7
7
|
# consistency in the applied styles.
|
|
8
8
|
class Token
|
|
9
|
+
# Raised when an unrecognized style key is used
|
|
9
10
|
class InvalidStyle < ArgumentError; end
|
|
10
11
|
|
|
11
12
|
STYLES = {
|
|
@@ -23,6 +24,28 @@ module Minitest
|
|
|
23
24
|
muted: %i[light default]
|
|
24
25
|
}.freeze
|
|
25
26
|
|
|
27
|
+
ESC_SEQUENCE = "\e["
|
|
28
|
+
END_SEQUENCE = 'm'
|
|
29
|
+
|
|
30
|
+
WEIGHTS = {
|
|
31
|
+
default: 0,
|
|
32
|
+
bold: 1,
|
|
33
|
+
light: 2,
|
|
34
|
+
italic: 3
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
COLORS = {
|
|
38
|
+
black: 30,
|
|
39
|
+
red: 31,
|
|
40
|
+
green: 32,
|
|
41
|
+
yellow: 33,
|
|
42
|
+
blue: 34,
|
|
43
|
+
magenta: 35,
|
|
44
|
+
cyan: 36,
|
|
45
|
+
gray: 37,
|
|
46
|
+
default: 39
|
|
47
|
+
}.freeze
|
|
48
|
+
|
|
26
49
|
attr_accessor :style_key, :content
|
|
27
50
|
|
|
28
51
|
def initialize(style_key, content)
|
|
@@ -47,28 +70,6 @@ module Minitest
|
|
|
47
70
|
|
|
48
71
|
private
|
|
49
72
|
|
|
50
|
-
ESC_SEQUENCE = "\e["
|
|
51
|
-
END_SEQUENCE = 'm'
|
|
52
|
-
|
|
53
|
-
WEIGHTS = {
|
|
54
|
-
default: 0,
|
|
55
|
-
bold: 1,
|
|
56
|
-
light: 2,
|
|
57
|
-
italic: 3
|
|
58
|
-
}.freeze
|
|
59
|
-
|
|
60
|
-
COLORS = {
|
|
61
|
-
black: 30,
|
|
62
|
-
red: 31,
|
|
63
|
-
green: 32,
|
|
64
|
-
yellow: 33,
|
|
65
|
-
blue: 34,
|
|
66
|
-
magenta: 35,
|
|
67
|
-
cyan: 36,
|
|
68
|
-
gray: 37,
|
|
69
|
-
default: 39
|
|
70
|
-
}.freeze
|
|
71
|
-
|
|
72
73
|
def style_string
|
|
73
74
|
"#{ESC_SEQUENCE}#{weight};#{color}#{END_SEQUENCE}"
|
|
74
75
|
end
|
data/lib/minitest/heat/output.rb
CHANGED
|
@@ -11,7 +11,7 @@ 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
|
|
15
15
|
SYMBOLS = {
|
|
16
16
|
middot: '·',
|
|
17
17
|
arrow: '➜',
|
|
@@ -33,12 +33,12 @@ module Minitest
|
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def print(*
|
|
37
|
-
stream.print(*
|
|
36
|
+
def print(*)
|
|
37
|
+
stream.print(*)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def puts(*
|
|
41
|
-
stream.puts(*
|
|
40
|
+
def puts(*)
|
|
41
|
+
stream.puts(*)
|
|
42
42
|
end
|
|
43
43
|
alias newline puts
|
|
44
44
|
|
|
@@ -152,12 +152,10 @@ module Minitest
|
|
|
152
152
|
def print_tokens(lines_of_tokens)
|
|
153
153
|
lines_of_tokens.each do |tokens|
|
|
154
154
|
tokens.each do |token|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
puts "Token error (#{e.message}): #{token.inspect}"
|
|
160
|
-
end
|
|
155
|
+
print Token.new(*token).to_s(token_format)
|
|
156
|
+
rescue ArgumentError => e
|
|
157
|
+
# Token format error - output debug info and continue
|
|
158
|
+
puts "Token error (#{e.message}): #{token.inspect}"
|
|
161
159
|
end
|
|
162
160
|
newline
|
|
163
161
|
end
|