minitest-heat 0.0.9 → 0.0.13
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 +49 -14
- data/examples/exceptions.png +0 -0
- data/examples/failures.png +0 -0
- data/examples/map.png +0 -0
- data/examples/markers.png +0 -0
- data/examples/skips.png +0 -0
- data/examples/slows.png +0 -0
- data/lib/minitest/heat/backtrace/line_parser.rb +25 -0
- data/lib/minitest/heat/backtrace.rb +39 -43
- data/lib/minitest/heat/hit.rb +36 -19
- data/lib/minitest/heat/issue.rb +118 -81
- data/lib/minitest/heat/location.rb +115 -116
- data/lib/minitest/heat/locations.rb +105 -0
- data/lib/minitest/heat/map.rb +16 -4
- data/lib/minitest/heat/output/backtrace.rb +90 -67
- data/lib/minitest/heat/output/issue.rb +76 -67
- data/lib/minitest/heat/output/map.rb +127 -25
- 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 +76 -6
- data/lib/minitest/heat/results.rb +23 -3
- 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 +25 -35
- metadata +11 -4
- data/lib/minitest/heat/line.rb +0 -74
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Minitest
|
4
|
+
module Heat
|
5
|
+
# Convenience methods for determining the file and line number where the problem occurred.
|
6
|
+
# There are several layers of specificity to help make it easy to communicate the relative
|
7
|
+
# location of the failure:
|
8
|
+
# - 'final' represents the final line of the backtrace regardless of where it is
|
9
|
+
# - 'test_definition' represents where the test is defined
|
10
|
+
# - 'test_failure' represents the last line from the project's tests. It is further differentiated by
|
11
|
+
# the line where the test is defined and the actual line of code in the test that geneated
|
12
|
+
# the failure or exception
|
13
|
+
# - 'source_code' represents the last line from the project's source code
|
14
|
+
# - 'project' represents the last source line, but falls back to the last test line
|
15
|
+
# - 'most_relevant' represents the most specific file to investigate starting with the source
|
16
|
+
# code and then looking to the test code with final line of the backtrace as a fallback
|
17
|
+
class Locations
|
18
|
+
attr_reader :test_definition, :backtrace
|
19
|
+
|
20
|
+
def initialize(test_definition_location, backtrace = [])
|
21
|
+
test_definition_pathname, test_definition_line_number = test_definition_location
|
22
|
+
@test_definition = ::Minitest::Heat::Location.new(pathname: test_definition_pathname, line_number: test_definition_line_number)
|
23
|
+
|
24
|
+
@backtrace = Backtrace.new(backtrace)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Prints the pathname and line number of the location most likely to be the source of the
|
28
|
+
# test failure
|
29
|
+
#
|
30
|
+
# @return [String] ex. 'path/to/file.rb:12'
|
31
|
+
def to_s
|
32
|
+
"#{most_relevant.absolute_filename}:#{most_relevant.line_number}"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Knows if the failure is contained within the test. For example, if there's bad code in a
|
36
|
+
# test, and it raises an exception, then it's really a broken test rather than a proper
|
37
|
+
# faiure.
|
38
|
+
#
|
39
|
+
# @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
|
43
|
+
|
44
|
+
# Knows if the failure occurred in the actual project source code—as opposed to the test or
|
45
|
+
# an external piece of code like a gem.
|
46
|
+
#
|
47
|
+
# @return [Boolean] true if there's a non-test project file in the stacktrace but it's not
|
48
|
+
# a result of a broken test
|
49
|
+
def proper_failure?
|
50
|
+
!source_code.nil? && !broken_test?
|
51
|
+
end
|
52
|
+
|
53
|
+
# The file most likely to be the source of the underlying problem. Often, the most recent
|
54
|
+
# backtrace files will be a gem or external library that's failing indirectly as a result
|
55
|
+
# of a problem with local source code (not always, but frequently). In that case, the best
|
56
|
+
# first place to focus is on the code you control.
|
57
|
+
#
|
58
|
+
# @return [Array] file and line number of the most likely source of the problem
|
59
|
+
def most_relevant
|
60
|
+
[
|
61
|
+
source_code,
|
62
|
+
test_failure,
|
63
|
+
final
|
64
|
+
].compact.first
|
65
|
+
end
|
66
|
+
|
67
|
+
def freshest
|
68
|
+
backtrace.recently_modified_locations.first
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the final test location based on the backtrace if present. Otherwise falls back to
|
72
|
+
# the test location which represents the test definition. The `test_definition` attribute
|
73
|
+
# provides the location of where the test is defined. `test_failure` represents the actual
|
74
|
+
# line from within the test where the problem occurred
|
75
|
+
#
|
76
|
+
# @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
|
80
|
+
|
81
|
+
# Returns the final source code location based on the backtrace
|
82
|
+
#
|
83
|
+
# @return [Location] the final location from the source code files
|
84
|
+
def source_code
|
85
|
+
backtrace.source_code_locations.first
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns the final project location based on the backtrace if present. Otherwise falls back
|
89
|
+
# to the test location which represents the test definition.
|
90
|
+
#
|
91
|
+
# @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
|
95
|
+
|
96
|
+
# The line number from within the `test_file` test definition where the failure occurred
|
97
|
+
#
|
98
|
+
# @return [Location] the last location from the backtrace or the test location if a backtrace
|
99
|
+
# was not passed to the initializer
|
100
|
+
def final
|
101
|
+
backtrace.locations.any? ? backtrace.locations.first : test_definition
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
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,29 @@ module Minitest
|
|
11
12
|
@hits = {}
|
12
13
|
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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]
|
21
|
+
def add(pathname, line_number, type, backtrace: [])
|
22
|
+
@hits[pathname.to_s] ||= Hit.new(pathname)
|
23
|
+
@hits[pathname.to_s].log(type.to_sym, line_number, backtrace: backtrace)
|
18
24
|
end
|
19
25
|
|
26
|
+
# Returns a subset of affected files to keep the list from being overwhelming
|
27
|
+
#
|
28
|
+
# @return [Array] the list of files and the line numbers for each encountered issue type
|
20
29
|
def file_hits
|
21
30
|
hot_files.take(MAXIMUM_FILES_TO_SHOW)
|
22
31
|
end
|
23
32
|
|
24
33
|
private
|
25
34
|
|
35
|
+
# Sorts the files by hit "weight" so that the most problematic files are at the beginning
|
36
|
+
#
|
37
|
+
# @return [Array] the collection of files that encountred issues
|
26
38
|
def hot_files
|
27
39
|
hits.values.sort_by(&:weight).reverse
|
28
40
|
end
|
@@ -3,110 +3,137 @@
|
|
3
3
|
module Minitest
|
4
4
|
module Heat
|
5
5
|
class Output
|
6
|
-
# Builds the collection of tokens for a backtrace when an exception occurs
|
6
|
+
# Builds the collection of tokens for displaying a backtrace when an exception occurs
|
7
7
|
class Backtrace
|
8
8
|
DEFAULT_LINE_COUNT = 10
|
9
9
|
DEFAULT_INDENTATION_SPACES = 2
|
10
10
|
|
11
|
-
attr_accessor :
|
11
|
+
attr_accessor :locations, :backtrace
|
12
12
|
|
13
|
-
def initialize(
|
14
|
-
@
|
15
|
-
@backtrace =
|
13
|
+
def initialize(locations)
|
14
|
+
@locations = locations
|
15
|
+
@backtrace = locations.backtrace
|
16
16
|
@tokens = []
|
17
17
|
end
|
18
18
|
|
19
19
|
def tokens
|
20
|
-
# There could be option to expand and display more than one line of source code for the
|
21
|
-
# final backtrace line if it might be relevant/helpful?
|
22
|
-
|
23
20
|
# Iterate over the selected lines from the backtrace
|
24
|
-
|
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
|
-
]
|
32
|
-
|
33
|
-
parts << file_freshness(backtrace_entry) if most_recently_modified?(backtrace_entry)
|
34
|
-
|
35
|
-
@tokens << parts
|
36
|
-
end
|
37
|
-
|
38
|
-
@tokens
|
21
|
+
@tokens = backtrace_locations.map { |location| backtrace_location_tokens(location) }
|
39
22
|
end
|
40
23
|
|
24
|
+
# Determines the number of lines to display from the backtrace.
|
25
|
+
#
|
26
|
+
# @return [Integer] the number of lines to limit the backtrace to
|
41
27
|
def line_count
|
28
|
+
# Defined as a method instead of using the constant directlyr in order to easily support
|
29
|
+
# adding options for controlling how many lines are displayed from a backtrace.
|
30
|
+
#
|
31
|
+
# For example, instead of a fixed number, the backtrace could dynamically calculate how
|
32
|
+
# many lines it should displaye in order to get to the origination point. Or it could have
|
33
|
+
# a default, but inteligently go back further if the backtrace meets some criteria for
|
34
|
+
# displaying more lines.
|
42
35
|
DEFAULT_LINE_COUNT
|
43
36
|
end
|
44
37
|
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
38
|
+
# A subset of parsed lines from the backtrace.
|
39
|
+
#
|
40
|
+
# @return [Array<Location>] the backtrace locations determined to be most relevant to the
|
41
|
+
# context of the underlying issue
|
42
|
+
def backtrace_locations
|
43
|
+
# This could eventually have additional intelligence to determine what lines are most
|
44
|
+
# relevant for a given type of issue. For now, it simply takes the line numbers, but the
|
45
|
+
# idea is that long-term, it could adjust that on the fly to keep the line count as low
|
46
|
+
# as possible but expand it if necessary to ensure enough context is displayed.
|
47
|
+
#
|
48
|
+
# - If there's no clear cut details about the source of the error from within the project,
|
49
|
+
# it could display the entire backtrace without filtering anything.
|
50
|
+
# - It could scan the backtrace to the first appearance of project files and then display
|
51
|
+
# all of the lines that occurred after that instance
|
52
|
+
# - It coudl filter the lines differently whether the issue originated from a test or from
|
53
|
+
# the source code.
|
54
|
+
# - It could allow supporting a "compact" or "robust" reporter style so that someone on
|
55
|
+
# a smaller screen could easily reduce the information shown so that the results could
|
56
|
+
# be higher density even if it means truncating some occasionally useful details
|
57
|
+
# - It could be smarter about displaying context/guidance when the full backtrace is from
|
58
|
+
# outside the project's code
|
59
|
+
#
|
60
|
+
# But for now. It just grabs some lines.
|
61
|
+
backtrace.locations.take(line_count)
|
56
62
|
end
|
57
63
|
|
58
64
|
private
|
59
65
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
backtrace.project_entries.take(line_count)
|
66
|
+
def backtrace_location_tokens(location)
|
67
|
+
[
|
68
|
+
indentation_token,
|
69
|
+
path_token(location),
|
70
|
+
*file_and_line_number_tokens(location),
|
71
|
+
containining_element_token(location),
|
72
|
+
source_code_line_token(location),
|
73
|
+
most_recently_modified_token(location),
|
74
|
+
].compact
|
70
75
|
end
|
71
76
|
|
72
|
-
|
73
|
-
|
77
|
+
# Determines if all lines to be displayed are from within the project directory
|
78
|
+
#
|
79
|
+
# @return [Boolean] true if all lines of the backtrace being displayed are from the project
|
80
|
+
def all_backtrace_from_project?
|
81
|
+
backtrace_locations.all?(&:project_file?)
|
74
82
|
end
|
75
83
|
|
76
|
-
|
77
|
-
|
78
|
-
|
84
|
+
# Determines if the file referenced by a backtrace line is the most recently modified file
|
85
|
+
# of all the files referenced in the visible backtrace locations.
|
86
|
+
#
|
87
|
+
# @param [Location] location the location to examine
|
88
|
+
#
|
89
|
+
# @return [<type>] <description>
|
90
|
+
#
|
91
|
+
def most_recently_modified?(location)
|
92
|
+
# If there's more than one line being displayed (otherwise, with one line, of course it's
|
93
|
+
# the most recently modified because there_aren't any others) and the current line is the
|
94
|
+
# same as the freshest location in the backtrace
|
95
|
+
backtrace_locations.size > 1 && location == locations.freshest
|
79
96
|
end
|
80
97
|
|
81
98
|
def indentation_token
|
82
99
|
[:default, ' ' * indentation]
|
83
100
|
end
|
84
101
|
|
85
|
-
def path_token(
|
86
|
-
|
87
|
-
|
102
|
+
def path_token(location)
|
103
|
+
# If the line is a project file, help it stand out from the backtrace noise
|
104
|
+
style = location.project_file? ? :default : :muted
|
88
105
|
|
89
|
-
# If all of the backtrace lines are from the project, no point in the added redundant
|
90
|
-
#
|
91
|
-
|
106
|
+
# If *all* of the backtrace lines are from the project, no point in the added redundant
|
107
|
+
# noise of showing the project root directory over and over again
|
108
|
+
path_format = all_backtrace_from_project? ? :relative_path : :absolute_path
|
92
109
|
|
93
|
-
[style,
|
110
|
+
[style, location.send(path_format)]
|
94
111
|
end
|
95
112
|
|
96
|
-
def file_and_line_number_tokens(
|
97
|
-
style =
|
113
|
+
def file_and_line_number_tokens(location)
|
114
|
+
style = location.to_s.include?(Dir.pwd) ? :bold : :muted
|
98
115
|
[
|
99
|
-
[style,
|
116
|
+
[style, location.filename],
|
100
117
|
[:muted, ':'],
|
101
|
-
[style,
|
118
|
+
[style, location.line_number]
|
102
119
|
]
|
103
120
|
end
|
104
121
|
|
105
|
-
def source_code_line_token(
|
106
|
-
|
122
|
+
def source_code_line_token(location)
|
123
|
+
return nil unless location.project_file?
|
124
|
+
|
125
|
+
[:muted, " #{Output::SYMBOLS[:arrow]} `#{location.source_code.line.strip}`"]
|
107
126
|
end
|
108
127
|
|
109
|
-
def
|
128
|
+
def containining_element_token(location)
|
129
|
+
return nil if !location.project_file? || location.container.nil? || location.container.empty?
|
130
|
+
|
131
|
+
[:muted, " in #{location.container}"]
|
132
|
+
end
|
133
|
+
|
134
|
+
def most_recently_modified_token(location)
|
135
|
+
return nil unless most_recently_modified?(location)
|
136
|
+
|
110
137
|
[:default, " #{Output::SYMBOLS[:middot]} Most Recently Modified File"]
|
111
138
|
end
|
112
139
|
|
@@ -121,10 +148,6 @@ module Minitest
|
|
121
148
|
def indentation
|
122
149
|
DEFAULT_INDENTATION_SPACES
|
123
150
|
end
|
124
|
-
|
125
|
-
def style_for(path)
|
126
|
-
style = path.to_s.include?(Dir.pwd) ? :default : :muted
|
127
|
-
end
|
128
151
|
end
|
129
152
|
end
|
130
153
|
end
|
@@ -3,37 +3,27 @@
|
|
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
|
14
16
|
case issue.type
|
15
|
-
when :error then
|
16
|
-
when :
|
17
|
-
when :
|
18
|
-
when :
|
19
|
-
when :painful then painful_tokens
|
20
|
-
when :slow then slow_tokens
|
17
|
+
when :error, :broken then exception_tokens
|
18
|
+
when :failure then failure_tokens
|
19
|
+
when :skipped then skipped_tokens
|
20
|
+
when :painful, :slow then slow_tokens
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
private
|
25
25
|
|
26
|
-
def
|
27
|
-
[
|
28
|
-
headline_tokens,
|
29
|
-
test_location_tokens,
|
30
|
-
summary_tokens,
|
31
|
-
*backtrace_tokens,
|
32
|
-
newline_tokens
|
33
|
-
]
|
34
|
-
end
|
35
|
-
|
36
|
-
def broken_tokens
|
26
|
+
def exception_tokens
|
37
27
|
[
|
38
28
|
headline_tokens,
|
39
29
|
test_location_tokens,
|
@@ -60,14 +50,6 @@ module Minitest
|
|
60
50
|
]
|
61
51
|
end
|
62
52
|
|
63
|
-
def painful_tokens
|
64
|
-
[
|
65
|
-
headline_tokens,
|
66
|
-
slowness_summary_tokens,
|
67
|
-
newline_tokens
|
68
|
-
]
|
69
|
-
end
|
70
|
-
|
71
53
|
def slow_tokens
|
72
54
|
[
|
73
55
|
headline_tokens,
|
@@ -77,75 +59,82 @@ module Minitest
|
|
77
59
|
end
|
78
60
|
|
79
61
|
def headline_tokens
|
80
|
-
[
|
62
|
+
[label_token(issue), spacer_token, [:default, test_name(issue)]]
|
63
|
+
end
|
64
|
+
|
65
|
+
# Creates a display-friendly version of the test name with underscores removed and the
|
66
|
+
# first letter capitalized regardless of the formatt used for the test definition
|
67
|
+
# @param issue [Issue] the issue to use to generate the test name
|
68
|
+
#
|
69
|
+
# @return [String] the cleaned up version of the test name
|
70
|
+
def test_name(issue)
|
71
|
+
test_prefix = 'test_'
|
72
|
+
identifier = issue.test_identifier
|
73
|
+
|
74
|
+
if identifier.start_with?(test_prefix)
|
75
|
+
identifier.delete_prefix(test_prefix).gsub('_', ' ').capitalize
|
76
|
+
else
|
77
|
+
identifier
|
78
|
+
end
|
81
79
|
end
|
82
80
|
|
83
|
-
def
|
84
|
-
[
|
81
|
+
def label_token(issue)
|
82
|
+
[issue.type, issue_label(issue.type)]
|
85
83
|
end
|
86
84
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
90
|
-
backtrace.tokens
|
85
|
+
def test_name_and_class_tokens
|
86
|
+
[[:default, issue.test_class], *test_location_tokens]
|
91
87
|
end
|
92
88
|
|
93
89
|
def test_location_tokens
|
94
|
-
[
|
90
|
+
[
|
91
|
+
[:default, locations.test_definition.relative_filename],
|
92
|
+
[:muted, ':'],
|
93
|
+
[:default, locations.test_definition.line_number],
|
94
|
+
arrow_token,
|
95
|
+
[:default, locations.test_failure.line_number],
|
96
|
+
[:muted, "\n #{locations.test_failure.source_code.line.strip}"]
|
97
|
+
]
|
95
98
|
end
|
96
99
|
|
97
100
|
def location_tokens
|
98
|
-
[
|
101
|
+
[
|
102
|
+
[:default, locations.most_relevant.relative_filename],
|
103
|
+
[:muted, ':'],
|
104
|
+
[:default, locations.most_relevant.line_number],
|
105
|
+
[:muted, "\n #{locations.most_relevant.source_code.line.strip}"]
|
106
|
+
]
|
99
107
|
end
|
100
108
|
|
101
109
|
def source_tokens
|
102
|
-
filename =
|
103
|
-
line_number =
|
104
|
-
|
110
|
+
filename = locations.project.filename
|
111
|
+
line_number = locations.project.line_number
|
105
112
|
source = Minitest::Heat::Source.new(filename, line_number: line_number)
|
113
|
+
|
106
114
|
[[:muted, " #{Output::SYMBOLS[:arrow]} `#{source.line.strip}`"]]
|
107
115
|
end
|
108
116
|
|
109
117
|
def summary_tokens
|
110
|
-
[[:italicized, issue.summary.delete_suffix(
|
118
|
+
[[:italicized, issue.summary.delete_suffix('---------------').strip]]
|
111
119
|
end
|
112
120
|
|
113
121
|
def slowness_summary_tokens
|
114
122
|
[
|
115
|
-
[:bold, issue
|
123
|
+
[:bold, slowness(issue)],
|
116
124
|
spacer_token,
|
117
|
-
[:default,
|
125
|
+
[:default, locations.test_definition.relative_path],
|
126
|
+
[:default, locations.test_definition.filename],
|
118
127
|
[:muted, ':'],
|
119
|
-
[:default,
|
128
|
+
[:default, locations.test_definition.line_number]
|
120
129
|
]
|
121
130
|
end
|
122
131
|
|
123
|
-
def
|
124
|
-
|
125
|
-
end
|
126
|
-
|
127
|
-
def most_relevant_short_location
|
128
|
-
issue.location.most_relevant_file.to_s.delete_prefix("#{Dir.pwd}/")
|
129
|
-
end
|
130
|
-
|
131
|
-
def test_file_short_location
|
132
|
-
issue.location.test_file.to_s.delete_prefix("#{Dir.pwd}/")
|
132
|
+
def slowness(issue)
|
133
|
+
"#{issue.execution_time.round(2)}s"
|
133
134
|
end
|
134
135
|
|
135
|
-
def
|
136
|
-
|
137
|
-
line_number = issue.location.project_failure_line
|
138
|
-
|
139
|
-
source = Minitest::Heat::Source.new(filename, line_number: line_number)
|
140
|
-
"\n #{source.line.strip}"
|
141
|
-
end
|
142
|
-
|
143
|
-
def test_line_source
|
144
|
-
filename = issue.location.test_file
|
145
|
-
line_number = issue.location.test_failure_line
|
146
|
-
|
147
|
-
source = Minitest::Heat::Source.new(filename, line_number: line_number)
|
148
|
-
"\n #{source.line.strip}"
|
136
|
+
def newline_tokens
|
137
|
+
[]
|
149
138
|
end
|
150
139
|
|
151
140
|
def spacer_token
|
@@ -156,6 +145,26 @@ module Minitest
|
|
156
145
|
Output::TOKENS[:muted_arrow]
|
157
146
|
end
|
158
147
|
|
148
|
+
def backtrace_tokens
|
149
|
+
@backtrace_tokens ||= ::Minitest::Heat::Output::Backtrace.new(locations).tokens
|
150
|
+
end
|
151
|
+
|
152
|
+
# The string to use to describe the failure type when displaying results/
|
153
|
+
# @param issue_type [Symbol] the symbol representing the issue's failure type
|
154
|
+
#
|
155
|
+
# @return [String] the display-friendly string describing the failure reason
|
156
|
+
def issue_label(issue_type)
|
157
|
+
case issue_type
|
158
|
+
when :error then 'Error'
|
159
|
+
when :broken then 'Broken Test'
|
160
|
+
when :failure then 'Failure'
|
161
|
+
when :skipped then 'Skipped'
|
162
|
+
when :slow then 'Passed but Slow'
|
163
|
+
when :painful then 'Passed but Very Slow'
|
164
|
+
when :passed then 'Success'
|
165
|
+
else 'Unknown'
|
166
|
+
end
|
167
|
+
end
|
159
168
|
end
|
160
169
|
end
|
161
170
|
end
|